@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.mjs
CHANGED
|
@@ -28,7 +28,7 @@ import { URL as URL$1 } from "url";
|
|
|
28
28
|
import { spawn as spawn$1 } from "node:child_process";
|
|
29
29
|
import os, { tmpdir } from "node:os";
|
|
30
30
|
import { createReadStream } from "node:fs";
|
|
31
|
-
//#region ../types/dist/event-category-
|
|
31
|
+
//#region ../types/dist/event-category-DmZDPDf_.mjs
|
|
32
32
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
33
33
|
EventCategory["SystemBoot"] = "system.boot";
|
|
34
34
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -655,7 +655,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
655
655
|
* (a track was promoted to a stationary-registry entry) or departs (the
|
|
656
656
|
* object moved / was removed). Telemetry (D8): lossy, drives a UI refresh of
|
|
657
657
|
* the dedicated "Stationary" section — never the live event feed. Payload:
|
|
658
|
-
* `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp }`.
|
|
658
|
+
* `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp, observedAt, zones }`.
|
|
659
659
|
*/
|
|
660
660
|
EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
|
|
661
661
|
/**
|
|
@@ -793,6 +793,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
793
793
|
*/
|
|
794
794
|
EventCategory["AccessoriesChanged"] = "accessories.onAccessoriesChanged";
|
|
795
795
|
/**
|
|
796
|
+
* An action run started or ended. Emitted by the `actions` surface.
|
|
797
|
+
* Payload: `ActionRunChanged` (`{ actionId, runId, isRunning, outcome?, lastRunAt, lastError }`).
|
|
798
|
+
*/
|
|
799
|
+
EventCategory["ActionsRunChanged"] = "actions.onRunChanged";
|
|
800
|
+
/**
|
|
796
801
|
* Progress update from a running model conversion job.
|
|
797
802
|
* Payload: `{ kind: 'model-convert', phase, sessionId?, pct?, detail? }`.
|
|
798
803
|
* Emitted by `addon-model-studio` on the converting node.
|
|
@@ -5471,7 +5476,7 @@ var ZodIssueCode = {
|
|
|
5471
5476
|
var ZodFirstPartyTypeKind;
|
|
5472
5477
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5473
5478
|
//#endregion
|
|
5474
|
-
//#region ../types/dist/sleep-
|
|
5479
|
+
//#region ../types/dist/sleep-Crlmu_tO.mjs
|
|
5475
5480
|
/**
|
|
5476
5481
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5477
5482
|
* window to float samples (D455).
|
|
@@ -8629,6 +8634,8 @@ Object.freeze({ "doorbell": "lastPressedAt" });
|
|
|
8629
8634
|
/** Every capability name, so addon code can check a name without the schema graph. */
|
|
8630
8635
|
var CAP_NAMES = Object.freeze([
|
|
8631
8636
|
"accessories",
|
|
8637
|
+
"action-source",
|
|
8638
|
+
"actions",
|
|
8632
8639
|
"addon-pages",
|
|
8633
8640
|
"addon-pages-source",
|
|
8634
8641
|
"addon-routes",
|
|
@@ -8702,6 +8709,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
8702
8709
|
"filesystem-browse",
|
|
8703
8710
|
"flood",
|
|
8704
8711
|
"gas",
|
|
8712
|
+
"group-active",
|
|
8705
8713
|
"group-commands",
|
|
8706
8714
|
"group-status",
|
|
8707
8715
|
"heating-valve",
|
|
@@ -8724,6 +8732,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
8724
8732
|
"login-method",
|
|
8725
8733
|
"media-player",
|
|
8726
8734
|
"mesh-network",
|
|
8735
|
+
"meter",
|
|
8727
8736
|
"metrics-provider",
|
|
8728
8737
|
"model-convert",
|
|
8729
8738
|
"model-distributor",
|
|
@@ -8815,11 +8824,11 @@ var CAP_NAMES = Object.freeze([
|
|
|
8815
8824
|
Object.freeze({
|
|
8816
8825
|
"air-quality-sensor": [
|
|
8817
8826
|
{
|
|
8818
|
-
"path": "
|
|
8827
|
+
"path": "co2",
|
|
8819
8828
|
"valueType": "number"
|
|
8820
8829
|
},
|
|
8821
8830
|
{
|
|
8822
|
-
"path": "
|
|
8831
|
+
"path": "voc",
|
|
8823
8832
|
"valueType": "number"
|
|
8824
8833
|
},
|
|
8825
8834
|
{
|
|
@@ -8869,7 +8878,7 @@ Object.freeze({
|
|
|
8869
8878
|
}
|
|
8870
8879
|
],
|
|
8871
8880
|
"ambient-light-sensor": [{
|
|
8872
|
-
"path": "
|
|
8881
|
+
"path": "illuminance",
|
|
8873
8882
|
"valueType": "number"
|
|
8874
8883
|
}],
|
|
8875
8884
|
"audio-metrics": [
|
|
@@ -8930,7 +8939,7 @@ Object.freeze({
|
|
|
8930
8939
|
],
|
|
8931
8940
|
"battery": [
|
|
8932
8941
|
{
|
|
8933
|
-
"path": "
|
|
8942
|
+
"path": "level",
|
|
8934
8943
|
"valueType": "number"
|
|
8935
8944
|
},
|
|
8936
8945
|
{
|
|
@@ -8952,7 +8961,7 @@ Object.freeze({
|
|
|
8952
8961
|
"valueType": "number"
|
|
8953
8962
|
},
|
|
8954
8963
|
{
|
|
8955
|
-
"path": "
|
|
8964
|
+
"path": "voltage",
|
|
8956
8965
|
"valueType": "number"
|
|
8957
8966
|
},
|
|
8958
8967
|
{
|
|
@@ -8973,7 +8982,7 @@ Object.freeze({
|
|
|
8973
8982
|
"valueType": "boolean"
|
|
8974
8983
|
}],
|
|
8975
8984
|
"brightness": [{
|
|
8976
|
-
"path": "
|
|
8985
|
+
"path": "level",
|
|
8977
8986
|
"valueType": "number"
|
|
8978
8987
|
}],
|
|
8979
8988
|
"broker": [{
|
|
@@ -9174,7 +9183,7 @@ Object.freeze({
|
|
|
9174
9183
|
"valueType": "number"
|
|
9175
9184
|
},
|
|
9176
9185
|
{
|
|
9177
|
-
"path": "
|
|
9186
|
+
"path": "colorTemperature",
|
|
9178
9187
|
"valueType": "number"
|
|
9179
9188
|
}
|
|
9180
9189
|
],
|
|
@@ -9353,15 +9362,15 @@ Object.freeze({
|
|
|
9353
9362
|
"valueType": "string"
|
|
9354
9363
|
},
|
|
9355
9364
|
{
|
|
9356
|
-
"path": "
|
|
9365
|
+
"path": "remaining",
|
|
9357
9366
|
"valueType": "number"
|
|
9358
9367
|
},
|
|
9359
9368
|
{
|
|
9360
|
-
"path": "
|
|
9369
|
+
"path": "delayedStart",
|
|
9361
9370
|
"valueType": "number"
|
|
9362
9371
|
},
|
|
9363
9372
|
{
|
|
9364
|
-
"path": "
|
|
9373
|
+
"path": "progress",
|
|
9365
9374
|
"valueType": "number"
|
|
9366
9375
|
},
|
|
9367
9376
|
{
|
|
@@ -9475,7 +9484,7 @@ Object.freeze({
|
|
|
9475
9484
|
],
|
|
9476
9485
|
"fan-control": [
|
|
9477
9486
|
{
|
|
9478
|
-
"path": "
|
|
9487
|
+
"path": "speed",
|
|
9479
9488
|
"valueType": "number"
|
|
9480
9489
|
},
|
|
9481
9490
|
{
|
|
@@ -9522,6 +9531,10 @@ Object.freeze({
|
|
|
9522
9531
|
"path": "detected",
|
|
9523
9532
|
"valueType": "boolean"
|
|
9524
9533
|
}],
|
|
9534
|
+
"group-active": [{
|
|
9535
|
+
"path": "active",
|
|
9536
|
+
"valueType": "boolean"
|
|
9537
|
+
}],
|
|
9525
9538
|
"group-status": [
|
|
9526
9539
|
{
|
|
9527
9540
|
"path": "included",
|
|
@@ -9571,7 +9584,7 @@ Object.freeze({
|
|
|
9571
9584
|
}
|
|
9572
9585
|
],
|
|
9573
9586
|
"humidity-sensor": [{
|
|
9574
|
-
"path": "
|
|
9587
|
+
"path": "humidity",
|
|
9575
9588
|
"valueType": "number"
|
|
9576
9589
|
}],
|
|
9577
9590
|
"image": [{
|
|
@@ -9681,7 +9694,7 @@ Object.freeze({
|
|
|
9681
9694
|
"valueType": "number"
|
|
9682
9695
|
},
|
|
9683
9696
|
{
|
|
9684
|
-
"path": "
|
|
9697
|
+
"path": "progress",
|
|
9685
9698
|
"valueType": "number"
|
|
9686
9699
|
},
|
|
9687
9700
|
{
|
|
@@ -9760,11 +9773,11 @@ Object.freeze({
|
|
|
9760
9773
|
"valueType": "string"
|
|
9761
9774
|
},
|
|
9762
9775
|
{
|
|
9763
|
-
"path": "
|
|
9776
|
+
"path": "position",
|
|
9764
9777
|
"valueType": "number"
|
|
9765
9778
|
},
|
|
9766
9779
|
{
|
|
9767
|
-
"path": "
|
|
9780
|
+
"path": "duration",
|
|
9768
9781
|
"valueType": "number"
|
|
9769
9782
|
},
|
|
9770
9783
|
{
|
|
@@ -9781,6 +9794,10 @@ Object.freeze({
|
|
|
9781
9794
|
]
|
|
9782
9795
|
}
|
|
9783
9796
|
],
|
|
9797
|
+
"meter": [{
|
|
9798
|
+
"path": "value",
|
|
9799
|
+
"valueType": "number"
|
|
9800
|
+
}],
|
|
9784
9801
|
"motion": [
|
|
9785
9802
|
{
|
|
9786
9803
|
"path": "detected",
|
|
@@ -9862,11 +9879,11 @@ Object.freeze({
|
|
|
9862
9879
|
]
|
|
9863
9880
|
},
|
|
9864
9881
|
{
|
|
9865
|
-
"path": "
|
|
9882
|
+
"path": "signal",
|
|
9866
9883
|
"valueType": "number"
|
|
9867
9884
|
},
|
|
9868
9885
|
{
|
|
9869
|
-
"path": "
|
|
9886
|
+
"path": "rssi",
|
|
9870
9887
|
"valueType": "number"
|
|
9871
9888
|
},
|
|
9872
9889
|
{
|
|
@@ -9921,6 +9938,18 @@ Object.freeze({
|
|
|
9921
9938
|
"path": "foodLevel",
|
|
9922
9939
|
"valueType": "number"
|
|
9923
9940
|
},
|
|
9941
|
+
{
|
|
9942
|
+
"path": "bowlFood",
|
|
9943
|
+
"valueType": "number"
|
|
9944
|
+
},
|
|
9945
|
+
{
|
|
9946
|
+
"path": "eating",
|
|
9947
|
+
"valueType": "boolean"
|
|
9948
|
+
},
|
|
9949
|
+
{
|
|
9950
|
+
"path": "supportsMarkRefilled",
|
|
9951
|
+
"valueType": "boolean"
|
|
9952
|
+
},
|
|
9924
9953
|
{
|
|
9925
9954
|
"path": "food1",
|
|
9926
9955
|
"valueType": "number"
|
|
@@ -9985,23 +10014,23 @@ Object.freeze({
|
|
|
9985
10014
|
],
|
|
9986
10015
|
"power-meter": [
|
|
9987
10016
|
{
|
|
9988
|
-
"path": "
|
|
10017
|
+
"path": "power",
|
|
9989
10018
|
"valueType": "number"
|
|
9990
10019
|
},
|
|
9991
10020
|
{
|
|
9992
|
-
"path": "
|
|
10021
|
+
"path": "energy",
|
|
9993
10022
|
"valueType": "number"
|
|
9994
10023
|
},
|
|
9995
10024
|
{
|
|
9996
|
-
"path": "
|
|
10025
|
+
"path": "voltage",
|
|
9997
10026
|
"valueType": "number"
|
|
9998
10027
|
},
|
|
9999
10028
|
{
|
|
10000
|
-
"path": "
|
|
10029
|
+
"path": "current",
|
|
10001
10030
|
"valueType": "number"
|
|
10002
10031
|
},
|
|
10003
10032
|
{
|
|
10004
|
-
"path": "
|
|
10033
|
+
"path": "apparentPower",
|
|
10005
10034
|
"valueType": "number"
|
|
10006
10035
|
},
|
|
10007
10036
|
{
|
|
@@ -10029,14 +10058,10 @@ Object.freeze({
|
|
|
10029
10058
|
{
|
|
10030
10059
|
"path": "gps.accuracyMeters",
|
|
10031
10060
|
"valueType": "number"
|
|
10032
|
-
},
|
|
10033
|
-
{
|
|
10034
|
-
"path": "batteryPercent",
|
|
10035
|
-
"valueType": "number"
|
|
10036
10061
|
}
|
|
10037
10062
|
],
|
|
10038
10063
|
"pressure-sensor": [{
|
|
10039
|
-
"path": "
|
|
10064
|
+
"path": "pressure",
|
|
10040
10065
|
"valueType": "number"
|
|
10041
10066
|
}],
|
|
10042
10067
|
"privacy-mask": [{
|
|
@@ -10350,7 +10375,7 @@ Object.freeze({
|
|
|
10350
10375
|
"valueType": "boolean"
|
|
10351
10376
|
}],
|
|
10352
10377
|
"temperature-sensor": [{
|
|
10353
|
-
"path": "
|
|
10378
|
+
"path": "temperature",
|
|
10354
10379
|
"valueType": "number"
|
|
10355
10380
|
}],
|
|
10356
10381
|
"update": [
|
|
@@ -10398,7 +10423,7 @@ Object.freeze({
|
|
|
10398
10423
|
"valueType": "string"
|
|
10399
10424
|
},
|
|
10400
10425
|
{
|
|
10401
|
-
"path": "
|
|
10426
|
+
"path": "progress",
|
|
10402
10427
|
"valueType": "number"
|
|
10403
10428
|
},
|
|
10404
10429
|
{
|
|
@@ -10562,7 +10587,7 @@ function sleep$2(ms) {
|
|
|
10562
10587
|
return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
|
|
10563
10588
|
}
|
|
10564
10589
|
//#endregion
|
|
10565
|
-
//#region ../types/dist/err-msg-
|
|
10590
|
+
//#region ../types/dist/err-msg-DIorGYeC.mjs
|
|
10566
10591
|
var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
10567
10592
|
DeviceType["Camera"] = "camera";
|
|
10568
10593
|
DeviceType["Hub"] = "hub";
|
|
@@ -10656,8 +10681,14 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
|
10656
10681
|
DeviceType["PetFeeder"] = "pet-feeder";
|
|
10657
10682
|
/** Built-in dishwasher — program lifecycle, door / remote-start gates,
|
|
10658
10683
|
* salt and rinse-aid levels. Installed with the `dishwasher` cap.
|
|
10659
|
-
* Sources: native Bosch Home Connect, reusable by similar integrations.
|
|
10684
|
+
* Sources: native Bosch Home Connect, reusable by similar integrations.
|
|
10685
|
+
* A plug-built dishwasher carries running-state and power instead. */
|
|
10660
10686
|
DeviceType["Dishwasher"] = "dishwasher";
|
|
10687
|
+
/** Washing machine. A plug-built one is running while its power stays
|
|
10688
|
+
* above a threshold. No program cap — that surface is the dishwasher's. */
|
|
10689
|
+
DeviceType["Washer"] = "washer";
|
|
10690
|
+
/** Tumble dryer. Same plug-built running surface as {@link Washer}. */
|
|
10691
|
+
DeviceType["Dryer"] = "dryer";
|
|
10661
10692
|
return DeviceType;
|
|
10662
10693
|
}({});
|
|
10663
10694
|
var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
|
|
@@ -10905,7 +10936,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
|
|
|
10905
10936
|
return DeviceRole;
|
|
10906
10937
|
}({});
|
|
10907
10938
|
//#endregion
|
|
10908
|
-
//#region ../types/dist/composition-
|
|
10939
|
+
//#region ../types/dist/composition-CZImLpOI.mjs
|
|
10909
10940
|
/**
|
|
10910
10941
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
10911
10942
|
*
|
|
@@ -11220,6 +11251,13 @@ var table$1 = {
|
|
|
11220
11251
|
apply: () => {
|
|
11221
11252
|
throw new ExpressionEvalError("latchedAt: stateful evaluation context is required");
|
|
11222
11253
|
}
|
|
11254
|
+
},
|
|
11255
|
+
schmitt: {
|
|
11256
|
+
minArgs: 3,
|
|
11257
|
+
maxArgs: 5,
|
|
11258
|
+
apply: () => {
|
|
11259
|
+
throw new ExpressionEvalError("schmitt: stateful evaluation context is required");
|
|
11260
|
+
}
|
|
11223
11261
|
}
|
|
11224
11262
|
};
|
|
11225
11263
|
Object.freeze(Object.assign(Object.create(null), table$1));
|
|
@@ -11661,6 +11699,99 @@ function validateExpressionSource(src) {
|
|
|
11661
11699
|
return null;
|
|
11662
11700
|
}
|
|
11663
11701
|
/**
|
|
11702
|
+
* One power reading becomes a washer, dryer or dishwasher (D944 helper).
|
|
11703
|
+
*
|
|
11704
|
+
* Standby of the control board is under 5 W; a motor or a heater is far
|
|
11705
|
+
* above that, so 5 W is the line. A door lock or a short pump spike is not
|
|
11706
|
+
* a cycle, so running starts only after 30 s above the line.
|
|
11707
|
+
*
|
|
11708
|
+
* A wash, dry or dishwasher pause (soak, heat-wait, heater off between
|
|
11709
|
+
* bursts) lasts several minutes. Ending the cycle on the first dip would
|
|
11710
|
+
* report "finished" in the middle of the program, so the stop dwell is
|
|
11711
|
+
* 10 minutes on every type. The number is stored per type so one machine
|
|
11712
|
+
* can move later without moving the others.
|
|
11713
|
+
*/
|
|
11714
|
+
var APPLIANCE_TEMPLATE_ID = "appliance";
|
|
11715
|
+
var APPLIANCE_KINDS = [
|
|
11716
|
+
"washer",
|
|
11717
|
+
"dryer",
|
|
11718
|
+
"dishwasher"
|
|
11719
|
+
];
|
|
11720
|
+
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
11721
|
+
/**
|
|
11722
|
+
* Statistics and integrals over a history read (D944).
|
|
11723
|
+
*
|
|
11724
|
+
* Pure: the same points always give the same answer, so a restart recomputes
|
|
11725
|
+
* rather than trusting a buffer. A gauge mean is time-weighted (value × ms),
|
|
11726
|
+
* never the mean of the samples. A hole with no held value is not integrated
|
|
11727
|
+
* as zero. A truncated read is not published.
|
|
11728
|
+
*/
|
|
11729
|
+
var STATISTIC_STATS = [
|
|
11730
|
+
"min",
|
|
11731
|
+
"max",
|
|
11732
|
+
"mean",
|
|
11733
|
+
"sum",
|
|
11734
|
+
"count",
|
|
11735
|
+
"change"
|
|
11736
|
+
];
|
|
11737
|
+
/**
|
|
11738
|
+
* The range a statistics or integral helper asks history for (D944).
|
|
11739
|
+
*
|
|
11740
|
+
* Calendar edges are the hub's timezone, never UTC midnight: a day is 23, 24
|
|
11741
|
+
* or 25 hours across a DST change. A tier is eligible only when its retention
|
|
11742
|
+
* is strictly longer than the longest that range can be. Forever qualifies.
|
|
11743
|
+
* Equal to a tier's retention is a refusal, not a short answer.
|
|
11744
|
+
*/
|
|
11745
|
+
var MINUTE_MS = 6e4;
|
|
11746
|
+
var HOUR_MS = 60 * MINUTE_MS;
|
|
11747
|
+
var DAY_MS = 24 * HOUR_MS;
|
|
11748
|
+
var ROLLING_SPANS = [
|
|
11749
|
+
"1h",
|
|
11750
|
+
"24h",
|
|
11751
|
+
"2d",
|
|
11752
|
+
"7d",
|
|
11753
|
+
"30d",
|
|
11754
|
+
"custom"
|
|
11755
|
+
];
|
|
11756
|
+
var CALENDAR_PERIODS = [
|
|
11757
|
+
"today",
|
|
11758
|
+
"yesterday",
|
|
11759
|
+
"this-week",
|
|
11760
|
+
"this-month"
|
|
11761
|
+
];
|
|
11762
|
+
var RESET_PERIODS = [
|
|
11763
|
+
"never",
|
|
11764
|
+
"daily",
|
|
11765
|
+
"weekly",
|
|
11766
|
+
"monthly"
|
|
11767
|
+
];
|
|
11768
|
+
5 * MINUTE_MS;
|
|
11769
|
+
24 * HOUR_MS, 2 * DAY_MS, 7 * DAY_MS, 30 * DAY_MS;
|
|
11770
|
+
7 * DAY_MS, 30 * DAY_MS, 365 * DAY_MS;
|
|
11771
|
+
var rollingWindowObject = object({
|
|
11772
|
+
kind: literal("rolling"),
|
|
11773
|
+
span: _enum(ROLLING_SPANS),
|
|
11774
|
+
customMinutes: number().int().min(5).optional()
|
|
11775
|
+
}).check((payload) => {
|
|
11776
|
+
if (payload.value.span === "custom" && payload.value.customMinutes === void 0) payload.issues.push({
|
|
11777
|
+
code: "custom",
|
|
11778
|
+
message: "a custom range needs a duration in minutes",
|
|
11779
|
+
path: ["customMinutes"],
|
|
11780
|
+
input: payload.value
|
|
11781
|
+
});
|
|
11782
|
+
if (payload.value.span !== "custom" && payload.value.customMinutes !== void 0) payload.issues.push({
|
|
11783
|
+
code: "custom",
|
|
11784
|
+
message: "only a custom range carries a duration",
|
|
11785
|
+
path: ["customMinutes"],
|
|
11786
|
+
input: payload.value
|
|
11787
|
+
});
|
|
11788
|
+
});
|
|
11789
|
+
var RollingWindowSchema = rollingWindowObject;
|
|
11790
|
+
var StatisticWindowSchema = discriminatedUnion("kind", [rollingWindowObject, object({
|
|
11791
|
+
kind: literal("calendar"),
|
|
11792
|
+
period: _enum(CALENDAR_PERIODS)
|
|
11793
|
+
})]);
|
|
11794
|
+
/**
|
|
11664
11795
|
* A composition source identity: `(addonId, stableId)`, never a numeric device
|
|
11665
11796
|
* id. Split out so helper schemas can name it without a cycle through
|
|
11666
11797
|
* `composition.ts` (D659).
|
|
@@ -11686,7 +11817,13 @@ var GROUP_TEMPLATE_IDS = [
|
|
|
11686
11817
|
"occupancy",
|
|
11687
11818
|
"binary",
|
|
11688
11819
|
"switch",
|
|
11689
|
-
"light"
|
|
11820
|
+
"light",
|
|
11821
|
+
"fan",
|
|
11822
|
+
"cover",
|
|
11823
|
+
"valve",
|
|
11824
|
+
"lock",
|
|
11825
|
+
"media-player",
|
|
11826
|
+
"climate"
|
|
11690
11827
|
];
|
|
11691
11828
|
var NUMERIC_AGGREGATES = [
|
|
11692
11829
|
"mean",
|
|
@@ -11716,14 +11853,73 @@ var HelperMemberSchema = object({
|
|
|
11716
11853
|
cap: string().min(1),
|
|
11717
11854
|
fieldPath: string().min(1)
|
|
11718
11855
|
});
|
|
11719
|
-
var HelperSpecSchema = discriminatedUnion("kind", [
|
|
11720
|
-
|
|
11721
|
-
|
|
11722
|
-
|
|
11723
|
-
|
|
11724
|
-
|
|
11725
|
-
|
|
11726
|
-
|
|
11856
|
+
var HelperSpecSchema = discriminatedUnion("kind", [
|
|
11857
|
+
object({
|
|
11858
|
+
kind: literal("group"),
|
|
11859
|
+
templateId: GroupTemplateIdSchema,
|
|
11860
|
+
aggregate: AggregateReducerSchema,
|
|
11861
|
+
members: array(HelperMemberSchema).min(1).max(64),
|
|
11862
|
+
/** Actuator groups only. Readings leave this unset. */
|
|
11863
|
+
active: ActiveCriterionSchema.optional()
|
|
11864
|
+
}),
|
|
11865
|
+
object({
|
|
11866
|
+
kind: literal("appliance"),
|
|
11867
|
+
templateId: literal(APPLIANCE_TEMPLATE_ID),
|
|
11868
|
+
appliance: _enum(APPLIANCE_KINDS),
|
|
11869
|
+
thresholdWatts: number().positive(),
|
|
11870
|
+
startDwellMs: number().int().nonnegative(),
|
|
11871
|
+
stopDwellMs: number().int().nonnegative(),
|
|
11872
|
+
member: HelperMemberSchema
|
|
11873
|
+
}),
|
|
11874
|
+
object({
|
|
11875
|
+
kind: literal("statistic"),
|
|
11876
|
+
stat: _enum(STATISTIC_STATS),
|
|
11877
|
+
window: StatisticWindowSchema,
|
|
11878
|
+
source: HelperMemberSchema
|
|
11879
|
+
}),
|
|
11880
|
+
object({
|
|
11881
|
+
kind: literal("integral"),
|
|
11882
|
+
reset: _enum(RESET_PERIODS),
|
|
11883
|
+
output: _enum(["energy", "meter"]),
|
|
11884
|
+
source: HelperMemberSchema
|
|
11885
|
+
}),
|
|
11886
|
+
object({
|
|
11887
|
+
kind: literal("threshold"),
|
|
11888
|
+
source: HelperMemberSchema,
|
|
11889
|
+
high: number(),
|
|
11890
|
+
low: number(),
|
|
11891
|
+
onForMs: number().int().nonnegative(),
|
|
11892
|
+
offForMs: number().int().nonnegative()
|
|
11893
|
+
}),
|
|
11894
|
+
object({
|
|
11895
|
+
kind: literal("arithmetic"),
|
|
11896
|
+
op: _enum([
|
|
11897
|
+
"sum",
|
|
11898
|
+
"difference",
|
|
11899
|
+
"ratio"
|
|
11900
|
+
]),
|
|
11901
|
+
inputs: array(HelperMemberSchema).min(2).max(64)
|
|
11902
|
+
}),
|
|
11903
|
+
object({
|
|
11904
|
+
kind: literal("expression"),
|
|
11905
|
+
expr: string().min(1),
|
|
11906
|
+
inputs: array(object({
|
|
11907
|
+
name: string().min(1),
|
|
11908
|
+
member: HelperMemberSchema
|
|
11909
|
+
})).min(1).max(64),
|
|
11910
|
+
output: _enum(["number", "boolean"])
|
|
11911
|
+
}),
|
|
11912
|
+
object({
|
|
11913
|
+
kind: literal("derivative"),
|
|
11914
|
+
window: RollingWindowSchema,
|
|
11915
|
+
per: _enum([
|
|
11916
|
+
"second",
|
|
11917
|
+
"minute",
|
|
11918
|
+
"hour"
|
|
11919
|
+
]),
|
|
11920
|
+
source: HelperMemberSchema
|
|
11921
|
+
})
|
|
11922
|
+
]);
|
|
11727
11923
|
/** A composed device's stableId is this prefix plus the block id. */
|
|
11728
11924
|
var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
|
|
11729
11925
|
/** Longest snippet a `code` source may carry (slice 4). */
|
|
@@ -11784,6 +11980,12 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
|
|
|
11784
11980
|
"all"
|
|
11785
11981
|
]),
|
|
11786
11982
|
reads: array(CompositionFieldReadSchema).min(1).max(64),
|
|
11983
|
+
/**
|
|
11984
|
+
* Set when the reads are an enum state, not a yes/no: the listed states
|
|
11985
|
+
* count as active (a cover is `open`). A state outside the list is inactive;
|
|
11986
|
+
* a read that is not a state at all is left out. Only `any` / `all` use it.
|
|
11987
|
+
*/
|
|
11988
|
+
activeWhen: array(string().min(1)).min(1).optional(),
|
|
11787
11989
|
/** `value` is the reading; the others are the member counts. */
|
|
11788
11990
|
publish: _enum([
|
|
11789
11991
|
"value",
|
|
@@ -11797,6 +11999,26 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
|
|
|
11797
11999
|
"omit",
|
|
11798
12000
|
"offline"
|
|
11799
12001
|
])
|
|
12002
|
+
}),
|
|
12003
|
+
object({
|
|
12004
|
+
kind: literal("series"),
|
|
12005
|
+
read: CompositionFieldReadSchema,
|
|
12006
|
+
/** `value` is the statistic or the integral. The others are the gap counts. */
|
|
12007
|
+
publish: _enum([
|
|
12008
|
+
"value",
|
|
12009
|
+
"included",
|
|
12010
|
+
"excluded",
|
|
12011
|
+
"total"
|
|
12012
|
+
]),
|
|
12013
|
+
/**
|
|
12014
|
+
* What an unanswered `value` does. Counts ignore this. `offline` leaves the
|
|
12015
|
+
* device unseeded until history answers, so a restart cannot publish 0.
|
|
12016
|
+
*/
|
|
12017
|
+
empty: _enum([
|
|
12018
|
+
"null",
|
|
12019
|
+
"omit",
|
|
12020
|
+
"offline"
|
|
12021
|
+
])
|
|
11800
12022
|
})
|
|
11801
12023
|
]);
|
|
11802
12024
|
var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
|
|
@@ -15804,6 +16026,14 @@ var AutomationConditionLeafSchema = object({
|
|
|
15804
16026
|
array(union([string(), number()]))
|
|
15805
16027
|
])
|
|
15806
16028
|
});
|
|
16029
|
+
var AutomationConditionStationaryPresentSchema = object({
|
|
16030
|
+
kind: literal("stationary-present"),
|
|
16031
|
+
deviceId: number().int().nonnegative(),
|
|
16032
|
+
/** Class names; a macro class matches every raw class that maps to it. */
|
|
16033
|
+
classes: array(string().min(1)).min(1).optional(),
|
|
16034
|
+
/** The parked object must be in at least one of these zones. */
|
|
16035
|
+
zones: array(string().min(1)).min(1).optional()
|
|
16036
|
+
});
|
|
15807
16037
|
/**
|
|
15808
16038
|
* The expression leaf, declared as a plain object rather than an intersection
|
|
15809
16039
|
* with {@link ExpressionSourceSchema}: a discriminated union has to be able to
|
|
@@ -15837,7 +16067,8 @@ var AutomationConditionSchema = lazy(() => discriminatedUnion("kind", [
|
|
|
15837
16067
|
child: AutomationConditionSchema
|
|
15838
16068
|
}),
|
|
15839
16069
|
AutomationConditionLeafSchema,
|
|
15840
|
-
AutomationConditionExpressionSchema
|
|
16070
|
+
AutomationConditionExpressionSchema,
|
|
16071
|
+
AutomationConditionStationaryPresentSchema
|
|
15841
16072
|
]));
|
|
15842
16073
|
/**
|
|
15843
16074
|
* What starts a run.
|
|
@@ -15878,7 +16109,34 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
|
|
|
15878
16109
|
kind: literal("schedule"),
|
|
15879
16110
|
cron: string().min(1).max(120)
|
|
15880
16111
|
}),
|
|
15881
|
-
object({ kind: literal("manual") })
|
|
16112
|
+
object({ kind: literal("manual") }),
|
|
16113
|
+
object({
|
|
16114
|
+
kind: literal("detection"),
|
|
16115
|
+
/** Class names. A macro class (`animal`, `vehicle`, `person`, `package`)
|
|
16116
|
+
* matches every raw class that maps to it; any other name matches
|
|
16117
|
+
* exactly. */
|
|
16118
|
+
classes: array(string().min(1)).min(1),
|
|
16119
|
+
deviceIds: array(number().int().nonnegative()).min(1),
|
|
16120
|
+
/** The track must have visited at least one of these zones. */
|
|
16121
|
+
zones: array(string().min(1)).min(1).optional(),
|
|
16122
|
+
/** Best-so-far confidence the track must reach. */
|
|
16123
|
+
minScore: number().min(0).max(1).optional()
|
|
16124
|
+
}),
|
|
16125
|
+
object({
|
|
16126
|
+
kind: literal("stationary"),
|
|
16127
|
+
deviceIds: array(number().int().nonnegative()).min(1),
|
|
16128
|
+
/** Class names; a macro class matches like the detection trigger. */
|
|
16129
|
+
classes: array(string().min(1)).min(1).optional(),
|
|
16130
|
+
/** The parked object must be in at least one of these zones. */
|
|
16131
|
+
zones: array(string().min(1)).min(1).optional(),
|
|
16132
|
+
/** `appeared`: it became parked. `departed`: the registry retired it.
|
|
16133
|
+
* The vocabulary of `PipelineAnalyticsStationaryChanged`. */
|
|
16134
|
+
phase: _enum(["appeared", "departed"])
|
|
16135
|
+
}),
|
|
16136
|
+
object({
|
|
16137
|
+
kind: literal("interval"),
|
|
16138
|
+
minutes: number().int().min(1).max(1440)
|
|
16139
|
+
})
|
|
15882
16140
|
]);
|
|
15883
16141
|
/**
|
|
15884
16142
|
* One action step.
|
|
@@ -15898,28 +16156,47 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
|
|
|
15898
16156
|
* is called. The runner still receives a number and is untouched; the
|
|
15899
16157
|
* resolution is the recipe's job, not the runner's.
|
|
15900
16158
|
*/
|
|
16159
|
+
var AutomationWaitStepSchema = object({
|
|
16160
|
+
kind: literal("wait"),
|
|
16161
|
+
seconds: number().min(0).max(300)
|
|
16162
|
+
});
|
|
16163
|
+
var AutomationCapStepSchema = object({
|
|
16164
|
+
kind: literal("cap"),
|
|
16165
|
+
/**
|
|
16166
|
+
* One device, several same-family devices (locks / climate family / …),
|
|
16167
|
+
* or `{ $var }` for the device that triggered the run. An array is
|
|
16168
|
+
* fan-out at codegen — each id gets the same cap.method call.
|
|
16169
|
+
*/
|
|
16170
|
+
deviceId: union([
|
|
16171
|
+
number().int(),
|
|
16172
|
+
array(number().int()).min(1),
|
|
16173
|
+
object({ $var: string().min(1) })
|
|
16174
|
+
]),
|
|
16175
|
+
cap: string().min(1),
|
|
16176
|
+
method: string().min(1),
|
|
16177
|
+
/** Values may carry `{{vars.x}}` slots, or be exactly `{ $var: 'name' }`, which
|
|
16178
|
+
* SUBSTITUTE and do not evaluate (§3.2.3). Anything beyond substitution is
|
|
16179
|
+
* the expression leaf. */
|
|
16180
|
+
args: record(string(), unknown()).optional()
|
|
16181
|
+
});
|
|
16182
|
+
/**
|
|
16183
|
+
* The steps an internal Action may contain: cap and wait, and nothing that can
|
|
16184
|
+
* call back into an Action — so an Action can never reach itself (no cycles).
|
|
16185
|
+
*/
|
|
16186
|
+
var ActionStepSchema = discriminatedUnion("kind", [AutomationWaitStepSchema, AutomationCapStepSchema]);
|
|
15901
16187
|
var AutomationActionSchema = discriminatedUnion("kind", [
|
|
16188
|
+
AutomationWaitStepSchema,
|
|
16189
|
+
AutomationCapStepSchema,
|
|
15902
16190
|
object({
|
|
15903
|
-
kind: literal("
|
|
15904
|
-
|
|
15905
|
-
|
|
15906
|
-
|
|
15907
|
-
|
|
15908
|
-
|
|
15909
|
-
|
|
15910
|
-
|
|
15911
|
-
|
|
15912
|
-
*/
|
|
15913
|
-
deviceId: union([
|
|
15914
|
-
number().int(),
|
|
15915
|
-
array(number().int()).min(1),
|
|
15916
|
-
object({ $var: string().min(1) })
|
|
15917
|
-
]),
|
|
15918
|
-
cap: string().min(1),
|
|
15919
|
-
method: string().min(1),
|
|
15920
|
-
/** Values may carry `{{vars.x}}` slots, which SUBSTITUTE and do not
|
|
15921
|
-
* evaluate (§3.2.3). Anything beyond substitution is the expression leaf. */
|
|
15922
|
-
args: record(string(), unknown()).optional()
|
|
16191
|
+
kind: literal("action"),
|
|
16192
|
+
/** An entry of the `actions` surface (`internal:<uuid>` or `<addonId>:<localId>`). */
|
|
16193
|
+
actionId: string().min(1),
|
|
16194
|
+
/** Values for the action's declared variables; the others take their default. */
|
|
16195
|
+
variables: record(string(), union([
|
|
16196
|
+
string(),
|
|
16197
|
+
number(),
|
|
16198
|
+
boolean()
|
|
16199
|
+
])).optional()
|
|
15923
16200
|
}),
|
|
15924
16201
|
object({
|
|
15925
16202
|
kind: literal("code"),
|
|
@@ -15928,10 +16205,357 @@ var AutomationActionSchema = discriminatedUnion("kind", [
|
|
|
15928
16205
|
code: string().min(1).max(2e4)
|
|
15929
16206
|
})
|
|
15930
16207
|
]);
|
|
16208
|
+
/**
|
|
16209
|
+
* How a trigger that arrives while the automation is busy (or cooling down) is
|
|
16210
|
+
* handled. One of the automation's execution modes, never a property of its
|
|
16211
|
+
* actions. Absent means `single`.
|
|
16212
|
+
*
|
|
16213
|
+
* - `single` — drop the trigger while a run is in progress (traced + logged).
|
|
16214
|
+
* - `restart` — abort the run in progress (at its next step boundary), start fresh.
|
|
16215
|
+
* - `queued` — queue up to `max` triggers, drop the rest (traced).
|
|
16216
|
+
* - `parallel` — up to `max` runs at once, drop the rest (traced).
|
|
16217
|
+
* - `cooldown` — `single`, and no new run until `minutes` after the last run
|
|
16218
|
+
* STARTED; persisted across a restart. A manual trigger bypasses only this
|
|
16219
|
+
* wait, never the single/queued limits.
|
|
16220
|
+
*/
|
|
16221
|
+
var AutomationRunModeSchema = discriminatedUnion("mode", [
|
|
16222
|
+
object({ mode: literal("single") }),
|
|
16223
|
+
object({ mode: literal("restart") }),
|
|
16224
|
+
object({
|
|
16225
|
+
mode: literal("queued"),
|
|
16226
|
+
max: number().int().min(1).max(10)
|
|
16227
|
+
}),
|
|
16228
|
+
object({
|
|
16229
|
+
mode: literal("parallel"),
|
|
16230
|
+
max: number().int().min(1).max(10)
|
|
16231
|
+
}),
|
|
16232
|
+
object({
|
|
16233
|
+
mode: literal("cooldown"),
|
|
16234
|
+
minutes: number().int().min(1).max(1440)
|
|
16235
|
+
})
|
|
16236
|
+
]);
|
|
15931
16237
|
var AutomationRecipeSchema = object({
|
|
15932
16238
|
triggers: array(AutomationTriggerSchema),
|
|
15933
16239
|
conditions: AutomationConditionSchema.optional(),
|
|
15934
|
-
actions: array(AutomationActionSchema)
|
|
16240
|
+
actions: array(AutomationActionSchema),
|
|
16241
|
+
runMode: AutomationRunModeSchema.optional(),
|
|
16242
|
+
/** How many run traces the automation keeps (default {@link DEFAULT_TRACE_KEEP}). */
|
|
16243
|
+
keepTraces: number().int().min(1).max(100).optional()
|
|
16244
|
+
});
|
|
16245
|
+
new Set([
|
|
16246
|
+
{
|
|
16247
|
+
id: "person",
|
|
16248
|
+
name: "Person"
|
|
16249
|
+
},
|
|
16250
|
+
{
|
|
16251
|
+
id: "vehicle",
|
|
16252
|
+
name: "Vehicle"
|
|
16253
|
+
},
|
|
16254
|
+
{
|
|
16255
|
+
id: "animal",
|
|
16256
|
+
name: "Animal"
|
|
16257
|
+
},
|
|
16258
|
+
{
|
|
16259
|
+
id: "package",
|
|
16260
|
+
name: "Package"
|
|
16261
|
+
}
|
|
16262
|
+
].map((l) => l.id));
|
|
16263
|
+
var COCO_TO_MACRO = {
|
|
16264
|
+
mapping: {
|
|
16265
|
+
person: "person",
|
|
16266
|
+
bicycle: "vehicle",
|
|
16267
|
+
car: "vehicle",
|
|
16268
|
+
motorcycle: "vehicle",
|
|
16269
|
+
airplane: "vehicle",
|
|
16270
|
+
bus: "vehicle",
|
|
16271
|
+
train: "vehicle",
|
|
16272
|
+
truck: "vehicle",
|
|
16273
|
+
boat: "vehicle",
|
|
16274
|
+
bird: "animal",
|
|
16275
|
+
cat: "animal",
|
|
16276
|
+
dog: "animal",
|
|
16277
|
+
horse: "animal",
|
|
16278
|
+
sheep: "animal",
|
|
16279
|
+
cow: "animal",
|
|
16280
|
+
elephant: "animal",
|
|
16281
|
+
bear: "animal",
|
|
16282
|
+
zebra: "animal",
|
|
16283
|
+
giraffe: "animal",
|
|
16284
|
+
suitcase: "package",
|
|
16285
|
+
backpack: "package",
|
|
16286
|
+
handbag: "package"
|
|
16287
|
+
},
|
|
16288
|
+
preserveOriginal: false
|
|
16289
|
+
};
|
|
16290
|
+
new Set(Object.values(COCO_TO_MACRO.mapping));
|
|
16291
|
+
/**
|
|
16292
|
+
* Run traces — what an automation (or, later, an Action run) DID, node by node,
|
|
16293
|
+
* so the editor can paint every block green or red with its reason.
|
|
16294
|
+
*
|
|
16295
|
+
* A trace is data, not a log: it covers the WHOLE tree — the trigger stage,
|
|
16296
|
+
* every condition node (containers and leaves, including the ones a
|
|
16297
|
+
* short-circuit never reached), the run-mode verdict and every action step —
|
|
16298
|
+
* each addressed by a stable path (`conditions.0.1`, `actions.2`, or `steps.N`
|
|
16299
|
+
* for an Action run). Skipped
|
|
16300
|
+
* evaluations are traced too, and identical consecutive skips collapse into a
|
|
16301
|
+
* repeat count so a trigger that fails every 5 minutes cannot flush real runs.
|
|
16302
|
+
*
|
|
16303
|
+
* {@link AutomationStepTraceSchema} is deliberately free of automation
|
|
16304
|
+
* vocabulary so an Action run records the same step shape.
|
|
16305
|
+
*/
|
|
16306
|
+
/** A value a trace may show: JSON primitives and short lists, nothing deeper. */
|
|
16307
|
+
var TraceValueSchema = union([
|
|
16308
|
+
string(),
|
|
16309
|
+
number(),
|
|
16310
|
+
boolean(),
|
|
16311
|
+
_null(),
|
|
16312
|
+
array(union([string(), number()]))
|
|
16313
|
+
]);
|
|
16314
|
+
var AutomationRunOutcomeSchema = _enum([
|
|
16315
|
+
"ran",
|
|
16316
|
+
"skipped-trigger",
|
|
16317
|
+
"skipped-conditions",
|
|
16318
|
+
"skipped-cooldown",
|
|
16319
|
+
"skipped-overlap",
|
|
16320
|
+
"failed",
|
|
16321
|
+
"aborted"
|
|
16322
|
+
]);
|
|
16323
|
+
/** `not-evaluated` = a short-circuit never reached the node. */
|
|
16324
|
+
var TraceNodeOutcomeSchema = _enum([
|
|
16325
|
+
"pass",
|
|
16326
|
+
"fail",
|
|
16327
|
+
"not-evaluated"
|
|
16328
|
+
]);
|
|
16329
|
+
var AutomationTriggerTraceSchema = object({
|
|
16330
|
+
kind: string().min(1),
|
|
16331
|
+
outcome: _enum(["pass", "fail"]),
|
|
16332
|
+
/** Why an event was rejected ("class person not in [dog]") or what fired. */
|
|
16333
|
+
reason: string(),
|
|
16334
|
+
/** deviceId, className, trackId, score … — only what the trigger exposes. */
|
|
16335
|
+
vars: record(string(), TraceValueSchema)
|
|
16336
|
+
});
|
|
16337
|
+
var AutomationConditionTraceNodeSchema = object({
|
|
16338
|
+
/** `conditions` for the root, `conditions.<i>` for its i-th child, … */
|
|
16339
|
+
path: string().min(1),
|
|
16340
|
+
kind: _enum([
|
|
16341
|
+
"all",
|
|
16342
|
+
"any",
|
|
16343
|
+
"not",
|
|
16344
|
+
"condition",
|
|
16345
|
+
"expression",
|
|
16346
|
+
"stationary-present"
|
|
16347
|
+
]),
|
|
16348
|
+
outcome: TraceNodeOutcomeSchema,
|
|
16349
|
+
reason: string(),
|
|
16350
|
+
deviceId: number().int().optional(),
|
|
16351
|
+
cap: string().optional(),
|
|
16352
|
+
fieldPath: string().optional(),
|
|
16353
|
+
operator: string().optional(),
|
|
16354
|
+
expected: TraceValueSchema.optional(),
|
|
16355
|
+
/** What the mirror held; `null` when it held nothing (unknown). */
|
|
16356
|
+
observed: TraceValueSchema.optional()
|
|
16357
|
+
});
|
|
16358
|
+
var AutomationRunVerdictSchema = object({
|
|
16359
|
+
mode: AutomationRunModeSchema,
|
|
16360
|
+
decision: _enum([
|
|
16361
|
+
"run",
|
|
16362
|
+
"queued",
|
|
16363
|
+
"restart",
|
|
16364
|
+
"drop",
|
|
16365
|
+
"cooldown",
|
|
16366
|
+
"bypass"
|
|
16367
|
+
]),
|
|
16368
|
+
reason: string()
|
|
16369
|
+
});
|
|
16370
|
+
/** One executed (or not-run) step — shared by automations and Action runs. */
|
|
16371
|
+
var AutomationStepTraceSchema = object({
|
|
16372
|
+
/** `actions.<i>` */
|
|
16373
|
+
path: string().min(1),
|
|
16374
|
+
kind: string().min(1),
|
|
16375
|
+
cap: string().optional(),
|
|
16376
|
+
method: string().optional(),
|
|
16377
|
+
deviceId: number().int().optional(),
|
|
16378
|
+
startedAt: number().optional(),
|
|
16379
|
+
endedAt: number().optional(),
|
|
16380
|
+
outcome: _enum([
|
|
16381
|
+
"ok",
|
|
16382
|
+
"error",
|
|
16383
|
+
"not-run"
|
|
16384
|
+
]),
|
|
16385
|
+
message: string().optional()
|
|
16386
|
+
});
|
|
16387
|
+
var AutomationRunTraceSchema = object({
|
|
16388
|
+
runId: string().min(1),
|
|
16389
|
+
startedAt: number(),
|
|
16390
|
+
endedAt: number(),
|
|
16391
|
+
trigger: AutomationTriggerTraceSchema,
|
|
16392
|
+
conditions: array(AutomationConditionTraceNodeSchema),
|
|
16393
|
+
verdict: AutomationRunVerdictSchema.optional(),
|
|
16394
|
+
steps: array(AutomationStepTraceSchema),
|
|
16395
|
+
outcome: AutomationRunOutcomeSchema,
|
|
16396
|
+
/** How many identical consecutive skips this trace stands for (>= 1). */
|
|
16397
|
+
repeat: number().int().min(1),
|
|
16398
|
+
/** When the latest of those repeats happened. */
|
|
16399
|
+
lastAt: number()
|
|
16400
|
+
});
|
|
16401
|
+
/**
|
|
16402
|
+
* What the hub's `runs` ledger holds for one owner (`automation:<blockId>` or
|
|
16403
|
+
* `action:<actionId>`): the cooldown stamp and the trace ring.
|
|
16404
|
+
*/
|
|
16405
|
+
var AutomationStateDocSchema = object({
|
|
16406
|
+
version: literal(1),
|
|
16407
|
+
lastRunStartedAt: number().nullable(),
|
|
16408
|
+
traces: array(AutomationRunTraceSchema)
|
|
16409
|
+
});
|
|
16410
|
+
/**
|
|
16411
|
+
* Actions — named, runnable things an operator (or an automation) can start,
|
|
16412
|
+
* declared as LIST ENTRIES, never as devices.
|
|
16413
|
+
*
|
|
16414
|
+
* An entry is either **internal** (a short list of cap/wait steps kept in the
|
|
16415
|
+
* hub's `actions` record kind) or declared by an addon through the
|
|
16416
|
+
* `action-source` collection (a Home Assistant script, a scene …). The one
|
|
16417
|
+
* surface the UI and automations talk to is the `actions` cap; it keeps no
|
|
16418
|
+
* catalog of its own (D3) — every `list` asks the providers live.
|
|
16419
|
+
*
|
|
16420
|
+
* This file is the data half: the entry schema, the variables an entry may
|
|
16421
|
+
* declare, and the pure resolution of the values a run was given. It holds no
|
|
16422
|
+
* runtime.
|
|
16423
|
+
*/
|
|
16424
|
+
/** What a variable's value is. `select` takes one of `options`. */
|
|
16425
|
+
var ActionVariableTypeSchema = _enum([
|
|
16426
|
+
"string",
|
|
16427
|
+
"number",
|
|
16428
|
+
"boolean",
|
|
16429
|
+
"select"
|
|
16430
|
+
]);
|
|
16431
|
+
var ACTION_VARIABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
16432
|
+
var ActionVariableValueSchema = union([
|
|
16433
|
+
string(),
|
|
16434
|
+
number(),
|
|
16435
|
+
boolean()
|
|
16436
|
+
]);
|
|
16437
|
+
/**
|
|
16438
|
+
* One declared variable. Steps reference it as `{ $var: 'name' }` (the whole
|
|
16439
|
+
* value) or `{{vars.name}}` (inside a string) in their args.
|
|
16440
|
+
*/
|
|
16441
|
+
var ActionVariableSchema = object({
|
|
16442
|
+
name: string().regex(ACTION_VARIABLE_NAME_RE),
|
|
16443
|
+
label: string().min(1).max(120).optional(),
|
|
16444
|
+
type: ActionVariableTypeSchema,
|
|
16445
|
+
required: boolean().optional(),
|
|
16446
|
+
default: ActionVariableValueSchema.optional(),
|
|
16447
|
+
/** The allowed values of a `select`. */
|
|
16448
|
+
options: array(string().min(1)).min(1).optional()
|
|
16449
|
+
}).superRefine((v, ctx) => {
|
|
16450
|
+
if (v.type === "select" && v.options === void 0) ctx.addIssue({
|
|
16451
|
+
code: "custom",
|
|
16452
|
+
message: "a select variable needs options",
|
|
16453
|
+
path: ["options"]
|
|
16454
|
+
});
|
|
16455
|
+
if (v.default !== void 0 && variableValueError(v, v.default) !== null) ctx.addIssue({
|
|
16456
|
+
code: "custom",
|
|
16457
|
+
message: variableValueError(v, v.default) ?? "invalid default",
|
|
16458
|
+
path: ["default"]
|
|
16459
|
+
});
|
|
16460
|
+
});
|
|
16461
|
+
/** Why `value` is not a legal value for the variable, or null when it is. */
|
|
16462
|
+
function variableValueError(variable, value) {
|
|
16463
|
+
switch (variable.type) {
|
|
16464
|
+
case "string": return typeof value === "string" ? null : `variable "${variable.name}" must be a string`;
|
|
16465
|
+
case "number": return typeof value === "number" && Number.isFinite(value) ? null : `variable "${variable.name}" must be a number`;
|
|
16466
|
+
case "boolean": return typeof value === "boolean" ? null : `variable "${variable.name}" must be true or false`;
|
|
16467
|
+
case "select": return typeof value === "string" && (variable.options ?? []).includes(value) ? null : `variable "${variable.name}" must be one of ${(variable.options ?? []).join(", ")}`;
|
|
16468
|
+
}
|
|
16469
|
+
}
|
|
16470
|
+
var ActionSourceKindSchema = _enum(["internal", "addon"]);
|
|
16471
|
+
function isInternalActionId(actionId) {
|
|
16472
|
+
return actionId.startsWith("internal:") && actionId.length > 9;
|
|
16473
|
+
}
|
|
16474
|
+
/** The addon behind a `<addonId>:<localId>` id, or null for anything else (internal included). */
|
|
16475
|
+
function addonIdOfActionId(actionId) {
|
|
16476
|
+
if (isInternalActionId(actionId)) return null;
|
|
16477
|
+
const at = actionId.indexOf(":");
|
|
16478
|
+
return at > 0 && at < actionId.length - 1 ? actionId.slice(0, at) : null;
|
|
16479
|
+
}
|
|
16480
|
+
var ActionStatusSchema = object({
|
|
16481
|
+
isRunning: boolean(),
|
|
16482
|
+
/** Ms epoch the latest run STARTED; null when it never ran. */
|
|
16483
|
+
lastRunAt: number().nullable(),
|
|
16484
|
+
/** The latest run's failure; null on success or when it never ran. */
|
|
16485
|
+
lastError: string().nullable()
|
|
16486
|
+
});
|
|
16487
|
+
var ActionEntrySchema = object({
|
|
16488
|
+
/** `internal:<uuid>` or `<addonId>:<localId>`. */
|
|
16489
|
+
id: string().min(3),
|
|
16490
|
+
name: string().min(1).max(120),
|
|
16491
|
+
/** The addon that declared it; `core-blocks` for an internal one. */
|
|
16492
|
+
addonId: string().min(1),
|
|
16493
|
+
source: ActionSourceKindSchema,
|
|
16494
|
+
/** An addon-declared entry is edited where it was declared. */
|
|
16495
|
+
readOnly: boolean(),
|
|
16496
|
+
/** Internal only: cap and wait steps. No action-in-action, so no cycles. */
|
|
16497
|
+
steps: array(ActionStepSchema).min(1).max(50).optional(),
|
|
16498
|
+
variables: array(ActionVariableSchema).max(20).optional(),
|
|
16499
|
+
status: ActionStatusSchema
|
|
16500
|
+
}).superRefine((entry, ctx) => {
|
|
16501
|
+
if (entry.source === "internal") {
|
|
16502
|
+
if (!isInternalActionId(entry.id)) ctx.addIssue({
|
|
16503
|
+
code: "custom",
|
|
16504
|
+
message: "an internal action id is internal:<uuid>",
|
|
16505
|
+
path: ["id"]
|
|
16506
|
+
});
|
|
16507
|
+
return;
|
|
16508
|
+
}
|
|
16509
|
+
if (entry.steps !== void 0) ctx.addIssue({
|
|
16510
|
+
code: "custom",
|
|
16511
|
+
message: "only an internal action carries steps",
|
|
16512
|
+
path: ["steps"]
|
|
16513
|
+
});
|
|
16514
|
+
if (!entry.readOnly) ctx.addIssue({
|
|
16515
|
+
code: "custom",
|
|
16516
|
+
message: "an addon action is read-only here",
|
|
16517
|
+
path: ["readOnly"]
|
|
16518
|
+
});
|
|
16519
|
+
if (addonIdOfActionId(entry.id) === null) ctx.addIssue({
|
|
16520
|
+
code: "custom",
|
|
16521
|
+
message: "an addon action id is <addonId>:<localId>",
|
|
16522
|
+
path: ["id"]
|
|
16523
|
+
});
|
|
16524
|
+
});
|
|
16525
|
+
/** What an operator writes for an internal action. */
|
|
16526
|
+
var ActionInputSchema = object({
|
|
16527
|
+
name: string().min(1).max(120),
|
|
16528
|
+
steps: array(ActionStepSchema).min(1).max(50),
|
|
16529
|
+
variables: array(ActionVariableSchema).max(20).optional()
|
|
16530
|
+
});
|
|
16531
|
+
/** Who started a run. Absent = the operator pressed it. */
|
|
16532
|
+
var ActionRunOriginSchema = object({
|
|
16533
|
+
/** The automation block whose step started it. */
|
|
16534
|
+
blockId: string().min(1).optional() });
|
|
16535
|
+
/** `started` = the caller did not ask to wait. */
|
|
16536
|
+
var ActionRunOutcomeSchema = union([AutomationRunOutcomeSchema, literal("started")]);
|
|
16537
|
+
var ActionRunResultSchema = object({
|
|
16538
|
+
runId: string().min(1),
|
|
16539
|
+
outcome: ActionRunOutcomeSchema,
|
|
16540
|
+
/** Why it did not run, or what failed. */
|
|
16541
|
+
message: string().optional()
|
|
16542
|
+
});
|
|
16543
|
+
_enum([
|
|
16544
|
+
"not-found",
|
|
16545
|
+
"read-only",
|
|
16546
|
+
"invalid-variables",
|
|
16547
|
+
"invalid-steps",
|
|
16548
|
+
"not-linked",
|
|
16549
|
+
"busy"
|
|
16550
|
+
]);
|
|
16551
|
+
object({
|
|
16552
|
+
actionId: string(),
|
|
16553
|
+
runId: string(),
|
|
16554
|
+
isRunning: boolean(),
|
|
16555
|
+
/** Set when the run ended. */
|
|
16556
|
+
outcome: AutomationRunOutcomeSchema.optional(),
|
|
16557
|
+
lastRunAt: number().nullable(),
|
|
16558
|
+
lastError: string().nullable()
|
|
15935
16559
|
});
|
|
15936
16560
|
/**
|
|
15937
16561
|
* `addon-pages` — system-scoped singleton aggregator cap. Public-facing
|
|
@@ -17526,7 +18150,22 @@ var BrokerProviderInfoSchema = object({
|
|
|
17526
18150
|
*/
|
|
17527
18151
|
interactiveConnectPath: string().min(1).optional(),
|
|
17528
18152
|
/** Relative hub path to disconnect the linked account (same popup pattern). */
|
|
17529
|
-
interactiveDisconnectPath: string().min(1).optional()
|
|
18153
|
+
interactiveDisconnectPath: string().min(1).optional(),
|
|
18154
|
+
/**
|
|
18155
|
+
* The kind signs in through a server-held interactive session instead of
|
|
18156
|
+
* (or before) typed credentials. `basePath` is a hub-relative route set
|
|
18157
|
+
* served by the owning addon (`/addon/<id>/...`) speaking the sign-in
|
|
18158
|
+
* contract: `POST {basePath}/start` (body: the form's settings) answers
|
|
18159
|
+
* `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
|
|
18160
|
+
* `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
|
|
18161
|
+
* 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
|
|
18162
|
+
* (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
|
|
18163
|
+
* existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
|
|
18164
|
+
* (`{ sessionId }`) drops the session. The dialog that edits a broker
|
|
18165
|
+
* offers it as the default path; the fields the session fills are
|
|
18166
|
+
* marked `filledBySignIn` in the kind's settings schema.
|
|
18167
|
+
*/
|
|
18168
|
+
signIn: object({ basePath: string().min(1) }).optional()
|
|
17530
18169
|
}))
|
|
17531
18170
|
});
|
|
17532
18171
|
var ListInputSchema = object({
|
|
@@ -18603,7 +19242,7 @@ var CompositionEventPlanSchema = object({
|
|
|
18603
19242
|
stateField: string(),
|
|
18604
19243
|
fieldKey: string()
|
|
18605
19244
|
});
|
|
18606
|
-
/** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.
|
|
19245
|
+
/** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.temperature`. */
|
|
18607
19246
|
var CompositionProblemSchema = object({
|
|
18608
19247
|
code: _enum([
|
|
18609
19248
|
"reserved-variant",
|
|
@@ -18631,7 +19270,8 @@ var CompositionProblemSchema = object({
|
|
|
18631
19270
|
"native-capability-replace",
|
|
18632
19271
|
"mixed-quantity",
|
|
18633
19272
|
"helper-mismatch",
|
|
18634
|
-
"member-constraint"
|
|
19273
|
+
"member-constraint",
|
|
19274
|
+
"retention"
|
|
18635
19275
|
]),
|
|
18636
19276
|
path: string(),
|
|
18637
19277
|
message: string()
|
|
@@ -18865,13 +19505,17 @@ Object.freeze({
|
|
|
18865
19505
|
latchedAt: {
|
|
18866
19506
|
kind: "fixed",
|
|
18867
19507
|
result: ["number", "null"]
|
|
19508
|
+
},
|
|
19509
|
+
schmitt: {
|
|
19510
|
+
kind: "fixed",
|
|
19511
|
+
result: ["boolean", "null"]
|
|
18868
19512
|
}
|
|
18869
19513
|
});
|
|
18870
19514
|
/**
|
|
18871
19515
|
* One catalog row per base reading a group helper can build (D944 §3).
|
|
18872
19516
|
* Data, not a form default written into the block.
|
|
18873
19517
|
*/
|
|
18874
|
-
var NOW = {
|
|
19518
|
+
var NOW$2 = {
|
|
18875
19519
|
fieldPath: "lastFetchedAt",
|
|
18876
19520
|
expr: "now"
|
|
18877
19521
|
};
|
|
@@ -18885,6 +19529,8 @@ function numeric(input) {
|
|
|
18885
19529
|
family: "numeric",
|
|
18886
19530
|
deviceType: DeviceType.Sensor,
|
|
18887
19531
|
memberDeviceType: DeviceType.Sensor,
|
|
19532
|
+
memberDeviceTypes: [DeviceType.Sensor],
|
|
19533
|
+
activeWhen: void 0,
|
|
18888
19534
|
commandCaps: [],
|
|
18889
19535
|
baseUnit: baseUnitOf(input.quantity),
|
|
18890
19536
|
aggregates: NUMERIC_AGGREGATES,
|
|
@@ -18900,6 +19546,8 @@ function binary(input) {
|
|
|
18900
19546
|
family: "binary",
|
|
18901
19547
|
deviceType: DeviceType.Sensor,
|
|
18902
19548
|
memberDeviceType: DeviceType.Sensor,
|
|
19549
|
+
memberDeviceTypes: [DeviceType.Sensor],
|
|
19550
|
+
activeWhen: void 0,
|
|
18903
19551
|
commandCaps: [],
|
|
18904
19552
|
quantity: "none",
|
|
18905
19553
|
baseUnit: void 0,
|
|
@@ -18913,73 +19561,73 @@ var GROUP_TEMPLATES = [
|
|
|
18913
19561
|
summary: "Mean, median, min, max or sum of temperatures. Offline and unknown members are left out, never read as 0 °C.",
|
|
18914
19562
|
role: DeviceRole.TemperatureSensor,
|
|
18915
19563
|
outputCap: "temperature-sensor",
|
|
18916
|
-
outputField: "
|
|
19564
|
+
outputField: "temperature",
|
|
18917
19565
|
quantity: "temperature",
|
|
18918
19566
|
defaultAggregate: "mean",
|
|
18919
19567
|
empty: "null",
|
|
18920
|
-
companions: [NOW]
|
|
19568
|
+
companions: [NOW$2]
|
|
18921
19569
|
}),
|
|
18922
19570
|
numeric({
|
|
18923
19571
|
id: "humidity",
|
|
18924
19572
|
summary: "Humidity in percent. Offline and unknown members are left out, never read as 0 %.",
|
|
18925
19573
|
role: DeviceRole.HumiditySensor,
|
|
18926
19574
|
outputCap: "humidity-sensor",
|
|
18927
|
-
outputField: "
|
|
19575
|
+
outputField: "humidity",
|
|
18928
19576
|
quantity: "humidity",
|
|
18929
19577
|
defaultAggregate: "mean",
|
|
18930
19578
|
empty: "null",
|
|
18931
|
-
companions: [NOW]
|
|
19579
|
+
companions: [NOW$2]
|
|
18932
19580
|
}),
|
|
18933
19581
|
numeric({
|
|
18934
19582
|
id: "pressure",
|
|
18935
19583
|
summary: "Pressure in hectopascals. Offline and unknown members are left out, never read as 0 hPa.",
|
|
18936
19584
|
role: DeviceRole.PressureSensor,
|
|
18937
19585
|
outputCap: "pressure-sensor",
|
|
18938
|
-
outputField: "
|
|
19586
|
+
outputField: "pressure",
|
|
18939
19587
|
quantity: "pressure",
|
|
18940
19588
|
defaultAggregate: "mean",
|
|
18941
19589
|
empty: "null",
|
|
18942
|
-
companions: [NOW]
|
|
19590
|
+
companions: [NOW$2]
|
|
18943
19591
|
}),
|
|
18944
19592
|
numeric({
|
|
18945
19593
|
id: "illuminance",
|
|
18946
19594
|
summary: "Illuminance in lux. Offline and unknown members are left out, never read as 0 lx.",
|
|
18947
19595
|
role: DeviceRole.AmbientLightSensor,
|
|
18948
19596
|
outputCap: "ambient-light-sensor",
|
|
18949
|
-
outputField: "
|
|
19597
|
+
outputField: "illuminance",
|
|
18950
19598
|
quantity: "illuminance",
|
|
18951
19599
|
defaultAggregate: "mean",
|
|
18952
19600
|
empty: "null",
|
|
18953
|
-
companions: [NOW]
|
|
19601
|
+
companions: [NOW$2]
|
|
18954
19602
|
}),
|
|
18955
19603
|
numeric({
|
|
18956
19604
|
id: "power",
|
|
18957
19605
|
summary: "Power in watts, summed by default. Nobody reporting omits the key rather than writing 0 W.",
|
|
18958
19606
|
role: DeviceRole.PowerSensor,
|
|
18959
19607
|
outputCap: "power-meter",
|
|
18960
|
-
outputField: "
|
|
19608
|
+
outputField: "power",
|
|
18961
19609
|
quantity: "power",
|
|
18962
19610
|
defaultAggregate: "sum",
|
|
18963
19611
|
empty: "omit",
|
|
18964
|
-
companions: [NOW]
|
|
19612
|
+
companions: [NOW$2]
|
|
18965
19613
|
}),
|
|
18966
19614
|
numeric({
|
|
18967
19615
|
id: "energy",
|
|
18968
19616
|
summary: "Energy in kilowatt-hours, summed by default. Nobody reporting omits the key rather than writing 0 kWh.",
|
|
18969
19617
|
role: DeviceRole.EnergySensor,
|
|
18970
19618
|
outputCap: "power-meter",
|
|
18971
|
-
outputField: "
|
|
19619
|
+
outputField: "energy",
|
|
18972
19620
|
quantity: "energy",
|
|
18973
19621
|
defaultAggregate: "sum",
|
|
18974
19622
|
empty: "omit",
|
|
18975
|
-
companions: [NOW]
|
|
19623
|
+
companions: [NOW$2]
|
|
18976
19624
|
}),
|
|
18977
19625
|
numeric({
|
|
18978
19626
|
id: "battery",
|
|
18979
19627
|
summary: "Battery charge in percent. Offline and unknown members are left out, never read as 0 %.",
|
|
18980
19628
|
role: DeviceRole.BatterySensor,
|
|
18981
19629
|
outputCap: "battery",
|
|
18982
|
-
outputField: "
|
|
19630
|
+
outputField: "level",
|
|
18983
19631
|
quantity: "percentage",
|
|
18984
19632
|
defaultAggregate: "mean",
|
|
18985
19633
|
empty: "null",
|
|
@@ -19007,7 +19655,7 @@ var GROUP_TEMPLATES = [
|
|
|
19007
19655
|
quantity: "device",
|
|
19008
19656
|
defaultAggregate: "mean",
|
|
19009
19657
|
empty: "null",
|
|
19010
|
-
companions: [NOW]
|
|
19658
|
+
companions: [NOW$2]
|
|
19011
19659
|
}),
|
|
19012
19660
|
binary({
|
|
19013
19661
|
id: "motion",
|
|
@@ -19101,6 +19749,11 @@ var GROUP_TEMPLATES = [
|
|
|
19101
19749
|
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.",
|
|
19102
19750
|
deviceType: DeviceType.Switch,
|
|
19103
19751
|
outputCap: "switch",
|
|
19752
|
+
outputField: "on",
|
|
19753
|
+
member: {
|
|
19754
|
+
cap: "switch",
|
|
19755
|
+
fieldPath: "on"
|
|
19756
|
+
},
|
|
19104
19757
|
commandCaps: ["switch"]
|
|
19105
19758
|
}),
|
|
19106
19759
|
actuator({
|
|
@@ -19108,34 +19761,136 @@ var GROUP_TEMPLATES = [
|
|
|
19108
19761
|
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.",
|
|
19109
19762
|
deviceType: DeviceType.Light,
|
|
19110
19763
|
outputCap: "switch",
|
|
19764
|
+
outputField: "on",
|
|
19765
|
+
member: {
|
|
19766
|
+
cap: "switch",
|
|
19767
|
+
fieldPath: "on"
|
|
19768
|
+
},
|
|
19111
19769
|
commandCaps: [
|
|
19112
19770
|
"switch",
|
|
19113
19771
|
"brightness",
|
|
19114
19772
|
"color"
|
|
19115
19773
|
]
|
|
19774
|
+
}),
|
|
19775
|
+
actuator({
|
|
19776
|
+
id: "fan",
|
|
19777
|
+
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.",
|
|
19778
|
+
deviceType: DeviceType.Fan,
|
|
19779
|
+
outputCap: "switch",
|
|
19780
|
+
outputField: "on",
|
|
19781
|
+
member: {
|
|
19782
|
+
cap: "switch",
|
|
19783
|
+
fieldPath: "on"
|
|
19784
|
+
},
|
|
19785
|
+
commandCaps: ["switch", "fan-control"]
|
|
19786
|
+
}),
|
|
19787
|
+
actuator({
|
|
19788
|
+
id: "cover",
|
|
19789
|
+
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.",
|
|
19790
|
+
deviceType: DeviceType.Cover,
|
|
19791
|
+
outputCap: "group-active",
|
|
19792
|
+
outputField: "active",
|
|
19793
|
+
member: {
|
|
19794
|
+
cap: "cover",
|
|
19795
|
+
fieldPath: "state"
|
|
19796
|
+
},
|
|
19797
|
+
activeWhen: [
|
|
19798
|
+
"open",
|
|
19799
|
+
"opening",
|
|
19800
|
+
"stopped"
|
|
19801
|
+
],
|
|
19802
|
+
commandCaps: ["cover"]
|
|
19803
|
+
}),
|
|
19804
|
+
actuator({
|
|
19805
|
+
id: "valve",
|
|
19806
|
+
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.",
|
|
19807
|
+
deviceType: DeviceType.Valve,
|
|
19808
|
+
outputCap: "group-active",
|
|
19809
|
+
outputField: "active",
|
|
19810
|
+
member: {
|
|
19811
|
+
cap: "valve",
|
|
19812
|
+
fieldPath: "state"
|
|
19813
|
+
},
|
|
19814
|
+
activeWhen: [
|
|
19815
|
+
"open",
|
|
19816
|
+
"opening",
|
|
19817
|
+
"stopped"
|
|
19818
|
+
],
|
|
19819
|
+
commandCaps: ["valve"]
|
|
19820
|
+
}),
|
|
19821
|
+
actuator({
|
|
19822
|
+
id: "lock",
|
|
19823
|
+
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.",
|
|
19824
|
+
deviceType: DeviceType.Lock,
|
|
19825
|
+
outputCap: "group-active",
|
|
19826
|
+
outputField: "active",
|
|
19827
|
+
member: {
|
|
19828
|
+
cap: "lock-control",
|
|
19829
|
+
fieldPath: "state"
|
|
19830
|
+
},
|
|
19831
|
+
activeWhen: ["locked"],
|
|
19832
|
+
commandCaps: ["lock-control"]
|
|
19833
|
+
}),
|
|
19834
|
+
actuator({
|
|
19835
|
+
id: "media-player",
|
|
19836
|
+
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.",
|
|
19837
|
+
deviceType: DeviceType.MediaPlayer,
|
|
19838
|
+
outputCap: "group-active",
|
|
19839
|
+
outputField: "active",
|
|
19840
|
+
member: {
|
|
19841
|
+
cap: "media-player",
|
|
19842
|
+
fieldPath: "state"
|
|
19843
|
+
},
|
|
19844
|
+
activeWhen: ["playing", "buffering"],
|
|
19845
|
+
commandCaps: ["media-player"]
|
|
19846
|
+
}),
|
|
19847
|
+
actuator({
|
|
19848
|
+
id: "climate",
|
|
19849
|
+
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.",
|
|
19850
|
+
deviceType: DeviceType.Thermostat,
|
|
19851
|
+
memberDeviceTypes: [DeviceType.Thermostat, DeviceType.Climate],
|
|
19852
|
+
outputCap: "group-active",
|
|
19853
|
+
outputField: "active",
|
|
19854
|
+
member: {
|
|
19855
|
+
cap: "climate-control",
|
|
19856
|
+
fieldPath: "mode"
|
|
19857
|
+
},
|
|
19858
|
+
activeWhen: [
|
|
19859
|
+
"heat",
|
|
19860
|
+
"cool",
|
|
19861
|
+
"auto",
|
|
19862
|
+
"heat_cool",
|
|
19863
|
+
"fan_only",
|
|
19864
|
+
"dry"
|
|
19865
|
+
],
|
|
19866
|
+
commandCaps: ["climate-control"]
|
|
19116
19867
|
})
|
|
19117
19868
|
];
|
|
19118
19869
|
function actuator(input) {
|
|
19119
19870
|
return {
|
|
19120
|
-
|
|
19871
|
+
id: input.id,
|
|
19872
|
+
summary: input.summary,
|
|
19873
|
+
deviceType: input.deviceType,
|
|
19874
|
+
outputCap: input.outputCap,
|
|
19875
|
+
outputField: input.outputField,
|
|
19876
|
+
commandCaps: input.commandCaps,
|
|
19121
19877
|
family: "actuator",
|
|
19122
19878
|
role: void 0,
|
|
19123
19879
|
memberDeviceType: input.deviceType,
|
|
19124
|
-
|
|
19880
|
+
memberDeviceTypes: input.memberDeviceTypes ?? [input.deviceType],
|
|
19881
|
+
activeWhen: input.activeWhen,
|
|
19125
19882
|
quantity: "none",
|
|
19126
19883
|
baseUnit: void 0,
|
|
19127
19884
|
aggregates: BINARY_AGGREGATES,
|
|
19128
19885
|
defaultAggregate: "any",
|
|
19129
19886
|
empty: "offline",
|
|
19130
|
-
accept: [
|
|
19131
|
-
cap: "switch",
|
|
19132
|
-
fieldPath: "on"
|
|
19133
|
-
}],
|
|
19887
|
+
accept: [input.member],
|
|
19134
19888
|
companions: [CHANGED]
|
|
19135
19889
|
};
|
|
19136
19890
|
}
|
|
19137
19891
|
new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
|
|
19138
19892
|
DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
|
|
19893
|
+
DeviceType.Camera;
|
|
19139
19894
|
`${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
|
|
19140
19895
|
/**
|
|
19141
19896
|
* What a composition may BUILD on a device type (G2, D676): the caps a new
|
|
@@ -19450,7 +20205,18 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
|
|
|
19450
20205
|
kind: "mutation",
|
|
19451
20206
|
auth: "admin",
|
|
19452
20207
|
access: "view"
|
|
19453
|
-
})
|
|
20208
|
+
}), method(object({
|
|
20209
|
+
blockId: string().min(1),
|
|
20210
|
+
trace: AutomationRunTraceSchema.optional(),
|
|
20211
|
+
runStartedAt: number().optional(),
|
|
20212
|
+
keepTraces: number().int().min(1).max(100).optional()
|
|
20213
|
+
}), _void(), {
|
|
20214
|
+
kind: "mutation",
|
|
20215
|
+
auth: "admin"
|
|
20216
|
+
}), method(object({ blockId: string().min(1) }), AutomationStateDocSchema, { auth: "admin" }), method(object({ blockId: string().min(1) }), object({
|
|
20217
|
+
traces: array(AutomationRunTraceSchema),
|
|
20218
|
+
lastError: string().nullable()
|
|
20219
|
+
}), { auth: "admin" });
|
|
19454
20220
|
/**
|
|
19455
20221
|
* `custom-model-registry` — collection cap exposing operator-registered
|
|
19456
20222
|
* custom detection models. Each provider (today: `addon-model-studio`)
|
|
@@ -44495,7 +45261,7 @@ var NetworkAccessStatusSchema = object({
|
|
|
44495
45261
|
});
|
|
44496
45262
|
/**
|
|
44497
45263
|
* Optional, richer endpoint shape returned by providers that expose
|
|
44498
|
-
* MORE than one ingress concurrently (Tailscale
|
|
45264
|
+
* MORE than one ingress concurrently (Tailscale Funnel with mixed
|
|
44499
45265
|
* serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
|
|
44500
45266
|
* the originating provider config (mode + sourcePort) so the
|
|
44501
45267
|
* orchestrator UI can label rows distinctly. Providers that expose only
|
|
@@ -44888,51 +45654,6 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
|
|
|
44888
45654
|
kind: "mutation",
|
|
44889
45655
|
auth: "admin"
|
|
44890
45656
|
});
|
|
44891
|
-
new Set([
|
|
44892
|
-
{
|
|
44893
|
-
id: "person",
|
|
44894
|
-
name: "Person"
|
|
44895
|
-
},
|
|
44896
|
-
{
|
|
44897
|
-
id: "vehicle",
|
|
44898
|
-
name: "Vehicle"
|
|
44899
|
-
},
|
|
44900
|
-
{
|
|
44901
|
-
id: "animal",
|
|
44902
|
-
name: "Animal"
|
|
44903
|
-
},
|
|
44904
|
-
{
|
|
44905
|
-
id: "package",
|
|
44906
|
-
name: "Package"
|
|
44907
|
-
}
|
|
44908
|
-
].map((l) => l.id));
|
|
44909
|
-
var COCO_TO_MACRO = {
|
|
44910
|
-
mapping: {
|
|
44911
|
-
person: "person",
|
|
44912
|
-
bicycle: "vehicle",
|
|
44913
|
-
car: "vehicle",
|
|
44914
|
-
motorcycle: "vehicle",
|
|
44915
|
-
airplane: "vehicle",
|
|
44916
|
-
bus: "vehicle",
|
|
44917
|
-
train: "vehicle",
|
|
44918
|
-
truck: "vehicle",
|
|
44919
|
-
boat: "vehicle",
|
|
44920
|
-
bird: "animal",
|
|
44921
|
-
cat: "animal",
|
|
44922
|
-
dog: "animal",
|
|
44923
|
-
horse: "animal",
|
|
44924
|
-
sheep: "animal",
|
|
44925
|
-
cow: "animal",
|
|
44926
|
-
elephant: "animal",
|
|
44927
|
-
bear: "animal",
|
|
44928
|
-
zebra: "animal",
|
|
44929
|
-
giraffe: "animal",
|
|
44930
|
-
suitcase: "package",
|
|
44931
|
-
backpack: "package",
|
|
44932
|
-
handbag: "package"
|
|
44933
|
-
},
|
|
44934
|
-
preserveOriginal: false
|
|
44935
|
-
};
|
|
44936
45657
|
var AUDIO_MACRO_LABELS = [
|
|
44937
45658
|
{
|
|
44938
45659
|
id: "speech",
|
|
@@ -47182,7 +47903,20 @@ var NcRuleInputSchema = object({
|
|
|
47182
47903
|
* save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
|
|
47183
47904
|
* path does not run Zod defaults.
|
|
47184
47905
|
*/
|
|
47185
|
-
groupingSec: number().int().min(5).max(60).optional()
|
|
47906
|
+
groupingSec: number().int().min(5).max(60).optional(),
|
|
47907
|
+
/**
|
|
47908
|
+
* ASK ME TO CONFIRM DETECTIONS (#243): a DETECTION notification of this rule
|
|
47909
|
+
* carries "Correct" / "Wrong" buttons (and, where the channel has room, a
|
|
47910
|
+
* "Wrong, it is a <class>" choice). A tap marks the track for retraining, the
|
|
47911
|
+
* same mark the Library writes, and records whose notification it was and
|
|
47912
|
+
* when. A channel with no buttons gets the same two choices as links.
|
|
47913
|
+
*
|
|
47914
|
+
* `.optional()`, never `.default()` (a Zod default does not run on the addon
|
|
47915
|
+
* cap path): absent and `false` both mean off, which is what every rule
|
|
47916
|
+
* authored before this field meant. An editor turns it off by writing
|
|
47917
|
+
* `false`, so it needs no `clear` key.
|
|
47918
|
+
*/
|
|
47919
|
+
askVerdict: boolean().optional()
|
|
47186
47920
|
});
|
|
47187
47921
|
/**
|
|
47188
47922
|
* Partial patch for `updateRule` — any subset of the input fields, plus the
|
|
@@ -58008,9 +58742,9 @@ method(object({ nodeId: string().optional() }).optional(), array(AddonListItemSc
|
|
|
58008
58742
|
*/
|
|
58009
58743
|
var AirQualitySensorStatusSchema = object({
|
|
58010
58744
|
/** Carbon dioxide concentration in ppm. */
|
|
58011
|
-
|
|
58745
|
+
co2: number().min(0).optional().meta(readingMeta("concentration-volume")),
|
|
58012
58746
|
/** Total volatile organic compounds in ppb. */
|
|
58013
|
-
|
|
58747
|
+
voc: number().min(0).optional().meta(readingMeta("concentration-ppb")),
|
|
58014
58748
|
/** Particulate matter ≤ 2.5 μm in µg/m³. */
|
|
58015
58749
|
pm25: number().min(0).optional().meta(readingMeta("concentration-mass")),
|
|
58016
58750
|
/** Particulate matter ≤ 10 μm in µg/m³. */
|
|
@@ -58043,12 +58777,12 @@ var airQualitySensorCapability = {
|
|
|
58043
58777
|
* History (D850): recorded series.
|
|
58044
58778
|
*/
|
|
58045
58779
|
history: { fields: {
|
|
58046
|
-
|
|
58780
|
+
co2: {
|
|
58047
58781
|
kind: "gauge",
|
|
58048
58782
|
unit: "ppm",
|
|
58049
58783
|
deadband: 5
|
|
58050
58784
|
},
|
|
58051
|
-
|
|
58785
|
+
voc: {
|
|
58052
58786
|
kind: "gauge",
|
|
58053
58787
|
unit: "ppb",
|
|
58054
58788
|
deadband: 5
|
|
@@ -58079,7 +58813,7 @@ var airQualitySensorCapability = {
|
|
|
58079
58813
|
*/
|
|
58080
58814
|
var AmbientLightSensorStatusSchema = object({
|
|
58081
58815
|
/** Current illuminance in lux (lx). `null` when the sample was refused (D393). */
|
|
58082
|
-
|
|
58816
|
+
illuminance: number().min(0).nullable().meta(readingMeta("illuminance", {
|
|
58083
58817
|
precision: 0,
|
|
58084
58818
|
label: "Illuminance"
|
|
58085
58819
|
})),
|
|
@@ -58108,7 +58842,7 @@ var ambientLightSensorCapability = {
|
|
|
58108
58842
|
/**
|
|
58109
58843
|
* History (D850): recorded series. Wide range, so a RELATIVE deadband (10 %).
|
|
58110
58844
|
*/
|
|
58111
|
-
history: { fields: {
|
|
58845
|
+
history: { fields: { illuminance: {
|
|
58112
58846
|
kind: "gauge",
|
|
58113
58847
|
unit: "lx",
|
|
58114
58848
|
deadbandPct: 10,
|
|
@@ -58247,6 +58981,84 @@ var audioMetricsCapability = {
|
|
|
58247
58981
|
*/
|
|
58248
58982
|
history: "none"
|
|
58249
58983
|
};
|
|
58984
|
+
method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
|
|
58985
|
+
actionId: string().min(1),
|
|
58986
|
+
variables: record(string(), ActionVariableValueSchema).optional()
|
|
58987
|
+
}), _void(), {
|
|
58988
|
+
kind: "mutation",
|
|
58989
|
+
auth: "admin"
|
|
58990
|
+
}), method(object({ actionId: string().min(1) }), _void(), {
|
|
58991
|
+
kind: "mutation",
|
|
58992
|
+
auth: "admin"
|
|
58993
|
+
});
|
|
58994
|
+
/**
|
|
58995
|
+
* actions — the ONE surface for runnable actions, for the UI and for
|
|
58996
|
+
* automations. Provided by the core-blocks builtin on the hub.
|
|
58997
|
+
*
|
|
58998
|
+
* An action is a LIST ENTRY, not a device (D883): either internal (cap and wait
|
|
58999
|
+
* steps, kept in the hub's `actions` record kind) or declared by an addon over
|
|
59000
|
+
* `action-source`. `list` asks every `action-source` provider LIVE and overlays
|
|
59001
|
+
* the run status from the hub's `runs` ledger; there is no catalog here to
|
|
59002
|
+
* drift (D3).
|
|
59003
|
+
*
|
|
59004
|
+
* **Permissions follow the camera, as linked devices do.** An action is linked
|
|
59005
|
+
* to cameras by the operator (`setLinks`, D356: nothing links itself). A
|
|
59006
|
+
* non-admin may `get` / `run` an action only through a camera they may see that
|
|
59007
|
+
* links it: they name the camera (`deviceId`, a top-level field so the scope
|
|
59008
|
+
* gate sees it) and the provider refuses an action that camera does not link.
|
|
59009
|
+
* An admin, and an in-process caller (an automation block), may run any action.
|
|
59010
|
+
* Every refusal is typed (`[not-linked] …`) and logged.
|
|
59011
|
+
*
|
|
59012
|
+
* A second `run` of an action that is already running is REFUSED — traced and
|
|
59013
|
+
* logged — never queued.
|
|
59014
|
+
*/
|
|
59015
|
+
var ActionIdInput = object({ actionId: string().min(1) });
|
|
59016
|
+
var ActionLinksSchema = object({
|
|
59017
|
+
deviceId: number().int().nonnegative(),
|
|
59018
|
+
actionIds: array(string().min(1)).max(100)
|
|
59019
|
+
});
|
|
59020
|
+
method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
|
|
59021
|
+
actionId: string().min(1),
|
|
59022
|
+
deviceId: number().int().nonnegative().optional()
|
|
59023
|
+
}), object({ action: ActionEntrySchema.nullable() }), {
|
|
59024
|
+
role: "viewer",
|
|
59025
|
+
auth: "protected",
|
|
59026
|
+
kind: "query",
|
|
59027
|
+
caller: "required"
|
|
59028
|
+
}), method(object({
|
|
59029
|
+
actionId: string().min(1),
|
|
59030
|
+
variables: record(string(), ActionVariableValueSchema).optional(),
|
|
59031
|
+
wait: boolean().optional(),
|
|
59032
|
+
origin: ActionRunOriginSchema.optional(),
|
|
59033
|
+
deviceId: number().int().nonnegative().optional()
|
|
59034
|
+
}), ActionRunResultSchema, {
|
|
59035
|
+
role: "viewer",
|
|
59036
|
+
auth: "protected",
|
|
59037
|
+
kind: "mutation",
|
|
59038
|
+
caller: "required"
|
|
59039
|
+
}), method(ActionIdInput, _void(), {
|
|
59040
|
+
kind: "mutation",
|
|
59041
|
+
auth: "admin"
|
|
59042
|
+
}), method(ActionIdInput, object({ traces: array(AutomationRunTraceSchema) }), { auth: "admin" }), method(object({ action: ActionInputSchema }), object({ action: ActionEntrySchema }), {
|
|
59043
|
+
kind: "mutation",
|
|
59044
|
+
auth: "admin"
|
|
59045
|
+
}), method(object({
|
|
59046
|
+
actionId: string().min(1),
|
|
59047
|
+
action: ActionInputSchema.partial()
|
|
59048
|
+
}), object({ action: ActionEntrySchema }), {
|
|
59049
|
+
kind: "mutation",
|
|
59050
|
+
auth: "admin"
|
|
59051
|
+
}), method(ActionIdInput, _void(), {
|
|
59052
|
+
kind: "mutation",
|
|
59053
|
+
auth: "admin"
|
|
59054
|
+
}), method(object({ deviceId: number().int().nonnegative() }), array(ActionEntrySchema), {
|
|
59055
|
+
role: "viewer",
|
|
59056
|
+
auth: "protected",
|
|
59057
|
+
kind: "query"
|
|
59058
|
+
}), method(object({ deviceId: number().int().nonnegative() }), ActionLinksSchema, { auth: "admin" }), method(ActionLinksSchema, ActionLinksSchema, {
|
|
59059
|
+
kind: "mutation",
|
|
59060
|
+
auth: "admin"
|
|
59061
|
+
});
|
|
58250
59062
|
/**
|
|
58251
59063
|
* Automation-control cap. Models HA `automation.*` entities on
|
|
58252
59064
|
* `DeviceType.Automation`. An automation is a trigger+condition+
|
|
@@ -58343,7 +59155,7 @@ var BatteryStatusSchema = object({
|
|
|
58343
59155
|
* Consumers must SKIP a null rather than coerce it — `battery-band` already
|
|
58344
59156
|
* declines to band a non-finite reading, which is the correct shape.
|
|
58345
59157
|
*/
|
|
58346
|
-
|
|
59158
|
+
level: number().min(0).max(100).nullable().meta(readingMeta("percentage", { label: "Charge" })),
|
|
58347
59159
|
/**
|
|
58348
59160
|
* Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
|
|
58349
59161
|
* Reolink-specific for the Solar Panel 2 accessory (will become
|
|
@@ -58353,7 +59165,7 @@ var BatteryStatusSchema = object({
|
|
|
58353
59165
|
* `'unknown'` is the absence of an answer: the provider has not read the
|
|
58354
59166
|
* power fields yet, or the firmware reported neither. Before it existed,
|
|
58355
59167
|
* two unreadable fields produced `'none'`, and a camera nobody had reached
|
|
58356
|
-
* was drawn "on battery alone" — the same D315 defect `
|
|
59168
|
+
* was drawn "on battery alone" — the same D315 defect `level` closed
|
|
58357
59169
|
* by going nullable. Consumers SKIP it (no charger state published, no
|
|
58358
59170
|
* glyph, no HomeKit ChargingState) rather than coerce it either way.
|
|
58359
59171
|
*/
|
|
@@ -58377,7 +59189,7 @@ var BatteryStatusSchema = object({
|
|
|
58377
59189
|
* that only report charge. A second reading on this cap, not a second
|
|
58378
59190
|
* `numeric-sensor` on the same device.
|
|
58379
59191
|
*/
|
|
58380
|
-
|
|
59192
|
+
voltage: number().nullable().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
58381
59193
|
/**
|
|
58382
59194
|
* Ms epoch of the last time the device PROVED it was reachable — a
|
|
58383
59195
|
* completed firmware round-trip, an observed wake, or an inbound push
|
|
@@ -58411,7 +59223,7 @@ var BatteryStatusSchema = object({
|
|
|
58411
59223
|
/**
|
|
58412
59224
|
* True when the source is a BINARY low-battery indicator (HA
|
|
58413
59225
|
* `binary_sensor` device_class=battery / `LOW_BAT`) that has no real
|
|
58414
|
-
* charge level — `
|
|
59226
|
+
* charge level — `level` is then a coarse stand-in (100 = normal,
|
|
58415
59227
|
* sub-threshold = low). UI MUST render "Normal"/"Low" instead of a
|
|
58416
59228
|
* misleading exact percentage. Absent/false → genuine 0–100 % reading.
|
|
58417
59229
|
*/
|
|
@@ -58424,21 +59236,22 @@ var BATTERY_DEVICE_TYPES = [
|
|
|
58424
59236
|
DeviceType.Switch,
|
|
58425
59237
|
DeviceType.PetFeeder,
|
|
58426
59238
|
DeviceType.Container,
|
|
58427
|
-
DeviceType.Thermostat
|
|
59239
|
+
DeviceType.Thermostat,
|
|
59240
|
+
DeviceType.EventEmitter
|
|
58428
59241
|
];
|
|
58429
59242
|
/** Generic Customize target for the percentage reading (D936). */
|
|
58430
59243
|
var batteryCustomize = {
|
|
58431
59244
|
version: 1,
|
|
58432
59245
|
targets: [{
|
|
58433
|
-
id: "
|
|
59246
|
+
id: "level",
|
|
58434
59247
|
cardinality: "scalar",
|
|
58435
59248
|
vocabulary: {
|
|
58436
59249
|
cap: "battery",
|
|
58437
|
-
stateField: "
|
|
59250
|
+
stateField: "level"
|
|
58438
59251
|
},
|
|
58439
59252
|
deviceTypes: BATTERY_DEVICE_TYPES,
|
|
58440
59253
|
apply: "add",
|
|
58441
|
-
path: "
|
|
59254
|
+
path: "level",
|
|
58442
59255
|
sources: [{
|
|
58443
59256
|
kind: "field",
|
|
58444
59257
|
valueKinds: ["number", "string"],
|
|
@@ -58532,7 +59345,7 @@ onStatusChanged: { data: object({
|
|
|
58532
59345
|
schema: BatteryStatusSchema,
|
|
58533
59346
|
kind: "push",
|
|
58534
59347
|
empty: {
|
|
58535
|
-
|
|
59348
|
+
level: null,
|
|
58536
59349
|
charging: "unknown",
|
|
58537
59350
|
sleeping: false,
|
|
58538
59351
|
lastUpdated: 0
|
|
@@ -58559,11 +59372,11 @@ onStatusChanged: { data: object({
|
|
|
58559
59372
|
* History (D850): recorded series. The generic capture replaces the `battery` series of `pipeline-analytics:stats-5m`.
|
|
58560
59373
|
*/
|
|
58561
59374
|
history: { fields: {
|
|
58562
|
-
|
|
59375
|
+
level: {
|
|
58563
59376
|
kind: "gauge",
|
|
58564
59377
|
unit: "%"
|
|
58565
59378
|
},
|
|
58566
|
-
|
|
59379
|
+
voltage: {
|
|
58567
59380
|
kind: "gauge",
|
|
58568
59381
|
unit: "V"
|
|
58569
59382
|
}
|
|
@@ -58623,14 +59436,14 @@ var binaryCapability = {
|
|
|
58623
59436
|
* controls to this single-method surface.
|
|
58624
59437
|
*
|
|
58625
59438
|
* The cap is intentionally minimal: a single `setBrightness({deviceId,
|
|
58626
|
-
*
|
|
59439
|
+
* level})` mutation plus the auto-injected `getStatus`. Drivers
|
|
58627
59440
|
* that expose richer controls (color temperature, scenes, schedules)
|
|
58628
59441
|
* should surface those via the device's `getSettingsUISchema()`
|
|
58629
59442
|
* instead of bloating this cap.
|
|
58630
59443
|
*/
|
|
58631
59444
|
var BrightnessStatusSchema = object({
|
|
58632
59445
|
/** Current level as 0..100 inclusive. Firmware-reported. */
|
|
58633
|
-
|
|
59446
|
+
level: number().min(0).max(100).meta(readingMeta("percentage")),
|
|
58634
59447
|
/** Ms epoch of the last operator-driven change. Useful for UI freshness. */
|
|
58635
59448
|
lastChangedAt: number()
|
|
58636
59449
|
});
|
|
@@ -58642,7 +59455,7 @@ var brightnessCapability = {
|
|
|
58642
59455
|
deviceTypes: [DeviceType.Light],
|
|
58643
59456
|
methods: { setBrightness: method(object({
|
|
58644
59457
|
deviceId: number().int().nonnegative(),
|
|
58645
|
-
|
|
59458
|
+
level: number().min(0).max(100)
|
|
58646
59459
|
}), _void(), {
|
|
58647
59460
|
kind: "mutation",
|
|
58648
59461
|
auth: "protected"
|
|
@@ -58655,7 +59468,7 @@ var brightnessCapability = {
|
|
|
58655
59468
|
*/
|
|
58656
59469
|
onBrightnessChanged: { data: object({
|
|
58657
59470
|
deviceId: number(),
|
|
58658
|
-
|
|
59471
|
+
level: number().min(0).max(100),
|
|
58659
59472
|
lastChangedAt: number()
|
|
58660
59473
|
}) } },
|
|
58661
59474
|
status: {
|
|
@@ -58678,7 +59491,7 @@ onBrightnessChanged: { data: object({
|
|
|
58678
59491
|
/**
|
|
58679
59492
|
* History (D850): recorded series. Actuator tier: level keeps every change.
|
|
58680
59493
|
*/
|
|
58681
|
-
history: { fields: {
|
|
59494
|
+
history: { fields: { level: {
|
|
58682
59495
|
kind: "gauge",
|
|
58683
59496
|
unit: "%",
|
|
58684
59497
|
minIntervalMs: 0
|
|
@@ -59275,11 +60088,11 @@ var ColorInputSchema = discriminatedUnion("mode", [
|
|
|
59275
60088
|
}),
|
|
59276
60089
|
object({
|
|
59277
60090
|
mode: literal("mired"),
|
|
59278
|
-
|
|
60091
|
+
colorTemperature: number().int().min(50).max(1e3)
|
|
59279
60092
|
})
|
|
59280
60093
|
]);
|
|
59281
60094
|
var ColorStatusSchema = object({
|
|
59282
|
-
/** Active color mode — which of `rgb` / `hsv` / `
|
|
60095
|
+
/** Active color mode — which of `rgb` / `hsv` / `colorTemperature` reflects
|
|
59283
60096
|
* the bulb's current state. */
|
|
59284
60097
|
mode: _enum([
|
|
59285
60098
|
"rgb",
|
|
@@ -59291,7 +60104,7 @@ var ColorStatusSchema = object({
|
|
|
59291
60104
|
/** Populated when `mode === 'hsv'`. */
|
|
59292
60105
|
hsv: HsvTripletSchema.optional(),
|
|
59293
60106
|
/** Populated when `mode === 'mired'`. */
|
|
59294
|
-
|
|
60107
|
+
colorTemperature: number().int().optional().meta(readingMeta("colour-temperature")),
|
|
59295
60108
|
/** Ms epoch of the last operator-driven change. */
|
|
59296
60109
|
lastChangedAt: number()
|
|
59297
60110
|
});
|
|
@@ -59323,7 +60136,7 @@ onColorChanged: { data: object({
|
|
|
59323
60136
|
]),
|
|
59324
60137
|
rgb: RgbTripletSchema.optional(),
|
|
59325
60138
|
hsv: HsvTripletSchema.optional(),
|
|
59326
|
-
|
|
60139
|
+
colorTemperature: number().int().optional(),
|
|
59327
60140
|
lastChangedAt: number()
|
|
59328
60141
|
}) } },
|
|
59329
60142
|
status: {
|
|
@@ -60200,11 +61013,11 @@ var DishwasherStatusSchema = object({
|
|
|
60200
61013
|
/** Feature keys of the programs the appliance currently offers — drives the UI selector. */
|
|
60201
61014
|
availablePrograms: array(string()),
|
|
60202
61015
|
/** Seconds remaining on the active cycle, or null when not running / not known. */
|
|
60203
|
-
|
|
61016
|
+
remaining: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
60204
61017
|
/** Seconds the delayed start is set to (time until the cycle starts), or null. */
|
|
60205
|
-
|
|
61018
|
+
delayedStart: number().int().nonnegative().nullable().meta(readingMeta("duration")),
|
|
60206
61019
|
/** Cycle completion 0..100, or null when not applicable. */
|
|
60207
|
-
|
|
61020
|
+
progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
60208
61021
|
/** True when the door is reported open; null when the door state is not known. */
|
|
60209
61022
|
doorOpen: boolean().nullable(),
|
|
60210
61023
|
/** True when the door is latched (locked) during a cycle; null when not reported. */
|
|
@@ -60423,14 +61236,14 @@ var dishwasherCapability = {
|
|
|
60423
61236
|
runtimeState: DishwasherStatusSchema,
|
|
60424
61237
|
durability: "session",
|
|
60425
61238
|
history: { fields: {
|
|
60426
|
-
|
|
61239
|
+
progress: {
|
|
60427
61240
|
kind: "gauge",
|
|
60428
61241
|
unit: "%",
|
|
60429
61242
|
deadband: 2
|
|
60430
61243
|
},
|
|
60431
61244
|
state: { kind: "enum" },
|
|
60432
61245
|
operationState: { kind: "enum" },
|
|
60433
|
-
|
|
61246
|
+
remaining: {
|
|
60434
61247
|
kind: "gauge",
|
|
60435
61248
|
unit: "s",
|
|
60436
61249
|
minIntervalMs: 6e4
|
|
@@ -61021,7 +61834,7 @@ var FanDirectionSchema = _enum(["forward", "reverse"]);
|
|
|
61021
61834
|
var FanControlStatusSchema = object({
|
|
61022
61835
|
/** Active speed as 0..100 inclusive. Null when the device has no
|
|
61023
61836
|
* speed surface (single-speed fan). */
|
|
61024
|
-
|
|
61837
|
+
speed: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
61025
61838
|
/** Speed granularity the device accepts (HA `percentage_step`, e.g. 25 for
|
|
61026
61839
|
* a 4-speed fan). Absent when unknown — the UI then falls back to step 1. */
|
|
61027
61840
|
percentageStep: number().positive().optional().meta(readingMeta("percentage")),
|
|
@@ -61048,7 +61861,7 @@ var fanControlCapability = {
|
|
|
61048
61861
|
methods: {
|
|
61049
61862
|
setPercentage: method(object({
|
|
61050
61863
|
deviceId: number().int().nonnegative(),
|
|
61051
|
-
|
|
61864
|
+
speed: number().min(0).max(100)
|
|
61052
61865
|
}), _void(), {
|
|
61053
61866
|
kind: "mutation",
|
|
61054
61867
|
auth: "protected"
|
|
@@ -61081,7 +61894,7 @@ var fanControlCapability = {
|
|
|
61081
61894
|
},
|
|
61082
61895
|
/**
|
|
61083
61896
|
* Runtime-state slice — mirrored by the kernel. UI fan speed
|
|
61084
|
-
* sliders read `
|
|
61897
|
+
* sliders read `speed` for live updates.
|
|
61085
61898
|
*/
|
|
61086
61899
|
runtimeState: FanControlStatusSchema,
|
|
61087
61900
|
/**
|
|
@@ -61095,7 +61908,7 @@ var fanControlCapability = {
|
|
|
61095
61908
|
* History (D850): recorded series. Actuator tier: speed keeps every change.
|
|
61096
61909
|
*/
|
|
61097
61910
|
history: { fields: {
|
|
61098
|
-
|
|
61911
|
+
speed: {
|
|
61099
61912
|
kind: "gauge",
|
|
61100
61913
|
unit: "%",
|
|
61101
61914
|
minIntervalMs: 0
|
|
@@ -61284,7 +62097,17 @@ var groupCommandsCapability = {
|
|
|
61284
62097
|
scope: "device",
|
|
61285
62098
|
deviceNative: true,
|
|
61286
62099
|
mode: "singleton",
|
|
61287
|
-
deviceTypes: [
|
|
62100
|
+
deviceTypes: [
|
|
62101
|
+
DeviceType.Switch,
|
|
62102
|
+
DeviceType.Light,
|
|
62103
|
+
DeviceType.Fan,
|
|
62104
|
+
DeviceType.Cover,
|
|
62105
|
+
DeviceType.Valve,
|
|
62106
|
+
DeviceType.Lock,
|
|
62107
|
+
DeviceType.MediaPlayer,
|
|
62108
|
+
DeviceType.Thermostat,
|
|
62109
|
+
DeviceType.Climate
|
|
62110
|
+
],
|
|
61288
62111
|
methods: {},
|
|
61289
62112
|
status: {
|
|
61290
62113
|
schema: GroupCommandsSchema,
|
|
@@ -61302,6 +62125,49 @@ var groupCommandsCapability = {
|
|
|
61302
62125
|
history: "none"
|
|
61303
62126
|
};
|
|
61304
62127
|
/**
|
|
62128
|
+
* Whether a helper actuator group counts as active (D944): at least one
|
|
62129
|
+
* included member is, or every included member is. What "active" means is the
|
|
62130
|
+
* type's: a cover is open, a lock is locked, a media player is playing, a
|
|
62131
|
+
* climate device is not off. A group of lights, switches and fans reports
|
|
62132
|
+
* `switch.on` instead; this cap is for the types whose own state is not a
|
|
62133
|
+
* yes/no. Unknown members are left out (`group-status` counts them) and an
|
|
62134
|
+
* empty included set takes the device offline, never `active: false`.
|
|
62135
|
+
*/
|
|
62136
|
+
var GroupActiveSchema = object({
|
|
62137
|
+
active: boolean().meta({ label: "Active" }),
|
|
62138
|
+
/** Ms epoch when the slice was last updated. */
|
|
62139
|
+
lastChangedAt: number()
|
|
62140
|
+
});
|
|
62141
|
+
var groupActiveCapability = {
|
|
62142
|
+
name: "group-active",
|
|
62143
|
+
scope: "device",
|
|
62144
|
+
deviceNative: true,
|
|
62145
|
+
mode: "singleton",
|
|
62146
|
+
deviceTypes: [
|
|
62147
|
+
DeviceType.Cover,
|
|
62148
|
+
DeviceType.Valve,
|
|
62149
|
+
DeviceType.Lock,
|
|
62150
|
+
DeviceType.MediaPlayer,
|
|
62151
|
+
DeviceType.Climate,
|
|
62152
|
+
DeviceType.Thermostat
|
|
62153
|
+
],
|
|
62154
|
+
methods: {},
|
|
62155
|
+
status: {
|
|
62156
|
+
schema: GroupActiveSchema,
|
|
62157
|
+
kind: "push"
|
|
62158
|
+
},
|
|
62159
|
+
runtimeState: GroupActiveSchema,
|
|
62160
|
+
/**
|
|
62161
|
+
* Runtime-state durability: **session** — recomputed from the members on the
|
|
62162
|
+
* next source event; a restored value would outlive the members it named.
|
|
62163
|
+
*
|
|
62164
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
62165
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
62166
|
+
*/
|
|
62167
|
+
durability: "session",
|
|
62168
|
+
history: "none"
|
|
62169
|
+
};
|
|
62170
|
+
/**
|
|
61305
62171
|
* How many members of a helper group answered (D944). Counts are integers,
|
|
61306
62172
|
* not measurements: zero included is a real count, never a stand-in reading.
|
|
61307
62173
|
* The reading itself stays on the output capability. History is none — the
|
|
@@ -61332,7 +62198,14 @@ var groupStatusCapability = {
|
|
|
61332
62198
|
deviceTypes: [
|
|
61333
62199
|
DeviceType.Sensor,
|
|
61334
62200
|
DeviceType.Switch,
|
|
61335
|
-
DeviceType.Light
|
|
62201
|
+
DeviceType.Light,
|
|
62202
|
+
DeviceType.Fan,
|
|
62203
|
+
DeviceType.Cover,
|
|
62204
|
+
DeviceType.Valve,
|
|
62205
|
+
DeviceType.Lock,
|
|
62206
|
+
DeviceType.MediaPlayer,
|
|
62207
|
+
DeviceType.Thermostat,
|
|
62208
|
+
DeviceType.Climate
|
|
61336
62209
|
],
|
|
61337
62210
|
methods: {},
|
|
61338
62211
|
status: {
|
|
@@ -61480,7 +62353,7 @@ var humidifierCapability = {
|
|
|
61480
62353
|
*/
|
|
61481
62354
|
var HumiditySensorStatusSchema = object({
|
|
61482
62355
|
/** Current relative humidity, 0..100. `null` when the sample was refused (D393). */
|
|
61483
|
-
|
|
62356
|
+
humidity: number().min(0).max(100).nullable().meta(readingMeta("humidity", {
|
|
61484
62357
|
precision: 0,
|
|
61485
62358
|
label: "Humidity"
|
|
61486
62359
|
})),
|
|
@@ -61509,7 +62382,7 @@ var humiditySensorCapability = {
|
|
|
61509
62382
|
/**
|
|
61510
62383
|
* History (D850): recorded series.
|
|
61511
62384
|
*/
|
|
61512
|
-
history: { fields: {
|
|
62385
|
+
history: { fields: { humidity: {
|
|
61513
62386
|
kind: "gauge",
|
|
61514
62387
|
unit: "%",
|
|
61515
62388
|
deadband: .5
|
|
@@ -62149,7 +63022,7 @@ var LawnMowerControlStatusSchema = object({
|
|
|
62149
63022
|
batteryLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
62150
63023
|
/** 0..100 mowing-completion percentage of the current task, or null when no
|
|
62151
63024
|
* task is active / progress is unavailable. */
|
|
62152
|
-
|
|
63025
|
+
progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
62153
63026
|
/** Current device/event code (dynamic — mostly status, sometimes an error),
|
|
62154
63027
|
* or null when unknown. */
|
|
62155
63028
|
currentCode: number().nullable().meta(readingMeta("none")),
|
|
@@ -62605,7 +63478,14 @@ var lockControlCapability = {
|
|
|
62605
63478
|
}),
|
|
62606
63479
|
unlock: method(object({
|
|
62607
63480
|
deviceId: number().int().nonnegative(),
|
|
62608
|
-
code: string().min(1).optional()
|
|
63481
|
+
code: string().min(1).optional(),
|
|
63482
|
+
/**
|
|
63483
|
+
* Honoured by a helper lock GROUP only (D944): unlocking several doors
|
|
63484
|
+
* with one call is refused unless the caller says it was confirmed. A
|
|
63485
|
+
* single lock ignores it. The refusal lives in the callee, so no
|
|
63486
|
+
* automation or API caller reaches the doors by skipping a dialog.
|
|
63487
|
+
*/
|
|
63488
|
+
confirm: boolean().optional()
|
|
62609
63489
|
}), _void(), {
|
|
62610
63490
|
kind: "mutation",
|
|
62611
63491
|
auth: "protected"
|
|
@@ -62642,6 +63522,44 @@ var lockControlCapability = {
|
|
|
62642
63522
|
volatileStateFields: ["lastChangedAt"]
|
|
62643
63523
|
};
|
|
62644
63524
|
/**
|
|
63525
|
+
* A monotonic total between resets (D944). Energy uses `power-meter.energy`
|
|
63526
|
+
* so dashboards keep one series; every other instantaneous reading lands here.
|
|
63527
|
+
* `value` is the time integral in the source quantity's accumulated base
|
|
63528
|
+
* (base-unit seconds, or kWh on the power meter). `null` is a gap, never zero.
|
|
63529
|
+
*/
|
|
63530
|
+
var MeterStatusSchema = object({
|
|
63531
|
+
value: number().nullable().meta(readingMeta("device", { label: "Total" })),
|
|
63532
|
+
lastFetchedAt: number()
|
|
63533
|
+
});
|
|
63534
|
+
var meterCapability = {
|
|
63535
|
+
name: "meter",
|
|
63536
|
+
scope: "device",
|
|
63537
|
+
deviceNative: true,
|
|
63538
|
+
mode: "singleton",
|
|
63539
|
+
deviceTypes: [DeviceType.Sensor],
|
|
63540
|
+
methods: {},
|
|
63541
|
+
status: {
|
|
63542
|
+
schema: MeterStatusSchema,
|
|
63543
|
+
kind: "push"
|
|
63544
|
+
},
|
|
63545
|
+
runtimeState: MeterStatusSchema,
|
|
63546
|
+
/**
|
|
63547
|
+
* Runtime-state durability: **restored** — the total is a cache of the
|
|
63548
|
+
* history integral. A restart shows it only until the history pass
|
|
63549
|
+
* recomputes the open period; it is not an in-memory buffer.
|
|
63550
|
+
*
|
|
63551
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
63552
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
63553
|
+
*/
|
|
63554
|
+
durability: "restored",
|
|
63555
|
+
history: { fields: { value: {
|
|
63556
|
+
kind: "counter",
|
|
63557
|
+
deadband: .001,
|
|
63558
|
+
minIntervalMs: 6e4
|
|
63559
|
+
} } },
|
|
63560
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
63561
|
+
};
|
|
63562
|
+
/**
|
|
62645
63563
|
* Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
|
|
62646
63564
|
* Apple TV, Spotify, Roku, generic OTT receivers, …) on
|
|
62647
63565
|
* `DeviceType.MediaPlayer`. Disjoint from the camera streaming
|
|
@@ -62698,10 +63616,10 @@ var MediaPlayerStatusSchema = object({
|
|
|
62698
63616
|
currentMedia: MediaInfoSchema.nullable(),
|
|
62699
63617
|
/** Current playback position in ms. Null when not seekable or
|
|
62700
63618
|
* nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
|
|
62701
|
-
|
|
63619
|
+
position: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
62702
63620
|
/** Total duration of the current media in ms. Null when unknown
|
|
62703
63621
|
* (live stream) or nothing is playing. */
|
|
62704
|
-
|
|
63622
|
+
duration: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
62705
63623
|
/** Shuffle toggle. Null when no shuffle surface.
|
|
62706
63624
|
* Pair with `DeviceFeature.MediaPlayerShuffle`. */
|
|
62707
63625
|
shuffle: boolean().nullable(),
|
|
@@ -62740,7 +63658,7 @@ var mediaPlayerCapability = {
|
|
|
62740
63658
|
}),
|
|
62741
63659
|
seek: method(object({
|
|
62742
63660
|
deviceId: number().int().nonnegative(),
|
|
62743
|
-
|
|
63661
|
+
position: number().int().nonnegative()
|
|
62744
63662
|
}), _void(), {
|
|
62745
63663
|
kind: "mutation",
|
|
62746
63664
|
auth: "protected"
|
|
@@ -62812,7 +63730,7 @@ var mediaPlayerCapability = {
|
|
|
62812
63730
|
/**
|
|
62813
63731
|
* History (D850): volume (a level an operator or an automation sets; the
|
|
62814
63732
|
* default 10 s floor folds a slider drag into its end value) and mute.
|
|
62815
|
-
* `
|
|
63733
|
+
* `position` is not recorded: it moves continuously while playing.
|
|
62816
63734
|
*/
|
|
62817
63735
|
history: { fields: {
|
|
62818
63736
|
volumeLevel: {
|
|
@@ -63774,9 +64692,9 @@ var NetworkLinkStatusSchema = object({
|
|
|
63774
64692
|
* one whose reading has not landed must not be drawn at 0 %. Consumers
|
|
63775
64693
|
* SKIP a null rather than coerce it.
|
|
63776
64694
|
*/
|
|
63777
|
-
|
|
64695
|
+
signal: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
63778
64696
|
/** Raw received signal strength in dBm, when the firmware reports one. */
|
|
63779
|
-
|
|
64697
|
+
rssi: number().optional().meta(readingMeta("signal-level")),
|
|
63780
64698
|
/** Network name of a wireless link, when the firmware reports it. */
|
|
63781
64699
|
ssid: string().optional(),
|
|
63782
64700
|
/** Ms epoch of the last observation. Lets consumers reason about freshness. */
|
|
@@ -63785,7 +64703,7 @@ var NetworkLinkStatusSchema = object({
|
|
|
63785
64703
|
/** The slice a provider seeds before its first read: nothing is known yet. */
|
|
63786
64704
|
var NETWORK_LINK_UNKNOWN = {
|
|
63787
64705
|
type: "unknown",
|
|
63788
|
-
|
|
64706
|
+
signal: null,
|
|
63789
64707
|
lastUpdated: 0
|
|
63790
64708
|
};
|
|
63791
64709
|
/**
|
|
@@ -63802,9 +64720,9 @@ function signalPercentFromBars(bars, maxBars) {
|
|
|
63802
64720
|
* below is 0 %, -50 dBm and above is 100 %, linear between. Non-finite or
|
|
63803
64721
|
* positive input is not an RSSI: `null`.
|
|
63804
64722
|
*/
|
|
63805
|
-
function signalPercentFromRssi(
|
|
63806
|
-
if (
|
|
63807
|
-
return Math.round((Math.min(-50, Math.max(-100,
|
|
64723
|
+
function signalPercentFromRssi(rssi) {
|
|
64724
|
+
if (rssi === void 0 || !Number.isFinite(rssi) || rssi > 0) return null;
|
|
64725
|
+
return Math.round((Math.min(-50, Math.max(-100, rssi)) + 100) * 2);
|
|
63808
64726
|
}
|
|
63809
64727
|
var networkLinkCapability = {
|
|
63810
64728
|
name: "network-link",
|
|
@@ -63855,13 +64773,13 @@ onStatusChanged: { data: object({
|
|
|
63855
64773
|
* History (D850): recorded series.
|
|
63856
64774
|
*/
|
|
63857
64775
|
history: { fields: {
|
|
63858
|
-
|
|
64776
|
+
signal: {
|
|
63859
64777
|
kind: "gauge",
|
|
63860
64778
|
unit: "%",
|
|
63861
64779
|
deadband: 3,
|
|
63862
64780
|
minIntervalMs: 6e4
|
|
63863
64781
|
},
|
|
63864
|
-
|
|
64782
|
+
rssi: {
|
|
63865
64783
|
kind: "gauge",
|
|
63866
64784
|
unit: "dBm",
|
|
63867
64785
|
deadband: 2,
|
|
@@ -64652,10 +65570,20 @@ var PetFeederDeviceStatusSchema = _enum([
|
|
|
64652
65570
|
]);
|
|
64653
65571
|
var gramsPortion = number().int().min(4).max(200);
|
|
64654
65572
|
var PetFeederStatusSchema = object({
|
|
64655
|
-
/** Food
|
|
64656
|
-
*
|
|
64657
|
-
* bowl
|
|
65573
|
+
/** Food in the TANK (hopper), as the feeder's own sensor reports it
|
|
65574
|
+
* (`state.food`). A coarse level code, not grams — `0` is empty. The
|
|
65575
|
+
* amount in the bowl is `bowlFood`. On dual-hopper models this is the
|
|
65576
|
+
* combined reading; per-hopper levels live in `food1`/`food2`. Null when
|
|
65577
|
+
* the device has not reported yet. */
|
|
64658
65578
|
foodLevel: number().nullable().meta(readingMeta("none")),
|
|
65579
|
+
/** Food in the BOWL as the feeder reports it (`state.bowl`), in the
|
|
65580
|
+
* feeder's own units. Null when the model reports none. */
|
|
65581
|
+
bowlFood: number().nullable().meta(readingMeta("none")),
|
|
65582
|
+
/** True while the pet is eating (`state.eating`). */
|
|
65583
|
+
eating: boolean(),
|
|
65584
|
+
/** Whether `markFoodReplenished` does anything on this model. When false
|
|
65585
|
+
* the client must not offer it. */
|
|
65586
|
+
supportsMarkRefilled: boolean(),
|
|
64659
65587
|
/** Hopper-1 food level (grams) on dual-hopper feeders; null on
|
|
64660
65588
|
* single-hopper models. */
|
|
64661
65589
|
food1: number().nullable().meta(readingMeta("none")),
|
|
@@ -64663,8 +65591,8 @@ var PetFeederStatusSchema = object({
|
|
|
64663
65591
|
* single-hopper models. */
|
|
64664
65592
|
food2: number().nullable().meta(readingMeta("none")),
|
|
64665
65593
|
/** Derived low-food flag — mirrors the HA `food_level` binary_sensor
|
|
64666
|
-
* (`device_class: problem`, on = low). True when the
|
|
64667
|
-
*
|
|
65594
|
+
* (`device_class: problem`, on = low). True when the tank reports empty,
|
|
65595
|
+
* or, on a dual-hopper model, when EITHER hopper is empty. */
|
|
64668
65596
|
lowFood: boolean(),
|
|
64669
65597
|
/** Battery charge 0..100 (%). Null on mains-powered models or when the
|
|
64670
65598
|
* device has no battery reading. */
|
|
@@ -64732,7 +65660,9 @@ var petFeederCapability = {
|
|
|
64732
65660
|
kind: "mutation",
|
|
64733
65661
|
auth: "protected"
|
|
64734
65662
|
}),
|
|
64735
|
-
/**
|
|
65663
|
+
/** Tell the PetKit cloud a hopper was refilled so it stops its low-food
|
|
65664
|
+
* reminders (D4H/D4S/D4SH only — see `supportsMarkRefilled`). It does not
|
|
65665
|
+
* change any sensor reading. */
|
|
64736
65666
|
markFoodReplenished: method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
64737
65667
|
kind: "mutation",
|
|
64738
65668
|
auth: "protected"
|
|
@@ -64805,6 +65735,7 @@ var petFeederCapability = {
|
|
|
64805
65735
|
*/
|
|
64806
65736
|
history: { fields: {
|
|
64807
65737
|
foodLevel: { kind: "gauge" },
|
|
65738
|
+
bowlFood: { kind: "gauge" },
|
|
64808
65739
|
food1: { kind: "gauge" },
|
|
64809
65740
|
food2: { kind: "gauge" },
|
|
64810
65741
|
batteryPower: {
|
|
@@ -65214,27 +66145,89 @@ method(_void(), PlatformCapabilitiesSchema), method(_void(), HardwareInfoSchema)
|
|
|
65214
66145
|
* in real-world devices.
|
|
65215
66146
|
*/
|
|
65216
66147
|
var PowerMeterStatusSchema = object({
|
|
65217
|
-
/** Instantaneous power draw
|
|
65218
|
-
|
|
66148
|
+
/** Instantaneous power draw (watts). */
|
|
66149
|
+
power: number().optional().meta(readingMeta("power", { label: "Power" })),
|
|
65219
66150
|
/** Cumulative energy in kilowatt-hours since the meter was reset. */
|
|
65220
|
-
|
|
65221
|
-
/** Voltage
|
|
65222
|
-
|
|
66151
|
+
energy: number().optional().meta(readingMeta("energy", { label: "Energy" })),
|
|
66152
|
+
/** Voltage (volts). */
|
|
66153
|
+
voltage: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
65223
66154
|
/** Current in amperes. */
|
|
65224
|
-
|
|
66155
|
+
current: number().optional().meta(readingMeta("current", { label: "Current" })),
|
|
65225
66156
|
/** Apparent power in volt-amperes. */
|
|
65226
|
-
|
|
66157
|
+
apparentPower: number().optional().meta(readingMeta("apparent-power")),
|
|
65227
66158
|
/** Power factor (0..1) if reported. */
|
|
65228
66159
|
powerFactor: number().min(0).max(1).optional().meta(readingMeta("ratio")),
|
|
65229
66160
|
/** Ms epoch when the slice was last updated. */
|
|
65230
66161
|
lastFetchedAt: number()
|
|
65231
66162
|
});
|
|
66163
|
+
/**
|
|
66164
|
+
* Loads that draw metered mains power (D951). The reading is a feature of the
|
|
66165
|
+
* device itself — a plug, a lamp, a fan, an air conditioner, a boiler, a
|
|
66166
|
+
* vacuum dock — not a separate sensor beside it. A `Sensor` stays: a clamp
|
|
66167
|
+
* meter or a standalone energy monitor IS the device. Cover, lock, valve,
|
|
66168
|
+
* camera and the like are left out: their draw is not what anyone meters.
|
|
66169
|
+
*/
|
|
66170
|
+
var POWER_METER_DEVICE_TYPES = [
|
|
66171
|
+
DeviceType.Sensor,
|
|
66172
|
+
DeviceType.Switch,
|
|
66173
|
+
DeviceType.Light,
|
|
66174
|
+
DeviceType.Fan,
|
|
66175
|
+
DeviceType.Climate,
|
|
66176
|
+
DeviceType.Thermostat,
|
|
66177
|
+
DeviceType.Humidifier,
|
|
66178
|
+
DeviceType.WaterHeater,
|
|
66179
|
+
DeviceType.MediaPlayer,
|
|
66180
|
+
DeviceType.Washer,
|
|
66181
|
+
DeviceType.Dryer,
|
|
66182
|
+
DeviceType.Dishwasher,
|
|
66183
|
+
DeviceType.Vacuum,
|
|
66184
|
+
DeviceType.LawnMower,
|
|
66185
|
+
DeviceType.PetFeeder
|
|
66186
|
+
];
|
|
66187
|
+
/** Everything but a bare `Sensor`: a sensor that takes its reading from another sensor adds nothing. */
|
|
66188
|
+
var POWER_METER_CUSTOMIZE_DEVICE_TYPES = POWER_METER_DEVICE_TYPES.filter((type) => type !== DeviceType.Sensor);
|
|
66189
|
+
/** One scalar target per reading; each is sourced from a device of its own (D951). */
|
|
66190
|
+
function powerTarget(id, unit) {
|
|
66191
|
+
return {
|
|
66192
|
+
id,
|
|
66193
|
+
cardinality: "scalar",
|
|
66194
|
+
vocabulary: {
|
|
66195
|
+
cap: "power-meter",
|
|
66196
|
+
stateField: id
|
|
66197
|
+
},
|
|
66198
|
+
deviceTypes: POWER_METER_CUSTOMIZE_DEVICE_TYPES,
|
|
66199
|
+
apply: "add",
|
|
66200
|
+
path: id,
|
|
66201
|
+
sources: [{
|
|
66202
|
+
kind: "numeric",
|
|
66203
|
+
requiredUnit: unit,
|
|
66204
|
+
transforms: [{ kind: "identity" }]
|
|
66205
|
+
}],
|
|
66206
|
+
companions: [{
|
|
66207
|
+
kind: "clock",
|
|
66208
|
+
path: "lastFetchedAt",
|
|
66209
|
+
when: "always"
|
|
66210
|
+
}],
|
|
66211
|
+
sourceReport: { status: "assignment-status" },
|
|
66212
|
+
advancedExpression: true
|
|
66213
|
+
};
|
|
66214
|
+
}
|
|
66215
|
+
/** "Power and energy" extension: power, energy, voltage and current from other devices (D951). */
|
|
66216
|
+
var powerMeterCustomize = {
|
|
66217
|
+
version: 1,
|
|
66218
|
+
targets: [
|
|
66219
|
+
powerTarget("power", "W"),
|
|
66220
|
+
powerTarget("energy", "kWh"),
|
|
66221
|
+
powerTarget("voltage", "V"),
|
|
66222
|
+
powerTarget("current", "A")
|
|
66223
|
+
]
|
|
66224
|
+
};
|
|
65232
66225
|
var powerMeterCapability = {
|
|
65233
66226
|
name: "power-meter",
|
|
65234
66227
|
scope: "device",
|
|
65235
66228
|
deviceNative: true,
|
|
65236
66229
|
mode: "singleton",
|
|
65237
|
-
deviceTypes:
|
|
66230
|
+
deviceTypes: POWER_METER_DEVICE_TYPES,
|
|
65238
66231
|
methods: {},
|
|
65239
66232
|
status: {
|
|
65240
66233
|
schema: PowerMeterStatusSchema,
|
|
@@ -65242,41 +66235,41 @@ var powerMeterCapability = {
|
|
|
65242
66235
|
},
|
|
65243
66236
|
runtimeState: PowerMeterStatusSchema,
|
|
65244
66237
|
/**
|
|
65245
|
-
* Runtime-state durability: **restored** — as `numeric-sensor`; `
|
|
66238
|
+
* Runtime-state durability: **restored** — as `numeric-sensor`; `energy` is an accumulator whose restored value is the baseline.
|
|
65246
66239
|
*
|
|
65247
66240
|
* See `RuntimeStateDurability`. Enforced by
|
|
65248
66241
|
* `scripts/check-runtime-state-durability.ts`.
|
|
65249
66242
|
*/
|
|
65250
66243
|
durability: "restored",
|
|
65251
66244
|
/**
|
|
65252
|
-
* History (D850): recorded series. Gauges are floored at 30 s; `
|
|
66245
|
+
* History (D850): recorded series. Gauges are floored at 30 s; `energy` is the one monotonic meter (counter).
|
|
65253
66246
|
*/
|
|
65254
66247
|
history: { fields: {
|
|
65255
|
-
|
|
66248
|
+
power: {
|
|
65256
66249
|
kind: "gauge",
|
|
65257
66250
|
unit: "W",
|
|
65258
66251
|
deadband: 1,
|
|
65259
66252
|
minIntervalMs: 3e4
|
|
65260
66253
|
},
|
|
65261
|
-
|
|
66254
|
+
energy: {
|
|
65262
66255
|
kind: "counter",
|
|
65263
66256
|
unit: "kWh",
|
|
65264
66257
|
deadband: .01,
|
|
65265
66258
|
minIntervalMs: 6e4
|
|
65266
66259
|
},
|
|
65267
|
-
|
|
66260
|
+
voltage: {
|
|
65268
66261
|
kind: "gauge",
|
|
65269
66262
|
unit: "V",
|
|
65270
66263
|
deadband: 1,
|
|
65271
66264
|
minIntervalMs: 3e4
|
|
65272
66265
|
},
|
|
65273
|
-
|
|
66266
|
+
current: {
|
|
65274
66267
|
kind: "gauge",
|
|
65275
66268
|
unit: "A",
|
|
65276
66269
|
deadband: .05,
|
|
65277
66270
|
minIntervalMs: 3e4
|
|
65278
66271
|
},
|
|
65279
|
-
|
|
66272
|
+
apparentPower: {
|
|
65280
66273
|
kind: "gauge",
|
|
65281
66274
|
unit: "VA",
|
|
65282
66275
|
deadband: 1,
|
|
@@ -65291,7 +66284,8 @@ var powerMeterCapability = {
|
|
|
65291
66284
|
} },
|
|
65292
66285
|
/** Clock fields: written, but excluded from the compare that decides
|
|
65293
66286
|
* whether persisting is worth a SQLite commit. */
|
|
65294
|
-
volatileStateFields: ["lastFetchedAt"]
|
|
66287
|
+
volatileStateFields: ["lastFetchedAt"],
|
|
66288
|
+
customize: powerMeterCustomize
|
|
65295
66289
|
};
|
|
65296
66290
|
/**
|
|
65297
66291
|
* Presence cap. Models HA `person.*` and `device_tracker.*` entities
|
|
@@ -65325,9 +66319,6 @@ var PresenceStatusSchema = object({
|
|
|
65325
66319
|
location: string().nullable(),
|
|
65326
66320
|
/** GPS coordinates when available. Null otherwise. */
|
|
65327
66321
|
gps: GpsLocationSchema.nullable(),
|
|
65328
|
-
/** Optional battery level of the tracking device (0..100). Null
|
|
65329
|
-
* when not reported. */
|
|
65330
|
-
batteryPercent: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
65331
66322
|
/** Ms epoch when the slice was last updated. */
|
|
65332
66323
|
lastChangedAt: number()
|
|
65333
66324
|
});
|
|
@@ -65356,14 +66347,7 @@ var presenceCapability = {
|
|
|
65356
66347
|
* `scripts/check-runtime-state-durability.ts`.
|
|
65357
66348
|
*/
|
|
65358
66349
|
durability: "restored",
|
|
65359
|
-
|
|
65360
|
-
* History (D850): recorded series.
|
|
65361
|
-
*/
|
|
65362
|
-
history: { fields: { batteryPercent: {
|
|
65363
|
-
kind: "gauge",
|
|
65364
|
-
unit: "%",
|
|
65365
|
-
deadband: 1
|
|
65366
|
-
} } },
|
|
66350
|
+
history: "none",
|
|
65367
66351
|
/** Clock fields: written, but excluded from the compare that decides
|
|
65368
66352
|
* whether persisting is worth a SQLite commit. */
|
|
65369
66353
|
volatileStateFields: ["lastChangedAt"]
|
|
@@ -65378,7 +66362,7 @@ var presenceCapability = {
|
|
|
65378
66362
|
*/
|
|
65379
66363
|
var PressureSensorStatusSchema = object({
|
|
65380
66364
|
/** Current pressure in hPa. `null` when the sample was refused (D393). */
|
|
65381
|
-
|
|
66365
|
+
pressure: number().nullable().meta(readingMeta("pressure", { precision: 0 })),
|
|
65382
66366
|
/** Ms epoch when the slice was last updated. */
|
|
65383
66367
|
lastFetchedAt: number()
|
|
65384
66368
|
});
|
|
@@ -65404,7 +66388,7 @@ var pressureSensorCapability = {
|
|
|
65404
66388
|
/**
|
|
65405
66389
|
* History (D850): recorded series.
|
|
65406
66390
|
*/
|
|
65407
|
-
history: { fields: {
|
|
66391
|
+
history: { fields: { pressure: {
|
|
65408
66392
|
kind: "gauge",
|
|
65409
66393
|
unit: "hPa",
|
|
65410
66394
|
deadband: .1
|
|
@@ -66084,6 +67068,8 @@ var runningCustomize = {
|
|
|
66084
67068
|
stateField: "running"
|
|
66085
67069
|
},
|
|
66086
67070
|
deviceTypes: [
|
|
67071
|
+
DeviceType.Washer,
|
|
67072
|
+
DeviceType.Dryer,
|
|
66087
67073
|
DeviceType.Dishwasher,
|
|
66088
67074
|
DeviceType.Vacuum,
|
|
66089
67075
|
DeviceType.LawnMower,
|
|
@@ -68965,7 +69951,8 @@ var switchCapability = {
|
|
|
68965
69951
|
deviceTypes: [
|
|
68966
69952
|
DeviceType.Switch,
|
|
68967
69953
|
DeviceType.Siren,
|
|
68968
|
-
DeviceType.Light
|
|
69954
|
+
DeviceType.Light,
|
|
69955
|
+
DeviceType.Fan
|
|
68969
69956
|
],
|
|
68970
69957
|
methods: { setState: method(object({
|
|
68971
69958
|
deviceId: number(),
|
|
@@ -69604,7 +70591,7 @@ var tamperCapability = {
|
|
|
69604
70591
|
*/
|
|
69605
70592
|
var TemperatureSensorStatusSchema = object({
|
|
69606
70593
|
/** Current temperature in Celsius. `null` when the sample was refused (D393). */
|
|
69607
|
-
|
|
70594
|
+
temperature: number().nullable().meta(readingMeta("temperature", {
|
|
69608
70595
|
precision: 1,
|
|
69609
70596
|
label: "Temperature"
|
|
69610
70597
|
})),
|
|
@@ -69633,7 +70620,7 @@ var temperatureSensorCapability = {
|
|
|
69633
70620
|
/**
|
|
69634
70621
|
* History (D850): recorded series.
|
|
69635
70622
|
*/
|
|
69636
|
-
history: { fields: {
|
|
70623
|
+
history: { fields: { temperature: {
|
|
69637
70624
|
kind: "gauge",
|
|
69638
70625
|
unit: "°C",
|
|
69639
70626
|
deadband: .1
|
|
@@ -69930,7 +70917,7 @@ var VacuumControlStatusSchema = object({
|
|
|
69930
70917
|
/** Speed tokens the hardware accepts — drives the UI selector. */
|
|
69931
70918
|
availableFanSpeeds: array(string()),
|
|
69932
70919
|
/** 0..100 cleaning-completion percentage of the current task, or null. */
|
|
69933
|
-
|
|
70920
|
+
progress: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
69934
70921
|
/** Current error code (0 / null = no error). */
|
|
69935
70922
|
errorCode: number().nullable().meta(readingMeta("none")),
|
|
69936
70923
|
/** Human label for {@link errorCode}, or null when none / undecodable. */
|
|
@@ -71311,8 +72298,9 @@ function withClipEventLabels(provider, deps) {
|
|
|
71311
72298
|
listClips
|
|
71312
72299
|
};
|
|
71313
72300
|
}
|
|
72301
|
+
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
71314
72302
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
71315
|
-
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;
|
|
72303
|
+
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;
|
|
71316
72304
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
71317
72305
|
DeviceRole.BinarySensor, DeviceRole.MotionSensor;
|
|
71318
72306
|
new Set(CAP_NAMES);
|
|
@@ -71656,6 +72644,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
71656
72644
|
featureProbe: featureProbeCapability,
|
|
71657
72645
|
flood: floodCapability,
|
|
71658
72646
|
gas: gasCapability,
|
|
72647
|
+
groupActive: groupActiveCapability,
|
|
71659
72648
|
groupCommands: groupCommandsCapability,
|
|
71660
72649
|
groupStatus: groupStatusCapability,
|
|
71661
72650
|
heatingValve: heatingValveCapability,
|
|
@@ -71667,6 +72656,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
71667
72656
|
lawnMowerControl: lawnMowerControlCapability,
|
|
71668
72657
|
lockControl: lockControlCapability,
|
|
71669
72658
|
mediaPlayer: mediaPlayerCapability,
|
|
72659
|
+
meter: meterCapability,
|
|
71670
72660
|
motion: motionCapability,
|
|
71671
72661
|
motionTrigger: motionTriggerCapability,
|
|
71672
72662
|
motionZones: motionZonesCapability,
|
|
@@ -71729,7 +72719,7 @@ var BaseDevice = class {
|
|
|
71729
72719
|
* `device-local-state.ts`. Drivers access via:
|
|
71730
72720
|
*
|
|
71731
72721
|
* `this.state.battery.sleeping = true` // patches the battery slice
|
|
71732
|
-
* `const pct = this.state.battery.
|
|
72722
|
+
* `const pct = this.state.battery.level` // reads the battery slice
|
|
71733
72723
|
* `this.state.deviceStatus.online = true` // mirrors `markOnline(true)`
|
|
71734
72724
|
*
|
|
71735
72725
|
* Adding a new cap with `runtimeState:` automatically extends this
|
|
@@ -71769,7 +72759,7 @@ var BaseDevice = class {
|
|
|
71769
72759
|
* shape; drivers don't redeclare it, they just write through.
|
|
71770
72760
|
*
|
|
71771
72761
|
* Read: `this.runtimeState.getCapState('battery')` →
|
|
71772
|
-
* `{
|
|
72762
|
+
* `{level, charging, sleeping, lastUpdated}` for any
|
|
71773
72763
|
* provider that registers `batteryCapability`.
|
|
71774
72764
|
* Write: `this.runtimeState.setCapState('battery', { … })`.
|
|
71775
72765
|
*
|
|
@@ -72926,7 +73916,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
72926
73916
|
return toDeviceSummary(device, this.addonId);
|
|
72927
73917
|
}
|
|
72928
73918
|
};
|
|
72929
|
-
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;
|
|
73919
|
+
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;
|
|
72930
73920
|
new Set(Object.values(DeviceType));
|
|
72931
73921
|
Object.freeze({
|
|
72932
73922
|
"accessories.getStatus": {
|
|
@@ -72943,6 +73933,104 @@ Object.freeze({
|
|
|
72943
73933
|
access: "create",
|
|
72944
73934
|
role: "admin"
|
|
72945
73935
|
},
|
|
73936
|
+
"actions.create": {
|
|
73937
|
+
capName: "actions",
|
|
73938
|
+
capScope: "system",
|
|
73939
|
+
addonId: null,
|
|
73940
|
+
access: "create",
|
|
73941
|
+
role: "admin"
|
|
73942
|
+
},
|
|
73943
|
+
"actions.delete": {
|
|
73944
|
+
capName: "actions",
|
|
73945
|
+
capScope: "system",
|
|
73946
|
+
addonId: null,
|
|
73947
|
+
access: "delete",
|
|
73948
|
+
role: "admin"
|
|
73949
|
+
},
|
|
73950
|
+
"actions.get": {
|
|
73951
|
+
capName: "actions",
|
|
73952
|
+
capScope: "system",
|
|
73953
|
+
addonId: null,
|
|
73954
|
+
access: "view",
|
|
73955
|
+
role: "viewer"
|
|
73956
|
+
},
|
|
73957
|
+
"actions.getLinks": {
|
|
73958
|
+
capName: "actions",
|
|
73959
|
+
capScope: "system",
|
|
73960
|
+
addonId: null,
|
|
73961
|
+
access: "view",
|
|
73962
|
+
role: "admin"
|
|
73963
|
+
},
|
|
73964
|
+
"actions.getTraces": {
|
|
73965
|
+
capName: "actions",
|
|
73966
|
+
capScope: "system",
|
|
73967
|
+
addonId: null,
|
|
73968
|
+
access: "view",
|
|
73969
|
+
role: "admin"
|
|
73970
|
+
},
|
|
73971
|
+
"actions.list": {
|
|
73972
|
+
capName: "actions",
|
|
73973
|
+
capScope: "system",
|
|
73974
|
+
addonId: null,
|
|
73975
|
+
access: "view",
|
|
73976
|
+
role: "admin"
|
|
73977
|
+
},
|
|
73978
|
+
"actions.listForCamera": {
|
|
73979
|
+
capName: "actions",
|
|
73980
|
+
capScope: "system",
|
|
73981
|
+
addonId: null,
|
|
73982
|
+
access: "view",
|
|
73983
|
+
role: "viewer"
|
|
73984
|
+
},
|
|
73985
|
+
"actions.run": {
|
|
73986
|
+
capName: "actions",
|
|
73987
|
+
capScope: "system",
|
|
73988
|
+
addonId: null,
|
|
73989
|
+
access: "create",
|
|
73990
|
+
role: "viewer"
|
|
73991
|
+
},
|
|
73992
|
+
"actions.setLinks": {
|
|
73993
|
+
capName: "actions",
|
|
73994
|
+
capScope: "system",
|
|
73995
|
+
addonId: null,
|
|
73996
|
+
access: "create",
|
|
73997
|
+
role: "admin"
|
|
73998
|
+
},
|
|
73999
|
+
"actions.stop": {
|
|
74000
|
+
capName: "actions",
|
|
74001
|
+
capScope: "system",
|
|
74002
|
+
addonId: null,
|
|
74003
|
+
access: "create",
|
|
74004
|
+
role: "admin"
|
|
74005
|
+
},
|
|
74006
|
+
"actions.update": {
|
|
74007
|
+
capName: "actions",
|
|
74008
|
+
capScope: "system",
|
|
74009
|
+
addonId: null,
|
|
74010
|
+
access: "create",
|
|
74011
|
+
role: "admin"
|
|
74012
|
+
},
|
|
74013
|
+
"actionSource.listActions": {
|
|
74014
|
+
capName: "action-source",
|
|
74015
|
+
capScope: "system",
|
|
74016
|
+
addonId: null,
|
|
74017
|
+
access: "view",
|
|
74018
|
+
role: "admin"
|
|
74019
|
+
},
|
|
74020
|
+
"actionSource.runAction": {
|
|
74021
|
+
capName: "action-source",
|
|
74022
|
+
capScope: "system",
|
|
74023
|
+
addonId: null,
|
|
74024
|
+
access: "create",
|
|
74025
|
+
role: "admin"
|
|
74026
|
+
},
|
|
74027
|
+
"actionSource.stopAction": {
|
|
74028
|
+
capName: "action-source",
|
|
74029
|
+
capScope: "system",
|
|
74030
|
+
addonId: null,
|
|
74031
|
+
access: "create",
|
|
74032
|
+
role: "admin"
|
|
74033
|
+
},
|
|
72946
74034
|
"addonPages.listPages": {
|
|
72947
74035
|
capName: "addon-pages",
|
|
72948
74036
|
capScope: "system",
|
|
@@ -74224,6 +75312,13 @@ Object.freeze({
|
|
|
74224
75312
|
access: "view",
|
|
74225
75313
|
role: "admin"
|
|
74226
75314
|
},
|
|
75315
|
+
"coreBlocks.getAutomationRunState": {
|
|
75316
|
+
capName: "core-blocks",
|
|
75317
|
+
capScope: "system",
|
|
75318
|
+
addonId: null,
|
|
75319
|
+
access: "view",
|
|
75320
|
+
role: "admin"
|
|
75321
|
+
},
|
|
74227
75322
|
"coreBlocks.getBlockSettings": {
|
|
74228
75323
|
capName: "core-blocks",
|
|
74229
75324
|
capScope: "system",
|
|
@@ -74245,6 +75340,13 @@ Object.freeze({
|
|
|
74245
75340
|
access: "view",
|
|
74246
75341
|
role: "admin"
|
|
74247
75342
|
},
|
|
75343
|
+
"coreBlocks.getTraces": {
|
|
75344
|
+
capName: "core-blocks",
|
|
75345
|
+
capScope: "system",
|
|
75346
|
+
addonId: null,
|
|
75347
|
+
access: "view",
|
|
75348
|
+
role: "admin"
|
|
75349
|
+
},
|
|
74248
75350
|
"coreBlocks.getTypeDefs": {
|
|
74249
75351
|
capName: "core-blocks",
|
|
74250
75352
|
capScope: "system",
|
|
@@ -74280,6 +75382,13 @@ Object.freeze({
|
|
|
74280
75382
|
access: "create",
|
|
74281
75383
|
role: "admin"
|
|
74282
75384
|
},
|
|
75385
|
+
"coreBlocks.reportAutomationRun": {
|
|
75386
|
+
capName: "core-blocks",
|
|
75387
|
+
capScope: "system",
|
|
75388
|
+
addonId: null,
|
|
75389
|
+
access: "create",
|
|
75390
|
+
role: "admin"
|
|
75391
|
+
},
|
|
74283
75392
|
"coreBlocks.restart": {
|
|
74284
75393
|
capName: "core-blocks",
|
|
74285
75394
|
capScope: "system",
|
|
@@ -76149,6 +77258,13 @@ Object.freeze({
|
|
|
76149
77258
|
access: "view",
|
|
76150
77259
|
role: "viewer"
|
|
76151
77260
|
},
|
|
77261
|
+
"groupActive.getStatus": {
|
|
77262
|
+
capName: "group-active",
|
|
77263
|
+
capScope: "device",
|
|
77264
|
+
addonId: null,
|
|
77265
|
+
access: "view",
|
|
77266
|
+
role: "viewer"
|
|
77267
|
+
},
|
|
76152
77268
|
"groupCommands.getStatus": {
|
|
76153
77269
|
capName: "group-commands",
|
|
76154
77270
|
capScope: "device",
|
|
@@ -77115,6 +78231,13 @@ Object.freeze({
|
|
|
77115
78231
|
access: "create",
|
|
77116
78232
|
role: "admin"
|
|
77117
78233
|
},
|
|
78234
|
+
"meter.getStatus": {
|
|
78235
|
+
capName: "meter",
|
|
78236
|
+
capScope: "device",
|
|
78237
|
+
addonId: null,
|
|
78238
|
+
access: "view",
|
|
78239
|
+
role: "viewer"
|
|
78240
|
+
},
|
|
77118
78241
|
"metricsProvider.collectSnapshot": {
|
|
77119
78242
|
capName: "metrics-provider",
|
|
77120
78243
|
capScope: "system",
|
|
@@ -82839,7 +83962,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
82839
83962
|
label: t("Full brightness", "Luminosità massima"),
|
|
82840
83963
|
phrase: t("Set {device} to full brightness", "Porta {device} alla massima luminosità"),
|
|
82841
83964
|
icon: "sun",
|
|
82842
|
-
args: {
|
|
83965
|
+
args: { level: 100 }
|
|
82843
83966
|
},
|
|
82844
83967
|
{
|
|
82845
83968
|
id: "brightness.half",
|
|
@@ -82847,7 +83970,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
82847
83970
|
label: t("Half brightness", "Luminosità a metà"),
|
|
82848
83971
|
phrase: t("Dim {device} to half", "Abbassa {device} a metà luminosità"),
|
|
82849
83972
|
icon: "sun-dim",
|
|
82850
|
-
args: {
|
|
83973
|
+
args: { level: 50 }
|
|
82851
83974
|
},
|
|
82852
83975
|
{
|
|
82853
83976
|
id: "brightness.off",
|
|
@@ -82855,16 +83978,16 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
82855
83978
|
label: t("Brightness off", "Luminosità a zero"),
|
|
82856
83979
|
phrase: t("Dim {device} to zero", "Abbassa {device} a zero"),
|
|
82857
83980
|
icon: "lightbulb-off",
|
|
82858
|
-
args: {
|
|
83981
|
+
args: { level: 0 }
|
|
82859
83982
|
},
|
|
82860
83983
|
{
|
|
82861
83984
|
id: "brightness.set",
|
|
82862
83985
|
method: "setBrightness",
|
|
82863
83986
|
label: t("Set brightness", "Imposta la luminosità"),
|
|
82864
|
-
phrase: t("Set the brightness of {device} to {
|
|
83987
|
+
phrase: t("Set the brightness of {device} to {level}", "Imposta la luminosità di {device} a {level}"),
|
|
82865
83988
|
icon: "sun-medium",
|
|
82866
83989
|
openArgs: [{
|
|
82867
|
-
key: "
|
|
83990
|
+
key: "level",
|
|
82868
83991
|
label: PERCENT,
|
|
82869
83992
|
offer: {
|
|
82870
83993
|
kind: "number",
|
|
@@ -82878,13 +84001,13 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
82878
84001
|
],
|
|
82879
84002
|
state: [{
|
|
82880
84003
|
kind: "numeric",
|
|
82881
|
-
field: "
|
|
84004
|
+
field: "level",
|
|
82882
84005
|
quantity: t("the brightness", "la luminosità"),
|
|
82883
84006
|
label: t("Brightness", "Luminosità"),
|
|
82884
84007
|
unit: "%",
|
|
82885
84008
|
icon: "sun"
|
|
82886
84009
|
}],
|
|
82887
|
-
events: { onBrightnessChanged: { notOperatorFacing: "the `
|
|
84010
|
+
events: { onBrightnessChanged: { notOperatorFacing: "the `level` state trigger reads the same change" } }
|
|
82888
84011
|
},
|
|
82889
84012
|
color: {
|
|
82890
84013
|
scope: "device",
|
|
@@ -82898,7 +84021,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
82898
84021
|
icon: "sun",
|
|
82899
84022
|
args: { color: {
|
|
82900
84023
|
mode: "mired",
|
|
82901
|
-
|
|
84024
|
+
colorTemperature: 370
|
|
82902
84025
|
} }
|
|
82903
84026
|
},
|
|
82904
84027
|
{
|
|
@@ -82909,7 +84032,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
82909
84032
|
icon: "snowflake",
|
|
82910
84033
|
args: { color: {
|
|
82911
84034
|
mode: "mired",
|
|
82912
|
-
|
|
84035
|
+
colorTemperature: 200
|
|
82913
84036
|
} }
|
|
82914
84037
|
},
|
|
82915
84038
|
{
|
|
@@ -83523,7 +84646,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
83523
84646
|
label: t("Fan off", "Ventilatore spento"),
|
|
83524
84647
|
phrase: t("Stop {device}", "Ferma {device}"),
|
|
83525
84648
|
icon: "power-off",
|
|
83526
|
-
args: {
|
|
84649
|
+
args: { speed: 0 }
|
|
83527
84650
|
},
|
|
83528
84651
|
{
|
|
83529
84652
|
id: "fan.full",
|
|
@@ -83531,16 +84654,16 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
83531
84654
|
label: t("Fan full", "Ventilatore al massimo"),
|
|
83532
84655
|
phrase: t("Run {device} at full speed", "Porta {device} alla massima velocità"),
|
|
83533
84656
|
icon: "fan",
|
|
83534
|
-
args: {
|
|
84657
|
+
args: { speed: 100 }
|
|
83535
84658
|
},
|
|
83536
84659
|
{
|
|
83537
84660
|
id: "fan.speed",
|
|
83538
84661
|
method: "setPercentage",
|
|
83539
84662
|
label: t("Set speed", "Imposta la velocità"),
|
|
83540
|
-
phrase: t("Set the speed of {device} to {
|
|
84663
|
+
phrase: t("Set the speed of {device} to {speed}", "Imposta la velocità di {device} al {speed}"),
|
|
83541
84664
|
icon: "fan",
|
|
83542
84665
|
openArgs: [{
|
|
83543
|
-
key: "
|
|
84666
|
+
key: "speed",
|
|
83544
84667
|
label: t("Speed", "Velocità"),
|
|
83545
84668
|
offer: {
|
|
83546
84669
|
kind: "number",
|
|
@@ -83602,7 +84725,7 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
83602
84725
|
],
|
|
83603
84726
|
state: [{
|
|
83604
84727
|
kind: "numeric",
|
|
83605
|
-
field: "
|
|
84728
|
+
field: "speed",
|
|
83606
84729
|
quantity: t("the speed", "la velocità"),
|
|
83607
84730
|
label: t("Speed", "Velocità"),
|
|
83608
84731
|
unit: "%",
|
|
@@ -84400,7 +85523,7 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
84400
85523
|
},
|
|
84401
85524
|
{
|
|
84402
85525
|
kind: "numeric",
|
|
84403
|
-
field: "
|
|
85526
|
+
field: "progress",
|
|
84404
85527
|
quantity: t("the cycle progress", "l’avanzamento del lavaggio"),
|
|
84405
85528
|
label: t("Progress", "Avanzamento"),
|
|
84406
85529
|
unit: "%",
|
|
@@ -84758,10 +85881,10 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
84758
85881
|
id: "media.seek",
|
|
84759
85882
|
method: "seek",
|
|
84760
85883
|
label: t("Seek", "Vai al punto"),
|
|
84761
|
-
phrase: t("Move {device} to {
|
|
85884
|
+
phrase: t("Move {device} to {position}", "Porta {device} al punto {position}"),
|
|
84762
85885
|
icon: "fast-forward",
|
|
84763
85886
|
openArgs: [{
|
|
84764
|
-
key: "
|
|
85887
|
+
key: "position",
|
|
84765
85888
|
label: t("Position", "Posizione"),
|
|
84766
85889
|
offer: {
|
|
84767
85890
|
kind: "number",
|
|
@@ -85213,7 +86336,39 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
85213
86336
|
phrase: t("When {device} finishes", "Quando {device} finisce"),
|
|
85214
86337
|
icon: "square"
|
|
85215
86338
|
}
|
|
85216
|
-
}
|
|
86339
|
+
},
|
|
86340
|
+
overrides: { byType: {
|
|
86341
|
+
[DeviceType.Washer]: {
|
|
86342
|
+
state: { running: {
|
|
86343
|
+
whenTrue: t("When the washing machine starts", "Quando la lavatrice inizia"),
|
|
86344
|
+
whenFalse: t("When the washing machine has finished", "Quando la lavatrice ha finito")
|
|
86345
|
+
} },
|
|
86346
|
+
events: {
|
|
86347
|
+
onStarted: { phrase: t("When the washing machine starts", "Quando la lavatrice inizia") },
|
|
86348
|
+
onFinished: { phrase: t("When the washing machine has finished", "Quando la lavatrice ha finito") }
|
|
86349
|
+
}
|
|
86350
|
+
},
|
|
86351
|
+
[DeviceType.Dryer]: {
|
|
86352
|
+
state: { running: {
|
|
86353
|
+
whenTrue: t("When the dryer starts", "Quando l'asciugatrice inizia"),
|
|
86354
|
+
whenFalse: t("When the dryer has finished", "Quando l'asciugatrice ha finito")
|
|
86355
|
+
} },
|
|
86356
|
+
events: {
|
|
86357
|
+
onStarted: { phrase: t("When the dryer starts", "Quando l'asciugatrice inizia") },
|
|
86358
|
+
onFinished: { phrase: t("When the dryer has finished", "Quando l'asciugatrice ha finito") }
|
|
86359
|
+
}
|
|
86360
|
+
},
|
|
86361
|
+
[DeviceType.Dishwasher]: {
|
|
86362
|
+
state: { running: {
|
|
86363
|
+
whenTrue: t("When the dishwasher starts", "Quando la lavastoviglie inizia"),
|
|
86364
|
+
whenFalse: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito")
|
|
86365
|
+
} },
|
|
86366
|
+
events: {
|
|
86367
|
+
onStarted: { phrase: t("When the dishwasher starts", "Quando la lavastoviglie inizia") },
|
|
86368
|
+
onFinished: { phrase: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito") }
|
|
86369
|
+
}
|
|
86370
|
+
}
|
|
86371
|
+
} }
|
|
85217
86372
|
},
|
|
85218
86373
|
update: {
|
|
85219
86374
|
scope: "device",
|
|
@@ -85778,7 +86933,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
85778
86933
|
state: [
|
|
85779
86934
|
{
|
|
85780
86935
|
kind: "numeric",
|
|
85781
|
-
field: "
|
|
86936
|
+
field: "co2",
|
|
85782
86937
|
quantity: t("the CO₂", "la CO₂"),
|
|
85783
86938
|
label: t("CO₂", "CO₂"),
|
|
85784
86939
|
unit: "ppm",
|
|
@@ -85786,7 +86941,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
85786
86941
|
},
|
|
85787
86942
|
{
|
|
85788
86943
|
kind: "numeric",
|
|
85789
|
-
field: "
|
|
86944
|
+
field: "voc",
|
|
85790
86945
|
quantity: t("the VOC level", "il livello di COV"),
|
|
85791
86946
|
label: t("VOC", "COV"),
|
|
85792
86947
|
unit: "ppb",
|
|
@@ -85822,7 +86977,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
85822
86977
|
noun: t("Light level", "Luminosità ambiente"),
|
|
85823
86978
|
state: [{
|
|
85824
86979
|
kind: "numeric",
|
|
85825
|
-
field: "
|
|
86980
|
+
field: "illuminance",
|
|
85826
86981
|
quantity: t("the light level", "la luminosità"),
|
|
85827
86982
|
label: t("Illuminance", "Illuminamento"),
|
|
85828
86983
|
unit: "lx",
|
|
@@ -85985,6 +87140,10 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
85985
87140
|
icon: "flame"
|
|
85986
87141
|
}]
|
|
85987
87142
|
},
|
|
87143
|
+
"group-active": {
|
|
87144
|
+
scope: "device",
|
|
87145
|
+
noun: t("Group state", "Stato del gruppo")
|
|
87146
|
+
},
|
|
85988
87147
|
"group-commands": {
|
|
85989
87148
|
scope: "device",
|
|
85990
87149
|
noun: t("Group command", "Comando di gruppo")
|
|
@@ -86021,7 +87180,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
86021
87180
|
noun: t("Humidity", "Umidità"),
|
|
86022
87181
|
state: [{
|
|
86023
87182
|
kind: "numeric",
|
|
86024
|
-
field: "
|
|
87183
|
+
field: "humidity",
|
|
86025
87184
|
quantity: t("the humidity", "l’umidità"),
|
|
86026
87185
|
label: t("Humidity", "Umidità"),
|
|
86027
87186
|
unit: "%",
|
|
@@ -86041,6 +87200,17 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
86041
87200
|
}],
|
|
86042
87201
|
events: { onMotionChanged: { notOperatorFacing: "the `detected` state trigger phrases both edges, from the mirror that survives a dropped event (D49)" } }
|
|
86043
87202
|
},
|
|
87203
|
+
meter: {
|
|
87204
|
+
scope: "device",
|
|
87205
|
+
noun: t("Meter", "Contatore"),
|
|
87206
|
+
state: [{
|
|
87207
|
+
kind: "numeric",
|
|
87208
|
+
field: "value",
|
|
87209
|
+
quantity: t("the total", "il totale"),
|
|
87210
|
+
label: t("Total", "Totale"),
|
|
87211
|
+
icon: "gauge"
|
|
87212
|
+
}]
|
|
87213
|
+
},
|
|
86044
87214
|
"numeric-sensor": {
|
|
86045
87215
|
scope: "device",
|
|
86046
87216
|
noun: t("Sensor", "Sensore"),
|
|
@@ -86058,7 +87228,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
86058
87228
|
state: [
|
|
86059
87229
|
{
|
|
86060
87230
|
kind: "numeric",
|
|
86061
|
-
field: "
|
|
87231
|
+
field: "power",
|
|
86062
87232
|
quantity: t("the power draw", "il consumo"),
|
|
86063
87233
|
label: t("Power", "Potenza"),
|
|
86064
87234
|
unit: "W",
|
|
@@ -86066,7 +87236,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
86066
87236
|
},
|
|
86067
87237
|
{
|
|
86068
87238
|
kind: "numeric",
|
|
86069
|
-
field: "
|
|
87239
|
+
field: "energy",
|
|
86070
87240
|
quantity: t("the energy used", "l’energia consumata"),
|
|
86071
87241
|
label: t("Energy", "Energia"),
|
|
86072
87242
|
unit: "kWh",
|
|
@@ -86074,7 +87244,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
86074
87244
|
},
|
|
86075
87245
|
{
|
|
86076
87246
|
kind: "numeric",
|
|
86077
|
-
field: "
|
|
87247
|
+
field: "voltage",
|
|
86078
87248
|
quantity: t("the voltage", "la tensione"),
|
|
86079
87249
|
label: t("Voltage", "Tensione"),
|
|
86080
87250
|
unit: "V",
|
|
@@ -86082,7 +87252,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
86082
87252
|
},
|
|
86083
87253
|
{
|
|
86084
87254
|
kind: "numeric",
|
|
86085
|
-
field: "
|
|
87255
|
+
field: "current",
|
|
86086
87256
|
quantity: t("the current", "la corrente"),
|
|
86087
87257
|
label: t("Current", "Corrente"),
|
|
86088
87258
|
unit: "A",
|
|
@@ -86099,13 +87269,6 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
86099
87269
|
label: t("Where", "Posizione"),
|
|
86100
87270
|
phrase: t("When {device} is at {value}", "Quando {device} è in {value}"),
|
|
86101
87271
|
icon: "map-pin"
|
|
86102
|
-
}, {
|
|
86103
|
-
kind: "numeric",
|
|
86104
|
-
field: "batteryPercent",
|
|
86105
|
-
quantity: t("the phone battery", "la batteria del telefono"),
|
|
86106
|
-
label: t("Battery", "Batteria"),
|
|
86107
|
-
unit: "%",
|
|
86108
|
-
icon: "battery"
|
|
86109
87272
|
}]
|
|
86110
87273
|
},
|
|
86111
87274
|
"pressure-sensor": {
|
|
@@ -86113,7 +87276,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
86113
87276
|
noun: t("Pressure", "Pressione"),
|
|
86114
87277
|
state: [{
|
|
86115
87278
|
kind: "numeric",
|
|
86116
|
-
field: "
|
|
87279
|
+
field: "pressure",
|
|
86117
87280
|
quantity: t("the pressure", "la pressione"),
|
|
86118
87281
|
label: t("Pressure", "Pressione"),
|
|
86119
87282
|
unit: "hPa",
|
|
@@ -86149,7 +87312,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
86149
87312
|
noun: t("Temperature", "Temperatura"),
|
|
86150
87313
|
state: [{
|
|
86151
87314
|
kind: "numeric",
|
|
86152
|
-
field: "
|
|
87315
|
+
field: "temperature",
|
|
86153
87316
|
quantity: t("the temperature", "la temperatura"),
|
|
86154
87317
|
label: t("Temperature", "Temperatura"),
|
|
86155
87318
|
unit: "°C",
|
|
@@ -86220,7 +87383,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
86220
87383
|
state: [
|
|
86221
87384
|
{
|
|
86222
87385
|
kind: "numeric",
|
|
86223
|
-
field: "
|
|
87386
|
+
field: "level",
|
|
86224
87387
|
quantity: t("the battery level", "il livello della batteria"),
|
|
86225
87388
|
label: t("Battery", "Batteria"),
|
|
86226
87389
|
unit: "%",
|
|
@@ -86262,7 +87425,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
86262
87425
|
icon: "battery-charging"
|
|
86263
87426
|
}
|
|
86264
87427
|
],
|
|
86265
|
-
events: { onStatusChanged: { notOperatorFacing: "the
|
|
87428
|
+
events: { onStatusChanged: { notOperatorFacing: "the level / sleeping / charging state triggers each read one field of this change" } },
|
|
86266
87429
|
extraEvents: [{
|
|
86267
87430
|
category: EventCategory.DeviceAwake,
|
|
86268
87431
|
label: t("Woke up", "Risvegliato"),
|
|
@@ -86280,7 +87443,7 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
86280
87443
|
noun: t("Network link", "Collegamento di rete"),
|
|
86281
87444
|
state: [{
|
|
86282
87445
|
kind: "numeric",
|
|
86283
|
-
field: "
|
|
87446
|
+
field: "signal",
|
|
86284
87447
|
quantity: t("the signal strength", "l’intensità del segnale"),
|
|
86285
87448
|
label: t("Signal", "Segnale"),
|
|
86286
87449
|
unit: "%",
|
|
@@ -86373,6 +87536,8 @@ var ML = "model and training machinery — no device action";
|
|
|
86373
87536
|
var INTERNAL = "internal plumbing between addons — never operator-facing";
|
|
86374
87537
|
/** Every system-scoped cap, with why it is not a device's vocabulary. */
|
|
86375
87538
|
var SYSTEM_CAP_VOCABULARY = {
|
|
87539
|
+
"action-source": system(INTERNAL),
|
|
87540
|
+
actions: system("declared actions — run from the Actions page and from the cameras that link them"),
|
|
86376
87541
|
"addon-pages": system(UI_PLUMBING),
|
|
86377
87542
|
"addon-pages-source": system(UI_PLUMBING),
|
|
86378
87543
|
"addon-routes": system(UI_PLUMBING),
|
|
@@ -86513,8 +87678,47 @@ function projectAction(cap, action) {
|
|
|
86513
87678
|
};
|
|
86514
87679
|
}
|
|
86515
87680
|
Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
|
|
86516
|
-
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;
|
|
87681
|
+
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;
|
|
86517
87682
|
DeviceFeature.BatteryOperated;
|
|
87683
|
+
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
87684
|
+
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.";
|
|
87685
|
+
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.";
|
|
87686
|
+
var ALEXA_REASON = "Alexa has no laundry category in this export, and neither power nor running has an Alexa interface.";
|
|
87687
|
+
var GOOGLE_REASON = "Google WASHER, DRYER and DISHWASHER require RunCycle. A power threshold is not that trait, and this export does not publish RunCycle.";
|
|
87688
|
+
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.";
|
|
87689
|
+
function withheld(reason) {
|
|
87690
|
+
return {
|
|
87691
|
+
[DeviceType.Washer]: {
|
|
87692
|
+
exported: false,
|
|
87693
|
+
reason
|
|
87694
|
+
},
|
|
87695
|
+
[DeviceType.Dryer]: {
|
|
87696
|
+
exported: false,
|
|
87697
|
+
reason
|
|
87698
|
+
},
|
|
87699
|
+
[DeviceType.Dishwasher]: {
|
|
87700
|
+
exported: false,
|
|
87701
|
+
reason
|
|
87702
|
+
}
|
|
87703
|
+
};
|
|
87704
|
+
}
|
|
87705
|
+
function offered(received) {
|
|
87706
|
+
return {
|
|
87707
|
+
[DeviceType.Washer]: {
|
|
87708
|
+
exported: true,
|
|
87709
|
+
as: received
|
|
87710
|
+
},
|
|
87711
|
+
[DeviceType.Dryer]: {
|
|
87712
|
+
exported: true,
|
|
87713
|
+
as: received
|
|
87714
|
+
},
|
|
87715
|
+
[DeviceType.Dishwasher]: {
|
|
87716
|
+
exported: true,
|
|
87717
|
+
as: received
|
|
87718
|
+
}
|
|
87719
|
+
};
|
|
87720
|
+
}
|
|
87721
|
+
offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
|
|
86518
87722
|
var LAST_FETCHED_FIELD = "lastFetchedAt";
|
|
86519
87723
|
/**
|
|
86520
87724
|
* How long a bridge stops re-attempting a refresh that did not land.
|
|
@@ -86682,6 +87886,31 @@ Object.freeze({
|
|
|
86682
87886
|
form: "single",
|
|
86683
87887
|
optional: false
|
|
86684
87888
|
}],
|
|
87889
|
+
"actions.get": [{
|
|
87890
|
+
name: "deviceId",
|
|
87891
|
+
form: "single",
|
|
87892
|
+
optional: true
|
|
87893
|
+
}],
|
|
87894
|
+
"actions.getLinks": [{
|
|
87895
|
+
name: "deviceId",
|
|
87896
|
+
form: "single",
|
|
87897
|
+
optional: false
|
|
87898
|
+
}],
|
|
87899
|
+
"actions.listForCamera": [{
|
|
87900
|
+
name: "deviceId",
|
|
87901
|
+
form: "single",
|
|
87902
|
+
optional: false
|
|
87903
|
+
}],
|
|
87904
|
+
"actions.run": [{
|
|
87905
|
+
name: "deviceId",
|
|
87906
|
+
form: "single",
|
|
87907
|
+
optional: true
|
|
87908
|
+
}],
|
|
87909
|
+
"actions.setLinks": [{
|
|
87910
|
+
name: "deviceId",
|
|
87911
|
+
form: "single",
|
|
87912
|
+
optional: false
|
|
87913
|
+
}],
|
|
86685
87914
|
"addonSettings.getDeviceSettings": [{
|
|
86686
87915
|
name: "deviceId",
|
|
86687
87916
|
form: "single",
|
|
@@ -87633,6 +88862,11 @@ Object.freeze({
|
|
|
87633
88862
|
form: "single",
|
|
87634
88863
|
optional: false
|
|
87635
88864
|
}],
|
|
88865
|
+
"groupActive.getStatus": [{
|
|
88866
|
+
name: "deviceId",
|
|
88867
|
+
form: "single",
|
|
88868
|
+
optional: false
|
|
88869
|
+
}],
|
|
87636
88870
|
"groupCommands.getStatus": [{
|
|
87637
88871
|
name: "deviceId",
|
|
87638
88872
|
form: "single",
|
|
@@ -87843,6 +89077,11 @@ Object.freeze({
|
|
|
87843
89077
|
form: "single",
|
|
87844
89078
|
optional: false
|
|
87845
89079
|
}],
|
|
89080
|
+
"meter.getStatus": [{
|
|
89081
|
+
name: "deviceId",
|
|
89082
|
+
form: "single",
|
|
89083
|
+
optional: false
|
|
89084
|
+
}],
|
|
87846
89085
|
"motion.getStatus": [{
|
|
87847
89086
|
name: "deviceId",
|
|
87848
89087
|
form: "single",
|
|
@@ -89450,7 +90689,6 @@ Object.freeze({
|
|
|
89450
90689
|
"network-access": "ingress",
|
|
89451
90690
|
"smtp-provider": "email"
|
|
89452
90691
|
});
|
|
89453
|
-
DeviceType.Camera;
|
|
89454
90692
|
/**
|
|
89455
90693
|
* A saved Customize assignment (D936). The schema is the type: callers use
|
|
89456
90694
|
* `z.infer`, they do not restate the shape.
|
|
@@ -90805,6 +92043,7 @@ DEFAULT_NATIVE_LEASE_SETTINGS.activityMs;
|
|
|
90805
92043
|
DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
|
|
90806
92044
|
DEFAULT_NATIVE_LEASE_SETTINGS.sceneBudgetMb;
|
|
90807
92045
|
DEFAULT_NATIVE_LEASE_SETTINGS.admission;
|
|
92046
|
+
String.raw`(?<=^|["'(\s>,\[{])`;
|
|
90808
92047
|
var MB = 1024 * 1024;
|
|
90809
92048
|
1024 * MB, 3072 * MB;
|
|
90810
92049
|
//#endregion
|
|
@@ -200174,7 +201413,7 @@ var FloodlightAccessory = class extends BaseDevice {
|
|
|
200174
201413
|
lastChangedAt: ts
|
|
200175
201414
|
});
|
|
200176
201415
|
if (brightness !== void 0) this.setCapSlice(brightnessCapability, {
|
|
200177
|
-
|
|
201416
|
+
level: brightness,
|
|
200178
201417
|
lastChangedAt: ts
|
|
200179
201418
|
});
|
|
200180
201419
|
this.hasFetchedFromFirmware = true;
|
|
@@ -200293,7 +201532,7 @@ var FloodlightAccessory = class extends BaseDevice {
|
|
|
200293
201532
|
}
|
|
200294
201533
|
brightnessView() {
|
|
200295
201534
|
return this.getCapSlice(brightnessCapability) ?? {
|
|
200296
|
-
|
|
201535
|
+
level: 100,
|
|
200297
201536
|
lastChangedAt: 0
|
|
200298
201537
|
};
|
|
200299
201538
|
}
|
|
@@ -200318,19 +201557,19 @@ var FloodlightAccessory = class extends BaseDevice {
|
|
|
200318
201557
|
registerBrightnessCap() {
|
|
200319
201558
|
this.ctx.registerNativeCap(brightnessCapability, {
|
|
200320
201559
|
getStatus: async () => this.brightnessView(),
|
|
200321
|
-
setBrightness: async ({ deviceId, percentage }) => {
|
|
201560
|
+
setBrightness: async ({ deviceId, level: percentage }) => {
|
|
200322
201561
|
if (deviceId !== this.id) return;
|
|
200323
201562
|
const value = Math.max(0, Math.min(100, percentage));
|
|
200324
201563
|
if (this.switchView().on) await (await this.api).setWhiteLedState(this.channel, void 0, value);
|
|
200325
201564
|
this.setCapSlice(brightnessCapability, {
|
|
200326
|
-
|
|
201565
|
+
level: value,
|
|
200327
201566
|
lastChangedAt: Date.now()
|
|
200328
201567
|
});
|
|
200329
201568
|
this.lastSetAtMs = Date.now();
|
|
200330
201569
|
}
|
|
200331
201570
|
});
|
|
200332
201571
|
this.setCapSlice(brightnessCapability, {
|
|
200333
|
-
|
|
201572
|
+
level: 100,
|
|
200334
201573
|
lastChangedAt: 0
|
|
200335
201574
|
});
|
|
200336
201575
|
}
|
|
@@ -201006,13 +202245,13 @@ function mapNetworkReadout(readout, now) {
|
|
|
201006
202245
|
const type = linkTypeOf(readout);
|
|
201007
202246
|
const raw = readout.wifiSignal;
|
|
201008
202247
|
const wireless = type === "wifi" || type === "cellular";
|
|
201009
|
-
const
|
|
202248
|
+
const signal = wireless ? signalPercentOf(raw) : null;
|
|
201010
202249
|
const rssiDbm = wireless && raw !== void 0 && Number.isFinite(raw) && raw < 0 ? raw : void 0;
|
|
201011
202250
|
const ssid = wireless && readout.ssid !== void 0 && readout.ssid !== "" ? readout.ssid : void 0;
|
|
201012
202251
|
return {
|
|
201013
202252
|
type,
|
|
201014
|
-
|
|
201015
|
-
...rssiDbm !== void 0 ? { rssiDbm } : {},
|
|
202253
|
+
signal,
|
|
202254
|
+
...rssiDbm !== void 0 ? { rssi: rssiDbm } : {},
|
|
201016
202255
|
...ssid !== void 0 ? { ssid } : {},
|
|
201017
202256
|
lastUpdated: now
|
|
201018
202257
|
};
|
|
@@ -204255,7 +205494,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
204255
205494
|
*/
|
|
204256
205495
|
mapBatteryInfo(info) {
|
|
204257
205496
|
return {
|
|
204258
|
-
|
|
205497
|
+
level: typeof info.batteryPercent === "number" ? Math.max(0, Math.min(100, Math.round(info.batteryPercent))) : this.state.battery.level ?? null,
|
|
204259
205498
|
charging: reportsPowerSource(info) ? deriveChargingSource(info.adapterStatus, info.chargeStatus) : this.state.battery.charging ?? "unknown",
|
|
204260
205499
|
sleeping: info.sleeping === true || this.sleeping,
|
|
204261
205500
|
lastUpdated: Date.now()
|
|
@@ -204391,7 +205630,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
204391
205630
|
}
|
|
204392
205631
|
});
|
|
204393
205632
|
if (this.getCapSlice(batteryCapability) === null) this.setCapSlice(batteryCapability, {
|
|
204394
|
-
|
|
205633
|
+
level: null,
|
|
204395
205634
|
charging: "none",
|
|
204396
205635
|
sleeping: false,
|
|
204397
205636
|
lastUpdated: Date.now()
|
|
@@ -204619,7 +205858,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
|
|
|
204619
205858
|
});
|
|
204620
205859
|
this.setCapSlice(batteryCapability, {
|
|
204621
205860
|
...this.state.battery,
|
|
204622
|
-
|
|
205861
|
+
level: mapped.level,
|
|
204623
205862
|
charging: mapped.charging,
|
|
204624
205863
|
lastUpdated: Date.now()
|
|
204625
205864
|
});
|