@camstack/addon-provider-enpal 0.1.21 → 0.1.23
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 +12 -0
- package/dist/addon.js +1769 -213
- package/dist/addon.mjs +1769 -213
- package/package.json +2 -2
package/dist/addon.js
CHANGED
|
@@ -3,7 +3,7 @@ let node_child_process = require("node:child_process");
|
|
|
3
3
|
let node_fs = require("node:fs");
|
|
4
4
|
let node_path = require("node:path");
|
|
5
5
|
let node_url = require("node:url");
|
|
6
|
-
//#region ../types/dist/event-category-
|
|
6
|
+
//#region ../types/dist/event-category-Dhcd0TRH.mjs
|
|
7
7
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
8
8
|
EventCategory["SystemBoot"] = "system.boot";
|
|
9
9
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -277,6 +277,18 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
277
277
|
EventCategory["StorageLocationCritical"] = "storage.location.critical";
|
|
278
278
|
/** Recovery from low/critical on a storage location. */
|
|
279
279
|
EventCategory["StorageLocationOk"] = "storage.location.ok";
|
|
280
|
+
/**
|
|
281
|
+
* A `drain` location's regular progress ping (#538) — location id + name,
|
|
282
|
+
* bytes remaining, the observed cede rate and the estimated empty date, or
|
|
283
|
+
* `null` fields when the occupancy read that sweep failed (D393: never
|
|
284
|
+
* folded to 0). Cadence is the named constant
|
|
285
|
+
* `DRAIN_PROGRESS_EVENT_INTERVAL_MS` (15 min) plus once on drain start — no
|
|
286
|
+
* operator knob. Telemetry (D8): a lost tick is a stale progress bar the
|
|
287
|
+
* next sweep corrects.
|
|
288
|
+
*/
|
|
289
|
+
EventCategory["StorageDrainProgress"] = "storage.drain.progress";
|
|
290
|
+
/** Fires once when a draining location reaches zero evictable bytes. */
|
|
291
|
+
EventCategory["StorageDrainComplete"] = "storage.drain.complete";
|
|
280
292
|
EventCategory["RecordingSegmentWritten"] = "recording.segment.written";
|
|
281
293
|
EventCategory["RecordingPolicyFallback"] = "recording.policy.fallback";
|
|
282
294
|
EventCategory["RecordingRetentionCompleted"] = "recording.retention.completed";
|
|
@@ -815,7 +827,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
815
827
|
return EventCategory;
|
|
816
828
|
}({});
|
|
817
829
|
//#endregion
|
|
818
|
-
//#region ../types/dist/composed-stable-id-
|
|
830
|
+
//#region ../types/dist/composed-stable-id-RvQIEpqg.mjs
|
|
819
831
|
/**
|
|
820
832
|
import { errMsg } from '@camstack/types'
|
|
821
833
|
* Extract a human-readable message from an unknown error value.
|
|
@@ -828,8 +840,6 @@ function errMsg(err) {
|
|
|
828
840
|
}
|
|
829
841
|
var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
830
842
|
DeviceType["Camera"] = "camera";
|
|
831
|
-
/** Legacy stored value, read only by the hub-to-integration migrations; no live device carries it. */
|
|
832
|
-
DeviceType["Hub"] = "hub";
|
|
833
843
|
DeviceType["Light"] = "light";
|
|
834
844
|
DeviceType["Siren"] = "siren";
|
|
835
845
|
DeviceType["Switch"] = "switch";
|
|
@@ -899,6 +909,9 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
|
899
909
|
/** Weather provider (HA `weather.*`). Tier-3, low MVP priority.
|
|
900
910
|
* `weather` cap. */
|
|
901
911
|
DeviceType["Weather"] = "weather";
|
|
912
|
+
/** A physical weather station (Ecowitt outdoor array). `weather-station`
|
|
913
|
+
* cap. Distinct from {@link Weather}, which is a forecast provider. */
|
|
914
|
+
DeviceType["WeatherStation"] = "weather-station";
|
|
902
915
|
/** Robot vacuum (HA `vacuum.*`). Tier-3. `vacuum-control` cap. */
|
|
903
916
|
DeviceType["Vacuum"] = "vacuum";
|
|
904
917
|
/** Robotic lawn mower (HA `lawn_mower.*`). Tier-3.
|
|
@@ -6066,7 +6079,7 @@ var ZodIssueCode = {
|
|
|
6066
6079
|
var ZodFirstPartyTypeKind;
|
|
6067
6080
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
6068
6081
|
//#endregion
|
|
6069
|
-
//#region ../types/dist/sleep-
|
|
6082
|
+
//#region ../types/dist/sleep-CLDVTB5R.mjs
|
|
6070
6083
|
/**
|
|
6071
6084
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
6072
6085
|
* window to float samples (D455).
|
|
@@ -6729,7 +6742,8 @@ async function runAddonMigrations(host, migrations, logger, options = {}) {
|
|
|
6729
6742
|
try {
|
|
6730
6743
|
declaration = await host.declareOwner({
|
|
6731
6744
|
owner,
|
|
6732
|
-
metas: migrations.map((m) => m.meta)
|
|
6745
|
+
metas: migrations.map((m) => m.meta),
|
|
6746
|
+
...options.retired === void 0 ? {} : { retired: [...options.retired] }
|
|
6733
6747
|
});
|
|
6734
6748
|
} catch (err) {
|
|
6735
6749
|
log.error("the migration engine refused or could not take this addon catalogue — none ran", { meta: { error: errMsg(err) } });
|
|
@@ -8722,6 +8736,14 @@ var BaseAddon = class {
|
|
|
8722
8736
|
declareRegistrySwitch(spec) {
|
|
8723
8737
|
this.registrySwitchSpec = spec;
|
|
8724
8738
|
}
|
|
8739
|
+
/**
|
|
8740
|
+
* Ids this addon removed from its catalogue on purpose (`declareRetiredMigrations`): sent with
|
|
8741
|
+
* every declaration so the engine does not read their applied rows as a newer database.
|
|
8742
|
+
*/
|
|
8743
|
+
retiredMigrationIds = [];
|
|
8744
|
+
declareRetiredMigrations(ids) {
|
|
8745
|
+
this.retiredMigrationIds = ids;
|
|
8746
|
+
}
|
|
8725
8747
|
registrySwitchSpec = null;
|
|
8726
8748
|
registrySwitchBound() {
|
|
8727
8749
|
const spec = this.registrySwitchSpec;
|
|
@@ -8745,10 +8767,14 @@ var BaseAddon = class {
|
|
|
8745
8767
|
async runBound(declared, options) {
|
|
8746
8768
|
const added = this.registrySwitchBound();
|
|
8747
8769
|
const migrations = [...declared, ...added];
|
|
8748
|
-
const
|
|
8770
|
+
const widened = options?.only === void 0 || added.length === 0 ? options : {
|
|
8749
8771
|
...options,
|
|
8750
8772
|
only: [...options.only, ...added.map((m) => m.meta.id)]
|
|
8751
8773
|
};
|
|
8774
|
+
const phased = this.retiredMigrationIds.length === 0 ? widened ?? {} : {
|
|
8775
|
+
...widened,
|
|
8776
|
+
retired: this.retiredMigrationIds
|
|
8777
|
+
};
|
|
8752
8778
|
const api = this.ctx.api.migrations;
|
|
8753
8779
|
const report = await runAddonMigrations({
|
|
8754
8780
|
declareOwner: (input) => api.declareOwner.mutate(input),
|
|
@@ -13435,6 +13461,76 @@ var CAP_VOCABULARY_BY_NAME = {
|
|
|
13435
13461
|
en: "Control",
|
|
13436
13462
|
it: "Controllo"
|
|
13437
13463
|
},
|
|
13464
|
+
actions: [
|
|
13465
|
+
{
|
|
13466
|
+
id: "select.next",
|
|
13467
|
+
method: "next",
|
|
13468
|
+
label: {
|
|
13469
|
+
en: "Next option",
|
|
13470
|
+
it: "Opzione successiva"
|
|
13471
|
+
},
|
|
13472
|
+
phrase: {
|
|
13473
|
+
en: "Select the next option on {device}",
|
|
13474
|
+
it: "Seleziona l’opzione successiva su {device}"
|
|
13475
|
+
},
|
|
13476
|
+
icon: "skip-forward",
|
|
13477
|
+
openArgs: [{
|
|
13478
|
+
key: "cycle",
|
|
13479
|
+
label: {
|
|
13480
|
+
en: "Wrap to the first option",
|
|
13481
|
+
it: "Ricomincia dalla prima opzione"
|
|
13482
|
+
},
|
|
13483
|
+
offer: { kind: "boolean" }
|
|
13484
|
+
}]
|
|
13485
|
+
},
|
|
13486
|
+
{
|
|
13487
|
+
id: "select.previous",
|
|
13488
|
+
method: "previous",
|
|
13489
|
+
label: {
|
|
13490
|
+
en: "Previous option",
|
|
13491
|
+
it: "Opzione precedente"
|
|
13492
|
+
},
|
|
13493
|
+
phrase: {
|
|
13494
|
+
en: "Select the previous option on {device}",
|
|
13495
|
+
it: "Seleziona l’opzione precedente su {device}"
|
|
13496
|
+
},
|
|
13497
|
+
icon: "skip-back",
|
|
13498
|
+
openArgs: [{
|
|
13499
|
+
key: "cycle",
|
|
13500
|
+
label: {
|
|
13501
|
+
en: "Wrap to the last option",
|
|
13502
|
+
it: "Ricomincia dall’ultima opzione"
|
|
13503
|
+
},
|
|
13504
|
+
offer: { kind: "boolean" }
|
|
13505
|
+
}]
|
|
13506
|
+
},
|
|
13507
|
+
{
|
|
13508
|
+
id: "select.first",
|
|
13509
|
+
method: "first",
|
|
13510
|
+
label: {
|
|
13511
|
+
en: "First option",
|
|
13512
|
+
it: "Prima opzione"
|
|
13513
|
+
},
|
|
13514
|
+
phrase: {
|
|
13515
|
+
en: "Select the first option on {device}",
|
|
13516
|
+
it: "Seleziona la prima opzione su {device}"
|
|
13517
|
+
},
|
|
13518
|
+
icon: "chevron-first"
|
|
13519
|
+
},
|
|
13520
|
+
{
|
|
13521
|
+
id: "select.last",
|
|
13522
|
+
method: "last",
|
|
13523
|
+
label: {
|
|
13524
|
+
en: "Last option",
|
|
13525
|
+
it: "Ultima opzione"
|
|
13526
|
+
},
|
|
13527
|
+
phrase: {
|
|
13528
|
+
en: "Select the last option on {device}",
|
|
13529
|
+
it: "Seleziona l’ultima opzione su {device}"
|
|
13530
|
+
},
|
|
13531
|
+
icon: "chevron-last"
|
|
13532
|
+
}
|
|
13533
|
+
],
|
|
13438
13534
|
notOperatorFacing: { setValue: "the value shape depends on the control kind (numeric / select / text / datetime) read from status; a typed picker for it is a follow-up of the automations rework" },
|
|
13439
13535
|
state: [{
|
|
13440
13536
|
kind: "value",
|
|
@@ -18581,6 +18677,295 @@ var CAP_VOCABULARY_BY_NAME = {
|
|
|
18581
18677
|
}
|
|
18582
18678
|
]
|
|
18583
18679
|
},
|
|
18680
|
+
"weather-station": {
|
|
18681
|
+
scope: "device",
|
|
18682
|
+
noun: {
|
|
18683
|
+
en: "Weather station",
|
|
18684
|
+
it: "Stazione meteo"
|
|
18685
|
+
},
|
|
18686
|
+
state: [
|
|
18687
|
+
{
|
|
18688
|
+
kind: "numeric",
|
|
18689
|
+
field: "temperature",
|
|
18690
|
+
quantity: {
|
|
18691
|
+
en: "the outdoor temperature",
|
|
18692
|
+
it: "la temperatura esterna"
|
|
18693
|
+
},
|
|
18694
|
+
label: {
|
|
18695
|
+
en: "Temperature",
|
|
18696
|
+
it: "Temperatura"
|
|
18697
|
+
},
|
|
18698
|
+
unit: "°C",
|
|
18699
|
+
icon: "thermometer"
|
|
18700
|
+
},
|
|
18701
|
+
{
|
|
18702
|
+
kind: "numeric",
|
|
18703
|
+
field: "humidity",
|
|
18704
|
+
quantity: {
|
|
18705
|
+
en: "the outdoor humidity",
|
|
18706
|
+
it: "l’umidità esterna"
|
|
18707
|
+
},
|
|
18708
|
+
label: {
|
|
18709
|
+
en: "Humidity",
|
|
18710
|
+
it: "Umidità"
|
|
18711
|
+
},
|
|
18712
|
+
unit: "%",
|
|
18713
|
+
icon: "droplets"
|
|
18714
|
+
},
|
|
18715
|
+
{
|
|
18716
|
+
kind: "numeric",
|
|
18717
|
+
field: "pressure",
|
|
18718
|
+
quantity: {
|
|
18719
|
+
en: "the air pressure",
|
|
18720
|
+
it: "la pressione atmosferica"
|
|
18721
|
+
},
|
|
18722
|
+
label: {
|
|
18723
|
+
en: "Pressure",
|
|
18724
|
+
it: "Pressione"
|
|
18725
|
+
},
|
|
18726
|
+
unit: "hPa",
|
|
18727
|
+
icon: "gauge"
|
|
18728
|
+
},
|
|
18729
|
+
{
|
|
18730
|
+
kind: "numeric",
|
|
18731
|
+
field: "windSpeed",
|
|
18732
|
+
quantity: {
|
|
18733
|
+
en: "the wind speed",
|
|
18734
|
+
it: "la velocità del vento"
|
|
18735
|
+
},
|
|
18736
|
+
label: {
|
|
18737
|
+
en: "Wind",
|
|
18738
|
+
it: "Vento"
|
|
18739
|
+
},
|
|
18740
|
+
unit: "m/s",
|
|
18741
|
+
icon: "wind"
|
|
18742
|
+
},
|
|
18743
|
+
{
|
|
18744
|
+
kind: "numeric",
|
|
18745
|
+
field: "windGust",
|
|
18746
|
+
quantity: {
|
|
18747
|
+
en: "the wind gust",
|
|
18748
|
+
it: "la raffica di vento"
|
|
18749
|
+
},
|
|
18750
|
+
label: {
|
|
18751
|
+
en: "Gust",
|
|
18752
|
+
it: "Raffica"
|
|
18753
|
+
},
|
|
18754
|
+
unit: "m/s",
|
|
18755
|
+
icon: "wind"
|
|
18756
|
+
},
|
|
18757
|
+
{
|
|
18758
|
+
kind: "numeric",
|
|
18759
|
+
field: "windBearing",
|
|
18760
|
+
quantity: {
|
|
18761
|
+
en: "the wind bearing",
|
|
18762
|
+
it: "la direzione del vento"
|
|
18763
|
+
},
|
|
18764
|
+
label: {
|
|
18765
|
+
en: "Wind bearing",
|
|
18766
|
+
it: "Direzione del vento"
|
|
18767
|
+
},
|
|
18768
|
+
unit: "°",
|
|
18769
|
+
icon: "wind"
|
|
18770
|
+
},
|
|
18771
|
+
{
|
|
18772
|
+
kind: "numeric",
|
|
18773
|
+
field: "rainRate",
|
|
18774
|
+
quantity: {
|
|
18775
|
+
en: "the rain rate",
|
|
18776
|
+
it: "l’intensità della pioggia"
|
|
18777
|
+
},
|
|
18778
|
+
label: {
|
|
18779
|
+
en: "Rain rate",
|
|
18780
|
+
it: "Intensità pioggia"
|
|
18781
|
+
},
|
|
18782
|
+
unit: "mm/h",
|
|
18783
|
+
icon: "droplets"
|
|
18784
|
+
},
|
|
18785
|
+
{
|
|
18786
|
+
kind: "numeric",
|
|
18787
|
+
field: "rainDaily",
|
|
18788
|
+
quantity: {
|
|
18789
|
+
en: "the rain today",
|
|
18790
|
+
it: "la pioggia di oggi"
|
|
18791
|
+
},
|
|
18792
|
+
label: {
|
|
18793
|
+
en: "Rain today",
|
|
18794
|
+
it: "Pioggia oggi"
|
|
18795
|
+
},
|
|
18796
|
+
unit: "mm",
|
|
18797
|
+
icon: "droplets"
|
|
18798
|
+
},
|
|
18799
|
+
{
|
|
18800
|
+
kind: "numeric",
|
|
18801
|
+
field: "rainEvent",
|
|
18802
|
+
quantity: {
|
|
18803
|
+
en: "the rain this event",
|
|
18804
|
+
it: "la pioggia di questo evento"
|
|
18805
|
+
},
|
|
18806
|
+
label: {
|
|
18807
|
+
en: "Rain event",
|
|
18808
|
+
it: "Evento di pioggia"
|
|
18809
|
+
},
|
|
18810
|
+
unit: "mm",
|
|
18811
|
+
icon: "droplets"
|
|
18812
|
+
},
|
|
18813
|
+
{
|
|
18814
|
+
kind: "numeric",
|
|
18815
|
+
field: "uv",
|
|
18816
|
+
quantity: {
|
|
18817
|
+
en: "the UV index",
|
|
18818
|
+
it: "l’indice UV"
|
|
18819
|
+
},
|
|
18820
|
+
label: {
|
|
18821
|
+
en: "UV",
|
|
18822
|
+
it: "UV"
|
|
18823
|
+
},
|
|
18824
|
+
unit: "",
|
|
18825
|
+
icon: "cloud-sun"
|
|
18826
|
+
},
|
|
18827
|
+
{
|
|
18828
|
+
kind: "numeric",
|
|
18829
|
+
field: "solarRadiation",
|
|
18830
|
+
quantity: {
|
|
18831
|
+
en: "the solar radiation",
|
|
18832
|
+
it: "la radiazione solare"
|
|
18833
|
+
},
|
|
18834
|
+
label: {
|
|
18835
|
+
en: "Solar",
|
|
18836
|
+
it: "Sole"
|
|
18837
|
+
},
|
|
18838
|
+
unit: "W/m²",
|
|
18839
|
+
icon: "cloud-sun"
|
|
18840
|
+
},
|
|
18841
|
+
{
|
|
18842
|
+
kind: "numeric",
|
|
18843
|
+
field: "dewPoint",
|
|
18844
|
+
quantity: {
|
|
18845
|
+
en: "the dew point",
|
|
18846
|
+
it: "il punto di rugiada"
|
|
18847
|
+
},
|
|
18848
|
+
label: {
|
|
18849
|
+
en: "Dew point",
|
|
18850
|
+
it: "Punto di rugiada"
|
|
18851
|
+
},
|
|
18852
|
+
unit: "°C",
|
|
18853
|
+
icon: "thermometer"
|
|
18854
|
+
},
|
|
18855
|
+
{
|
|
18856
|
+
kind: "numeric",
|
|
18857
|
+
field: "feelsLike",
|
|
18858
|
+
quantity: {
|
|
18859
|
+
en: "the feels-like temperature",
|
|
18860
|
+
it: "la temperatura percepita"
|
|
18861
|
+
},
|
|
18862
|
+
label: {
|
|
18863
|
+
en: "Feels like",
|
|
18864
|
+
it: "Percepita"
|
|
18865
|
+
},
|
|
18866
|
+
unit: "°C",
|
|
18867
|
+
icon: "thermometer"
|
|
18868
|
+
},
|
|
18869
|
+
{
|
|
18870
|
+
kind: "numeric",
|
|
18871
|
+
field: "vpd",
|
|
18872
|
+
quantity: {
|
|
18873
|
+
en: "the vapour-pressure deficit",
|
|
18874
|
+
it: "il deficit di pressione di vapore"
|
|
18875
|
+
},
|
|
18876
|
+
label: {
|
|
18877
|
+
en: "VPD",
|
|
18878
|
+
it: "VPD"
|
|
18879
|
+
},
|
|
18880
|
+
unit: "kPa",
|
|
18881
|
+
icon: "gauge"
|
|
18882
|
+
},
|
|
18883
|
+
{
|
|
18884
|
+
kind: "numeric",
|
|
18885
|
+
field: "windGustMax",
|
|
18886
|
+
quantity: {
|
|
18887
|
+
en: "the highest gust today",
|
|
18888
|
+
it: "la raffica massima di oggi"
|
|
18889
|
+
},
|
|
18890
|
+
label: {
|
|
18891
|
+
en: "Max gust",
|
|
18892
|
+
it: "Raffica max"
|
|
18893
|
+
},
|
|
18894
|
+
unit: "m/s",
|
|
18895
|
+
icon: "wind"
|
|
18896
|
+
},
|
|
18897
|
+
{
|
|
18898
|
+
kind: "numeric",
|
|
18899
|
+
field: "windBearingAvg",
|
|
18900
|
+
quantity: {
|
|
18901
|
+
en: "the average wind bearing",
|
|
18902
|
+
it: "la direzione media del vento"
|
|
18903
|
+
},
|
|
18904
|
+
label: {
|
|
18905
|
+
en: "Avg bearing",
|
|
18906
|
+
it: "Direzione media"
|
|
18907
|
+
},
|
|
18908
|
+
unit: "°",
|
|
18909
|
+
icon: "wind"
|
|
18910
|
+
},
|
|
18911
|
+
{
|
|
18912
|
+
kind: "numeric",
|
|
18913
|
+
field: "rain24h",
|
|
18914
|
+
quantity: {
|
|
18915
|
+
en: "the rain in the last 24 hours",
|
|
18916
|
+
it: "la pioggia delle ultime 24 ore"
|
|
18917
|
+
},
|
|
18918
|
+
label: {
|
|
18919
|
+
en: "Rain 24 h",
|
|
18920
|
+
it: "Pioggia 24 h"
|
|
18921
|
+
},
|
|
18922
|
+
unit: "mm",
|
|
18923
|
+
icon: "droplets"
|
|
18924
|
+
},
|
|
18925
|
+
{
|
|
18926
|
+
kind: "numeric",
|
|
18927
|
+
field: "rainWeekly",
|
|
18928
|
+
quantity: {
|
|
18929
|
+
en: "the rain this week",
|
|
18930
|
+
it: "la pioggia di questa settimana"
|
|
18931
|
+
},
|
|
18932
|
+
label: {
|
|
18933
|
+
en: "Rain week",
|
|
18934
|
+
it: "Pioggia settimana"
|
|
18935
|
+
},
|
|
18936
|
+
unit: "mm",
|
|
18937
|
+
icon: "droplets"
|
|
18938
|
+
},
|
|
18939
|
+
{
|
|
18940
|
+
kind: "numeric",
|
|
18941
|
+
field: "rainMonthly",
|
|
18942
|
+
quantity: {
|
|
18943
|
+
en: "the rain this month",
|
|
18944
|
+
it: "la pioggia di questo mese"
|
|
18945
|
+
},
|
|
18946
|
+
label: {
|
|
18947
|
+
en: "Rain month",
|
|
18948
|
+
it: "Pioggia mese"
|
|
18949
|
+
},
|
|
18950
|
+
unit: "mm",
|
|
18951
|
+
icon: "droplets"
|
|
18952
|
+
},
|
|
18953
|
+
{
|
|
18954
|
+
kind: "numeric",
|
|
18955
|
+
field: "rainYearly",
|
|
18956
|
+
quantity: {
|
|
18957
|
+
en: "the rain this year",
|
|
18958
|
+
it: "la pioggia di quest’anno"
|
|
18959
|
+
},
|
|
18960
|
+
label: {
|
|
18961
|
+
en: "Rain year",
|
|
18962
|
+
it: "Pioggia anno"
|
|
18963
|
+
},
|
|
18964
|
+
unit: "mm",
|
|
18965
|
+
icon: "droplets"
|
|
18966
|
+
}
|
|
18967
|
+
]
|
|
18968
|
+
},
|
|
18584
18969
|
"battery": {
|
|
18585
18970
|
scope: "device",
|
|
18586
18971
|
noun: {
|
|
@@ -18805,6 +19190,30 @@ var CAP_VOCABULARY_BY_NAME = {
|
|
|
18805
19190
|
icon: "wifi-off"
|
|
18806
19191
|
}]
|
|
18807
19192
|
},
|
|
19193
|
+
"error": {
|
|
19194
|
+
scope: "device",
|
|
19195
|
+
noun: {
|
|
19196
|
+
en: "Fault",
|
|
19197
|
+
it: "Guasto"
|
|
19198
|
+
},
|
|
19199
|
+
state: [{
|
|
19200
|
+
kind: "boolean",
|
|
19201
|
+
field: "active",
|
|
19202
|
+
label: {
|
|
19203
|
+
en: "Fault",
|
|
19204
|
+
it: "Guasto"
|
|
19205
|
+
},
|
|
19206
|
+
whenTrue: {
|
|
19207
|
+
en: "When {device} reports a fault",
|
|
19208
|
+
it: "Quando {device} segnala un guasto"
|
|
19209
|
+
},
|
|
19210
|
+
whenFalse: {
|
|
19211
|
+
en: "When the fault of {device} clears",
|
|
19212
|
+
it: "Quando il guasto di {device} si risolve"
|
|
19213
|
+
},
|
|
19214
|
+
icon: "triangle-alert"
|
|
19215
|
+
}]
|
|
19216
|
+
},
|
|
18808
19217
|
"doorbell": {
|
|
18809
19218
|
scope: "device",
|
|
18810
19219
|
noun: {
|
|
@@ -19495,6 +19904,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
19495
19904
|
"embedding-encoder",
|
|
19496
19905
|
"energy-system",
|
|
19497
19906
|
"enum-sensor",
|
|
19907
|
+
"error",
|
|
19498
19908
|
"event-emitter",
|
|
19499
19909
|
"face-gallery",
|
|
19500
19910
|
"failure-contribution",
|
|
@@ -19625,6 +20035,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
19625
20035
|
"viewer-ui",
|
|
19626
20036
|
"water-heater",
|
|
19627
20037
|
"weather",
|
|
20038
|
+
"weather-station",
|
|
19628
20039
|
"webrtc-session",
|
|
19629
20040
|
"zone-analytics",
|
|
19630
20041
|
"zone-rules",
|
|
@@ -19904,6 +20315,18 @@ Object.freeze({
|
|
|
19904
20315
|
"dry"
|
|
19905
20316
|
]
|
|
19906
20317
|
},
|
|
20318
|
+
{
|
|
20319
|
+
"path": "action",
|
|
20320
|
+
"valueType": "enum",
|
|
20321
|
+
"enumValues": [
|
|
20322
|
+
"heating",
|
|
20323
|
+
"cooling",
|
|
20324
|
+
"drying",
|
|
20325
|
+
"fan",
|
|
20326
|
+
"idle",
|
|
20327
|
+
"off"
|
|
20328
|
+
]
|
|
20329
|
+
},
|
|
19907
20330
|
{
|
|
19908
20331
|
"path": "fanMode",
|
|
19909
20332
|
"valueType": "string"
|
|
@@ -20372,6 +20795,29 @@ Object.freeze({
|
|
|
20372
20795
|
"path": "timestamp",
|
|
20373
20796
|
"valueType": "boolean"
|
|
20374
20797
|
}],
|
|
20798
|
+
"error": [
|
|
20799
|
+
{
|
|
20800
|
+
"path": "active",
|
|
20801
|
+
"valueType": "boolean"
|
|
20802
|
+
},
|
|
20803
|
+
{
|
|
20804
|
+
"path": "code",
|
|
20805
|
+
"valueType": "string"
|
|
20806
|
+
},
|
|
20807
|
+
{
|
|
20808
|
+
"path": "label",
|
|
20809
|
+
"valueType": "string"
|
|
20810
|
+
},
|
|
20811
|
+
{
|
|
20812
|
+
"path": "severity",
|
|
20813
|
+
"valueType": "enum",
|
|
20814
|
+
"enumValues": ["warning", "error"]
|
|
20815
|
+
},
|
|
20816
|
+
{
|
|
20817
|
+
"path": "since",
|
|
20818
|
+
"valueType": "number"
|
|
20819
|
+
}
|
|
20820
|
+
],
|
|
20375
20821
|
"event-emitter": [
|
|
20376
20822
|
{
|
|
20377
20823
|
"path": "lastEvent.deviceId",
|
|
@@ -21591,6 +22037,88 @@ Object.freeze({
|
|
|
21591
22037
|
"valueType": "number"
|
|
21592
22038
|
}
|
|
21593
22039
|
],
|
|
22040
|
+
"weather-station": [
|
|
22041
|
+
{
|
|
22042
|
+
"path": "temperature",
|
|
22043
|
+
"valueType": "number"
|
|
22044
|
+
},
|
|
22045
|
+
{
|
|
22046
|
+
"path": "humidity",
|
|
22047
|
+
"valueType": "number"
|
|
22048
|
+
},
|
|
22049
|
+
{
|
|
22050
|
+
"path": "pressure",
|
|
22051
|
+
"valueType": "number"
|
|
22052
|
+
},
|
|
22053
|
+
{
|
|
22054
|
+
"path": "windSpeed",
|
|
22055
|
+
"valueType": "number"
|
|
22056
|
+
},
|
|
22057
|
+
{
|
|
22058
|
+
"path": "windGust",
|
|
22059
|
+
"valueType": "number"
|
|
22060
|
+
},
|
|
22061
|
+
{
|
|
22062
|
+
"path": "windBearing",
|
|
22063
|
+
"valueType": "number"
|
|
22064
|
+
},
|
|
22065
|
+
{
|
|
22066
|
+
"path": "rainRate",
|
|
22067
|
+
"valueType": "number"
|
|
22068
|
+
},
|
|
22069
|
+
{
|
|
22070
|
+
"path": "rainDaily",
|
|
22071
|
+
"valueType": "number"
|
|
22072
|
+
},
|
|
22073
|
+
{
|
|
22074
|
+
"path": "rainEvent",
|
|
22075
|
+
"valueType": "number"
|
|
22076
|
+
},
|
|
22077
|
+
{
|
|
22078
|
+
"path": "uv",
|
|
22079
|
+
"valueType": "number"
|
|
22080
|
+
},
|
|
22081
|
+
{
|
|
22082
|
+
"path": "solarRadiation",
|
|
22083
|
+
"valueType": "number"
|
|
22084
|
+
},
|
|
22085
|
+
{
|
|
22086
|
+
"path": "dewPoint",
|
|
22087
|
+
"valueType": "number"
|
|
22088
|
+
},
|
|
22089
|
+
{
|
|
22090
|
+
"path": "feelsLike",
|
|
22091
|
+
"valueType": "number"
|
|
22092
|
+
},
|
|
22093
|
+
{
|
|
22094
|
+
"path": "vpd",
|
|
22095
|
+
"valueType": "number"
|
|
22096
|
+
},
|
|
22097
|
+
{
|
|
22098
|
+
"path": "windGustMax",
|
|
22099
|
+
"valueType": "number"
|
|
22100
|
+
},
|
|
22101
|
+
{
|
|
22102
|
+
"path": "windBearingAvg",
|
|
22103
|
+
"valueType": "number"
|
|
22104
|
+
},
|
|
22105
|
+
{
|
|
22106
|
+
"path": "rain24h",
|
|
22107
|
+
"valueType": "number"
|
|
22108
|
+
},
|
|
22109
|
+
{
|
|
22110
|
+
"path": "rainWeekly",
|
|
22111
|
+
"valueType": "number"
|
|
22112
|
+
},
|
|
22113
|
+
{
|
|
22114
|
+
"path": "rainMonthly",
|
|
22115
|
+
"valueType": "number"
|
|
22116
|
+
},
|
|
22117
|
+
{
|
|
22118
|
+
"path": "rainYearly",
|
|
22119
|
+
"valueType": "number"
|
|
22120
|
+
}
|
|
22121
|
+
],
|
|
21594
22122
|
"zone-analytics": [
|
|
21595
22123
|
{
|
|
21596
22124
|
"path": "ts",
|
|
@@ -21683,7 +22211,7 @@ Object.freeze({
|
|
|
21683
22211
|
}
|
|
21684
22212
|
});
|
|
21685
22213
|
//#endregion
|
|
21686
|
-
//#region ../types/dist/implemented-methods-
|
|
22214
|
+
//#region ../types/dist/implemented-methods-C5FMXLGP.mjs
|
|
21687
22215
|
/**
|
|
21688
22216
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
21689
22217
|
*
|
|
@@ -48012,69 +48540,6 @@ var EventVocabularyEntrySchema = object({
|
|
|
48012
48540
|
kind: EventVocabularyKindSchema.optional(),
|
|
48013
48541
|
payload: array(EventVocabularyFieldSchema).optional()
|
|
48014
48542
|
});
|
|
48015
|
-
/**
|
|
48016
|
-
* The link an automation made FROM a template carries (#527): which template,
|
|
48017
|
-
* the values the operator filled in, and — when the template has since changed
|
|
48018
|
-
* so that those values no longer fit — the problems that say why.
|
|
48019
|
-
*
|
|
48020
|
-
* It lives inside the recipe (`AutomationRecipe.template`), so the one row in
|
|
48021
|
-
* `core_blocks` stays the one artefact: there is no second store of instances
|
|
48022
|
-
* to drift from the blocks, and deleting the automation deletes the link.
|
|
48023
|
-
* "Convert to a free automation" is dropping this field.
|
|
48024
|
-
*
|
|
48025
|
-
* This file depends on nothing in `recipe.ts`, so the recipe can embed it and
|
|
48026
|
-
* the template engine (`template/automation-template.ts`) can import both.
|
|
48027
|
-
*/
|
|
48028
|
-
/** An `event` input's value: the same three narrowing lists a `device-fired` row takes. */
|
|
48029
|
-
var TemplateEventValueSchema = object({
|
|
48030
|
-
eventIds: array(string().min(1)).min(1).max(64).optional(),
|
|
48031
|
-
sourceIds: array(string().min(1)).min(1).max(64).optional(),
|
|
48032
|
-
kinds: array(EventVocabularyKindSchema).min(1).optional()
|
|
48033
|
-
}).refine((value) => value.eventIds !== void 0 || value.sourceIds !== void 0 || value.kinds !== void 0, { message: "an event names at least one event, part or kind" });
|
|
48034
|
-
/** An `action` input's value: a vocabulary action and its open args. */
|
|
48035
|
-
var TemplateActionValueSchema = object({
|
|
48036
|
-
actionId: string().min(1),
|
|
48037
|
-
args: record(string(), unknown()).optional()
|
|
48038
|
-
});
|
|
48039
|
-
/** The value of one non-group input. Which member is right is the input kind's to judge. */
|
|
48040
|
-
var TemplateScalarValueSchema = union([
|
|
48041
|
-
number$1(),
|
|
48042
|
-
string(),
|
|
48043
|
-
boolean(),
|
|
48044
|
-
array(number$1().int().nonnegative()),
|
|
48045
|
-
TemplateEventValueSchema,
|
|
48046
|
-
TemplateActionValueSchema
|
|
48047
|
-
]);
|
|
48048
|
-
var TemplateInputValueSchema = union([TemplateScalarValueSchema, array(record(string(), TemplateScalarValueSchema))]);
|
|
48049
|
-
/** Input key → value. */
|
|
48050
|
-
var TemplateInputValuesSchema = record(string(), TemplateInputValueSchema);
|
|
48051
|
-
var TemplateInstanceProblemSchema = object({
|
|
48052
|
-
code: _enum([
|
|
48053
|
-
"missing-input",
|
|
48054
|
-
"invalid-input",
|
|
48055
|
-
"invalid-recipe",
|
|
48056
|
-
"missing-template",
|
|
48057
|
-
"edited-by-hand"
|
|
48058
|
-
]),
|
|
48059
|
-
/** Dotted input address (`buttons.2.targets`); absent for a whole-recipe problem. */
|
|
48060
|
-
input: string().optional(),
|
|
48061
|
-
detail: string()
|
|
48062
|
-
});
|
|
48063
|
-
var AutomationTemplateLinkSchema = object({
|
|
48064
|
-
templateId: string().min(1),
|
|
48065
|
-
values: TemplateInputValuesSchema,
|
|
48066
|
-
/**
|
|
48067
|
-
* Fingerprint of the generated code with this literal blanked
|
|
48068
|
-
* (`instanceCodeHash`), stamped by `generateAutomationBlock`. Code that no
|
|
48069
|
-
* longer matches it was edited by hand, and is never regenerated over.
|
|
48070
|
-
*/
|
|
48071
|
-
codeHash: string().optional(),
|
|
48072
|
-
/**
|
|
48073
|
-
* Present when the last re-expansion refused these values. The automation
|
|
48074
|
-
* keeps running the body of its last good expansion; nothing is dropped.
|
|
48075
|
-
*/
|
|
48076
|
-
problems: array(TemplateInstanceProblemSchema).min(1).optional()
|
|
48077
|
-
});
|
|
48078
48543
|
var ExpressionBindingSourceSchema = union([
|
|
48079
48544
|
object({
|
|
48080
48545
|
kind: literal("field").optional(),
|
|
@@ -48110,6 +48575,58 @@ object({
|
|
|
48110
48575
|
path: ["expr"]
|
|
48111
48576
|
});
|
|
48112
48577
|
});
|
|
48578
|
+
Math.PI / 180;
|
|
48579
|
+
/**
|
|
48580
|
+
* WHEN something happens, as data (D983) — the one time vocabulary of the
|
|
48581
|
+
* scheduler and of an automation's schedule leaf. Three shapes, no cron:
|
|
48582
|
+
*
|
|
48583
|
+
* - `clock` — a wall-clock time every day (`07:30`), read in the site's zone;
|
|
48584
|
+
* - `sun` — a sun event (`sunrise`, `sunset`, `dawn`, `dusk`) plus or minus
|
|
48585
|
+
* up to twelve hours, from the site's coordinates;
|
|
48586
|
+
* - `date` — one local date and time (`2026-12-24T18:00`), once.
|
|
48587
|
+
*
|
|
48588
|
+
* A site is a place and its IANA zone ({@link TimeSite}). Every function here
|
|
48589
|
+
* is pure: the zone arithmetic uses `Intl`, no date library.
|
|
48590
|
+
*
|
|
48591
|
+
* Wall-clock honesty across a DST change: a local time that does not exist
|
|
48592
|
+
* (02:30 on the spring-forward day) happens at the first minute that does
|
|
48593
|
+
* (03:00); one that happens twice (02:30 on the fall-back day) happens at its
|
|
48594
|
+
* FIRST occurrence.
|
|
48595
|
+
*/
|
|
48596
|
+
var SunEventSchema = _enum([
|
|
48597
|
+
"sunrise",
|
|
48598
|
+
"sunset",
|
|
48599
|
+
"dawn",
|
|
48600
|
+
"dusk"
|
|
48601
|
+
]);
|
|
48602
|
+
var TimeSpecSchema = discriminatedUnion("kind", [
|
|
48603
|
+
object({
|
|
48604
|
+
kind: literal("clock"),
|
|
48605
|
+
hh: number$1().int().min(0).max(23),
|
|
48606
|
+
mm: number$1().int().min(0).max(59)
|
|
48607
|
+
}),
|
|
48608
|
+
object({
|
|
48609
|
+
kind: literal("sun"),
|
|
48610
|
+
event: SunEventSchema,
|
|
48611
|
+
/** Minutes after the event (negative: before). */
|
|
48612
|
+
offsetMin: number$1().int().min(-720).max(720)
|
|
48613
|
+
}),
|
|
48614
|
+
object({
|
|
48615
|
+
kind: literal("date"),
|
|
48616
|
+
/** `YYYY-MM-DDTHH:mm`, site-local. */
|
|
48617
|
+
local: string().regex(/^(\d{4}-\d{2}-\d{2})T([01]\d|2[0-3]):([0-5]\d)$/, "expected YYYY-MM-DDTHH:mm")
|
|
48618
|
+
})
|
|
48619
|
+
]);
|
|
48620
|
+
/** A day of the week as a schedule names it. */
|
|
48621
|
+
var WeekdayKeySchema = _enum([
|
|
48622
|
+
"mon",
|
|
48623
|
+
"tue",
|
|
48624
|
+
"wed",
|
|
48625
|
+
"thu",
|
|
48626
|
+
"fri",
|
|
48627
|
+
"sat",
|
|
48628
|
+
"sun"
|
|
48629
|
+
]);
|
|
48113
48630
|
/**
|
|
48114
48631
|
* Identity envelope for a device's upstream-system metadata.
|
|
48115
48632
|
*
|
|
@@ -48502,58 +49019,6 @@ function macroOnly(macro) {
|
|
|
48502
49019
|
};
|
|
48503
49020
|
}
|
|
48504
49021
|
}
|
|
48505
|
-
Math.PI / 180;
|
|
48506
|
-
/**
|
|
48507
|
-
* WHEN something happens, as data (D983) — the one time vocabulary of the
|
|
48508
|
-
* scheduler and of an automation's schedule leaf. Three shapes, no cron:
|
|
48509
|
-
*
|
|
48510
|
-
* - `clock` — a wall-clock time every day (`07:30`), read in the site's zone;
|
|
48511
|
-
* - `sun` — a sun event (`sunrise`, `sunset`, `dawn`, `dusk`) plus or minus
|
|
48512
|
-
* up to twelve hours, from the site's coordinates;
|
|
48513
|
-
* - `date` — one local date and time (`2026-12-24T18:00`), once.
|
|
48514
|
-
*
|
|
48515
|
-
* A site is a place and its IANA zone ({@link TimeSite}). Every function here
|
|
48516
|
-
* is pure: the zone arithmetic uses `Intl`, no date library.
|
|
48517
|
-
*
|
|
48518
|
-
* Wall-clock honesty across a DST change: a local time that does not exist
|
|
48519
|
-
* (02:30 on the spring-forward day) happens at the first minute that does
|
|
48520
|
-
* (03:00); one that happens twice (02:30 on the fall-back day) happens at its
|
|
48521
|
-
* FIRST occurrence.
|
|
48522
|
-
*/
|
|
48523
|
-
var SunEventSchema = _enum([
|
|
48524
|
-
"sunrise",
|
|
48525
|
-
"sunset",
|
|
48526
|
-
"dawn",
|
|
48527
|
-
"dusk"
|
|
48528
|
-
]);
|
|
48529
|
-
var TimeSpecSchema = discriminatedUnion("kind", [
|
|
48530
|
-
object({
|
|
48531
|
-
kind: literal("clock"),
|
|
48532
|
-
hh: number$1().int().min(0).max(23),
|
|
48533
|
-
mm: number$1().int().min(0).max(59)
|
|
48534
|
-
}),
|
|
48535
|
-
object({
|
|
48536
|
-
kind: literal("sun"),
|
|
48537
|
-
event: SunEventSchema,
|
|
48538
|
-
/** Minutes after the event (negative: before). */
|
|
48539
|
-
offsetMin: number$1().int().min(-720).max(720)
|
|
48540
|
-
}),
|
|
48541
|
-
object({
|
|
48542
|
-
kind: literal("date"),
|
|
48543
|
-
/** `YYYY-MM-DDTHH:mm`, site-local. */
|
|
48544
|
-
local: string().regex(/^(\d{4}-\d{2}-\d{2})T([01]\d|2[0-3]):([0-5]\d)$/, "expected YYYY-MM-DDTHH:mm")
|
|
48545
|
-
})
|
|
48546
|
-
]);
|
|
48547
|
-
/** A day of the week as a schedule names it. */
|
|
48548
|
-
var WeekdayKeySchema = _enum([
|
|
48549
|
-
"mon",
|
|
48550
|
-
"tue",
|
|
48551
|
-
"wed",
|
|
48552
|
-
"thu",
|
|
48553
|
-
"fri",
|
|
48554
|
-
"sat",
|
|
48555
|
-
"sun"
|
|
48556
|
-
]);
|
|
48557
49022
|
/**
|
|
48558
49023
|
* Hold semantics — "for N seconds" on a leaf of the `when` tree (#498).
|
|
48559
49024
|
*
|
|
@@ -48810,24 +49275,33 @@ var StationaryPresentLeafSchema = object({
|
|
|
48810
49275
|
* (`kind: 'date'`) is refused: only a clock time or a sun event makes a window
|
|
48811
49276
|
* that repeats every day.
|
|
48812
49277
|
*/
|
|
48813
|
-
var
|
|
48814
|
-
kind: literal("time-is"),
|
|
49278
|
+
var timeIsWindowShape = {
|
|
48815
49279
|
between: object({
|
|
48816
49280
|
from: TimeSpecSchema,
|
|
48817
49281
|
to: TimeSpecSchema
|
|
48818
49282
|
}).optional(),
|
|
48819
49283
|
days: array(WeekdayKeySchema).min(1).max(7).optional()
|
|
48820
|
-
}
|
|
48821
|
-
|
|
49284
|
+
};
|
|
49285
|
+
function timeIsWindowProblems(window, ctx) {
|
|
49286
|
+
if (window.between === void 0 && window.days === void 0) ctx.addIssue({
|
|
48822
49287
|
code: "custom",
|
|
48823
49288
|
message: "a time-is leaf needs a between window, days, or both"
|
|
48824
49289
|
});
|
|
48825
|
-
for (const end of ["from", "to"]) if (
|
|
49290
|
+
for (const end of ["from", "to"]) if (window.between?.[end].kind === "date") ctx.addIssue({
|
|
48826
49291
|
code: "custom",
|
|
48827
49292
|
message: "a one-date time cannot bound a daily window: use a clock time or a sun event",
|
|
48828
49293
|
path: ["between", end]
|
|
48829
49294
|
});
|
|
48830
|
-
}
|
|
49295
|
+
}
|
|
49296
|
+
/**
|
|
49297
|
+
* What a `time-is` leaf holds beside its kind, judged by the leaf's own rules.
|
|
49298
|
+
* A template's time-window input stores exactly this.
|
|
49299
|
+
*/
|
|
49300
|
+
var TimeIsWindowSchema = object(timeIsWindowShape).superRefine(timeIsWindowProblems);
|
|
49301
|
+
var TimeIsLeafSchema = object({
|
|
49302
|
+
kind: literal("time-is"),
|
|
49303
|
+
...timeIsWindowShape
|
|
49304
|
+
}).superRefine(timeIsWindowProblems);
|
|
48831
49305
|
/**
|
|
48832
49306
|
* True for the run that one of the named event leaves started. It reads the
|
|
48833
49307
|
* run, not a device: in the tree it is judged against the leaf that fired, in
|
|
@@ -49035,6 +49509,79 @@ function validateWhenTree(when) {
|
|
|
49035
49509
|
return null;
|
|
49036
49510
|
}
|
|
49037
49511
|
/**
|
|
49512
|
+
* The link an automation made FROM a template carries (#527): which template,
|
|
49513
|
+
* the values the operator filled in, and — when the template has since changed
|
|
49514
|
+
* so that those values no longer fit — the problems that say why.
|
|
49515
|
+
*
|
|
49516
|
+
* It lives inside the recipe (`AutomationRecipe.template`), so the one row in
|
|
49517
|
+
* `core_blocks` stays the one artefact: there is no second store of instances
|
|
49518
|
+
* to drift from the blocks, and deleting the automation deletes the link.
|
|
49519
|
+
* "Convert to a free automation" is dropping this field.
|
|
49520
|
+
*
|
|
49521
|
+
* This file depends on nothing in `recipe.ts`, so the recipe can embed it and
|
|
49522
|
+
* the template engine (`template/automation-template.ts`) can import both.
|
|
49523
|
+
*/
|
|
49524
|
+
/** An `event` input's value: the same three narrowing lists a `device-fired` row takes. */
|
|
49525
|
+
var TemplateEventValueSchema = object({
|
|
49526
|
+
eventIds: array(string().min(1)).min(1).max(64).optional(),
|
|
49527
|
+
sourceIds: array(string().min(1)).min(1).max(64).optional(),
|
|
49528
|
+
kinds: array(EventVocabularyKindSchema).min(1).optional()
|
|
49529
|
+
}).refine((value) => value.eventIds !== void 0 || value.sourceIds !== void 0 || value.kinds !== void 0, { message: "an event names at least one event, part or kind" });
|
|
49530
|
+
/** An `action` input's value: a vocabulary action and its open args. */
|
|
49531
|
+
var TemplateActionValueSchema = object({
|
|
49532
|
+
actionId: string().min(1),
|
|
49533
|
+
args: record(string(), unknown()).optional()
|
|
49534
|
+
});
|
|
49535
|
+
/**
|
|
49536
|
+
* A `time-window` input's value: what a `time-is` leaf holds beside its kind.
|
|
49537
|
+
* "No limit" is the input left without a value, never an empty object.
|
|
49538
|
+
*/
|
|
49539
|
+
var TemplateTimeWindowValueSchema = TimeIsWindowSchema;
|
|
49540
|
+
var TemplateEntriesValueSchema = array(record(string(), unknown()));
|
|
49541
|
+
/** The value of one non-group input. Which member is right is the input kind's to judge. */
|
|
49542
|
+
var TemplateScalarValueSchema = union([
|
|
49543
|
+
number$1(),
|
|
49544
|
+
string(),
|
|
49545
|
+
boolean(),
|
|
49546
|
+
array(number$1().int().nonnegative()),
|
|
49547
|
+
TemplateEventValueSchema,
|
|
49548
|
+
TemplateActionValueSchema,
|
|
49549
|
+
TemplateTimeWindowValueSchema,
|
|
49550
|
+
TemplateEntriesValueSchema
|
|
49551
|
+
]);
|
|
49552
|
+
/** What a group row holds under one key: a value, or the rows of a group it nests. */
|
|
49553
|
+
var TemplateRowValueSchema = union([TemplateScalarValueSchema, array(record(string(), TemplateScalarValueSchema))]);
|
|
49554
|
+
var TemplateInputValueSchema = union([TemplateScalarValueSchema, array(record(string(), TemplateRowValueSchema))]);
|
|
49555
|
+
/** Input key → value. */
|
|
49556
|
+
var TemplateInputValuesSchema = record(string(), TemplateInputValueSchema);
|
|
49557
|
+
var TemplateInstanceProblemSchema = object({
|
|
49558
|
+
code: _enum([
|
|
49559
|
+
"missing-input",
|
|
49560
|
+
"invalid-input",
|
|
49561
|
+
"invalid-recipe",
|
|
49562
|
+
"missing-template",
|
|
49563
|
+
"edited-by-hand"
|
|
49564
|
+
]),
|
|
49565
|
+
/** Dotted input address (`buttons.2.targets`, `buttons.0.commands.1.targets`); absent for a whole-recipe problem. */
|
|
49566
|
+
input: string().optional(),
|
|
49567
|
+
detail: string()
|
|
49568
|
+
});
|
|
49569
|
+
var AutomationTemplateLinkSchema = object({
|
|
49570
|
+
templateId: string().min(1),
|
|
49571
|
+
values: TemplateInputValuesSchema,
|
|
49572
|
+
/**
|
|
49573
|
+
* Fingerprint of the generated code with this literal blanked
|
|
49574
|
+
* (`instanceCodeHash`), stamped by `generateAutomationBlock`. Code that no
|
|
49575
|
+
* longer matches it was edited by hand, and is never regenerated over.
|
|
49576
|
+
*/
|
|
49577
|
+
codeHash: string().optional(),
|
|
49578
|
+
/**
|
|
49579
|
+
* Present when the last re-expansion refused these values. The automation
|
|
49580
|
+
* keeps running the body of its last good expansion; nothing is dropped.
|
|
49581
|
+
*/
|
|
49582
|
+
problems: array(TemplateInstanceProblemSchema).min(1).optional()
|
|
49583
|
+
});
|
|
49584
|
+
/**
|
|
49038
49585
|
* The recipe shape before the single `when` tree: a flat list of triggers
|
|
49039
49586
|
* (OR'd, each an instant) and an optional condition tree. Stored recipes —
|
|
49040
49587
|
* the `AUTOMATION_RECIPE` literal of every block saved so far, and whatever an
|
|
@@ -55012,6 +55559,16 @@ var HvacModeSchema = _enum([
|
|
|
55012
55559
|
var ClimateControlStatusSchema = object({
|
|
55013
55560
|
/** Active HVAC mode. */
|
|
55014
55561
|
mode: HvacModeSchema,
|
|
55562
|
+
/** What the device is doing now (`heating` while the valve is open, `idle`
|
|
55563
|
+
* at setpoint). Absent or null = the device does not say; never a guess (D393). */
|
|
55564
|
+
action: _enum([
|
|
55565
|
+
"heating",
|
|
55566
|
+
"cooling",
|
|
55567
|
+
"drying",
|
|
55568
|
+
"fan",
|
|
55569
|
+
"idle",
|
|
55570
|
+
"off"
|
|
55571
|
+
]).nullable().optional(),
|
|
55015
55572
|
/** Available HVAC modes the device accepts. Subset of HvacMode. */
|
|
55016
55573
|
availableModes: array(HvacModeSchema),
|
|
55017
55574
|
/** Active fan mode (`auto` / `low` / `medium` / `high` / `on` / `off`
|
|
@@ -55065,6 +55622,40 @@ var ClimateControlStatusSchema = object({
|
|
|
55065
55622
|
/** Ms epoch when the slice was last updated (push or command). */
|
|
55066
55623
|
lastFetchedAt: number$1()
|
|
55067
55624
|
});
|
|
55625
|
+
/**
|
|
55626
|
+
* Linked room sensor (Customize): the OPERATOR may replace the thermostat's own
|
|
55627
|
+
* `currentTemp` / `currentHumidity` with a sensor in the room. Never linked on
|
|
55628
|
+
* its own (D356). Once linked it ALWAYS replaces the native value, a null
|
|
55629
|
+
* native included — the operator chose the better reading.
|
|
55630
|
+
*/
|
|
55631
|
+
var CLIMATE_CUSTOMIZE_DEVICE_TYPES = [DeviceType.Climate];
|
|
55632
|
+
function roomSensorTarget(id, path) {
|
|
55633
|
+
return {
|
|
55634
|
+
id,
|
|
55635
|
+
cardinality: "scalar",
|
|
55636
|
+
vocabulary: {
|
|
55637
|
+
cap: "climate-control",
|
|
55638
|
+
stateField: path
|
|
55639
|
+
},
|
|
55640
|
+
deviceTypes: CLIMATE_CUSTOMIZE_DEVICE_TYPES,
|
|
55641
|
+
apply: "replace",
|
|
55642
|
+
path,
|
|
55643
|
+
sources: [{
|
|
55644
|
+
kind: "numeric",
|
|
55645
|
+
transforms: [{ kind: "identity" }]
|
|
55646
|
+
}],
|
|
55647
|
+
sourceReport: { status: "assignment-status" }
|
|
55648
|
+
};
|
|
55649
|
+
}
|
|
55650
|
+
var CLIMATE_CONTROL_TARGETS = [roomSensorTarget("room-temperature", "currentTemp"), roomSensorTarget("room-humidity", "currentHumidity")];
|
|
55651
|
+
var climateControlCustomize = {
|
|
55652
|
+
version: 1,
|
|
55653
|
+
overridable: CLIMATE_CONTROL_TARGETS.map((target) => ({
|
|
55654
|
+
path: target.path,
|
|
55655
|
+
by: target.id
|
|
55656
|
+
})),
|
|
55657
|
+
targets: CLIMATE_CONTROL_TARGETS
|
|
55658
|
+
};
|
|
55068
55659
|
var climateControlCapability = {
|
|
55069
55660
|
name: "climate-control",
|
|
55070
55661
|
scope: "device",
|
|
@@ -55375,6 +55966,7 @@ var climateControlCapability = {
|
|
|
55375
55966
|
* on every push without re-querying the provider.
|
|
55376
55967
|
*/
|
|
55377
55968
|
runtimeState: ClimateControlStatusSchema,
|
|
55969
|
+
customize: climateControlCustomize,
|
|
55378
55970
|
/**
|
|
55379
55971
|
* Runtime-state durability: **session** — as `brightness`; `currentTemp` moves continuously and is re-published on connect.
|
|
55380
55972
|
*
|
|
@@ -56130,13 +56722,14 @@ var connectivityCapability = {
|
|
|
56130
56722
|
* its device tracks consumables registers it; the provider names each item
|
|
56131
56723
|
* verbatim and says which `kind` it is.
|
|
56132
56724
|
*
|
|
56133
|
-
* The admin UI
|
|
56134
|
-
*
|
|
56135
|
-
*
|
|
56725
|
+
* The admin UI shows it in the device card's consumables chip and popover
|
|
56726
|
+
* (kind-aware level, remaining time, "Reset"), for every device bound to this
|
|
56727
|
+
* cap (D1023). It no longer has a section of its own under the hero card
|
|
56728
|
+
* (D915 on that point).
|
|
56136
56729
|
*
|
|
56137
56730
|
* Like `childLayout`, the cap is INERT until a provider sets items: no
|
|
56138
56731
|
* provider populates it by guessing (no HA inference). The UI renders a
|
|
56139
|
-
* "No consumables reported"
|
|
56732
|
+
* "No consumables reported" line when `items` is empty.
|
|
56140
56733
|
*/
|
|
56141
56734
|
/**
|
|
56142
56735
|
* What an item IS, which decides which direction is bad:
|
|
@@ -56223,7 +56816,7 @@ var ConsumablesStatusSchema = object({
|
|
|
56223
56816
|
});
|
|
56224
56817
|
/** Synthesize seed for a link-built status (no native provider).
|
|
56225
56818
|
*
|
|
56226
|
-
*
|
|
56819
|
+
* ! NO LIVE CALLER since 2026-08-08 (device-links deleted; see
|
|
56227
56820
|
* `CapabilityStatusSchema.empty`). The wiring guidance below was written for
|
|
56228
56821
|
* exactly the case the operator is asking about again — a Dreame's HA
|
|
56229
56822
|
* consumable-life sensors — so it is kept for that conversation, not because
|
|
@@ -56327,14 +56920,6 @@ var consumablesCapability = {
|
|
|
56327
56920
|
deviceNative: true,
|
|
56328
56921
|
mode: "singleton",
|
|
56329
56922
|
deviceTypes: Object.values(DeviceType),
|
|
56330
|
-
deviceConfig: { ui: {
|
|
56331
|
-
kind: "widget",
|
|
56332
|
-
widgetId: "host/consumables-panel",
|
|
56333
|
-
tab: "consumables",
|
|
56334
|
-
placement: "hero",
|
|
56335
|
-
label: "Consumables",
|
|
56336
|
-
order: 5
|
|
56337
|
-
} },
|
|
56338
56923
|
methods: {
|
|
56339
56924
|
/** Mark a consumable as replaced — resets its remaining life. Only
|
|
56340
56925
|
* meaningful when the item's `resettable` is true. */
|
|
@@ -56563,20 +57148,95 @@ var ControlSetValueInputSchema = discriminatedUnion("kind", [
|
|
|
56563
57148
|
value: string().min(1)
|
|
56564
57149
|
})
|
|
56565
57150
|
]);
|
|
57151
|
+
/**
|
|
57152
|
+
* `next` / `previous` input (#544, operator scripts driving a selector).
|
|
57153
|
+
* `cycle` is a TOP-LEVEL default so a direct `ctx.api.control.next` call
|
|
57154
|
+
* (which skips the tRPC router's Zod parse) still gets `true` via
|
|
57155
|
+
* `CAP_INPUT_DEFAULTS` — matching HA's own `select_next` / `select_previous`
|
|
57156
|
+
* default.
|
|
57157
|
+
*/
|
|
57158
|
+
var ControlStepInputSchema = object({
|
|
57159
|
+
deviceId: number$1().int().nonnegative(),
|
|
57160
|
+
cycle: boolean().default(true)
|
|
57161
|
+
});
|
|
57162
|
+
/** `first` / `last` input — no `cycle`, there is no boundary to stop at. */
|
|
57163
|
+
var ControlBoundaryInputSchema = object({ deviceId: number$1().int().nonnegative() });
|
|
56566
57164
|
var controlCapability = {
|
|
56567
57165
|
name: "control",
|
|
56568
57166
|
scope: "device",
|
|
56569
57167
|
deviceNative: true,
|
|
56570
57168
|
mode: "singleton",
|
|
56571
57169
|
deviceTypes: [DeviceType.Control],
|
|
56572
|
-
methods: {
|
|
56573
|
-
|
|
56574
|
-
|
|
56575
|
-
|
|
56576
|
-
|
|
56577
|
-
|
|
56578
|
-
|
|
56579
|
-
|
|
57170
|
+
methods: {
|
|
57171
|
+
setValue: method(object({
|
|
57172
|
+
deviceId: number$1().int().nonnegative(),
|
|
57173
|
+
control: ControlSetValueInputSchema
|
|
57174
|
+
}), _void(), {
|
|
57175
|
+
kind: "mutation",
|
|
57176
|
+
auth: "protected",
|
|
57177
|
+
notOperatorFacing: "the value shape depends on the control kind (numeric / select / text / datetime) read from status; a typed picker for it is a follow-up of the automations rework"
|
|
57178
|
+
}),
|
|
57179
|
+
/**
|
|
57180
|
+
* Select one step forward through `options` (#544). `providerOptional`
|
|
57181
|
+
* (hence `optionalMethod`): only a `kind: 'select'` provider implements
|
|
57182
|
+
* it — the stepping math is NOT re-derived per provider, it is the one
|
|
57183
|
+
* shared helper `selectNavigationMethods` (`control-select-navigation.ts`)
|
|
57184
|
+
* every select-kind driver spreads in.
|
|
57185
|
+
*/
|
|
57186
|
+
next: optionalMethod(ControlStepInputSchema, _void(), {
|
|
57187
|
+
kind: "mutation",
|
|
57188
|
+
auth: "protected",
|
|
57189
|
+
actions: [{
|
|
57190
|
+
id: "select.next",
|
|
57191
|
+
label: t("Next option", "Opzione successiva"),
|
|
57192
|
+
phrase: t("Select the next option on {device}", "Seleziona l’opzione successiva su {device}"),
|
|
57193
|
+
icon: "skip-forward",
|
|
57194
|
+
openArgs: [{
|
|
57195
|
+
key: "cycle",
|
|
57196
|
+
label: t("Wrap to the first option", "Ricomincia dalla prima opzione"),
|
|
57197
|
+
offer: { kind: "boolean" }
|
|
57198
|
+
}]
|
|
57199
|
+
}]
|
|
57200
|
+
}),
|
|
57201
|
+
/** Select one step backward through `options` (#544) — mirrors `next`. */
|
|
57202
|
+
previous: optionalMethod(ControlStepInputSchema, _void(), {
|
|
57203
|
+
kind: "mutation",
|
|
57204
|
+
auth: "protected",
|
|
57205
|
+
actions: [{
|
|
57206
|
+
id: "select.previous",
|
|
57207
|
+
label: t("Previous option", "Opzione precedente"),
|
|
57208
|
+
phrase: t("Select the previous option on {device}", "Seleziona l’opzione precedente su {device}"),
|
|
57209
|
+
icon: "skip-back",
|
|
57210
|
+
openArgs: [{
|
|
57211
|
+
key: "cycle",
|
|
57212
|
+
label: t("Wrap to the last option", "Ricomincia dall’ultima opzione"),
|
|
57213
|
+
offer: { kind: "boolean" }
|
|
57214
|
+
}]
|
|
57215
|
+
}]
|
|
57216
|
+
}),
|
|
57217
|
+
/** Select the first entry of `options` (#544). */
|
|
57218
|
+
first: optionalMethod(ControlBoundaryInputSchema, _void(), {
|
|
57219
|
+
kind: "mutation",
|
|
57220
|
+
auth: "protected",
|
|
57221
|
+
actions: [{
|
|
57222
|
+
id: "select.first",
|
|
57223
|
+
label: t("First option", "Prima opzione"),
|
|
57224
|
+
phrase: t("Select the first option on {device}", "Seleziona la prima opzione su {device}"),
|
|
57225
|
+
icon: "chevron-first"
|
|
57226
|
+
}]
|
|
57227
|
+
}),
|
|
57228
|
+
/** Select the last entry of `options` (#544). */
|
|
57229
|
+
last: optionalMethod(ControlBoundaryInputSchema, _void(), {
|
|
57230
|
+
kind: "mutation",
|
|
57231
|
+
auth: "protected",
|
|
57232
|
+
actions: [{
|
|
57233
|
+
id: "select.last",
|
|
57234
|
+
label: t("Last option", "Ultima opzione"),
|
|
57235
|
+
phrase: t("Select the last option on {device}", "Seleziona l’ultima opzione su {device}"),
|
|
57236
|
+
icon: "chevron-last"
|
|
57237
|
+
}]
|
|
57238
|
+
})
|
|
57239
|
+
},
|
|
56580
57240
|
status: {
|
|
56581
57241
|
schema: ControlStatusSchema,
|
|
56582
57242
|
kind: "push"
|
|
@@ -57553,7 +58213,7 @@ var mediaPlayerCapability = {
|
|
|
57553
58213
|
]
|
|
57554
58214
|
}
|
|
57555
58215
|
};
|
|
57556
|
-
DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Climate, DeviceType.EventEmitter, DeviceType.
|
|
58216
|
+
DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Climate, DeviceType.EventEmitter, DeviceType.Climate, DeviceType.Climate, DeviceType.Camera, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, 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.WeatherStation, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Gateway, DeviceType.HeatPump, DeviceType.EnergySystem, DeviceType.Camera, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Lock, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, 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.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.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.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Gateway, DeviceType.Camera, DeviceType.Sensor, DeviceType.MediaPlayer, DeviceType.Vacuum, DeviceType.Generic, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.PetFountain, DeviceType.Fan, DeviceType.Humidifier, DeviceType.Climate, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.PetFeeder, DeviceType.MediaPlayer, DeviceType.WeatherStation, DeviceType.WeatherStation, DeviceType.WeatherStation, DeviceType.WeatherStation, DeviceType.WeatherStation, DeviceType.WeatherStation, DeviceType.WeatherStation, DeviceType.WeatherStation, DeviceType.WeatherStation, DeviceType.WeatherStation, DeviceType.WeatherStation, DeviceType.WeatherStation, DeviceType.WeatherStation, DeviceType.WeatherStation, DeviceType.WeatherStation, DeviceType.WeatherStation, DeviceType.WeatherStation, DeviceType.WeatherStation, DeviceType.WeatherStation, DeviceType.WeatherStation;
|
|
57557
58217
|
var NUMBER$1 = {
|
|
57558
58218
|
kind: "fixed",
|
|
57559
58219
|
result: ["number"]
|
|
@@ -61880,7 +62540,7 @@ var TransitionsStatusSchema = object({
|
|
|
61880
62540
|
/** Refused rows since start, by reason. */
|
|
61881
62541
|
refused: record(string(), number$1())
|
|
61882
62542
|
});
|
|
61883
|
-
Object.values(DeviceType).filter((type) => type !== DeviceType.
|
|
62543
|
+
Object.values(DeviceType).filter((type) => type !== DeviceType.Container), method(object({
|
|
61884
62544
|
deviceId: number$1().int(),
|
|
61885
62545
|
capName: string().optional(),
|
|
61886
62546
|
actorKind: TransitionActorKindSchema.optional(),
|
|
@@ -63122,6 +63782,10 @@ method(object({
|
|
|
63122
63782
|
* system did not report is `null`, never `0` (D393).
|
|
63123
63783
|
*
|
|
63124
63784
|
* Signs, all in watts:
|
|
63785
|
+
* - The branches are DISJOINT: `house` is the consumption EXCLUDING the heat pump
|
|
63786
|
+
* whenever `heatPump` is reported, so solar + grid import + storage discharge =
|
|
63787
|
+
* house + heatPump + grid export + storage charge. A provider whose meter totals
|
|
63788
|
+
* include the pump subtracts it; when it cannot, `house` is null (D393).
|
|
63125
63789
|
* - `solar.powerW`, `house.powerW`, `heatPump.powerW` are magnitudes (≥ 0);
|
|
63126
63790
|
* - `grid.powerW` is + importing, − exporting;
|
|
63127
63791
|
* - `storage.powerW` is + charging, − discharging.
|
|
@@ -63361,6 +64025,85 @@ var enumSensorCapability = {
|
|
|
63361
64025
|
}]
|
|
63362
64026
|
}
|
|
63363
64027
|
};
|
|
64028
|
+
/** How loudly a fault wants the operator. Both tiers raise the attention event. */
|
|
64029
|
+
var DeviceErrorSeveritySchema = _enum(["warning", "error"]);
|
|
64030
|
+
/** One fault of a device that can raise several at once. */
|
|
64031
|
+
var DeviceFaultSchema = object({
|
|
64032
|
+
/** The device's own code, as the vendor names it. */
|
|
64033
|
+
code: string(),
|
|
64034
|
+
/** Already translated by the provider (it owns the vendor vocabulary). */
|
|
64035
|
+
label: string(),
|
|
64036
|
+
severity: DeviceErrorSeveritySchema
|
|
64037
|
+
});
|
|
64038
|
+
/**
|
|
64039
|
+
* The fault a device reports right now. One slice per device.
|
|
64040
|
+
*
|
|
64041
|
+
* `active` is tri-state: `null` is "the provider has not read it yet" and is
|
|
64042
|
+
* never drawn as "no fault" nor as a fault (D315, D393). The provider filters
|
|
64043
|
+
* what is not a fault (a completion or reminder code) BEFORE it writes `true`.
|
|
64044
|
+
*
|
|
64045
|
+
* The cap's name is the key a template reads (`{{ alias.error.label }}`, D1021).
|
|
64046
|
+
*/
|
|
64047
|
+
var ErrorStatusSchema = object({
|
|
64048
|
+
active: boolean().nullable(),
|
|
64049
|
+
/** The code of the principal fault; `null` when none (or unknown). */
|
|
64050
|
+
code: string().nullable(),
|
|
64051
|
+
/** The principal fault's words, provider-translated; `null` when none. */
|
|
64052
|
+
label: string().nullable(),
|
|
64053
|
+
severity: DeviceErrorSeveritySchema,
|
|
64054
|
+
/** Ms epoch the current fault started; `null` when none or not known. */
|
|
64055
|
+
since: number$1().nullable(),
|
|
64056
|
+
/** Every fault, for a device that raises several. The principal is the first. */
|
|
64057
|
+
faults: array(DeviceFaultSchema).optional(),
|
|
64058
|
+
/** Ms epoch of the last read the provider made of the fault. */
|
|
64059
|
+
lastFetchedAt: number$1()
|
|
64060
|
+
});
|
|
64061
|
+
/** The slice before the provider has read anything: unknown, not "fine". */
|
|
64062
|
+
var ERROR_STATUS_COLD = {
|
|
64063
|
+
active: null,
|
|
64064
|
+
code: null,
|
|
64065
|
+
label: null,
|
|
64066
|
+
severity: "error",
|
|
64067
|
+
since: null,
|
|
64068
|
+
lastFetchedAt: 0
|
|
64069
|
+
};
|
|
64070
|
+
var errorCapability = {
|
|
64071
|
+
name: "error",
|
|
64072
|
+
scope: "device",
|
|
64073
|
+
deviceNative: true,
|
|
64074
|
+
mode: "singleton",
|
|
64075
|
+
methods: {},
|
|
64076
|
+
status: {
|
|
64077
|
+
schema: ErrorStatusSchema,
|
|
64078
|
+
kind: "push",
|
|
64079
|
+
empty: ERROR_STATUS_COLD
|
|
64080
|
+
},
|
|
64081
|
+
runtimeState: ErrorStatusSchema,
|
|
64082
|
+
/**
|
|
64083
|
+
* Runtime-state durability: **restored** — the previous observation is what
|
|
64084
|
+
* lets the first reading after a restart be a COMPARISON: a fault that was
|
|
64085
|
+
* already open must not announce itself a second time.
|
|
64086
|
+
*
|
|
64087
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
64088
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
64089
|
+
*/
|
|
64090
|
+
durability: "restored",
|
|
64091
|
+
volatileStateFields: ["lastFetchedAt"],
|
|
64092
|
+
/** The fault itself is carried by the attention alerts; no series is recorded. */
|
|
64093
|
+
history: "none",
|
|
64094
|
+
vocabulary: {
|
|
64095
|
+
noun: t("Fault", "Guasto"),
|
|
64096
|
+
order: 5265,
|
|
64097
|
+
state: [{
|
|
64098
|
+
kind: "boolean",
|
|
64099
|
+
field: "active",
|
|
64100
|
+
label: t("Fault", "Guasto"),
|
|
64101
|
+
whenTrue: t("When {device} reports a fault", "Quando {device} segnala un guasto"),
|
|
64102
|
+
whenFalse: t("When the fault of {device} clears", "Quando il guasto di {device} si risolve"),
|
|
64103
|
+
icon: "triangle-alert"
|
|
64104
|
+
}]
|
|
64105
|
+
}
|
|
64106
|
+
};
|
|
63364
64107
|
var IdentitySchema = object({
|
|
63365
64108
|
id: string(),
|
|
63366
64109
|
name: string(),
|
|
@@ -63375,7 +64118,19 @@ var IdentitySampleInfoSchema = object({
|
|
|
63375
64118
|
source: _enum(["detected", "photo"]),
|
|
63376
64119
|
addedAt: number$1().int(),
|
|
63377
64120
|
deviceId: number$1().int().optional(),
|
|
63378
|
-
base64: string().optional()
|
|
64121
|
+
base64: string().optional(),
|
|
64122
|
+
/**
|
|
64123
|
+
* The sample's own crop, when it has one. The bytes stay on the event-media
|
|
64124
|
+
* plane — this list does not inline them.
|
|
64125
|
+
*/
|
|
64126
|
+
mediaId: number$1().int().optional(),
|
|
64127
|
+
/**
|
|
64128
|
+
* The source face's key frame (or the photo the sample was cut from), when
|
|
64129
|
+
* the sample has no crop of its own. Draw {@link faceBbox} on it.
|
|
64130
|
+
*/
|
|
64131
|
+
keyFrameMediaId: number$1().int().optional(),
|
|
64132
|
+
/** The face box on {@link keyFrameMediaId}, pixel space. */
|
|
64133
|
+
faceBbox: BoundingBoxSchema.optional()
|
|
63379
64134
|
});
|
|
63380
64135
|
/**
|
|
63381
64136
|
* One enrolled sample, judged against the gallery it lives in.
|
|
@@ -63590,6 +64345,12 @@ includeCrops: boolean().optional() }).optional(), array(IdentitySchema).readonly
|
|
|
63590
64345
|
}), _void(), {
|
|
63591
64346
|
kind: "mutation",
|
|
63592
64347
|
auth: "admin"
|
|
64348
|
+
}), method(object({
|
|
64349
|
+
sampleId: string().min(1),
|
|
64350
|
+
toIdentityId: string().min(1)
|
|
64351
|
+
}), _void(), {
|
|
64352
|
+
kind: "mutation",
|
|
64353
|
+
auth: "admin"
|
|
63593
64354
|
}), method(object({
|
|
63594
64355
|
/**
|
|
63595
64356
|
* Restrict to ONE camera. Absent keeps the cluster-wide gallery view.
|
|
@@ -65827,7 +66588,7 @@ var HeroSceneSchema = object({
|
|
|
65827
66588
|
/** 0..1: volume, cover position, cycle progress. */
|
|
65828
66589
|
level: number$1().min(0).max(1).nullable()
|
|
65829
66590
|
});
|
|
65830
|
-
DeviceType.Camera, DeviceType.
|
|
66591
|
+
DeviceType.Camera, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, 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.HeatPump, DeviceType.EnergySystem, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.WeatherStation, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Gateway;
|
|
65831
66592
|
DeviceRole.Siren, DeviceRole.Floodlight, DeviceRole.Spotlight, DeviceRole.PirSensor, DeviceRole.Chime, DeviceRole.Autotrack, DeviceRole.Nightvision, DeviceRole.PrivacyMask, DeviceRole.Doorbell, DeviceRole.CoverBlind, DeviceRole.CoverShutter, DeviceRole.CoverAwning, DeviceRole.CoverCurtain, DeviceRole.CoverGarage, DeviceRole.CoverGate, DeviceRole.CoverDoor, DeviceRole.CoverWindow, DeviceRole.BinaryHelper, DeviceRole.Outlet, DeviceRole.SwitchHeater, DeviceRole.SwitchFan, DeviceRole.SwitchLight, DeviceRole.ClimateAc, DeviceRole.ClimateThermostat, DeviceRole.ClimateFloor, DeviceRole.ClimateRadiator, DeviceRole.ClimateHeater, DeviceRole.ClimateHeatPumpCircuit, DeviceRole.MotionSensor, DeviceRole.ContactSensor, DeviceRole.LeakSensor, DeviceRole.SmokeSensor, DeviceRole.COSensor, DeviceRole.GasSensor, DeviceRole.TamperSensor, DeviceRole.VibrationSensor, DeviceRole.ConnectivitySensor, DeviceRole.SoundSensor, DeviceRole.BinarySensor, DeviceRole.TemperatureSensor, DeviceRole.HumiditySensor, DeviceRole.AmbientLightSensor, DeviceRole.PressureSensor, DeviceRole.WindSensor, DeviceRole.RainSensor, DeviceRole.UvSensor, DeviceRole.SolarRadiationSensor, DeviceRole.SoilMoistureSensor, DeviceRole.PowerSensor, DeviceRole.EnergySensor, DeviceRole.VoltageSensor, DeviceRole.CurrentSensor, DeviceRole.AirQualitySensor, DeviceRole.BatterySensor, DeviceRole.NumericSensor, DeviceRole.EnumSensor, DeviceRole.DateTimeSensor, DeviceRole.GenericSensor, DeviceRole.NumericControl, DeviceRole.SelectControl, DeviceRole.TextControl, DeviceRole.DateTimeControl, DeviceRole.MobilePushNotifier, DeviceRole.MessagingNotifier, DeviceRole.EmailNotifier, DeviceRole.GenericNotifier, DeviceRole.IrTransmitter;
|
|
65832
66593
|
var HeroRowIdSchema = _enum([
|
|
65833
66594
|
"progress",
|
|
@@ -70583,7 +71344,9 @@ method(object({}), object({ entries: array(MigrationLedgerEntrySchema) }), { aut
|
|
|
70583
71344
|
auth: "admin"
|
|
70584
71345
|
}), method(object({
|
|
70585
71346
|
owner: string().min(1),
|
|
70586
|
-
metas: array(MigrationMetaSchema)
|
|
71347
|
+
metas: array(MigrationMetaSchema),
|
|
71348
|
+
/** Ids the owner deliberately removed: an applied row of one is fine, never "newer". */
|
|
71349
|
+
retired: array(string().min(1)).optional()
|
|
70587
71350
|
}), object({
|
|
70588
71351
|
bootId: string(),
|
|
70589
71352
|
snapshot: _enum([
|
|
@@ -77850,6 +78613,8 @@ var NcSystemEventKindSchema = _enum([
|
|
|
77850
78613
|
"storage-location-low",
|
|
77851
78614
|
"storage-location-critical",
|
|
77852
78615
|
"storage-location-ok",
|
|
78616
|
+
"storage-drain-progress",
|
|
78617
|
+
"storage-drain-complete",
|
|
77853
78618
|
"addon-crash-loop",
|
|
77854
78619
|
"addon-update-available",
|
|
77855
78620
|
"server-update-available",
|
|
@@ -82292,7 +83057,19 @@ var VehicleSampleInfoSchema = object({
|
|
|
82292
83057
|
score: number$1(),
|
|
82293
83058
|
addedAt: number$1().int(),
|
|
82294
83059
|
deviceId: number$1().int().optional(),
|
|
82295
|
-
base64: string().optional()
|
|
83060
|
+
base64: string().optional(),
|
|
83061
|
+
/**
|
|
83062
|
+
* The sample's own crop, when it has one. Prefer this over {@link base64}:
|
|
83063
|
+
* the bytes are on the event-media plane (`/m/<mediaId>`), never inlined.
|
|
83064
|
+
*/
|
|
83065
|
+
mediaId: number$1().int().optional(),
|
|
83066
|
+
/**
|
|
83067
|
+
* The source plate's key frame, when the sample has no crop of its own.
|
|
83068
|
+
* Draw {@link plateBbox} on it. Absent when {@link mediaId} is set.
|
|
83069
|
+
*/
|
|
83070
|
+
keyFrameMediaId: number$1().int().optional(),
|
|
83071
|
+
/** The plate box on {@link keyFrameMediaId}, pixel space. */
|
|
83072
|
+
plateBbox: BoundingBoxSchema.optional()
|
|
82296
83073
|
});
|
|
82297
83074
|
var PlateInfoSchema = object({
|
|
82298
83075
|
plateId: string(),
|
|
@@ -82322,7 +83099,17 @@ var PlateInfoSchema = object({
|
|
|
82322
83099
|
* and `searchPlates` inline the crop only when their `includeCrops` input is
|
|
82323
83100
|
* left at its `true` default.
|
|
82324
83101
|
*/
|
|
82325
|
-
cropUrl: string().optional()
|
|
83102
|
+
cropUrl: string().optional(),
|
|
83103
|
+
/**
|
|
83104
|
+
* Why {@link cropUrl} is absent. One of `no-frame-handle`,
|
|
83105
|
+
* `live-capture-missed`, `live-capture-threw`, `no-key-frame`,
|
|
83106
|
+
* `derive-not-wired`, `subject-moved`, `temporal-skew`, `key-frame-absent`,
|
|
83107
|
+
* `bad-frame-size`, `decode-empty`, `derive-threw`, `derive-returned-null`,
|
|
83108
|
+
* `put-failed`. A crossing vehicle whose live capture missed is
|
|
83109
|
+
* `subject-moved`: the key frame is a different instant, and the crop is
|
|
83110
|
+
* legitimately absent.
|
|
83111
|
+
*/
|
|
83112
|
+
cropMissReason: string().optional()
|
|
82326
83113
|
});
|
|
82327
83114
|
/**
|
|
82328
83115
|
* One gallery media row: what the crop is, how big it is, and WHERE its bytes
|
|
@@ -82422,6 +83209,12 @@ includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly(
|
|
|
82422
83209
|
}), _void(), {
|
|
82423
83210
|
kind: "mutation",
|
|
82424
83211
|
auth: "admin"
|
|
83212
|
+
}), method(object({
|
|
83213
|
+
sampleId: string().min(1),
|
|
83214
|
+
toVehicleId: string().min(1)
|
|
83215
|
+
}), _void(), {
|
|
83216
|
+
kind: "mutation",
|
|
83217
|
+
auth: "admin"
|
|
82425
83218
|
}), method(object({
|
|
82426
83219
|
plateId: string(),
|
|
82427
83220
|
vehicleId: string()
|
|
@@ -86250,7 +87043,10 @@ object({
|
|
|
86250
87043
|
value: boolean(),
|
|
86251
87044
|
at: number$1()
|
|
86252
87045
|
}).nullable(),
|
|
86253
|
-
/**
|
|
87046
|
+
/**
|
|
87047
|
+
* Retired: a sent source is never a state (an infrared set cannot report its
|
|
87048
|
+
* input). Kept only so records written before parse; nothing writes or reads it.
|
|
87049
|
+
*/
|
|
86254
87050
|
source: object({
|
|
86255
87051
|
name: string(),
|
|
86256
87052
|
at: number$1()
|
|
@@ -89879,31 +90675,57 @@ var TemplateSelectOptionSchema = object({
|
|
|
89879
90675
|
value: string().min(1),
|
|
89880
90676
|
label: string().min(1).max(80)
|
|
89881
90677
|
});
|
|
90678
|
+
var SelectInputSchema = object({
|
|
90679
|
+
kind: literal("select"),
|
|
90680
|
+
...inputBase,
|
|
90681
|
+
options: array(TemplateSelectOptionSchema).min(1).max(50),
|
|
90682
|
+
default: string().optional()
|
|
90683
|
+
});
|
|
90684
|
+
var BooleanInputSchema = object({
|
|
90685
|
+
kind: literal("boolean"),
|
|
90686
|
+
...inputBase,
|
|
90687
|
+
default: boolean().optional()
|
|
90688
|
+
});
|
|
90689
|
+
/** A limit on when the automation may run; only a `gate` binding reads it. */
|
|
90690
|
+
var TimeWindowInputSchema = object({
|
|
90691
|
+
kind: literal("time-window"),
|
|
90692
|
+
...inputBase
|
|
90693
|
+
});
|
|
89882
90694
|
var TemplateScalarInputSchema = discriminatedUnion("kind", [
|
|
90695
|
+
object({
|
|
90696
|
+
kind: literal("entries"),
|
|
90697
|
+
...inputBase,
|
|
90698
|
+
entry: _enum(["trigger", "step"]),
|
|
90699
|
+
allow: array(string().min(1)).min(1).max(10),
|
|
90700
|
+
maxItems: number$1().int().min(1).max(20).optional()
|
|
90701
|
+
}),
|
|
89883
90702
|
DeviceInputSchema,
|
|
89884
90703
|
EventInputSchema,
|
|
89885
90704
|
ActionInputSchema$1,
|
|
89886
90705
|
NumberInputSchema,
|
|
89887
90706
|
DurationInputSchema,
|
|
89888
90707
|
TextInputSchema,
|
|
89889
|
-
|
|
89890
|
-
|
|
89891
|
-
|
|
89892
|
-
options: array(TemplateSelectOptionSchema).min(1).max(50),
|
|
89893
|
-
default: string().optional()
|
|
89894
|
-
}),
|
|
89895
|
-
object({
|
|
89896
|
-
kind: literal("boolean"),
|
|
89897
|
-
...inputBase,
|
|
89898
|
-
default: boolean().optional()
|
|
89899
|
-
})
|
|
90708
|
+
SelectInputSchema,
|
|
90709
|
+
BooleanInputSchema,
|
|
90710
|
+
TimeWindowInputSchema
|
|
89900
90711
|
]);
|
|
89901
|
-
var
|
|
90712
|
+
var groupBase = {
|
|
89902
90713
|
kind: literal("group"),
|
|
89903
90714
|
...inputBase,
|
|
89904
|
-
|
|
90715
|
+
/** What one row is, in the singular and lower case ("button"): "Add button". */
|
|
90716
|
+
rowLabel: string().min(1).max(40).optional(),
|
|
89905
90717
|
minRows: number$1().int().min(0).max(20).optional(),
|
|
89906
90718
|
maxRows: number$1().int().min(1).max(20).optional()
|
|
90719
|
+
};
|
|
90720
|
+
/** A group inside a group row: its rows hold values only, never another group. */
|
|
90721
|
+
var NestedGroupInputSchema = object({
|
|
90722
|
+
...groupBase,
|
|
90723
|
+
inputs: array(TemplateScalarInputSchema).min(1).max(10)
|
|
90724
|
+
});
|
|
90725
|
+
var TemplateRowInputSchema = discriminatedUnion("kind", [...TemplateScalarInputSchema.options, NestedGroupInputSchema]);
|
|
90726
|
+
var GroupInputSchema = object({
|
|
90727
|
+
...groupBase,
|
|
90728
|
+
inputs: array(TemplateRowInputSchema).min(1).max(10)
|
|
89907
90729
|
});
|
|
89908
90730
|
var TemplateInputSchema = discriminatedUnion("kind", [...TemplateScalarInputSchema.options, GroupInputSchema]);
|
|
89909
90731
|
var TemplatePathSchema = array(union([string().min(1), number$1().int().nonnegative()])).max(24);
|
|
@@ -89914,41 +90736,154 @@ var TemplateValueBindingSchema = object({
|
|
|
89914
90736
|
/** A device value written as a step target (`{ kind: 'devices', ids }`). */
|
|
89915
90737
|
as: literal("target").optional()
|
|
89916
90738
|
});
|
|
89917
|
-
var
|
|
90739
|
+
var TemplateRowIdBindingSchema = object({
|
|
89918
90740
|
kind: literal("row-id"),
|
|
89919
90741
|
path: TemplatePathSchema
|
|
89920
|
-
})
|
|
89921
|
-
|
|
89922
|
-
|
|
90742
|
+
});
|
|
90743
|
+
/**
|
|
90744
|
+
* Appends an `entries` input's elements, verbatim, to the list at `path`. In a
|
|
90745
|
+
* row's bindings `input` is the row's own entries input and `path` is from the
|
|
90746
|
+
* row's item; at the top level, with `in`, it appends the `input` entries of
|
|
90747
|
+
* EVERY row of the group `in` (all buttons' triggers into one `when`), and
|
|
90748
|
+
* without `in` it appends a top-level entries input.
|
|
90749
|
+
*/
|
|
90750
|
+
var TemplateEntriesBindingSchema = object({
|
|
90751
|
+
kind: literal("entries"),
|
|
89923
90752
|
input: inputKey,
|
|
89924
|
-
/** The list in the body the rows are appended to. */
|
|
89925
90753
|
path: TemplatePathSchema,
|
|
89926
|
-
|
|
89927
|
-
|
|
89928
|
-
|
|
89929
|
-
|
|
89930
|
-
|
|
89931
|
-
|
|
89932
|
-
|
|
89933
|
-
|
|
89934
|
-
|
|
90754
|
+
in: inputKey.optional()
|
|
90755
|
+
});
|
|
90756
|
+
/** What fills the item of a row that holds values only. */
|
|
90757
|
+
var TemplateLeafRowBindingSchema = discriminatedUnion("kind", [TemplateValueBindingSchema, TemplateRowIdBindingSchema]);
|
|
90758
|
+
var TemplateRowItemBindingSchema = discriminatedUnion("kind", [
|
|
90759
|
+
TemplateValueBindingSchema,
|
|
90760
|
+
TemplateEntriesBindingSchema,
|
|
90761
|
+
TemplateRowIdBindingSchema,
|
|
90762
|
+
object({
|
|
90763
|
+
kind: literal("row-ids"),
|
|
90764
|
+
/** The nested group whose rows are named. */
|
|
90765
|
+
input: inputKey,
|
|
90766
|
+
path: TemplatePathSchema
|
|
90767
|
+
}),
|
|
90768
|
+
object({
|
|
90769
|
+
kind: literal("rows"),
|
|
90770
|
+
/** The nested group, declared by the parent row's group. */
|
|
90771
|
+
input: inputKey,
|
|
90772
|
+
/** The list the nested rows are appended to, from the parent row's item. */
|
|
90773
|
+
path: TemplatePathSchema,
|
|
90774
|
+
item: unknown(),
|
|
90775
|
+
bindings: array(TemplateLeafRowBindingSchema).max(40)
|
|
90776
|
+
})
|
|
90777
|
+
]);
|
|
90778
|
+
var TemplateBindingSchema = discriminatedUnion("kind", [
|
|
90779
|
+
TemplateValueBindingSchema,
|
|
90780
|
+
TemplateEntriesBindingSchema,
|
|
90781
|
+
object({
|
|
90782
|
+
kind: literal("rows"),
|
|
90783
|
+
input: inputKey,
|
|
90784
|
+
/**
|
|
90785
|
+
* A group the rows of `input` hold: the binding then appends one item per
|
|
90786
|
+
* NESTED row of every row, and its bindings read the nested row.
|
|
90787
|
+
*/
|
|
90788
|
+
nested: inputKey.optional(),
|
|
90789
|
+
/** The list in the body the rows are appended to. */
|
|
90790
|
+
path: TemplatePathSchema,
|
|
90791
|
+
/** The JSON copied once per row. */
|
|
90792
|
+
item: unknown(),
|
|
90793
|
+
bindings: array(TemplateRowItemBindingSchema).max(40)
|
|
90794
|
+
}),
|
|
90795
|
+
object({
|
|
90796
|
+
kind: literal("gate"),
|
|
90797
|
+
/** A top-level `time-window` input. */
|
|
90798
|
+
input: inputKey,
|
|
90799
|
+
path: TemplatePathSchema
|
|
90800
|
+
})
|
|
90801
|
+
]);
|
|
90802
|
+
/** Twice-declared keys and event / action inputs that read no device beside them. */
|
|
90803
|
+
function levelProblems(inputs, path, inRow = false) {
|
|
90804
|
+
const issues = [];
|
|
90805
|
+
const seen = /* @__PURE__ */ new Set();
|
|
90806
|
+
for (const input of inputs) {
|
|
90807
|
+
if (seen.has(input.key)) issues.push({
|
|
89935
90808
|
message: `input "${input.key}" is declared twice`,
|
|
89936
|
-
path
|
|
90809
|
+
path
|
|
89937
90810
|
});
|
|
89938
|
-
|
|
89939
|
-
|
|
89940
|
-
|
|
89941
|
-
|
|
89942
|
-
|
|
89943
|
-
|
|
89944
|
-
|
|
89945
|
-
|
|
89946
|
-
|
|
89947
|
-
|
|
89948
|
-
|
|
90811
|
+
seen.add(input.key);
|
|
90812
|
+
if (input.kind === "group") {
|
|
90813
|
+
issues.push(...levelProblems(input.inputs, path, true));
|
|
90814
|
+
continue;
|
|
90815
|
+
}
|
|
90816
|
+
if (input.kind === "time-window" && inRow) issues.push({
|
|
90817
|
+
message: `time window "${input.key}" limits the whole automation: it is not a row input`,
|
|
90818
|
+
path
|
|
90819
|
+
});
|
|
90820
|
+
if (input.kind !== "event" && input.kind !== "action") continue;
|
|
90821
|
+
if (input.deviceInput === void 0) continue;
|
|
90822
|
+
const device = inputs.find((sibling) => sibling.key === input.deviceInput);
|
|
90823
|
+
if (device === void 0 || device.kind !== "device") issues.push({
|
|
90824
|
+
message: `"${input.key}" reads the device input "${input.deviceInput}", which is not a device input beside it`,
|
|
90825
|
+
path
|
|
90826
|
+
});
|
|
90827
|
+
}
|
|
90828
|
+
return issues;
|
|
90829
|
+
}
|
|
90830
|
+
function nestedGroupOf(group, key) {
|
|
90831
|
+
const found = group.inputs.find((input) => input.key === key);
|
|
90832
|
+
return found !== void 0 && found.kind === "group" ? found : void 0;
|
|
90833
|
+
}
|
|
90834
|
+
function leafBindingProblems(bindings, owner) {
|
|
90835
|
+
const issues = [];
|
|
90836
|
+
for (const row of bindings) {
|
|
90837
|
+
if (row.kind === "row-id") continue;
|
|
90838
|
+
if (row.kind === "value") {
|
|
90839
|
+
const input = owner.inputs.find((i) => i.key === row.input);
|
|
90840
|
+
if (input === void 0) issues.push(`row binding names "${row.input}", which group "${owner.key}" does not declare`);
|
|
90841
|
+
else if (input.kind === "group") issues.push(`group "${input.key}" is bound with rows, not a value`);
|
|
90842
|
+
else if (input.kind === "entries") issues.push(`entries "${input.key}" are bound with entries, not a value`);
|
|
90843
|
+
continue;
|
|
90844
|
+
}
|
|
90845
|
+
const nested = owner.inputs.find((i) => i.key === row.input);
|
|
90846
|
+
if (row.kind === "entries") {
|
|
90847
|
+
if (nested === void 0 || nested.kind !== "entries") issues.push(`"${row.input}" is not an entries input inside "${owner.key}"`);
|
|
90848
|
+
continue;
|
|
89949
90849
|
}
|
|
90850
|
+
if (row.kind === "row-ids") {
|
|
90851
|
+
if (nested === void 0 || nested.kind !== "group" && nested.kind !== "entries") issues.push(`"${row.input}" is not a group or entries input inside "${owner.key}"`);
|
|
90852
|
+
continue;
|
|
90853
|
+
}
|
|
90854
|
+
if (nested === void 0 || nested.kind !== "group") {
|
|
90855
|
+
issues.push(`"${row.input}" is not a group inside "${owner.key}"`);
|
|
90856
|
+
continue;
|
|
90857
|
+
}
|
|
90858
|
+
issues.push(...leafBindingProblems(row.bindings, nested));
|
|
90859
|
+
}
|
|
90860
|
+
return issues;
|
|
90861
|
+
}
|
|
90862
|
+
function rowsBindingProblems(binding, group) {
|
|
90863
|
+
if (binding.nested === void 0) return leafBindingProblems(binding.bindings, group);
|
|
90864
|
+
const nested = nestedGroupOf(group, binding.nested);
|
|
90865
|
+
if (nested === void 0) return [`"${binding.nested}" is not a group inside "${group.key}"`];
|
|
90866
|
+
return leafBindingProblems(binding.bindings, nested);
|
|
90867
|
+
}
|
|
90868
|
+
function bindingProblems(payload, ctx) {
|
|
90869
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
90870
|
+
for (const input of payload.inputs) byKey.set(input.key, input);
|
|
90871
|
+
for (const issue of levelProblems(payload.inputs, ["inputs"])) ctx.addIssue({
|
|
90872
|
+
code: "custom",
|
|
90873
|
+
message: issue.message,
|
|
90874
|
+
path: [...issue.path]
|
|
89950
90875
|
});
|
|
89951
90876
|
payload.bindings.forEach((binding, index) => {
|
|
90877
|
+
if (binding.kind === "entries" && binding.in !== void 0) {
|
|
90878
|
+
const group = byKey.get(binding.in);
|
|
90879
|
+
const entries = group !== void 0 && group.kind === "group" ? group.inputs.find((i) => i.key === binding.input) : void 0;
|
|
90880
|
+
if (entries === void 0 || entries.kind !== "entries") ctx.addIssue({
|
|
90881
|
+
code: "custom",
|
|
90882
|
+
message: `"${binding.input}" is not an entries input inside the group "${binding.in}"`,
|
|
90883
|
+
path: ["bindings", index]
|
|
90884
|
+
});
|
|
90885
|
+
return;
|
|
90886
|
+
}
|
|
89952
90887
|
const input = byKey.get(binding.input);
|
|
89953
90888
|
if (input === void 0) {
|
|
89954
90889
|
ctx.addIssue({
|
|
@@ -89963,6 +90898,18 @@ function bindingProblems(payload, ctx) {
|
|
|
89963
90898
|
message: `group "${input.key}" is bound with rows, not a value`,
|
|
89964
90899
|
path: ["bindings", index]
|
|
89965
90900
|
});
|
|
90901
|
+
if (binding.kind === "entries" !== (input.kind === "entries") && binding.kind !== "rows") {
|
|
90902
|
+
if (binding.kind === "entries" || binding.kind === "value") ctx.addIssue({
|
|
90903
|
+
code: "custom",
|
|
90904
|
+
message: binding.kind === "entries" ? `entries bindings read an entries input: "${input.key}" is ${input.kind}` : `entries "${input.key}" are bound with entries, not a value`,
|
|
90905
|
+
path: ["bindings", index]
|
|
90906
|
+
});
|
|
90907
|
+
}
|
|
90908
|
+
if (binding.kind === "gate" !== (input.kind === "time-window")) ctx.addIssue({
|
|
90909
|
+
code: "custom",
|
|
90910
|
+
message: `a gate reads a time window and a time window is bound with a gate: "${input.key}" is ${input.kind}`,
|
|
90911
|
+
path: ["bindings", index]
|
|
90912
|
+
});
|
|
89966
90913
|
if (binding.kind === "rows") {
|
|
89967
90914
|
if (input.kind !== "group") {
|
|
89968
90915
|
ctx.addIssue({
|
|
@@ -89972,9 +90919,9 @@ function bindingProblems(payload, ctx) {
|
|
|
89972
90919
|
});
|
|
89973
90920
|
return;
|
|
89974
90921
|
}
|
|
89975
|
-
for (const
|
|
90922
|
+
for (const message of rowsBindingProblems(binding, input)) ctx.addIssue({
|
|
89976
90923
|
code: "custom",
|
|
89977
|
-
message
|
|
90924
|
+
message,
|
|
89978
90925
|
path: ["bindings", index]
|
|
89979
90926
|
});
|
|
89980
90927
|
}
|
|
@@ -90153,6 +91100,17 @@ var TerminalOutputEventSchema = discriminatedUnion("kind", [object({
|
|
|
90153
91100
|
exitCode: number$1().int(),
|
|
90154
91101
|
signal: number$1().int().optional()
|
|
90155
91102
|
})]);
|
|
91103
|
+
var ServerShellRefusalSchema = object({
|
|
91104
|
+
code: literal("server-shell-disabled"),
|
|
91105
|
+
nodeId: string(),
|
|
91106
|
+
message: string()
|
|
91107
|
+
});
|
|
91108
|
+
var ServerShellStatusSchema = object({
|
|
91109
|
+
nodeId: string(),
|
|
91110
|
+
enabled: boolean(),
|
|
91111
|
+
idleMinutes: number$1().int().positive(),
|
|
91112
|
+
refusal: ServerShellRefusalSchema.nullable()
|
|
91113
|
+
});
|
|
90156
91114
|
var TerminalOutputBatchSchema = object({
|
|
90157
91115
|
cursor: number$1().int().nonnegative(),
|
|
90158
91116
|
reset: boolean(),
|
|
@@ -90165,7 +91123,7 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
|
|
|
90165
91123
|
}), TerminalInstanceInfoSchema, {
|
|
90166
91124
|
kind: "mutation",
|
|
90167
91125
|
auth: "admin"
|
|
90168
|
-
}), method(_void(), array(TerminalSessionInfoSchema).readonly(), { auth: "admin" }), method(object({
|
|
91126
|
+
}), method(_void(), array(TerminalSessionInfoSchema).readonly(), { auth: "admin" }), method(_void(), ServerShellStatusSchema, { auth: "admin" }), method(object({
|
|
90169
91127
|
profileId: string(),
|
|
90170
91128
|
cols: number$1().int().positive(),
|
|
90171
91129
|
rows: number$1().int().positive(),
|
|
@@ -90175,7 +91133,8 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
|
|
|
90175
91133
|
environment: array(string().max(4096)).max(64).optional()
|
|
90176
91134
|
}), TerminalSessionInfoSchema, {
|
|
90177
91135
|
kind: "mutation",
|
|
90178
|
-
auth: "admin"
|
|
91136
|
+
auth: "admin",
|
|
91137
|
+
caller: "required"
|
|
90179
91138
|
}), method(object({
|
|
90180
91139
|
sessionId: string(),
|
|
90181
91140
|
cols: number$1().int().positive(),
|
|
@@ -90196,6 +91155,7 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
|
|
|
90196
91155
|
}), TerminalOutputBatchSchema, {
|
|
90197
91156
|
kind: "mutation",
|
|
90198
91157
|
auth: "admin",
|
|
91158
|
+
caller: "required",
|
|
90199
91159
|
timeoutMs: 15e3
|
|
90200
91160
|
}), method(object({
|
|
90201
91161
|
sessionId: string(),
|
|
@@ -90205,7 +91165,8 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
|
|
|
90205
91165
|
auth: "admin"
|
|
90206
91166
|
}), method(object({ sessionId: string() }), _void(), {
|
|
90207
91167
|
kind: "mutation",
|
|
90208
|
-
auth: "admin"
|
|
91168
|
+
auth: "admin",
|
|
91169
|
+
caller: "required"
|
|
90209
91170
|
}), NOT_A_DEVICE.CLUSTER;
|
|
90210
91171
|
/**
|
|
90211
91172
|
* toast — system-scoped singleton capability that streams toast
|
|
@@ -92159,6 +93120,265 @@ var weatherCapability = {
|
|
|
92159
93120
|
]
|
|
92160
93121
|
}
|
|
92161
93122
|
};
|
|
93123
|
+
var WeatherStationStatusSchema = object({
|
|
93124
|
+
temperature: number$1().nullable().default(null).meta(readingMeta("temperature", { precision: 1 })),
|
|
93125
|
+
humidity: number$1().min(0).max(100).nullable().default(null).meta(readingMeta("humidity")),
|
|
93126
|
+
pressure: number$1().nullable().default(null).meta(readingMeta("pressure")),
|
|
93127
|
+
windSpeed: number$1().nullable().default(null).meta(readingMeta("speed")),
|
|
93128
|
+
windGust: number$1().nullable().default(null).meta(readingMeta("speed")),
|
|
93129
|
+
windBearing: number$1().nullable().default(null).meta(readingMeta("angle")),
|
|
93130
|
+
rainRate: number$1().nullable().default(null).meta(readingMeta("precipitation-rate", { precision: 1 })),
|
|
93131
|
+
rainDaily: number$1().nullable().default(null).meta(readingMeta("precipitation", { precision: 1 })),
|
|
93132
|
+
rainEvent: number$1().nullable().default(null).meta(readingMeta("precipitation", { precision: 1 })),
|
|
93133
|
+
uv: number$1().nullable().default(null).meta(readingMeta("index", { precision: 1 })),
|
|
93134
|
+
solarRadiation: number$1().nullable().default(null).meta(readingMeta("irradiance")),
|
|
93135
|
+
dewPoint: number$1().nullable().default(null).meta(readingMeta("temperature", { precision: 1 })),
|
|
93136
|
+
/** Apparent ("feels like") temperature. */
|
|
93137
|
+
feelsLike: number$1().nullable().default(null).meta(readingMeta("temperature", { precision: 1 })),
|
|
93138
|
+
/** Vapour-pressure deficit, stored in kPa (the unit stations and growers read it in). */
|
|
93139
|
+
vpd: number$1().nullable().default(null).meta(readingMeta("none", { precision: 2 })),
|
|
93140
|
+
/** Highest wind gust since local midnight. */
|
|
93141
|
+
windGustMax: number$1().nullable().default(null).meta(readingMeta("speed")),
|
|
93142
|
+
/** Wind bearing averaged over the station's own window. */
|
|
93143
|
+
windBearingAvg: number$1().nullable().default(null).meta(readingMeta("angle")),
|
|
93144
|
+
rain24h: number$1().nullable().default(null).meta(readingMeta("precipitation", { precision: 1 })),
|
|
93145
|
+
rainWeekly: number$1().nullable().default(null).meta(readingMeta("precipitation", { precision: 1 })),
|
|
93146
|
+
rainMonthly: number$1().nullable().default(null).meta(readingMeta("precipitation", { precision: 1 })),
|
|
93147
|
+
rainYearly: number$1().nullable().default(null).meta(readingMeta("precipitation", { precision: 1 }))
|
|
93148
|
+
}).extend({
|
|
93149
|
+
/** Ms epoch of the poll that produced this slice. 0 before the first one. */
|
|
93150
|
+
lastFetchedAt: number$1() });
|
|
93151
|
+
var numeric$2 = (field, quantity, label, unit, icon) => ({
|
|
93152
|
+
kind: "numeric",
|
|
93153
|
+
field,
|
|
93154
|
+
quantity,
|
|
93155
|
+
label,
|
|
93156
|
+
unit,
|
|
93157
|
+
icon
|
|
93158
|
+
});
|
|
93159
|
+
/** Every reading of the station with the unit a Customize source must carry ('' = dimensionless). */
|
|
93160
|
+
var STATION_FIELD_UNITS = {
|
|
93161
|
+
temperature: "°C",
|
|
93162
|
+
humidity: "%",
|
|
93163
|
+
pressure: "hPa",
|
|
93164
|
+
windSpeed: "m/s",
|
|
93165
|
+
windGust: "m/s",
|
|
93166
|
+
windBearing: "°",
|
|
93167
|
+
rainRate: "mm/h",
|
|
93168
|
+
rainDaily: "mm",
|
|
93169
|
+
rainEvent: "mm",
|
|
93170
|
+
uv: "",
|
|
93171
|
+
solarRadiation: "W/m²",
|
|
93172
|
+
dewPoint: "°C",
|
|
93173
|
+
feelsLike: "°C",
|
|
93174
|
+
vpd: "kPa",
|
|
93175
|
+
windGustMax: "m/s",
|
|
93176
|
+
windBearingAvg: "°",
|
|
93177
|
+
rain24h: "mm",
|
|
93178
|
+
rainWeekly: "mm",
|
|
93179
|
+
rainMonthly: "mm",
|
|
93180
|
+
rainYearly: "mm"
|
|
93181
|
+
};
|
|
93182
|
+
function isStationField(key) {
|
|
93183
|
+
return Object.prototype.hasOwnProperty.call(STATION_FIELD_UNITS, key);
|
|
93184
|
+
}
|
|
93185
|
+
var STATION_FIELDS = Object.keys(STATION_FIELD_UNITS).filter(isStationField);
|
|
93186
|
+
/**
|
|
93187
|
+
* One scalar target per reading, `apply: 'add'` and listed in `overridable` by its own id
|
|
93188
|
+
* (the running-state / climate-control mechanism, D984 / D990): a station that reports a
|
|
93189
|
+
* reading natively keeps it until the operator assigns another device to that ONE field,
|
|
93190
|
+
* and a device with no `weather-station` slice becomes one from the sources picked. On an
|
|
93191
|
+
* add, every other reading is absent from the claim and the schema defaults it to null
|
|
93192
|
+
* (a missing sensor is never 0, D393) — the fan-control precedent, never a cross-field companion
|
|
93193
|
+
* (the scalar merge treats a companion as owned by its target).
|
|
93194
|
+
*/
|
|
93195
|
+
function stationTarget(field) {
|
|
93196
|
+
const unit = STATION_FIELD_UNITS[field];
|
|
93197
|
+
return {
|
|
93198
|
+
id: field,
|
|
93199
|
+
cardinality: "scalar",
|
|
93200
|
+
vocabulary: {
|
|
93201
|
+
cap: "weather-station",
|
|
93202
|
+
stateField: field
|
|
93203
|
+
},
|
|
93204
|
+
deviceTypes: [DeviceType.WeatherStation],
|
|
93205
|
+
apply: "add",
|
|
93206
|
+
path: field,
|
|
93207
|
+
sources: [{
|
|
93208
|
+
kind: "numeric",
|
|
93209
|
+
...unit.length > 0 ? { requiredUnit: unit } : {},
|
|
93210
|
+
transforms: [{ kind: "identity" }]
|
|
93211
|
+
}],
|
|
93212
|
+
companions: [{
|
|
93213
|
+
kind: "clock",
|
|
93214
|
+
path: "lastFetchedAt",
|
|
93215
|
+
when: "add"
|
|
93216
|
+
}],
|
|
93217
|
+
sourceReport: { status: "assignment-status" }
|
|
93218
|
+
};
|
|
93219
|
+
}
|
|
93220
|
+
var weatherStationCustomize = {
|
|
93221
|
+
version: 1,
|
|
93222
|
+
overridable: STATION_FIELDS.map((field) => ({
|
|
93223
|
+
path: field,
|
|
93224
|
+
by: field
|
|
93225
|
+
})),
|
|
93226
|
+
targets: STATION_FIELDS.map(stationTarget)
|
|
93227
|
+
};
|
|
93228
|
+
var weatherStationCapability = {
|
|
93229
|
+
name: "weather-station",
|
|
93230
|
+
scope: "device",
|
|
93231
|
+
deviceNative: true,
|
|
93232
|
+
mode: "singleton",
|
|
93233
|
+
deviceTypes: [DeviceType.WeatherStation],
|
|
93234
|
+
methods: {},
|
|
93235
|
+
status: {
|
|
93236
|
+
schema: WeatherStationStatusSchema,
|
|
93237
|
+
kind: "push"
|
|
93238
|
+
},
|
|
93239
|
+
runtimeState: WeatherStationStatusSchema,
|
|
93240
|
+
/**
|
|
93241
|
+
* History (D850): the station records its own readings. Rain totals that
|
|
93242
|
+
* reset (event, day, week, month, year) and the sliding 24 h total are
|
|
93243
|
+
* levels, so `gauge`; `counter` is for a monotonic meter only.
|
|
93244
|
+
*/
|
|
93245
|
+
history: { fields: {
|
|
93246
|
+
temperature: {
|
|
93247
|
+
kind: "gauge",
|
|
93248
|
+
unit: "°C",
|
|
93249
|
+
deadband: .1
|
|
93250
|
+
},
|
|
93251
|
+
humidity: {
|
|
93252
|
+
kind: "gauge",
|
|
93253
|
+
unit: "%",
|
|
93254
|
+
deadband: 1
|
|
93255
|
+
},
|
|
93256
|
+
pressure: {
|
|
93257
|
+
kind: "gauge",
|
|
93258
|
+
unit: "hPa",
|
|
93259
|
+
deadband: .2
|
|
93260
|
+
},
|
|
93261
|
+
windSpeed: {
|
|
93262
|
+
kind: "gauge",
|
|
93263
|
+
unit: "m/s",
|
|
93264
|
+
deadband: .2
|
|
93265
|
+
},
|
|
93266
|
+
windGust: {
|
|
93267
|
+
kind: "gauge",
|
|
93268
|
+
unit: "m/s",
|
|
93269
|
+
deadband: .2
|
|
93270
|
+
},
|
|
93271
|
+
windBearing: {
|
|
93272
|
+
kind: "gauge",
|
|
93273
|
+
unit: "°",
|
|
93274
|
+
deadband: 5
|
|
93275
|
+
},
|
|
93276
|
+
rainRate: {
|
|
93277
|
+
kind: "gauge",
|
|
93278
|
+
unit: "mm/h",
|
|
93279
|
+
deadband: .1
|
|
93280
|
+
},
|
|
93281
|
+
rainDaily: {
|
|
93282
|
+
kind: "gauge",
|
|
93283
|
+
unit: "mm",
|
|
93284
|
+
deadband: .1
|
|
93285
|
+
},
|
|
93286
|
+
rainEvent: {
|
|
93287
|
+
kind: "gauge",
|
|
93288
|
+
unit: "mm",
|
|
93289
|
+
deadband: .1
|
|
93290
|
+
},
|
|
93291
|
+
uv: {
|
|
93292
|
+
kind: "gauge",
|
|
93293
|
+
unit: "index",
|
|
93294
|
+
deadband: .1
|
|
93295
|
+
},
|
|
93296
|
+
solarRadiation: {
|
|
93297
|
+
kind: "gauge",
|
|
93298
|
+
unit: "W/m²",
|
|
93299
|
+
deadband: 5
|
|
93300
|
+
},
|
|
93301
|
+
dewPoint: {
|
|
93302
|
+
kind: "gauge",
|
|
93303
|
+
unit: "°C",
|
|
93304
|
+
deadband: .1
|
|
93305
|
+
},
|
|
93306
|
+
feelsLike: {
|
|
93307
|
+
kind: "gauge",
|
|
93308
|
+
unit: "°C",
|
|
93309
|
+
deadband: .1
|
|
93310
|
+
},
|
|
93311
|
+
vpd: {
|
|
93312
|
+
kind: "gauge",
|
|
93313
|
+
unit: "kPa",
|
|
93314
|
+
deadband: .05
|
|
93315
|
+
},
|
|
93316
|
+
windGustMax: {
|
|
93317
|
+
kind: "gauge",
|
|
93318
|
+
unit: "m/s",
|
|
93319
|
+
deadband: .2
|
|
93320
|
+
},
|
|
93321
|
+
windBearingAvg: {
|
|
93322
|
+
kind: "gauge",
|
|
93323
|
+
unit: "°",
|
|
93324
|
+
deadband: 5
|
|
93325
|
+
},
|
|
93326
|
+
rain24h: {
|
|
93327
|
+
kind: "gauge",
|
|
93328
|
+
unit: "mm",
|
|
93329
|
+
deadband: .1
|
|
93330
|
+
},
|
|
93331
|
+
rainWeekly: {
|
|
93332
|
+
kind: "gauge",
|
|
93333
|
+
unit: "mm",
|
|
93334
|
+
deadband: .1
|
|
93335
|
+
},
|
|
93336
|
+
rainMonthly: {
|
|
93337
|
+
kind: "gauge",
|
|
93338
|
+
unit: "mm",
|
|
93339
|
+
deadband: .1
|
|
93340
|
+
},
|
|
93341
|
+
rainYearly: {
|
|
93342
|
+
kind: "gauge",
|
|
93343
|
+
unit: "mm",
|
|
93344
|
+
deadband: .1
|
|
93345
|
+
}
|
|
93346
|
+
} },
|
|
93347
|
+
customize: weatherStationCustomize,
|
|
93348
|
+
/** The poll clock is written but is no reason to commit a row. */
|
|
93349
|
+
volatileStateFields: ["lastFetchedAt"],
|
|
93350
|
+
/**
|
|
93351
|
+
* Runtime-state durability: **session** — a station reading is stale the
|
|
93352
|
+
* moment the hub is down; the provider re-reads on the next poll.
|
|
93353
|
+
*/
|
|
93354
|
+
durability: "session",
|
|
93355
|
+
vocabulary: {
|
|
93356
|
+
noun: t("Weather station", "Stazione meteo"),
|
|
93357
|
+
order: 5235,
|
|
93358
|
+
state: [
|
|
93359
|
+
numeric$2("temperature", t("the outdoor temperature", "la temperatura esterna"), t("Temperature", "Temperatura"), "°C", "thermometer"),
|
|
93360
|
+
numeric$2("humidity", t("the outdoor humidity", "l’umidità esterna"), t("Humidity", "Umidità"), "%", "droplets"),
|
|
93361
|
+
numeric$2("pressure", t("the air pressure", "la pressione atmosferica"), t("Pressure", "Pressione"), "hPa", "gauge"),
|
|
93362
|
+
numeric$2("windSpeed", t("the wind speed", "la velocità del vento"), t("Wind", "Vento"), "m/s", "wind"),
|
|
93363
|
+
numeric$2("windGust", t("the wind gust", "la raffica di vento"), t("Gust", "Raffica"), "m/s", "wind"),
|
|
93364
|
+
numeric$2("windBearing", t("the wind bearing", "la direzione del vento"), t("Wind bearing", "Direzione del vento"), "°", "wind"),
|
|
93365
|
+
numeric$2("rainRate", t("the rain rate", "l’intensità della pioggia"), t("Rain rate", "Intensità pioggia"), "mm/h", "droplets"),
|
|
93366
|
+
numeric$2("rainDaily", t("the rain today", "la pioggia di oggi"), t("Rain today", "Pioggia oggi"), "mm", "droplets"),
|
|
93367
|
+
numeric$2("rainEvent", t("the rain this event", "la pioggia di questo evento"), t("Rain event", "Evento di pioggia"), "mm", "droplets"),
|
|
93368
|
+
numeric$2("uv", t("the UV index", "l’indice UV"), t("UV", "UV"), "", "cloud-sun"),
|
|
93369
|
+
numeric$2("solarRadiation", t("the solar radiation", "la radiazione solare"), t("Solar", "Sole"), "W/m²", "cloud-sun"),
|
|
93370
|
+
numeric$2("dewPoint", t("the dew point", "il punto di rugiada"), t("Dew point", "Punto di rugiada"), "°C", "thermometer"),
|
|
93371
|
+
numeric$2("feelsLike", t("the feels-like temperature", "la temperatura percepita"), t("Feels like", "Percepita"), "°C", "thermometer"),
|
|
93372
|
+
numeric$2("vpd", t("the vapour-pressure deficit", "il deficit di pressione di vapore"), t("VPD", "VPD"), "kPa", "gauge"),
|
|
93373
|
+
numeric$2("windGustMax", t("the highest gust today", "la raffica massima di oggi"), t("Max gust", "Raffica max"), "m/s", "wind"),
|
|
93374
|
+
numeric$2("windBearingAvg", t("the average wind bearing", "la direzione media del vento"), t("Avg bearing", "Direzione media"), "°", "wind"),
|
|
93375
|
+
numeric$2("rain24h", t("the rain in the last 24 hours", "la pioggia delle ultime 24 ore"), t("Rain 24 h", "Pioggia 24 h"), "mm", "droplets"),
|
|
93376
|
+
numeric$2("rainWeekly", t("the rain this week", "la pioggia di questa settimana"), t("Rain week", "Pioggia settimana"), "mm", "droplets"),
|
|
93377
|
+
numeric$2("rainMonthly", t("the rain this month", "la pioggia di questo mese"), t("Rain month", "Pioggia mese"), "mm", "droplets"),
|
|
93378
|
+
numeric$2("rainYearly", t("the rain this year", "la pioggia di quest’anno"), t("Rain year", "Pioggia anno"), "mm", "droplets")
|
|
93379
|
+
]
|
|
93380
|
+
}
|
|
93381
|
+
};
|
|
92162
93382
|
/**
|
|
92163
93383
|
* Optional client-side hints sent at session creation to help the provider
|
|
92164
93384
|
* pick the best native source. All fields optional — a viewer that knows
|
|
@@ -93724,6 +94944,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
93724
94944
|
doorbell: doorbellCapability,
|
|
93725
94945
|
energySystem: energySystemCapability,
|
|
93726
94946
|
enumSensor: enumSensorCapability,
|
|
94947
|
+
error: errorCapability,
|
|
93727
94948
|
eventEmitter: eventEmitterCapability,
|
|
93728
94949
|
fanControl: fanControlCapability,
|
|
93729
94950
|
featureProbe: featureProbeCapability,
|
|
@@ -93780,10 +95001,59 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
93780
95001
|
vibration: vibrationCapability,
|
|
93781
95002
|
waterHeater: waterHeaterCapability,
|
|
93782
95003
|
weather: weatherCapability,
|
|
95004
|
+
weatherStation: weatherStationCapability,
|
|
93783
95005
|
zoneAnalytics: zoneAnalyticsCapability,
|
|
93784
95006
|
zoneRules: zoneRulesCapability,
|
|
93785
95007
|
zones: zonesCapability
|
|
93786
95008
|
};
|
|
95009
|
+
/**
|
|
95010
|
+
* The edge between two readings, or `null` for no edge.
|
|
95011
|
+
*
|
|
95012
|
+
* - `null`/cold → `true` is a START (a fault already open at boot is one);
|
|
95013
|
+
* - `true` → `false` is a RESOLVE;
|
|
95014
|
+
* - an UNKNOWN reading (`active: null`) is never an edge, in either direction —
|
|
95015
|
+
* a failed read must neither raise nor close an episode (D393, D49);
|
|
95016
|
+
* - `true` → `true` is the same episode, whatever the code did.
|
|
95017
|
+
*/
|
|
95018
|
+
function deviceErrorEdge(previous, next) {
|
|
95019
|
+
if (next.active === true && previous?.active !== true) return "start";
|
|
95020
|
+
if (next.active === false && previous?.active === true) return "resolve";
|
|
95021
|
+
return null;
|
|
95022
|
+
}
|
|
95023
|
+
function deviceErrorAlertId(deviceId) {
|
|
95024
|
+
return `device-error:${String(deviceId)}`;
|
|
95025
|
+
}
|
|
95026
|
+
/** The alert that opens an episode. `since` is the fault's own start, else `now`. */
|
|
95027
|
+
function buildDeviceErrorAlert(deviceId, deviceName, status, now) {
|
|
95028
|
+
const label = status.label ?? status.code ?? "Fault";
|
|
95029
|
+
const startedAt = status.since ?? now;
|
|
95030
|
+
const alertId = deviceErrorAlertId(deviceId);
|
|
95031
|
+
return {
|
|
95032
|
+
id: alertId,
|
|
95033
|
+
category: "device.error",
|
|
95034
|
+
severity: "critical",
|
|
95035
|
+
title: `${deviceName}: ${label}`,
|
|
95036
|
+
message: `${deviceName} reports a fault: ${label}.`,
|
|
95037
|
+
action: {
|
|
95038
|
+
label: "Open device",
|
|
95039
|
+
href: `/devices/${String(deviceId)}`,
|
|
95040
|
+
navigation: "app"
|
|
95041
|
+
},
|
|
95042
|
+
status: "active",
|
|
95043
|
+
read: false,
|
|
95044
|
+
createdAt: now,
|
|
95045
|
+
updatedAt: now,
|
|
95046
|
+
source: {
|
|
95047
|
+
type: "device",
|
|
95048
|
+
id: String(deviceId)
|
|
95049
|
+
},
|
|
95050
|
+
metadata: {
|
|
95051
|
+
episodeId: `${alertId}@${String(startedAt)}`,
|
|
95052
|
+
code: status.code,
|
|
95053
|
+
deviceSeverity: status.severity
|
|
95054
|
+
}
|
|
95055
|
+
};
|
|
95056
|
+
}
|
|
93787
95057
|
var BaseDevice = class {
|
|
93788
95058
|
id;
|
|
93789
95059
|
stableId;
|
|
@@ -94083,6 +95353,68 @@ var BaseDevice = class {
|
|
|
94083
95353
|
} catch (err) {}
|
|
94084
95354
|
}
|
|
94085
95355
|
/**
|
|
95356
|
+
* Declare that this device can report a fault (the `error` cap). The slice
|
|
95357
|
+
* starts UNKNOWN — a restored slice is kept, so a fault that was open before
|
|
95358
|
+
* a restart is compared, not announced again.
|
|
95359
|
+
*/
|
|
95360
|
+
registerErrorCap() {
|
|
95361
|
+
this.ctx.registerNativeCap?.(errorCapability, {});
|
|
95362
|
+
if (this.runtimeState.getCapState(errorCapability.name) === void 0) this.setCapSlice(errorCapability, ERROR_STATUS_COLD);
|
|
95363
|
+
}
|
|
95364
|
+
/**
|
|
95365
|
+
* Publish the device's fault. The slice is written; on an EDGE (a fault
|
|
95366
|
+
* starts, or one resolves) an attention episode opens or closes through the
|
|
95367
|
+
* alerts cap, which the notification center announces as
|
|
95368
|
+
* `device-attention-required` / `-resolved`. An unknown reading is never an
|
|
95369
|
+
* edge. No rule exists by default: it notifies when the operator makes one.
|
|
95370
|
+
*/
|
|
95371
|
+
async publishError(reading) {
|
|
95372
|
+
const previous = this.getCapSlice(errorCapability);
|
|
95373
|
+
if (reading.active === null && previous !== null && previous.active !== null) return;
|
|
95374
|
+
const now = Date.now();
|
|
95375
|
+
const edge = deviceErrorEdge(previous, reading);
|
|
95376
|
+
const next = edge === "start" && reading.since === null ? {
|
|
95377
|
+
...reading,
|
|
95378
|
+
since: now
|
|
95379
|
+
} : reading;
|
|
95380
|
+
this.setCapSlice(errorCapability, next);
|
|
95381
|
+
if (edge === null) return;
|
|
95382
|
+
const alerts = this.ctx.api.alerts;
|
|
95383
|
+
if (alerts === void 0) {
|
|
95384
|
+
this.ctx.logger.warn("device error: attention event skipped — alerts capability missing", {
|
|
95385
|
+
tags: { deviceId: this.id },
|
|
95386
|
+
meta: { edge }
|
|
95387
|
+
});
|
|
95388
|
+
return;
|
|
95389
|
+
}
|
|
95390
|
+
try {
|
|
95391
|
+
if (edge === "start") await alerts.emit.mutate(buildDeviceErrorAlert(this.id, this.name, next, now));
|
|
95392
|
+
else await alerts.update.mutate({
|
|
95393
|
+
alertId: deviceErrorAlertId(this.id),
|
|
95394
|
+
patch: {
|
|
95395
|
+
status: "completed",
|
|
95396
|
+
read: true,
|
|
95397
|
+
updatedAt: now
|
|
95398
|
+
}
|
|
95399
|
+
});
|
|
95400
|
+
this.ctx.logger.info(`device error: ${edge === "start" ? "raised" : "resolved"}`, {
|
|
95401
|
+
tags: { deviceId: this.id },
|
|
95402
|
+
meta: {
|
|
95403
|
+
code: next.code,
|
|
95404
|
+
label: next.label
|
|
95405
|
+
}
|
|
95406
|
+
});
|
|
95407
|
+
} catch (err) {
|
|
95408
|
+
this.ctx.logger.warn("device error: attention event failed", {
|
|
95409
|
+
tags: { deviceId: this.id },
|
|
95410
|
+
meta: {
|
|
95411
|
+
edge,
|
|
95412
|
+
error: errMsg(err)
|
|
95413
|
+
}
|
|
95414
|
+
});
|
|
95415
|
+
}
|
|
95416
|
+
}
|
|
95417
|
+
/**
|
|
94086
95418
|
* Typed read-through to a cap-keyed runtime-state slice. Drivers
|
|
94087
95419
|
* call `this.getCapSlice(batteryCapability)` and the return type
|
|
94088
95420
|
* is inferred from the cap's `runtimeState` Zod schema — no string
|
|
@@ -95030,7 +96362,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
95030
96362
|
}
|
|
95031
96363
|
};
|
|
95032
96364
|
DeviceType.HeatPump;
|
|
95033
|
-
DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.HeatPump, DeviceType.EnergySystem, DeviceType.Camera, DeviceType.
|
|
96365
|
+
DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.HeatPump, DeviceType.EnergySystem, DeviceType.Camera, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, 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.WeatherStation, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Gateway;
|
|
95034
96366
|
new Set(Object.values(DeviceType));
|
|
95035
96367
|
/** Parses the raw slice with the cap's own schema; `undefined` when it does not parse. */
|
|
95036
96368
|
function contribution(cap, schema, build) {
|
|
@@ -96399,6 +97731,17 @@ var SENSORS_FAMILY_CONTRIBUTIONS = {
|
|
|
96399
97731
|
doorbell: doorbellContribution,
|
|
96400
97732
|
meter: meterContribution,
|
|
96401
97733
|
weather: weatherContribution,
|
|
97734
|
+
"weather-station": contribution("weather-station", WeatherStationStatusSchema, (slice) => {
|
|
97735
|
+
if (slice === void 0 || slice.temperature === null) return { reading: reading("weather", "unknown") };
|
|
97736
|
+
const celsius = `${number(slice.temperature)}°C`;
|
|
97737
|
+
const facts = [fact("reading", "thermometer", t(celsius, celsius), "primary")];
|
|
97738
|
+
if (slice.humidity !== null) facts.push(fact("reading", "droplets", t(`${Math.round(slice.humidity)}%`, `${Math.round(slice.humidity)}%`)));
|
|
97739
|
+
if (slice.windSpeed !== null) facts.push(fact("reading", "wind", t(`${number(slice.windSpeed)} m/s`, `${number(slice.windSpeed)} m/s`)));
|
|
97740
|
+
return {
|
|
97741
|
+
reading: reading("weather", "on", false, t(celsius, celsius)),
|
|
97742
|
+
facts
|
|
97743
|
+
};
|
|
97744
|
+
}),
|
|
96402
97745
|
update: updateContribution,
|
|
96403
97746
|
image: imageContribution,
|
|
96404
97747
|
button: buttonContribution,
|
|
@@ -97075,7 +98418,7 @@ var HEAT_PUMP_FAMILY_CONTRIBUTIONS = { "heat-pump-status": contribution("heat-pu
|
|
|
97075
98418
|
...Object.values(CLIMATE_FAMILY_CONTRIBUTIONS),
|
|
97076
98419
|
...Object.values(HEAT_PUMP_FAMILY_CONTRIBUTIONS)
|
|
97077
98420
|
];
|
|
97078
|
-
DeviceType.Camera, DeviceType.
|
|
98421
|
+
DeviceType.Camera, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, 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.HeatPump, DeviceType.EnergySystem, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.WeatherStation, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Gateway;
|
|
97079
98422
|
/**
|
|
97080
98423
|
* Who looks like what (D1023). One look per {@link DeviceType}, refined by a
|
|
97081
98424
|
* {@link DeviceRole} where the role changes how the device reads (a leak
|
|
@@ -97719,7 +99062,7 @@ lookOf({
|
|
|
97719
99062
|
standby: MUTED
|
|
97720
99063
|
});
|
|
97721
99064
|
DeviceType.Climate;
|
|
97722
|
-
DeviceType.Camera, DeviceType.
|
|
99065
|
+
DeviceType.Camera, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, 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.HeatPump, DeviceType.EnergySystem, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.WeatherStation, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Gateway;
|
|
97723
99066
|
DeviceRole.Siren, DeviceRole.Floodlight, DeviceRole.Spotlight, DeviceRole.PirSensor, DeviceRole.Chime, DeviceRole.Autotrack, DeviceRole.Nightvision, DeviceRole.PrivacyMask, DeviceRole.Doorbell, DeviceRole.CoverBlind, DeviceRole.CoverShutter, DeviceRole.CoverAwning, DeviceRole.CoverCurtain, DeviceRole.CoverGarage, DeviceRole.CoverGate, DeviceRole.CoverDoor, DeviceRole.CoverWindow, DeviceRole.BinaryHelper, DeviceRole.Outlet, DeviceRole.SwitchHeater, DeviceRole.SwitchFan, DeviceRole.SwitchLight, DeviceRole.ClimateAc, DeviceRole.ClimateThermostat, DeviceRole.ClimateFloor, DeviceRole.ClimateRadiator, DeviceRole.ClimateHeater, DeviceRole.ClimateHeatPumpCircuit, DeviceRole.MotionSensor, DeviceRole.ContactSensor, DeviceRole.LeakSensor, DeviceRole.SmokeSensor, DeviceRole.COSensor, DeviceRole.GasSensor, DeviceRole.TamperSensor, DeviceRole.VibrationSensor, DeviceRole.ConnectivitySensor, DeviceRole.SoundSensor, DeviceRole.BinarySensor, DeviceRole.TemperatureSensor, DeviceRole.HumiditySensor, DeviceRole.AmbientLightSensor, DeviceRole.PressureSensor, DeviceRole.WindSensor, DeviceRole.RainSensor, DeviceRole.UvSensor, DeviceRole.SolarRadiationSensor, DeviceRole.SoilMoistureSensor, DeviceRole.PowerSensor, DeviceRole.EnergySensor, DeviceRole.VoltageSensor, DeviceRole.CurrentSensor, DeviceRole.AirQualitySensor, DeviceRole.BatterySensor, DeviceRole.NumericSensor, DeviceRole.EnumSensor, DeviceRole.DateTimeSensor, DeviceRole.GenericSensor, DeviceRole.NumericControl, DeviceRole.SelectControl, DeviceRole.TextControl, DeviceRole.DateTimeControl, DeviceRole.MobilePushNotifier, DeviceRole.MessagingNotifier, DeviceRole.EmailNotifier, DeviceRole.GenericNotifier, DeviceRole.IrTransmitter;
|
|
97724
99067
|
Object.freeze({
|
|
97725
99068
|
"accessories.getStatus": {
|
|
@@ -99024,6 +100367,13 @@ Object.freeze({
|
|
|
99024
100367
|
access: "view",
|
|
99025
100368
|
role: "viewer"
|
|
99026
100369
|
},
|
|
100370
|
+
"control.first": {
|
|
100371
|
+
capName: "control",
|
|
100372
|
+
capScope: "device",
|
|
100373
|
+
addonId: null,
|
|
100374
|
+
access: "create",
|
|
100375
|
+
role: "viewer"
|
|
100376
|
+
},
|
|
99027
100377
|
"control.getStatus": {
|
|
99028
100378
|
capName: "control",
|
|
99029
100379
|
capScope: "device",
|
|
@@ -99031,6 +100381,27 @@ Object.freeze({
|
|
|
99031
100381
|
access: "view",
|
|
99032
100382
|
role: "viewer"
|
|
99033
100383
|
},
|
|
100384
|
+
"control.last": {
|
|
100385
|
+
capName: "control",
|
|
100386
|
+
capScope: "device",
|
|
100387
|
+
addonId: null,
|
|
100388
|
+
access: "create",
|
|
100389
|
+
role: "viewer"
|
|
100390
|
+
},
|
|
100391
|
+
"control.next": {
|
|
100392
|
+
capName: "control",
|
|
100393
|
+
capScope: "device",
|
|
100394
|
+
addonId: null,
|
|
100395
|
+
access: "create",
|
|
100396
|
+
role: "viewer"
|
|
100397
|
+
},
|
|
100398
|
+
"control.previous": {
|
|
100399
|
+
capName: "control",
|
|
100400
|
+
capScope: "device",
|
|
100401
|
+
addonId: null,
|
|
100402
|
+
access: "create",
|
|
100403
|
+
role: "viewer"
|
|
100404
|
+
},
|
|
99034
100405
|
"control.setValue": {
|
|
99035
100406
|
capName: "control",
|
|
99036
100407
|
capScope: "device",
|
|
@@ -100984,6 +102355,13 @@ Object.freeze({
|
|
|
100984
102355
|
access: "view",
|
|
100985
102356
|
role: "viewer"
|
|
100986
102357
|
},
|
|
102358
|
+
"error.getStatus": {
|
|
102359
|
+
capName: "error",
|
|
102360
|
+
capScope: "device",
|
|
102361
|
+
addonId: null,
|
|
102362
|
+
access: "view",
|
|
102363
|
+
role: "viewer"
|
|
102364
|
+
},
|
|
100987
102365
|
"eventEmitter.getStatus": {
|
|
100988
102366
|
capName: "event-emitter",
|
|
100989
102367
|
capScope: "device",
|
|
@@ -101068,6 +102446,13 @@ Object.freeze({
|
|
|
101068
102446
|
access: "view",
|
|
101069
102447
|
role: "feature:face-assignment"
|
|
101070
102448
|
},
|
|
102449
|
+
"faceGallery.moveIdentitySample": {
|
|
102450
|
+
capName: "face-gallery",
|
|
102451
|
+
capScope: "system",
|
|
102452
|
+
addonId: null,
|
|
102453
|
+
access: "create",
|
|
102454
|
+
role: "feature:face-assignment"
|
|
102455
|
+
},
|
|
101071
102456
|
"faceGallery.removeSample": {
|
|
101072
102457
|
capName: "face-gallery",
|
|
101073
102458
|
capScope: "system",
|
|
@@ -104988,6 +106373,13 @@ Object.freeze({
|
|
|
104988
106373
|
access: "view",
|
|
104989
106374
|
role: "feature:plate-assignment"
|
|
104990
106375
|
},
|
|
106376
|
+
"plateGallery.moveVehicleSample": {
|
|
106377
|
+
capName: "plate-gallery",
|
|
106378
|
+
capScope: "system",
|
|
106379
|
+
addonId: null,
|
|
106380
|
+
access: "create",
|
|
106381
|
+
role: "feature:plate-assignment"
|
|
106382
|
+
},
|
|
104991
106383
|
"plateGallery.removeVehicleSample": {
|
|
104992
106384
|
capName: "plate-gallery",
|
|
104993
106385
|
capScope: "system",
|
|
@@ -107242,6 +108634,13 @@ Object.freeze({
|
|
|
107242
108634
|
access: "create",
|
|
107243
108635
|
role: "admin"
|
|
107244
108636
|
},
|
|
108637
|
+
"terminalSession.serverShellStatus": {
|
|
108638
|
+
capName: "terminal-session",
|
|
108639
|
+
capScope: "system",
|
|
108640
|
+
addonId: null,
|
|
108641
|
+
access: "view",
|
|
108642
|
+
role: "admin"
|
|
108643
|
+
},
|
|
107245
108644
|
"terminalSession.writeInput": {
|
|
107246
108645
|
capName: "terminal-session",
|
|
107247
108646
|
capScope: "system",
|
|
@@ -107977,6 +109376,13 @@ Object.freeze({
|
|
|
107977
109376
|
access: "view",
|
|
107978
109377
|
role: "viewer"
|
|
107979
109378
|
},
|
|
109379
|
+
"weatherStation.getStatus": {
|
|
109380
|
+
capName: "weather-station",
|
|
109381
|
+
capScope: "device",
|
|
109382
|
+
addonId: null,
|
|
109383
|
+
access: "view",
|
|
109384
|
+
role: "viewer"
|
|
109385
|
+
},
|
|
107980
109386
|
"webrtcSession.addIceCandidate": {
|
|
107981
109387
|
capName: "webrtc-session",
|
|
107982
109388
|
capScope: "device",
|
|
@@ -108190,7 +109596,7 @@ function offered(received) {
|
|
|
108190
109596
|
};
|
|
108191
109597
|
}
|
|
108192
109598
|
offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
|
|
108193
|
-
DeviceType.Camera, DeviceType.
|
|
109599
|
+
DeviceType.Camera, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Climate, DeviceType.HeatPump, DeviceType.EnergySystem, 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.WeatherStation, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Gateway;
|
|
108194
109600
|
DeviceRole.Siren, DeviceRole.Floodlight, DeviceRole.Spotlight, DeviceRole.PirSensor, DeviceRole.Chime, DeviceRole.Autotrack, DeviceRole.Nightvision, DeviceRole.PrivacyMask, DeviceRole.Doorbell, DeviceRole.CoverBlind, DeviceRole.CoverShutter, DeviceRole.CoverAwning, DeviceRole.CoverCurtain, DeviceRole.CoverGarage, DeviceRole.CoverGate, DeviceRole.CoverDoor, DeviceRole.CoverWindow, DeviceRole.BinaryHelper, DeviceRole.Outlet, DeviceRole.SwitchHeater, DeviceRole.SwitchFan, DeviceRole.SwitchLight, DeviceRole.ClimateAc, DeviceRole.ClimateThermostat, DeviceRole.ClimateFloor, DeviceRole.ClimateRadiator, DeviceRole.ClimateHeater, DeviceRole.ClimateHeatPumpCircuit, DeviceRole.MotionSensor, DeviceRole.ContactSensor, DeviceRole.LeakSensor, DeviceRole.SmokeSensor, DeviceRole.COSensor, DeviceRole.GasSensor, DeviceRole.TamperSensor, DeviceRole.VibrationSensor, DeviceRole.ConnectivitySensor, DeviceRole.SoundSensor, DeviceRole.BinarySensor, DeviceRole.TemperatureSensor, DeviceRole.HumiditySensor, DeviceRole.AmbientLightSensor, DeviceRole.PressureSensor, DeviceRole.WindSensor, DeviceRole.RainSensor, DeviceRole.UvSensor, DeviceRole.SolarRadiationSensor, DeviceRole.SoilMoistureSensor, DeviceRole.PowerSensor, DeviceRole.EnergySensor, DeviceRole.VoltageSensor, DeviceRole.CurrentSensor, DeviceRole.AirQualitySensor, DeviceRole.BatterySensor, DeviceRole.NumericSensor, DeviceRole.EnumSensor, DeviceRole.DateTimeSensor, DeviceRole.GenericSensor, DeviceRole.NumericControl, DeviceRole.SelectControl, DeviceRole.TextControl, DeviceRole.DateTimeControl, DeviceRole.MobilePushNotifier, DeviceRole.MessagingNotifier, DeviceRole.EmailNotifier, DeviceRole.GenericNotifier, DeviceRole.IrTransmitter;
|
|
108195
109601
|
new Set(Object.values(DeviceType));
|
|
108196
109602
|
new Set(Object.values(DeviceRole));
|
|
@@ -108529,11 +109935,31 @@ Object.freeze({
|
|
|
108529
109935
|
form: "single",
|
|
108530
109936
|
optional: false
|
|
108531
109937
|
}],
|
|
109938
|
+
"control.first": [{
|
|
109939
|
+
name: "deviceId",
|
|
109940
|
+
form: "single",
|
|
109941
|
+
optional: false
|
|
109942
|
+
}],
|
|
108532
109943
|
"control.getStatus": [{
|
|
108533
109944
|
name: "deviceId",
|
|
108534
109945
|
form: "single",
|
|
108535
109946
|
optional: false
|
|
108536
109947
|
}],
|
|
109948
|
+
"control.last": [{
|
|
109949
|
+
name: "deviceId",
|
|
109950
|
+
form: "single",
|
|
109951
|
+
optional: false
|
|
109952
|
+
}],
|
|
109953
|
+
"control.next": [{
|
|
109954
|
+
name: "deviceId",
|
|
109955
|
+
form: "single",
|
|
109956
|
+
optional: false
|
|
109957
|
+
}],
|
|
109958
|
+
"control.previous": [{
|
|
109959
|
+
name: "deviceId",
|
|
109960
|
+
form: "single",
|
|
109961
|
+
optional: false
|
|
109962
|
+
}],
|
|
108537
109963
|
"control.setValue": [{
|
|
108538
109964
|
name: "deviceId",
|
|
108539
109965
|
form: "single",
|
|
@@ -109158,6 +110584,11 @@ Object.freeze({
|
|
|
109158
110584
|
form: "single",
|
|
109159
110585
|
optional: false
|
|
109160
110586
|
}],
|
|
110587
|
+
"error.getStatus": [{
|
|
110588
|
+
name: "deviceId",
|
|
110589
|
+
form: "single",
|
|
110590
|
+
optional: false
|
|
110591
|
+
}],
|
|
109161
110592
|
"eventEmitter.getStatus": [{
|
|
109162
110593
|
name: "deviceId",
|
|
109163
110594
|
form: "single",
|
|
@@ -111199,6 +112630,11 @@ Object.freeze({
|
|
|
111199
112630
|
form: "single",
|
|
111200
112631
|
optional: false
|
|
111201
112632
|
}],
|
|
112633
|
+
"weatherStation.getStatus": [{
|
|
112634
|
+
name: "deviceId",
|
|
112635
|
+
form: "single",
|
|
112636
|
+
optional: false
|
|
112637
|
+
}],
|
|
111202
112638
|
"webrtcSession.addIceCandidate": [{
|
|
111203
112639
|
name: "deviceId",
|
|
111204
112640
|
form: "single",
|
|
@@ -111435,6 +112871,8 @@ new Map(Object.entries({
|
|
|
111435
112871
|
"storage-location-low": "system",
|
|
111436
112872
|
"storage-location-critical": "system",
|
|
111437
112873
|
"storage-location-ok": "system",
|
|
112874
|
+
"storage-drain-progress": "system",
|
|
112875
|
+
"storage-drain-complete": "system",
|
|
111438
112876
|
"alarm-triggered": "alarm",
|
|
111439
112877
|
"alarm-armed": "alarm",
|
|
111440
112878
|
"alarm-disarmed": "alarm",
|
|
@@ -111570,6 +113008,12 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
|
|
|
111570
113008
|
"storage-location-ok"
|
|
111571
113009
|
]
|
|
111572
113010
|
},
|
|
113011
|
+
{
|
|
113012
|
+
id: "storage-drain",
|
|
113013
|
+
family: "system",
|
|
113014
|
+
label: "Storage drain progress",
|
|
113015
|
+
kinds: ["storage-drain-progress", "storage-drain-complete"]
|
|
113016
|
+
},
|
|
111573
113017
|
{
|
|
111574
113018
|
id: "alerts",
|
|
111575
113019
|
family: "system",
|
|
@@ -111764,6 +113208,7 @@ var ALERT_EPISODE = [
|
|
|
111764
113208
|
"alert-error",
|
|
111765
113209
|
"alert-critical-resolved"
|
|
111766
113210
|
];
|
|
113211
|
+
var STORAGE_DRAIN = ["storage-drain-progress", "storage-drain-complete"];
|
|
111767
113212
|
var DEVICE_ATTENTION = ["device-attention-required", "device-attention-resolved"];
|
|
111768
113213
|
var UPDATES = [
|
|
111769
113214
|
"addon-update-available",
|
|
@@ -111975,6 +113420,7 @@ var STATE_SUMMARY_VARS = [
|
|
|
111975
113420
|
[...RULE_TEXT],
|
|
111976
113421
|
[...RULE_TEXT],
|
|
111977
113422
|
[...RULE_TEXT],
|
|
113423
|
+
[...RULE_TEXT],
|
|
111978
113424
|
[...RULE_BODY_ONLY],
|
|
111979
113425
|
[...RULE_TEXT],
|
|
111980
113426
|
([...RULE_TEXT], [...DEVICE_EVENTS]),
|
|
@@ -111987,8 +113433,16 @@ var STATE_SUMMARY_VARS = [
|
|
|
111987
113433
|
([...RULE_TEXT], [...BATTERY, ...CONSUMABLE]),
|
|
111988
113434
|
([...RULE_TEXT], [...CONSUMABLE]),
|
|
111989
113435
|
[...RULE_TEXT],
|
|
111990
|
-
([...RULE_TEXT], [
|
|
111991
|
-
|
|
113436
|
+
([...RULE_TEXT], [
|
|
113437
|
+
...UPDATES,
|
|
113438
|
+
...STORAGE_DRAIN,
|
|
113439
|
+
...ALERT_EPISODE
|
|
113440
|
+
]),
|
|
113441
|
+
([...RULE_TEXT], [
|
|
113442
|
+
...UPDATES,
|
|
113443
|
+
...STORAGE_DRAIN,
|
|
113444
|
+
...ALERT_EPISODE
|
|
113445
|
+
]),
|
|
111992
113446
|
[...RULE_TEXT],
|
|
111993
113447
|
[...RULE_TEXT],
|
|
111994
113448
|
[...RULE_TEXT],
|
|
@@ -112002,6 +113456,9 @@ var STATE_SUMMARY_VARS = [
|
|
|
112002
113456
|
[...RULE_TEXT],
|
|
112003
113457
|
[...RULE_TEXT],
|
|
112004
113458
|
[...RULE_TEXT],
|
|
113459
|
+
([...RULE_TEXT], [...STORAGE_DRAIN]),
|
|
113460
|
+
([...RULE_TEXT], [...STORAGE_DRAIN]),
|
|
113461
|
+
[...RULE_TEXT],
|
|
112005
113462
|
[...RULE_TEXT],
|
|
112006
113463
|
[...RULE_TEXT],
|
|
112007
113464
|
[...RULE_TEXT],
|
|
@@ -113777,6 +115234,17 @@ function present(part) {
|
|
|
113777
115234
|
return Object.values(part).some((v) => v !== null) ? part : null;
|
|
113778
115235
|
}
|
|
113779
115236
|
/**
|
|
115237
|
+
* A house reading without the heat pump, from Enpal's total that includes it. Both known:
|
|
115238
|
+
* the difference, floored at 0. No pump in the system: the total as reported. A pump that
|
|
115239
|
+
* exists but whose reading is unknown makes the result unknown, never the inflated total.
|
|
115240
|
+
*/
|
|
115241
|
+
function withoutPump(total, pump, hasPump) {
|
|
115242
|
+
if (total === null) return null;
|
|
115243
|
+
if (!hasPump) return total;
|
|
115244
|
+
if (pump === null) return null;
|
|
115245
|
+
return Math.max(0, total - pump);
|
|
115246
|
+
}
|
|
115247
|
+
/**
|
|
113780
115248
|
* The grid power, + import / − export, from the two halves the catalog splits it into.
|
|
113781
115249
|
* With both halves known it is their difference. With one half only, it is that half
|
|
113782
115250
|
* while it flows: a zero import with no export reading cannot say whether the house
|
|
@@ -113806,16 +115274,17 @@ function computeEnergySystemStatus(snapshot, options, now) {
|
|
|
113806
115274
|
importTodayKwh: read.numeric("grid-import-day"),
|
|
113807
115275
|
exportTodayKwh: read.numeric("grid-export-day")
|
|
113808
115276
|
};
|
|
113809
|
-
const house = {
|
|
113810
|
-
powerW: read.power("house"),
|
|
113811
|
-
energyKwh: read.energy("house"),
|
|
113812
|
-
todayKwh: read.numeric("house-energy-day")
|
|
113813
|
-
};
|
|
113814
115277
|
const heatPump = {
|
|
113815
115278
|
powerW: read.power("heatpump"),
|
|
113816
115279
|
energyKwh: read.energy("heatpump"),
|
|
113817
115280
|
todayKwh: null
|
|
113818
115281
|
};
|
|
115282
|
+
const hasPump = present(heatPump) !== null;
|
|
115283
|
+
const house = {
|
|
115284
|
+
powerW: withoutPump(read.power("house"), heatPump.powerW, hasPump),
|
|
115285
|
+
energyKwh: withoutPump(read.energy("house"), heatPump.energyKwh, hasPump),
|
|
115286
|
+
todayKwh: hasPump ? null : read.numeric("house-energy-day")
|
|
115287
|
+
};
|
|
113819
115288
|
const storage = {
|
|
113820
115289
|
stateOfChargePct: read.numeric("battery"),
|
|
113821
115290
|
storedKwh: read.numeric("battery-stored-energy"),
|
|
@@ -114218,6 +115687,53 @@ function accessoryChildSpecs(config) {
|
|
|
114218
115687
|
return out;
|
|
114219
115688
|
}
|
|
114220
115689
|
//#endregion
|
|
115690
|
+
//#region src/devices/enpal-catalog-rename.ts
|
|
115691
|
+
/**
|
|
115692
|
+
* House and heat-pump meters used to be created as "Consumption". A restore
|
|
115693
|
+
* may replace that default; a name the operator chose stays.
|
|
115694
|
+
*/
|
|
115695
|
+
/** Keep unless the stored name is still the old catalogue default for this meter. */
|
|
115696
|
+
function catalogNameWrite(input) {
|
|
115697
|
+
const spec = findSpec(input.specId);
|
|
115698
|
+
if (spec === void 0) return { kind: "keep" };
|
|
115699
|
+
if (input.specId !== "house" && input.specId !== "heatpump") return { kind: "keep" };
|
|
115700
|
+
if (input.name === spec.name) return { kind: "keep" };
|
|
115701
|
+
if (input.name !== "Consumption") return { kind: "keep" };
|
|
115702
|
+
return {
|
|
115703
|
+
kind: "rename",
|
|
115704
|
+
name: spec.name,
|
|
115705
|
+
stampUserNamed: input.userNamed === true
|
|
115706
|
+
};
|
|
115707
|
+
}
|
|
115708
|
+
/** Rename each child that still carries the old default. Logs every rename. */
|
|
115709
|
+
async function renameLegacyCatalogDevices(input) {
|
|
115710
|
+
for (const child of input.children) {
|
|
115711
|
+
const decision = catalogNameWrite(child);
|
|
115712
|
+
if (decision.kind === "keep") continue;
|
|
115713
|
+
try {
|
|
115714
|
+
await input.rename(child.deviceId, decision.name, decision.stampUserNamed);
|
|
115715
|
+
} catch (err) {
|
|
115716
|
+
input.logger.warn("enpal: could not rename a device still carrying the old catalogue name", {
|
|
115717
|
+
tags: { deviceId: child.deviceId },
|
|
115718
|
+
meta: {
|
|
115719
|
+
from: child.name,
|
|
115720
|
+
to: decision.name,
|
|
115721
|
+
error: errMsg(err)
|
|
115722
|
+
}
|
|
115723
|
+
});
|
|
115724
|
+
continue;
|
|
115725
|
+
}
|
|
115726
|
+
input.logger.info("enpal: renamed a device that still had the old catalogue name", {
|
|
115727
|
+
tags: { deviceId: child.deviceId },
|
|
115728
|
+
meta: {
|
|
115729
|
+
specId: child.specId,
|
|
115730
|
+
from: child.name,
|
|
115731
|
+
to: decision.name
|
|
115732
|
+
}
|
|
115733
|
+
});
|
|
115734
|
+
}
|
|
115735
|
+
}
|
|
115736
|
+
//#endregion
|
|
114221
115737
|
//#region src/devices/enpal-metadata-sync.ts
|
|
114222
115738
|
/**
|
|
114223
115739
|
* Writes the identity facts a piece reported (model, serial, firmware) as device attributes.
|
|
@@ -114333,6 +115849,7 @@ var EnpalEquipmentDevice = class extends BaseDevice {
|
|
|
114333
115849
|
},
|
|
114334
115850
|
logger: this.ctx.logger
|
|
114335
115851
|
});
|
|
115852
|
+
await this.renameLegacyNames();
|
|
114336
115853
|
const metadata = new EnpalMetadataSync({
|
|
114337
115854
|
equipment: this.equipment,
|
|
114338
115855
|
deviceId: this.id,
|
|
@@ -114350,6 +115867,45 @@ var EnpalEquipmentDevice = class extends BaseDevice {
|
|
|
114350
115867
|
});
|
|
114351
115868
|
await metadata.apply();
|
|
114352
115869
|
}
|
|
115870
|
+
/**
|
|
115871
|
+
* A meter created as "Consumption" takes the current catalogue name. A name
|
|
115872
|
+
* the operator changed is left as it is.
|
|
115873
|
+
*/
|
|
115874
|
+
async renameLegacyNames() {
|
|
115875
|
+
const manager = this.ctx.api.deviceManager;
|
|
115876
|
+
try {
|
|
115877
|
+
const legacy = (await manager.getChildren.query({ parentDeviceId: this.id })).flatMap((child) => {
|
|
115878
|
+
const specId = child.config["specId"];
|
|
115879
|
+
if (typeof specId !== "string" || child.name !== "Consumption") return [];
|
|
115880
|
+
return [{
|
|
115881
|
+
deviceId: child.id,
|
|
115882
|
+
specId,
|
|
115883
|
+
name: child.name
|
|
115884
|
+
}];
|
|
115885
|
+
});
|
|
115886
|
+
await renameLegacyCatalogDevices({
|
|
115887
|
+
children: await Promise.all(legacy.map(async (child) => {
|
|
115888
|
+
const meta = await manager.loadMeta.query({ deviceId: child.deviceId });
|
|
115889
|
+
return {
|
|
115890
|
+
...child,
|
|
115891
|
+
name: meta?.name ?? child.name,
|
|
115892
|
+
userNamed: meta?.userNamed
|
|
115893
|
+
};
|
|
115894
|
+
})),
|
|
115895
|
+
rename: (deviceId, name, stampUserNamed) => manager.setName.mutate({
|
|
115896
|
+
deviceId,
|
|
115897
|
+
name,
|
|
115898
|
+
...stampUserNamed ? {} : { stampUserNamed: false }
|
|
115899
|
+
}),
|
|
115900
|
+
logger: this.ctx.logger
|
|
115901
|
+
});
|
|
115902
|
+
} catch (err) {
|
|
115903
|
+
this.ctx.logger.warn("enpal: could not rename devices still carrying the old catalogue name", {
|
|
115904
|
+
tags: { deviceId: this.id },
|
|
115905
|
+
meta: { error: errMsg(err) }
|
|
115906
|
+
});
|
|
115907
|
+
}
|
|
115908
|
+
}
|
|
114353
115909
|
async removeDevice() {
|
|
114354
115910
|
this.snapshotUnsub?.();
|
|
114355
115911
|
this.snapshotUnsub = null;
|