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