@camstack/addon-provider-reolink 1.2.211 → 1.2.213
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -1
- package/README.md +47 -10
- package/dist/addon.js +1525 -286
- package/dist/addon.mjs +1525 -286
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -36,7 +36,7 @@ let node_os = require("node:os");
|
|
|
36
36
|
node_os = require_chunk.__toESM(node_os, 1);
|
|
37
37
|
let node_fs = require("node:fs");
|
|
38
38
|
node_fs = require_chunk.__toESM(node_fs, 1);
|
|
39
|
-
//#region ../types/dist/event-category-
|
|
39
|
+
//#region ../types/dist/event-category-DmZDPDf_.mjs
|
|
40
40
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
41
41
|
EventCategory["SystemBoot"] = "system.boot";
|
|
42
42
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -663,7 +663,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
663
663
|
* (a track was promoted to a stationary-registry entry) or departs (the
|
|
664
664
|
* object moved / was removed). Telemetry (D8): lossy, drives a UI refresh of
|
|
665
665
|
* the dedicated "Stationary" section — never the live event feed. Payload:
|
|
666
|
-
* `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp }`.
|
|
666
|
+
* `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp, observedAt, zones }`.
|
|
667
667
|
*/
|
|
668
668
|
EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
|
|
669
669
|
/**
|
|
@@ -801,6 +801,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
801
801
|
*/
|
|
802
802
|
EventCategory["AccessoriesChanged"] = "accessories.onAccessoriesChanged";
|
|
803
803
|
/**
|
|
804
|
+
* An action run started or ended. Emitted by the `actions` surface.
|
|
805
|
+
* Payload: `ActionRunChanged` (`{ actionId, runId, isRunning, outcome?, lastRunAt, lastError }`).
|
|
806
|
+
*/
|
|
807
|
+
EventCategory["ActionsRunChanged"] = "actions.onRunChanged";
|
|
808
|
+
/**
|
|
804
809
|
* Progress update from a running model conversion job.
|
|
805
810
|
* Payload: `{ kind: 'model-convert', phase, sessionId?, pct?, detail? }`.
|
|
806
811
|
* Emitted by `addon-model-studio` on the converting node.
|
|
@@ -5479,7 +5484,7 @@ var ZodIssueCode = {
|
|
|
5479
5484
|
var ZodFirstPartyTypeKind;
|
|
5480
5485
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5481
5486
|
//#endregion
|
|
5482
|
-
//#region ../types/dist/sleep-
|
|
5487
|
+
//#region ../types/dist/sleep-Crlmu_tO.mjs
|
|
5483
5488
|
/**
|
|
5484
5489
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5485
5490
|
* window to float samples (D455).
|
|
@@ -8637,6 +8642,8 @@ Object.freeze({ "doorbell": "lastPressedAt" });
|
|
|
8637
8642
|
/** Every capability name, so addon code can check a name without the schema graph. */
|
|
8638
8643
|
var CAP_NAMES = Object.freeze([
|
|
8639
8644
|
"accessories",
|
|
8645
|
+
"action-source",
|
|
8646
|
+
"actions",
|
|
8640
8647
|
"addon-pages",
|
|
8641
8648
|
"addon-pages-source",
|
|
8642
8649
|
"addon-routes",
|
|
@@ -8710,6 +8717,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
8710
8717
|
"filesystem-browse",
|
|
8711
8718
|
"flood",
|
|
8712
8719
|
"gas",
|
|
8720
|
+
"group-active",
|
|
8713
8721
|
"group-commands",
|
|
8714
8722
|
"group-status",
|
|
8715
8723
|
"heating-valve",
|
|
@@ -8732,6 +8740,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
8732
8740
|
"login-method",
|
|
8733
8741
|
"media-player",
|
|
8734
8742
|
"mesh-network",
|
|
8743
|
+
"meter",
|
|
8735
8744
|
"metrics-provider",
|
|
8736
8745
|
"model-convert",
|
|
8737
8746
|
"model-distributor",
|
|
@@ -8823,11 +8832,11 @@ var CAP_NAMES = Object.freeze([
|
|
|
8823
8832
|
Object.freeze({
|
|
8824
8833
|
"air-quality-sensor": [
|
|
8825
8834
|
{
|
|
8826
|
-
"path": "
|
|
8835
|
+
"path": "co2",
|
|
8827
8836
|
"valueType": "number"
|
|
8828
8837
|
},
|
|
8829
8838
|
{
|
|
8830
|
-
"path": "
|
|
8839
|
+
"path": "voc",
|
|
8831
8840
|
"valueType": "number"
|
|
8832
8841
|
},
|
|
8833
8842
|
{
|
|
@@ -8877,7 +8886,7 @@ Object.freeze({
|
|
|
8877
8886
|
}
|
|
8878
8887
|
],
|
|
8879
8888
|
"ambient-light-sensor": [{
|
|
8880
|
-
"path": "
|
|
8889
|
+
"path": "illuminance",
|
|
8881
8890
|
"valueType": "number"
|
|
8882
8891
|
}],
|
|
8883
8892
|
"audio-metrics": [
|
|
@@ -8938,7 +8947,7 @@ Object.freeze({
|
|
|
8938
8947
|
],
|
|
8939
8948
|
"battery": [
|
|
8940
8949
|
{
|
|
8941
|
-
"path": "
|
|
8950
|
+
"path": "level",
|
|
8942
8951
|
"valueType": "number"
|
|
8943
8952
|
},
|
|
8944
8953
|
{
|
|
@@ -8960,7 +8969,7 @@ Object.freeze({
|
|
|
8960
8969
|
"valueType": "number"
|
|
8961
8970
|
},
|
|
8962
8971
|
{
|
|
8963
|
-
"path": "
|
|
8972
|
+
"path": "voltage",
|
|
8964
8973
|
"valueType": "number"
|
|
8965
8974
|
},
|
|
8966
8975
|
{
|
|
@@ -8981,7 +8990,7 @@ Object.freeze({
|
|
|
8981
8990
|
"valueType": "boolean"
|
|
8982
8991
|
}],
|
|
8983
8992
|
"brightness": [{
|
|
8984
|
-
"path": "
|
|
8993
|
+
"path": "level",
|
|
8985
8994
|
"valueType": "number"
|
|
8986
8995
|
}],
|
|
8987
8996
|
"broker": [{
|
|
@@ -9182,7 +9191,7 @@ Object.freeze({
|
|
|
9182
9191
|
"valueType": "number"
|
|
9183
9192
|
},
|
|
9184
9193
|
{
|
|
9185
|
-
"path": "
|
|
9194
|
+
"path": "colorTemperature",
|
|
9186
9195
|
"valueType": "number"
|
|
9187
9196
|
}
|
|
9188
9197
|
],
|
|
@@ -9361,15 +9370,15 @@ Object.freeze({
|
|
|
9361
9370
|
"valueType": "string"
|
|
9362
9371
|
},
|
|
9363
9372
|
{
|
|
9364
|
-
"path": "
|
|
9373
|
+
"path": "remaining",
|
|
9365
9374
|
"valueType": "number"
|
|
9366
9375
|
},
|
|
9367
9376
|
{
|
|
9368
|
-
"path": "
|
|
9377
|
+
"path": "delayedStart",
|
|
9369
9378
|
"valueType": "number"
|
|
9370
9379
|
},
|
|
9371
9380
|
{
|
|
9372
|
-
"path": "
|
|
9381
|
+
"path": "progress",
|
|
9373
9382
|
"valueType": "number"
|
|
9374
9383
|
},
|
|
9375
9384
|
{
|
|
@@ -9483,7 +9492,7 @@ Object.freeze({
|
|
|
9483
9492
|
],
|
|
9484
9493
|
"fan-control": [
|
|
9485
9494
|
{
|
|
9486
|
-
"path": "
|
|
9495
|
+
"path": "speed",
|
|
9487
9496
|
"valueType": "number"
|
|
9488
9497
|
},
|
|
9489
9498
|
{
|
|
@@ -9530,6 +9539,10 @@ Object.freeze({
|
|
|
9530
9539
|
"path": "detected",
|
|
9531
9540
|
"valueType": "boolean"
|
|
9532
9541
|
}],
|
|
9542
|
+
"group-active": [{
|
|
9543
|
+
"path": "active",
|
|
9544
|
+
"valueType": "boolean"
|
|
9545
|
+
}],
|
|
9533
9546
|
"group-status": [
|
|
9534
9547
|
{
|
|
9535
9548
|
"path": "included",
|
|
@@ -9579,7 +9592,7 @@ Object.freeze({
|
|
|
9579
9592
|
}
|
|
9580
9593
|
],
|
|
9581
9594
|
"humidity-sensor": [{
|
|
9582
|
-
"path": "
|
|
9595
|
+
"path": "humidity",
|
|
9583
9596
|
"valueType": "number"
|
|
9584
9597
|
}],
|
|
9585
9598
|
"image": [{
|
|
@@ -9689,7 +9702,7 @@ Object.freeze({
|
|
|
9689
9702
|
"valueType": "number"
|
|
9690
9703
|
},
|
|
9691
9704
|
{
|
|
9692
|
-
"path": "
|
|
9705
|
+
"path": "progress",
|
|
9693
9706
|
"valueType": "number"
|
|
9694
9707
|
},
|
|
9695
9708
|
{
|
|
@@ -9768,11 +9781,11 @@ Object.freeze({
|
|
|
9768
9781
|
"valueType": "string"
|
|
9769
9782
|
},
|
|
9770
9783
|
{
|
|
9771
|
-
"path": "
|
|
9784
|
+
"path": "position",
|
|
9772
9785
|
"valueType": "number"
|
|
9773
9786
|
},
|
|
9774
9787
|
{
|
|
9775
|
-
"path": "
|
|
9788
|
+
"path": "duration",
|
|
9776
9789
|
"valueType": "number"
|
|
9777
9790
|
},
|
|
9778
9791
|
{
|
|
@@ -9789,6 +9802,10 @@ Object.freeze({
|
|
|
9789
9802
|
]
|
|
9790
9803
|
}
|
|
9791
9804
|
],
|
|
9805
|
+
"meter": [{
|
|
9806
|
+
"path": "value",
|
|
9807
|
+
"valueType": "number"
|
|
9808
|
+
}],
|
|
9792
9809
|
"motion": [
|
|
9793
9810
|
{
|
|
9794
9811
|
"path": "detected",
|
|
@@ -9870,11 +9887,11 @@ Object.freeze({
|
|
|
9870
9887
|
]
|
|
9871
9888
|
},
|
|
9872
9889
|
{
|
|
9873
|
-
"path": "
|
|
9890
|
+
"path": "signal",
|
|
9874
9891
|
"valueType": "number"
|
|
9875
9892
|
},
|
|
9876
9893
|
{
|
|
9877
|
-
"path": "
|
|
9894
|
+
"path": "rssi",
|
|
9878
9895
|
"valueType": "number"
|
|
9879
9896
|
},
|
|
9880
9897
|
{
|
|
@@ -9929,6 +9946,18 @@ Object.freeze({
|
|
|
9929
9946
|
"path": "foodLevel",
|
|
9930
9947
|
"valueType": "number"
|
|
9931
9948
|
},
|
|
9949
|
+
{
|
|
9950
|
+
"path": "bowlFood",
|
|
9951
|
+
"valueType": "number"
|
|
9952
|
+
},
|
|
9953
|
+
{
|
|
9954
|
+
"path": "eating",
|
|
9955
|
+
"valueType": "boolean"
|
|
9956
|
+
},
|
|
9957
|
+
{
|
|
9958
|
+
"path": "supportsMarkRefilled",
|
|
9959
|
+
"valueType": "boolean"
|
|
9960
|
+
},
|
|
9932
9961
|
{
|
|
9933
9962
|
"path": "food1",
|
|
9934
9963
|
"valueType": "number"
|
|
@@ -9993,23 +10022,23 @@ Object.freeze({
|
|
|
9993
10022
|
],
|
|
9994
10023
|
"power-meter": [
|
|
9995
10024
|
{
|
|
9996
|
-
"path": "
|
|
10025
|
+
"path": "power",
|
|
9997
10026
|
"valueType": "number"
|
|
9998
10027
|
},
|
|
9999
10028
|
{
|
|
10000
|
-
"path": "
|
|
10029
|
+
"path": "energy",
|
|
10001
10030
|
"valueType": "number"
|
|
10002
10031
|
},
|
|
10003
10032
|
{
|
|
10004
|
-
"path": "
|
|
10033
|
+
"path": "voltage",
|
|
10005
10034
|
"valueType": "number"
|
|
10006
10035
|
},
|
|
10007
10036
|
{
|
|
10008
|
-
"path": "
|
|
10037
|
+
"path": "current",
|
|
10009
10038
|
"valueType": "number"
|
|
10010
10039
|
},
|
|
10011
10040
|
{
|
|
10012
|
-
"path": "
|
|
10041
|
+
"path": "apparentPower",
|
|
10013
10042
|
"valueType": "number"
|
|
10014
10043
|
},
|
|
10015
10044
|
{
|
|
@@ -10037,14 +10066,10 @@ Object.freeze({
|
|
|
10037
10066
|
{
|
|
10038
10067
|
"path": "gps.accuracyMeters",
|
|
10039
10068
|
"valueType": "number"
|
|
10040
|
-
},
|
|
10041
|
-
{
|
|
10042
|
-
"path": "batteryPercent",
|
|
10043
|
-
"valueType": "number"
|
|
10044
10069
|
}
|
|
10045
10070
|
],
|
|
10046
10071
|
"pressure-sensor": [{
|
|
10047
|
-
"path": "
|
|
10072
|
+
"path": "pressure",
|
|
10048
10073
|
"valueType": "number"
|
|
10049
10074
|
}],
|
|
10050
10075
|
"privacy-mask": [{
|
|
@@ -10358,7 +10383,7 @@ Object.freeze({
|
|
|
10358
10383
|
"valueType": "boolean"
|
|
10359
10384
|
}],
|
|
10360
10385
|
"temperature-sensor": [{
|
|
10361
|
-
"path": "
|
|
10386
|
+
"path": "temperature",
|
|
10362
10387
|
"valueType": "number"
|
|
10363
10388
|
}],
|
|
10364
10389
|
"update": [
|
|
@@ -10406,7 +10431,7 @@ Object.freeze({
|
|
|
10406
10431
|
"valueType": "string"
|
|
10407
10432
|
},
|
|
10408
10433
|
{
|
|
10409
|
-
"path": "
|
|
10434
|
+
"path": "progress",
|
|
10410
10435
|
"valueType": "number"
|
|
10411
10436
|
},
|
|
10412
10437
|
{
|
|
@@ -10570,7 +10595,7 @@ function sleep$2(ms) {
|
|
|
10570
10595
|
return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
|
|
10571
10596
|
}
|
|
10572
10597
|
//#endregion
|
|
10573
|
-
//#region ../types/dist/err-msg-
|
|
10598
|
+
//#region ../types/dist/err-msg-DIorGYeC.mjs
|
|
10574
10599
|
var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
10575
10600
|
DeviceType["Camera"] = "camera";
|
|
10576
10601
|
DeviceType["Hub"] = "hub";
|
|
@@ -10664,8 +10689,14 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
|
10664
10689
|
DeviceType["PetFeeder"] = "pet-feeder";
|
|
10665
10690
|
/** Built-in dishwasher — program lifecycle, door / remote-start gates,
|
|
10666
10691
|
* salt and rinse-aid levels. Installed with the `dishwasher` cap.
|
|
10667
|
-
* Sources: native Bosch Home Connect, reusable by similar integrations.
|
|
10692
|
+
* Sources: native Bosch Home Connect, reusable by similar integrations.
|
|
10693
|
+
* A plug-built dishwasher carries running-state and power instead. */
|
|
10668
10694
|
DeviceType["Dishwasher"] = "dishwasher";
|
|
10695
|
+
/** Washing machine. A plug-built one is running while its power stays
|
|
10696
|
+
* above a threshold. No program cap — that surface is the dishwasher's. */
|
|
10697
|
+
DeviceType["Washer"] = "washer";
|
|
10698
|
+
/** Tumble dryer. Same plug-built running surface as {@link Washer}. */
|
|
10699
|
+
DeviceType["Dryer"] = "dryer";
|
|
10669
10700
|
return DeviceType;
|
|
10670
10701
|
}({});
|
|
10671
10702
|
var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
|
|
@@ -10913,7 +10944,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
|
|
|
10913
10944
|
return DeviceRole;
|
|
10914
10945
|
}({});
|
|
10915
10946
|
//#endregion
|
|
10916
|
-
//#region ../types/dist/composition-
|
|
10947
|
+
//#region ../types/dist/composition-CZImLpOI.mjs
|
|
10917
10948
|
/**
|
|
10918
10949
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
10919
10950
|
*
|
|
@@ -11228,6 +11259,13 @@ var table$1 = {
|
|
|
11228
11259
|
apply: () => {
|
|
11229
11260
|
throw new ExpressionEvalError("latchedAt: stateful evaluation context is required");
|
|
11230
11261
|
}
|
|
11262
|
+
},
|
|
11263
|
+
schmitt: {
|
|
11264
|
+
minArgs: 3,
|
|
11265
|
+
maxArgs: 5,
|
|
11266
|
+
apply: () => {
|
|
11267
|
+
throw new ExpressionEvalError("schmitt: stateful evaluation context is required");
|
|
11268
|
+
}
|
|
11231
11269
|
}
|
|
11232
11270
|
};
|
|
11233
11271
|
Object.freeze(Object.assign(Object.create(null), table$1));
|
|
@@ -11669,6 +11707,99 @@ function validateExpressionSource(src) {
|
|
|
11669
11707
|
return null;
|
|
11670
11708
|
}
|
|
11671
11709
|
/**
|
|
11710
|
+
* One power reading becomes a washer, dryer or dishwasher (D944 helper).
|
|
11711
|
+
*
|
|
11712
|
+
* Standby of the control board is under 5 W; a motor or a heater is far
|
|
11713
|
+
* above that, so 5 W is the line. A door lock or a short pump spike is not
|
|
11714
|
+
* a cycle, so running starts only after 30 s above the line.
|
|
11715
|
+
*
|
|
11716
|
+
* A wash, dry or dishwasher pause (soak, heat-wait, heater off between
|
|
11717
|
+
* bursts) lasts several minutes. Ending the cycle on the first dip would
|
|
11718
|
+
* report "finished" in the middle of the program, so the stop dwell is
|
|
11719
|
+
* 10 minutes on every type. The number is stored per type so one machine
|
|
11720
|
+
* can move later without moving the others.
|
|
11721
|
+
*/
|
|
11722
|
+
var APPLIANCE_TEMPLATE_ID = "appliance";
|
|
11723
|
+
var APPLIANCE_KINDS = [
|
|
11724
|
+
"washer",
|
|
11725
|
+
"dryer",
|
|
11726
|
+
"dishwasher"
|
|
11727
|
+
];
|
|
11728
|
+
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
11729
|
+
/**
|
|
11730
|
+
* Statistics and integrals over a history read (D944).
|
|
11731
|
+
*
|
|
11732
|
+
* Pure: the same points always give the same answer, so a restart recomputes
|
|
11733
|
+
* rather than trusting a buffer. A gauge mean is time-weighted (value × ms),
|
|
11734
|
+
* never the mean of the samples. A hole with no held value is not integrated
|
|
11735
|
+
* as zero. A truncated read is not published.
|
|
11736
|
+
*/
|
|
11737
|
+
var STATISTIC_STATS = [
|
|
11738
|
+
"min",
|
|
11739
|
+
"max",
|
|
11740
|
+
"mean",
|
|
11741
|
+
"sum",
|
|
11742
|
+
"count",
|
|
11743
|
+
"change"
|
|
11744
|
+
];
|
|
11745
|
+
/**
|
|
11746
|
+
* The range a statistics or integral helper asks history for (D944).
|
|
11747
|
+
*
|
|
11748
|
+
* Calendar edges are the hub's timezone, never UTC midnight: a day is 23, 24
|
|
11749
|
+
* or 25 hours across a DST change. A tier is eligible only when its retention
|
|
11750
|
+
* is strictly longer than the longest that range can be. Forever qualifies.
|
|
11751
|
+
* Equal to a tier's retention is a refusal, not a short answer.
|
|
11752
|
+
*/
|
|
11753
|
+
var MINUTE_MS = 6e4;
|
|
11754
|
+
var HOUR_MS = 60 * MINUTE_MS;
|
|
11755
|
+
var DAY_MS = 24 * HOUR_MS;
|
|
11756
|
+
var ROLLING_SPANS = [
|
|
11757
|
+
"1h",
|
|
11758
|
+
"24h",
|
|
11759
|
+
"2d",
|
|
11760
|
+
"7d",
|
|
11761
|
+
"30d",
|
|
11762
|
+
"custom"
|
|
11763
|
+
];
|
|
11764
|
+
var CALENDAR_PERIODS = [
|
|
11765
|
+
"today",
|
|
11766
|
+
"yesterday",
|
|
11767
|
+
"this-week",
|
|
11768
|
+
"this-month"
|
|
11769
|
+
];
|
|
11770
|
+
var RESET_PERIODS = [
|
|
11771
|
+
"never",
|
|
11772
|
+
"daily",
|
|
11773
|
+
"weekly",
|
|
11774
|
+
"monthly"
|
|
11775
|
+
];
|
|
11776
|
+
5 * MINUTE_MS;
|
|
11777
|
+
24 * HOUR_MS, 2 * DAY_MS, 7 * DAY_MS, 30 * DAY_MS;
|
|
11778
|
+
7 * DAY_MS, 30 * DAY_MS, 365 * DAY_MS;
|
|
11779
|
+
var rollingWindowObject = object({
|
|
11780
|
+
kind: literal("rolling"),
|
|
11781
|
+
span: _enum(ROLLING_SPANS),
|
|
11782
|
+
customMinutes: number().int().min(5).optional()
|
|
11783
|
+
}).check((payload) => {
|
|
11784
|
+
if (payload.value.span === "custom" && payload.value.customMinutes === void 0) payload.issues.push({
|
|
11785
|
+
code: "custom",
|
|
11786
|
+
message: "a custom range needs a duration in minutes",
|
|
11787
|
+
path: ["customMinutes"],
|
|
11788
|
+
input: payload.value
|
|
11789
|
+
});
|
|
11790
|
+
if (payload.value.span !== "custom" && payload.value.customMinutes !== void 0) payload.issues.push({
|
|
11791
|
+
code: "custom",
|
|
11792
|
+
message: "only a custom range carries a duration",
|
|
11793
|
+
path: ["customMinutes"],
|
|
11794
|
+
input: payload.value
|
|
11795
|
+
});
|
|
11796
|
+
});
|
|
11797
|
+
var RollingWindowSchema = rollingWindowObject;
|
|
11798
|
+
var StatisticWindowSchema = discriminatedUnion("kind", [rollingWindowObject, object({
|
|
11799
|
+
kind: literal("calendar"),
|
|
11800
|
+
period: _enum(CALENDAR_PERIODS)
|
|
11801
|
+
})]);
|
|
11802
|
+
/**
|
|
11672
11803
|
* A composition source identity: `(addonId, stableId)`, never a numeric device
|
|
11673
11804
|
* id. Split out so helper schemas can name it without a cycle through
|
|
11674
11805
|
* `composition.ts` (D659).
|
|
@@ -11694,7 +11825,13 @@ var GROUP_TEMPLATE_IDS = [
|
|
|
11694
11825
|
"occupancy",
|
|
11695
11826
|
"binary",
|
|
11696
11827
|
"switch",
|
|
11697
|
-
"light"
|
|
11828
|
+
"light",
|
|
11829
|
+
"fan",
|
|
11830
|
+
"cover",
|
|
11831
|
+
"valve",
|
|
11832
|
+
"lock",
|
|
11833
|
+
"media-player",
|
|
11834
|
+
"climate"
|
|
11698
11835
|
];
|
|
11699
11836
|
var NUMERIC_AGGREGATES = [
|
|
11700
11837
|
"mean",
|
|
@@ -11724,14 +11861,73 @@ var HelperMemberSchema = object({
|
|
|
11724
11861
|
cap: string().min(1),
|
|
11725
11862
|
fieldPath: string().min(1)
|
|
11726
11863
|
});
|
|
11727
|
-
var HelperSpecSchema = discriminatedUnion("kind", [
|
|
11728
|
-
|
|
11729
|
-
|
|
11730
|
-
|
|
11731
|
-
|
|
11732
|
-
|
|
11733
|
-
|
|
11734
|
-
|
|
11864
|
+
var HelperSpecSchema = discriminatedUnion("kind", [
|
|
11865
|
+
object({
|
|
11866
|
+
kind: literal("group"),
|
|
11867
|
+
templateId: GroupTemplateIdSchema,
|
|
11868
|
+
aggregate: AggregateReducerSchema,
|
|
11869
|
+
members: array(HelperMemberSchema).min(1).max(64),
|
|
11870
|
+
/** Actuator groups only. Readings leave this unset. */
|
|
11871
|
+
active: ActiveCriterionSchema.optional()
|
|
11872
|
+
}),
|
|
11873
|
+
object({
|
|
11874
|
+
kind: literal("appliance"),
|
|
11875
|
+
templateId: literal(APPLIANCE_TEMPLATE_ID),
|
|
11876
|
+
appliance: _enum(APPLIANCE_KINDS),
|
|
11877
|
+
thresholdWatts: number().positive(),
|
|
11878
|
+
startDwellMs: number().int().nonnegative(),
|
|
11879
|
+
stopDwellMs: number().int().nonnegative(),
|
|
11880
|
+
member: HelperMemberSchema
|
|
11881
|
+
}),
|
|
11882
|
+
object({
|
|
11883
|
+
kind: literal("statistic"),
|
|
11884
|
+
stat: _enum(STATISTIC_STATS),
|
|
11885
|
+
window: StatisticWindowSchema,
|
|
11886
|
+
source: HelperMemberSchema
|
|
11887
|
+
}),
|
|
11888
|
+
object({
|
|
11889
|
+
kind: literal("integral"),
|
|
11890
|
+
reset: _enum(RESET_PERIODS),
|
|
11891
|
+
output: _enum(["energy", "meter"]),
|
|
11892
|
+
source: HelperMemberSchema
|
|
11893
|
+
}),
|
|
11894
|
+
object({
|
|
11895
|
+
kind: literal("threshold"),
|
|
11896
|
+
source: HelperMemberSchema,
|
|
11897
|
+
high: number(),
|
|
11898
|
+
low: number(),
|
|
11899
|
+
onForMs: number().int().nonnegative(),
|
|
11900
|
+
offForMs: number().int().nonnegative()
|
|
11901
|
+
}),
|
|
11902
|
+
object({
|
|
11903
|
+
kind: literal("arithmetic"),
|
|
11904
|
+
op: _enum([
|
|
11905
|
+
"sum",
|
|
11906
|
+
"difference",
|
|
11907
|
+
"ratio"
|
|
11908
|
+
]),
|
|
11909
|
+
inputs: array(HelperMemberSchema).min(2).max(64)
|
|
11910
|
+
}),
|
|
11911
|
+
object({
|
|
11912
|
+
kind: literal("expression"),
|
|
11913
|
+
expr: string().min(1),
|
|
11914
|
+
inputs: array(object({
|
|
11915
|
+
name: string().min(1),
|
|
11916
|
+
member: HelperMemberSchema
|
|
11917
|
+
})).min(1).max(64),
|
|
11918
|
+
output: _enum(["number", "boolean"])
|
|
11919
|
+
}),
|
|
11920
|
+
object({
|
|
11921
|
+
kind: literal("derivative"),
|
|
11922
|
+
window: RollingWindowSchema,
|
|
11923
|
+
per: _enum([
|
|
11924
|
+
"second",
|
|
11925
|
+
"minute",
|
|
11926
|
+
"hour"
|
|
11927
|
+
]),
|
|
11928
|
+
source: HelperMemberSchema
|
|
11929
|
+
})
|
|
11930
|
+
]);
|
|
11735
11931
|
/** A composed device's stableId is this prefix plus the block id. */
|
|
11736
11932
|
var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
|
|
11737
11933
|
/** Longest snippet a `code` source may carry (slice 4). */
|
|
@@ -11792,6 +11988,12 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
|
|
|
11792
11988
|
"all"
|
|
11793
11989
|
]),
|
|
11794
11990
|
reads: array(CompositionFieldReadSchema).min(1).max(64),
|
|
11991
|
+
/**
|
|
11992
|
+
* Set when the reads are an enum state, not a yes/no: the listed states
|
|
11993
|
+
* count as active (a cover is `open`). A state outside the list is inactive;
|
|
11994
|
+
* a read that is not a state at all is left out. Only `any` / `all` use it.
|
|
11995
|
+
*/
|
|
11996
|
+
activeWhen: array(string().min(1)).min(1).optional(),
|
|
11795
11997
|
/** `value` is the reading; the others are the member counts. */
|
|
11796
11998
|
publish: _enum([
|
|
11797
11999
|
"value",
|
|
@@ -11805,6 +12007,26 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
|
|
|
11805
12007
|
"omit",
|
|
11806
12008
|
"offline"
|
|
11807
12009
|
])
|
|
12010
|
+
}),
|
|
12011
|
+
object({
|
|
12012
|
+
kind: literal("series"),
|
|
12013
|
+
read: CompositionFieldReadSchema,
|
|
12014
|
+
/** `value` is the statistic or the integral. The others are the gap counts. */
|
|
12015
|
+
publish: _enum([
|
|
12016
|
+
"value",
|
|
12017
|
+
"included",
|
|
12018
|
+
"excluded",
|
|
12019
|
+
"total"
|
|
12020
|
+
]),
|
|
12021
|
+
/**
|
|
12022
|
+
* What an unanswered `value` does. Counts ignore this. `offline` leaves the
|
|
12023
|
+
* device unseeded until history answers, so a restart cannot publish 0.
|
|
12024
|
+
*/
|
|
12025
|
+
empty: _enum([
|
|
12026
|
+
"null",
|
|
12027
|
+
"omit",
|
|
12028
|
+
"offline"
|
|
12029
|
+
])
|
|
11808
12030
|
})
|
|
11809
12031
|
]);
|
|
11810
12032
|
var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
|
|
@@ -15812,6 +16034,14 @@ var AutomationConditionLeafSchema = object({
|
|
|
15812
16034
|
array(union([string(), number()]))
|
|
15813
16035
|
])
|
|
15814
16036
|
});
|
|
16037
|
+
var AutomationConditionStationaryPresentSchema = object({
|
|
16038
|
+
kind: literal("stationary-present"),
|
|
16039
|
+
deviceId: number().int().nonnegative(),
|
|
16040
|
+
/** Class names; a macro class matches every raw class that maps to it. */
|
|
16041
|
+
classes: array(string().min(1)).min(1).optional(),
|
|
16042
|
+
/** The parked object must be in at least one of these zones. */
|
|
16043
|
+
zones: array(string().min(1)).min(1).optional()
|
|
16044
|
+
});
|
|
15815
16045
|
/**
|
|
15816
16046
|
* The expression leaf, declared as a plain object rather than an intersection
|
|
15817
16047
|
* with {@link ExpressionSourceSchema}: a discriminated union has to be able to
|
|
@@ -15845,7 +16075,8 @@ var AutomationConditionSchema = lazy(() => discriminatedUnion("kind", [
|
|
|
15845
16075
|
child: AutomationConditionSchema
|
|
15846
16076
|
}),
|
|
15847
16077
|
AutomationConditionLeafSchema,
|
|
15848
|
-
AutomationConditionExpressionSchema
|
|
16078
|
+
AutomationConditionExpressionSchema,
|
|
16079
|
+
AutomationConditionStationaryPresentSchema
|
|
15849
16080
|
]));
|
|
15850
16081
|
/**
|
|
15851
16082
|
* What starts a run.
|
|
@@ -15886,7 +16117,34 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
|
|
|
15886
16117
|
kind: literal("schedule"),
|
|
15887
16118
|
cron: string().min(1).max(120)
|
|
15888
16119
|
}),
|
|
15889
|
-
object({ kind: literal("manual") })
|
|
16120
|
+
object({ kind: literal("manual") }),
|
|
16121
|
+
object({
|
|
16122
|
+
kind: literal("detection"),
|
|
16123
|
+
/** Class names. A macro class (`animal`, `vehicle`, `person`, `package`)
|
|
16124
|
+
* matches every raw class that maps to it; any other name matches
|
|
16125
|
+
* exactly. */
|
|
16126
|
+
classes: array(string().min(1)).min(1),
|
|
16127
|
+
deviceIds: array(number().int().nonnegative()).min(1),
|
|
16128
|
+
/** The track must have visited at least one of these zones. */
|
|
16129
|
+
zones: array(string().min(1)).min(1).optional(),
|
|
16130
|
+
/** Best-so-far confidence the track must reach. */
|
|
16131
|
+
minScore: number().min(0).max(1).optional()
|
|
16132
|
+
}),
|
|
16133
|
+
object({
|
|
16134
|
+
kind: literal("stationary"),
|
|
16135
|
+
deviceIds: array(number().int().nonnegative()).min(1),
|
|
16136
|
+
/** Class names; a macro class matches like the detection trigger. */
|
|
16137
|
+
classes: array(string().min(1)).min(1).optional(),
|
|
16138
|
+
/** The parked object must be in at least one of these zones. */
|
|
16139
|
+
zones: array(string().min(1)).min(1).optional(),
|
|
16140
|
+
/** `appeared`: it became parked. `departed`: the registry retired it.
|
|
16141
|
+
* The vocabulary of `PipelineAnalyticsStationaryChanged`. */
|
|
16142
|
+
phase: _enum(["appeared", "departed"])
|
|
16143
|
+
}),
|
|
16144
|
+
object({
|
|
16145
|
+
kind: literal("interval"),
|
|
16146
|
+
minutes: number().int().min(1).max(1440)
|
|
16147
|
+
})
|
|
15890
16148
|
]);
|
|
15891
16149
|
/**
|
|
15892
16150
|
* One action step.
|
|
@@ -15906,28 +16164,47 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
|
|
|
15906
16164
|
* is called. The runner still receives a number and is untouched; the
|
|
15907
16165
|
* resolution is the recipe's job, not the runner's.
|
|
15908
16166
|
*/
|
|
16167
|
+
var AutomationWaitStepSchema = object({
|
|
16168
|
+
kind: literal("wait"),
|
|
16169
|
+
seconds: number().min(0).max(300)
|
|
16170
|
+
});
|
|
16171
|
+
var AutomationCapStepSchema = object({
|
|
16172
|
+
kind: literal("cap"),
|
|
16173
|
+
/**
|
|
16174
|
+
* One device, several same-family devices (locks / climate family / …),
|
|
16175
|
+
* or `{ $var }` for the device that triggered the run. An array is
|
|
16176
|
+
* fan-out at codegen — each id gets the same cap.method call.
|
|
16177
|
+
*/
|
|
16178
|
+
deviceId: union([
|
|
16179
|
+
number().int(),
|
|
16180
|
+
array(number().int()).min(1),
|
|
16181
|
+
object({ $var: string().min(1) })
|
|
16182
|
+
]),
|
|
16183
|
+
cap: string().min(1),
|
|
16184
|
+
method: string().min(1),
|
|
16185
|
+
/** Values may carry `{{vars.x}}` slots, or be exactly `{ $var: 'name' }`, which
|
|
16186
|
+
* SUBSTITUTE and do not evaluate (§3.2.3). Anything beyond substitution is
|
|
16187
|
+
* the expression leaf. */
|
|
16188
|
+
args: record(string(), unknown()).optional()
|
|
16189
|
+
});
|
|
16190
|
+
/**
|
|
16191
|
+
* The steps an internal Action may contain: cap and wait, and nothing that can
|
|
16192
|
+
* call back into an Action — so an Action can never reach itself (no cycles).
|
|
16193
|
+
*/
|
|
16194
|
+
var ActionStepSchema = discriminatedUnion("kind", [AutomationWaitStepSchema, AutomationCapStepSchema]);
|
|
15909
16195
|
var AutomationActionSchema = discriminatedUnion("kind", [
|
|
16196
|
+
AutomationWaitStepSchema,
|
|
16197
|
+
AutomationCapStepSchema,
|
|
15910
16198
|
object({
|
|
15911
|
-
kind: literal("
|
|
15912
|
-
|
|
15913
|
-
|
|
15914
|
-
|
|
15915
|
-
|
|
15916
|
-
|
|
15917
|
-
|
|
15918
|
-
|
|
15919
|
-
|
|
15920
|
-
*/
|
|
15921
|
-
deviceId: union([
|
|
15922
|
-
number().int(),
|
|
15923
|
-
array(number().int()).min(1),
|
|
15924
|
-
object({ $var: string().min(1) })
|
|
15925
|
-
]),
|
|
15926
|
-
cap: string().min(1),
|
|
15927
|
-
method: string().min(1),
|
|
15928
|
-
/** Values may carry `{{vars.x}}` slots, which SUBSTITUTE and do not
|
|
15929
|
-
* evaluate (§3.2.3). Anything beyond substitution is the expression leaf. */
|
|
15930
|
-
args: record(string(), unknown()).optional()
|
|
16199
|
+
kind: literal("action"),
|
|
16200
|
+
/** An entry of the `actions` surface (`internal:<uuid>` or `<addonId>:<localId>`). */
|
|
16201
|
+
actionId: string().min(1),
|
|
16202
|
+
/** Values for the action's declared variables; the others take their default. */
|
|
16203
|
+
variables: record(string(), union([
|
|
16204
|
+
string(),
|
|
16205
|
+
number(),
|
|
16206
|
+
boolean()
|
|
16207
|
+
])).optional()
|
|
15931
16208
|
}),
|
|
15932
16209
|
object({
|
|
15933
16210
|
kind: literal("code"),
|
|
@@ -15936,10 +16213,357 @@ var AutomationActionSchema = discriminatedUnion("kind", [
|
|
|
15936
16213
|
code: string().min(1).max(2e4)
|
|
15937
16214
|
})
|
|
15938
16215
|
]);
|
|
16216
|
+
/**
|
|
16217
|
+
* How a trigger that arrives while the automation is busy (or cooling down) is
|
|
16218
|
+
* handled. One of the automation's execution modes, never a property of its
|
|
16219
|
+
* actions. Absent means `single`.
|
|
16220
|
+
*
|
|
16221
|
+
* - `single` — drop the trigger while a run is in progress (traced + logged).
|
|
16222
|
+
* - `restart` — abort the run in progress (at its next step boundary), start fresh.
|
|
16223
|
+
* - `queued` — queue up to `max` triggers, drop the rest (traced).
|
|
16224
|
+
* - `parallel` — up to `max` runs at once, drop the rest (traced).
|
|
16225
|
+
* - `cooldown` — `single`, and no new run until `minutes` after the last run
|
|
16226
|
+
* STARTED; persisted across a restart. A manual trigger bypasses only this
|
|
16227
|
+
* wait, never the single/queued limits.
|
|
16228
|
+
*/
|
|
16229
|
+
var AutomationRunModeSchema = discriminatedUnion("mode", [
|
|
16230
|
+
object({ mode: literal("single") }),
|
|
16231
|
+
object({ mode: literal("restart") }),
|
|
16232
|
+
object({
|
|
16233
|
+
mode: literal("queued"),
|
|
16234
|
+
max: number().int().min(1).max(10)
|
|
16235
|
+
}),
|
|
16236
|
+
object({
|
|
16237
|
+
mode: literal("parallel"),
|
|
16238
|
+
max: number().int().min(1).max(10)
|
|
16239
|
+
}),
|
|
16240
|
+
object({
|
|
16241
|
+
mode: literal("cooldown"),
|
|
16242
|
+
minutes: number().int().min(1).max(1440)
|
|
16243
|
+
})
|
|
16244
|
+
]);
|
|
15939
16245
|
var AutomationRecipeSchema = object({
|
|
15940
16246
|
triggers: array(AutomationTriggerSchema),
|
|
15941
16247
|
conditions: AutomationConditionSchema.optional(),
|
|
15942
|
-
actions: array(AutomationActionSchema)
|
|
16248
|
+
actions: array(AutomationActionSchema),
|
|
16249
|
+
runMode: AutomationRunModeSchema.optional(),
|
|
16250
|
+
/** How many run traces the automation keeps (default {@link DEFAULT_TRACE_KEEP}). */
|
|
16251
|
+
keepTraces: number().int().min(1).max(100).optional()
|
|
16252
|
+
});
|
|
16253
|
+
new Set([
|
|
16254
|
+
{
|
|
16255
|
+
id: "person",
|
|
16256
|
+
name: "Person"
|
|
16257
|
+
},
|
|
16258
|
+
{
|
|
16259
|
+
id: "vehicle",
|
|
16260
|
+
name: "Vehicle"
|
|
16261
|
+
},
|
|
16262
|
+
{
|
|
16263
|
+
id: "animal",
|
|
16264
|
+
name: "Animal"
|
|
16265
|
+
},
|
|
16266
|
+
{
|
|
16267
|
+
id: "package",
|
|
16268
|
+
name: "Package"
|
|
16269
|
+
}
|
|
16270
|
+
].map((l) => l.id));
|
|
16271
|
+
var COCO_TO_MACRO = {
|
|
16272
|
+
mapping: {
|
|
16273
|
+
person: "person",
|
|
16274
|
+
bicycle: "vehicle",
|
|
16275
|
+
car: "vehicle",
|
|
16276
|
+
motorcycle: "vehicle",
|
|
16277
|
+
airplane: "vehicle",
|
|
16278
|
+
bus: "vehicle",
|
|
16279
|
+
train: "vehicle",
|
|
16280
|
+
truck: "vehicle",
|
|
16281
|
+
boat: "vehicle",
|
|
16282
|
+
bird: "animal",
|
|
16283
|
+
cat: "animal",
|
|
16284
|
+
dog: "animal",
|
|
16285
|
+
horse: "animal",
|
|
16286
|
+
sheep: "animal",
|
|
16287
|
+
cow: "animal",
|
|
16288
|
+
elephant: "animal",
|
|
16289
|
+
bear: "animal",
|
|
16290
|
+
zebra: "animal",
|
|
16291
|
+
giraffe: "animal",
|
|
16292
|
+
suitcase: "package",
|
|
16293
|
+
backpack: "package",
|
|
16294
|
+
handbag: "package"
|
|
16295
|
+
},
|
|
16296
|
+
preserveOriginal: false
|
|
16297
|
+
};
|
|
16298
|
+
new Set(Object.values(COCO_TO_MACRO.mapping));
|
|
16299
|
+
/**
|
|
16300
|
+
* Run traces — what an automation (or, later, an Action run) DID, node by node,
|
|
16301
|
+
* so the editor can paint every block green or red with its reason.
|
|
16302
|
+
*
|
|
16303
|
+
* A trace is data, not a log: it covers the WHOLE tree — the trigger stage,
|
|
16304
|
+
* every condition node (containers and leaves, including the ones a
|
|
16305
|
+
* short-circuit never reached), the run-mode verdict and every action step —
|
|
16306
|
+
* each addressed by a stable path (`conditions.0.1`, `actions.2`, or `steps.N`
|
|
16307
|
+
* for an Action run). Skipped
|
|
16308
|
+
* evaluations are traced too, and identical consecutive skips collapse into a
|
|
16309
|
+
* repeat count so a trigger that fails every 5 minutes cannot flush real runs.
|
|
16310
|
+
*
|
|
16311
|
+
* {@link AutomationStepTraceSchema} is deliberately free of automation
|
|
16312
|
+
* vocabulary so an Action run records the same step shape.
|
|
16313
|
+
*/
|
|
16314
|
+
/** A value a trace may show: JSON primitives and short lists, nothing deeper. */
|
|
16315
|
+
var TraceValueSchema = union([
|
|
16316
|
+
string(),
|
|
16317
|
+
number(),
|
|
16318
|
+
boolean(),
|
|
16319
|
+
_null(),
|
|
16320
|
+
array(union([string(), number()]))
|
|
16321
|
+
]);
|
|
16322
|
+
var AutomationRunOutcomeSchema = _enum([
|
|
16323
|
+
"ran",
|
|
16324
|
+
"skipped-trigger",
|
|
16325
|
+
"skipped-conditions",
|
|
16326
|
+
"skipped-cooldown",
|
|
16327
|
+
"skipped-overlap",
|
|
16328
|
+
"failed",
|
|
16329
|
+
"aborted"
|
|
16330
|
+
]);
|
|
16331
|
+
/** `not-evaluated` = a short-circuit never reached the node. */
|
|
16332
|
+
var TraceNodeOutcomeSchema = _enum([
|
|
16333
|
+
"pass",
|
|
16334
|
+
"fail",
|
|
16335
|
+
"not-evaluated"
|
|
16336
|
+
]);
|
|
16337
|
+
var AutomationTriggerTraceSchema = object({
|
|
16338
|
+
kind: string().min(1),
|
|
16339
|
+
outcome: _enum(["pass", "fail"]),
|
|
16340
|
+
/** Why an event was rejected ("class person not in [dog]") or what fired. */
|
|
16341
|
+
reason: string(),
|
|
16342
|
+
/** deviceId, className, trackId, score … — only what the trigger exposes. */
|
|
16343
|
+
vars: record(string(), TraceValueSchema)
|
|
16344
|
+
});
|
|
16345
|
+
var AutomationConditionTraceNodeSchema = object({
|
|
16346
|
+
/** `conditions` for the root, `conditions.<i>` for its i-th child, … */
|
|
16347
|
+
path: string().min(1),
|
|
16348
|
+
kind: _enum([
|
|
16349
|
+
"all",
|
|
16350
|
+
"any",
|
|
16351
|
+
"not",
|
|
16352
|
+
"condition",
|
|
16353
|
+
"expression",
|
|
16354
|
+
"stationary-present"
|
|
16355
|
+
]),
|
|
16356
|
+
outcome: TraceNodeOutcomeSchema,
|
|
16357
|
+
reason: string(),
|
|
16358
|
+
deviceId: number().int().optional(),
|
|
16359
|
+
cap: string().optional(),
|
|
16360
|
+
fieldPath: string().optional(),
|
|
16361
|
+
operator: string().optional(),
|
|
16362
|
+
expected: TraceValueSchema.optional(),
|
|
16363
|
+
/** What the mirror held; `null` when it held nothing (unknown). */
|
|
16364
|
+
observed: TraceValueSchema.optional()
|
|
16365
|
+
});
|
|
16366
|
+
var AutomationRunVerdictSchema = object({
|
|
16367
|
+
mode: AutomationRunModeSchema,
|
|
16368
|
+
decision: _enum([
|
|
16369
|
+
"run",
|
|
16370
|
+
"queued",
|
|
16371
|
+
"restart",
|
|
16372
|
+
"drop",
|
|
16373
|
+
"cooldown",
|
|
16374
|
+
"bypass"
|
|
16375
|
+
]),
|
|
16376
|
+
reason: string()
|
|
16377
|
+
});
|
|
16378
|
+
/** One executed (or not-run) step — shared by automations and Action runs. */
|
|
16379
|
+
var AutomationStepTraceSchema = object({
|
|
16380
|
+
/** `actions.<i>` */
|
|
16381
|
+
path: string().min(1),
|
|
16382
|
+
kind: string().min(1),
|
|
16383
|
+
cap: string().optional(),
|
|
16384
|
+
method: string().optional(),
|
|
16385
|
+
deviceId: number().int().optional(),
|
|
16386
|
+
startedAt: number().optional(),
|
|
16387
|
+
endedAt: number().optional(),
|
|
16388
|
+
outcome: _enum([
|
|
16389
|
+
"ok",
|
|
16390
|
+
"error",
|
|
16391
|
+
"not-run"
|
|
16392
|
+
]),
|
|
16393
|
+
message: string().optional()
|
|
16394
|
+
});
|
|
16395
|
+
var AutomationRunTraceSchema = object({
|
|
16396
|
+
runId: string().min(1),
|
|
16397
|
+
startedAt: number(),
|
|
16398
|
+
endedAt: number(),
|
|
16399
|
+
trigger: AutomationTriggerTraceSchema,
|
|
16400
|
+
conditions: array(AutomationConditionTraceNodeSchema),
|
|
16401
|
+
verdict: AutomationRunVerdictSchema.optional(),
|
|
16402
|
+
steps: array(AutomationStepTraceSchema),
|
|
16403
|
+
outcome: AutomationRunOutcomeSchema,
|
|
16404
|
+
/** How many identical consecutive skips this trace stands for (>= 1). */
|
|
16405
|
+
repeat: number().int().min(1),
|
|
16406
|
+
/** When the latest of those repeats happened. */
|
|
16407
|
+
lastAt: number()
|
|
16408
|
+
});
|
|
16409
|
+
/**
|
|
16410
|
+
* What the hub's `runs` ledger holds for one owner (`automation:<blockId>` or
|
|
16411
|
+
* `action:<actionId>`): the cooldown stamp and the trace ring.
|
|
16412
|
+
*/
|
|
16413
|
+
var AutomationStateDocSchema = object({
|
|
16414
|
+
version: literal(1),
|
|
16415
|
+
lastRunStartedAt: number().nullable(),
|
|
16416
|
+
traces: array(AutomationRunTraceSchema)
|
|
16417
|
+
});
|
|
16418
|
+
/**
|
|
16419
|
+
* Actions — named, runnable things an operator (or an automation) can start,
|
|
16420
|
+
* declared as LIST ENTRIES, never as devices.
|
|
16421
|
+
*
|
|
16422
|
+
* An entry is either **internal** (a short list of cap/wait steps kept in the
|
|
16423
|
+
* hub's `actions` record kind) or declared by an addon through the
|
|
16424
|
+
* `action-source` collection (a Home Assistant script, a scene …). The one
|
|
16425
|
+
* surface the UI and automations talk to is the `actions` cap; it keeps no
|
|
16426
|
+
* catalog of its own (D3) — every `list` asks the providers live.
|
|
16427
|
+
*
|
|
16428
|
+
* This file is the data half: the entry schema, the variables an entry may
|
|
16429
|
+
* declare, and the pure resolution of the values a run was given. It holds no
|
|
16430
|
+
* runtime.
|
|
16431
|
+
*/
|
|
16432
|
+
/** What a variable's value is. `select` takes one of `options`. */
|
|
16433
|
+
var ActionVariableTypeSchema = _enum([
|
|
16434
|
+
"string",
|
|
16435
|
+
"number",
|
|
16436
|
+
"boolean",
|
|
16437
|
+
"select"
|
|
16438
|
+
]);
|
|
16439
|
+
var ACTION_VARIABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
16440
|
+
var ActionVariableValueSchema = union([
|
|
16441
|
+
string(),
|
|
16442
|
+
number(),
|
|
16443
|
+
boolean()
|
|
16444
|
+
]);
|
|
16445
|
+
/**
|
|
16446
|
+
* One declared variable. Steps reference it as `{ $var: 'name' }` (the whole
|
|
16447
|
+
* value) or `{{vars.name}}` (inside a string) in their args.
|
|
16448
|
+
*/
|
|
16449
|
+
var ActionVariableSchema = object({
|
|
16450
|
+
name: string().regex(ACTION_VARIABLE_NAME_RE),
|
|
16451
|
+
label: string().min(1).max(120).optional(),
|
|
16452
|
+
type: ActionVariableTypeSchema,
|
|
16453
|
+
required: boolean().optional(),
|
|
16454
|
+
default: ActionVariableValueSchema.optional(),
|
|
16455
|
+
/** The allowed values of a `select`. */
|
|
16456
|
+
options: array(string().min(1)).min(1).optional()
|
|
16457
|
+
}).superRefine((v, ctx) => {
|
|
16458
|
+
if (v.type === "select" && v.options === void 0) ctx.addIssue({
|
|
16459
|
+
code: "custom",
|
|
16460
|
+
message: "a select variable needs options",
|
|
16461
|
+
path: ["options"]
|
|
16462
|
+
});
|
|
16463
|
+
if (v.default !== void 0 && variableValueError(v, v.default) !== null) ctx.addIssue({
|
|
16464
|
+
code: "custom",
|
|
16465
|
+
message: variableValueError(v, v.default) ?? "invalid default",
|
|
16466
|
+
path: ["default"]
|
|
16467
|
+
});
|
|
16468
|
+
});
|
|
16469
|
+
/** Why `value` is not a legal value for the variable, or null when it is. */
|
|
16470
|
+
function variableValueError(variable, value) {
|
|
16471
|
+
switch (variable.type) {
|
|
16472
|
+
case "string": return typeof value === "string" ? null : `variable "${variable.name}" must be a string`;
|
|
16473
|
+
case "number": return typeof value === "number" && Number.isFinite(value) ? null : `variable "${variable.name}" must be a number`;
|
|
16474
|
+
case "boolean": return typeof value === "boolean" ? null : `variable "${variable.name}" must be true or false`;
|
|
16475
|
+
case "select": return typeof value === "string" && (variable.options ?? []).includes(value) ? null : `variable "${variable.name}" must be one of ${(variable.options ?? []).join(", ")}`;
|
|
16476
|
+
}
|
|
16477
|
+
}
|
|
16478
|
+
var ActionSourceKindSchema = _enum(["internal", "addon"]);
|
|
16479
|
+
function isInternalActionId(actionId) {
|
|
16480
|
+
return actionId.startsWith("internal:") && actionId.length > 9;
|
|
16481
|
+
}
|
|
16482
|
+
/** The addon behind a `<addonId>:<localId>` id, or null for anything else (internal included). */
|
|
16483
|
+
function addonIdOfActionId(actionId) {
|
|
16484
|
+
if (isInternalActionId(actionId)) return null;
|
|
16485
|
+
const at = actionId.indexOf(":");
|
|
16486
|
+
return at > 0 && at < actionId.length - 1 ? actionId.slice(0, at) : null;
|
|
16487
|
+
}
|
|
16488
|
+
var ActionStatusSchema = object({
|
|
16489
|
+
isRunning: boolean(),
|
|
16490
|
+
/** Ms epoch the latest run STARTED; null when it never ran. */
|
|
16491
|
+
lastRunAt: number().nullable(),
|
|
16492
|
+
/** The latest run's failure; null on success or when it never ran. */
|
|
16493
|
+
lastError: string().nullable()
|
|
16494
|
+
});
|
|
16495
|
+
var ActionEntrySchema = object({
|
|
16496
|
+
/** `internal:<uuid>` or `<addonId>:<localId>`. */
|
|
16497
|
+
id: string().min(3),
|
|
16498
|
+
name: string().min(1).max(120),
|
|
16499
|
+
/** The addon that declared it; `core-blocks` for an internal one. */
|
|
16500
|
+
addonId: string().min(1),
|
|
16501
|
+
source: ActionSourceKindSchema,
|
|
16502
|
+
/** An addon-declared entry is edited where it was declared. */
|
|
16503
|
+
readOnly: boolean(),
|
|
16504
|
+
/** Internal only: cap and wait steps. No action-in-action, so no cycles. */
|
|
16505
|
+
steps: array(ActionStepSchema).min(1).max(50).optional(),
|
|
16506
|
+
variables: array(ActionVariableSchema).max(20).optional(),
|
|
16507
|
+
status: ActionStatusSchema
|
|
16508
|
+
}).superRefine((entry, ctx) => {
|
|
16509
|
+
if (entry.source === "internal") {
|
|
16510
|
+
if (!isInternalActionId(entry.id)) ctx.addIssue({
|
|
16511
|
+
code: "custom",
|
|
16512
|
+
message: "an internal action id is internal:<uuid>",
|
|
16513
|
+
path: ["id"]
|
|
16514
|
+
});
|
|
16515
|
+
return;
|
|
16516
|
+
}
|
|
16517
|
+
if (entry.steps !== void 0) ctx.addIssue({
|
|
16518
|
+
code: "custom",
|
|
16519
|
+
message: "only an internal action carries steps",
|
|
16520
|
+
path: ["steps"]
|
|
16521
|
+
});
|
|
16522
|
+
if (!entry.readOnly) ctx.addIssue({
|
|
16523
|
+
code: "custom",
|
|
16524
|
+
message: "an addon action is read-only here",
|
|
16525
|
+
path: ["readOnly"]
|
|
16526
|
+
});
|
|
16527
|
+
if (addonIdOfActionId(entry.id) === null) ctx.addIssue({
|
|
16528
|
+
code: "custom",
|
|
16529
|
+
message: "an addon action id is <addonId>:<localId>",
|
|
16530
|
+
path: ["id"]
|
|
16531
|
+
});
|
|
16532
|
+
});
|
|
16533
|
+
/** What an operator writes for an internal action. */
|
|
16534
|
+
var ActionInputSchema = object({
|
|
16535
|
+
name: string().min(1).max(120),
|
|
16536
|
+
steps: array(ActionStepSchema).min(1).max(50),
|
|
16537
|
+
variables: array(ActionVariableSchema).max(20).optional()
|
|
16538
|
+
});
|
|
16539
|
+
/** Who started a run. Absent = the operator pressed it. */
|
|
16540
|
+
var ActionRunOriginSchema = object({
|
|
16541
|
+
/** The automation block whose step started it. */
|
|
16542
|
+
blockId: string().min(1).optional() });
|
|
16543
|
+
/** `started` = the caller did not ask to wait. */
|
|
16544
|
+
var ActionRunOutcomeSchema = union([AutomationRunOutcomeSchema, literal("started")]);
|
|
16545
|
+
var ActionRunResultSchema = object({
|
|
16546
|
+
runId: string().min(1),
|
|
16547
|
+
outcome: ActionRunOutcomeSchema,
|
|
16548
|
+
/** Why it did not run, or what failed. */
|
|
16549
|
+
message: string().optional()
|
|
16550
|
+
});
|
|
16551
|
+
_enum([
|
|
16552
|
+
"not-found",
|
|
16553
|
+
"read-only",
|
|
16554
|
+
"invalid-variables",
|
|
16555
|
+
"invalid-steps",
|
|
16556
|
+
"not-linked",
|
|
16557
|
+
"busy"
|
|
16558
|
+
]);
|
|
16559
|
+
object({
|
|
16560
|
+
actionId: string(),
|
|
16561
|
+
runId: string(),
|
|
16562
|
+
isRunning: boolean(),
|
|
16563
|
+
/** Set when the run ended. */
|
|
16564
|
+
outcome: AutomationRunOutcomeSchema.optional(),
|
|
16565
|
+
lastRunAt: number().nullable(),
|
|
16566
|
+
lastError: string().nullable()
|
|
15943
16567
|
});
|
|
15944
16568
|
/**
|
|
15945
16569
|
* `addon-pages` — system-scoped singleton aggregator cap. Public-facing
|
|
@@ -17534,7 +18158,22 @@ var BrokerProviderInfoSchema = object({
|
|
|
17534
18158
|
*/
|
|
17535
18159
|
interactiveConnectPath: string().min(1).optional(),
|
|
17536
18160
|
/** Relative hub path to disconnect the linked account (same popup pattern). */
|
|
17537
|
-
interactiveDisconnectPath: string().min(1).optional()
|
|
18161
|
+
interactiveDisconnectPath: string().min(1).optional(),
|
|
18162
|
+
/**
|
|
18163
|
+
* The kind signs in through a server-held interactive session instead of
|
|
18164
|
+
* (or before) typed credentials. `basePath` is a hub-relative route set
|
|
18165
|
+
* served by the owning addon (`/addon/<id>/...`) speaking the sign-in
|
|
18166
|
+
* contract: `POST {basePath}/start` (body: the form's settings) answers
|
|
18167
|
+
* `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
|
|
18168
|
+
* `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
|
|
18169
|
+
* 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
|
|
18170
|
+
* (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
|
|
18171
|
+
* existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
|
|
18172
|
+
* (`{ sessionId }`) drops the session. The dialog that edits a broker
|
|
18173
|
+
* offers it as the default path; the fields the session fills are
|
|
18174
|
+
* marked `filledBySignIn` in the kind's settings schema.
|
|
18175
|
+
*/
|
|
18176
|
+
signIn: object({ basePath: string().min(1) }).optional()
|
|
17538
18177
|
}))
|
|
17539
18178
|
});
|
|
17540
18179
|
var ListInputSchema = object({
|
|
@@ -18611,7 +19250,7 @@ var CompositionEventPlanSchema = object({
|
|
|
18611
19250
|
stateField: string(),
|
|
18612
19251
|
fieldKey: string()
|
|
18613
19252
|
});
|
|
18614
|
-
/** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.
|
|
19253
|
+
/** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.temperature`. */
|
|
18615
19254
|
var CompositionProblemSchema = object({
|
|
18616
19255
|
code: _enum([
|
|
18617
19256
|
"reserved-variant",
|
|
@@ -18639,7 +19278,8 @@ var CompositionProblemSchema = object({
|
|
|
18639
19278
|
"native-capability-replace",
|
|
18640
19279
|
"mixed-quantity",
|
|
18641
19280
|
"helper-mismatch",
|
|
18642
|
-
"member-constraint"
|
|
19281
|
+
"member-constraint",
|
|
19282
|
+
"retention"
|
|
18643
19283
|
]),
|
|
18644
19284
|
path: string(),
|
|
18645
19285
|
message: string()
|
|
@@ -18873,13 +19513,17 @@ Object.freeze({
|
|
|
18873
19513
|
latchedAt: {
|
|
18874
19514
|
kind: "fixed",
|
|
18875
19515
|
result: ["number", "null"]
|
|
19516
|
+
},
|
|
19517
|
+
schmitt: {
|
|
19518
|
+
kind: "fixed",
|
|
19519
|
+
result: ["boolean", "null"]
|
|
18876
19520
|
}
|
|
18877
19521
|
});
|
|
18878
19522
|
/**
|
|
18879
19523
|
* One catalog row per base reading a group helper can build (D944 §3).
|
|
18880
19524
|
* Data, not a form default written into the block.
|
|
18881
19525
|
*/
|
|
18882
|
-
var NOW = {
|
|
19526
|
+
var NOW$2 = {
|
|
18883
19527
|
fieldPath: "lastFetchedAt",
|
|
18884
19528
|
expr: "now"
|
|
18885
19529
|
};
|
|
@@ -18893,6 +19537,8 @@ function numeric(input) {
|
|
|
18893
19537
|
family: "numeric",
|
|
18894
19538
|
deviceType: DeviceType.Sensor,
|
|
18895
19539
|
memberDeviceType: DeviceType.Sensor,
|
|
19540
|
+
memberDeviceTypes: [DeviceType.Sensor],
|
|
19541
|
+
activeWhen: void 0,
|
|
18896
19542
|
commandCaps: [],
|
|
18897
19543
|
baseUnit: baseUnitOf(input.quantity),
|
|
18898
19544
|
aggregates: NUMERIC_AGGREGATES,
|
|
@@ -18908,6 +19554,8 @@ function binary(input) {
|
|
|
18908
19554
|
family: "binary",
|
|
18909
19555
|
deviceType: DeviceType.Sensor,
|
|
18910
19556
|
memberDeviceType: DeviceType.Sensor,
|
|
19557
|
+
memberDeviceTypes: [DeviceType.Sensor],
|
|
19558
|
+
activeWhen: void 0,
|
|
18911
19559
|
commandCaps: [],
|
|
18912
19560
|
quantity: "none",
|
|
18913
19561
|
baseUnit: void 0,
|
|
@@ -18921,73 +19569,73 @@ var GROUP_TEMPLATES = [
|
|
|
18921
19569
|
summary: "Mean, median, min, max or sum of temperatures. Offline and unknown members are left out, never read as 0 °C.",
|
|
18922
19570
|
role: DeviceRole.TemperatureSensor,
|
|
18923
19571
|
outputCap: "temperature-sensor",
|
|
18924
|
-
outputField: "
|
|
19572
|
+
outputField: "temperature",
|
|
18925
19573
|
quantity: "temperature",
|
|
18926
19574
|
defaultAggregate: "mean",
|
|
18927
19575
|
empty: "null",
|
|
18928
|
-
companions: [NOW]
|
|
19576
|
+
companions: [NOW$2]
|
|
18929
19577
|
}),
|
|
18930
19578
|
numeric({
|
|
18931
19579
|
id: "humidity",
|
|
18932
19580
|
summary: "Humidity in percent. Offline and unknown members are left out, never read as 0 %.",
|
|
18933
19581
|
role: DeviceRole.HumiditySensor,
|
|
18934
19582
|
outputCap: "humidity-sensor",
|
|
18935
|
-
outputField: "
|
|
19583
|
+
outputField: "humidity",
|
|
18936
19584
|
quantity: "humidity",
|
|
18937
19585
|
defaultAggregate: "mean",
|
|
18938
19586
|
empty: "null",
|
|
18939
|
-
companions: [NOW]
|
|
19587
|
+
companions: [NOW$2]
|
|
18940
19588
|
}),
|
|
18941
19589
|
numeric({
|
|
18942
19590
|
id: "pressure",
|
|
18943
19591
|
summary: "Pressure in hectopascals. Offline and unknown members are left out, never read as 0 hPa.",
|
|
18944
19592
|
role: DeviceRole.PressureSensor,
|
|
18945
19593
|
outputCap: "pressure-sensor",
|
|
18946
|
-
outputField: "
|
|
19594
|
+
outputField: "pressure",
|
|
18947
19595
|
quantity: "pressure",
|
|
18948
19596
|
defaultAggregate: "mean",
|
|
18949
19597
|
empty: "null",
|
|
18950
|
-
companions: [NOW]
|
|
19598
|
+
companions: [NOW$2]
|
|
18951
19599
|
}),
|
|
18952
19600
|
numeric({
|
|
18953
19601
|
id: "illuminance",
|
|
18954
19602
|
summary: "Illuminance in lux. Offline and unknown members are left out, never read as 0 lx.",
|
|
18955
19603
|
role: DeviceRole.AmbientLightSensor,
|
|
18956
19604
|
outputCap: "ambient-light-sensor",
|
|
18957
|
-
outputField: "
|
|
19605
|
+
outputField: "illuminance",
|
|
18958
19606
|
quantity: "illuminance",
|
|
18959
19607
|
defaultAggregate: "mean",
|
|
18960
19608
|
empty: "null",
|
|
18961
|
-
companions: [NOW]
|
|
19609
|
+
companions: [NOW$2]
|
|
18962
19610
|
}),
|
|
18963
19611
|
numeric({
|
|
18964
19612
|
id: "power",
|
|
18965
19613
|
summary: "Power in watts, summed by default. Nobody reporting omits the key rather than writing 0 W.",
|
|
18966
19614
|
role: DeviceRole.PowerSensor,
|
|
18967
19615
|
outputCap: "power-meter",
|
|
18968
|
-
outputField: "
|
|
19616
|
+
outputField: "power",
|
|
18969
19617
|
quantity: "power",
|
|
18970
19618
|
defaultAggregate: "sum",
|
|
18971
19619
|
empty: "omit",
|
|
18972
|
-
companions: [NOW]
|
|
19620
|
+
companions: [NOW$2]
|
|
18973
19621
|
}),
|
|
18974
19622
|
numeric({
|
|
18975
19623
|
id: "energy",
|
|
18976
19624
|
summary: "Energy in kilowatt-hours, summed by default. Nobody reporting omits the key rather than writing 0 kWh.",
|
|
18977
19625
|
role: DeviceRole.EnergySensor,
|
|
18978
19626
|
outputCap: "power-meter",
|
|
18979
|
-
outputField: "
|
|
19627
|
+
outputField: "energy",
|
|
18980
19628
|
quantity: "energy",
|
|
18981
19629
|
defaultAggregate: "sum",
|
|
18982
19630
|
empty: "omit",
|
|
18983
|
-
companions: [NOW]
|
|
19631
|
+
companions: [NOW$2]
|
|
18984
19632
|
}),
|
|
18985
19633
|
numeric({
|
|
18986
19634
|
id: "battery",
|
|
18987
19635
|
summary: "Battery charge in percent. Offline and unknown members are left out, never read as 0 %.",
|
|
18988
19636
|
role: DeviceRole.BatterySensor,
|
|
18989
19637
|
outputCap: "battery",
|
|
18990
|
-
outputField: "
|
|
19638
|
+
outputField: "level",
|
|
18991
19639
|
quantity: "percentage",
|
|
18992
19640
|
defaultAggregate: "mean",
|
|
18993
19641
|
empty: "null",
|
|
@@ -19015,7 +19663,7 @@ var GROUP_TEMPLATES = [
|
|
|
19015
19663
|
quantity: "device",
|
|
19016
19664
|
defaultAggregate: "mean",
|
|
19017
19665
|
empty: "null",
|
|
19018
|
-
companions: [NOW]
|
|
19666
|
+
companions: [NOW$2]
|
|
19019
19667
|
}),
|
|
19020
19668
|
binary({
|
|
19021
19669
|
id: "motion",
|
|
@@ -19109,6 +19757,11 @@ var GROUP_TEMPLATES = [
|
|
|
19109
19757
|
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.",
|
|
19110
19758
|
deviceType: DeviceType.Switch,
|
|
19111
19759
|
outputCap: "switch",
|
|
19760
|
+
outputField: "on",
|
|
19761
|
+
member: {
|
|
19762
|
+
cap: "switch",
|
|
19763
|
+
fieldPath: "on"
|
|
19764
|
+
},
|
|
19112
19765
|
commandCaps: ["switch"]
|
|
19113
19766
|
}),
|
|
19114
19767
|
actuator({
|
|
@@ -19116,34 +19769,136 @@ var GROUP_TEMPLATES = [
|
|
|
19116
19769
|
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.",
|
|
19117
19770
|
deviceType: DeviceType.Light,
|
|
19118
19771
|
outputCap: "switch",
|
|
19772
|
+
outputField: "on",
|
|
19773
|
+
member: {
|
|
19774
|
+
cap: "switch",
|
|
19775
|
+
fieldPath: "on"
|
|
19776
|
+
},
|
|
19119
19777
|
commandCaps: [
|
|
19120
19778
|
"switch",
|
|
19121
19779
|
"brightness",
|
|
19122
19780
|
"color"
|
|
19123
19781
|
]
|
|
19782
|
+
}),
|
|
19783
|
+
actuator({
|
|
19784
|
+
id: "fan",
|
|
19785
|
+
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.",
|
|
19786
|
+
deviceType: DeviceType.Fan,
|
|
19787
|
+
outputCap: "switch",
|
|
19788
|
+
outputField: "on",
|
|
19789
|
+
member: {
|
|
19790
|
+
cap: "switch",
|
|
19791
|
+
fieldPath: "on"
|
|
19792
|
+
},
|
|
19793
|
+
commandCaps: ["switch", "fan-control"]
|
|
19794
|
+
}),
|
|
19795
|
+
actuator({
|
|
19796
|
+
id: "cover",
|
|
19797
|
+
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.",
|
|
19798
|
+
deviceType: DeviceType.Cover,
|
|
19799
|
+
outputCap: "group-active",
|
|
19800
|
+
outputField: "active",
|
|
19801
|
+
member: {
|
|
19802
|
+
cap: "cover",
|
|
19803
|
+
fieldPath: "state"
|
|
19804
|
+
},
|
|
19805
|
+
activeWhen: [
|
|
19806
|
+
"open",
|
|
19807
|
+
"opening",
|
|
19808
|
+
"stopped"
|
|
19809
|
+
],
|
|
19810
|
+
commandCaps: ["cover"]
|
|
19811
|
+
}),
|
|
19812
|
+
actuator({
|
|
19813
|
+
id: "valve",
|
|
19814
|
+
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.",
|
|
19815
|
+
deviceType: DeviceType.Valve,
|
|
19816
|
+
outputCap: "group-active",
|
|
19817
|
+
outputField: "active",
|
|
19818
|
+
member: {
|
|
19819
|
+
cap: "valve",
|
|
19820
|
+
fieldPath: "state"
|
|
19821
|
+
},
|
|
19822
|
+
activeWhen: [
|
|
19823
|
+
"open",
|
|
19824
|
+
"opening",
|
|
19825
|
+
"stopped"
|
|
19826
|
+
],
|
|
19827
|
+
commandCaps: ["valve"]
|
|
19828
|
+
}),
|
|
19829
|
+
actuator({
|
|
19830
|
+
id: "lock",
|
|
19831
|
+
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.",
|
|
19832
|
+
deviceType: DeviceType.Lock,
|
|
19833
|
+
outputCap: "group-active",
|
|
19834
|
+
outputField: "active",
|
|
19835
|
+
member: {
|
|
19836
|
+
cap: "lock-control",
|
|
19837
|
+
fieldPath: "state"
|
|
19838
|
+
},
|
|
19839
|
+
activeWhen: ["locked"],
|
|
19840
|
+
commandCaps: ["lock-control"]
|
|
19841
|
+
}),
|
|
19842
|
+
actuator({
|
|
19843
|
+
id: "media-player",
|
|
19844
|
+
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.",
|
|
19845
|
+
deviceType: DeviceType.MediaPlayer,
|
|
19846
|
+
outputCap: "group-active",
|
|
19847
|
+
outputField: "active",
|
|
19848
|
+
member: {
|
|
19849
|
+
cap: "media-player",
|
|
19850
|
+
fieldPath: "state"
|
|
19851
|
+
},
|
|
19852
|
+
activeWhen: ["playing", "buffering"],
|
|
19853
|
+
commandCaps: ["media-player"]
|
|
19854
|
+
}),
|
|
19855
|
+
actuator({
|
|
19856
|
+
id: "climate",
|
|
19857
|
+
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.",
|
|
19858
|
+
deviceType: DeviceType.Thermostat,
|
|
19859
|
+
memberDeviceTypes: [DeviceType.Thermostat, DeviceType.Climate],
|
|
19860
|
+
outputCap: "group-active",
|
|
19861
|
+
outputField: "active",
|
|
19862
|
+
member: {
|
|
19863
|
+
cap: "climate-control",
|
|
19864
|
+
fieldPath: "mode"
|
|
19865
|
+
},
|
|
19866
|
+
activeWhen: [
|
|
19867
|
+
"heat",
|
|
19868
|
+
"cool",
|
|
19869
|
+
"auto",
|
|
19870
|
+
"heat_cool",
|
|
19871
|
+
"fan_only",
|
|
19872
|
+
"dry"
|
|
19873
|
+
],
|
|
19874
|
+
commandCaps: ["climate-control"]
|
|
19124
19875
|
})
|
|
19125
19876
|
];
|
|
19126
19877
|
function actuator(input) {
|
|
19127
19878
|
return {
|
|
19128
|
-
|
|
19879
|
+
id: input.id,
|
|
19880
|
+
summary: input.summary,
|
|
19881
|
+
deviceType: input.deviceType,
|
|
19882
|
+
outputCap: input.outputCap,
|
|
19883
|
+
outputField: input.outputField,
|
|
19884
|
+
commandCaps: input.commandCaps,
|
|
19129
19885
|
family: "actuator",
|
|
19130
19886
|
role: void 0,
|
|
19131
19887
|
memberDeviceType: input.deviceType,
|
|
19132
|
-
|
|
19888
|
+
memberDeviceTypes: input.memberDeviceTypes ?? [input.deviceType],
|
|
19889
|
+
activeWhen: input.activeWhen,
|
|
19133
19890
|
quantity: "none",
|
|
19134
19891
|
baseUnit: void 0,
|
|
19135
19892
|
aggregates: BINARY_AGGREGATES,
|
|
19136
19893
|
defaultAggregate: "any",
|
|
19137
19894
|
empty: "offline",
|
|
19138
|
-
accept: [
|
|
19139
|
-
cap: "switch",
|
|
19140
|
-
fieldPath: "on"
|
|
19141
|
-
}],
|
|
19895
|
+
accept: [input.member],
|
|
19142
19896
|
companions: [CHANGED]
|
|
19143
19897
|
};
|
|
19144
19898
|
}
|
|
19145
19899
|
new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
|
|
19146
19900
|
DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
|
|
19901
|
+
DeviceType.Camera;
|
|
19147
19902
|
`${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
|
|
19148
19903
|
/**
|
|
19149
19904
|
* What a composition may BUILD on a device type (G2, D676): the caps a new
|
|
@@ -19458,7 +20213,18 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
|
|
|
19458
20213
|
kind: "mutation",
|
|
19459
20214
|
auth: "admin",
|
|
19460
20215
|
access: "view"
|
|
19461
|
-
})
|
|
20216
|
+
}), method(object({
|
|
20217
|
+
blockId: string().min(1),
|
|
20218
|
+
trace: AutomationRunTraceSchema.optional(),
|
|
20219
|
+
runStartedAt: number().optional(),
|
|
20220
|
+
keepTraces: number().int().min(1).max(100).optional()
|
|
20221
|
+
}), _void(), {
|
|
20222
|
+
kind: "mutation",
|
|
20223
|
+
auth: "admin"
|
|
20224
|
+
}), method(object({ blockId: string().min(1) }), AutomationStateDocSchema, { auth: "admin" }), method(object({ blockId: string().min(1) }), object({
|
|
20225
|
+
traces: array(AutomationRunTraceSchema),
|
|
20226
|
+
lastError: string().nullable()
|
|
20227
|
+
}), { auth: "admin" });
|
|
19462
20228
|
/**
|
|
19463
20229
|
* `custom-model-registry` — collection cap exposing operator-registered
|
|
19464
20230
|
* custom detection models. Each provider (today: `addon-model-studio`)
|
|
@@ -44503,7 +45269,7 @@ var NetworkAccessStatusSchema = object({
|
|
|
44503
45269
|
});
|
|
44504
45270
|
/**
|
|
44505
45271
|
* Optional, richer endpoint shape returned by providers that expose
|
|
44506
|
-
* MORE than one ingress concurrently (Tailscale
|
|
45272
|
+
* MORE than one ingress concurrently (Tailscale Funnel with mixed
|
|
44507
45273
|
* serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
|
|
44508
45274
|
* the originating provider config (mode + sourcePort) so the
|
|
44509
45275
|
* orchestrator UI can label rows distinctly. Providers that expose only
|
|
@@ -44896,51 +45662,6 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
|
|
|
44896
45662
|
kind: "mutation",
|
|
44897
45663
|
auth: "admin"
|
|
44898
45664
|
});
|
|
44899
|
-
new Set([
|
|
44900
|
-
{
|
|
44901
|
-
id: "person",
|
|
44902
|
-
name: "Person"
|
|
44903
|
-
},
|
|
44904
|
-
{
|
|
44905
|
-
id: "vehicle",
|
|
44906
|
-
name: "Vehicle"
|
|
44907
|
-
},
|
|
44908
|
-
{
|
|
44909
|
-
id: "animal",
|
|
44910
|
-
name: "Animal"
|
|
44911
|
-
},
|
|
44912
|
-
{
|
|
44913
|
-
id: "package",
|
|
44914
|
-
name: "Package"
|
|
44915
|
-
}
|
|
44916
|
-
].map((l) => l.id));
|
|
44917
|
-
var COCO_TO_MACRO = {
|
|
44918
|
-
mapping: {
|
|
44919
|
-
person: "person",
|
|
44920
|
-
bicycle: "vehicle",
|
|
44921
|
-
car: "vehicle",
|
|
44922
|
-
motorcycle: "vehicle",
|
|
44923
|
-
airplane: "vehicle",
|
|
44924
|
-
bus: "vehicle",
|
|
44925
|
-
train: "vehicle",
|
|
44926
|
-
truck: "vehicle",
|
|
44927
|
-
boat: "vehicle",
|
|
44928
|
-
bird: "animal",
|
|
44929
|
-
cat: "animal",
|
|
44930
|
-
dog: "animal",
|
|
44931
|
-
horse: "animal",
|
|
44932
|
-
sheep: "animal",
|
|
44933
|
-
cow: "animal",
|
|
44934
|
-
elephant: "animal",
|
|
44935
|
-
bear: "animal",
|
|
44936
|
-
zebra: "animal",
|
|
44937
|
-
giraffe: "animal",
|
|
44938
|
-
suitcase: "package",
|
|
44939
|
-
backpack: "package",
|
|
44940
|
-
handbag: "package"
|
|
44941
|
-
},
|
|
44942
|
-
preserveOriginal: false
|
|
44943
|
-
};
|
|
44944
45665
|
var AUDIO_MACRO_LABELS = [
|
|
44945
45666
|
{
|
|
44946
45667
|
id: "speech",
|
|
@@ -47190,7 +47911,20 @@ var NcRuleInputSchema = object({
|
|
|
47190
47911
|
* save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
|
|
47191
47912
|
* path does not run Zod defaults.
|
|
47192
47913
|
*/
|
|
47193
|
-
groupingSec: number().int().min(5).max(60).optional()
|
|
47914
|
+
groupingSec: number().int().min(5).max(60).optional(),
|
|
47915
|
+
/**
|
|
47916
|
+
* ASK ME TO CONFIRM DETECTIONS (#243): a DETECTION notification of this rule
|
|
47917
|
+
* carries "Correct" / "Wrong" buttons (and, where the channel has room, a
|
|
47918
|
+
* "Wrong, it is a <class>" choice). A tap marks the track for retraining, the
|
|
47919
|
+
* same mark the Library writes, and records whose notification it was and
|
|
47920
|
+
* when. A channel with no buttons gets the same two choices as links.
|
|
47921
|
+
*
|
|
47922
|
+
* `.optional()`, never `.default()` (a Zod default does not run on the addon
|
|
47923
|
+
* cap path): absent and `false` both mean off, which is what every rule
|
|
47924
|
+
* authored before this field meant. An editor turns it off by writing
|
|
47925
|
+
* `false`, so it needs no `clear` key.
|
|
47926
|
+
*/
|
|
47927
|
+
askVerdict: boolean().optional()
|
|
47194
47928
|
});
|
|
47195
47929
|
/**
|
|
47196
47930
|
* Partial patch for `updateRule` — any subset of the input fields, plus the
|
|
@@ -58016,9 +58750,9 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
|
|
|
58016
58750
|
*/
|
|
58017
58751
|
var AirQualitySensorStatusSchema = object({
|
|
58018
58752
|
/** Carbon dioxide concentration in ppm. */
|
|
58019
|
-
|
|
58753
|
+
co2: number().min(0).optional().meta(readingMeta("concentration-volume")),
|
|
58020
58754
|
/** Total volatile organic compounds in ppb. */
|
|
58021
|
-
|
|
58755
|
+
voc: number().min(0).optional().meta(readingMeta("concentration-ppb")),
|
|
58022
58756
|
/** Particulate matter ≤ 2.5 μm in µg/m³. */
|
|
58023
58757
|
pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
|
|
58024
58758
|
/** Particulate matter ≤ 10 μm in µg/m³. */
|
|
@@ -58051,12 +58785,12 @@ var airQualitySensorCapability = {
|
|
|
58051
58785
|
* History (D850): recorded series.
|
|
58052
58786
|
*/
|
|
58053
58787
|
history: { fields: {
|
|
58054
|
-
|
|
58788
|
+
co2: {
|
|
58055
58789
|
kind: "gauge",
|
|
58056
58790
|
unit: "ppm",
|
|
58057
58791
|
deadband: 5
|
|
58058
58792
|
},
|
|
58059
|
-
|
|
58793
|
+
voc: {
|
|
58060
58794
|
kind: "gauge",
|
|
58061
58795
|
unit: "ppb",
|
|
58062
58796
|
deadband: 5
|
|
@@ -58087,7 +58821,7 @@ var airQualitySensorCapability = {
|
|
|
58087
58821
|
*/
|
|
58088
58822
|
var AmbientLightSensorStatusSchema = object({
|
|
58089
58823
|
/** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
|
|
58090
|
-
|
|
58824
|
+
illuminance: number().min(0).nullable().meta(readingMeta("illuminance", {
|
|
58091
58825
|
precision: 0,
|
|
58092
58826
|
label: "Illuminance"
|
|
58093
58827
|
})),
|
|
@@ -58116,7 +58850,7 @@ var ambientLightSensorCapability = {
|
|
|
58116
58850
|
/**
|
|
58117
58851
|
* History (D850): recorded series. Wide range, so a RELATIVE deadband (10 %).
|
|
58118
58852
|
*/
|
|
58119
|
-
history: { fields: {
|
|
58853
|
+
history: { fields: { illuminance: {
|
|
58120
58854
|
kind: "gauge",
|
|
58121
58855
|
unit: "lx",
|
|
58122
58856
|
deadbandPct: 10,
|
|
@@ -58255,6 +58989,84 @@ var audioMetricsCapability = {
|
|
|
58255
58989
|
*/
|
|
58256
58990
|
history: "none"
|
|
58257
58991
|
};
|
|
58992
|
+
method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
|
|
58993
|
+
actionId: string().min(1),
|
|
58994
|
+
variables: record(string(), ActionVariableValueSchema).optional()
|
|
58995
|
+
}), _void(), {
|
|
58996
|
+
kind: "mutation",
|
|
58997
|
+
auth: "admin"
|
|
58998
|
+
}), method(object({ actionId: string().min(1) }), _void(), {
|
|
58999
|
+
kind: "mutation",
|
|
59000
|
+
auth: "admin"
|
|
59001
|
+
});
|
|
59002
|
+
/**
|
|
59003
|
+
* actions — the ONE surface for runnable actions, for the UI and for
|
|
59004
|
+
* automations. Provided by the core-blocks builtin on the hub.
|
|
59005
|
+
*
|
|
59006
|
+
* An action is a LIST ENTRY, not a device (D883): either internal (cap and wait
|
|
59007
|
+
* steps, kept in the hub's `actions` record kind) or declared by an addon over
|
|
59008
|
+
* `action-source`. `list` asks every `action-source` provider LIVE and overlays
|
|
59009
|
+
* the run status from the hub's `runs` ledger; there is no catalog here to
|
|
59010
|
+
* drift (D3).
|
|
59011
|
+
*
|
|
59012
|
+
* **Permissions follow the camera, as linked devices do.** An action is linked
|
|
59013
|
+
* to cameras by the operator (`setLinks`, D356: nothing links itself). A
|
|
59014
|
+
* non-admin may `get` / `run` an action only through a camera they may see that
|
|
59015
|
+
* links it: they name the camera (`deviceId`, a top-level field so the scope
|
|
59016
|
+
* gate sees it) and the provider refuses an action that camera does not link.
|
|
59017
|
+
* An admin, and an in-process caller (an automation block), may run any action.
|
|
59018
|
+
* Every refusal is typed (`[not-linked] …`) and logged.
|
|
59019
|
+
*
|
|
59020
|
+
* A second `run` of an action that is already running is REFUSED — traced and
|
|
59021
|
+
* logged — never queued.
|
|
59022
|
+
*/
|
|
59023
|
+
var ActionIdInput = object({ actionId: string().min(1) });
|
|
59024
|
+
var ActionLinksSchema = object({
|
|
59025
|
+
deviceId: number().int().nonnegative(),
|
|
59026
|
+
actionIds: array(string().min(1)).max(100)
|
|
59027
|
+
});
|
|
59028
|
+
method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
|
|
59029
|
+
actionId: string().min(1),
|
|
59030
|
+
deviceId: number().int().nonnegative().optional()
|
|
59031
|
+
}), object({ action: ActionEntrySchema.nullable() }), {
|
|
59032
|
+
role: "viewer",
|
|
59033
|
+
auth: "protected",
|
|
59034
|
+
kind: "query",
|
|
59035
|
+
caller: "required"
|
|
59036
|
+
}), method(object({
|
|
59037
|
+
actionId: string().min(1),
|
|
59038
|
+
variables: record(string(), ActionVariableValueSchema).optional(),
|
|
59039
|
+
wait: boolean().optional(),
|
|
59040
|
+
origin: ActionRunOriginSchema.optional(),
|
|
59041
|
+
deviceId: number().int().nonnegative().optional()
|
|
59042
|
+
}), ActionRunResultSchema, {
|
|
59043
|
+
role: "viewer",
|
|
59044
|
+
auth: "protected",
|
|
59045
|
+
kind: "mutation",
|
|
59046
|
+
caller: "required"
|
|
59047
|
+
}), method(ActionIdInput, _void(), {
|
|
59048
|
+
kind: "mutation",
|
|
59049
|
+
auth: "admin"
|
|
59050
|
+
}), method(ActionIdInput, object({ traces: array(AutomationRunTraceSchema) }), { auth: "admin" }), method(object({ action: ActionInputSchema }), object({ action: ActionEntrySchema }), {
|
|
59051
|
+
kind: "mutation",
|
|
59052
|
+
auth: "admin"
|
|
59053
|
+
}), method(object({
|
|
59054
|
+
actionId: string().min(1),
|
|
59055
|
+
action: ActionInputSchema.partial()
|
|
59056
|
+
}), object({ action: ActionEntrySchema }), {
|
|
59057
|
+
kind: "mutation",
|
|
59058
|
+
auth: "admin"
|
|
59059
|
+
}), method(ActionIdInput, _void(), {
|
|
59060
|
+
kind: "mutation",
|
|
59061
|
+
auth: "admin"
|
|
59062
|
+
}), method(object({ deviceId: number().int().nonnegative() }), array(ActionEntrySchema), {
|
|
59063
|
+
role: "viewer",
|
|
59064
|
+
auth: "protected",
|
|
59065
|
+
kind: "query"
|
|
59066
|
+
}), method(object({ deviceId: number().int().nonnegative() }), ActionLinksSchema, { auth: "admin" }), method(ActionLinksSchema, ActionLinksSchema, {
|
|
59067
|
+
kind: "mutation",
|
|
59068
|
+
auth: "admin"
|
|
59069
|
+
});
|
|
58258
59070
|
/**
|
|
58259
59071
|
* Automation-control cap. Models HA `automation.*` entities on
|
|
58260
59072
|
* `DeviceType.Automation`. An automation is a trigger+condition+
|
|
@@ -58351,7 +59163,7 @@ var BatteryStatusSchema = object({
|
|
|
58351
59163
|
* Consumers must SKIP a null rather than coerce it — `battery-band` already
|
|
58352
59164
|
* declines to band a non-finite reading, which is the correct shape.
|
|
58353
59165
|
*/
|
|
58354
|
-
|
|
59166
|
+
level: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
|
|
58355
59167
|
/**
|
|
58356
59168
|
* Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
|
|
58357
59169
|
* Reolink-specific for the Solar Panel 2 accessory (will become
|
|
@@ -58361,7 +59173,7 @@ var BatteryStatusSchema = object({
|
|
|
58361
59173
|
* `'unknown'` is the absence of an answer: the provider has not read the
|
|
58362
59174
|
* power fields yet, or the firmware reported neither. Before it existed,
|
|
58363
59175
|
* two unreadable fields produced `'none'`, and a camera nobody had reached
|
|
58364
|
-
* was drawn "on battery alone" — the same D315 defect `
|
|
59176
|
+
* was drawn "on battery alone" — the same D315 defect `level` closed
|
|
58365
59177
|
* by going nullable. Consumers SKIP it (no charger state published, no
|
|
58366
59178
|
* glyph, no HomeKit ChargingState) rather than coerce it either way.
|
|
58367
59179
|
*/
|
|
@@ -58385,7 +59197,7 @@ var BatteryStatusSchema = object({
|
|
|
58385
59197
|
* that only report charge. A second reading on this cap, not a second
|
|
58386
59198
|
* `numeric-sensor` on the same device.
|
|
58387
59199
|
*/
|
|
58388
|
-
|
|
59200
|
+
voltage: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
58389
59201
|
/**
|
|
58390
59202
|
* Ms epoch of the last time the device PROVED it was reachable — a
|
|
58391
59203
|
* completed firmware round-trip, an observed wake, or an inbound push
|
|
@@ -58419,7 +59231,7 @@ var BatteryStatusSchema = object({
|
|
|
58419
59231
|
/**
|
|
58420
59232
|
* True when the source is a BINARY low-battery indicator (HA
|
|
58421
59233
|
* `binary_sensor` device_class=battery / `LOW_BAT`) that has no real
|
|
58422
|
-
* charge level — `
|
|
59234
|
+
* charge level — `level` is then a coarse stand-in (100 = normal,
|
|
58423
59235
|
* sub-threshold = low). UI MUST render "Normal"/"Low" instead of a
|
|
58424
59236
|
* misleading exact percentage. Absent/false → genuine 0–100 % reading.
|
|
58425
59237
|
*/
|
|
@@ -58432,21 +59244,22 @@ var BATTERY_DEVICE_TYPES = [
|
|
|
58432
59244
|
DeviceType.Switch,
|
|
58433
59245
|
DeviceType.PetFeeder,
|
|
58434
59246
|
DeviceType.Container,
|
|
58435
|
-
DeviceType.Thermostat
|
|
59247
|
+
DeviceType.Thermostat,
|
|
59248
|
+
DeviceType.EventEmitter
|
|
58436
59249
|
];
|
|
58437
59250
|
/** Generic Customize target for the percentage reading (D936). */
|
|
58438
59251
|
var batteryCustomize = {
|
|
58439
59252
|
version: 1,
|
|
58440
59253
|
targets: [{
|
|
58441
|
-
id: "
|
|
59254
|
+
id: "level",
|
|
58442
59255
|
cardinality: "scalar",
|
|
58443
59256
|
vocabulary: {
|
|
58444
59257
|
cap: "battery",
|
|
58445
|
-
stateField: "
|
|
59258
|
+
stateField: "level"
|
|
58446
59259
|
},
|
|
58447
59260
|
deviceTypes: BATTERY_DEVICE_TYPES,
|
|
58448
59261
|
apply: "add",
|
|
58449
|
-
path: "
|
|
59262
|
+
path: "level",
|
|
58450
59263
|
sources: [{
|
|
58451
59264
|
kind: "field",
|
|
58452
59265
|
valueKinds: ["number", "string"],
|
|
@@ -58540,7 +59353,7 @@ onStatusChanged: { data: object({
|
|
|
58540
59353
|
schema: BatteryStatusSchema,
|
|
58541
59354
|
kind: "push",
|
|
58542
59355
|
empty: {
|
|
58543
|
-
|
|
59356
|
+
level: null,
|
|
58544
59357
|
charging: "unknown",
|
|
58545
59358
|
sleeping: false,
|
|
58546
59359
|
lastUpdated: 0
|
|
@@ -58567,11 +59380,11 @@ onStatusChanged: { data: object({
|
|
|
58567
59380
|
* History (D850): recorded series. The generic capture replaces the `battery` series of `pipeline-analytics:stats-5m`.
|
|
58568
59381
|
*/
|
|
58569
59382
|
history: { fields: {
|
|
58570
|
-
|
|
59383
|
+
level: {
|
|
58571
59384
|
kind: "gauge",
|
|
58572
59385
|
unit: "%"
|
|
58573
59386
|
},
|
|
58574
|
-
|
|
59387
|
+
voltage: {
|
|
58575
59388
|
kind: "gauge",
|
|
58576
59389
|
unit: "V"
|
|
58577
59390
|
}
|
|
@@ -58631,14 +59444,14 @@ var binaryCapability = {
|
|
|
58631
59444
|
* controls to this single-method surface.
|
|
58632
59445
|
*
|
|
58633
59446
|
* The cap is intentionally minimal: a single `setBrightness({deviceId,
|
|
58634
|
-
*
|
|
59447
|
+
* level})` mutation plus the auto-injected `getStatus`. Drivers
|
|
58635
59448
|
* that expose richer controls (color temperature, scenes, schedules)
|
|
58636
59449
|
* should surface those via the device's `getSettingsUISchema()`
|
|
58637
59450
|
* instead of bloating this cap.
|
|
58638
59451
|
*/
|
|
58639
59452
|
var BrightnessStatusSchema = object({
|
|
58640
59453
|
/** Current level as 0..100 inclusive. Firmware-reported. */
|
|
58641
|
-
|
|
59454
|
+
level: number().min(0).max(100).meta(readingMeta("percentage")),
|
|
58642
59455
|
/** Ms epoch of the last operator-driven change. Useful for UI freshness. */
|
|
58643
59456
|
lastChangedAt: number()
|
|
58644
59457
|
});
|
|
@@ -58650,7 +59463,7 @@ var brightnessCapability = {
|
|
|
58650
59463
|
deviceTypes: [DeviceType.Light],
|
|
58651
59464
|
methods: { setBrightness: method(object({
|
|
58652
59465
|
deviceId: number().int().nonnegative(),
|
|
58653
|
-
|
|
59466
|
+
level: number().min(0).max(100)
|
|
58654
59467
|
}), _void(), {
|
|
58655
59468
|
kind: "mutation",
|
|
58656
59469
|
auth: "protected"
|
|
@@ -58663,7 +59476,7 @@ var brightnessCapability = {
|
|
|
58663
59476
|
*/
|
|
58664
59477
|
onBrightnessChanged: { data: object({
|
|
58665
59478
|
deviceId: number(),
|
|
58666
|
-
|
|
59479
|
+
level: number().min(0).max(100),
|
|
58667
59480
|
lastChangedAt: number()
|
|
58668
59481
|
}) } },
|
|
58669
59482
|
status: {
|
|
@@ -58686,7 +59499,7 @@ onBrightnessChanged: { data: object({
|
|
|
58686
59499
|
/**
|
|
58687
59500
|
* History (D850): recorded series. Actuator tier: level keeps every change.
|
|
58688
59501
|
*/
|
|
58689
|
-
history: { fields: {
|
|
59502
|
+
history: { fields: { level: {
|
|
58690
59503
|
kind: "gauge",
|
|
58691
59504
|
unit: "%",
|
|
58692
59505
|
minIntervalMs: 0
|
|
@@ -59283,11 +60096,11 @@ var ColorInputSchema = discriminatedUnion("mode", [
|
|
|
59283
60096
|
}),
|
|
59284
60097
|
object({
|
|
59285
60098
|
mode: literal("mired"),
|
|
59286
|
-
|
|
60099
|
+
colorTemperature: number().int().min(50).max(1e3)
|
|
59287
60100
|
})
|
|
59288
60101
|
]);
|
|
59289
60102
|
var ColorStatusSchema = object({
|
|
59290
|
-
/** Active color mode — which of `rgb` / `hsv` / `
|
|
60103
|
+
/** Active color mode — which of `rgb` / `hsv` / `colorTemperature` reflects
|
|
59291
60104
|
* the bulb's current state. */
|
|
59292
60105
|
mode: _enum([
|
|
59293
60106
|
"rgb",
|
|
@@ -59299,7 +60112,7 @@ var ColorStatusSchema = object({
|
|
|
59299
60112
|
/** Populated when `mode === 'hsv'`. */
|
|
59300
60113
|
hsv: HsvTripletSchema.optional(),
|
|
59301
60114
|
/** Populated when `mode === 'mired'`. */
|
|
59302
|
-
|
|
60115
|
+
colorTemperature: number().int().optional().meta(readingMeta("colour-temperature")),
|
|
59303
60116
|
/** Ms epoch of the last operator-driven change. */
|
|
59304
60117
|
lastChangedAt: number()
|
|
59305
60118
|
});
|
|
@@ -59331,7 +60144,7 @@ onColorChanged: { data: object({
|
|
|
59331
60144
|
]),
|
|
59332
60145
|
rgb: RgbTripletSchema.optional(),
|
|
59333
60146
|
hsv: HsvTripletSchema.optional(),
|
|
59334
|
-
|
|
60147
|
+
colorTemperature: number().int().optional(),
|
|
59335
60148
|
lastChangedAt: number()
|
|
59336
60149
|
}) } },
|
|
59337
60150
|
status: {
|
|
@@ -60208,11 +61021,11 @@ var DishwasherStatusSchema = object({
|
|
|
60208
61021
|
/** Feature keys of the programs the appliance currently offers — drives the UI selector. */
|
|
60209
61022
|
availablePrograms: array(string()),
|
|
60210
61023
|
/** Seconds remaining on the active cycle, or null when not running / not known. */
|
|
60211
|
-
|
|
61024
|
+
remaining: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
60212
61025
|
/** Seconds the delayed start is set to (time until the cycle starts), or null. */
|
|
60213
|
-
|
|
61026
|
+
delayedStart: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
60214
61027
|
/** Cycle completion 0..100, or null when not applicable. */
|
|
60215
|
-
|
|
61028
|
+
progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
60216
61029
|
/** True when the door is reported open; null when the door state is not known. */
|
|
60217
61030
|
doorOpen: boolean().nullable(),
|
|
60218
61031
|
/** True when the door is latched (locked) during a cycle; null when not reported. */
|
|
@@ -60431,14 +61244,14 @@ var dishwasherCapability = {
|
|
|
60431
61244
|
runtimeState: DishwasherStatusSchema,
|
|
60432
61245
|
durability: "session",
|
|
60433
61246
|
history: { fields: {
|
|
60434
|
-
|
|
61247
|
+
progress: {
|
|
60435
61248
|
kind: "gauge",
|
|
60436
61249
|
unit: "%",
|
|
60437
61250
|
deadband: 2
|
|
60438
61251
|
},
|
|
60439
61252
|
state: { kind: "enum" },
|
|
60440
61253
|
operationState: { kind: "enum" },
|
|
60441
|
-
|
|
61254
|
+
remaining: {
|
|
60442
61255
|
kind: "gauge",
|
|
60443
61256
|
unit: "s",
|
|
60444
61257
|
minIntervalMs: 6e4
|
|
@@ -61029,7 +61842,7 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
|
|
|
61029
61842
|
var FanControlStatusSchema = object({
|
|
61030
61843
|
/** Active speed as 0..100 inclusive. Null when the device has no
|
|
61031
61844
|
* speed surface (single-speed fan). */
|
|
61032
|
-
|
|
61845
|
+
speed: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
61033
61846
|
/** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
|
|
61034
61847
|
* a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
|
|
61035
61848
|
percentageStep: number().positive().optional().meta(readingMeta("percentage")),
|
|
@@ -61056,7 +61869,7 @@ var fanControlCapability = {
|
|
|
61056
61869
|
methods: {
|
|
61057
61870
|
setPercentage: method(object({
|
|
61058
61871
|
deviceId: number().int().nonnegative(),
|
|
61059
|
-
|
|
61872
|
+
speed: number().min(0).max(100)
|
|
61060
61873
|
}), _void(), {
|
|
61061
61874
|
kind: "mutation",
|
|
61062
61875
|
auth: "protected"
|
|
@@ -61089,7 +61902,7 @@ var fanControlCapability = {
|
|
|
61089
61902
|
},
|
|
61090
61903
|
/**
|
|
61091
61904
|
* Runtime-state slice — mirrored by the kernel. UI fan speed
|
|
61092
|
-
* sliders read `
|
|
61905
|
+
* sliders read `speed` for live updates.
|
|
61093
61906
|
*/
|
|
61094
61907
|
runtimeState: FanControlStatusSchema,
|
|
61095
61908
|
/**
|
|
@@ -61103,7 +61916,7 @@ var fanControlCapability = {
|
|
|
61103
61916
|
* History (D850): recorded series. Actuator tier: speed keeps every change.
|
|
61104
61917
|
*/
|
|
61105
61918
|
history: { fields: {
|
|
61106
|
-
|
|
61919
|
+
speed: {
|
|
61107
61920
|
kind: "gauge",
|
|
61108
61921
|
unit: "%",
|
|
61109
61922
|
minIntervalMs: 0
|
|
@@ -61292,7 +62105,17 @@ var groupCommandsCapability = {
|
|
|
61292
62105
|
scope: "device",
|
|
61293
62106
|
deviceNative: true,
|
|
61294
62107
|
mode: "singleton",
|
|
61295
|
-
deviceTypes: [
|
|
62108
|
+
deviceTypes: [
|
|
62109
|
+
DeviceType.Switch,
|
|
62110
|
+
DeviceType.Light,
|
|
62111
|
+
DeviceType.Fan,
|
|
62112
|
+
DeviceType.Cover,
|
|
62113
|
+
DeviceType.Valve,
|
|
62114
|
+
DeviceType.Lock,
|
|
62115
|
+
DeviceType.MediaPlayer,
|
|
62116
|
+
DeviceType.Thermostat,
|
|
62117
|
+
DeviceType.Climate
|
|
62118
|
+
],
|
|
61296
62119
|
methods: {},
|
|
61297
62120
|
status: {
|
|
61298
62121
|
schema: GroupCommandsSchema,
|
|
@@ -61310,6 +62133,49 @@ var groupCommandsCapability = {
|
|
|
61310
62133
|
history: "none"
|
|
61311
62134
|
};
|
|
61312
62135
|
/**
|
|
62136
|
+
* Whether a helper actuator group counts as active (D944): at least one
|
|
62137
|
+
* included member is, or every included member is. What "active" means is the
|
|
62138
|
+
* type's: a cover is open, a lock is locked, a media player is playing, a
|
|
62139
|
+
* climate device is not off. A group of lights, switches and fans reports
|
|
62140
|
+
* `switch.on` instead; this cap is for the types whose own state is not a
|
|
62141
|
+
* yes/no. Unknown members are left out (`group-status` counts them) and an
|
|
62142
|
+
* empty included set takes the device offline, never `active: false`.
|
|
62143
|
+
*/
|
|
62144
|
+
var GroupActiveSchema = object({
|
|
62145
|
+
active: boolean().meta({ label: "Active" }),
|
|
62146
|
+
/** Ms epoch when the slice was last updated. */
|
|
62147
|
+
lastChangedAt: number()
|
|
62148
|
+
});
|
|
62149
|
+
var groupActiveCapability = {
|
|
62150
|
+
name: "group-active",
|
|
62151
|
+
scope: "device",
|
|
62152
|
+
deviceNative: true,
|
|
62153
|
+
mode: "singleton",
|
|
62154
|
+
deviceTypes: [
|
|
62155
|
+
DeviceType.Cover,
|
|
62156
|
+
DeviceType.Valve,
|
|
62157
|
+
DeviceType.Lock,
|
|
62158
|
+
DeviceType.MediaPlayer,
|
|
62159
|
+
DeviceType.Climate,
|
|
62160
|
+
DeviceType.Thermostat
|
|
62161
|
+
],
|
|
62162
|
+
methods: {},
|
|
62163
|
+
status: {
|
|
62164
|
+
schema: GroupActiveSchema,
|
|
62165
|
+
kind: "push"
|
|
62166
|
+
},
|
|
62167
|
+
runtimeState: GroupActiveSchema,
|
|
62168
|
+
/**
|
|
62169
|
+
* Runtime-state durability: **session** — recomputed from the members on the
|
|
62170
|
+
* next source event; a restored value would outlive the members it named.
|
|
62171
|
+
*
|
|
62172
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
62173
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
62174
|
+
*/
|
|
62175
|
+
durability: "session",
|
|
62176
|
+
history: "none"
|
|
62177
|
+
};
|
|
62178
|
+
/**
|
|
61313
62179
|
* How many members of a helper group answered (D944). Counts are integers,
|
|
61314
62180
|
* not measurements: zero included is a real count, never a stand-in reading.
|
|
61315
62181
|
* The reading itself stays on the output capability. History is none — the
|
|
@@ -61340,7 +62206,14 @@ var groupStatusCapability = {
|
|
|
61340
62206
|
deviceTypes: [
|
|
61341
62207
|
DeviceType.Sensor,
|
|
61342
62208
|
DeviceType.Switch,
|
|
61343
|
-
DeviceType.Light
|
|
62209
|
+
DeviceType.Light,
|
|
62210
|
+
DeviceType.Fan,
|
|
62211
|
+
DeviceType.Cover,
|
|
62212
|
+
DeviceType.Valve,
|
|
62213
|
+
DeviceType.Lock,
|
|
62214
|
+
DeviceType.MediaPlayer,
|
|
62215
|
+
DeviceType.Thermostat,
|
|
62216
|
+
DeviceType.Climate
|
|
61344
62217
|
],
|
|
61345
62218
|
methods: {},
|
|
61346
62219
|
status: {
|
|
@@ -61488,7 +62361,7 @@ var humidifierCapability = {
|
|
|
61488
62361
|
*/
|
|
61489
62362
|
var HumiditySensorStatusSchema = object({
|
|
61490
62363
|
/** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
|
|
61491
|
-
|
|
62364
|
+
humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
|
|
61492
62365
|
precision: 0,
|
|
61493
62366
|
label: "Humidity"
|
|
61494
62367
|
})),
|
|
@@ -61517,7 +62390,7 @@ var humiditySensorCapability = {
|
|
|
61517
62390
|
/**
|
|
61518
62391
|
* History (D850): recorded series.
|
|
61519
62392
|
*/
|
|
61520
|
-
history: { fields: {
|
|
62393
|
+
history: { fields: { humidity: {
|
|
61521
62394
|
kind: "gauge",
|
|
61522
62395
|
unit: "%",
|
|
61523
62396
|
deadband: .5
|
|
@@ -62157,7 +63030,7 @@ var LawnMowerControlStatusSchema = object({
|
|
|
62157
63030
|
batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
62158
63031
|
/** 0..100 mowing-completion percentage of the current task, or null when no
|
|
62159
63032
|
* task is active / progress is unavailable. */
|
|
62160
|
-
|
|
63033
|
+
progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
62161
63034
|
/** Current device/event code (dynamic — mostly status, sometimes an error),
|
|
62162
63035
|
* or null when unknown. */
|
|
62163
63036
|
currentCode: number().nullable().meta(readingMeta("none")),
|
|
@@ -62613,7 +63486,14 @@ var lockControlCapability = {
|
|
|
62613
63486
|
}),
|
|
62614
63487
|
unlock: method(object({
|
|
62615
63488
|
deviceId: number().int().nonnegative(),
|
|
62616
|
-
code: string().min(1).optional()
|
|
63489
|
+
code: string().min(1).optional(),
|
|
63490
|
+
/**
|
|
63491
|
+
* Honoured by a helper lock GROUP only (D944): unlocking several doors
|
|
63492
|
+
* with one call is refused unless the caller says it was confirmed. A
|
|
63493
|
+
* single lock ignores it. The refusal lives in the callee, so no
|
|
63494
|
+
* automation or API caller reaches the doors by skipping a dialog.
|
|
63495
|
+
*/
|
|
63496
|
+
confirm: boolean().optional()
|
|
62617
63497
|
}), _void(), {
|
|
62618
63498
|
kind: "mutation",
|
|
62619
63499
|
auth: "protected"
|
|
@@ -62650,6 +63530,44 @@ var lockControlCapability = {
|
|
|
62650
63530
|
volatileStateFields: ["lastChangedAt"]
|
|
62651
63531
|
};
|
|
62652
63532
|
/**
|
|
63533
|
+
* A monotonic total between resets (D944). Energy uses `power-meter.energy`
|
|
63534
|
+
* so dashboards keep one series; every other instantaneous reading lands here.
|
|
63535
|
+
* `value` is the time integral in the source quantity's accumulated base
|
|
63536
|
+
* (base-unit seconds, or kWh on the power meter). `null` is a gap, never zero.
|
|
63537
|
+
*/
|
|
63538
|
+
var MeterStatusSchema = object({
|
|
63539
|
+
value: number().nullable().meta(readingMeta("device", { label: "Total" })),
|
|
63540
|
+
lastFetchedAt: number()
|
|
63541
|
+
});
|
|
63542
|
+
var meterCapability = {
|
|
63543
|
+
name: "meter",
|
|
63544
|
+
scope: "device",
|
|
63545
|
+
deviceNative: true,
|
|
63546
|
+
mode: "singleton",
|
|
63547
|
+
deviceTypes: [DeviceType.Sensor],
|
|
63548
|
+
methods: {},
|
|
63549
|
+
status: {
|
|
63550
|
+
schema: MeterStatusSchema,
|
|
63551
|
+
kind: "push"
|
|
63552
|
+
},
|
|
63553
|
+
runtimeState: MeterStatusSchema,
|
|
63554
|
+
/**
|
|
63555
|
+
* Runtime-state durability: **restored** — the total is a cache of the
|
|
63556
|
+
* history integral. A restart shows it only until the history pass
|
|
63557
|
+
* recomputes the open period; it is not an in-memory buffer.
|
|
63558
|
+
*
|
|
63559
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
63560
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
63561
|
+
*/
|
|
63562
|
+
durability: "restored",
|
|
63563
|
+
history: { fields: { value: {
|
|
63564
|
+
kind: "counter",
|
|
63565
|
+
deadband: .001,
|
|
63566
|
+
minIntervalMs: 6e4
|
|
63567
|
+
} } },
|
|
63568
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
63569
|
+
};
|
|
63570
|
+
/**
|
|
62653
63571
|
* Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
|
|
62654
63572
|
* Apple TV, Spotify, Roku, generic OTT receivers, …) on
|
|
62655
63573
|
* `DeviceType.MediaPlayer`. Disjoint from the camera streaming
|
|
@@ -62706,10 +63624,10 @@ var MediaPlayerStatusSchema = object({
|
|
|
62706
63624
|
currentMedia: MediaInfoSchema.nullable(),
|
|
62707
63625
|
/** Current playback position in ms. Null when not seekable or
|
|
62708
63626
|
* nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
|
|
62709
|
-
|
|
63627
|
+
position: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
62710
63628
|
/** Total duration of the current media in ms. Null when unknown
|
|
62711
63629
|
* (live stream) or nothing is playing. */
|
|
62712
|
-
|
|
63630
|
+
duration: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
62713
63631
|
/** Shuffle toggle. Null when no shuffle surface.
|
|
62714
63632
|
* Pair with `DeviceFeature.MediaPlayerShuffle`. */
|
|
62715
63633
|
shuffle: boolean().nullable(),
|
|
@@ -62748,7 +63666,7 @@ var mediaPlayerCapability = {
|
|
|
62748
63666
|
}),
|
|
62749
63667
|
seek: method(object({
|
|
62750
63668
|
deviceId: number().int().nonnegative(),
|
|
62751
|
-
|
|
63669
|
+
position: number().int().nonnegative()
|
|
62752
63670
|
}), _void(), {
|
|
62753
63671
|
kind: "mutation",
|
|
62754
63672
|
auth: "protected"
|
|
@@ -62820,7 +63738,7 @@ var mediaPlayerCapability = {
|
|
|
62820
63738
|
/**
|
|
62821
63739
|
* History (D850): volume (a level an operator or an automation sets; the
|
|
62822
63740
|
* default 10 s floor folds a slider drag into its end value) and mute.
|
|
62823
|
-
* `
|
|
63741
|
+
* `position` is not recorded: it moves continuously while playing.
|
|
62824
63742
|
*/
|
|
62825
63743
|
history: { fields: {
|
|
62826
63744
|
volumeLevel: {
|
|
@@ -63782,9 +64700,9 @@ var NetworkLinkStatusSchema = object({
|
|
|
63782
64700
|
* one whose reading has not landed must not be drawn at 0 %. Consumers
|
|
63783
64701
|
* SKIP a null rather than coerce it.
|
|
63784
64702
|
*/
|
|
63785
|
-
|
|
64703
|
+
signal: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
63786
64704
|
/** Raw received signal strength in dBm, when the firmware reports one. */
|
|
63787
|
-
|
|
64705
|
+
rssi: number().optional().meta(readingMeta("signal-level")),
|
|
63788
64706
|
/** Network name of a wireless link, when the firmware reports it. */
|
|
63789
64707
|
ssid: string().optional(),
|
|
63790
64708
|
/** Ms epoch of the last observation. Lets consumers reason about freshness. */
|
|
@@ -63793,7 +64711,7 @@ var NetworkLinkStatusSchema = object({
|
|
|
63793
64711
|
/** The slice a provider seeds before its first read: nothing is known yet. */
|
|
63794
64712
|
var NETWORK_LINK_UNKNOWN = {
|
|
63795
64713
|
type: "unknown",
|
|
63796
|
-
|
|
64714
|
+
signal: null,
|
|
63797
64715
|
lastUpdated: 0
|
|
63798
64716
|
};
|
|
63799
64717
|
/**
|
|
@@ -63810,9 +64728,9 @@ function signalPercentFromBars(bars, maxBars) {
|
|
|
63810
64728
|
* below is 0 %, -50 dBm and above is 100 %, linear between. Non-finite or
|
|
63811
64729
|
* positive input is not an RSSI: `null`.
|
|
63812
64730
|
*/
|
|
63813
|
-
function signalPercentFromRssi(
|
|
63814
|
-
if (
|
|
63815
|
-
return Math.round((Math.min(-50, Math.max(-100,
|
|
64731
|
+
function signalPercentFromRssi(rssi) {
|
|
64732
|
+
if (rssi === void 0 || !Number.isFinite(rssi) || rssi > 0) return null;
|
|
64733
|
+
return Math.round((Math.min(-50, Math.max(-100, rssi)) + 100) * 2);
|
|
63816
64734
|
}
|
|
63817
64735
|
var networkLinkCapability = {
|
|
63818
64736
|
name: "network-link",
|
|
@@ -63863,13 +64781,13 @@ onStatusChanged: { data: object({
|
|
|
63863
64781
|
* History (D850): recorded series.
|
|
63864
64782
|
*/
|
|
63865
64783
|
history: { fields: {
|
|
63866
|
-
|
|
64784
|
+
signal: {
|
|
63867
64785
|
kind: "gauge",
|
|
63868
64786
|
unit: "%",
|
|
63869
64787
|
deadband: 3,
|
|
63870
64788
|
minIntervalMs: 6e4
|
|
63871
64789
|
},
|
|
63872
|
-
|
|
64790
|
+
rssi: {
|
|
63873
64791
|
kind: "gauge",
|
|
63874
64792
|
unit: "dBm",
|
|
63875
64793
|
deadband: 2,
|
|
@@ -64660,10 +65578,20 @@ var PetFeederDeviceStatusSchema = _enum([
|
|
|
64660
65578
|
]);
|
|
64661
65579
|
var gramsPortion = number().int().min(4).max(200);
|
|
64662
65580
|
var PetFeederStatusSchema = object({
|
|
64663
|
-
/** Food
|
|
64664
|
-
*
|
|
64665
|
-
* bowl
|
|
65581
|
+
/** Food in the TANK (hopper), as the feeder's own sensor reports it
|
|
65582
|
+
* (`state.food`). A coarse level code, not grams — `0` is empty. The
|
|
65583
|
+
* amount in the bowl is `bowlFood`. On dual-hopper models this is the
|
|
65584
|
+
* combined reading; per-hopper levels live in `food1`/`food2`. Null when
|
|
65585
|
+
* the device has not reported yet. */
|
|
64666
65586
|
foodLevel: number().nullable().meta(readingMeta("none")),
|
|
65587
|
+
/** Food in the BOWL as the feeder reports it (`state.bowl`), in the
|
|
65588
|
+
* feeder's own units. Null when the model reports none. */
|
|
65589
|
+
bowlFood: number().nullable().meta(readingMeta("none")),
|
|
65590
|
+
/** True while the pet is eating (`state.eating`). */
|
|
65591
|
+
eating: boolean(),
|
|
65592
|
+
/** Whether `markFoodReplenished` does anything on this model. When false
|
|
65593
|
+
* the client must not offer it. */
|
|
65594
|
+
supportsMarkRefilled: boolean(),
|
|
64667
65595
|
/** Hopper-1 food level (grams) on dual-hopper feeders; null on
|
|
64668
65596
|
* single-hopper models. */
|
|
64669
65597
|
food1: number().nullable().meta(readingMeta("none")),
|
|
@@ -64671,8 +65599,8 @@ var PetFeederStatusSchema = object({
|
|
|
64671
65599
|
* single-hopper models. */
|
|
64672
65600
|
food2: number().nullable().meta(readingMeta("none")),
|
|
64673
65601
|
/** Derived low-food flag — mirrors the HA `food_level` binary_sensor
|
|
64674
|
-
* (`device_class: problem`, on = low). True when the
|
|
64675
|
-
*
|
|
65602
|
+
* (`device_class: problem`, on = low). True when the tank reports empty,
|
|
65603
|
+
* or, on a dual-hopper model, when EITHER hopper is empty. */
|
|
64676
65604
|
lowFood: boolean(),
|
|
64677
65605
|
/** Battery charge 0..100 (%). Null on mains-powered models or when the
|
|
64678
65606
|
* device has no battery reading. */
|
|
@@ -64740,7 +65668,9 @@ var petFeederCapability = {
|
|
|
64740
65668
|
kind: "mutation",
|
|
64741
65669
|
auth: "protected"
|
|
64742
65670
|
}),
|
|
64743
|
-
/**
|
|
65671
|
+
/** Tell the PetKit cloud a hopper was refilled so it stops its low-food
|
|
65672
|
+
* reminders (D4H/D4S/D4SH only — see `supportsMarkRefilled`). It does not
|
|
65673
|
+
* change any sensor reading. */
|
|
64744
65674
|
markFoodReplenished: method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
64745
65675
|
kind: "mutation",
|
|
64746
65676
|
auth: "protected"
|
|
@@ -64813,6 +65743,7 @@ var petFeederCapability = {
|
|
|
64813
65743
|
*/
|
|
64814
65744
|
history: { fields: {
|
|
64815
65745
|
foodLevel: { kind: "gauge" },
|
|
65746
|
+
bowlFood: { kind: "gauge" },
|
|
64816
65747
|
food1: { kind: "gauge" },
|
|
64817
65748
|
food2: { kind: "gauge" },
|
|
64818
65749
|
batteryPower: {
|
|
@@ -65222,27 +66153,89 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
|
|
|
65222
66153
|
* in real-world devices.
|
|
65223
66154
|
*/
|
|
65224
66155
|
var PowerMeterStatusSchema = object({
|
|
65225
|
-
/** Instantaneous power draw
|
|
65226
|
-
|
|
66156
|
+
/** Instantaneous power draw (watts). */
|
|
66157
|
+
power: number().optional().meta(readingMeta("power", { label: "Power" })),
|
|
65227
66158
|
/** Cumulative energy in kilowatt-hours since the meter was reset. */
|
|
65228
|
-
|
|
65229
|
-
/** Voltage
|
|
65230
|
-
|
|
66159
|
+
energy: number().optional().meta(readingMeta("energy", { label: "Energy" })),
|
|
66160
|
+
/** Voltage (volts). */
|
|
66161
|
+
voltage: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
65231
66162
|
/** Current in amperes. */
|
|
65232
|
-
|
|
66163
|
+
current: number().optional().meta(readingMeta("current", { label: "Current" })),
|
|
65233
66164
|
/** Apparent power in volt-amperes. */
|
|
65234
|
-
|
|
66165
|
+
apparentPower: number().optional().meta(readingMeta("apparent-power")),
|
|
65235
66166
|
/** Power factor (0..1) if reported. */
|
|
65236
66167
|
powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
|
|
65237
66168
|
/** Ms epoch when the slice was last updated. */
|
|
65238
66169
|
lastFetchedAt: number()
|
|
65239
66170
|
});
|
|
66171
|
+
/**
|
|
66172
|
+
* Loads that draw metered mains power (D951). The reading is a feature of the
|
|
66173
|
+
* device itself — a plug, a lamp, a fan, an air conditioner, a boiler, a
|
|
66174
|
+
* vacuum dock — not a separate sensor beside it. A `Sensor` stays: a clamp
|
|
66175
|
+
* meter or a standalone energy monitor IS the device. Cover, lock, valve,
|
|
66176
|
+
* camera and the like are left out: their draw is not what anyone meters.
|
|
66177
|
+
*/
|
|
66178
|
+
var POWER_METER_DEVICE_TYPES = [
|
|
66179
|
+
DeviceType.Sensor,
|
|
66180
|
+
DeviceType.Switch,
|
|
66181
|
+
DeviceType.Light,
|
|
66182
|
+
DeviceType.Fan,
|
|
66183
|
+
DeviceType.Climate,
|
|
66184
|
+
DeviceType.Thermostat,
|
|
66185
|
+
DeviceType.Humidifier,
|
|
66186
|
+
DeviceType.WaterHeater,
|
|
66187
|
+
DeviceType.MediaPlayer,
|
|
66188
|
+
DeviceType.Washer,
|
|
66189
|
+
DeviceType.Dryer,
|
|
66190
|
+
DeviceType.Dishwasher,
|
|
66191
|
+
DeviceType.Vacuum,
|
|
66192
|
+
DeviceType.LawnMower,
|
|
66193
|
+
DeviceType.PetFeeder
|
|
66194
|
+
];
|
|
66195
|
+
/** Everything but a bare `Sensor`: a sensor that takes its reading from another sensor adds nothing. */
|
|
66196
|
+
var POWER_METER_CUSTOMIZE_DEVICE_TYPES = POWER_METER_DEVICE_TYPES.filter((type) => type !== DeviceType.Sensor);
|
|
66197
|
+
/** One scalar target per reading; each is sourced from a device of its own (D951). */
|
|
66198
|
+
function powerTarget(id, unit) {
|
|
66199
|
+
return {
|
|
66200
|
+
id,
|
|
66201
|
+
cardinality: "scalar",
|
|
66202
|
+
vocabulary: {
|
|
66203
|
+
cap: "power-meter",
|
|
66204
|
+
stateField: id
|
|
66205
|
+
},
|
|
66206
|
+
deviceTypes: POWER_METER_CUSTOMIZE_DEVICE_TYPES,
|
|
66207
|
+
apply: "add",
|
|
66208
|
+
path: id,
|
|
66209
|
+
sources: [{
|
|
66210
|
+
kind: "numeric",
|
|
66211
|
+
requiredUnit: unit,
|
|
66212
|
+
transforms: [{ kind: "identity" }]
|
|
66213
|
+
}],
|
|
66214
|
+
companions: [{
|
|
66215
|
+
kind: "clock",
|
|
66216
|
+
path: "lastFetchedAt",
|
|
66217
|
+
when: "always"
|
|
66218
|
+
}],
|
|
66219
|
+
sourceReport: { status: "assignment-status" },
|
|
66220
|
+
advancedExpression: true
|
|
66221
|
+
};
|
|
66222
|
+
}
|
|
66223
|
+
/** "Power and energy" extension: power, energy, voltage and current from other devices (D951). */
|
|
66224
|
+
var powerMeterCustomize = {
|
|
66225
|
+
version: 1,
|
|
66226
|
+
targets: [
|
|
66227
|
+
powerTarget("power", "W"),
|
|
66228
|
+
powerTarget("energy", "kWh"),
|
|
66229
|
+
powerTarget("voltage", "V"),
|
|
66230
|
+
powerTarget("current", "A")
|
|
66231
|
+
]
|
|
66232
|
+
};
|
|
65240
66233
|
var powerMeterCapability = {
|
|
65241
66234
|
name: "power-meter",
|
|
65242
66235
|
scope: "device",
|
|
65243
66236
|
deviceNative: true,
|
|
65244
66237
|
mode: "singleton",
|
|
65245
|
-
deviceTypes:
|
|
66238
|
+
deviceTypes: POWER_METER_DEVICE_TYPES,
|
|
65246
66239
|
methods: {},
|
|
65247
66240
|
status: {
|
|
65248
66241
|
schema: PowerMeterStatusSchema,
|
|
@@ -65250,41 +66243,41 @@ var powerMeterCapability = {
|
|
|
65250
66243
|
},
|
|
65251
66244
|
runtimeState: PowerMeterStatusSchema,
|
|
65252
66245
|
/**
|
|
65253
|
-
* Runtime-state durability: **restored** — as `numeric-sensor`; `
|
|
66246
|
+
* Runtime-state durability: **restored** — as `numeric-sensor`; `energy` is an accumulator whose restored value is the baseline.
|
|
65254
66247
|
*
|
|
65255
66248
|
* See `RuntimeStateDurability`. Enforced by
|
|
65256
66249
|
* `scripts/check-runtime-state-durability.ts`.
|
|
65257
66250
|
*/
|
|
65258
66251
|
durability: "restored",
|
|
65259
66252
|
/**
|
|
65260
|
-
* History (D850): recorded series. Gauges are floored at 30 s; `
|
|
66253
|
+
* History (D850): recorded series. Gauges are floored at 30 s; `energy` is the one monotonic meter (counter).
|
|
65261
66254
|
*/
|
|
65262
66255
|
history: { fields: {
|
|
65263
|
-
|
|
66256
|
+
power: {
|
|
65264
66257
|
kind: "gauge",
|
|
65265
66258
|
unit: "W",
|
|
65266
66259
|
deadband: 1,
|
|
65267
66260
|
minIntervalMs: 3e4
|
|
65268
66261
|
},
|
|
65269
|
-
|
|
66262
|
+
energy: {
|
|
65270
66263
|
kind: "counter",
|
|
65271
66264
|
unit: "kWh",
|
|
65272
66265
|
deadband: .01,
|
|
65273
66266
|
minIntervalMs: 6e4
|
|
65274
66267
|
},
|
|
65275
|
-
|
|
66268
|
+
voltage: {
|
|
65276
66269
|
kind: "gauge",
|
|
65277
66270
|
unit: "V",
|
|
65278
66271
|
deadband: 1,
|
|
65279
66272
|
minIntervalMs: 3e4
|
|
65280
66273
|
},
|
|
65281
|
-
|
|
66274
|
+
current: {
|
|
65282
66275
|
kind: "gauge",
|
|
65283
66276
|
unit: "A",
|
|
65284
66277
|
deadband: .05,
|
|
65285
66278
|
minIntervalMs: 3e4
|
|
65286
66279
|
},
|
|
65287
|
-
|
|
66280
|
+
apparentPower: {
|
|
65288
66281
|
kind: "gauge",
|
|
65289
66282
|
unit: "VA",
|
|
65290
66283
|
deadband: 1,
|
|
@@ -65299,7 +66292,8 @@ var powerMeterCapability = {
|
|
|
65299
66292
|
} },
|
|
65300
66293
|
/** Clock fields: written, but excluded from the compare that decides
|
|
65301
66294
|
* whether persisting is worth a SQLite commit. */
|
|
65302
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
66295
|
+
volatileStateFields: ["lastFetchedAt"],
|
|
66296
|
+
customize: powerMeterCustomize
|
|
65303
66297
|
};
|
|
65304
66298
|
/**
|
|
65305
66299
|
* Presence cap. Models HA `person.*` and `device_tracker.*` entities
|
|
@@ -65333,9 +66327,6 @@ var PresenceStatusSchema = object({
|
|
|
65333
66327
|
location: string().nullable(),
|
|
65334
66328
|
/** GPS coordinates when available. Null otherwise. */
|
|
65335
66329
|
gps: GpsLocationSchema.nullable(),
|
|
65336
|
-
/** Optional battery level of the tracking device (0..100). Null
|
|
65337
|
-
* when not reported. */
|
|
65338
|
-
batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
65339
66330
|
/** Ms epoch when the slice was last updated. */
|
|
65340
66331
|
lastChangedAt: number()
|
|
65341
66332
|
});
|
|
@@ -65364,14 +66355,7 @@ var presenceCapability = {
|
|
|
65364
66355
|
* `scripts/check-runtime-state-durability.ts`.
|
|
65365
66356
|
*/
|
|
65366
66357
|
durability: "restored",
|
|
65367
|
-
|
|
65368
|
-
* History (D850): recorded series.
|
|
65369
|
-
*/
|
|
65370
|
-
history: { fields: { batteryPercent: {
|
|
65371
|
-
kind: "gauge",
|
|
65372
|
-
unit: "%",
|
|
65373
|
-
deadband: 1
|
|
65374
|
-
} } },
|
|
66358
|
+
history: "none",
|
|
65375
66359
|
/** Clock fields: written, but excluded from the compare that decides
|
|
65376
66360
|
* whether persisting is worth a SQLite commit. */
|
|
65377
66361
|
volatileStateFields: ["lastChangedAt"]
|
|
@@ -65386,7 +66370,7 @@ var presenceCapability = {
|
|
|
65386
66370
|
*/
|
|
65387
66371
|
var PressureSensorStatusSchema = object({
|
|
65388
66372
|
/** Current pressure in hPa. `null` when the sample was refused (D393). */
|
|
65389
|
-
|
|
66373
|
+
pressure: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
|
|
65390
66374
|
/** Ms epoch when the slice was last updated. */
|
|
65391
66375
|
lastFetchedAt: number()
|
|
65392
66376
|
});
|
|
@@ -65412,7 +66396,7 @@ var pressureSensorCapability = {
|
|
|
65412
66396
|
/**
|
|
65413
66397
|
* History (D850): recorded series.
|
|
65414
66398
|
*/
|
|
65415
|
-
history: { fields: {
|
|
66399
|
+
history: { fields: { pressure: {
|
|
65416
66400
|
kind: "gauge",
|
|
65417
66401
|
unit: "hPa",
|
|
65418
66402
|
deadband: .1
|
|
@@ -66092,6 +67076,8 @@ var runningCustomize = {
|
|
|
66092
67076
|
stateField: "running"
|
|
66093
67077
|
},
|
|
66094
67078
|
deviceTypes: [
|
|
67079
|
+
DeviceType.Washer,
|
|
67080
|
+
DeviceType.Dryer,
|
|
66095
67081
|
DeviceType.Dishwasher,
|
|
66096
67082
|
DeviceType.Vacuum,
|
|
66097
67083
|
DeviceType.LawnMower,
|
|
@@ -68973,7 +69959,8 @@ var switchCapability = {
|
|
|
68973
69959
|
deviceTypes: [
|
|
68974
69960
|
DeviceType.Switch,
|
|
68975
69961
|
DeviceType.Siren,
|
|
68976
|
-
DeviceType.Light
|
|
69962
|
+
DeviceType.Light,
|
|
69963
|
+
DeviceType.Fan
|
|
68977
69964
|
],
|
|
68978
69965
|
methods: { setState: method(object({
|
|
68979
69966
|
deviceId: number(),
|
|
@@ -69612,7 +70599,7 @@ var tamperCapability = {
|
|
|
69612
70599
|
*/
|
|
69613
70600
|
var TemperatureSensorStatusSchema = object({
|
|
69614
70601
|
/** Current temperature in Celsius. `null` when the sample was refused (D393). */
|
|
69615
|
-
|
|
70602
|
+
temperature: number().nullable().meta(readingMeta("temperature", {
|
|
69616
70603
|
precision: 1,
|
|
69617
70604
|
label: "Temperature"
|
|
69618
70605
|
})),
|
|
@@ -69641,7 +70628,7 @@ var temperatureSensorCapability = {
|
|
|
69641
70628
|
/**
|
|
69642
70629
|
* History (D850): recorded series.
|
|
69643
70630
|
*/
|
|
69644
|
-
history: { fields: {
|
|
70631
|
+
history: { fields: { temperature: {
|
|
69645
70632
|
kind: "gauge",
|
|
69646
70633
|
unit: "°C",
|
|
69647
70634
|
deadband: .1
|
|
@@ -69938,7 +70925,7 @@ var VacuumControlStatusSchema = object({
|
|
|
69938
70925
|
/** Speed tokens the hardware accepts — drives the UI selector. */
|
|
69939
70926
|
availableFanSpeeds: array(string()),
|
|
69940
70927
|
/** 0..100 cleaning-completion percentage of the current task, or null. */
|
|
69941
|
-
|
|
70928
|
+
progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
69942
70929
|
/** Current error code (0 / null = no error). */
|
|
69943
70930
|
errorCode: number().nullable().meta(readingMeta("none")),
|
|
69944
70931
|
/** Human label for {@link errorCode}, or null when none / undecodable. */
|
|
@@ -71319,8 +72306,9 @@ function withClipEventLabels(provider, deps) {
|
|
|
71319
72306
|
listClips
|
|
71320
72307
|
};
|
|
71321
72308
|
}
|
|
72309
|
+
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
71322
72310
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
71323
|
-
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;
|
|
72311
|
+
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;
|
|
71324
72312
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
71325
72313
|
DeviceRole.BinarySensor, DeviceRole.MotionSensor;
|
|
71326
72314
|
new Set(CAP_NAMES);
|
|
@@ -71664,6 +72652,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
71664
72652
|
featureProbe: featureProbeCapability,
|
|
71665
72653
|
flood: floodCapability,
|
|
71666
72654
|
gas: gasCapability,
|
|
72655
|
+
groupActive: groupActiveCapability,
|
|
71667
72656
|
groupCommands: groupCommandsCapability,
|
|
71668
72657
|
groupStatus: groupStatusCapability,
|
|
71669
72658
|
heatingValve: heatingValveCapability,
|
|
@@ -71675,6 +72664,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
71675
72664
|
lawnMowerControl: lawnMowerControlCapability,
|
|
71676
72665
|
lockControl: lockControlCapability,
|
|
71677
72666
|
mediaPlayer: mediaPlayerCapability,
|
|
72667
|
+
meter: meterCapability,
|
|
71678
72668
|
motion: motionCapability,
|
|
71679
72669
|
motionTrigger: motionTriggerCapability,
|
|
71680
72670
|
motionZones: motionZonesCapability,
|
|
@@ -71737,7 +72727,7 @@ var BaseDevice = class {
|
|
|
71737
72727
|
* `device-local-state.ts`. Drivers access via:
|
|
71738
72728
|
*
|
|
71739
72729
|
* `this.state.battery.sleeping = true` // patches the battery slice
|
|
71740
|
-
* `const pct = this.state.battery.
|
|
72730
|
+
* `const pct = this.state.battery.level` // reads the battery slice
|
|
71741
72731
|
* `this.state.deviceStatus.online = true` // mirrors `markOnline(true)`
|
|
71742
72732
|
*
|
|
71743
72733
|
* Adding a new cap with `runtimeState:` automatically extends this
|
|
@@ -71777,7 +72767,7 @@ var BaseDevice = class {
|
|
|
71777
72767
|
* shape; drivers don't redeclare it, they just write through.
|
|
71778
72768
|
*
|
|
71779
72769
|
* Read: `this.runtimeState.getCapState('battery')` →
|
|
71780
|
-
* `{
|
|
72770
|
+
* `{level, charging, sleeping, lastUpdated}` for any
|
|
71781
72771
|
* provider that registers `batteryCapability`.
|
|
71782
72772
|
* Write: `this.runtimeState.setCapState('battery', { … })`.
|
|
71783
72773
|
*
|
|
@@ -72934,7 +73924,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
72934
73924
|
return toDeviceSummary(device, this.addonId);
|
|
72935
73925
|
}
|
|
72936
73926
|
};
|
|
72937
|
-
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;
|
|
73927
|
+
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;
|
|
72938
73928
|
new Set(Object.values(DeviceType));
|
|
72939
73929
|
Object.freeze({
|
|
72940
73930
|
"accessories.getStatus": {
|
|
@@ -72951,6 +73941,104 @@ Object.freeze({
|
|
|
72951
73941
|
access: "create",
|
|
72952
73942
|
role: "admin"
|
|
72953
73943
|
},
|
|
73944
|
+
"actions.create": {
|
|
73945
|
+
capName: "actions",
|
|
73946
|
+
capScope: "system",
|
|
73947
|
+
addonId: null,
|
|
73948
|
+
access: "create",
|
|
73949
|
+
role: "admin"
|
|
73950
|
+
},
|
|
73951
|
+
"actions.delete": {
|
|
73952
|
+
capName: "actions",
|
|
73953
|
+
capScope: "system",
|
|
73954
|
+
addonId: null,
|
|
73955
|
+
access: "delete",
|
|
73956
|
+
role: "admin"
|
|
73957
|
+
},
|
|
73958
|
+
"actions.get": {
|
|
73959
|
+
capName: "actions",
|
|
73960
|
+
capScope: "system",
|
|
73961
|
+
addonId: null,
|
|
73962
|
+
access: "view",
|
|
73963
|
+
role: "viewer"
|
|
73964
|
+
},
|
|
73965
|
+
"actions.getLinks": {
|
|
73966
|
+
capName: "actions",
|
|
73967
|
+
capScope: "system",
|
|
73968
|
+
addonId: null,
|
|
73969
|
+
access: "view",
|
|
73970
|
+
role: "admin"
|
|
73971
|
+
},
|
|
73972
|
+
"actions.getTraces": {
|
|
73973
|
+
capName: "actions",
|
|
73974
|
+
capScope: "system",
|
|
73975
|
+
addonId: null,
|
|
73976
|
+
access: "view",
|
|
73977
|
+
role: "admin"
|
|
73978
|
+
},
|
|
73979
|
+
"actions.list": {
|
|
73980
|
+
capName: "actions",
|
|
73981
|
+
capScope: "system",
|
|
73982
|
+
addonId: null,
|
|
73983
|
+
access: "view",
|
|
73984
|
+
role: "admin"
|
|
73985
|
+
},
|
|
73986
|
+
"actions.listForCamera": {
|
|
73987
|
+
capName: "actions",
|
|
73988
|
+
capScope: "system",
|
|
73989
|
+
addonId: null,
|
|
73990
|
+
access: "view",
|
|
73991
|
+
role: "viewer"
|
|
73992
|
+
},
|
|
73993
|
+
"actions.run": {
|
|
73994
|
+
capName: "actions",
|
|
73995
|
+
capScope: "system",
|
|
73996
|
+
addonId: null,
|
|
73997
|
+
access: "create",
|
|
73998
|
+
role: "viewer"
|
|
73999
|
+
},
|
|
74000
|
+
"actions.setLinks": {
|
|
74001
|
+
capName: "actions",
|
|
74002
|
+
capScope: "system",
|
|
74003
|
+
addonId: null,
|
|
74004
|
+
access: "create",
|
|
74005
|
+
role: "admin"
|
|
74006
|
+
},
|
|
74007
|
+
"actions.stop": {
|
|
74008
|
+
capName: "actions",
|
|
74009
|
+
capScope: "system",
|
|
74010
|
+
addonId: null,
|
|
74011
|
+
access: "create",
|
|
74012
|
+
role: "admin"
|
|
74013
|
+
},
|
|
74014
|
+
"actions.update": {
|
|
74015
|
+
capName: "actions",
|
|
74016
|
+
capScope: "system",
|
|
74017
|
+
addonId: null,
|
|
74018
|
+
access: "create",
|
|
74019
|
+
role: "admin"
|
|
74020
|
+
},
|
|
74021
|
+
"actionSource.listActions": {
|
|
74022
|
+
capName: "action-source",
|
|
74023
|
+
capScope: "system",
|
|
74024
|
+
addonId: null,
|
|
74025
|
+
access: "view",
|
|
74026
|
+
role: "admin"
|
|
74027
|
+
},
|
|
74028
|
+
"actionSource.runAction": {
|
|
74029
|
+
capName: "action-source",
|
|
74030
|
+
capScope: "system",
|
|
74031
|
+
addonId: null,
|
|
74032
|
+
access: "create",
|
|
74033
|
+
role: "admin"
|
|
74034
|
+
},
|
|
74035
|
+
"actionSource.stopAction": {
|
|
74036
|
+
capName: "action-source",
|
|
74037
|
+
capScope: "system",
|
|
74038
|
+
addonId: null,
|
|
74039
|
+
access: "create",
|
|
74040
|
+
role: "admin"
|
|
74041
|
+
},
|
|
72954
74042
|
"addonPages.listPages": {
|
|
72955
74043
|
capName: "addon-pages",
|
|
72956
74044
|
capScope: "system",
|
|
@@ -74232,6 +75320,13 @@ Object.freeze({
|
|
|
74232
75320
|
access: "view",
|
|
74233
75321
|
role: "admin"
|
|
74234
75322
|
},
|
|
75323
|
+
"coreBlocks.getAutomationRunState": {
|
|
75324
|
+
capName: "core-blocks",
|
|
75325
|
+
capScope: "system",
|
|
75326
|
+
addonId: null,
|
|
75327
|
+
access: "view",
|
|
75328
|
+
role: "admin"
|
|
75329
|
+
},
|
|
74235
75330
|
"coreBlocks.getBlockSettings": {
|
|
74236
75331
|
capName: "core-blocks",
|
|
74237
75332
|
capScope: "system",
|
|
@@ -74253,6 +75348,13 @@ Object.freeze({
|
|
|
74253
75348
|
access: "view",
|
|
74254
75349
|
role: "admin"
|
|
74255
75350
|
},
|
|
75351
|
+
"coreBlocks.getTraces": {
|
|
75352
|
+
capName: "core-blocks",
|
|
75353
|
+
capScope: "system",
|
|
75354
|
+
addonId: null,
|
|
75355
|
+
access: "view",
|
|
75356
|
+
role: "admin"
|
|
75357
|
+
},
|
|
74256
75358
|
"coreBlocks.getTypeDefs": {
|
|
74257
75359
|
capName: "core-blocks",
|
|
74258
75360
|
capScope: "system",
|
|
@@ -74288,6 +75390,13 @@ Object.freeze({
|
|
|
74288
75390
|
access: "create",
|
|
74289
75391
|
role: "admin"
|
|
74290
75392
|
},
|
|
75393
|
+
"coreBlocks.reportAutomationRun": {
|
|
75394
|
+
capName: "core-blocks",
|
|
75395
|
+
capScope: "system",
|
|
75396
|
+
addonId: null,
|
|
75397
|
+
access: "create",
|
|
75398
|
+
role: "admin"
|
|
75399
|
+
},
|
|
74291
75400
|
"coreBlocks.restart": {
|
|
74292
75401
|
capName: "core-blocks",
|
|
74293
75402
|
capScope: "system",
|
|
@@ -76157,6 +77266,13 @@ Object.freeze({
|
|
|
76157
77266
|
access: "view",
|
|
76158
77267
|
role: "viewer"
|
|
76159
77268
|
},
|
|
77269
|
+
"groupActive.getStatus": {
|
|
77270
|
+
capName: "group-active",
|
|
77271
|
+
capScope: "device",
|
|
77272
|
+
addonId: null,
|
|
77273
|
+
access: "view",
|
|
77274
|
+
role: "viewer"
|
|
77275
|
+
},
|
|
76160
77276
|
"groupCommands.getStatus": {
|
|
76161
77277
|
capName: "group-commands",
|
|
76162
77278
|
capScope: "device",
|
|
@@ -77123,6 +78239,13 @@ Object.freeze({
|
|
|
77123
78239
|
access: "create",
|
|
77124
78240
|
role: "admin"
|
|
77125
78241
|
},
|
|
78242
|
+
"meter.getStatus": {
|
|
78243
|
+
capName: "meter",
|
|
78244
|
+
capScope: "device",
|
|
78245
|
+
addonId: null,
|
|
78246
|
+
access: "view",
|
|
78247
|
+
role: "viewer"
|
|
78248
|
+
},
|
|
77126
78249
|
"metricsProvider.collectSnapshot": {
|
|
77127
78250
|
capName: "metrics-provider",
|
|
77128
78251
|
capScope: "system",
|
|
@@ -82847,7 +83970,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
82847
83970
|
label: t("Full brightness", "Luminosità massima"),
|
|
82848
83971
|
phrase: t("Set {device} to full brightness", "Porta {device} alla massima luminosità"),
|
|
82849
83972
|
icon: "sun",
|
|
82850
|
-
args: {
|
|
83973
|
+
args: { level: 100 }
|
|
82851
83974
|
},
|
|
82852
83975
|
{
|
|
82853
83976
|
id: "brightness.half",
|
|
@@ -82855,7 +83978,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
82855
83978
|
label: t("Half brightness", "Luminosità a metà"),
|
|
82856
83979
|
phrase: t("Dim {device} to half", "Abbassa {device} a metà luminosità"),
|
|
82857
83980
|
icon: "sun-dim",
|
|
82858
|
-
args: {
|
|
83981
|
+
args: { level: 50 }
|
|
82859
83982
|
},
|
|
82860
83983
|
{
|
|
82861
83984
|
id: "brightness.off",
|
|
@@ -82863,16 +83986,16 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
82863
83986
|
label: t("Brightness off", "Luminosità a zero"),
|
|
82864
83987
|
phrase: t("Dim {device} to zero", "Abbassa {device} a zero"),
|
|
82865
83988
|
icon: "lightbulb-off",
|
|
82866
|
-
args: {
|
|
83989
|
+
args: { level: 0 }
|
|
82867
83990
|
},
|
|
82868
83991
|
{
|
|
82869
83992
|
id: "brightness.set",
|
|
82870
83993
|
method: "setBrightness",
|
|
82871
83994
|
label: t("Set brightness", "Imposta la luminosità"),
|
|
82872
|
-
phrase: t("Set the brightness of {device} to {
|
|
83995
|
+
phrase: t("Set the brightness of {device} to {level}", "Imposta la luminosità di {device} a {level}"),
|
|
82873
83996
|
icon: "sun-medium",
|
|
82874
83997
|
openArgs: [{
|
|
82875
|
-
key: "
|
|
83998
|
+
key: "level",
|
|
82876
83999
|
label: PERCENT,
|
|
82877
84000
|
offer: {
|
|
82878
84001
|
kind: "number",
|
|
@@ -82886,13 +84009,13 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
82886
84009
|
],
|
|
82887
84010
|
state: [{
|
|
82888
84011
|
kind: "numeric",
|
|
82889
|
-
field: "
|
|
84012
|
+
field: "level",
|
|
82890
84013
|
quantity: t("the brightness", "la luminosità"),
|
|
82891
84014
|
label: t("Brightness", "Luminosità"),
|
|
82892
84015
|
unit: "%",
|
|
82893
84016
|
icon: "sun"
|
|
82894
84017
|
}],
|
|
82895
|
-
events: { onBrightnessChanged: { notOperatorFacing: "the `
|
|
84018
|
+
events: { onBrightnessChanged: { notOperatorFacing: "the `level` state trigger reads the same change" } }
|
|
82896
84019
|
},
|
|
82897
84020
|
color: {
|
|
82898
84021
|
scope: "device",
|
|
@@ -82906,7 +84029,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
82906
84029
|
icon: "sun",
|
|
82907
84030
|
args: { color: {
|
|
82908
84031
|
mode: "mired",
|
|
82909
|
-
|
|
84032
|
+
colorTemperature: 370
|
|
82910
84033
|
} }
|
|
82911
84034
|
},
|
|
82912
84035
|
{
|
|
@@ -82917,7 +84040,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
82917
84040
|
icon: "snowflake",
|
|
82918
84041
|
args: { color: {
|
|
82919
84042
|
mode: "mired",
|
|
82920
|
-
|
|
84043
|
+
colorTemperature: 200
|
|
82921
84044
|
} }
|
|
82922
84045
|
},
|
|
82923
84046
|
{
|
|
@@ -83531,7 +84654,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
83531
84654
|
label: t("Fan off", "Ventilatore spento"),
|
|
83532
84655
|
phrase: t("Stop {device}", "Ferma {device}"),
|
|
83533
84656
|
icon: "power-off",
|
|
83534
|
-
args: {
|
|
84657
|
+
args: { speed: 0 }
|
|
83535
84658
|
},
|
|
83536
84659
|
{
|
|
83537
84660
|
id: "fan.full",
|
|
@@ -83539,16 +84662,16 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
83539
84662
|
label: t("Fan full", "Ventilatore al massimo"),
|
|
83540
84663
|
phrase: t("Run {device} at full speed", "Porta {device} alla massima velocità"),
|
|
83541
84664
|
icon: "fan",
|
|
83542
|
-
args: {
|
|
84665
|
+
args: { speed: 100 }
|
|
83543
84666
|
},
|
|
83544
84667
|
{
|
|
83545
84668
|
id: "fan.speed",
|
|
83546
84669
|
method: "setPercentage",
|
|
83547
84670
|
label: t("Set speed", "Imposta la velocità"),
|
|
83548
|
-
phrase: t("Set the speed of {device} to {
|
|
84671
|
+
phrase: t("Set the speed of {device} to {speed}", "Imposta la velocità di {device} al {speed}"),
|
|
83549
84672
|
icon: "fan",
|
|
83550
84673
|
openArgs: [{
|
|
83551
|
-
key: "
|
|
84674
|
+
key: "speed",
|
|
83552
84675
|
label: t("Speed", "Velocità"),
|
|
83553
84676
|
offer: {
|
|
83554
84677
|
kind: "number",
|
|
@@ -83610,7 +84733,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
83610
84733
|
],
|
|
83611
84734
|
state: [{
|
|
83612
84735
|
kind: "numeric",
|
|
83613
|
-
field: "
|
|
84736
|
+
field: "speed",
|
|
83614
84737
|
quantity: t("the speed", "la velocità"),
|
|
83615
84738
|
label: t("Speed", "Velocità"),
|
|
83616
84739
|
unit: "%",
|
|
@@ -84408,7 +85531,7 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
84408
85531
|
},
|
|
84409
85532
|
{
|
|
84410
85533
|
kind: "numeric",
|
|
84411
|
-
field: "
|
|
85534
|
+
field: "progress",
|
|
84412
85535
|
quantity: t("the cycle progress", "l’avanzamento del lavaggio"),
|
|
84413
85536
|
label: t("Progress", "Avanzamento"),
|
|
84414
85537
|
unit: "%",
|
|
@@ -84766,10 +85889,10 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
84766
85889
|
id: "media.seek",
|
|
84767
85890
|
method: "seek",
|
|
84768
85891
|
label: t("Seek", "Vai al punto"),
|
|
84769
|
-
phrase: t("Move {device} to {
|
|
85892
|
+
phrase: t("Move {device} to {position}", "Porta {device} al punto {position}"),
|
|
84770
85893
|
icon: "fast-forward",
|
|
84771
85894
|
openArgs: [{
|
|
84772
|
-
key: "
|
|
85895
|
+
key: "position",
|
|
84773
85896
|
label: t("Position", "Posizione"),
|
|
84774
85897
|
offer: {
|
|
84775
85898
|
kind: "number",
|
|
@@ -85221,7 +86344,39 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
85221
86344
|
phrase: t("When {device} finishes", "Quando {device} finisce"),
|
|
85222
86345
|
icon: "square"
|
|
85223
86346
|
}
|
|
85224
|
-
}
|
|
86347
|
+
},
|
|
86348
|
+
overrides: { byType: {
|
|
86349
|
+
[DeviceType.Washer]: {
|
|
86350
|
+
state: { running: {
|
|
86351
|
+
whenTrue: t("When the washing machine starts", "Quando la lavatrice inizia"),
|
|
86352
|
+
whenFalse: t("When the washing machine has finished", "Quando la lavatrice ha finito")
|
|
86353
|
+
} },
|
|
86354
|
+
events: {
|
|
86355
|
+
onStarted: { phrase: t("When the washing machine starts", "Quando la lavatrice inizia") },
|
|
86356
|
+
onFinished: { phrase: t("When the washing machine has finished", "Quando la lavatrice ha finito") }
|
|
86357
|
+
}
|
|
86358
|
+
},
|
|
86359
|
+
[DeviceType.Dryer]: {
|
|
86360
|
+
state: { running: {
|
|
86361
|
+
whenTrue: t("When the dryer starts", "Quando l'asciugatrice inizia"),
|
|
86362
|
+
whenFalse: t("When the dryer has finished", "Quando l'asciugatrice ha finito")
|
|
86363
|
+
} },
|
|
86364
|
+
events: {
|
|
86365
|
+
onStarted: { phrase: t("When the dryer starts", "Quando l'asciugatrice inizia") },
|
|
86366
|
+
onFinished: { phrase: t("When the dryer has finished", "Quando l'asciugatrice ha finito") }
|
|
86367
|
+
}
|
|
86368
|
+
},
|
|
86369
|
+
[DeviceType.Dishwasher]: {
|
|
86370
|
+
state: { running: {
|
|
86371
|
+
whenTrue: t("When the dishwasher starts", "Quando la lavastoviglie inizia"),
|
|
86372
|
+
whenFalse: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito")
|
|
86373
|
+
} },
|
|
86374
|
+
events: {
|
|
86375
|
+
onStarted: { phrase: t("When the dishwasher starts", "Quando la lavastoviglie inizia") },
|
|
86376
|
+
onFinished: { phrase: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito") }
|
|
86377
|
+
}
|
|
86378
|
+
}
|
|
86379
|
+
} }
|
|
85225
86380
|
},
|
|
85226
86381
|
update: {
|
|
85227
86382
|
scope: "device",
|
|
@@ -85786,7 +86941,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
85786
86941
|
state: [
|
|
85787
86942
|
{
|
|
85788
86943
|
kind: "numeric",
|
|
85789
|
-
field: "
|
|
86944
|
+
field: "co2",
|
|
85790
86945
|
quantity: t("the CO₂", "la CO₂"),
|
|
85791
86946
|
label: t("CO₂", "CO₂"),
|
|
85792
86947
|
unit: "ppm",
|
|
@@ -85794,7 +86949,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
85794
86949
|
},
|
|
85795
86950
|
{
|
|
85796
86951
|
kind: "numeric",
|
|
85797
|
-
field: "
|
|
86952
|
+
field: "voc",
|
|
85798
86953
|
quantity: t("the VOC level", "il livello di COV"),
|
|
85799
86954
|
label: t("VOC", "COV"),
|
|
85800
86955
|
unit: "ppb",
|
|
@@ -85830,7 +86985,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
85830
86985
|
noun: t("Light level", "Luminosità ambiente"),
|
|
85831
86986
|
state: [{
|
|
85832
86987
|
kind: "numeric",
|
|
85833
|
-
field: "
|
|
86988
|
+
field: "illuminance",
|
|
85834
86989
|
quantity: t("the light level", "la luminosità"),
|
|
85835
86990
|
label: t("Illuminance", "Illuminamento"),
|
|
85836
86991
|
unit: "lx",
|
|
@@ -85993,6 +87148,10 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
85993
87148
|
icon: "flame"
|
|
85994
87149
|
}]
|
|
85995
87150
|
},
|
|
87151
|
+
"group-active": {
|
|
87152
|
+
scope: "device",
|
|
87153
|
+
noun: t("Group state", "Stato del gruppo")
|
|
87154
|
+
},
|
|
85996
87155
|
"group-commands": {
|
|
85997
87156
|
scope: "device",
|
|
85998
87157
|
noun: t("Group command", "Comando di gruppo")
|
|
@@ -86029,7 +87188,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
86029
87188
|
noun: t("Humidity", "Umidità"),
|
|
86030
87189
|
state: [{
|
|
86031
87190
|
kind: "numeric",
|
|
86032
|
-
field: "
|
|
87191
|
+
field: "humidity",
|
|
86033
87192
|
quantity: t("the humidity", "l’umidità"),
|
|
86034
87193
|
label: t("Humidity", "Umidità"),
|
|
86035
87194
|
unit: "%",
|
|
@@ -86049,6 +87208,17 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
86049
87208
|
}],
|
|
86050
87209
|
events: { onMotionChanged: { notOperatorFacing: "the `detected` state trigger phrases both edges, from the mirror that survives a dropped event (D49)" } }
|
|
86051
87210
|
},
|
|
87211
|
+
meter: {
|
|
87212
|
+
scope: "device",
|
|
87213
|
+
noun: t("Meter", "Contatore"),
|
|
87214
|
+
state: [{
|
|
87215
|
+
kind: "numeric",
|
|
87216
|
+
field: "value",
|
|
87217
|
+
quantity: t("the total", "il totale"),
|
|
87218
|
+
label: t("Total", "Totale"),
|
|
87219
|
+
icon: "gauge"
|
|
87220
|
+
}]
|
|
87221
|
+
},
|
|
86052
87222
|
"numeric-sensor": {
|
|
86053
87223
|
scope: "device",
|
|
86054
87224
|
noun: t("Sensor", "Sensore"),
|
|
@@ -86066,7 +87236,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
86066
87236
|
state: [
|
|
86067
87237
|
{
|
|
86068
87238
|
kind: "numeric",
|
|
86069
|
-
field: "
|
|
87239
|
+
field: "power",
|
|
86070
87240
|
quantity: t("the power draw", "il consumo"),
|
|
86071
87241
|
label: t("Power", "Potenza"),
|
|
86072
87242
|
unit: "W",
|
|
@@ -86074,7 +87244,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
86074
87244
|
},
|
|
86075
87245
|
{
|
|
86076
87246
|
kind: "numeric",
|
|
86077
|
-
field: "
|
|
87247
|
+
field: "energy",
|
|
86078
87248
|
quantity: t("the energy used", "l’energia consumata"),
|
|
86079
87249
|
label: t("Energy", "Energia"),
|
|
86080
87250
|
unit: "kWh",
|
|
@@ -86082,7 +87252,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
86082
87252
|
},
|
|
86083
87253
|
{
|
|
86084
87254
|
kind: "numeric",
|
|
86085
|
-
field: "
|
|
87255
|
+
field: "voltage",
|
|
86086
87256
|
quantity: t("the voltage", "la tensione"),
|
|
86087
87257
|
label: t("Voltage", "Tensione"),
|
|
86088
87258
|
unit: "V",
|
|
@@ -86090,7 +87260,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
86090
87260
|
},
|
|
86091
87261
|
{
|
|
86092
87262
|
kind: "numeric",
|
|
86093
|
-
field: "
|
|
87263
|
+
field: "current",
|
|
86094
87264
|
quantity: t("the current", "la corrente"),
|
|
86095
87265
|
label: t("Current", "Corrente"),
|
|
86096
87266
|
unit: "A",
|
|
@@ -86107,13 +87277,6 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
86107
87277
|
label: t("Where", "Posizione"),
|
|
86108
87278
|
phrase: t("When {device} is at {value}", "Quando {device} è in {value}"),
|
|
86109
87279
|
icon: "map-pin"
|
|
86110
|
-
}, {
|
|
86111
|
-
kind: "numeric",
|
|
86112
|
-
field: "batteryPercent",
|
|
86113
|
-
quantity: t("the phone battery", "la batteria del telefono"),
|
|
86114
|
-
label: t("Battery", "Batteria"),
|
|
86115
|
-
unit: "%",
|
|
86116
|
-
icon: "battery"
|
|
86117
87280
|
}]
|
|
86118
87281
|
},
|
|
86119
87282
|
"pressure-sensor": {
|
|
@@ -86121,7 +87284,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
86121
87284
|
noun: t("Pressure", "Pressione"),
|
|
86122
87285
|
state: [{
|
|
86123
87286
|
kind: "numeric",
|
|
86124
|
-
field: "
|
|
87287
|
+
field: "pressure",
|
|
86125
87288
|
quantity: t("the pressure", "la pressione"),
|
|
86126
87289
|
label: t("Pressure", "Pressione"),
|
|
86127
87290
|
unit: "hPa",
|
|
@@ -86157,7 +87320,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
86157
87320
|
noun: t("Temperature", "Temperatura"),
|
|
86158
87321
|
state: [{
|
|
86159
87322
|
kind: "numeric",
|
|
86160
|
-
field: "
|
|
87323
|
+
field: "temperature",
|
|
86161
87324
|
quantity: t("the temperature", "la temperatura"),
|
|
86162
87325
|
label: t("Temperature", "Temperatura"),
|
|
86163
87326
|
unit: "°C",
|
|
@@ -86228,7 +87391,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
86228
87391
|
state: [
|
|
86229
87392
|
{
|
|
86230
87393
|
kind: "numeric",
|
|
86231
|
-
field: "
|
|
87394
|
+
field: "level",
|
|
86232
87395
|
quantity: t("the battery level", "il livello della batteria"),
|
|
86233
87396
|
label: t("Battery", "Batteria"),
|
|
86234
87397
|
unit: "%",
|
|
@@ -86270,7 +87433,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
86270
87433
|
icon: "battery-charging"
|
|
86271
87434
|
}
|
|
86272
87435
|
],
|
|
86273
|
-
events: { onStatusChanged: { notOperatorFacing: "the
|
|
87436
|
+
events: { onStatusChanged: { notOperatorFacing: "the level / sleeping / charging state triggers each read one field of this change" } },
|
|
86274
87437
|
extraEvents: [{
|
|
86275
87438
|
category: EventCategory.DeviceAwake,
|
|
86276
87439
|
label: t("Woke up", "Risvegliato"),
|
|
@@ -86288,7 +87451,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
86288
87451
|
noun: t("Network link", "Collegamento di rete"),
|
|
86289
87452
|
state: [{
|
|
86290
87453
|
kind: "numeric",
|
|
86291
|
-
field: "
|
|
87454
|
+
field: "signal",
|
|
86292
87455
|
quantity: t("the signal strength", "l’intensità del segnale"),
|
|
86293
87456
|
label: t("Signal", "Segnale"),
|
|
86294
87457
|
unit: "%",
|
|
@@ -86381,6 +87544,8 @@ var ML = "model and training machinery — no device action";
|
|
|
86381
87544
|
var INTERNAL = "internal plumbing between addons — never operator-facing";
|
|
86382
87545
|
/** Every system-scoped cap, with why it is not a device's vocabulary. */
|
|
86383
87546
|
var SYSTEM_CAP_VOCABULARY = {
|
|
87547
|
+
"action-source": system(INTERNAL),
|
|
87548
|
+
actions: system("declared actions — run from the Actions page and from the cameras that link them"),
|
|
86384
87549
|
"addon-pages": system(UI_PLUMBING),
|
|
86385
87550
|
"addon-pages-source": system(UI_PLUMBING),
|
|
86386
87551
|
"addon-routes": system(UI_PLUMBING),
|
|
@@ -86521,8 +87686,47 @@ function projectAction(cap, action) {
|
|
|
86521
87686
|
};
|
|
86522
87687
|
}
|
|
86523
87688
|
Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
|
|
86524
|
-
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;
|
|
87689
|
+
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;
|
|
86525
87690
|
DeviceFeature.BatteryOperated;
|
|
87691
|
+
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
87692
|
+
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.";
|
|
87693
|
+
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.";
|
|
87694
|
+
var ALEXA_REASON = "Alexa has no laundry category in this export, and neither power nor running has an Alexa interface.";
|
|
87695
|
+
var GOOGLE_REASON = "Google WASHER, DRYER and DISHWASHER require RunCycle. A power threshold is not that trait, and this export does not publish RunCycle.";
|
|
87696
|
+
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.";
|
|
87697
|
+
function withheld(reason) {
|
|
87698
|
+
return {
|
|
87699
|
+
[DeviceType.Washer]: {
|
|
87700
|
+
exported: false,
|
|
87701
|
+
reason
|
|
87702
|
+
},
|
|
87703
|
+
[DeviceType.Dryer]: {
|
|
87704
|
+
exported: false,
|
|
87705
|
+
reason
|
|
87706
|
+
},
|
|
87707
|
+
[DeviceType.Dishwasher]: {
|
|
87708
|
+
exported: false,
|
|
87709
|
+
reason
|
|
87710
|
+
}
|
|
87711
|
+
};
|
|
87712
|
+
}
|
|
87713
|
+
function offered(received) {
|
|
87714
|
+
return {
|
|
87715
|
+
[DeviceType.Washer]: {
|
|
87716
|
+
exported: true,
|
|
87717
|
+
as: received
|
|
87718
|
+
},
|
|
87719
|
+
[DeviceType.Dryer]: {
|
|
87720
|
+
exported: true,
|
|
87721
|
+
as: received
|
|
87722
|
+
},
|
|
87723
|
+
[DeviceType.Dishwasher]: {
|
|
87724
|
+
exported: true,
|
|
87725
|
+
as: received
|
|
87726
|
+
}
|
|
87727
|
+
};
|
|
87728
|
+
}
|
|
87729
|
+
offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
|
|
86526
87730
|
var LAST_FETCHED_FIELD = "lastFetchedAt";
|
|
86527
87731
|
/**
|
|
86528
87732
|
* How long a bridge stops re-attempting a refresh that did not land.
|
|
@@ -86690,6 +87894,31 @@ Object.freeze({
|
|
|
86690
87894
|
form: "single",
|
|
86691
87895
|
optional: false
|
|
86692
87896
|
}],
|
|
87897
|
+
"actions.get": [{
|
|
87898
|
+
name: "deviceId",
|
|
87899
|
+
form: "single",
|
|
87900
|
+
optional: true
|
|
87901
|
+
}],
|
|
87902
|
+
"actions.getLinks": [{
|
|
87903
|
+
name: "deviceId",
|
|
87904
|
+
form: "single",
|
|
87905
|
+
optional: false
|
|
87906
|
+
}],
|
|
87907
|
+
"actions.listForCamera": [{
|
|
87908
|
+
name: "deviceId",
|
|
87909
|
+
form: "single",
|
|
87910
|
+
optional: false
|
|
87911
|
+
}],
|
|
87912
|
+
"actions.run": [{
|
|
87913
|
+
name: "deviceId",
|
|
87914
|
+
form: "single",
|
|
87915
|
+
optional: true
|
|
87916
|
+
}],
|
|
87917
|
+
"actions.setLinks": [{
|
|
87918
|
+
name: "deviceId",
|
|
87919
|
+
form: "single",
|
|
87920
|
+
optional: false
|
|
87921
|
+
}],
|
|
86693
87922
|
"addonSettings.getDeviceSettings": [{
|
|
86694
87923
|
name: "deviceId",
|
|
86695
87924
|
form: "single",
|
|
@@ -87641,6 +88870,11 @@ Object.freeze({
|
|
|
87641
88870
|
form: "single",
|
|
87642
88871
|
optional: false
|
|
87643
88872
|
}],
|
|
88873
|
+
"groupActive.getStatus": [{
|
|
88874
|
+
name: "deviceId",
|
|
88875
|
+
form: "single",
|
|
88876
|
+
optional: false
|
|
88877
|
+
}],
|
|
87644
88878
|
"groupCommands.getStatus": [{
|
|
87645
88879
|
name: "deviceId",
|
|
87646
88880
|
form: "single",
|
|
@@ -87851,6 +89085,11 @@ Object.freeze({
|
|
|
87851
89085
|
form: "single",
|
|
87852
89086
|
optional: false
|
|
87853
89087
|
}],
|
|
89088
|
+
"meter.getStatus": [{
|
|
89089
|
+
name: "deviceId",
|
|
89090
|
+
form: "single",
|
|
89091
|
+
optional: false
|
|
89092
|
+
}],
|
|
87854
89093
|
"motion.getStatus": [{
|
|
87855
89094
|
name: "deviceId",
|
|
87856
89095
|
form: "single",
|
|
@@ -89458,7 +90697,6 @@ Object.freeze({
|
|
|
89458
90697
|
"network-access": "ingress",
|
|
89459
90698
|
"smtp-provider": "email"
|
|
89460
90699
|
});
|
|
89461
|
-
DeviceType.Camera;
|
|
89462
90700
|
/**
|
|
89463
90701
|
* A saved Customize assignment (D936). The schema is the type: callers use
|
|
89464
90702
|
* `z.infer`, they do not restate the shape.
|
|
@@ -90813,6 +92051,7 @@ DEFAULT_NATIVE_LEASE_SETTINGS.activityMs;
|
|
|
90813
92051
|
DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
|
|
90814
92052
|
DEFAULT_NATIVE_LEASE_SETTINGS.sceneBudgetMb;
|
|
90815
92053
|
DEFAULT_NATIVE_LEASE_SETTINGS.admission;
|
|
92054
|
+
String.raw`(?<=^|["'(\s>,\[{])`;
|
|
90816
92055
|
var MB = 1024 * 1024;
|
|
90817
92056
|
1024 * MB, 3072 * MB;
|
|
90818
92057
|
//#endregion
|
|
@@ -200182,7 +201421,7 @@ var FloodlightAccessory = class extends BaseDevice {
|
|
|
200182
201421
|
lastChangedAt: ts
|
|
200183
201422
|
});
|
|
200184
201423
|
if (brightness !== void 0) this.setCapSlice(brightnessCapability, {
|
|
200185
|
-
|
|
201424
|
+
level: brightness,
|
|
200186
201425
|
lastChangedAt: ts
|
|
200187
201426
|
});
|
|
200188
201427
|
this.hasFetchedFromFirmware = true;
|
|
@@ -200301,7 +201540,7 @@ var FloodlightAccessory = class extends BaseDevice {
|
|
|
200301
201540
|
}
|
|
200302
201541
|
brightnessView() {
|
|
200303
201542
|
return this.getCapSlice(brightnessCapability) ?? {
|
|
200304
|
-
|
|
201543
|
+
level: 100,
|
|
200305
201544
|
lastChangedAt: 0
|
|
200306
201545
|
};
|
|
200307
201546
|
}
|
|
@@ -200326,19 +201565,19 @@ var FloodlightAccessory = class extends BaseDevice {
|
|
|
200326
201565
|
registerBrightnessCap() {
|
|
200327
201566
|
this.ctx.registerNativeCap(brightnessCapability, {
|
|
200328
201567
|
getStatus: async () => this.brightnessView(),
|
|
200329
|
-
setBrightness: async ({ deviceId, percentage }) => {
|
|
201568
|
+
setBrightness: async ({ deviceId, level: percentage }) => {
|
|
200330
201569
|
if (deviceId !== this.id) return;
|
|
200331
201570
|
const value = Math.max(0, Math.min(100, percentage));
|
|
200332
201571
|
if (this.switchView().on) await (await this.api).setWhiteLedState(this.channel, void 0, value);
|
|
200333
201572
|
this.setCapSlice(brightnessCapability, {
|
|
200334
|
-
|
|
201573
|
+
level: value,
|
|
200335
201574
|
lastChangedAt: Date.now()
|
|
200336
201575
|
});
|
|
200337
201576
|
this.lastSetAtMs = Date.now();
|
|
200338
201577
|
}
|
|
200339
201578
|
});
|
|
200340
201579
|
this.setCapSlice(brightnessCapability, {
|
|
200341
|
-
|
|
201580
|
+
level: 100,
|
|
200342
201581
|
lastChangedAt: 0
|
|
200343
201582
|
});
|
|
200344
201583
|
}
|
|
@@ -201014,13 +202253,13 @@ function mapNetworkReadout(readout, now) {
|
|
|
201014
202253
|
const type = linkTypeOf(readout);
|
|
201015
202254
|
const raw = readout.wifiSignal;
|
|
201016
202255
|
const wireless = type === "wifi" || type === "cellular";
|
|
201017
|
-
const
|
|
202256
|
+
const signal = wireless ? signalPercentOf(raw) : null;
|
|
201018
202257
|
const rssiDbm = wireless && raw !== void 0 && Number.isFinite(raw) && raw < 0 ? raw : void 0;
|
|
201019
202258
|
const ssid = wireless && readout.ssid !== void 0 && readout.ssid !== "" ? readout.ssid : void 0;
|
|
201020
202259
|
return {
|
|
201021
202260
|
type,
|
|
201022
|
-
|
|
201023
|
-
...rssiDbm !== void 0 ? { rssiDbm } : {},
|
|
202261
|
+
signal,
|
|
202262
|
+
...rssiDbm !== void 0 ? { rssi: rssiDbm } : {},
|
|
201024
202263
|
...ssid !== void 0 ? { ssid } : {},
|
|
201025
202264
|
lastUpdated: now
|
|
201026
202265
|
};
|
|
@@ -204263,7 +205502,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
204263
205502
|
*/
|
|
204264
205503
|
mapBatteryInfo(info) {
|
|
204265
205504
|
return {
|
|
204266
|
-
|
|
205505
|
+
level: typeof info.batteryPercent === "number" ? Math.max(0, Math.min(100, Math.round(info.batteryPercent))) : this.state.battery.level ?? null,
|
|
204267
205506
|
charging: reportsPowerSource(info) ? deriveChargingSource(info.adapterStatus, info.chargeStatus) : this.state.battery.charging ?? "unknown",
|
|
204268
205507
|
sleeping: info.sleeping === true || this.sleeping,
|
|
204269
205508
|
lastUpdated: Date.now()
|
|
@@ -204399,7 +205638,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
204399
205638
|
}
|
|
204400
205639
|
});
|
|
204401
205640
|
if (this.getCapSlice(batteryCapability) === null) this.setCapSlice(batteryCapability, {
|
|
204402
|
-
|
|
205641
|
+
level: null,
|
|
204403
205642
|
charging: "none",
|
|
204404
205643
|
sleeping: false,
|
|
204405
205644
|
lastUpdated: Date.now()
|
|
@@ -204627,7 +205866,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
204627
205866
|
});
|
|
204628
205867
|
this.setCapSlice(batteryCapability, {
|
|
204629
205868
|
...this.state.battery,
|
|
204630
|
-
|
|
205869
|
+
level: mapped.level,
|
|
204631
205870
|
charging: mapped.charging,
|
|
204632
205871
|
lastUpdated: Date.now()
|
|
204633
205872
|
});
|