@camstack/addon-model-studio 1.1.325 → 1.1.327
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/{MotionZonesSettings-BFSzpDlc.mjs → MotionZonesSettings-3MG9zvaA.mjs} +2 -2
- package/dist/{PrivacyMaskSettings-bzbS1q77.mjs → PrivacyMaskSettings-BA58yuWC.mjs} +4 -4
- package/dist/{SceneMonitorEditor-5jtlWR_n.mjs → SceneMonitorEditor-BEf_PNcV.mjs} +3 -3
- package/dist/_stub.js +4154 -4081
- package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_model_studio_page-Cw6pUSlz.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_model_studio_page-BP5KTsDn.mjs} +3 -3
- package/dist/_virtual_mf___mfe_internal__addon_model_studio_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Bfyumm6x.mjs +26 -0
- package/dist/addon-model-studio.css +1 -1
- package/dist/{hostInit-BCmIggoT.mjs → hostInit-DZPtjh_2.mjs} +2 -2
- package/dist/model-studio.addon.js +927 -122
- package/dist/model-studio.addon.mjs +927 -122
- package/dist/{player-overlays-DwnklNvS.mjs → player-overlays-BzTF5DiJ.mjs} +1 -1
- package/dist/remoteEntry.js +1 -1
- package/dist/{responsive-DFhiWOST.mjs → responsive-BmfFp5be.mjs} +1 -1
- package/dist/{scene-monitor-copy-DVaiZyk_.mjs → scene-monitor-copy-BQA2BQQq.mjs} +1 -1
- package/dist/{square-CL1GZCDy.mjs → square-MPgKEZFC.mjs} +1 -1
- package/dist/{trash-2-Bs4Zqar1.mjs → trash-2-C3CU6OqE.mjs} +1 -1
- package/dist/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_model_studio_page__remoteEntry_js-BX2O_m8t.mjs → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_model_studio_page__remoteEntry_js-DXGb6Oaj.mjs} +1 -1
- package/package.json +1 -1
- package/dist/_virtual_mf___mfe_internal__addon_model_studio_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-D8h8qfbd.mjs +0 -26
|
@@ -5850,7 +5850,7 @@ var ZodIssueCode = {
|
|
|
5850
5850
|
var ZodFirstPartyTypeKind;
|
|
5851
5851
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5852
5852
|
//#endregion
|
|
5853
|
-
//#region ../types/dist/sleep-
|
|
5853
|
+
//#region ../types/dist/sleep-CeyhrcOk.mjs
|
|
5854
5854
|
/**
|
|
5855
5855
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5856
5856
|
* window to float samples (D455).
|
|
@@ -7917,6 +7917,7 @@ var BaseAddon = class {
|
|
|
7917
7917
|
* a warning and falls back rather than crashing boot.
|
|
7918
7918
|
*/
|
|
7919
7919
|
state(key, schema, fallback) {
|
|
7920
|
+
this._declaredStateKeys.add(key);
|
|
7920
7921
|
return createDurableState({
|
|
7921
7922
|
key,
|
|
7922
7923
|
schema,
|
|
@@ -7932,6 +7933,96 @@ var BaseAddon = class {
|
|
|
7932
7933
|
} })
|
|
7933
7934
|
});
|
|
7934
7935
|
}
|
|
7936
|
+
/** Durable-state keys this process registered through {@link state}. */
|
|
7937
|
+
_declaredStateKeys = /* @__PURE__ */ new Set();
|
|
7938
|
+
/**
|
|
7939
|
+
* Stale-data audit: every global-settings key this addon still declares —
|
|
7940
|
+
* constructor defaults, settings-schema fields (layout containers included),
|
|
7941
|
+
* durable-state keys and the root of each `secretStatePaths` entry. Answered
|
|
7942
|
+
* here, in the addon's own process, so the hub keeps no copy of it.
|
|
7943
|
+
*/
|
|
7944
|
+
async getDeclaredSettingsKeys() {
|
|
7945
|
+
return { keys: [...this.declaredKeySet()].toSorted() };
|
|
7946
|
+
}
|
|
7947
|
+
declaredKeySet() {
|
|
7948
|
+
const keys = new Set(Object.keys(this.defaults));
|
|
7949
|
+
const schema = this.globalSettingsSchema();
|
|
7950
|
+
if (schema) for (const section of schema.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
7951
|
+
for (const key of this._declaredStateKeys) keys.add(key);
|
|
7952
|
+
for (const path of this.secretStatePaths) {
|
|
7953
|
+
const root = path.split(/[.[]/, 1)[0];
|
|
7954
|
+
if (root) keys.add(root);
|
|
7955
|
+
}
|
|
7956
|
+
return keys;
|
|
7957
|
+
}
|
|
7958
|
+
/**
|
|
7959
|
+
* Stale-data audit: remove stored global-settings keys the addon no longer
|
|
7960
|
+
* declares, and clear the keychain values their markers held — except a value
|
|
7961
|
+
* a surviving key still points at. A declared or absent key is refused.
|
|
7962
|
+
*/
|
|
7963
|
+
async unsetStoredSettingsKeys(keys) {
|
|
7964
|
+
const settings = this._ctx?.settings;
|
|
7965
|
+
const raw = this.rawSettings();
|
|
7966
|
+
if (!settings || !raw) return {
|
|
7967
|
+
removed: [],
|
|
7968
|
+
refused: keys.map((key) => ({
|
|
7969
|
+
key,
|
|
7970
|
+
reason: "no settings store"
|
|
7971
|
+
})),
|
|
7972
|
+
secretsCleared: 0
|
|
7973
|
+
};
|
|
7974
|
+
let stored;
|
|
7975
|
+
try {
|
|
7976
|
+
stored = await raw.readAddonStore();
|
|
7977
|
+
} catch (error) {
|
|
7978
|
+
const reason = `store unreadable: ${error instanceof Error ? error.message : "unknown"}`;
|
|
7979
|
+
return {
|
|
7980
|
+
removed: [],
|
|
7981
|
+
refused: keys.map((key) => ({
|
|
7982
|
+
key,
|
|
7983
|
+
reason
|
|
7984
|
+
})),
|
|
7985
|
+
secretsCleared: 0
|
|
7986
|
+
};
|
|
7987
|
+
}
|
|
7988
|
+
const declared = this.declaredKeySet();
|
|
7989
|
+
const removed = [];
|
|
7990
|
+
const refused = [];
|
|
7991
|
+
for (const key of keys) {
|
|
7992
|
+
const base = key.split("@", 1)[0] ?? key;
|
|
7993
|
+
if (declared.has(base)) refused.push({
|
|
7994
|
+
key,
|
|
7995
|
+
reason: "still declared by the addon"
|
|
7996
|
+
});
|
|
7997
|
+
else if (!(key in stored)) refused.push({
|
|
7998
|
+
key,
|
|
7999
|
+
reason: "not stored"
|
|
8000
|
+
});
|
|
8001
|
+
else removed.push(key);
|
|
8002
|
+
}
|
|
8003
|
+
if (removed.length === 0) return {
|
|
8004
|
+
removed,
|
|
8005
|
+
refused,
|
|
8006
|
+
secretsCleared: 0
|
|
8007
|
+
};
|
|
8008
|
+
const kept = /* @__PURE__ */ new Set();
|
|
8009
|
+
const held = /* @__PURE__ */ new Set();
|
|
8010
|
+
for (const [key, value] of Object.entries(stored)) {
|
|
8011
|
+
const target = removed.includes(key) ? held : kept;
|
|
8012
|
+
for (const ref of collectSecretMarkerRefs(value)) target.add(ref);
|
|
8013
|
+
}
|
|
8014
|
+
const refsToClear = [...held].filter((ref) => !kept.has(ref));
|
|
8015
|
+
const patch = {};
|
|
8016
|
+
for (const key of removed) patch[key] = void 0;
|
|
8017
|
+
await settings.writeAddonStore(patch);
|
|
8018
|
+
const secretsCleared = refsToClear.length > 0 && this._ctx?.secrets ? await this._ctx.secrets.delete(refsToClear) : 0;
|
|
8019
|
+
await this.resolveConfig();
|
|
8020
|
+
return {
|
|
8021
|
+
removed,
|
|
8022
|
+
refused,
|
|
8023
|
+
secretsCleared
|
|
8024
|
+
};
|
|
8025
|
+
}
|
|
7935
8026
|
/** Per-device variant of {@link state}, backed by the per-device store. */
|
|
7936
8027
|
deviceState(deviceId, key, schema, fallback) {
|
|
7937
8028
|
return createDurableState({
|
|
@@ -8202,6 +8293,21 @@ function dropPerNodeFields(fields) {
|
|
|
8202
8293
|
}
|
|
8203
8294
|
return kept;
|
|
8204
8295
|
}
|
|
8296
|
+
function collectFieldKeys(fields) {
|
|
8297
|
+
const collected = [];
|
|
8298
|
+
for (const field of fields) {
|
|
8299
|
+
if (field.type === "group") {
|
|
8300
|
+
collected.push(...collectFieldKeys(field.fields));
|
|
8301
|
+
continue;
|
|
8302
|
+
}
|
|
8303
|
+
if (field.type === "sub-tabs") {
|
|
8304
|
+
for (const tab of field.tabs) collected.push(...collectFieldKeys(tab.fields));
|
|
8305
|
+
continue;
|
|
8306
|
+
}
|
|
8307
|
+
if ("key" in field && typeof field.key === "string") collected.push(field.key);
|
|
8308
|
+
}
|
|
8309
|
+
return collected;
|
|
8310
|
+
}
|
|
8205
8311
|
function collectPerNodeFieldKeys(fields) {
|
|
8206
8312
|
const collected = [];
|
|
8207
8313
|
for (const field of fields) {
|
|
@@ -11057,7 +11163,7 @@ object({
|
|
|
11057
11163
|
params: record(string(), union([string(), number()])).optional()
|
|
11058
11164
|
});
|
|
11059
11165
|
//#endregion
|
|
11060
|
-
//#region ../types/dist/err-msg-
|
|
11166
|
+
//#region ../types/dist/err-msg-BD5buxvT.mjs
|
|
11061
11167
|
var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
11062
11168
|
DeviceType["Camera"] = "camera";
|
|
11063
11169
|
DeviceType["Hub"] = "hub";
|
|
@@ -11163,6 +11269,12 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
|
11163
11269
|
DeviceType["Washer"] = "washer";
|
|
11164
11270
|
/** Tumble dryer. Same plug-built running surface as {@link Washer}. */
|
|
11165
11271
|
DeviceType["Dryer"] = "dryer";
|
|
11272
|
+
/** The physical bridge box of a vendor ecosystem (Velux KLF-200, and later
|
|
11273
|
+
* other radio-to-IP bridges). It carries the box's own facts (firmware,
|
|
11274
|
+
* address, connection state) and the actions the box really supports
|
|
11275
|
+
* (`reboot`, `device-admin-link`). Products are NOT its children — they are
|
|
11276
|
+
* adopted beside it — and its connection settings stay on the integration. */
|
|
11277
|
+
DeviceType["Gateway"] = "gateway";
|
|
11166
11278
|
return DeviceType;
|
|
11167
11279
|
}({});
|
|
11168
11280
|
var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
|
|
@@ -11413,7 +11525,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
|
|
|
11413
11525
|
return DeviceRole;
|
|
11414
11526
|
}({});
|
|
11415
11527
|
//#endregion
|
|
11416
|
-
//#region ../types/dist/composition-
|
|
11528
|
+
//#region ../types/dist/composition-C7WdThmP.mjs
|
|
11417
11529
|
/**
|
|
11418
11530
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
11419
11531
|
*
|
|
@@ -16541,8 +16653,172 @@ object({
|
|
|
16541
16653
|
quantity: ReadingQuantitySchema,
|
|
16542
16654
|
unit: string().min(1)
|
|
16543
16655
|
});
|
|
16544
|
-
|
|
16545
|
-
|
|
16656
|
+
new Set([
|
|
16657
|
+
{
|
|
16658
|
+
id: "person",
|
|
16659
|
+
name: "Person"
|
|
16660
|
+
},
|
|
16661
|
+
{
|
|
16662
|
+
id: "vehicle",
|
|
16663
|
+
name: "Vehicle"
|
|
16664
|
+
},
|
|
16665
|
+
{
|
|
16666
|
+
id: "animal",
|
|
16667
|
+
name: "Animal"
|
|
16668
|
+
},
|
|
16669
|
+
{
|
|
16670
|
+
id: "package",
|
|
16671
|
+
name: "Package"
|
|
16672
|
+
}
|
|
16673
|
+
].map((l) => l.id));
|
|
16674
|
+
var COCO_TO_MACRO = {
|
|
16675
|
+
mapping: {
|
|
16676
|
+
person: "person",
|
|
16677
|
+
bicycle: "vehicle",
|
|
16678
|
+
car: "vehicle",
|
|
16679
|
+
motorcycle: "vehicle",
|
|
16680
|
+
airplane: "vehicle",
|
|
16681
|
+
bus: "vehicle",
|
|
16682
|
+
train: "vehicle",
|
|
16683
|
+
truck: "vehicle",
|
|
16684
|
+
boat: "vehicle",
|
|
16685
|
+
bird: "animal",
|
|
16686
|
+
cat: "animal",
|
|
16687
|
+
dog: "animal",
|
|
16688
|
+
horse: "animal",
|
|
16689
|
+
sheep: "animal",
|
|
16690
|
+
cow: "animal",
|
|
16691
|
+
elephant: "animal",
|
|
16692
|
+
bear: "animal",
|
|
16693
|
+
zebra: "animal",
|
|
16694
|
+
giraffe: "animal",
|
|
16695
|
+
suitcase: "package",
|
|
16696
|
+
backpack: "package",
|
|
16697
|
+
handbag: "package"
|
|
16698
|
+
},
|
|
16699
|
+
preserveOriginal: false
|
|
16700
|
+
};
|
|
16701
|
+
/**
|
|
16702
|
+
* WHAT a detection-shaped leaf is about: one MACRO class (person, vehicle,
|
|
16703
|
+
* animal, package) plus an OPTIONAL label that narrows it.
|
|
16704
|
+
*
|
|
16705
|
+
* The taxonomy is `COCO_TO_MACRO` (and `CLASS_MAP_MACRO_TARGETS`) — never a
|
|
16706
|
+
* second list. The label that fits depends on the macro class:
|
|
16707
|
+
*
|
|
16708
|
+
* - `animal` → a sub-class (`dog`, `cat`, …);
|
|
16709
|
+
* - `person` → a known face (an identity of the `face-gallery`);
|
|
16710
|
+
* - `vehicle` → a plate (its text) or a sub-class (`car`, `truck`, …);
|
|
16711
|
+
* - `package` → a sub-class.
|
|
16712
|
+
*
|
|
16713
|
+
* `other` exists only so a class the taxonomy does not know (a custom model's
|
|
16714
|
+
* `forklift`) survives the conversion of a stored recipe without widening.
|
|
16715
|
+
*
|
|
16716
|
+
* A label is decided LATE (D683): face recognition and LPR land after the
|
|
16717
|
+
* detection starts. The track-lifecycle `update` event carries the newly
|
|
16718
|
+
* resolved `label` / `plateText`, which is why a leaf with a label fires when
|
|
16719
|
+
* the subject GETS it — not at the first detection.
|
|
16720
|
+
*/
|
|
16721
|
+
var SubClassLabelSchema = object({
|
|
16722
|
+
kind: literal("sub-class"),
|
|
16723
|
+
className: string().min(1)
|
|
16724
|
+
});
|
|
16725
|
+
var IdentityLabelSchema = object({
|
|
16726
|
+
kind: literal("identity"),
|
|
16727
|
+
/** The `face-gallery` identity id (stable across a rename). */
|
|
16728
|
+
identityId: string().min(1),
|
|
16729
|
+
/** The identity's name — what a track-lifecycle event carries as `label`. */
|
|
16730
|
+
name: string().min(1)
|
|
16731
|
+
});
|
|
16732
|
+
var PlateLabelSchema = object({
|
|
16733
|
+
kind: literal("plate"),
|
|
16734
|
+
text: string().min(1)
|
|
16735
|
+
});
|
|
16736
|
+
function macroOf$1(className) {
|
|
16737
|
+
return COCO_TO_MACRO.mapping[className.toLowerCase()] ?? className.toLowerCase();
|
|
16738
|
+
}
|
|
16739
|
+
/** A sub-class label must belong to the macro it is under. */
|
|
16740
|
+
function subClassBelongs(macro, label) {
|
|
16741
|
+
return label === void 0 || macroOf$1(label.className) === macro;
|
|
16742
|
+
}
|
|
16743
|
+
var subClassOf = (macro) => SubClassLabelSchema.refine((l) => subClassBelongs(macro, l), { message: `that class is not a kind of ${macro} in the detection taxonomy` });
|
|
16744
|
+
var DetectionSubjectSchema = discriminatedUnion("macro", [
|
|
16745
|
+
object({
|
|
16746
|
+
macro: literal("animal"),
|
|
16747
|
+
label: subClassOf("animal").optional()
|
|
16748
|
+
}),
|
|
16749
|
+
object({
|
|
16750
|
+
macro: literal("person"),
|
|
16751
|
+
label: IdentityLabelSchema.optional()
|
|
16752
|
+
}),
|
|
16753
|
+
object({
|
|
16754
|
+
macro: literal("vehicle"),
|
|
16755
|
+
label: discriminatedUnion("kind", [SubClassLabelSchema.refine((l) => subClassBelongs("vehicle", l), { message: "that class is not a kind of vehicle in the detection taxonomy" }), PlateLabelSchema]).optional()
|
|
16756
|
+
}),
|
|
16757
|
+
object({
|
|
16758
|
+
macro: literal("package"),
|
|
16759
|
+
label: subClassOf("package").optional()
|
|
16760
|
+
}),
|
|
16761
|
+
object({
|
|
16762
|
+
macro: literal("other"),
|
|
16763
|
+
className: string().min(1)
|
|
16764
|
+
})
|
|
16765
|
+
]);
|
|
16766
|
+
/**
|
|
16767
|
+
* The subject of a PARKED object. The stationary registry's event carries a
|
|
16768
|
+
* class and a zone, no face and no plate — an identity or plate label there
|
|
16769
|
+
* could never match, so the schema refuses it instead of letting it sit dead.
|
|
16770
|
+
*/
|
|
16771
|
+
var StationarySubjectSchema = DetectionSubjectSchema.refine((s) => s.macro === "other" || s.label === void 0 || s.label.kind === "sub-class", { message: "a parked object carries no face or plate — only a class can narrow it" });
|
|
16772
|
+
/**
|
|
16773
|
+
* A stored class string as a subject: a macro name stays a macro; a raw class
|
|
16774
|
+
* the taxonomy files under a macro becomes that macro plus the sub-class label
|
|
16775
|
+
* (`dog` → animal / dog); anything else is `other`, kept verbatim.
|
|
16776
|
+
*/
|
|
16777
|
+
function subjectFromClassName(className) {
|
|
16778
|
+
const name = className.trim().toLowerCase();
|
|
16779
|
+
const macro = COCO_TO_MACRO.mapping[name];
|
|
16780
|
+
if (new Set(Object.values(COCO_TO_MACRO.mapping)).has(name)) return macroOnly(name);
|
|
16781
|
+
if (macro === void 0) return {
|
|
16782
|
+
macro: "other",
|
|
16783
|
+
className
|
|
16784
|
+
};
|
|
16785
|
+
const label = {
|
|
16786
|
+
kind: "sub-class",
|
|
16787
|
+
className: name
|
|
16788
|
+
};
|
|
16789
|
+
switch (macro) {
|
|
16790
|
+
case "animal": return {
|
|
16791
|
+
macro: "animal",
|
|
16792
|
+
label
|
|
16793
|
+
};
|
|
16794
|
+
case "vehicle": return {
|
|
16795
|
+
macro: "vehicle",
|
|
16796
|
+
label
|
|
16797
|
+
};
|
|
16798
|
+
case "package": return {
|
|
16799
|
+
macro: "package",
|
|
16800
|
+
label
|
|
16801
|
+
};
|
|
16802
|
+
default: return {
|
|
16803
|
+
macro: "other",
|
|
16804
|
+
className
|
|
16805
|
+
};
|
|
16806
|
+
}
|
|
16807
|
+
}
|
|
16808
|
+
function macroOnly(macro) {
|
|
16809
|
+
switch (macro) {
|
|
16810
|
+
case "animal": return { macro: "animal" };
|
|
16811
|
+
case "person": return { macro: "person" };
|
|
16812
|
+
case "vehicle": return { macro: "vehicle" };
|
|
16813
|
+
case "package": return { macro: "package" };
|
|
16814
|
+
default: return {
|
|
16815
|
+
macro: "other",
|
|
16816
|
+
className: macro
|
|
16817
|
+
};
|
|
16818
|
+
}
|
|
16819
|
+
}
|
|
16820
|
+
/** How a state leaf compares a device field to a value. Derived from the
|
|
16821
|
+
* field's `kind` in `deviceManager.getWireableFields`, never hand-maintained. */
|
|
16546
16822
|
var AutomationConditionOperatorSchema = _enum([
|
|
16547
16823
|
"eq",
|
|
16548
16824
|
"ne",
|
|
@@ -16553,12 +16829,76 @@ var AutomationConditionOperatorSchema = _enum([
|
|
|
16553
16829
|
"contains",
|
|
16554
16830
|
"in"
|
|
16555
16831
|
]);
|
|
16556
|
-
|
|
16557
|
-
|
|
16832
|
+
/**
|
|
16833
|
+
* D8 compliance, and it is the reason a "becomes" leaf is not merely an event
|
|
16834
|
+
* subscription: it evaluates against the **state mirror**, which is reconciled,
|
|
16835
|
+
* and an event only WAKES the evaluation. A dropped event therefore DELAYS a
|
|
16836
|
+
* fire; it does not lose it.
|
|
16837
|
+
*/
|
|
16838
|
+
var DeviceBecomesLeafSchema = object({
|
|
16839
|
+
kind: literal("device-becomes"),
|
|
16840
|
+
deviceId: number().int().nonnegative(),
|
|
16841
|
+
cap: string().min(1),
|
|
16842
|
+
fieldPath: string().min(1),
|
|
16843
|
+
/** Fire when the field takes this value. Omit to fire on any change. */
|
|
16844
|
+
becomes: union([
|
|
16845
|
+
string(),
|
|
16846
|
+
number(),
|
|
16847
|
+
boolean()
|
|
16848
|
+
]).optional(),
|
|
16849
|
+
/** Only on a CHANGE of value, not on every re-report. */
|
|
16850
|
+
edge: boolean().optional(),
|
|
16851
|
+
/** The new value must hold this long before it counts. */
|
|
16852
|
+
forMs: number().int().min(0).max(864e5).optional(),
|
|
16853
|
+
/** Collapse a burst into one occurrence. */
|
|
16854
|
+
debounceMs: number().int().min(0).max(6e5).optional()
|
|
16855
|
+
});
|
|
16856
|
+
var DeviceEventLeafSchema = object({
|
|
16857
|
+
kind: literal("device-event"),
|
|
16858
|
+
/** An `EventCategory` value. */
|
|
16859
|
+
category: string().min(1),
|
|
16860
|
+
deviceId: number().int().nonnegative().optional()
|
|
16861
|
+
});
|
|
16862
|
+
var ScheduleLeafSchema = object({
|
|
16863
|
+
kind: literal("schedule"),
|
|
16864
|
+
cron: string().min(1).max(120)
|
|
16865
|
+
});
|
|
16866
|
+
var ManualLeafSchema = object({ kind: literal("manual") });
|
|
16867
|
+
var IntervalLeafSchema = object({
|
|
16868
|
+
kind: literal("interval"),
|
|
16869
|
+
minutes: number().int().min(1).max(1440)
|
|
16870
|
+
});
|
|
16871
|
+
var DetectionLeafSchema = object({
|
|
16872
|
+
kind: literal("detection"),
|
|
16873
|
+
/** One macro class each, optionally narrowed by a label; a track of any listed subject fires. */
|
|
16874
|
+
subjects: array(DetectionSubjectSchema).min(1),
|
|
16875
|
+
deviceIds: array(number().int().nonnegative()).min(1),
|
|
16876
|
+
/** The track must have visited at least one of these zones. */
|
|
16877
|
+
zones: array(string().min(1)).min(1).optional(),
|
|
16878
|
+
/** Best-so-far confidence the track must reach. */
|
|
16879
|
+
minScore: number().min(0).max(1).optional()
|
|
16880
|
+
});
|
|
16881
|
+
var StationaryLeafSchema = object({
|
|
16882
|
+
kind: literal("stationary"),
|
|
16883
|
+
deviceIds: array(number().int().nonnegative()).min(1),
|
|
16884
|
+
subjects: array(StationarySubjectSchema).min(1).optional(),
|
|
16885
|
+
zones: array(string().min(1)).min(1).optional(),
|
|
16886
|
+
/** `appeared`: it became parked. `departed`: the registry retired it. */
|
|
16887
|
+
phase: _enum(["appeared", "departed"])
|
|
16888
|
+
});
|
|
16889
|
+
/** "is": one device field compared to one value, NOW. */
|
|
16890
|
+
var DeviceIsLeafSchema = object({
|
|
16891
|
+
kind: literal("device-is"),
|
|
16558
16892
|
deviceId: number().int().nonnegative(),
|
|
16559
16893
|
cap: string().min(1),
|
|
16560
16894
|
fieldPath: string().min(1),
|
|
16561
16895
|
operator: AutomationConditionOperatorSchema,
|
|
16896
|
+
/**
|
|
16897
|
+
* The value. A number on a quantity field is stored in the field's base unit
|
|
16898
|
+
* (D944); a quantity value is the WRITE form, which the save boundary
|
|
16899
|
+
* replaces with the converted number and `unit`. A leaf that still carries
|
|
16900
|
+
* one is never evaluated (it fails).
|
|
16901
|
+
*/
|
|
16562
16902
|
value: union([
|
|
16563
16903
|
string(),
|
|
16564
16904
|
number(),
|
|
@@ -16566,24 +16906,26 @@ var AutomationConditionLeafSchema = object({
|
|
|
16566
16906
|
array(union([string(), number()])),
|
|
16567
16907
|
QuantityValueSchema
|
|
16568
16908
|
]),
|
|
16909
|
+
/** The unit the person picked for `value` (display only; `value` is in the base unit). */
|
|
16569
16910
|
unit: string().min(1).optional()
|
|
16570
16911
|
});
|
|
16571
|
-
|
|
16912
|
+
/**
|
|
16913
|
+
* True when the camera currently has a PARKED object matching `subjects` in at
|
|
16914
|
+
* least one of `zones`. Reads the zone-analytics snapshot through the same
|
|
16915
|
+
* mirror as every other state leaf, so a dropped event never decides it (D49).
|
|
16916
|
+
*/
|
|
16917
|
+
var StationaryPresentLeafSchema = object({
|
|
16572
16918
|
kind: literal("stationary-present"),
|
|
16573
16919
|
deviceId: number().int().nonnegative(),
|
|
16574
|
-
|
|
16575
|
-
classes: array(string().min(1)).min(1).optional(),
|
|
16576
|
-
/** The parked object must be in at least one of these zones. */
|
|
16920
|
+
subjects: array(StationarySubjectSchema).min(1).optional(),
|
|
16577
16921
|
zones: array(string().min(1)).min(1).optional()
|
|
16578
16922
|
});
|
|
16579
16923
|
/**
|
|
16580
|
-
* The
|
|
16581
|
-
*
|
|
16582
|
-
*
|
|
16583
|
-
* validation is the SAME function `ExpressionSourceSchema` runs, so the two
|
|
16584
|
-
* cannot drift — an expression that one accepts, the other accepts.
|
|
16924
|
+
* The escape hatch: the bounded expression engine over named device bindings.
|
|
16925
|
+
* Declared as a plain object so a discriminated union can read `kind`; the
|
|
16926
|
+
* author-time validation is the SAME function `ExpressionSourceSchema` runs.
|
|
16585
16927
|
*/
|
|
16586
|
-
var
|
|
16928
|
+
var ExpressionLeafSchema = object({
|
|
16587
16929
|
kind: literal("expression"),
|
|
16588
16930
|
expr: string().min(1).max(MAX_EXPRESSION_SOURCE_LENGTH),
|
|
16589
16931
|
bindings: record(string().regex(EXPRESSION_IDENTIFIER_RE), ExpressionBindingSourceSchema)
|
|
@@ -16595,55 +16937,62 @@ var AutomationConditionExpressionSchema = object({
|
|
|
16595
16937
|
path: ["expr"]
|
|
16596
16938
|
});
|
|
16597
16939
|
});
|
|
16598
|
-
var
|
|
16940
|
+
var AutomationWhenSchema = lazy(() => discriminatedUnion("kind", [
|
|
16599
16941
|
object({
|
|
16600
16942
|
kind: literal("all"),
|
|
16601
|
-
children: array(
|
|
16943
|
+
children: array(AutomationWhenSchema)
|
|
16602
16944
|
}),
|
|
16603
16945
|
object({
|
|
16604
16946
|
kind: literal("any"),
|
|
16605
|
-
children: array(
|
|
16947
|
+
children: array(AutomationWhenSchema)
|
|
16606
16948
|
}),
|
|
16607
16949
|
object({
|
|
16608
16950
|
kind: literal("not"),
|
|
16609
|
-
child:
|
|
16951
|
+
child: AutomationWhenSchema
|
|
16610
16952
|
}),
|
|
16611
|
-
|
|
16612
|
-
|
|
16613
|
-
|
|
16953
|
+
DeviceBecomesLeafSchema,
|
|
16954
|
+
DeviceEventLeafSchema,
|
|
16955
|
+
ScheduleLeafSchema,
|
|
16956
|
+
ManualLeafSchema,
|
|
16957
|
+
IntervalLeafSchema,
|
|
16958
|
+
DetectionLeafSchema,
|
|
16959
|
+
StationaryLeafSchema,
|
|
16960
|
+
DeviceIsLeafSchema,
|
|
16961
|
+
StationaryPresentLeafSchema,
|
|
16962
|
+
ExpressionLeafSchema
|
|
16614
16963
|
]));
|
|
16615
16964
|
/**
|
|
16616
|
-
*
|
|
16617
|
-
*
|
|
16618
|
-
*
|
|
16619
|
-
*
|
|
16620
|
-
*
|
|
16621
|
-
*
|
|
16622
|
-
*
|
|
16623
|
-
*
|
|
16624
|
-
|
|
16625
|
-
|
|
16965
|
+
* The recipe shape before the single `when` tree: a flat list of triggers
|
|
16966
|
+
* (OR'd, each an instant) and an optional condition tree. Stored recipes —
|
|
16967
|
+
* the `AUTOMATION_RECIPE` literal of every block saved so far, and whatever an
|
|
16968
|
+
* older client still posts — are in this shape, so it stays READABLE here, and
|
|
16969
|
+
* {@link convertLegacyRecipe} turns it into the new one, losslessly and one
|
|
16970
|
+
* way. Nothing writes this shape any more.
|
|
16971
|
+
*
|
|
16972
|
+
* `{ triggers: [t1…tn], conditions?: C }`
|
|
16973
|
+
* → `when = all(any(t1…tn), C)`, or `any(t1…tn)` without `C`;
|
|
16974
|
+
* a single trigger is not wrapped in `any`.
|
|
16975
|
+
* a `condition` leaf → a `device-is` leaf (the "is" verb);
|
|
16976
|
+
* a `device-state` trigger → a `device-becomes` leaf (the "becomes" verb);
|
|
16977
|
+
* `classes: ['dog']` → the macro `animal` with the label `dog`.
|
|
16978
|
+
*/
|
|
16979
|
+
var LegacyTriggerSchema = discriminatedUnion("kind", [
|
|
16626
16980
|
object({
|
|
16627
16981
|
kind: literal("device-state"),
|
|
16628
16982
|
deviceId: number().int().nonnegative(),
|
|
16629
16983
|
cap: string().min(1),
|
|
16630
16984
|
fieldPath: string().min(1),
|
|
16631
|
-
/** Fire when the field takes this value. Omit to fire on any change. */
|
|
16632
16985
|
becomes: union([
|
|
16633
16986
|
string(),
|
|
16634
16987
|
number(),
|
|
16635
16988
|
boolean()
|
|
16636
16989
|
]).optional(),
|
|
16637
|
-
/** Only on a CHANGE of value, not on every re-report. */
|
|
16638
16990
|
edge: boolean().optional(),
|
|
16639
|
-
/** The condition must hold this long before the run starts. */
|
|
16640
16991
|
forMs: number().int().min(0).max(864e5).optional(),
|
|
16641
|
-
/** Collapse a burst into one run. */
|
|
16642
16992
|
debounceMs: number().int().min(0).max(6e5).optional()
|
|
16643
16993
|
}),
|
|
16644
16994
|
object({
|
|
16645
16995
|
kind: literal("device-event"),
|
|
16646
|
-
/** An `EventCategory` value. */
|
|
16647
16996
|
category: string().min(1),
|
|
16648
16997
|
deviceId: number().int().nonnegative().optional()
|
|
16649
16998
|
}),
|
|
@@ -16654,25 +17003,16 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
|
|
|
16654
17003
|
object({ kind: literal("manual") }),
|
|
16655
17004
|
object({
|
|
16656
17005
|
kind: literal("detection"),
|
|
16657
|
-
/** Class names. A macro class (`animal`, `vehicle`, `person`, `package`)
|
|
16658
|
-
* matches every raw class that maps to it; any other name matches
|
|
16659
|
-
* exactly. */
|
|
16660
17006
|
classes: array(string().min(1)).min(1),
|
|
16661
17007
|
deviceIds: array(number().int().nonnegative()).min(1),
|
|
16662
|
-
/** The track must have visited at least one of these zones. */
|
|
16663
17008
|
zones: array(string().min(1)).min(1).optional(),
|
|
16664
|
-
/** Best-so-far confidence the track must reach. */
|
|
16665
17009
|
minScore: number().min(0).max(1).optional()
|
|
16666
17010
|
}),
|
|
16667
17011
|
object({
|
|
16668
17012
|
kind: literal("stationary"),
|
|
16669
17013
|
deviceIds: array(number().int().nonnegative()).min(1),
|
|
16670
|
-
/** Class names; a macro class matches like the detection trigger. */
|
|
16671
17014
|
classes: array(string().min(1)).min(1).optional(),
|
|
16672
|
-
/** The parked object must be in at least one of these zones. */
|
|
16673
17015
|
zones: array(string().min(1)).min(1).optional(),
|
|
16674
|
-
/** `appeared`: it became parked. `departed`: the registry retired it.
|
|
16675
|
-
* The vocabulary of `PipelineAnalyticsStationaryChanged`. */
|
|
16676
17016
|
phase: _enum(["appeared", "departed"])
|
|
16677
17017
|
}),
|
|
16678
17018
|
object({
|
|
@@ -16680,6 +17020,143 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
|
|
|
16680
17020
|
minutes: number().int().min(1).max(1440)
|
|
16681
17021
|
})
|
|
16682
17022
|
]);
|
|
17023
|
+
var LegacyConditionLeafSchema = object({
|
|
17024
|
+
kind: literal("condition"),
|
|
17025
|
+
deviceId: number().int().nonnegative(),
|
|
17026
|
+
cap: string().min(1),
|
|
17027
|
+
fieldPath: string().min(1),
|
|
17028
|
+
operator: AutomationConditionOperatorSchema,
|
|
17029
|
+
value: union([
|
|
17030
|
+
string(),
|
|
17031
|
+
number(),
|
|
17032
|
+
boolean(),
|
|
17033
|
+
array(union([string(), number()])),
|
|
17034
|
+
QuantityValueSchema
|
|
17035
|
+
]),
|
|
17036
|
+
unit: string().min(1).optional()
|
|
17037
|
+
});
|
|
17038
|
+
var LegacyStationaryPresentSchema = object({
|
|
17039
|
+
kind: literal("stationary-present"),
|
|
17040
|
+
deviceId: number().int().nonnegative(),
|
|
17041
|
+
classes: array(string().min(1)).min(1).optional(),
|
|
17042
|
+
zones: array(string().min(1)).min(1).optional()
|
|
17043
|
+
});
|
|
17044
|
+
var LegacyExpressionSchema = object({
|
|
17045
|
+
kind: literal("expression"),
|
|
17046
|
+
expr: string().min(1).max(MAX_EXPRESSION_SOURCE_LENGTH),
|
|
17047
|
+
bindings: record(string().regex(EXPRESSION_IDENTIFIER_RE), ExpressionBindingSourceSchema)
|
|
17048
|
+
}).superRefine((src, ctx) => {
|
|
17049
|
+
const err = validateExpressionSource(src);
|
|
17050
|
+
if (err !== null) ctx.addIssue({
|
|
17051
|
+
code: "custom",
|
|
17052
|
+
message: err,
|
|
17053
|
+
path: ["expr"]
|
|
17054
|
+
});
|
|
17055
|
+
});
|
|
17056
|
+
var LegacyConditionSchema = lazy(() => discriminatedUnion("kind", [
|
|
17057
|
+
object({
|
|
17058
|
+
kind: literal("all"),
|
|
17059
|
+
children: array(LegacyConditionSchema)
|
|
17060
|
+
}),
|
|
17061
|
+
object({
|
|
17062
|
+
kind: literal("any"),
|
|
17063
|
+
children: array(LegacyConditionSchema)
|
|
17064
|
+
}),
|
|
17065
|
+
object({
|
|
17066
|
+
kind: literal("not"),
|
|
17067
|
+
child: LegacyConditionSchema
|
|
17068
|
+
}),
|
|
17069
|
+
LegacyConditionLeafSchema,
|
|
17070
|
+
LegacyExpressionSchema,
|
|
17071
|
+
LegacyStationaryPresentSchema
|
|
17072
|
+
]));
|
|
17073
|
+
function convertTrigger(trigger) {
|
|
17074
|
+
switch (trigger.kind) {
|
|
17075
|
+
case "device-state": return {
|
|
17076
|
+
...trigger,
|
|
17077
|
+
kind: "device-becomes"
|
|
17078
|
+
};
|
|
17079
|
+
case "detection": {
|
|
17080
|
+
const { classes, ...rest } = trigger;
|
|
17081
|
+
return {
|
|
17082
|
+
...rest,
|
|
17083
|
+
subjects: classes.map(subjectFromClassName)
|
|
17084
|
+
};
|
|
17085
|
+
}
|
|
17086
|
+
case "stationary": {
|
|
17087
|
+
const { classes, ...rest } = trigger;
|
|
17088
|
+
return {
|
|
17089
|
+
...rest,
|
|
17090
|
+
...classes === void 0 ? {} : { subjects: classes.map(subjectFromClassName) }
|
|
17091
|
+
};
|
|
17092
|
+
}
|
|
17093
|
+
default: return trigger;
|
|
17094
|
+
}
|
|
17095
|
+
}
|
|
17096
|
+
/** An old condition tree as a `when` subtree: `condition` → "is", classes → subjects. */
|
|
17097
|
+
function convertLegacyCondition(condition) {
|
|
17098
|
+
switch (condition.kind) {
|
|
17099
|
+
case "condition": return {
|
|
17100
|
+
...condition,
|
|
17101
|
+
kind: "device-is"
|
|
17102
|
+
};
|
|
17103
|
+
case "stationary-present": {
|
|
17104
|
+
const { classes, ...rest } = condition;
|
|
17105
|
+
return {
|
|
17106
|
+
...rest,
|
|
17107
|
+
...classes === void 0 ? {} : { subjects: classes.map(subjectFromClassName) }
|
|
17108
|
+
};
|
|
17109
|
+
}
|
|
17110
|
+
case "expression": return condition;
|
|
17111
|
+
case "not": return {
|
|
17112
|
+
kind: "not",
|
|
17113
|
+
child: convertLegacyCondition(condition.child)
|
|
17114
|
+
};
|
|
17115
|
+
case "all":
|
|
17116
|
+
case "any": return {
|
|
17117
|
+
kind: condition.kind,
|
|
17118
|
+
children: condition.children.map(convertLegacyCondition)
|
|
17119
|
+
};
|
|
17120
|
+
}
|
|
17121
|
+
}
|
|
17122
|
+
/** `all(any(t1…tn), C)`, or `any(t1…tn)`; a single trigger is not wrapped in `any`. */
|
|
17123
|
+
function convertLegacyWhen(half) {
|
|
17124
|
+
const leaves = half.triggers.map(convertTrigger);
|
|
17125
|
+
const first = leaves[0];
|
|
17126
|
+
const triggers = leaves.length === 1 && first !== void 0 ? first : {
|
|
17127
|
+
kind: "any",
|
|
17128
|
+
children: leaves
|
|
17129
|
+
};
|
|
17130
|
+
if (half.conditions === void 0) return triggers;
|
|
17131
|
+
return {
|
|
17132
|
+
kind: "all",
|
|
17133
|
+
children: [triggers, convertLegacyCondition(half.conditions)]
|
|
17134
|
+
};
|
|
17135
|
+
}
|
|
17136
|
+
/**
|
|
17137
|
+
* The automation RECIPE — what a visually-authored automation is, before it
|
|
17138
|
+
* becomes code.
|
|
17139
|
+
*
|
|
17140
|
+
* This is the data half of `docs/design/2026-08-08-automation-scripts-custom-devices.md`
|
|
17141
|
+
* Phase 6. A recipe is not an engine and never evaluates anything: it is a
|
|
17142
|
+
* declarative description that {@link generateAutomationBlock} compiles to the
|
|
17143
|
+
* TypeScript of an ordinary core block. There is therefore no second rule
|
|
17144
|
+
* engine, no second store and no second runtime — the block runner is the
|
|
17145
|
+
* runtime, `core_blocks` is the store (§2.1).
|
|
17146
|
+
*
|
|
17147
|
+
* Two parts:
|
|
17148
|
+
*
|
|
17149
|
+
* - **`when`** — ONE tree (`./when.ts`) of all/any/not over event leaves
|
|
17150
|
+
* (instants) and state leaves (durations). It replaces the flat trigger list
|
|
17151
|
+
* plus the separate condition tree; the firing rule is in that file and in
|
|
17152
|
+
* ADR "one tree".
|
|
17153
|
+
* - **Actions** — `NcRuleActionSchema`'s two steps, plus the `{kind:'code'}`
|
|
17154
|
+
* fragment of §3.2.2. See {@link AutomationActionSchema} for the ONE place
|
|
17155
|
+
* this had to diverge from "verbatim", and why.
|
|
17156
|
+
*
|
|
17157
|
+
* A recipe stored in the old shape (`triggers` + `conditions`) still LOADS:
|
|
17158
|
+
* {@link AutomationRecipeSchema} (the parse-anywhere schema) converts it on parse. Nothing writes it.
|
|
17159
|
+
*/
|
|
16683
17160
|
/**
|
|
16684
17161
|
* One action step.
|
|
16685
17162
|
*
|
|
@@ -16782,69 +17259,49 @@ var AutomationRunModeSchema = discriminatedUnion("mode", [
|
|
|
16782
17259
|
minutes: number().int().min(1).max(1440)
|
|
16783
17260
|
})
|
|
16784
17261
|
]);
|
|
16785
|
-
var
|
|
16786
|
-
|
|
16787
|
-
conditions: AutomationConditionSchema.optional(),
|
|
17262
|
+
var CurrentAutomationRecipeSchema = object({
|
|
17263
|
+
when: AutomationWhenSchema,
|
|
16788
17264
|
actions: array(AutomationActionSchema),
|
|
16789
17265
|
runMode: AutomationRunModeSchema.optional(),
|
|
16790
17266
|
/** How many run traces the automation keeps (default {@link DEFAULT_TRACE_KEEP}). */
|
|
16791
17267
|
keepTraces: number().int().min(1).max(100).optional()
|
|
16792
17268
|
});
|
|
16793
|
-
|
|
16794
|
-
|
|
16795
|
-
|
|
16796
|
-
|
|
16797
|
-
|
|
16798
|
-
|
|
16799
|
-
|
|
16800
|
-
|
|
16801
|
-
|
|
16802
|
-
|
|
16803
|
-
|
|
16804
|
-
|
|
16805
|
-
|
|
16806
|
-
|
|
16807
|
-
|
|
16808
|
-
|
|
16809
|
-
|
|
16810
|
-
|
|
16811
|
-
|
|
16812
|
-
|
|
16813
|
-
|
|
16814
|
-
|
|
16815
|
-
|
|
16816
|
-
|
|
16817
|
-
|
|
16818
|
-
bus: "vehicle",
|
|
16819
|
-
train: "vehicle",
|
|
16820
|
-
truck: "vehicle",
|
|
16821
|
-
boat: "vehicle",
|
|
16822
|
-
bird: "animal",
|
|
16823
|
-
cat: "animal",
|
|
16824
|
-
dog: "animal",
|
|
16825
|
-
horse: "animal",
|
|
16826
|
-
sheep: "animal",
|
|
16827
|
-
cow: "animal",
|
|
16828
|
-
elephant: "animal",
|
|
16829
|
-
bear: "animal",
|
|
16830
|
-
zebra: "animal",
|
|
16831
|
-
giraffe: "animal",
|
|
16832
|
-
suitcase: "package",
|
|
16833
|
-
backpack: "package",
|
|
16834
|
-
handbag: "package"
|
|
16835
|
-
},
|
|
16836
|
-
preserveOriginal: false
|
|
16837
|
-
};
|
|
17269
|
+
/** A recipe as it was stored before the single tree. Read-only: nothing writes this. */
|
|
17270
|
+
var LegacyAutomationRecipeSchema = object({
|
|
17271
|
+
triggers: array(LegacyTriggerSchema),
|
|
17272
|
+
conditions: LegacyConditionSchema.optional(),
|
|
17273
|
+
actions: array(AutomationActionSchema),
|
|
17274
|
+
runMode: AutomationRunModeSchema.optional(),
|
|
17275
|
+
keepTraces: number().int().min(1).max(100).optional()
|
|
17276
|
+
});
|
|
17277
|
+
/** The old shape as the new one, lossless. */
|
|
17278
|
+
function convertLegacyRecipe(legacy) {
|
|
17279
|
+
const { triggers, conditions, ...rest } = legacy;
|
|
17280
|
+
return {
|
|
17281
|
+
when: convertLegacyWhen({
|
|
17282
|
+
triggers,
|
|
17283
|
+
conditions
|
|
17284
|
+
}),
|
|
17285
|
+
...rest
|
|
17286
|
+
};
|
|
17287
|
+
}
|
|
17288
|
+
/**
|
|
17289
|
+
* Where recipes are PARSED (the core-blocks cap, the MCP tool, the literal a
|
|
17290
|
+
* saved block carries): the new shape as it is, or the old shape converted to
|
|
17291
|
+
* it. The output type is always the new one.
|
|
17292
|
+
*/
|
|
17293
|
+
var AutomationRecipeSchema = union([CurrentAutomationRecipeSchema, LegacyAutomationRecipeSchema.transform(convertLegacyRecipe)]);
|
|
16838
17294
|
new Set(Object.values(COCO_TO_MACRO.mapping));
|
|
16839
17295
|
/**
|
|
16840
17296
|
* Run traces — what an automation (or, later, an Action run) DID, node by node,
|
|
16841
17297
|
* so the editor can paint every block green or red with its reason.
|
|
16842
17298
|
*
|
|
16843
|
-
* A trace is data, not a log: it covers the WHOLE tree — the
|
|
16844
|
-
* every
|
|
16845
|
-
* short-circuit never reached), the run-mode verdict and every action
|
|
16846
|
-
* each addressed by a stable path (`
|
|
16847
|
-
* for an Action run).
|
|
17299
|
+
* A trace is data, not a log: it covers the WHOLE tree — the event leaf that
|
|
17300
|
+
* fired, every node of the `when` tree (containers and leaves, including the
|
|
17301
|
+
* ones a short-circuit never reached), the run-mode verdict and every action
|
|
17302
|
+
* step — each addressed by a stable path (`when.0.1`, `actions.2`, or `steps.N`
|
|
17303
|
+
* for an Action run). Traces written before the single tree carry
|
|
17304
|
+
* `conditions.*` paths and still parse. Skipped
|
|
16848
17305
|
* evaluations are traced too, and identical consecutive skips collapse into a
|
|
16849
17306
|
* repeat count so a trigger that fails every 5 minutes cannot flush real runs.
|
|
16850
17307
|
*
|
|
@@ -16876,6 +17333,8 @@ var TraceNodeOutcomeSchema = _enum([
|
|
|
16876
17333
|
]);
|
|
16877
17334
|
var AutomationTriggerTraceSchema = object({
|
|
16878
17335
|
kind: string().min(1),
|
|
17336
|
+
/** The event leaf that fired (`when`, `when.0.1`); absent on traces written before the single tree. */
|
|
17337
|
+
path: string().min(1).optional(),
|
|
16879
17338
|
outcome: _enum(["pass", "fail"]),
|
|
16880
17339
|
/** Why an event was rejected ("class person not in [dog]") or what fired. */
|
|
16881
17340
|
reason: string(),
|
|
@@ -16883,15 +17342,23 @@ var AutomationTriggerTraceSchema = object({
|
|
|
16883
17342
|
vars: record(string(), TraceValueSchema)
|
|
16884
17343
|
});
|
|
16885
17344
|
var AutomationConditionTraceNodeSchema = object({
|
|
16886
|
-
/** `
|
|
17345
|
+
/** `when` for the root, `when.<i>` for its i-th child, … (`conditions…` on an old trace). */
|
|
16887
17346
|
path: string().min(1),
|
|
16888
17347
|
kind: _enum([
|
|
16889
17348
|
"all",
|
|
16890
17349
|
"any",
|
|
16891
17350
|
"not",
|
|
16892
17351
|
"condition",
|
|
17352
|
+
"device-is",
|
|
16893
17353
|
"expression",
|
|
16894
|
-
"stationary-present"
|
|
17354
|
+
"stationary-present",
|
|
17355
|
+
"device-becomes",
|
|
17356
|
+
"device-event",
|
|
17357
|
+
"schedule",
|
|
17358
|
+
"interval",
|
|
17359
|
+
"manual",
|
|
17360
|
+
"detection",
|
|
17361
|
+
"stationary"
|
|
16895
17362
|
]),
|
|
16896
17363
|
outcome: TraceNodeOutcomeSchema,
|
|
16897
17364
|
reason: string(),
|
|
@@ -16925,6 +17392,12 @@ var AutomationStepTraceSchema = object({
|
|
|
16925
17392
|
deviceId: number().int().optional(),
|
|
16926
17393
|
startedAt: number().optional(),
|
|
16927
17394
|
endedAt: number().optional(),
|
|
17395
|
+
/**
|
|
17396
|
+
* The parameters the step SENT, after `{{vars.x}}` substitution — what the
|
|
17397
|
+
* device was actually asked. Bounded like trigger `vars`; a secret parameter
|
|
17398
|
+
* is shown as {@link REDACTED_ARG}. Absent on traces written before it existed.
|
|
17399
|
+
*/
|
|
17400
|
+
args: record(string(), TraceValueSchema).optional(),
|
|
16928
17401
|
outcome: _enum([
|
|
16929
17402
|
"ok",
|
|
16930
17403
|
"error",
|
|
@@ -17674,6 +18147,23 @@ method(object({
|
|
|
17674
18147
|
addonId: string(),
|
|
17675
18148
|
nodeId: string().optional()
|
|
17676
18149
|
}), SettingsSchemaWithValuesSchema.nullable()), method(object({
|
|
18150
|
+
addonId: string(),
|
|
18151
|
+
nodeId: string().optional()
|
|
18152
|
+
}), object({ keys: array(string()) }).nullable(), { auth: "admin" }), method(object({
|
|
18153
|
+
addonId: string(),
|
|
18154
|
+
nodeId: string().optional(),
|
|
18155
|
+
keys: array(string())
|
|
18156
|
+
}), object({
|
|
18157
|
+
removed: array(string()),
|
|
18158
|
+
refused: array(object({
|
|
18159
|
+
key: string(),
|
|
18160
|
+
reason: string()
|
|
18161
|
+
})),
|
|
18162
|
+
secretsCleared: number()
|
|
18163
|
+
}), {
|
|
18164
|
+
kind: "mutation",
|
|
18165
|
+
auth: "admin"
|
|
18166
|
+
}), method(object({
|
|
17677
18167
|
addonId: string(),
|
|
17678
18168
|
deviceId: number(),
|
|
17679
18169
|
nodeId: string().optional()
|
|
@@ -19109,8 +19599,21 @@ var BrokerProviderInfoSchema = object({
|
|
|
19109
19599
|
* (`{ sessionId }`) drops the session. The dialog that edits a broker
|
|
19110
19600
|
* offers it as the default path; the fields the session fills are
|
|
19111
19601
|
* marked `filledBySignIn` in the kind's settings schema.
|
|
19602
|
+
*
|
|
19603
|
+
* `methods` lists the ways the session can sign in, recommended first:
|
|
19604
|
+
* `'device-code'` adds `POST {basePath}/code/start` (body: the form's
|
|
19605
|
+
* settings) answering `{ sessionId, userCode, verificationUrl,
|
|
19606
|
+
* expiresAt }` — the operator types `userCode` at `verificationUrl` on
|
|
19607
|
+
* any device, and the same `status` / `commit` / `cancel` routes follow
|
|
19608
|
+
* (status then carries `method`, `userCode`, `verificationUrl`,
|
|
19609
|
+
* `expiresAt` and the phases `'awaiting-code'` / `'expired'`);
|
|
19610
|
+
* `'proxy'` is the `{basePath}/start` flow above. Absent means
|
|
19611
|
+
* `['proxy']`.
|
|
19112
19612
|
*/
|
|
19113
|
-
signIn: object({
|
|
19613
|
+
signIn: object({
|
|
19614
|
+
basePath: string().min(1),
|
|
19615
|
+
methods: array(_enum(["device-code", "proxy"])).optional()
|
|
19616
|
+
}).optional()
|
|
19114
19617
|
}))
|
|
19115
19618
|
});
|
|
19116
19619
|
var ListInputSchema = object({
|
|
@@ -21612,6 +22115,8 @@ var StorageKindSchema = object({
|
|
|
21612
22115
|
"orphaned",
|
|
21613
22116
|
"dropped"
|
|
21614
22117
|
]),
|
|
22118
|
+
/** An owner declared this kind since the engine started. */
|
|
22119
|
+
declaredThisBoot: boolean(),
|
|
21615
22120
|
/** `null` when the caller asked for no counts (`counts: false`). */
|
|
21616
22121
|
rows: number().int().nonnegative().nullable(),
|
|
21617
22122
|
/** Rows x the average size of a sample; null for an empty kind. */
|
|
@@ -21623,6 +22128,51 @@ var StorageKindSchema = object({
|
|
|
21623
22128
|
/** The whole declaration, secret paths and entry descriptor included (the entries service builds its handle from it). */
|
|
21624
22129
|
declaration: RecordKindDeclarationSchema
|
|
21625
22130
|
});
|
|
22131
|
+
_enum([
|
|
22132
|
+
"orphaned",
|
|
22133
|
+
"stale",
|
|
22134
|
+
"unknown"
|
|
22135
|
+
]);
|
|
22136
|
+
/** A record kind nobody declared since its owner started, or whose owner is gone. */
|
|
22137
|
+
var StaleKindFindingSchema = object({
|
|
22138
|
+
owner: string(),
|
|
22139
|
+
kind: string(),
|
|
22140
|
+
/** `unknown`: the owner could not be judged (not running, or its state unreadable) - never dropped. */
|
|
22141
|
+
verdict: _enum(["orphaned", "unknown"]),
|
|
22142
|
+
reason: string(),
|
|
22143
|
+
rows: number().int().nonnegative().nullable(),
|
|
22144
|
+
/** local-auth's tables are never dropped by this audit. */
|
|
22145
|
+
protected: boolean()
|
|
22146
|
+
});
|
|
22147
|
+
/** A stored global-settings key the running addon no longer declares. */
|
|
22148
|
+
var StaleSettingsKeyFindingSchema = object({
|
|
22149
|
+
addonId: string(),
|
|
22150
|
+
/** The stored key as written, a per-node `<key>@<nodeId>` suffix included. */
|
|
22151
|
+
key: string(),
|
|
22152
|
+
/**
|
|
22153
|
+
* Keychain refs whose ONLY marker sits under this key: in use by stale data,
|
|
22154
|
+
* not orphans yet. Removing the key releases them.
|
|
22155
|
+
*/
|
|
22156
|
+
secretRefs: array(string())
|
|
22157
|
+
});
|
|
22158
|
+
/** An addon the audit could not judge, with the reason. Never reported as stale. */
|
|
22159
|
+
var StaleUnknownSchema = object({
|
|
22160
|
+
subject: string(),
|
|
22161
|
+
reason: string()
|
|
22162
|
+
});
|
|
22163
|
+
var StaleDataReportSchema = object({
|
|
22164
|
+
/** Epoch ms of the last finished scan; `null` before the first. */
|
|
22165
|
+
scannedAt: number().nullable(),
|
|
22166
|
+
scanning: boolean(),
|
|
22167
|
+
trigger: _enum([
|
|
22168
|
+
"boot",
|
|
22169
|
+
"interval",
|
|
22170
|
+
"manual"
|
|
22171
|
+
]).nullable(),
|
|
22172
|
+
kinds: array(StaleKindFindingSchema),
|
|
22173
|
+
settingsKeys: array(StaleSettingsKeyFindingSchema),
|
|
22174
|
+
unknown: array(StaleUnknownSchema)
|
|
22175
|
+
});
|
|
21626
22176
|
method(object({ ownerPrefix: string().min(1).optional() }), object({ tables: array(StorageTableSchema) }), { auth: "admin" }), method(object({
|
|
21627
22177
|
owner: string().min(1).optional(),
|
|
21628
22178
|
/** `false` skips the per-kind row count (a catalog read only). Default true. */
|
|
@@ -21630,7 +22180,30 @@ method(object({ ownerPrefix: string().min(1).optional() }), object({ tables: arr
|
|
|
21630
22180
|
}), object({ kinds: array(StorageKindSchema) }), { auth: "admin" }), method(RecordQueryInputSchema.extend({
|
|
21631
22181
|
owner: string().min(1),
|
|
21632
22182
|
kind: RecordKindNameSchema
|
|
21633
|
-
}), RecordQueryResultSchema, { auth: "admin" })
|
|
22183
|
+
}), RecordQueryResultSchema, { auth: "admin" }), method(object({}), StaleDataReportSchema, { auth: "admin" }), method(object({}), StaleDataReportSchema, {
|
|
22184
|
+
kind: "mutation",
|
|
22185
|
+
auth: "admin"
|
|
22186
|
+
}), method(object({
|
|
22187
|
+
owner: string().min(1),
|
|
22188
|
+
kind: RecordKindNameSchema
|
|
22189
|
+
}), object({
|
|
22190
|
+
deleted: number().int().nonnegative(),
|
|
22191
|
+
secretsReleased: number().int().nonnegative()
|
|
22192
|
+
}), {
|
|
22193
|
+
kind: "mutation",
|
|
22194
|
+
auth: "admin",
|
|
22195
|
+
caller: "required"
|
|
22196
|
+
}), method(object({
|
|
22197
|
+
addonId: string().min(1),
|
|
22198
|
+
key: string().min(1)
|
|
22199
|
+
}), object({
|
|
22200
|
+
removed: boolean(),
|
|
22201
|
+
secretsCleared: number().int().nonnegative()
|
|
22202
|
+
}), {
|
|
22203
|
+
kind: "mutation",
|
|
22204
|
+
auth: "admin",
|
|
22205
|
+
caller: "required"
|
|
22206
|
+
});
|
|
21634
22207
|
/**
|
|
21635
22208
|
* record-entries - the ONE admin surface for every list of user-created entries
|
|
21636
22209
|
* (D881): notification targets, brokers, LLM profiles, anything an addon
|
|
@@ -21769,6 +22342,12 @@ method(object({
|
|
|
21769
22342
|
}), method(object({ deviceId: number().int() }), object({ refs: array(string()).readonly() }), {
|
|
21770
22343
|
kind: "mutation",
|
|
21771
22344
|
auth: "admin"
|
|
22345
|
+
}), method(object({
|
|
22346
|
+
refs: RefListSchema,
|
|
22347
|
+
onBehalfOf: string().min(1)
|
|
22348
|
+
}), object({ released: number().int() }), {
|
|
22349
|
+
kind: "mutation",
|
|
22350
|
+
auth: "admin"
|
|
21772
22351
|
}), method(object({}), array(KeychainSecretMetaSchema).readonly(), { auth: "admin" }), method(object({}), KeychainStateSchema, { auth: "admin" });
|
|
21773
22352
|
/**
|
|
21774
22353
|
* device-history — the ONE mechanism for numeric history (D850).
|
|
@@ -22558,7 +23137,9 @@ var DiscoveredChildDeviceSchema = lazy(() => object({
|
|
|
22558
23137
|
/** Upstream-system identity + rendering hints for the candidate. */
|
|
22559
23138
|
sourceInfo: SourceInfoSchema.optional(),
|
|
22560
23139
|
/** Nested entity-children for accordion preview — integrations populate, Hubs omit. */
|
|
22561
|
-
children: array(DiscoveredChildDeviceSchema).readonly().optional()
|
|
23140
|
+
children: array(DiscoveredChildDeviceSchema).readonly().optional(),
|
|
23141
|
+
/** `childNativeId` of the gateway candidate in the same list this device sits behind. */
|
|
23142
|
+
parentStableId: string().optional()
|
|
22562
23143
|
}));
|
|
22563
23144
|
/**
|
|
22564
23145
|
* Status block — populated by the integration after each
|
|
@@ -42873,6 +43454,79 @@ object({
|
|
|
42873
43454
|
metadata: record(string(), unknown()).optional()
|
|
42874
43455
|
});
|
|
42875
43456
|
/**
|
|
43457
|
+
* HOW a provider can be found on the network. The provider only DECLARES the
|
|
43458
|
+
* mode; the `local-network` scan engine runs it once for every provider that
|
|
43459
|
+
* asked (one multicast browse, one host sweep) and hands the raw hits back to
|
|
43460
|
+
* `classifyDiscoveryHits`. A provider never opens a discovery socket itself,
|
|
43461
|
+
* except through a `custom` mode.
|
|
43462
|
+
*/
|
|
43463
|
+
var DiscoveryModeSchema = discriminatedUnion("kind", [
|
|
43464
|
+
object({
|
|
43465
|
+
kind: literal("mdns"),
|
|
43466
|
+
/** DNS-SD service type, e.g. `_esphomelib._tcp`. */
|
|
43467
|
+
serviceType: string().min(1),
|
|
43468
|
+
/** Regular expression an instance name must match (case-insensitive). */
|
|
43469
|
+
instancePattern: string().optional()
|
|
43470
|
+
}),
|
|
43471
|
+
object({
|
|
43472
|
+
kind: literal("ws-discovery"),
|
|
43473
|
+
/** Probe types (e.g. `dn:NetworkVideoTransmitter`); omitted = any device. */
|
|
43474
|
+
types: array(string()).optional()
|
|
43475
|
+
}),
|
|
43476
|
+
object({
|
|
43477
|
+
kind: literal("ssdp"),
|
|
43478
|
+
searchTarget: string().min(1)
|
|
43479
|
+
}),
|
|
43480
|
+
object({
|
|
43481
|
+
kind: literal("udp-broadcast"),
|
|
43482
|
+
port: number().int().min(1).max(65535),
|
|
43483
|
+
payloadHex: string().regex(/^([0-9a-fA-F]{2})*$/),
|
|
43484
|
+
/** Port replies arrive on, when the device answers somewhere other than the source port. */
|
|
43485
|
+
listenPort: number().int().min(1).max(65535).optional()
|
|
43486
|
+
}),
|
|
43487
|
+
object({
|
|
43488
|
+
kind: literal("http-probe"),
|
|
43489
|
+
port: number().int().min(1).max(65535),
|
|
43490
|
+
path: string().startsWith("/"),
|
|
43491
|
+
method: _enum(["GET", "POST"]).default("GET"),
|
|
43492
|
+
bodyText: string().optional(),
|
|
43493
|
+
contentType: string().optional(),
|
|
43494
|
+
tls: boolean().default(false),
|
|
43495
|
+
/** Status codes that count as a hit. Omitted = any HTTP answer. */
|
|
43496
|
+
acceptStatus: array(number().int()).optional()
|
|
43497
|
+
}),
|
|
43498
|
+
object({
|
|
43499
|
+
kind: literal("custom"),
|
|
43500
|
+
/** Why the engine cannot do it (an allowlisted exception, e.g. a vendor UDP protocol). */
|
|
43501
|
+
reason: string().min(1)
|
|
43502
|
+
})
|
|
43503
|
+
]);
|
|
43504
|
+
/** What the engine saw on the wire for ONE declared mode; the provider classifies it. */
|
|
43505
|
+
var DiscoveryHitSchema = object({
|
|
43506
|
+
/** Index into the provider's own `getDiscoveryModes()` array. */
|
|
43507
|
+
modeIndex: number().int().min(0),
|
|
43508
|
+
host: string(),
|
|
43509
|
+
port: number().int(),
|
|
43510
|
+
http: object({
|
|
43511
|
+
status: number().int(),
|
|
43512
|
+
headers: record(string(), string()),
|
|
43513
|
+
/** At most 16 KiB. */
|
|
43514
|
+
bodyText: string()
|
|
43515
|
+
}).optional(),
|
|
43516
|
+
mdns: object({
|
|
43517
|
+
instance: string(),
|
|
43518
|
+
host: string(),
|
|
43519
|
+
port: number().int(),
|
|
43520
|
+
txt: record(string(), string())
|
|
43521
|
+
}).optional(),
|
|
43522
|
+
udp: object({ payloadHex: string() }).optional(),
|
|
43523
|
+
wsd: object({
|
|
43524
|
+
xaddrs: array(string()),
|
|
43525
|
+
types: array(string()),
|
|
43526
|
+
scopes: array(string())
|
|
43527
|
+
}).optional()
|
|
43528
|
+
});
|
|
43529
|
+
/**
|
|
42876
43530
|
* Candidate handed back from discovery and accepted by
|
|
42877
43531
|
* `adoptDiscoveredDevice`. Shape mirrors the in-process
|
|
42878
43532
|
* `DiscoveredDevice` interface used by `DeviceDiscovery`.
|
|
@@ -42907,7 +43561,17 @@ var DiscoveryCandidateSchema = object({
|
|
|
42907
43561
|
/** Numeric id of the device this candidate was matched to. Set with `alreadyOnboarded`. */
|
|
42908
43562
|
onboardedDeviceId: number().optional(),
|
|
42909
43563
|
/** Operator-facing name of the matched device, so the UI can say WHICH one it is. */
|
|
42910
|
-
onboardedName: string().optional()
|
|
43564
|
+
onboardedName: string().optional(),
|
|
43565
|
+
/**
|
|
43566
|
+
* This candidate is an INTEGRATION to set up (a gateway, a hub, a controller),
|
|
43567
|
+
* not a device to adopt. Replaces the `prefilledConfig.setupIntegration` marker,
|
|
43568
|
+
* which is still read for one release.
|
|
43569
|
+
*/
|
|
43570
|
+
setupIntegration: boolean().optional(),
|
|
43571
|
+
/** `stableId` of the gateway candidate this device was found BEHIND. */
|
|
43572
|
+
parentStableId: string().optional(),
|
|
43573
|
+
/** `false` = shown for context but not pickable. Default true. */
|
|
43574
|
+
selectable: boolean().optional()
|
|
42911
43575
|
});
|
|
42912
43576
|
/**
|
|
42913
43577
|
* Flat device summary returned by `createDevice` / `adoptDiscoveredDevice`.
|
|
@@ -42960,6 +43624,12 @@ method(_void(), _void(), { kind: "mutation" }), method(_void(), _void(), { kind:
|
|
|
42960
43624
|
}), method(object({}), boolean()), method(object({ params: record(string(), unknown()).optional() }), array(DiscoveryCandidateSchema), {
|
|
42961
43625
|
kind: "mutation",
|
|
42962
43626
|
auth: "admin"
|
|
43627
|
+
}), method(object({}), array(DiscoveryModeSchema)), method(object({
|
|
43628
|
+
hits: array(DiscoveryHitSchema),
|
|
43629
|
+
params: record(string(), unknown()).optional()
|
|
43630
|
+
}), array(DiscoveryCandidateSchema), {
|
|
43631
|
+
kind: "mutation",
|
|
43632
|
+
auth: "admin"
|
|
42963
43633
|
}), method(object({}), CreationSchemaOutputSchema), method(object({}), object({ deviceType: _enum(DeviceType).nullable() })), method(object({ candidate: DiscoveryCandidateSchema }), DeviceSummarySchema, {
|
|
42964
43634
|
kind: "mutation",
|
|
42965
43635
|
auth: "admin"
|
|
@@ -43687,17 +44357,23 @@ method(object({
|
|
|
43687
44357
|
}), method(object({}), object({ providers: array(object({
|
|
43688
44358
|
addonId: string(),
|
|
43689
44359
|
label: string()
|
|
43690
|
-
})).readonly() }), { auth: "admin" }), method(
|
|
43691
|
-
|
|
43692
|
-
|
|
43693
|
-
|
|
43694
|
-
|
|
43695
|
-
|
|
43696
|
-
|
|
43697
|
-
|
|
43698
|
-
}),
|
|
44360
|
+
})).readonly() }), { auth: "admin" }), method(
|
|
44361
|
+
/** `subnets`: extra CIDRs for THIS scan, on top of the hub's own and the saved list. */
|
|
44362
|
+
object({ subnets: array(string()).optional() }),
|
|
44363
|
+
object({ groups: array(object({
|
|
44364
|
+
addonId: string(),
|
|
44365
|
+
label: string(),
|
|
44366
|
+
candidates: array(DiscoveryCandidateSchema).readonly(),
|
|
44367
|
+
error: string().nullable()
|
|
44368
|
+
})).readonly() }),
|
|
44369
|
+
{
|
|
44370
|
+
kind: "mutation",
|
|
44371
|
+
auth: "admin"
|
|
44372
|
+
}
|
|
44373
|
+
), method(object({
|
|
43699
44374
|
addonId: string(),
|
|
43700
|
-
params: record(string(), unknown()).optional()
|
|
44375
|
+
params: record(string(), unknown()).optional(),
|
|
44376
|
+
subnets: array(string()).optional()
|
|
43701
44377
|
}), object({ candidates: array(DiscoveryCandidateSchema).readonly() }), {
|
|
43702
44378
|
kind: "mutation",
|
|
43703
44379
|
auth: "admin"
|
|
@@ -61323,7 +61999,9 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
|
|
|
61323
61999
|
id: string(),
|
|
61324
62000
|
settings: record(string(), unknown()),
|
|
61325
62001
|
/** Keys the form's schema declares secret (by type) — keychain-stored (D787). */
|
|
61326
|
-
secretKeys: array(string()).optional()
|
|
62002
|
+
secretKeys: array(string()).optional(),
|
|
62003
|
+
/** The integration's own addon applies the change in-process and asks the hub not to respawn it. */
|
|
62004
|
+
skipRestart: boolean().optional()
|
|
61327
62005
|
}), object({ success: boolean() }), {
|
|
61328
62006
|
kind: "mutation",
|
|
61329
62007
|
auth: "admin"
|
|
@@ -61800,6 +62478,39 @@ var TlsStatusSchema = object({
|
|
|
61800
62478
|
hasCa: boolean()
|
|
61801
62479
|
}).nullable()
|
|
61802
62480
|
});
|
|
62481
|
+
/** One provider's ask of the scan engine: the modes it declared. */
|
|
62482
|
+
var ScanRequestSchema = object({
|
|
62483
|
+
providerId: string().min(1),
|
|
62484
|
+
modes: array(DiscoveryModeSchema)
|
|
62485
|
+
});
|
|
62486
|
+
var ScanErrorSchema = object({
|
|
62487
|
+
/** Absent for a failure that is the scan's own (a bad subnet, a socket that would not open). */
|
|
62488
|
+
providerId: string().optional(),
|
|
62489
|
+
/** The mode family that failed (`mdns`, `http-probe`, …). */
|
|
62490
|
+
mode: string().optional(),
|
|
62491
|
+
message: string()
|
|
62492
|
+
});
|
|
62493
|
+
/** What one scan did, per mode family; the engine logs the same numbers once. */
|
|
62494
|
+
var ScanStatsSchema = object({
|
|
62495
|
+
hostsProbed: number().int(),
|
|
62496
|
+
hits: number().int(),
|
|
62497
|
+
timeouts: number().int(),
|
|
62498
|
+
byMode: record(string(), object({
|
|
62499
|
+
probed: number().int(),
|
|
62500
|
+
hits: number().int(),
|
|
62501
|
+
timeouts: number().int()
|
|
62502
|
+
}))
|
|
62503
|
+
});
|
|
62504
|
+
var ScanNetworkResultSchema = object({
|
|
62505
|
+
hits: array(object({
|
|
62506
|
+
providerId: string(),
|
|
62507
|
+
hit: DiscoveryHitSchema
|
|
62508
|
+
})),
|
|
62509
|
+
/** CIDRs actually covered: the hub's own /24s, the saved list and the per-call list. */
|
|
62510
|
+
subnetsScanned: array(string()),
|
|
62511
|
+
errors: array(ScanErrorSchema),
|
|
62512
|
+
stats: ScanStatsSchema
|
|
62513
|
+
});
|
|
61803
62514
|
method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
|
|
61804
62515
|
/**
|
|
61805
62516
|
* LEGACY HINT — do not send from new code. Kept optional so clients
|
|
@@ -61854,6 +62565,16 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
|
|
|
61854
62565
|
}), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
|
|
61855
62566
|
kind: "mutation",
|
|
61856
62567
|
auth: "admin"
|
|
62568
|
+
}), method(object({
|
|
62569
|
+
requests: array(ScanRequestSchema),
|
|
62570
|
+
subnets: array(string()).optional()
|
|
62571
|
+
}), ScanNetworkResultSchema, {
|
|
62572
|
+
kind: "mutation",
|
|
62573
|
+
auth: "admin",
|
|
62574
|
+
timeoutMs: 9e4
|
|
62575
|
+
}), method(_void(), object({ subnets: array(string()) }), { auth: "admin" }), method(object({ subnets: array(string()) }), object({ subnets: array(string()) }), {
|
|
62576
|
+
kind: "mutation",
|
|
62577
|
+
auth: "admin"
|
|
61857
62578
|
}), method(_void(), TlsStatusSchema, {
|
|
61858
62579
|
kind: "mutation",
|
|
61859
62580
|
auth: "admin"
|
|
@@ -64396,7 +65117,7 @@ object({
|
|
|
64396
65117
|
});
|
|
64397
65118
|
Object.values(DeviceType);
|
|
64398
65119
|
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
|
|
64399
|
-
DeviceType.Camera, DeviceType.Sensor, DeviceType.Switch, method(object({ deviceId: number().int().nonnegative() }), object({ success: literal(true) }), {
|
|
65120
|
+
DeviceType.Camera, DeviceType.Sensor, DeviceType.Switch, DeviceType.Gateway, method(object({ deviceId: number().int().nonnegative() }), object({ success: literal(true) }), {
|
|
64400
65121
|
kind: "mutation",
|
|
64401
65122
|
auth: "admin"
|
|
64402
65123
|
});
|
|
@@ -68254,7 +68975,7 @@ discriminatedUnion("kind", [object({
|
|
|
68254
68975
|
})]);
|
|
68255
68976
|
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
68256
68977
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
68257
|
-
DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Camera, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
|
|
68978
|
+
DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Gateway, DeviceType.Camera, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
|
|
68258
68979
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
68259
68980
|
DeviceRole.BinarySensor, DeviceRole.MotionSensor;
|
|
68260
68981
|
new Set(CAP_NAMES);
|
|
@@ -68405,7 +69126,7 @@ var AccessoryKind = {
|
|
|
68405
69126
|
PrivacyMask: DeviceRole.PrivacyMask
|
|
68406
69127
|
};
|
|
68407
69128
|
AccessoryKind.Siren, AccessoryKind.Floodlight, AccessoryKind.Spotlight, AccessoryKind.PirSensor, AccessoryKind.Chime, AccessoryKind.Autotrack, AccessoryKind.Nightvision, AccessoryKind.PrivacyMask;
|
|
68408
|
-
DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer;
|
|
69129
|
+
DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Gateway;
|
|
68409
69130
|
new Set(Object.values(DeviceType));
|
|
68410
69131
|
Object.freeze({
|
|
68411
69132
|
"accessories.getStatus": {
|
|
@@ -68793,6 +69514,13 @@ Object.freeze({
|
|
|
68793
69514
|
access: "create",
|
|
68794
69515
|
role: "admin"
|
|
68795
69516
|
},
|
|
69517
|
+
"addonSettings.getDeclaredSettingsKeys": {
|
|
69518
|
+
capName: "addon-settings",
|
|
69519
|
+
capScope: "system",
|
|
69520
|
+
addonId: null,
|
|
69521
|
+
access: "view",
|
|
69522
|
+
role: "admin"
|
|
69523
|
+
},
|
|
68796
69524
|
"addonSettings.getDeviceSettings": {
|
|
68797
69525
|
capName: "addon-settings",
|
|
68798
69526
|
capScope: "system",
|
|
@@ -68814,6 +69542,13 @@ Object.freeze({
|
|
|
68814
69542
|
access: "view",
|
|
68815
69543
|
role: "admin"
|
|
68816
69544
|
},
|
|
69545
|
+
"addonSettings.unsetStoredSettingsKeys": {
|
|
69546
|
+
capName: "addon-settings",
|
|
69547
|
+
capScope: "system",
|
|
69548
|
+
addonId: null,
|
|
69549
|
+
access: "create",
|
|
69550
|
+
role: "admin"
|
|
69551
|
+
},
|
|
68817
69552
|
"addonSettings.updateDeviceSettings": {
|
|
68818
69553
|
capName: "addon-settings",
|
|
68819
69554
|
capScope: "system",
|
|
@@ -71229,6 +71964,13 @@ Object.freeze({
|
|
|
71229
71964
|
access: "create",
|
|
71230
71965
|
role: "admin"
|
|
71231
71966
|
},
|
|
71967
|
+
"deviceProvider.classifyDiscoveryHits": {
|
|
71968
|
+
capName: "device-provider",
|
|
71969
|
+
capScope: "system",
|
|
71970
|
+
addonId: null,
|
|
71971
|
+
access: "create",
|
|
71972
|
+
role: "admin"
|
|
71973
|
+
},
|
|
71232
71974
|
"deviceProvider.createDevice": {
|
|
71233
71975
|
capName: "device-provider",
|
|
71234
71976
|
capScope: "system",
|
|
@@ -71257,6 +71999,13 @@ Object.freeze({
|
|
|
71257
71999
|
access: "view",
|
|
71258
72000
|
role: "admin"
|
|
71259
72001
|
},
|
|
72002
|
+
"deviceProvider.getDiscoveryModes": {
|
|
72003
|
+
capName: "device-provider",
|
|
72004
|
+
capScope: "system",
|
|
72005
|
+
addonId: null,
|
|
72006
|
+
access: "view",
|
|
72007
|
+
role: "admin"
|
|
72008
|
+
},
|
|
71260
72009
|
"deviceProvider.getDiscoveryParamsSchema": {
|
|
71261
72010
|
capName: "device-provider",
|
|
71262
72011
|
capScope: "system",
|
|
@@ -72034,6 +72783,13 @@ Object.freeze({
|
|
|
72034
72783
|
access: "create",
|
|
72035
72784
|
role: "admin"
|
|
72036
72785
|
},
|
|
72786
|
+
"keychain.releaseRecordRefs": {
|
|
72787
|
+
capName: "keychain",
|
|
72788
|
+
capScope: "system",
|
|
72789
|
+
addonId: null,
|
|
72790
|
+
access: "create",
|
|
72791
|
+
role: "admin"
|
|
72792
|
+
},
|
|
72037
72793
|
"keychain.resolve": {
|
|
72038
72794
|
capName: "keychain",
|
|
72039
72795
|
capScope: "system",
|
|
@@ -72454,6 +73210,13 @@ Object.freeze({
|
|
|
72454
73210
|
access: "view",
|
|
72455
73211
|
role: "admin"
|
|
72456
73212
|
},
|
|
73213
|
+
"localNetwork.getScanSubnets": {
|
|
73214
|
+
capName: "local-network",
|
|
73215
|
+
capScope: "system",
|
|
73216
|
+
addonId: null,
|
|
73217
|
+
access: "view",
|
|
73218
|
+
role: "admin"
|
|
73219
|
+
},
|
|
72457
73220
|
"localNetwork.getTlsStatus": {
|
|
72458
73221
|
capName: "local-network",
|
|
72459
73222
|
capScope: "system",
|
|
@@ -72503,6 +73266,13 @@ Object.freeze({
|
|
|
72503
73266
|
access: "create",
|
|
72504
73267
|
role: "admin"
|
|
72505
73268
|
},
|
|
73269
|
+
"localNetwork.scanNetwork": {
|
|
73270
|
+
capName: "local-network",
|
|
73271
|
+
capScope: "system",
|
|
73272
|
+
addonId: null,
|
|
73273
|
+
access: "create",
|
|
73274
|
+
role: "admin"
|
|
73275
|
+
},
|
|
72506
73276
|
"localNetwork.setAddressRole": {
|
|
72507
73277
|
capName: "local-network",
|
|
72508
73278
|
capScope: "system",
|
|
@@ -72524,6 +73294,13 @@ Object.freeze({
|
|
|
72524
73294
|
access: "create",
|
|
72525
73295
|
role: "admin"
|
|
72526
73296
|
},
|
|
73297
|
+
"localNetwork.setScanSubnets": {
|
|
73298
|
+
capName: "local-network",
|
|
73299
|
+
capScope: "system",
|
|
73300
|
+
addonId: null,
|
|
73301
|
+
access: "create",
|
|
73302
|
+
role: "admin"
|
|
73303
|
+
},
|
|
72527
73304
|
"localNetwork.setViewerEndpoints": {
|
|
72528
73305
|
capName: "local-network",
|
|
72529
73306
|
capScope: "system",
|
|
@@ -76745,6 +77522,13 @@ Object.freeze({
|
|
|
76745
77522
|
access: "create",
|
|
76746
77523
|
role: "admin"
|
|
76747
77524
|
},
|
|
77525
|
+
"storageAdmin.dropOrphanedKind": {
|
|
77526
|
+
capName: "storage-admin",
|
|
77527
|
+
capScope: "system",
|
|
77528
|
+
addonId: null,
|
|
77529
|
+
access: "delete",
|
|
77530
|
+
role: "admin"
|
|
77531
|
+
},
|
|
76748
77532
|
"storageAdmin.listKinds": {
|
|
76749
77533
|
capName: "storage-admin",
|
|
76750
77534
|
capScope: "system",
|
|
@@ -76766,6 +77550,27 @@ Object.freeze({
|
|
|
76766
77550
|
access: "view",
|
|
76767
77551
|
role: "admin"
|
|
76768
77552
|
},
|
|
77553
|
+
"storageAdmin.scanStaleData": {
|
|
77554
|
+
capName: "storage-admin",
|
|
77555
|
+
capScope: "system",
|
|
77556
|
+
addonId: null,
|
|
77557
|
+
access: "create",
|
|
77558
|
+
role: "admin"
|
|
77559
|
+
},
|
|
77560
|
+
"storageAdmin.staleDataReport": {
|
|
77561
|
+
capName: "storage-admin",
|
|
77562
|
+
capScope: "system",
|
|
77563
|
+
addonId: null,
|
|
77564
|
+
access: "view",
|
|
77565
|
+
role: "admin"
|
|
77566
|
+
},
|
|
77567
|
+
"storageAdmin.unsetStaleSettingsKey": {
|
|
77568
|
+
capName: "storage-admin",
|
|
77569
|
+
capScope: "system",
|
|
77570
|
+
addonId: null,
|
|
77571
|
+
access: "create",
|
|
77572
|
+
role: "admin"
|
|
77573
|
+
},
|
|
76769
77574
|
"storageEvictable.evict": {
|
|
76770
77575
|
capName: "storage-evictable",
|
|
76771
77576
|
capScope: "system",
|
|
@@ -82462,7 +83267,7 @@ function projectAction(cap, action) {
|
|
|
82462
83267
|
};
|
|
82463
83268
|
}
|
|
82464
83269
|
Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
|
|
82465
|
-
DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer;
|
|
83270
|
+
DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Gateway;
|
|
82466
83271
|
DeviceFeature.BatteryOperated;
|
|
82467
83272
|
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
82468
83273
|
var HA_AS = "a power sensor (device_class power). Home Assistant has no laundry domain. running-state stays unexported until a null reading can be published as unavailable.";
|