@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
|
@@ -5824,7 +5824,7 @@ var ZodIssueCode = {
|
|
|
5824
5824
|
var ZodFirstPartyTypeKind;
|
|
5825
5825
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5826
5826
|
//#endregion
|
|
5827
|
-
//#region ../types/dist/sleep-
|
|
5827
|
+
//#region ../types/dist/sleep-CeyhrcOk.mjs
|
|
5828
5828
|
/**
|
|
5829
5829
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5830
5830
|
* window to float samples (D455).
|
|
@@ -7891,6 +7891,7 @@ var BaseAddon = class {
|
|
|
7891
7891
|
* a warning and falls back rather than crashing boot.
|
|
7892
7892
|
*/
|
|
7893
7893
|
state(key, schema, fallback) {
|
|
7894
|
+
this._declaredStateKeys.add(key);
|
|
7894
7895
|
return createDurableState({
|
|
7895
7896
|
key,
|
|
7896
7897
|
schema,
|
|
@@ -7906,6 +7907,96 @@ var BaseAddon = class {
|
|
|
7906
7907
|
} })
|
|
7907
7908
|
});
|
|
7908
7909
|
}
|
|
7910
|
+
/** Durable-state keys this process registered through {@link state}. */
|
|
7911
|
+
_declaredStateKeys = /* @__PURE__ */ new Set();
|
|
7912
|
+
/**
|
|
7913
|
+
* Stale-data audit: every global-settings key this addon still declares —
|
|
7914
|
+
* constructor defaults, settings-schema fields (layout containers included),
|
|
7915
|
+
* durable-state keys and the root of each `secretStatePaths` entry. Answered
|
|
7916
|
+
* here, in the addon's own process, so the hub keeps no copy of it.
|
|
7917
|
+
*/
|
|
7918
|
+
async getDeclaredSettingsKeys() {
|
|
7919
|
+
return { keys: [...this.declaredKeySet()].toSorted() };
|
|
7920
|
+
}
|
|
7921
|
+
declaredKeySet() {
|
|
7922
|
+
const keys = new Set(Object.keys(this.defaults));
|
|
7923
|
+
const schema = this.globalSettingsSchema();
|
|
7924
|
+
if (schema) for (const section of schema.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
7925
|
+
for (const key of this._declaredStateKeys) keys.add(key);
|
|
7926
|
+
for (const path of this.secretStatePaths) {
|
|
7927
|
+
const root = path.split(/[.[]/, 1)[0];
|
|
7928
|
+
if (root) keys.add(root);
|
|
7929
|
+
}
|
|
7930
|
+
return keys;
|
|
7931
|
+
}
|
|
7932
|
+
/**
|
|
7933
|
+
* Stale-data audit: remove stored global-settings keys the addon no longer
|
|
7934
|
+
* declares, and clear the keychain values their markers held — except a value
|
|
7935
|
+
* a surviving key still points at. A declared or absent key is refused.
|
|
7936
|
+
*/
|
|
7937
|
+
async unsetStoredSettingsKeys(keys) {
|
|
7938
|
+
const settings = this._ctx?.settings;
|
|
7939
|
+
const raw = this.rawSettings();
|
|
7940
|
+
if (!settings || !raw) return {
|
|
7941
|
+
removed: [],
|
|
7942
|
+
refused: keys.map((key) => ({
|
|
7943
|
+
key,
|
|
7944
|
+
reason: "no settings store"
|
|
7945
|
+
})),
|
|
7946
|
+
secretsCleared: 0
|
|
7947
|
+
};
|
|
7948
|
+
let stored;
|
|
7949
|
+
try {
|
|
7950
|
+
stored = await raw.readAddonStore();
|
|
7951
|
+
} catch (error) {
|
|
7952
|
+
const reason = `store unreadable: ${error instanceof Error ? error.message : "unknown"}`;
|
|
7953
|
+
return {
|
|
7954
|
+
removed: [],
|
|
7955
|
+
refused: keys.map((key) => ({
|
|
7956
|
+
key,
|
|
7957
|
+
reason
|
|
7958
|
+
})),
|
|
7959
|
+
secretsCleared: 0
|
|
7960
|
+
};
|
|
7961
|
+
}
|
|
7962
|
+
const declared = this.declaredKeySet();
|
|
7963
|
+
const removed = [];
|
|
7964
|
+
const refused = [];
|
|
7965
|
+
for (const key of keys) {
|
|
7966
|
+
const base = key.split("@", 1)[0] ?? key;
|
|
7967
|
+
if (declared.has(base)) refused.push({
|
|
7968
|
+
key,
|
|
7969
|
+
reason: "still declared by the addon"
|
|
7970
|
+
});
|
|
7971
|
+
else if (!(key in stored)) refused.push({
|
|
7972
|
+
key,
|
|
7973
|
+
reason: "not stored"
|
|
7974
|
+
});
|
|
7975
|
+
else removed.push(key);
|
|
7976
|
+
}
|
|
7977
|
+
if (removed.length === 0) return {
|
|
7978
|
+
removed,
|
|
7979
|
+
refused,
|
|
7980
|
+
secretsCleared: 0
|
|
7981
|
+
};
|
|
7982
|
+
const kept = /* @__PURE__ */ new Set();
|
|
7983
|
+
const held = /* @__PURE__ */ new Set();
|
|
7984
|
+
for (const [key, value] of Object.entries(stored)) {
|
|
7985
|
+
const target = removed.includes(key) ? held : kept;
|
|
7986
|
+
for (const ref of collectSecretMarkerRefs(value)) target.add(ref);
|
|
7987
|
+
}
|
|
7988
|
+
const refsToClear = [...held].filter((ref) => !kept.has(ref));
|
|
7989
|
+
const patch = {};
|
|
7990
|
+
for (const key of removed) patch[key] = void 0;
|
|
7991
|
+
await settings.writeAddonStore(patch);
|
|
7992
|
+
const secretsCleared = refsToClear.length > 0 && this._ctx?.secrets ? await this._ctx.secrets.delete(refsToClear) : 0;
|
|
7993
|
+
await this.resolveConfig();
|
|
7994
|
+
return {
|
|
7995
|
+
removed,
|
|
7996
|
+
refused,
|
|
7997
|
+
secretsCleared
|
|
7998
|
+
};
|
|
7999
|
+
}
|
|
7909
8000
|
/** Per-device variant of {@link state}, backed by the per-device store. */
|
|
7910
8001
|
deviceState(deviceId, key, schema, fallback) {
|
|
7911
8002
|
return createDurableState({
|
|
@@ -8176,6 +8267,21 @@ function dropPerNodeFields(fields) {
|
|
|
8176
8267
|
}
|
|
8177
8268
|
return kept;
|
|
8178
8269
|
}
|
|
8270
|
+
function collectFieldKeys(fields) {
|
|
8271
|
+
const collected = [];
|
|
8272
|
+
for (const field of fields) {
|
|
8273
|
+
if (field.type === "group") {
|
|
8274
|
+
collected.push(...collectFieldKeys(field.fields));
|
|
8275
|
+
continue;
|
|
8276
|
+
}
|
|
8277
|
+
if (field.type === "sub-tabs") {
|
|
8278
|
+
for (const tab of field.tabs) collected.push(...collectFieldKeys(tab.fields));
|
|
8279
|
+
continue;
|
|
8280
|
+
}
|
|
8281
|
+
if ("key" in field && typeof field.key === "string") collected.push(field.key);
|
|
8282
|
+
}
|
|
8283
|
+
return collected;
|
|
8284
|
+
}
|
|
8179
8285
|
function collectPerNodeFieldKeys(fields) {
|
|
8180
8286
|
const collected = [];
|
|
8181
8287
|
for (const field of fields) {
|
|
@@ -11031,7 +11137,7 @@ object({
|
|
|
11031
11137
|
params: record(string(), union([string(), number()])).optional()
|
|
11032
11138
|
});
|
|
11033
11139
|
//#endregion
|
|
11034
|
-
//#region ../types/dist/err-msg-
|
|
11140
|
+
//#region ../types/dist/err-msg-BD5buxvT.mjs
|
|
11035
11141
|
var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
11036
11142
|
DeviceType["Camera"] = "camera";
|
|
11037
11143
|
DeviceType["Hub"] = "hub";
|
|
@@ -11137,6 +11243,12 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
|
11137
11243
|
DeviceType["Washer"] = "washer";
|
|
11138
11244
|
/** Tumble dryer. Same plug-built running surface as {@link Washer}. */
|
|
11139
11245
|
DeviceType["Dryer"] = "dryer";
|
|
11246
|
+
/** The physical bridge box of a vendor ecosystem (Velux KLF-200, and later
|
|
11247
|
+
* other radio-to-IP bridges). It carries the box's own facts (firmware,
|
|
11248
|
+
* address, connection state) and the actions the box really supports
|
|
11249
|
+
* (`reboot`, `device-admin-link`). Products are NOT its children — they are
|
|
11250
|
+
* adopted beside it — and its connection settings stay on the integration. */
|
|
11251
|
+
DeviceType["Gateway"] = "gateway";
|
|
11140
11252
|
return DeviceType;
|
|
11141
11253
|
}({});
|
|
11142
11254
|
var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
|
|
@@ -11387,7 +11499,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
|
|
|
11387
11499
|
return DeviceRole;
|
|
11388
11500
|
}({});
|
|
11389
11501
|
//#endregion
|
|
11390
|
-
//#region ../types/dist/composition-
|
|
11502
|
+
//#region ../types/dist/composition-C7WdThmP.mjs
|
|
11391
11503
|
/**
|
|
11392
11504
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
11393
11505
|
*
|
|
@@ -16515,8 +16627,172 @@ object({
|
|
|
16515
16627
|
quantity: ReadingQuantitySchema,
|
|
16516
16628
|
unit: string().min(1)
|
|
16517
16629
|
});
|
|
16518
|
-
|
|
16519
|
-
|
|
16630
|
+
new Set([
|
|
16631
|
+
{
|
|
16632
|
+
id: "person",
|
|
16633
|
+
name: "Person"
|
|
16634
|
+
},
|
|
16635
|
+
{
|
|
16636
|
+
id: "vehicle",
|
|
16637
|
+
name: "Vehicle"
|
|
16638
|
+
},
|
|
16639
|
+
{
|
|
16640
|
+
id: "animal",
|
|
16641
|
+
name: "Animal"
|
|
16642
|
+
},
|
|
16643
|
+
{
|
|
16644
|
+
id: "package",
|
|
16645
|
+
name: "Package"
|
|
16646
|
+
}
|
|
16647
|
+
].map((l) => l.id));
|
|
16648
|
+
var COCO_TO_MACRO = {
|
|
16649
|
+
mapping: {
|
|
16650
|
+
person: "person",
|
|
16651
|
+
bicycle: "vehicle",
|
|
16652
|
+
car: "vehicle",
|
|
16653
|
+
motorcycle: "vehicle",
|
|
16654
|
+
airplane: "vehicle",
|
|
16655
|
+
bus: "vehicle",
|
|
16656
|
+
train: "vehicle",
|
|
16657
|
+
truck: "vehicle",
|
|
16658
|
+
boat: "vehicle",
|
|
16659
|
+
bird: "animal",
|
|
16660
|
+
cat: "animal",
|
|
16661
|
+
dog: "animal",
|
|
16662
|
+
horse: "animal",
|
|
16663
|
+
sheep: "animal",
|
|
16664
|
+
cow: "animal",
|
|
16665
|
+
elephant: "animal",
|
|
16666
|
+
bear: "animal",
|
|
16667
|
+
zebra: "animal",
|
|
16668
|
+
giraffe: "animal",
|
|
16669
|
+
suitcase: "package",
|
|
16670
|
+
backpack: "package",
|
|
16671
|
+
handbag: "package"
|
|
16672
|
+
},
|
|
16673
|
+
preserveOriginal: false
|
|
16674
|
+
};
|
|
16675
|
+
/**
|
|
16676
|
+
* WHAT a detection-shaped leaf is about: one MACRO class (person, vehicle,
|
|
16677
|
+
* animal, package) plus an OPTIONAL label that narrows it.
|
|
16678
|
+
*
|
|
16679
|
+
* The taxonomy is `COCO_TO_MACRO` (and `CLASS_MAP_MACRO_TARGETS`) — never a
|
|
16680
|
+
* second list. The label that fits depends on the macro class:
|
|
16681
|
+
*
|
|
16682
|
+
* - `animal` → a sub-class (`dog`, `cat`, …);
|
|
16683
|
+
* - `person` → a known face (an identity of the `face-gallery`);
|
|
16684
|
+
* - `vehicle` → a plate (its text) or a sub-class (`car`, `truck`, …);
|
|
16685
|
+
* - `package` → a sub-class.
|
|
16686
|
+
*
|
|
16687
|
+
* `other` exists only so a class the taxonomy does not know (a custom model's
|
|
16688
|
+
* `forklift`) survives the conversion of a stored recipe without widening.
|
|
16689
|
+
*
|
|
16690
|
+
* A label is decided LATE (D683): face recognition and LPR land after the
|
|
16691
|
+
* detection starts. The track-lifecycle `update` event carries the newly
|
|
16692
|
+
* resolved `label` / `plateText`, which is why a leaf with a label fires when
|
|
16693
|
+
* the subject GETS it — not at the first detection.
|
|
16694
|
+
*/
|
|
16695
|
+
var SubClassLabelSchema = object({
|
|
16696
|
+
kind: literal("sub-class"),
|
|
16697
|
+
className: string().min(1)
|
|
16698
|
+
});
|
|
16699
|
+
var IdentityLabelSchema = object({
|
|
16700
|
+
kind: literal("identity"),
|
|
16701
|
+
/** The `face-gallery` identity id (stable across a rename). */
|
|
16702
|
+
identityId: string().min(1),
|
|
16703
|
+
/** The identity's name — what a track-lifecycle event carries as `label`. */
|
|
16704
|
+
name: string().min(1)
|
|
16705
|
+
});
|
|
16706
|
+
var PlateLabelSchema = object({
|
|
16707
|
+
kind: literal("plate"),
|
|
16708
|
+
text: string().min(1)
|
|
16709
|
+
});
|
|
16710
|
+
function macroOf$1(className) {
|
|
16711
|
+
return COCO_TO_MACRO.mapping[className.toLowerCase()] ?? className.toLowerCase();
|
|
16712
|
+
}
|
|
16713
|
+
/** A sub-class label must belong to the macro it is under. */
|
|
16714
|
+
function subClassBelongs(macro, label) {
|
|
16715
|
+
return label === void 0 || macroOf$1(label.className) === macro;
|
|
16716
|
+
}
|
|
16717
|
+
var subClassOf = (macro) => SubClassLabelSchema.refine((l) => subClassBelongs(macro, l), { message: `that class is not a kind of ${macro} in the detection taxonomy` });
|
|
16718
|
+
var DetectionSubjectSchema = discriminatedUnion("macro", [
|
|
16719
|
+
object({
|
|
16720
|
+
macro: literal("animal"),
|
|
16721
|
+
label: subClassOf("animal").optional()
|
|
16722
|
+
}),
|
|
16723
|
+
object({
|
|
16724
|
+
macro: literal("person"),
|
|
16725
|
+
label: IdentityLabelSchema.optional()
|
|
16726
|
+
}),
|
|
16727
|
+
object({
|
|
16728
|
+
macro: literal("vehicle"),
|
|
16729
|
+
label: discriminatedUnion("kind", [SubClassLabelSchema.refine((l) => subClassBelongs("vehicle", l), { message: "that class is not a kind of vehicle in the detection taxonomy" }), PlateLabelSchema]).optional()
|
|
16730
|
+
}),
|
|
16731
|
+
object({
|
|
16732
|
+
macro: literal("package"),
|
|
16733
|
+
label: subClassOf("package").optional()
|
|
16734
|
+
}),
|
|
16735
|
+
object({
|
|
16736
|
+
macro: literal("other"),
|
|
16737
|
+
className: string().min(1)
|
|
16738
|
+
})
|
|
16739
|
+
]);
|
|
16740
|
+
/**
|
|
16741
|
+
* The subject of a PARKED object. The stationary registry's event carries a
|
|
16742
|
+
* class and a zone, no face and no plate — an identity or plate label there
|
|
16743
|
+
* could never match, so the schema refuses it instead of letting it sit dead.
|
|
16744
|
+
*/
|
|
16745
|
+
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" });
|
|
16746
|
+
/**
|
|
16747
|
+
* A stored class string as a subject: a macro name stays a macro; a raw class
|
|
16748
|
+
* the taxonomy files under a macro becomes that macro plus the sub-class label
|
|
16749
|
+
* (`dog` → animal / dog); anything else is `other`, kept verbatim.
|
|
16750
|
+
*/
|
|
16751
|
+
function subjectFromClassName(className) {
|
|
16752
|
+
const name = className.trim().toLowerCase();
|
|
16753
|
+
const macro = COCO_TO_MACRO.mapping[name];
|
|
16754
|
+
if (new Set(Object.values(COCO_TO_MACRO.mapping)).has(name)) return macroOnly(name);
|
|
16755
|
+
if (macro === void 0) return {
|
|
16756
|
+
macro: "other",
|
|
16757
|
+
className
|
|
16758
|
+
};
|
|
16759
|
+
const label = {
|
|
16760
|
+
kind: "sub-class",
|
|
16761
|
+
className: name
|
|
16762
|
+
};
|
|
16763
|
+
switch (macro) {
|
|
16764
|
+
case "animal": return {
|
|
16765
|
+
macro: "animal",
|
|
16766
|
+
label
|
|
16767
|
+
};
|
|
16768
|
+
case "vehicle": return {
|
|
16769
|
+
macro: "vehicle",
|
|
16770
|
+
label
|
|
16771
|
+
};
|
|
16772
|
+
case "package": return {
|
|
16773
|
+
macro: "package",
|
|
16774
|
+
label
|
|
16775
|
+
};
|
|
16776
|
+
default: return {
|
|
16777
|
+
macro: "other",
|
|
16778
|
+
className
|
|
16779
|
+
};
|
|
16780
|
+
}
|
|
16781
|
+
}
|
|
16782
|
+
function macroOnly(macro) {
|
|
16783
|
+
switch (macro) {
|
|
16784
|
+
case "animal": return { macro: "animal" };
|
|
16785
|
+
case "person": return { macro: "person" };
|
|
16786
|
+
case "vehicle": return { macro: "vehicle" };
|
|
16787
|
+
case "package": return { macro: "package" };
|
|
16788
|
+
default: return {
|
|
16789
|
+
macro: "other",
|
|
16790
|
+
className: macro
|
|
16791
|
+
};
|
|
16792
|
+
}
|
|
16793
|
+
}
|
|
16794
|
+
/** How a state leaf compares a device field to a value. Derived from the
|
|
16795
|
+
* field's `kind` in `deviceManager.getWireableFields`, never hand-maintained. */
|
|
16520
16796
|
var AutomationConditionOperatorSchema = _enum([
|
|
16521
16797
|
"eq",
|
|
16522
16798
|
"ne",
|
|
@@ -16527,12 +16803,76 @@ var AutomationConditionOperatorSchema = _enum([
|
|
|
16527
16803
|
"contains",
|
|
16528
16804
|
"in"
|
|
16529
16805
|
]);
|
|
16530
|
-
|
|
16531
|
-
|
|
16806
|
+
/**
|
|
16807
|
+
* D8 compliance, and it is the reason a "becomes" leaf is not merely an event
|
|
16808
|
+
* subscription: it evaluates against the **state mirror**, which is reconciled,
|
|
16809
|
+
* and an event only WAKES the evaluation. A dropped event therefore DELAYS a
|
|
16810
|
+
* fire; it does not lose it.
|
|
16811
|
+
*/
|
|
16812
|
+
var DeviceBecomesLeafSchema = object({
|
|
16813
|
+
kind: literal("device-becomes"),
|
|
16814
|
+
deviceId: number().int().nonnegative(),
|
|
16815
|
+
cap: string().min(1),
|
|
16816
|
+
fieldPath: string().min(1),
|
|
16817
|
+
/** Fire when the field takes this value. Omit to fire on any change. */
|
|
16818
|
+
becomes: union([
|
|
16819
|
+
string(),
|
|
16820
|
+
number(),
|
|
16821
|
+
boolean()
|
|
16822
|
+
]).optional(),
|
|
16823
|
+
/** Only on a CHANGE of value, not on every re-report. */
|
|
16824
|
+
edge: boolean().optional(),
|
|
16825
|
+
/** The new value must hold this long before it counts. */
|
|
16826
|
+
forMs: number().int().min(0).max(864e5).optional(),
|
|
16827
|
+
/** Collapse a burst into one occurrence. */
|
|
16828
|
+
debounceMs: number().int().min(0).max(6e5).optional()
|
|
16829
|
+
});
|
|
16830
|
+
var DeviceEventLeafSchema = object({
|
|
16831
|
+
kind: literal("device-event"),
|
|
16832
|
+
/** An `EventCategory` value. */
|
|
16833
|
+
category: string().min(1),
|
|
16834
|
+
deviceId: number().int().nonnegative().optional()
|
|
16835
|
+
});
|
|
16836
|
+
var ScheduleLeafSchema = object({
|
|
16837
|
+
kind: literal("schedule"),
|
|
16838
|
+
cron: string().min(1).max(120)
|
|
16839
|
+
});
|
|
16840
|
+
var ManualLeafSchema = object({ kind: literal("manual") });
|
|
16841
|
+
var IntervalLeafSchema = object({
|
|
16842
|
+
kind: literal("interval"),
|
|
16843
|
+
minutes: number().int().min(1).max(1440)
|
|
16844
|
+
});
|
|
16845
|
+
var DetectionLeafSchema = object({
|
|
16846
|
+
kind: literal("detection"),
|
|
16847
|
+
/** One macro class each, optionally narrowed by a label; a track of any listed subject fires. */
|
|
16848
|
+
subjects: array(DetectionSubjectSchema).min(1),
|
|
16849
|
+
deviceIds: array(number().int().nonnegative()).min(1),
|
|
16850
|
+
/** The track must have visited at least one of these zones. */
|
|
16851
|
+
zones: array(string().min(1)).min(1).optional(),
|
|
16852
|
+
/** Best-so-far confidence the track must reach. */
|
|
16853
|
+
minScore: number().min(0).max(1).optional()
|
|
16854
|
+
});
|
|
16855
|
+
var StationaryLeafSchema = object({
|
|
16856
|
+
kind: literal("stationary"),
|
|
16857
|
+
deviceIds: array(number().int().nonnegative()).min(1),
|
|
16858
|
+
subjects: array(StationarySubjectSchema).min(1).optional(),
|
|
16859
|
+
zones: array(string().min(1)).min(1).optional(),
|
|
16860
|
+
/** `appeared`: it became parked. `departed`: the registry retired it. */
|
|
16861
|
+
phase: _enum(["appeared", "departed"])
|
|
16862
|
+
});
|
|
16863
|
+
/** "is": one device field compared to one value, NOW. */
|
|
16864
|
+
var DeviceIsLeafSchema = object({
|
|
16865
|
+
kind: literal("device-is"),
|
|
16532
16866
|
deviceId: number().int().nonnegative(),
|
|
16533
16867
|
cap: string().min(1),
|
|
16534
16868
|
fieldPath: string().min(1),
|
|
16535
16869
|
operator: AutomationConditionOperatorSchema,
|
|
16870
|
+
/**
|
|
16871
|
+
* The value. A number on a quantity field is stored in the field's base unit
|
|
16872
|
+
* (D944); a quantity value is the WRITE form, which the save boundary
|
|
16873
|
+
* replaces with the converted number and `unit`. A leaf that still carries
|
|
16874
|
+
* one is never evaluated (it fails).
|
|
16875
|
+
*/
|
|
16536
16876
|
value: union([
|
|
16537
16877
|
string(),
|
|
16538
16878
|
number(),
|
|
@@ -16540,24 +16880,26 @@ var AutomationConditionLeafSchema = object({
|
|
|
16540
16880
|
array(union([string(), number()])),
|
|
16541
16881
|
QuantityValueSchema
|
|
16542
16882
|
]),
|
|
16883
|
+
/** The unit the person picked for `value` (display only; `value` is in the base unit). */
|
|
16543
16884
|
unit: string().min(1).optional()
|
|
16544
16885
|
});
|
|
16545
|
-
|
|
16886
|
+
/**
|
|
16887
|
+
* True when the camera currently has a PARKED object matching `subjects` in at
|
|
16888
|
+
* least one of `zones`. Reads the zone-analytics snapshot through the same
|
|
16889
|
+
* mirror as every other state leaf, so a dropped event never decides it (D49).
|
|
16890
|
+
*/
|
|
16891
|
+
var StationaryPresentLeafSchema = object({
|
|
16546
16892
|
kind: literal("stationary-present"),
|
|
16547
16893
|
deviceId: number().int().nonnegative(),
|
|
16548
|
-
|
|
16549
|
-
classes: array(string().min(1)).min(1).optional(),
|
|
16550
|
-
/** The parked object must be in at least one of these zones. */
|
|
16894
|
+
subjects: array(StationarySubjectSchema).min(1).optional(),
|
|
16551
16895
|
zones: array(string().min(1)).min(1).optional()
|
|
16552
16896
|
});
|
|
16553
16897
|
/**
|
|
16554
|
-
* The
|
|
16555
|
-
*
|
|
16556
|
-
*
|
|
16557
|
-
* validation is the SAME function `ExpressionSourceSchema` runs, so the two
|
|
16558
|
-
* cannot drift — an expression that one accepts, the other accepts.
|
|
16898
|
+
* The escape hatch: the bounded expression engine over named device bindings.
|
|
16899
|
+
* Declared as a plain object so a discriminated union can read `kind`; the
|
|
16900
|
+
* author-time validation is the SAME function `ExpressionSourceSchema` runs.
|
|
16559
16901
|
*/
|
|
16560
|
-
var
|
|
16902
|
+
var ExpressionLeafSchema = object({
|
|
16561
16903
|
kind: literal("expression"),
|
|
16562
16904
|
expr: string().min(1).max(MAX_EXPRESSION_SOURCE_LENGTH),
|
|
16563
16905
|
bindings: record(string().regex(EXPRESSION_IDENTIFIER_RE), ExpressionBindingSourceSchema)
|
|
@@ -16569,55 +16911,62 @@ var AutomationConditionExpressionSchema = object({
|
|
|
16569
16911
|
path: ["expr"]
|
|
16570
16912
|
});
|
|
16571
16913
|
});
|
|
16572
|
-
var
|
|
16914
|
+
var AutomationWhenSchema = lazy(() => discriminatedUnion("kind", [
|
|
16573
16915
|
object({
|
|
16574
16916
|
kind: literal("all"),
|
|
16575
|
-
children: array(
|
|
16917
|
+
children: array(AutomationWhenSchema)
|
|
16576
16918
|
}),
|
|
16577
16919
|
object({
|
|
16578
16920
|
kind: literal("any"),
|
|
16579
|
-
children: array(
|
|
16921
|
+
children: array(AutomationWhenSchema)
|
|
16580
16922
|
}),
|
|
16581
16923
|
object({
|
|
16582
16924
|
kind: literal("not"),
|
|
16583
|
-
child:
|
|
16925
|
+
child: AutomationWhenSchema
|
|
16584
16926
|
}),
|
|
16585
|
-
|
|
16586
|
-
|
|
16587
|
-
|
|
16927
|
+
DeviceBecomesLeafSchema,
|
|
16928
|
+
DeviceEventLeafSchema,
|
|
16929
|
+
ScheduleLeafSchema,
|
|
16930
|
+
ManualLeafSchema,
|
|
16931
|
+
IntervalLeafSchema,
|
|
16932
|
+
DetectionLeafSchema,
|
|
16933
|
+
StationaryLeafSchema,
|
|
16934
|
+
DeviceIsLeafSchema,
|
|
16935
|
+
StationaryPresentLeafSchema,
|
|
16936
|
+
ExpressionLeafSchema
|
|
16588
16937
|
]));
|
|
16589
16938
|
/**
|
|
16590
|
-
*
|
|
16591
|
-
*
|
|
16592
|
-
*
|
|
16593
|
-
*
|
|
16594
|
-
*
|
|
16595
|
-
*
|
|
16596
|
-
*
|
|
16597
|
-
*
|
|
16598
|
-
|
|
16599
|
-
|
|
16939
|
+
* The recipe shape before the single `when` tree: a flat list of triggers
|
|
16940
|
+
* (OR'd, each an instant) and an optional condition tree. Stored recipes —
|
|
16941
|
+
* the `AUTOMATION_RECIPE` literal of every block saved so far, and whatever an
|
|
16942
|
+
* older client still posts — are in this shape, so it stays READABLE here, and
|
|
16943
|
+
* {@link convertLegacyRecipe} turns it into the new one, losslessly and one
|
|
16944
|
+
* way. Nothing writes this shape any more.
|
|
16945
|
+
*
|
|
16946
|
+
* `{ triggers: [t1…tn], conditions?: C }`
|
|
16947
|
+
* → `when = all(any(t1…tn), C)`, or `any(t1…tn)` without `C`;
|
|
16948
|
+
* a single trigger is not wrapped in `any`.
|
|
16949
|
+
* a `condition` leaf → a `device-is` leaf (the "is" verb);
|
|
16950
|
+
* a `device-state` trigger → a `device-becomes` leaf (the "becomes" verb);
|
|
16951
|
+
* `classes: ['dog']` → the macro `animal` with the label `dog`.
|
|
16952
|
+
*/
|
|
16953
|
+
var LegacyTriggerSchema = discriminatedUnion("kind", [
|
|
16600
16954
|
object({
|
|
16601
16955
|
kind: literal("device-state"),
|
|
16602
16956
|
deviceId: number().int().nonnegative(),
|
|
16603
16957
|
cap: string().min(1),
|
|
16604
16958
|
fieldPath: string().min(1),
|
|
16605
|
-
/** Fire when the field takes this value. Omit to fire on any change. */
|
|
16606
16959
|
becomes: union([
|
|
16607
16960
|
string(),
|
|
16608
16961
|
number(),
|
|
16609
16962
|
boolean()
|
|
16610
16963
|
]).optional(),
|
|
16611
|
-
/** Only on a CHANGE of value, not on every re-report. */
|
|
16612
16964
|
edge: boolean().optional(),
|
|
16613
|
-
/** The condition must hold this long before the run starts. */
|
|
16614
16965
|
forMs: number().int().min(0).max(864e5).optional(),
|
|
16615
|
-
/** Collapse a burst into one run. */
|
|
16616
16966
|
debounceMs: number().int().min(0).max(6e5).optional()
|
|
16617
16967
|
}),
|
|
16618
16968
|
object({
|
|
16619
16969
|
kind: literal("device-event"),
|
|
16620
|
-
/** An `EventCategory` value. */
|
|
16621
16970
|
category: string().min(1),
|
|
16622
16971
|
deviceId: number().int().nonnegative().optional()
|
|
16623
16972
|
}),
|
|
@@ -16628,25 +16977,16 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
|
|
|
16628
16977
|
object({ kind: literal("manual") }),
|
|
16629
16978
|
object({
|
|
16630
16979
|
kind: literal("detection"),
|
|
16631
|
-
/** Class names. A macro class (`animal`, `vehicle`, `person`, `package`)
|
|
16632
|
-
* matches every raw class that maps to it; any other name matches
|
|
16633
|
-
* exactly. */
|
|
16634
16980
|
classes: array(string().min(1)).min(1),
|
|
16635
16981
|
deviceIds: array(number().int().nonnegative()).min(1),
|
|
16636
|
-
/** The track must have visited at least one of these zones. */
|
|
16637
16982
|
zones: array(string().min(1)).min(1).optional(),
|
|
16638
|
-
/** Best-so-far confidence the track must reach. */
|
|
16639
16983
|
minScore: number().min(0).max(1).optional()
|
|
16640
16984
|
}),
|
|
16641
16985
|
object({
|
|
16642
16986
|
kind: literal("stationary"),
|
|
16643
16987
|
deviceIds: array(number().int().nonnegative()).min(1),
|
|
16644
|
-
/** Class names; a macro class matches like the detection trigger. */
|
|
16645
16988
|
classes: array(string().min(1)).min(1).optional(),
|
|
16646
|
-
/** The parked object must be in at least one of these zones. */
|
|
16647
16989
|
zones: array(string().min(1)).min(1).optional(),
|
|
16648
|
-
/** `appeared`: it became parked. `departed`: the registry retired it.
|
|
16649
|
-
* The vocabulary of `PipelineAnalyticsStationaryChanged`. */
|
|
16650
16990
|
phase: _enum(["appeared", "departed"])
|
|
16651
16991
|
}),
|
|
16652
16992
|
object({
|
|
@@ -16654,6 +16994,143 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
|
|
|
16654
16994
|
minutes: number().int().min(1).max(1440)
|
|
16655
16995
|
})
|
|
16656
16996
|
]);
|
|
16997
|
+
var LegacyConditionLeafSchema = object({
|
|
16998
|
+
kind: literal("condition"),
|
|
16999
|
+
deviceId: number().int().nonnegative(),
|
|
17000
|
+
cap: string().min(1),
|
|
17001
|
+
fieldPath: string().min(1),
|
|
17002
|
+
operator: AutomationConditionOperatorSchema,
|
|
17003
|
+
value: union([
|
|
17004
|
+
string(),
|
|
17005
|
+
number(),
|
|
17006
|
+
boolean(),
|
|
17007
|
+
array(union([string(), number()])),
|
|
17008
|
+
QuantityValueSchema
|
|
17009
|
+
]),
|
|
17010
|
+
unit: string().min(1).optional()
|
|
17011
|
+
});
|
|
17012
|
+
var LegacyStationaryPresentSchema = object({
|
|
17013
|
+
kind: literal("stationary-present"),
|
|
17014
|
+
deviceId: number().int().nonnegative(),
|
|
17015
|
+
classes: array(string().min(1)).min(1).optional(),
|
|
17016
|
+
zones: array(string().min(1)).min(1).optional()
|
|
17017
|
+
});
|
|
17018
|
+
var LegacyExpressionSchema = object({
|
|
17019
|
+
kind: literal("expression"),
|
|
17020
|
+
expr: string().min(1).max(MAX_EXPRESSION_SOURCE_LENGTH),
|
|
17021
|
+
bindings: record(string().regex(EXPRESSION_IDENTIFIER_RE), ExpressionBindingSourceSchema)
|
|
17022
|
+
}).superRefine((src, ctx) => {
|
|
17023
|
+
const err = validateExpressionSource(src);
|
|
17024
|
+
if (err !== null) ctx.addIssue({
|
|
17025
|
+
code: "custom",
|
|
17026
|
+
message: err,
|
|
17027
|
+
path: ["expr"]
|
|
17028
|
+
});
|
|
17029
|
+
});
|
|
17030
|
+
var LegacyConditionSchema = lazy(() => discriminatedUnion("kind", [
|
|
17031
|
+
object({
|
|
17032
|
+
kind: literal("all"),
|
|
17033
|
+
children: array(LegacyConditionSchema)
|
|
17034
|
+
}),
|
|
17035
|
+
object({
|
|
17036
|
+
kind: literal("any"),
|
|
17037
|
+
children: array(LegacyConditionSchema)
|
|
17038
|
+
}),
|
|
17039
|
+
object({
|
|
17040
|
+
kind: literal("not"),
|
|
17041
|
+
child: LegacyConditionSchema
|
|
17042
|
+
}),
|
|
17043
|
+
LegacyConditionLeafSchema,
|
|
17044
|
+
LegacyExpressionSchema,
|
|
17045
|
+
LegacyStationaryPresentSchema
|
|
17046
|
+
]));
|
|
17047
|
+
function convertTrigger(trigger) {
|
|
17048
|
+
switch (trigger.kind) {
|
|
17049
|
+
case "device-state": return {
|
|
17050
|
+
...trigger,
|
|
17051
|
+
kind: "device-becomes"
|
|
17052
|
+
};
|
|
17053
|
+
case "detection": {
|
|
17054
|
+
const { classes, ...rest } = trigger;
|
|
17055
|
+
return {
|
|
17056
|
+
...rest,
|
|
17057
|
+
subjects: classes.map(subjectFromClassName)
|
|
17058
|
+
};
|
|
17059
|
+
}
|
|
17060
|
+
case "stationary": {
|
|
17061
|
+
const { classes, ...rest } = trigger;
|
|
17062
|
+
return {
|
|
17063
|
+
...rest,
|
|
17064
|
+
...classes === void 0 ? {} : { subjects: classes.map(subjectFromClassName) }
|
|
17065
|
+
};
|
|
17066
|
+
}
|
|
17067
|
+
default: return trigger;
|
|
17068
|
+
}
|
|
17069
|
+
}
|
|
17070
|
+
/** An old condition tree as a `when` subtree: `condition` → "is", classes → subjects. */
|
|
17071
|
+
function convertLegacyCondition(condition) {
|
|
17072
|
+
switch (condition.kind) {
|
|
17073
|
+
case "condition": return {
|
|
17074
|
+
...condition,
|
|
17075
|
+
kind: "device-is"
|
|
17076
|
+
};
|
|
17077
|
+
case "stationary-present": {
|
|
17078
|
+
const { classes, ...rest } = condition;
|
|
17079
|
+
return {
|
|
17080
|
+
...rest,
|
|
17081
|
+
...classes === void 0 ? {} : { subjects: classes.map(subjectFromClassName) }
|
|
17082
|
+
};
|
|
17083
|
+
}
|
|
17084
|
+
case "expression": return condition;
|
|
17085
|
+
case "not": return {
|
|
17086
|
+
kind: "not",
|
|
17087
|
+
child: convertLegacyCondition(condition.child)
|
|
17088
|
+
};
|
|
17089
|
+
case "all":
|
|
17090
|
+
case "any": return {
|
|
17091
|
+
kind: condition.kind,
|
|
17092
|
+
children: condition.children.map(convertLegacyCondition)
|
|
17093
|
+
};
|
|
17094
|
+
}
|
|
17095
|
+
}
|
|
17096
|
+
/** `all(any(t1…tn), C)`, or `any(t1…tn)`; a single trigger is not wrapped in `any`. */
|
|
17097
|
+
function convertLegacyWhen(half) {
|
|
17098
|
+
const leaves = half.triggers.map(convertTrigger);
|
|
17099
|
+
const first = leaves[0];
|
|
17100
|
+
const triggers = leaves.length === 1 && first !== void 0 ? first : {
|
|
17101
|
+
kind: "any",
|
|
17102
|
+
children: leaves
|
|
17103
|
+
};
|
|
17104
|
+
if (half.conditions === void 0) return triggers;
|
|
17105
|
+
return {
|
|
17106
|
+
kind: "all",
|
|
17107
|
+
children: [triggers, convertLegacyCondition(half.conditions)]
|
|
17108
|
+
};
|
|
17109
|
+
}
|
|
17110
|
+
/**
|
|
17111
|
+
* The automation RECIPE — what a visually-authored automation is, before it
|
|
17112
|
+
* becomes code.
|
|
17113
|
+
*
|
|
17114
|
+
* This is the data half of `docs/design/2026-08-08-automation-scripts-custom-devices.md`
|
|
17115
|
+
* Phase 6. A recipe is not an engine and never evaluates anything: it is a
|
|
17116
|
+
* declarative description that {@link generateAutomationBlock} compiles to the
|
|
17117
|
+
* TypeScript of an ordinary core block. There is therefore no second rule
|
|
17118
|
+
* engine, no second store and no second runtime — the block runner is the
|
|
17119
|
+
* runtime, `core_blocks` is the store (§2.1).
|
|
17120
|
+
*
|
|
17121
|
+
* Two parts:
|
|
17122
|
+
*
|
|
17123
|
+
* - **`when`** — ONE tree (`./when.ts`) of all/any/not over event leaves
|
|
17124
|
+
* (instants) and state leaves (durations). It replaces the flat trigger list
|
|
17125
|
+
* plus the separate condition tree; the firing rule is in that file and in
|
|
17126
|
+
* ADR "one tree".
|
|
17127
|
+
* - **Actions** — `NcRuleActionSchema`'s two steps, plus the `{kind:'code'}`
|
|
17128
|
+
* fragment of §3.2.2. See {@link AutomationActionSchema} for the ONE place
|
|
17129
|
+
* this had to diverge from "verbatim", and why.
|
|
17130
|
+
*
|
|
17131
|
+
* A recipe stored in the old shape (`triggers` + `conditions`) still LOADS:
|
|
17132
|
+
* {@link AutomationRecipeSchema} (the parse-anywhere schema) converts it on parse. Nothing writes it.
|
|
17133
|
+
*/
|
|
16657
17134
|
/**
|
|
16658
17135
|
* One action step.
|
|
16659
17136
|
*
|
|
@@ -16756,69 +17233,49 @@ var AutomationRunModeSchema = discriminatedUnion("mode", [
|
|
|
16756
17233
|
minutes: number().int().min(1).max(1440)
|
|
16757
17234
|
})
|
|
16758
17235
|
]);
|
|
16759
|
-
var
|
|
16760
|
-
|
|
16761
|
-
conditions: AutomationConditionSchema.optional(),
|
|
17236
|
+
var CurrentAutomationRecipeSchema = object({
|
|
17237
|
+
when: AutomationWhenSchema,
|
|
16762
17238
|
actions: array(AutomationActionSchema),
|
|
16763
17239
|
runMode: AutomationRunModeSchema.optional(),
|
|
16764
17240
|
/** How many run traces the automation keeps (default {@link DEFAULT_TRACE_KEEP}). */
|
|
16765
17241
|
keepTraces: number().int().min(1).max(100).optional()
|
|
16766
17242
|
});
|
|
16767
|
-
|
|
16768
|
-
|
|
16769
|
-
|
|
16770
|
-
|
|
16771
|
-
|
|
16772
|
-
|
|
16773
|
-
|
|
16774
|
-
|
|
16775
|
-
|
|
16776
|
-
|
|
16777
|
-
|
|
16778
|
-
|
|
16779
|
-
|
|
16780
|
-
|
|
16781
|
-
|
|
16782
|
-
|
|
16783
|
-
|
|
16784
|
-
|
|
16785
|
-
|
|
16786
|
-
|
|
16787
|
-
|
|
16788
|
-
|
|
16789
|
-
|
|
16790
|
-
|
|
16791
|
-
|
|
16792
|
-
bus: "vehicle",
|
|
16793
|
-
train: "vehicle",
|
|
16794
|
-
truck: "vehicle",
|
|
16795
|
-
boat: "vehicle",
|
|
16796
|
-
bird: "animal",
|
|
16797
|
-
cat: "animal",
|
|
16798
|
-
dog: "animal",
|
|
16799
|
-
horse: "animal",
|
|
16800
|
-
sheep: "animal",
|
|
16801
|
-
cow: "animal",
|
|
16802
|
-
elephant: "animal",
|
|
16803
|
-
bear: "animal",
|
|
16804
|
-
zebra: "animal",
|
|
16805
|
-
giraffe: "animal",
|
|
16806
|
-
suitcase: "package",
|
|
16807
|
-
backpack: "package",
|
|
16808
|
-
handbag: "package"
|
|
16809
|
-
},
|
|
16810
|
-
preserveOriginal: false
|
|
16811
|
-
};
|
|
17243
|
+
/** A recipe as it was stored before the single tree. Read-only: nothing writes this. */
|
|
17244
|
+
var LegacyAutomationRecipeSchema = object({
|
|
17245
|
+
triggers: array(LegacyTriggerSchema),
|
|
17246
|
+
conditions: LegacyConditionSchema.optional(),
|
|
17247
|
+
actions: array(AutomationActionSchema),
|
|
17248
|
+
runMode: AutomationRunModeSchema.optional(),
|
|
17249
|
+
keepTraces: number().int().min(1).max(100).optional()
|
|
17250
|
+
});
|
|
17251
|
+
/** The old shape as the new one, lossless. */
|
|
17252
|
+
function convertLegacyRecipe(legacy) {
|
|
17253
|
+
const { triggers, conditions, ...rest } = legacy;
|
|
17254
|
+
return {
|
|
17255
|
+
when: convertLegacyWhen({
|
|
17256
|
+
triggers,
|
|
17257
|
+
conditions
|
|
17258
|
+
}),
|
|
17259
|
+
...rest
|
|
17260
|
+
};
|
|
17261
|
+
}
|
|
17262
|
+
/**
|
|
17263
|
+
* Where recipes are PARSED (the core-blocks cap, the MCP tool, the literal a
|
|
17264
|
+
* saved block carries): the new shape as it is, or the old shape converted to
|
|
17265
|
+
* it. The output type is always the new one.
|
|
17266
|
+
*/
|
|
17267
|
+
var AutomationRecipeSchema = union([CurrentAutomationRecipeSchema, LegacyAutomationRecipeSchema.transform(convertLegacyRecipe)]);
|
|
16812
17268
|
new Set(Object.values(COCO_TO_MACRO.mapping));
|
|
16813
17269
|
/**
|
|
16814
17270
|
* Run traces — what an automation (or, later, an Action run) DID, node by node,
|
|
16815
17271
|
* so the editor can paint every block green or red with its reason.
|
|
16816
17272
|
*
|
|
16817
|
-
* A trace is data, not a log: it covers the WHOLE tree — the
|
|
16818
|
-
* every
|
|
16819
|
-
* short-circuit never reached), the run-mode verdict and every action
|
|
16820
|
-
* each addressed by a stable path (`
|
|
16821
|
-
* for an Action run).
|
|
17273
|
+
* A trace is data, not a log: it covers the WHOLE tree — the event leaf that
|
|
17274
|
+
* fired, every node of the `when` tree (containers and leaves, including the
|
|
17275
|
+
* ones a short-circuit never reached), the run-mode verdict and every action
|
|
17276
|
+
* step — each addressed by a stable path (`when.0.1`, `actions.2`, or `steps.N`
|
|
17277
|
+
* for an Action run). Traces written before the single tree carry
|
|
17278
|
+
* `conditions.*` paths and still parse. Skipped
|
|
16822
17279
|
* evaluations are traced too, and identical consecutive skips collapse into a
|
|
16823
17280
|
* repeat count so a trigger that fails every 5 minutes cannot flush real runs.
|
|
16824
17281
|
*
|
|
@@ -16850,6 +17307,8 @@ var TraceNodeOutcomeSchema = _enum([
|
|
|
16850
17307
|
]);
|
|
16851
17308
|
var AutomationTriggerTraceSchema = object({
|
|
16852
17309
|
kind: string().min(1),
|
|
17310
|
+
/** The event leaf that fired (`when`, `when.0.1`); absent on traces written before the single tree. */
|
|
17311
|
+
path: string().min(1).optional(),
|
|
16853
17312
|
outcome: _enum(["pass", "fail"]),
|
|
16854
17313
|
/** Why an event was rejected ("class person not in [dog]") or what fired. */
|
|
16855
17314
|
reason: string(),
|
|
@@ -16857,15 +17316,23 @@ var AutomationTriggerTraceSchema = object({
|
|
|
16857
17316
|
vars: record(string(), TraceValueSchema)
|
|
16858
17317
|
});
|
|
16859
17318
|
var AutomationConditionTraceNodeSchema = object({
|
|
16860
|
-
/** `
|
|
17319
|
+
/** `when` for the root, `when.<i>` for its i-th child, … (`conditions…` on an old trace). */
|
|
16861
17320
|
path: string().min(1),
|
|
16862
17321
|
kind: _enum([
|
|
16863
17322
|
"all",
|
|
16864
17323
|
"any",
|
|
16865
17324
|
"not",
|
|
16866
17325
|
"condition",
|
|
17326
|
+
"device-is",
|
|
16867
17327
|
"expression",
|
|
16868
|
-
"stationary-present"
|
|
17328
|
+
"stationary-present",
|
|
17329
|
+
"device-becomes",
|
|
17330
|
+
"device-event",
|
|
17331
|
+
"schedule",
|
|
17332
|
+
"interval",
|
|
17333
|
+
"manual",
|
|
17334
|
+
"detection",
|
|
17335
|
+
"stationary"
|
|
16869
17336
|
]),
|
|
16870
17337
|
outcome: TraceNodeOutcomeSchema,
|
|
16871
17338
|
reason: string(),
|
|
@@ -16899,6 +17366,12 @@ var AutomationStepTraceSchema = object({
|
|
|
16899
17366
|
deviceId: number().int().optional(),
|
|
16900
17367
|
startedAt: number().optional(),
|
|
16901
17368
|
endedAt: number().optional(),
|
|
17369
|
+
/**
|
|
17370
|
+
* The parameters the step SENT, after `{{vars.x}}` substitution — what the
|
|
17371
|
+
* device was actually asked. Bounded like trigger `vars`; a secret parameter
|
|
17372
|
+
* is shown as {@link REDACTED_ARG}. Absent on traces written before it existed.
|
|
17373
|
+
*/
|
|
17374
|
+
args: record(string(), TraceValueSchema).optional(),
|
|
16902
17375
|
outcome: _enum([
|
|
16903
17376
|
"ok",
|
|
16904
17377
|
"error",
|
|
@@ -17648,6 +18121,23 @@ method(object({
|
|
|
17648
18121
|
addonId: string(),
|
|
17649
18122
|
nodeId: string().optional()
|
|
17650
18123
|
}), SettingsSchemaWithValuesSchema.nullable()), method(object({
|
|
18124
|
+
addonId: string(),
|
|
18125
|
+
nodeId: string().optional()
|
|
18126
|
+
}), object({ keys: array(string()) }).nullable(), { auth: "admin" }), method(object({
|
|
18127
|
+
addonId: string(),
|
|
18128
|
+
nodeId: string().optional(),
|
|
18129
|
+
keys: array(string())
|
|
18130
|
+
}), object({
|
|
18131
|
+
removed: array(string()),
|
|
18132
|
+
refused: array(object({
|
|
18133
|
+
key: string(),
|
|
18134
|
+
reason: string()
|
|
18135
|
+
})),
|
|
18136
|
+
secretsCleared: number()
|
|
18137
|
+
}), {
|
|
18138
|
+
kind: "mutation",
|
|
18139
|
+
auth: "admin"
|
|
18140
|
+
}), method(object({
|
|
17651
18141
|
addonId: string(),
|
|
17652
18142
|
deviceId: number(),
|
|
17653
18143
|
nodeId: string().optional()
|
|
@@ -19083,8 +19573,21 @@ var BrokerProviderInfoSchema = object({
|
|
|
19083
19573
|
* (`{ sessionId }`) drops the session. The dialog that edits a broker
|
|
19084
19574
|
* offers it as the default path; the fields the session fills are
|
|
19085
19575
|
* marked `filledBySignIn` in the kind's settings schema.
|
|
19576
|
+
*
|
|
19577
|
+
* `methods` lists the ways the session can sign in, recommended first:
|
|
19578
|
+
* `'device-code'` adds `POST {basePath}/code/start` (body: the form's
|
|
19579
|
+
* settings) answering `{ sessionId, userCode, verificationUrl,
|
|
19580
|
+
* expiresAt }` — the operator types `userCode` at `verificationUrl` on
|
|
19581
|
+
* any device, and the same `status` / `commit` / `cancel` routes follow
|
|
19582
|
+
* (status then carries `method`, `userCode`, `verificationUrl`,
|
|
19583
|
+
* `expiresAt` and the phases `'awaiting-code'` / `'expired'`);
|
|
19584
|
+
* `'proxy'` is the `{basePath}/start` flow above. Absent means
|
|
19585
|
+
* `['proxy']`.
|
|
19086
19586
|
*/
|
|
19087
|
-
signIn: object({
|
|
19587
|
+
signIn: object({
|
|
19588
|
+
basePath: string().min(1),
|
|
19589
|
+
methods: array(_enum(["device-code", "proxy"])).optional()
|
|
19590
|
+
}).optional()
|
|
19088
19591
|
}))
|
|
19089
19592
|
});
|
|
19090
19593
|
var ListInputSchema = object({
|
|
@@ -21586,6 +22089,8 @@ var StorageKindSchema = object({
|
|
|
21586
22089
|
"orphaned",
|
|
21587
22090
|
"dropped"
|
|
21588
22091
|
]),
|
|
22092
|
+
/** An owner declared this kind since the engine started. */
|
|
22093
|
+
declaredThisBoot: boolean(),
|
|
21589
22094
|
/** `null` when the caller asked for no counts (`counts: false`). */
|
|
21590
22095
|
rows: number().int().nonnegative().nullable(),
|
|
21591
22096
|
/** Rows x the average size of a sample; null for an empty kind. */
|
|
@@ -21597,6 +22102,51 @@ var StorageKindSchema = object({
|
|
|
21597
22102
|
/** The whole declaration, secret paths and entry descriptor included (the entries service builds its handle from it). */
|
|
21598
22103
|
declaration: RecordKindDeclarationSchema
|
|
21599
22104
|
});
|
|
22105
|
+
_enum([
|
|
22106
|
+
"orphaned",
|
|
22107
|
+
"stale",
|
|
22108
|
+
"unknown"
|
|
22109
|
+
]);
|
|
22110
|
+
/** A record kind nobody declared since its owner started, or whose owner is gone. */
|
|
22111
|
+
var StaleKindFindingSchema = object({
|
|
22112
|
+
owner: string(),
|
|
22113
|
+
kind: string(),
|
|
22114
|
+
/** `unknown`: the owner could not be judged (not running, or its state unreadable) - never dropped. */
|
|
22115
|
+
verdict: _enum(["orphaned", "unknown"]),
|
|
22116
|
+
reason: string(),
|
|
22117
|
+
rows: number().int().nonnegative().nullable(),
|
|
22118
|
+
/** local-auth's tables are never dropped by this audit. */
|
|
22119
|
+
protected: boolean()
|
|
22120
|
+
});
|
|
22121
|
+
/** A stored global-settings key the running addon no longer declares. */
|
|
22122
|
+
var StaleSettingsKeyFindingSchema = object({
|
|
22123
|
+
addonId: string(),
|
|
22124
|
+
/** The stored key as written, a per-node `<key>@<nodeId>` suffix included. */
|
|
22125
|
+
key: string(),
|
|
22126
|
+
/**
|
|
22127
|
+
* Keychain refs whose ONLY marker sits under this key: in use by stale data,
|
|
22128
|
+
* not orphans yet. Removing the key releases them.
|
|
22129
|
+
*/
|
|
22130
|
+
secretRefs: array(string())
|
|
22131
|
+
});
|
|
22132
|
+
/** An addon the audit could not judge, with the reason. Never reported as stale. */
|
|
22133
|
+
var StaleUnknownSchema = object({
|
|
22134
|
+
subject: string(),
|
|
22135
|
+
reason: string()
|
|
22136
|
+
});
|
|
22137
|
+
var StaleDataReportSchema = object({
|
|
22138
|
+
/** Epoch ms of the last finished scan; `null` before the first. */
|
|
22139
|
+
scannedAt: number().nullable(),
|
|
22140
|
+
scanning: boolean(),
|
|
22141
|
+
trigger: _enum([
|
|
22142
|
+
"boot",
|
|
22143
|
+
"interval",
|
|
22144
|
+
"manual"
|
|
22145
|
+
]).nullable(),
|
|
22146
|
+
kinds: array(StaleKindFindingSchema),
|
|
22147
|
+
settingsKeys: array(StaleSettingsKeyFindingSchema),
|
|
22148
|
+
unknown: array(StaleUnknownSchema)
|
|
22149
|
+
});
|
|
21600
22150
|
method(object({ ownerPrefix: string().min(1).optional() }), object({ tables: array(StorageTableSchema) }), { auth: "admin" }), method(object({
|
|
21601
22151
|
owner: string().min(1).optional(),
|
|
21602
22152
|
/** `false` skips the per-kind row count (a catalog read only). Default true. */
|
|
@@ -21604,7 +22154,30 @@ method(object({ ownerPrefix: string().min(1).optional() }), object({ tables: arr
|
|
|
21604
22154
|
}), object({ kinds: array(StorageKindSchema) }), { auth: "admin" }), method(RecordQueryInputSchema.extend({
|
|
21605
22155
|
owner: string().min(1),
|
|
21606
22156
|
kind: RecordKindNameSchema
|
|
21607
|
-
}), RecordQueryResultSchema, { auth: "admin" })
|
|
22157
|
+
}), RecordQueryResultSchema, { auth: "admin" }), method(object({}), StaleDataReportSchema, { auth: "admin" }), method(object({}), StaleDataReportSchema, {
|
|
22158
|
+
kind: "mutation",
|
|
22159
|
+
auth: "admin"
|
|
22160
|
+
}), method(object({
|
|
22161
|
+
owner: string().min(1),
|
|
22162
|
+
kind: RecordKindNameSchema
|
|
22163
|
+
}), object({
|
|
22164
|
+
deleted: number().int().nonnegative(),
|
|
22165
|
+
secretsReleased: number().int().nonnegative()
|
|
22166
|
+
}), {
|
|
22167
|
+
kind: "mutation",
|
|
22168
|
+
auth: "admin",
|
|
22169
|
+
caller: "required"
|
|
22170
|
+
}), method(object({
|
|
22171
|
+
addonId: string().min(1),
|
|
22172
|
+
key: string().min(1)
|
|
22173
|
+
}), object({
|
|
22174
|
+
removed: boolean(),
|
|
22175
|
+
secretsCleared: number().int().nonnegative()
|
|
22176
|
+
}), {
|
|
22177
|
+
kind: "mutation",
|
|
22178
|
+
auth: "admin",
|
|
22179
|
+
caller: "required"
|
|
22180
|
+
});
|
|
21608
22181
|
/**
|
|
21609
22182
|
* record-entries - the ONE admin surface for every list of user-created entries
|
|
21610
22183
|
* (D881): notification targets, brokers, LLM profiles, anything an addon
|
|
@@ -21743,6 +22316,12 @@ method(object({
|
|
|
21743
22316
|
}), method(object({ deviceId: number().int() }), object({ refs: array(string()).readonly() }), {
|
|
21744
22317
|
kind: "mutation",
|
|
21745
22318
|
auth: "admin"
|
|
22319
|
+
}), method(object({
|
|
22320
|
+
refs: RefListSchema,
|
|
22321
|
+
onBehalfOf: string().min(1)
|
|
22322
|
+
}), object({ released: number().int() }), {
|
|
22323
|
+
kind: "mutation",
|
|
22324
|
+
auth: "admin"
|
|
21746
22325
|
}), method(object({}), array(KeychainSecretMetaSchema).readonly(), { auth: "admin" }), method(object({}), KeychainStateSchema, { auth: "admin" });
|
|
21747
22326
|
/**
|
|
21748
22327
|
* device-history — the ONE mechanism for numeric history (D850).
|
|
@@ -22532,7 +23111,9 @@ var DiscoveredChildDeviceSchema = lazy(() => object({
|
|
|
22532
23111
|
/** Upstream-system identity + rendering hints for the candidate. */
|
|
22533
23112
|
sourceInfo: SourceInfoSchema.optional(),
|
|
22534
23113
|
/** Nested entity-children for accordion preview — integrations populate, Hubs omit. */
|
|
22535
|
-
children: array(DiscoveredChildDeviceSchema).readonly().optional()
|
|
23114
|
+
children: array(DiscoveredChildDeviceSchema).readonly().optional(),
|
|
23115
|
+
/** `childNativeId` of the gateway candidate in the same list this device sits behind. */
|
|
23116
|
+
parentStableId: string().optional()
|
|
22536
23117
|
}));
|
|
22537
23118
|
/**
|
|
22538
23119
|
* Status block — populated by the integration after each
|
|
@@ -42847,6 +43428,79 @@ object({
|
|
|
42847
43428
|
metadata: record(string(), unknown()).optional()
|
|
42848
43429
|
});
|
|
42849
43430
|
/**
|
|
43431
|
+
* HOW a provider can be found on the network. The provider only DECLARES the
|
|
43432
|
+
* mode; the `local-network` scan engine runs it once for every provider that
|
|
43433
|
+
* asked (one multicast browse, one host sweep) and hands the raw hits back to
|
|
43434
|
+
* `classifyDiscoveryHits`. A provider never opens a discovery socket itself,
|
|
43435
|
+
* except through a `custom` mode.
|
|
43436
|
+
*/
|
|
43437
|
+
var DiscoveryModeSchema = discriminatedUnion("kind", [
|
|
43438
|
+
object({
|
|
43439
|
+
kind: literal("mdns"),
|
|
43440
|
+
/** DNS-SD service type, e.g. `_esphomelib._tcp`. */
|
|
43441
|
+
serviceType: string().min(1),
|
|
43442
|
+
/** Regular expression an instance name must match (case-insensitive). */
|
|
43443
|
+
instancePattern: string().optional()
|
|
43444
|
+
}),
|
|
43445
|
+
object({
|
|
43446
|
+
kind: literal("ws-discovery"),
|
|
43447
|
+
/** Probe types (e.g. `dn:NetworkVideoTransmitter`); omitted = any device. */
|
|
43448
|
+
types: array(string()).optional()
|
|
43449
|
+
}),
|
|
43450
|
+
object({
|
|
43451
|
+
kind: literal("ssdp"),
|
|
43452
|
+
searchTarget: string().min(1)
|
|
43453
|
+
}),
|
|
43454
|
+
object({
|
|
43455
|
+
kind: literal("udp-broadcast"),
|
|
43456
|
+
port: number().int().min(1).max(65535),
|
|
43457
|
+
payloadHex: string().regex(/^([0-9a-fA-F]{2})*$/),
|
|
43458
|
+
/** Port replies arrive on, when the device answers somewhere other than the source port. */
|
|
43459
|
+
listenPort: number().int().min(1).max(65535).optional()
|
|
43460
|
+
}),
|
|
43461
|
+
object({
|
|
43462
|
+
kind: literal("http-probe"),
|
|
43463
|
+
port: number().int().min(1).max(65535),
|
|
43464
|
+
path: string().startsWith("/"),
|
|
43465
|
+
method: _enum(["GET", "POST"]).default("GET"),
|
|
43466
|
+
bodyText: string().optional(),
|
|
43467
|
+
contentType: string().optional(),
|
|
43468
|
+
tls: boolean().default(false),
|
|
43469
|
+
/** Status codes that count as a hit. Omitted = any HTTP answer. */
|
|
43470
|
+
acceptStatus: array(number().int()).optional()
|
|
43471
|
+
}),
|
|
43472
|
+
object({
|
|
43473
|
+
kind: literal("custom"),
|
|
43474
|
+
/** Why the engine cannot do it (an allowlisted exception, e.g. a vendor UDP protocol). */
|
|
43475
|
+
reason: string().min(1)
|
|
43476
|
+
})
|
|
43477
|
+
]);
|
|
43478
|
+
/** What the engine saw on the wire for ONE declared mode; the provider classifies it. */
|
|
43479
|
+
var DiscoveryHitSchema = object({
|
|
43480
|
+
/** Index into the provider's own `getDiscoveryModes()` array. */
|
|
43481
|
+
modeIndex: number().int().min(0),
|
|
43482
|
+
host: string(),
|
|
43483
|
+
port: number().int(),
|
|
43484
|
+
http: object({
|
|
43485
|
+
status: number().int(),
|
|
43486
|
+
headers: record(string(), string()),
|
|
43487
|
+
/** At most 16 KiB. */
|
|
43488
|
+
bodyText: string()
|
|
43489
|
+
}).optional(),
|
|
43490
|
+
mdns: object({
|
|
43491
|
+
instance: string(),
|
|
43492
|
+
host: string(),
|
|
43493
|
+
port: number().int(),
|
|
43494
|
+
txt: record(string(), string())
|
|
43495
|
+
}).optional(),
|
|
43496
|
+
udp: object({ payloadHex: string() }).optional(),
|
|
43497
|
+
wsd: object({
|
|
43498
|
+
xaddrs: array(string()),
|
|
43499
|
+
types: array(string()),
|
|
43500
|
+
scopes: array(string())
|
|
43501
|
+
}).optional()
|
|
43502
|
+
});
|
|
43503
|
+
/**
|
|
42850
43504
|
* Candidate handed back from discovery and accepted by
|
|
42851
43505
|
* `adoptDiscoveredDevice`. Shape mirrors the in-process
|
|
42852
43506
|
* `DiscoveredDevice` interface used by `DeviceDiscovery`.
|
|
@@ -42881,7 +43535,17 @@ var DiscoveryCandidateSchema = object({
|
|
|
42881
43535
|
/** Numeric id of the device this candidate was matched to. Set with `alreadyOnboarded`. */
|
|
42882
43536
|
onboardedDeviceId: number().optional(),
|
|
42883
43537
|
/** Operator-facing name of the matched device, so the UI can say WHICH one it is. */
|
|
42884
|
-
onboardedName: string().optional()
|
|
43538
|
+
onboardedName: string().optional(),
|
|
43539
|
+
/**
|
|
43540
|
+
* This candidate is an INTEGRATION to set up (a gateway, a hub, a controller),
|
|
43541
|
+
* not a device to adopt. Replaces the `prefilledConfig.setupIntegration` marker,
|
|
43542
|
+
* which is still read for one release.
|
|
43543
|
+
*/
|
|
43544
|
+
setupIntegration: boolean().optional(),
|
|
43545
|
+
/** `stableId` of the gateway candidate this device was found BEHIND. */
|
|
43546
|
+
parentStableId: string().optional(),
|
|
43547
|
+
/** `false` = shown for context but not pickable. Default true. */
|
|
43548
|
+
selectable: boolean().optional()
|
|
42885
43549
|
});
|
|
42886
43550
|
/**
|
|
42887
43551
|
* Flat device summary returned by `createDevice` / `adoptDiscoveredDevice`.
|
|
@@ -42934,6 +43598,12 @@ method(_void(), _void(), { kind: "mutation" }), method(_void(), _void(), { kind:
|
|
|
42934
43598
|
}), method(object({}), boolean()), method(object({ params: record(string(), unknown()).optional() }), array(DiscoveryCandidateSchema), {
|
|
42935
43599
|
kind: "mutation",
|
|
42936
43600
|
auth: "admin"
|
|
43601
|
+
}), method(object({}), array(DiscoveryModeSchema)), method(object({
|
|
43602
|
+
hits: array(DiscoveryHitSchema),
|
|
43603
|
+
params: record(string(), unknown()).optional()
|
|
43604
|
+
}), array(DiscoveryCandidateSchema), {
|
|
43605
|
+
kind: "mutation",
|
|
43606
|
+
auth: "admin"
|
|
42937
43607
|
}), method(object({}), CreationSchemaOutputSchema), method(object({}), object({ deviceType: _enum(DeviceType).nullable() })), method(object({ candidate: DiscoveryCandidateSchema }), DeviceSummarySchema, {
|
|
42938
43608
|
kind: "mutation",
|
|
42939
43609
|
auth: "admin"
|
|
@@ -43661,17 +44331,23 @@ method(object({
|
|
|
43661
44331
|
}), method(object({}), object({ providers: array(object({
|
|
43662
44332
|
addonId: string(),
|
|
43663
44333
|
label: string()
|
|
43664
|
-
})).readonly() }), { auth: "admin" }), method(
|
|
43665
|
-
|
|
43666
|
-
|
|
43667
|
-
|
|
43668
|
-
|
|
43669
|
-
|
|
43670
|
-
|
|
43671
|
-
|
|
43672
|
-
}),
|
|
44334
|
+
})).readonly() }), { auth: "admin" }), method(
|
|
44335
|
+
/** `subnets`: extra CIDRs for THIS scan, on top of the hub's own and the saved list. */
|
|
44336
|
+
object({ subnets: array(string()).optional() }),
|
|
44337
|
+
object({ groups: array(object({
|
|
44338
|
+
addonId: string(),
|
|
44339
|
+
label: string(),
|
|
44340
|
+
candidates: array(DiscoveryCandidateSchema).readonly(),
|
|
44341
|
+
error: string().nullable()
|
|
44342
|
+
})).readonly() }),
|
|
44343
|
+
{
|
|
44344
|
+
kind: "mutation",
|
|
44345
|
+
auth: "admin"
|
|
44346
|
+
}
|
|
44347
|
+
), method(object({
|
|
43673
44348
|
addonId: string(),
|
|
43674
|
-
params: record(string(), unknown()).optional()
|
|
44349
|
+
params: record(string(), unknown()).optional(),
|
|
44350
|
+
subnets: array(string()).optional()
|
|
43675
44351
|
}), object({ candidates: array(DiscoveryCandidateSchema).readonly() }), {
|
|
43676
44352
|
kind: "mutation",
|
|
43677
44353
|
auth: "admin"
|
|
@@ -61297,7 +61973,9 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
|
|
|
61297
61973
|
id: string(),
|
|
61298
61974
|
settings: record(string(), unknown()),
|
|
61299
61975
|
/** Keys the form's schema declares secret (by type) — keychain-stored (D787). */
|
|
61300
|
-
secretKeys: array(string()).optional()
|
|
61976
|
+
secretKeys: array(string()).optional(),
|
|
61977
|
+
/** The integration's own addon applies the change in-process and asks the hub not to respawn it. */
|
|
61978
|
+
skipRestart: boolean().optional()
|
|
61301
61979
|
}), object({ success: boolean() }), {
|
|
61302
61980
|
kind: "mutation",
|
|
61303
61981
|
auth: "admin"
|
|
@@ -61774,6 +62452,39 @@ var TlsStatusSchema = object({
|
|
|
61774
62452
|
hasCa: boolean()
|
|
61775
62453
|
}).nullable()
|
|
61776
62454
|
});
|
|
62455
|
+
/** One provider's ask of the scan engine: the modes it declared. */
|
|
62456
|
+
var ScanRequestSchema = object({
|
|
62457
|
+
providerId: string().min(1),
|
|
62458
|
+
modes: array(DiscoveryModeSchema)
|
|
62459
|
+
});
|
|
62460
|
+
var ScanErrorSchema = object({
|
|
62461
|
+
/** Absent for a failure that is the scan's own (a bad subnet, a socket that would not open). */
|
|
62462
|
+
providerId: string().optional(),
|
|
62463
|
+
/** The mode family that failed (`mdns`, `http-probe`, …). */
|
|
62464
|
+
mode: string().optional(),
|
|
62465
|
+
message: string()
|
|
62466
|
+
});
|
|
62467
|
+
/** What one scan did, per mode family; the engine logs the same numbers once. */
|
|
62468
|
+
var ScanStatsSchema = object({
|
|
62469
|
+
hostsProbed: number().int(),
|
|
62470
|
+
hits: number().int(),
|
|
62471
|
+
timeouts: number().int(),
|
|
62472
|
+
byMode: record(string(), object({
|
|
62473
|
+
probed: number().int(),
|
|
62474
|
+
hits: number().int(),
|
|
62475
|
+
timeouts: number().int()
|
|
62476
|
+
}))
|
|
62477
|
+
});
|
|
62478
|
+
var ScanNetworkResultSchema = object({
|
|
62479
|
+
hits: array(object({
|
|
62480
|
+
providerId: string(),
|
|
62481
|
+
hit: DiscoveryHitSchema
|
|
62482
|
+
})),
|
|
62483
|
+
/** CIDRs actually covered: the hub's own /24s, the saved list and the per-call list. */
|
|
62484
|
+
subnetsScanned: array(string()),
|
|
62485
|
+
errors: array(ScanErrorSchema),
|
|
62486
|
+
stats: ScanStatsSchema
|
|
62487
|
+
});
|
|
61777
62488
|
method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
|
|
61778
62489
|
/**
|
|
61779
62490
|
* LEGACY HINT — do not send from new code. Kept optional so clients
|
|
@@ -61828,6 +62539,16 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
|
|
|
61828
62539
|
}), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
|
|
61829
62540
|
kind: "mutation",
|
|
61830
62541
|
auth: "admin"
|
|
62542
|
+
}), method(object({
|
|
62543
|
+
requests: array(ScanRequestSchema),
|
|
62544
|
+
subnets: array(string()).optional()
|
|
62545
|
+
}), ScanNetworkResultSchema, {
|
|
62546
|
+
kind: "mutation",
|
|
62547
|
+
auth: "admin",
|
|
62548
|
+
timeoutMs: 9e4
|
|
62549
|
+
}), method(_void(), object({ subnets: array(string()) }), { auth: "admin" }), method(object({ subnets: array(string()) }), object({ subnets: array(string()) }), {
|
|
62550
|
+
kind: "mutation",
|
|
62551
|
+
auth: "admin"
|
|
61831
62552
|
}), method(_void(), TlsStatusSchema, {
|
|
61832
62553
|
kind: "mutation",
|
|
61833
62554
|
auth: "admin"
|
|
@@ -64370,7 +65091,7 @@ object({
|
|
|
64370
65091
|
});
|
|
64371
65092
|
Object.values(DeviceType);
|
|
64372
65093
|
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
|
|
64373
|
-
DeviceType.Camera, DeviceType.Sensor, DeviceType.Switch, method(object({ deviceId: number().int().nonnegative() }), object({ success: literal(true) }), {
|
|
65094
|
+
DeviceType.Camera, DeviceType.Sensor, DeviceType.Switch, DeviceType.Gateway, method(object({ deviceId: number().int().nonnegative() }), object({ success: literal(true) }), {
|
|
64374
65095
|
kind: "mutation",
|
|
64375
65096
|
auth: "admin"
|
|
64376
65097
|
});
|
|
@@ -68228,7 +68949,7 @@ discriminatedUnion("kind", [object({
|
|
|
68228
68949
|
})]);
|
|
68229
68950
|
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
68230
68951
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
68231
|
-
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;
|
|
68952
|
+
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;
|
|
68232
68953
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
68233
68954
|
DeviceRole.BinarySensor, DeviceRole.MotionSensor;
|
|
68234
68955
|
new Set(CAP_NAMES);
|
|
@@ -68379,7 +69100,7 @@ var AccessoryKind = {
|
|
|
68379
69100
|
PrivacyMask: DeviceRole.PrivacyMask
|
|
68380
69101
|
};
|
|
68381
69102
|
AccessoryKind.Siren, AccessoryKind.Floodlight, AccessoryKind.Spotlight, AccessoryKind.PirSensor, AccessoryKind.Chime, AccessoryKind.Autotrack, AccessoryKind.Nightvision, AccessoryKind.PrivacyMask;
|
|
68382
|
-
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;
|
|
69103
|
+
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;
|
|
68383
69104
|
new Set(Object.values(DeviceType));
|
|
68384
69105
|
Object.freeze({
|
|
68385
69106
|
"accessories.getStatus": {
|
|
@@ -68767,6 +69488,13 @@ Object.freeze({
|
|
|
68767
69488
|
access: "create",
|
|
68768
69489
|
role: "admin"
|
|
68769
69490
|
},
|
|
69491
|
+
"addonSettings.getDeclaredSettingsKeys": {
|
|
69492
|
+
capName: "addon-settings",
|
|
69493
|
+
capScope: "system",
|
|
69494
|
+
addonId: null,
|
|
69495
|
+
access: "view",
|
|
69496
|
+
role: "admin"
|
|
69497
|
+
},
|
|
68770
69498
|
"addonSettings.getDeviceSettings": {
|
|
68771
69499
|
capName: "addon-settings",
|
|
68772
69500
|
capScope: "system",
|
|
@@ -68788,6 +69516,13 @@ Object.freeze({
|
|
|
68788
69516
|
access: "view",
|
|
68789
69517
|
role: "admin"
|
|
68790
69518
|
},
|
|
69519
|
+
"addonSettings.unsetStoredSettingsKeys": {
|
|
69520
|
+
capName: "addon-settings",
|
|
69521
|
+
capScope: "system",
|
|
69522
|
+
addonId: null,
|
|
69523
|
+
access: "create",
|
|
69524
|
+
role: "admin"
|
|
69525
|
+
},
|
|
68791
69526
|
"addonSettings.updateDeviceSettings": {
|
|
68792
69527
|
capName: "addon-settings",
|
|
68793
69528
|
capScope: "system",
|
|
@@ -71203,6 +71938,13 @@ Object.freeze({
|
|
|
71203
71938
|
access: "create",
|
|
71204
71939
|
role: "admin"
|
|
71205
71940
|
},
|
|
71941
|
+
"deviceProvider.classifyDiscoveryHits": {
|
|
71942
|
+
capName: "device-provider",
|
|
71943
|
+
capScope: "system",
|
|
71944
|
+
addonId: null,
|
|
71945
|
+
access: "create",
|
|
71946
|
+
role: "admin"
|
|
71947
|
+
},
|
|
71206
71948
|
"deviceProvider.createDevice": {
|
|
71207
71949
|
capName: "device-provider",
|
|
71208
71950
|
capScope: "system",
|
|
@@ -71231,6 +71973,13 @@ Object.freeze({
|
|
|
71231
71973
|
access: "view",
|
|
71232
71974
|
role: "admin"
|
|
71233
71975
|
},
|
|
71976
|
+
"deviceProvider.getDiscoveryModes": {
|
|
71977
|
+
capName: "device-provider",
|
|
71978
|
+
capScope: "system",
|
|
71979
|
+
addonId: null,
|
|
71980
|
+
access: "view",
|
|
71981
|
+
role: "admin"
|
|
71982
|
+
},
|
|
71234
71983
|
"deviceProvider.getDiscoveryParamsSchema": {
|
|
71235
71984
|
capName: "device-provider",
|
|
71236
71985
|
capScope: "system",
|
|
@@ -72008,6 +72757,13 @@ Object.freeze({
|
|
|
72008
72757
|
access: "create",
|
|
72009
72758
|
role: "admin"
|
|
72010
72759
|
},
|
|
72760
|
+
"keychain.releaseRecordRefs": {
|
|
72761
|
+
capName: "keychain",
|
|
72762
|
+
capScope: "system",
|
|
72763
|
+
addonId: null,
|
|
72764
|
+
access: "create",
|
|
72765
|
+
role: "admin"
|
|
72766
|
+
},
|
|
72011
72767
|
"keychain.resolve": {
|
|
72012
72768
|
capName: "keychain",
|
|
72013
72769
|
capScope: "system",
|
|
@@ -72428,6 +73184,13 @@ Object.freeze({
|
|
|
72428
73184
|
access: "view",
|
|
72429
73185
|
role: "admin"
|
|
72430
73186
|
},
|
|
73187
|
+
"localNetwork.getScanSubnets": {
|
|
73188
|
+
capName: "local-network",
|
|
73189
|
+
capScope: "system",
|
|
73190
|
+
addonId: null,
|
|
73191
|
+
access: "view",
|
|
73192
|
+
role: "admin"
|
|
73193
|
+
},
|
|
72431
73194
|
"localNetwork.getTlsStatus": {
|
|
72432
73195
|
capName: "local-network",
|
|
72433
73196
|
capScope: "system",
|
|
@@ -72477,6 +73240,13 @@ Object.freeze({
|
|
|
72477
73240
|
access: "create",
|
|
72478
73241
|
role: "admin"
|
|
72479
73242
|
},
|
|
73243
|
+
"localNetwork.scanNetwork": {
|
|
73244
|
+
capName: "local-network",
|
|
73245
|
+
capScope: "system",
|
|
73246
|
+
addonId: null,
|
|
73247
|
+
access: "create",
|
|
73248
|
+
role: "admin"
|
|
73249
|
+
},
|
|
72480
73250
|
"localNetwork.setAddressRole": {
|
|
72481
73251
|
capName: "local-network",
|
|
72482
73252
|
capScope: "system",
|
|
@@ -72498,6 +73268,13 @@ Object.freeze({
|
|
|
72498
73268
|
access: "create",
|
|
72499
73269
|
role: "admin"
|
|
72500
73270
|
},
|
|
73271
|
+
"localNetwork.setScanSubnets": {
|
|
73272
|
+
capName: "local-network",
|
|
73273
|
+
capScope: "system",
|
|
73274
|
+
addonId: null,
|
|
73275
|
+
access: "create",
|
|
73276
|
+
role: "admin"
|
|
73277
|
+
},
|
|
72501
73278
|
"localNetwork.setViewerEndpoints": {
|
|
72502
73279
|
capName: "local-network",
|
|
72503
73280
|
capScope: "system",
|
|
@@ -76719,6 +77496,13 @@ Object.freeze({
|
|
|
76719
77496
|
access: "create",
|
|
76720
77497
|
role: "admin"
|
|
76721
77498
|
},
|
|
77499
|
+
"storageAdmin.dropOrphanedKind": {
|
|
77500
|
+
capName: "storage-admin",
|
|
77501
|
+
capScope: "system",
|
|
77502
|
+
addonId: null,
|
|
77503
|
+
access: "delete",
|
|
77504
|
+
role: "admin"
|
|
77505
|
+
},
|
|
76722
77506
|
"storageAdmin.listKinds": {
|
|
76723
77507
|
capName: "storage-admin",
|
|
76724
77508
|
capScope: "system",
|
|
@@ -76740,6 +77524,27 @@ Object.freeze({
|
|
|
76740
77524
|
access: "view",
|
|
76741
77525
|
role: "admin"
|
|
76742
77526
|
},
|
|
77527
|
+
"storageAdmin.scanStaleData": {
|
|
77528
|
+
capName: "storage-admin",
|
|
77529
|
+
capScope: "system",
|
|
77530
|
+
addonId: null,
|
|
77531
|
+
access: "create",
|
|
77532
|
+
role: "admin"
|
|
77533
|
+
},
|
|
77534
|
+
"storageAdmin.staleDataReport": {
|
|
77535
|
+
capName: "storage-admin",
|
|
77536
|
+
capScope: "system",
|
|
77537
|
+
addonId: null,
|
|
77538
|
+
access: "view",
|
|
77539
|
+
role: "admin"
|
|
77540
|
+
},
|
|
77541
|
+
"storageAdmin.unsetStaleSettingsKey": {
|
|
77542
|
+
capName: "storage-admin",
|
|
77543
|
+
capScope: "system",
|
|
77544
|
+
addonId: null,
|
|
77545
|
+
access: "create",
|
|
77546
|
+
role: "admin"
|
|
77547
|
+
},
|
|
76743
77548
|
"storageEvictable.evict": {
|
|
76744
77549
|
capName: "storage-evictable",
|
|
76745
77550
|
capScope: "system",
|
|
@@ -82436,7 +83241,7 @@ function projectAction(cap, action) {
|
|
|
82436
83241
|
};
|
|
82437
83242
|
}
|
|
82438
83243
|
Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
|
|
82439
|
-
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;
|
|
83244
|
+
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;
|
|
82440
83245
|
DeviceFeature.BatteryOperated;
|
|
82441
83246
|
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
82442
83247
|
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.";
|