@camstack/addon-training 0.1.111 → 0.1.113
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-D4s4e-dn.mjs → MotionZonesSettings-Fo1pD1Rt.mjs} +2 -2
- package/dist/{PrivacyMaskSettings-BtdwrmHQ.mjs → PrivacyMaskSettings-CxJF-sR6.mjs} +2 -2
- package/dist/{SceneMonitorEditor-DOX6WQaZ.mjs → SceneMonitorEditor-BVjgmQ6j.mjs} +2 -2
- package/dist/_stub.js +6084 -6049
- package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_training_page-wgqnvZam.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_training_page-Bya5bIs3.mjs} +3 -3
- package/dist/_virtual_mf___mfe_internal__addon_training_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CMfDB80q.mjs +26 -0
- package/dist/{dist-BY4SxNbQ.mjs → dist-B7trMqj7.mjs} +927 -122
- package/dist/{dist-BsIozugK.js → dist-j2JtuQ21.js} +927 -122
- package/dist/{hostInit-COVVezu5.mjs → hostInit-7-7XYgjM.mjs} +2 -2
- package/dist/{icons-tE-y9KlM.mjs → icons-BAJBs2k2.mjs} +1124 -1153
- package/dist/index.js +1 -1
- package/dist/orchestrator/training.addon.js +1 -1
- package/dist/orchestrator/training.addon.mjs +1 -1
- package/dist/{player-overlays-C_B-yc3g.mjs → player-overlays-DVT_LAXt.mjs} +1 -1
- package/dist/providers/modal/training-modal.addon.js +1 -1
- package/dist/providers/modal/training-modal.addon.mjs +1 -1
- package/dist/remoteEntry.js +1 -1
- package/dist/{responsive-YANRyril.mjs → responsive-DhFXZ4Mn.mjs} +1 -1
- package/dist/{scene-monitor-copy-B0w92y1z.mjs → scene-monitor-copy-Busxg-ds.mjs} +1 -1
- package/dist/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_training_page__remoteEntry_js-Xpg6ATFx.mjs → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_training_page__remoteEntry_js-DlmMJ8_m.mjs} +1 -1
- package/dist/{zoom-out-CWDOvUsr.mjs → zoom-out-Bc8fFbXe.mjs} +88 -59
- package/package.json +1 -1
- package/dist/_virtual_mf___mfe_internal__addon_training_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CmVZHQ-k.mjs +0 -26
|
@@ -5621,7 +5621,7 @@ var ZodIssueCode = {
|
|
|
5621
5621
|
var ZodFirstPartyTypeKind;
|
|
5622
5622
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5623
5623
|
//#endregion
|
|
5624
|
-
//#region ../types/dist/sleep-
|
|
5624
|
+
//#region ../types/dist/sleep-CeyhrcOk.mjs
|
|
5625
5625
|
/**
|
|
5626
5626
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5627
5627
|
* window to float samples (D455).
|
|
@@ -7688,6 +7688,7 @@ var BaseAddon = class {
|
|
|
7688
7688
|
* a warning and falls back rather than crashing boot.
|
|
7689
7689
|
*/
|
|
7690
7690
|
state(key, schema, fallback) {
|
|
7691
|
+
this._declaredStateKeys.add(key);
|
|
7691
7692
|
return createDurableState({
|
|
7692
7693
|
key,
|
|
7693
7694
|
schema,
|
|
@@ -7703,6 +7704,96 @@ var BaseAddon = class {
|
|
|
7703
7704
|
} })
|
|
7704
7705
|
});
|
|
7705
7706
|
}
|
|
7707
|
+
/** Durable-state keys this process registered through {@link state}. */
|
|
7708
|
+
_declaredStateKeys = /* @__PURE__ */ new Set();
|
|
7709
|
+
/**
|
|
7710
|
+
* Stale-data audit: every global-settings key this addon still declares —
|
|
7711
|
+
* constructor defaults, settings-schema fields (layout containers included),
|
|
7712
|
+
* durable-state keys and the root of each `secretStatePaths` entry. Answered
|
|
7713
|
+
* here, in the addon's own process, so the hub keeps no copy of it.
|
|
7714
|
+
*/
|
|
7715
|
+
async getDeclaredSettingsKeys() {
|
|
7716
|
+
return { keys: [...this.declaredKeySet()].toSorted() };
|
|
7717
|
+
}
|
|
7718
|
+
declaredKeySet() {
|
|
7719
|
+
const keys = new Set(Object.keys(this.defaults));
|
|
7720
|
+
const schema = this.globalSettingsSchema();
|
|
7721
|
+
if (schema) for (const section of schema.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
7722
|
+
for (const key of this._declaredStateKeys) keys.add(key);
|
|
7723
|
+
for (const path of this.secretStatePaths) {
|
|
7724
|
+
const root = path.split(/[.[]/, 1)[0];
|
|
7725
|
+
if (root) keys.add(root);
|
|
7726
|
+
}
|
|
7727
|
+
return keys;
|
|
7728
|
+
}
|
|
7729
|
+
/**
|
|
7730
|
+
* Stale-data audit: remove stored global-settings keys the addon no longer
|
|
7731
|
+
* declares, and clear the keychain values their markers held — except a value
|
|
7732
|
+
* a surviving key still points at. A declared or absent key is refused.
|
|
7733
|
+
*/
|
|
7734
|
+
async unsetStoredSettingsKeys(keys) {
|
|
7735
|
+
const settings = this._ctx?.settings;
|
|
7736
|
+
const raw = this.rawSettings();
|
|
7737
|
+
if (!settings || !raw) return {
|
|
7738
|
+
removed: [],
|
|
7739
|
+
refused: keys.map((key) => ({
|
|
7740
|
+
key,
|
|
7741
|
+
reason: "no settings store"
|
|
7742
|
+
})),
|
|
7743
|
+
secretsCleared: 0
|
|
7744
|
+
};
|
|
7745
|
+
let stored;
|
|
7746
|
+
try {
|
|
7747
|
+
stored = await raw.readAddonStore();
|
|
7748
|
+
} catch (error) {
|
|
7749
|
+
const reason = `store unreadable: ${error instanceof Error ? error.message : "unknown"}`;
|
|
7750
|
+
return {
|
|
7751
|
+
removed: [],
|
|
7752
|
+
refused: keys.map((key) => ({
|
|
7753
|
+
key,
|
|
7754
|
+
reason
|
|
7755
|
+
})),
|
|
7756
|
+
secretsCleared: 0
|
|
7757
|
+
};
|
|
7758
|
+
}
|
|
7759
|
+
const declared = this.declaredKeySet();
|
|
7760
|
+
const removed = [];
|
|
7761
|
+
const refused = [];
|
|
7762
|
+
for (const key of keys) {
|
|
7763
|
+
const base = key.split("@", 1)[0] ?? key;
|
|
7764
|
+
if (declared.has(base)) refused.push({
|
|
7765
|
+
key,
|
|
7766
|
+
reason: "still declared by the addon"
|
|
7767
|
+
});
|
|
7768
|
+
else if (!(key in stored)) refused.push({
|
|
7769
|
+
key,
|
|
7770
|
+
reason: "not stored"
|
|
7771
|
+
});
|
|
7772
|
+
else removed.push(key);
|
|
7773
|
+
}
|
|
7774
|
+
if (removed.length === 0) return {
|
|
7775
|
+
removed,
|
|
7776
|
+
refused,
|
|
7777
|
+
secretsCleared: 0
|
|
7778
|
+
};
|
|
7779
|
+
const kept = /* @__PURE__ */ new Set();
|
|
7780
|
+
const held = /* @__PURE__ */ new Set();
|
|
7781
|
+
for (const [key, value] of Object.entries(stored)) {
|
|
7782
|
+
const target = removed.includes(key) ? held : kept;
|
|
7783
|
+
for (const ref of collectSecretMarkerRefs(value)) target.add(ref);
|
|
7784
|
+
}
|
|
7785
|
+
const refsToClear = [...held].filter((ref) => !kept.has(ref));
|
|
7786
|
+
const patch = {};
|
|
7787
|
+
for (const key of removed) patch[key] = void 0;
|
|
7788
|
+
await settings.writeAddonStore(patch);
|
|
7789
|
+
const secretsCleared = refsToClear.length > 0 && this._ctx?.secrets ? await this._ctx.secrets.delete(refsToClear) : 0;
|
|
7790
|
+
await this.resolveConfig();
|
|
7791
|
+
return {
|
|
7792
|
+
removed,
|
|
7793
|
+
refused,
|
|
7794
|
+
secretsCleared
|
|
7795
|
+
};
|
|
7796
|
+
}
|
|
7706
7797
|
/** Per-device variant of {@link state}, backed by the per-device store. */
|
|
7707
7798
|
deviceState(deviceId, key, schema, fallback) {
|
|
7708
7799
|
return createDurableState({
|
|
@@ -7973,6 +8064,21 @@ function dropPerNodeFields(fields) {
|
|
|
7973
8064
|
}
|
|
7974
8065
|
return kept;
|
|
7975
8066
|
}
|
|
8067
|
+
function collectFieldKeys(fields) {
|
|
8068
|
+
const collected = [];
|
|
8069
|
+
for (const field of fields) {
|
|
8070
|
+
if (field.type === "group") {
|
|
8071
|
+
collected.push(...collectFieldKeys(field.fields));
|
|
8072
|
+
continue;
|
|
8073
|
+
}
|
|
8074
|
+
if (field.type === "sub-tabs") {
|
|
8075
|
+
for (const tab of field.tabs) collected.push(...collectFieldKeys(tab.fields));
|
|
8076
|
+
continue;
|
|
8077
|
+
}
|
|
8078
|
+
if ("key" in field && typeof field.key === "string") collected.push(field.key);
|
|
8079
|
+
}
|
|
8080
|
+
return collected;
|
|
8081
|
+
}
|
|
7976
8082
|
function collectPerNodeFieldKeys(fields) {
|
|
7977
8083
|
const collected = [];
|
|
7978
8084
|
for (const field of fields) {
|
|
@@ -10995,7 +11101,7 @@ object({
|
|
|
10995
11101
|
params: record(string(), union([string(), number()])).optional()
|
|
10996
11102
|
});
|
|
10997
11103
|
//#endregion
|
|
10998
|
-
//#region ../types/dist/err-msg-
|
|
11104
|
+
//#region ../types/dist/err-msg-BD5buxvT.mjs
|
|
10999
11105
|
var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
11000
11106
|
DeviceType["Camera"] = "camera";
|
|
11001
11107
|
DeviceType["Hub"] = "hub";
|
|
@@ -11101,6 +11207,12 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
|
11101
11207
|
DeviceType["Washer"] = "washer";
|
|
11102
11208
|
/** Tumble dryer. Same plug-built running surface as {@link Washer}. */
|
|
11103
11209
|
DeviceType["Dryer"] = "dryer";
|
|
11210
|
+
/** The physical bridge box of a vendor ecosystem (Velux KLF-200, and later
|
|
11211
|
+
* other radio-to-IP bridges). It carries the box's own facts (firmware,
|
|
11212
|
+
* address, connection state) and the actions the box really supports
|
|
11213
|
+
* (`reboot`, `device-admin-link`). Products are NOT its children — they are
|
|
11214
|
+
* adopted beside it — and its connection settings stay on the integration. */
|
|
11215
|
+
DeviceType["Gateway"] = "gateway";
|
|
11104
11216
|
return DeviceType;
|
|
11105
11217
|
}({});
|
|
11106
11218
|
var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
|
|
@@ -11351,7 +11463,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
|
|
|
11351
11463
|
return DeviceRole;
|
|
11352
11464
|
}({});
|
|
11353
11465
|
//#endregion
|
|
11354
|
-
//#region ../types/dist/composition-
|
|
11466
|
+
//#region ../types/dist/composition-C7WdThmP.mjs
|
|
11355
11467
|
/**
|
|
11356
11468
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
11357
11469
|
*
|
|
@@ -16358,8 +16470,172 @@ object({
|
|
|
16358
16470
|
quantity: ReadingQuantitySchema,
|
|
16359
16471
|
unit: string().min(1)
|
|
16360
16472
|
});
|
|
16361
|
-
|
|
16362
|
-
|
|
16473
|
+
new Set([
|
|
16474
|
+
{
|
|
16475
|
+
id: "person",
|
|
16476
|
+
name: "Person"
|
|
16477
|
+
},
|
|
16478
|
+
{
|
|
16479
|
+
id: "vehicle",
|
|
16480
|
+
name: "Vehicle"
|
|
16481
|
+
},
|
|
16482
|
+
{
|
|
16483
|
+
id: "animal",
|
|
16484
|
+
name: "Animal"
|
|
16485
|
+
},
|
|
16486
|
+
{
|
|
16487
|
+
id: "package",
|
|
16488
|
+
name: "Package"
|
|
16489
|
+
}
|
|
16490
|
+
].map((l) => l.id));
|
|
16491
|
+
var COCO_TO_MACRO = {
|
|
16492
|
+
mapping: {
|
|
16493
|
+
person: "person",
|
|
16494
|
+
bicycle: "vehicle",
|
|
16495
|
+
car: "vehicle",
|
|
16496
|
+
motorcycle: "vehicle",
|
|
16497
|
+
airplane: "vehicle",
|
|
16498
|
+
bus: "vehicle",
|
|
16499
|
+
train: "vehicle",
|
|
16500
|
+
truck: "vehicle",
|
|
16501
|
+
boat: "vehicle",
|
|
16502
|
+
bird: "animal",
|
|
16503
|
+
cat: "animal",
|
|
16504
|
+
dog: "animal",
|
|
16505
|
+
horse: "animal",
|
|
16506
|
+
sheep: "animal",
|
|
16507
|
+
cow: "animal",
|
|
16508
|
+
elephant: "animal",
|
|
16509
|
+
bear: "animal",
|
|
16510
|
+
zebra: "animal",
|
|
16511
|
+
giraffe: "animal",
|
|
16512
|
+
suitcase: "package",
|
|
16513
|
+
backpack: "package",
|
|
16514
|
+
handbag: "package"
|
|
16515
|
+
},
|
|
16516
|
+
preserveOriginal: false
|
|
16517
|
+
};
|
|
16518
|
+
/**
|
|
16519
|
+
* WHAT a detection-shaped leaf is about: one MACRO class (person, vehicle,
|
|
16520
|
+
* animal, package) plus an OPTIONAL label that narrows it.
|
|
16521
|
+
*
|
|
16522
|
+
* The taxonomy is `COCO_TO_MACRO` (and `CLASS_MAP_MACRO_TARGETS`) — never a
|
|
16523
|
+
* second list. The label that fits depends on the macro class:
|
|
16524
|
+
*
|
|
16525
|
+
* - `animal` → a sub-class (`dog`, `cat`, …);
|
|
16526
|
+
* - `person` → a known face (an identity of the `face-gallery`);
|
|
16527
|
+
* - `vehicle` → a plate (its text) or a sub-class (`car`, `truck`, …);
|
|
16528
|
+
* - `package` → a sub-class.
|
|
16529
|
+
*
|
|
16530
|
+
* `other` exists only so a class the taxonomy does not know (a custom model's
|
|
16531
|
+
* `forklift`) survives the conversion of a stored recipe without widening.
|
|
16532
|
+
*
|
|
16533
|
+
* A label is decided LATE (D683): face recognition and LPR land after the
|
|
16534
|
+
* detection starts. The track-lifecycle `update` event carries the newly
|
|
16535
|
+
* resolved `label` / `plateText`, which is why a leaf with a label fires when
|
|
16536
|
+
* the subject GETS it — not at the first detection.
|
|
16537
|
+
*/
|
|
16538
|
+
var SubClassLabelSchema = object({
|
|
16539
|
+
kind: literal("sub-class"),
|
|
16540
|
+
className: string().min(1)
|
|
16541
|
+
});
|
|
16542
|
+
var IdentityLabelSchema = object({
|
|
16543
|
+
kind: literal("identity"),
|
|
16544
|
+
/** The `face-gallery` identity id (stable across a rename). */
|
|
16545
|
+
identityId: string().min(1),
|
|
16546
|
+
/** The identity's name — what a track-lifecycle event carries as `label`. */
|
|
16547
|
+
name: string().min(1)
|
|
16548
|
+
});
|
|
16549
|
+
var PlateLabelSchema = object({
|
|
16550
|
+
kind: literal("plate"),
|
|
16551
|
+
text: string().min(1)
|
|
16552
|
+
});
|
|
16553
|
+
function macroOf$1(className) {
|
|
16554
|
+
return COCO_TO_MACRO.mapping[className.toLowerCase()] ?? className.toLowerCase();
|
|
16555
|
+
}
|
|
16556
|
+
/** A sub-class label must belong to the macro it is under. */
|
|
16557
|
+
function subClassBelongs(macro, label) {
|
|
16558
|
+
return label === void 0 || macroOf$1(label.className) === macro;
|
|
16559
|
+
}
|
|
16560
|
+
var subClassOf = (macro) => SubClassLabelSchema.refine((l) => subClassBelongs(macro, l), { message: `that class is not a kind of ${macro} in the detection taxonomy` });
|
|
16561
|
+
var DetectionSubjectSchema = discriminatedUnion("macro", [
|
|
16562
|
+
object({
|
|
16563
|
+
macro: literal("animal"),
|
|
16564
|
+
label: subClassOf("animal").optional()
|
|
16565
|
+
}),
|
|
16566
|
+
object({
|
|
16567
|
+
macro: literal("person"),
|
|
16568
|
+
label: IdentityLabelSchema.optional()
|
|
16569
|
+
}),
|
|
16570
|
+
object({
|
|
16571
|
+
macro: literal("vehicle"),
|
|
16572
|
+
label: discriminatedUnion("kind", [SubClassLabelSchema.refine((l) => subClassBelongs("vehicle", l), { message: "that class is not a kind of vehicle in the detection taxonomy" }), PlateLabelSchema]).optional()
|
|
16573
|
+
}),
|
|
16574
|
+
object({
|
|
16575
|
+
macro: literal("package"),
|
|
16576
|
+
label: subClassOf("package").optional()
|
|
16577
|
+
}),
|
|
16578
|
+
object({
|
|
16579
|
+
macro: literal("other"),
|
|
16580
|
+
className: string().min(1)
|
|
16581
|
+
})
|
|
16582
|
+
]);
|
|
16583
|
+
/**
|
|
16584
|
+
* The subject of a PARKED object. The stationary registry's event carries a
|
|
16585
|
+
* class and a zone, no face and no plate — an identity or plate label there
|
|
16586
|
+
* could never match, so the schema refuses it instead of letting it sit dead.
|
|
16587
|
+
*/
|
|
16588
|
+
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" });
|
|
16589
|
+
/**
|
|
16590
|
+
* A stored class string as a subject: a macro name stays a macro; a raw class
|
|
16591
|
+
* the taxonomy files under a macro becomes that macro plus the sub-class label
|
|
16592
|
+
* (`dog` → animal / dog); anything else is `other`, kept verbatim.
|
|
16593
|
+
*/
|
|
16594
|
+
function subjectFromClassName(className) {
|
|
16595
|
+
const name = className.trim().toLowerCase();
|
|
16596
|
+
const macro = COCO_TO_MACRO.mapping[name];
|
|
16597
|
+
if (new Set(Object.values(COCO_TO_MACRO.mapping)).has(name)) return macroOnly(name);
|
|
16598
|
+
if (macro === void 0) return {
|
|
16599
|
+
macro: "other",
|
|
16600
|
+
className
|
|
16601
|
+
};
|
|
16602
|
+
const label = {
|
|
16603
|
+
kind: "sub-class",
|
|
16604
|
+
className: name
|
|
16605
|
+
};
|
|
16606
|
+
switch (macro) {
|
|
16607
|
+
case "animal": return {
|
|
16608
|
+
macro: "animal",
|
|
16609
|
+
label
|
|
16610
|
+
};
|
|
16611
|
+
case "vehicle": return {
|
|
16612
|
+
macro: "vehicle",
|
|
16613
|
+
label
|
|
16614
|
+
};
|
|
16615
|
+
case "package": return {
|
|
16616
|
+
macro: "package",
|
|
16617
|
+
label
|
|
16618
|
+
};
|
|
16619
|
+
default: return {
|
|
16620
|
+
macro: "other",
|
|
16621
|
+
className
|
|
16622
|
+
};
|
|
16623
|
+
}
|
|
16624
|
+
}
|
|
16625
|
+
function macroOnly(macro) {
|
|
16626
|
+
switch (macro) {
|
|
16627
|
+
case "animal": return { macro: "animal" };
|
|
16628
|
+
case "person": return { macro: "person" };
|
|
16629
|
+
case "vehicle": return { macro: "vehicle" };
|
|
16630
|
+
case "package": return { macro: "package" };
|
|
16631
|
+
default: return {
|
|
16632
|
+
macro: "other",
|
|
16633
|
+
className: macro
|
|
16634
|
+
};
|
|
16635
|
+
}
|
|
16636
|
+
}
|
|
16637
|
+
/** How a state leaf compares a device field to a value. Derived from the
|
|
16638
|
+
* field's `kind` in `deviceManager.getWireableFields`, never hand-maintained. */
|
|
16363
16639
|
var AutomationConditionOperatorSchema = _enum([
|
|
16364
16640
|
"eq",
|
|
16365
16641
|
"ne",
|
|
@@ -16370,12 +16646,76 @@ var AutomationConditionOperatorSchema = _enum([
|
|
|
16370
16646
|
"contains",
|
|
16371
16647
|
"in"
|
|
16372
16648
|
]);
|
|
16373
|
-
|
|
16374
|
-
|
|
16649
|
+
/**
|
|
16650
|
+
* D8 compliance, and it is the reason a "becomes" leaf is not merely an event
|
|
16651
|
+
* subscription: it evaluates against the **state mirror**, which is reconciled,
|
|
16652
|
+
* and an event only WAKES the evaluation. A dropped event therefore DELAYS a
|
|
16653
|
+
* fire; it does not lose it.
|
|
16654
|
+
*/
|
|
16655
|
+
var DeviceBecomesLeafSchema = object({
|
|
16656
|
+
kind: literal("device-becomes"),
|
|
16657
|
+
deviceId: number().int().nonnegative(),
|
|
16658
|
+
cap: string().min(1),
|
|
16659
|
+
fieldPath: string().min(1),
|
|
16660
|
+
/** Fire when the field takes this value. Omit to fire on any change. */
|
|
16661
|
+
becomes: union([
|
|
16662
|
+
string(),
|
|
16663
|
+
number(),
|
|
16664
|
+
boolean()
|
|
16665
|
+
]).optional(),
|
|
16666
|
+
/** Only on a CHANGE of value, not on every re-report. */
|
|
16667
|
+
edge: boolean().optional(),
|
|
16668
|
+
/** The new value must hold this long before it counts. */
|
|
16669
|
+
forMs: number().int().min(0).max(864e5).optional(),
|
|
16670
|
+
/** Collapse a burst into one occurrence. */
|
|
16671
|
+
debounceMs: number().int().min(0).max(6e5).optional()
|
|
16672
|
+
});
|
|
16673
|
+
var DeviceEventLeafSchema = object({
|
|
16674
|
+
kind: literal("device-event"),
|
|
16675
|
+
/** An `EventCategory` value. */
|
|
16676
|
+
category: string().min(1),
|
|
16677
|
+
deviceId: number().int().nonnegative().optional()
|
|
16678
|
+
});
|
|
16679
|
+
var ScheduleLeafSchema = object({
|
|
16680
|
+
kind: literal("schedule"),
|
|
16681
|
+
cron: string().min(1).max(120)
|
|
16682
|
+
});
|
|
16683
|
+
var ManualLeafSchema = object({ kind: literal("manual") });
|
|
16684
|
+
var IntervalLeafSchema = object({
|
|
16685
|
+
kind: literal("interval"),
|
|
16686
|
+
minutes: number().int().min(1).max(1440)
|
|
16687
|
+
});
|
|
16688
|
+
var DetectionLeafSchema = object({
|
|
16689
|
+
kind: literal("detection"),
|
|
16690
|
+
/** One macro class each, optionally narrowed by a label; a track of any listed subject fires. */
|
|
16691
|
+
subjects: array(DetectionSubjectSchema).min(1),
|
|
16692
|
+
deviceIds: array(number().int().nonnegative()).min(1),
|
|
16693
|
+
/** The track must have visited at least one of these zones. */
|
|
16694
|
+
zones: array(string().min(1)).min(1).optional(),
|
|
16695
|
+
/** Best-so-far confidence the track must reach. */
|
|
16696
|
+
minScore: number().min(0).max(1).optional()
|
|
16697
|
+
});
|
|
16698
|
+
var StationaryLeafSchema = object({
|
|
16699
|
+
kind: literal("stationary"),
|
|
16700
|
+
deviceIds: array(number().int().nonnegative()).min(1),
|
|
16701
|
+
subjects: array(StationarySubjectSchema).min(1).optional(),
|
|
16702
|
+
zones: array(string().min(1)).min(1).optional(),
|
|
16703
|
+
/** `appeared`: it became parked. `departed`: the registry retired it. */
|
|
16704
|
+
phase: _enum(["appeared", "departed"])
|
|
16705
|
+
});
|
|
16706
|
+
/** "is": one device field compared to one value, NOW. */
|
|
16707
|
+
var DeviceIsLeafSchema = object({
|
|
16708
|
+
kind: literal("device-is"),
|
|
16375
16709
|
deviceId: number().int().nonnegative(),
|
|
16376
16710
|
cap: string().min(1),
|
|
16377
16711
|
fieldPath: string().min(1),
|
|
16378
16712
|
operator: AutomationConditionOperatorSchema,
|
|
16713
|
+
/**
|
|
16714
|
+
* The value. A number on a quantity field is stored in the field's base unit
|
|
16715
|
+
* (D944); a quantity value is the WRITE form, which the save boundary
|
|
16716
|
+
* replaces with the converted number and `unit`. A leaf that still carries
|
|
16717
|
+
* one is never evaluated (it fails).
|
|
16718
|
+
*/
|
|
16379
16719
|
value: union([
|
|
16380
16720
|
string(),
|
|
16381
16721
|
number(),
|
|
@@ -16383,24 +16723,26 @@ var AutomationConditionLeafSchema = object({
|
|
|
16383
16723
|
array(union([string(), number()])),
|
|
16384
16724
|
QuantityValueSchema
|
|
16385
16725
|
]),
|
|
16726
|
+
/** The unit the person picked for `value` (display only; `value` is in the base unit). */
|
|
16386
16727
|
unit: string().min(1).optional()
|
|
16387
16728
|
});
|
|
16388
|
-
|
|
16729
|
+
/**
|
|
16730
|
+
* True when the camera currently has a PARKED object matching `subjects` in at
|
|
16731
|
+
* least one of `zones`. Reads the zone-analytics snapshot through the same
|
|
16732
|
+
* mirror as every other state leaf, so a dropped event never decides it (D49).
|
|
16733
|
+
*/
|
|
16734
|
+
var StationaryPresentLeafSchema = object({
|
|
16389
16735
|
kind: literal("stationary-present"),
|
|
16390
16736
|
deviceId: number().int().nonnegative(),
|
|
16391
|
-
|
|
16392
|
-
classes: array(string().min(1)).min(1).optional(),
|
|
16393
|
-
/** The parked object must be in at least one of these zones. */
|
|
16737
|
+
subjects: array(StationarySubjectSchema).min(1).optional(),
|
|
16394
16738
|
zones: array(string().min(1)).min(1).optional()
|
|
16395
16739
|
});
|
|
16396
16740
|
/**
|
|
16397
|
-
* The
|
|
16398
|
-
*
|
|
16399
|
-
*
|
|
16400
|
-
* validation is the SAME function `ExpressionSourceSchema` runs, so the two
|
|
16401
|
-
* cannot drift — an expression that one accepts, the other accepts.
|
|
16741
|
+
* The escape hatch: the bounded expression engine over named device bindings.
|
|
16742
|
+
* Declared as a plain object so a discriminated union can read `kind`; the
|
|
16743
|
+
* author-time validation is the SAME function `ExpressionSourceSchema` runs.
|
|
16402
16744
|
*/
|
|
16403
|
-
var
|
|
16745
|
+
var ExpressionLeafSchema = object({
|
|
16404
16746
|
kind: literal("expression"),
|
|
16405
16747
|
expr: string().min(1).max(MAX_EXPRESSION_SOURCE_LENGTH),
|
|
16406
16748
|
bindings: record(string().regex(EXPRESSION_IDENTIFIER_RE), ExpressionBindingSourceSchema)
|
|
@@ -16412,55 +16754,62 @@ var AutomationConditionExpressionSchema = object({
|
|
|
16412
16754
|
path: ["expr"]
|
|
16413
16755
|
});
|
|
16414
16756
|
});
|
|
16415
|
-
var
|
|
16757
|
+
var AutomationWhenSchema = lazy(() => discriminatedUnion("kind", [
|
|
16416
16758
|
object({
|
|
16417
16759
|
kind: literal("all"),
|
|
16418
|
-
children: array(
|
|
16760
|
+
children: array(AutomationWhenSchema)
|
|
16419
16761
|
}),
|
|
16420
16762
|
object({
|
|
16421
16763
|
kind: literal("any"),
|
|
16422
|
-
children: array(
|
|
16764
|
+
children: array(AutomationWhenSchema)
|
|
16423
16765
|
}),
|
|
16424
16766
|
object({
|
|
16425
16767
|
kind: literal("not"),
|
|
16426
|
-
child:
|
|
16768
|
+
child: AutomationWhenSchema
|
|
16427
16769
|
}),
|
|
16428
|
-
|
|
16429
|
-
|
|
16430
|
-
|
|
16770
|
+
DeviceBecomesLeafSchema,
|
|
16771
|
+
DeviceEventLeafSchema,
|
|
16772
|
+
ScheduleLeafSchema,
|
|
16773
|
+
ManualLeafSchema,
|
|
16774
|
+
IntervalLeafSchema,
|
|
16775
|
+
DetectionLeafSchema,
|
|
16776
|
+
StationaryLeafSchema,
|
|
16777
|
+
DeviceIsLeafSchema,
|
|
16778
|
+
StationaryPresentLeafSchema,
|
|
16779
|
+
ExpressionLeafSchema
|
|
16431
16780
|
]));
|
|
16432
16781
|
/**
|
|
16433
|
-
*
|
|
16434
|
-
*
|
|
16435
|
-
*
|
|
16436
|
-
*
|
|
16437
|
-
*
|
|
16438
|
-
*
|
|
16439
|
-
*
|
|
16440
|
-
*
|
|
16441
|
-
|
|
16442
|
-
|
|
16782
|
+
* The recipe shape before the single `when` tree: a flat list of triggers
|
|
16783
|
+
* (OR'd, each an instant) and an optional condition tree. Stored recipes —
|
|
16784
|
+
* the `AUTOMATION_RECIPE` literal of every block saved so far, and whatever an
|
|
16785
|
+
* older client still posts — are in this shape, so it stays READABLE here, and
|
|
16786
|
+
* {@link convertLegacyRecipe} turns it into the new one, losslessly and one
|
|
16787
|
+
* way. Nothing writes this shape any more.
|
|
16788
|
+
*
|
|
16789
|
+
* `{ triggers: [t1…tn], conditions?: C }`
|
|
16790
|
+
* → `when = all(any(t1…tn), C)`, or `any(t1…tn)` without `C`;
|
|
16791
|
+
* a single trigger is not wrapped in `any`.
|
|
16792
|
+
* a `condition` leaf → a `device-is` leaf (the "is" verb);
|
|
16793
|
+
* a `device-state` trigger → a `device-becomes` leaf (the "becomes" verb);
|
|
16794
|
+
* `classes: ['dog']` → the macro `animal` with the label `dog`.
|
|
16795
|
+
*/
|
|
16796
|
+
var LegacyTriggerSchema = discriminatedUnion("kind", [
|
|
16443
16797
|
object({
|
|
16444
16798
|
kind: literal("device-state"),
|
|
16445
16799
|
deviceId: number().int().nonnegative(),
|
|
16446
16800
|
cap: string().min(1),
|
|
16447
16801
|
fieldPath: string().min(1),
|
|
16448
|
-
/** Fire when the field takes this value. Omit to fire on any change. */
|
|
16449
16802
|
becomes: union([
|
|
16450
16803
|
string(),
|
|
16451
16804
|
number(),
|
|
16452
16805
|
boolean()
|
|
16453
16806
|
]).optional(),
|
|
16454
|
-
/** Only on a CHANGE of value, not on every re-report. */
|
|
16455
16807
|
edge: boolean().optional(),
|
|
16456
|
-
/** The condition must hold this long before the run starts. */
|
|
16457
16808
|
forMs: number().int().min(0).max(864e5).optional(),
|
|
16458
|
-
/** Collapse a burst into one run. */
|
|
16459
16809
|
debounceMs: number().int().min(0).max(6e5).optional()
|
|
16460
16810
|
}),
|
|
16461
16811
|
object({
|
|
16462
16812
|
kind: literal("device-event"),
|
|
16463
|
-
/** An `EventCategory` value. */
|
|
16464
16813
|
category: string().min(1),
|
|
16465
16814
|
deviceId: number().int().nonnegative().optional()
|
|
16466
16815
|
}),
|
|
@@ -16471,25 +16820,16 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
|
|
|
16471
16820
|
object({ kind: literal("manual") }),
|
|
16472
16821
|
object({
|
|
16473
16822
|
kind: literal("detection"),
|
|
16474
|
-
/** Class names. A macro class (`animal`, `vehicle`, `person`, `package`)
|
|
16475
|
-
* matches every raw class that maps to it; any other name matches
|
|
16476
|
-
* exactly. */
|
|
16477
16823
|
classes: array(string().min(1)).min(1),
|
|
16478
16824
|
deviceIds: array(number().int().nonnegative()).min(1),
|
|
16479
|
-
/** The track must have visited at least one of these zones. */
|
|
16480
16825
|
zones: array(string().min(1)).min(1).optional(),
|
|
16481
|
-
/** Best-so-far confidence the track must reach. */
|
|
16482
16826
|
minScore: number().min(0).max(1).optional()
|
|
16483
16827
|
}),
|
|
16484
16828
|
object({
|
|
16485
16829
|
kind: literal("stationary"),
|
|
16486
16830
|
deviceIds: array(number().int().nonnegative()).min(1),
|
|
16487
|
-
/** Class names; a macro class matches like the detection trigger. */
|
|
16488
16831
|
classes: array(string().min(1)).min(1).optional(),
|
|
16489
|
-
/** The parked object must be in at least one of these zones. */
|
|
16490
16832
|
zones: array(string().min(1)).min(1).optional(),
|
|
16491
|
-
/** `appeared`: it became parked. `departed`: the registry retired it.
|
|
16492
|
-
* The vocabulary of `PipelineAnalyticsStationaryChanged`. */
|
|
16493
16833
|
phase: _enum(["appeared", "departed"])
|
|
16494
16834
|
}),
|
|
16495
16835
|
object({
|
|
@@ -16497,6 +16837,143 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
|
|
|
16497
16837
|
minutes: number().int().min(1).max(1440)
|
|
16498
16838
|
})
|
|
16499
16839
|
]);
|
|
16840
|
+
var LegacyConditionLeafSchema = object({
|
|
16841
|
+
kind: literal("condition"),
|
|
16842
|
+
deviceId: number().int().nonnegative(),
|
|
16843
|
+
cap: string().min(1),
|
|
16844
|
+
fieldPath: string().min(1),
|
|
16845
|
+
operator: AutomationConditionOperatorSchema,
|
|
16846
|
+
value: union([
|
|
16847
|
+
string(),
|
|
16848
|
+
number(),
|
|
16849
|
+
boolean(),
|
|
16850
|
+
array(union([string(), number()])),
|
|
16851
|
+
QuantityValueSchema
|
|
16852
|
+
]),
|
|
16853
|
+
unit: string().min(1).optional()
|
|
16854
|
+
});
|
|
16855
|
+
var LegacyStationaryPresentSchema = object({
|
|
16856
|
+
kind: literal("stationary-present"),
|
|
16857
|
+
deviceId: number().int().nonnegative(),
|
|
16858
|
+
classes: array(string().min(1)).min(1).optional(),
|
|
16859
|
+
zones: array(string().min(1)).min(1).optional()
|
|
16860
|
+
});
|
|
16861
|
+
var LegacyExpressionSchema = object({
|
|
16862
|
+
kind: literal("expression"),
|
|
16863
|
+
expr: string().min(1).max(MAX_EXPRESSION_SOURCE_LENGTH),
|
|
16864
|
+
bindings: record(string().regex(EXPRESSION_IDENTIFIER_RE), ExpressionBindingSourceSchema)
|
|
16865
|
+
}).superRefine((src, ctx) => {
|
|
16866
|
+
const err = validateExpressionSource(src);
|
|
16867
|
+
if (err !== null) ctx.addIssue({
|
|
16868
|
+
code: "custom",
|
|
16869
|
+
message: err,
|
|
16870
|
+
path: ["expr"]
|
|
16871
|
+
});
|
|
16872
|
+
});
|
|
16873
|
+
var LegacyConditionSchema = lazy(() => discriminatedUnion("kind", [
|
|
16874
|
+
object({
|
|
16875
|
+
kind: literal("all"),
|
|
16876
|
+
children: array(LegacyConditionSchema)
|
|
16877
|
+
}),
|
|
16878
|
+
object({
|
|
16879
|
+
kind: literal("any"),
|
|
16880
|
+
children: array(LegacyConditionSchema)
|
|
16881
|
+
}),
|
|
16882
|
+
object({
|
|
16883
|
+
kind: literal("not"),
|
|
16884
|
+
child: LegacyConditionSchema
|
|
16885
|
+
}),
|
|
16886
|
+
LegacyConditionLeafSchema,
|
|
16887
|
+
LegacyExpressionSchema,
|
|
16888
|
+
LegacyStationaryPresentSchema
|
|
16889
|
+
]));
|
|
16890
|
+
function convertTrigger(trigger) {
|
|
16891
|
+
switch (trigger.kind) {
|
|
16892
|
+
case "device-state": return {
|
|
16893
|
+
...trigger,
|
|
16894
|
+
kind: "device-becomes"
|
|
16895
|
+
};
|
|
16896
|
+
case "detection": {
|
|
16897
|
+
const { classes, ...rest } = trigger;
|
|
16898
|
+
return {
|
|
16899
|
+
...rest,
|
|
16900
|
+
subjects: classes.map(subjectFromClassName)
|
|
16901
|
+
};
|
|
16902
|
+
}
|
|
16903
|
+
case "stationary": {
|
|
16904
|
+
const { classes, ...rest } = trigger;
|
|
16905
|
+
return {
|
|
16906
|
+
...rest,
|
|
16907
|
+
...classes === void 0 ? {} : { subjects: classes.map(subjectFromClassName) }
|
|
16908
|
+
};
|
|
16909
|
+
}
|
|
16910
|
+
default: return trigger;
|
|
16911
|
+
}
|
|
16912
|
+
}
|
|
16913
|
+
/** An old condition tree as a `when` subtree: `condition` → "is", classes → subjects. */
|
|
16914
|
+
function convertLegacyCondition(condition) {
|
|
16915
|
+
switch (condition.kind) {
|
|
16916
|
+
case "condition": return {
|
|
16917
|
+
...condition,
|
|
16918
|
+
kind: "device-is"
|
|
16919
|
+
};
|
|
16920
|
+
case "stationary-present": {
|
|
16921
|
+
const { classes, ...rest } = condition;
|
|
16922
|
+
return {
|
|
16923
|
+
...rest,
|
|
16924
|
+
...classes === void 0 ? {} : { subjects: classes.map(subjectFromClassName) }
|
|
16925
|
+
};
|
|
16926
|
+
}
|
|
16927
|
+
case "expression": return condition;
|
|
16928
|
+
case "not": return {
|
|
16929
|
+
kind: "not",
|
|
16930
|
+
child: convertLegacyCondition(condition.child)
|
|
16931
|
+
};
|
|
16932
|
+
case "all":
|
|
16933
|
+
case "any": return {
|
|
16934
|
+
kind: condition.kind,
|
|
16935
|
+
children: condition.children.map(convertLegacyCondition)
|
|
16936
|
+
};
|
|
16937
|
+
}
|
|
16938
|
+
}
|
|
16939
|
+
/** `all(any(t1…tn), C)`, or `any(t1…tn)`; a single trigger is not wrapped in `any`. */
|
|
16940
|
+
function convertLegacyWhen(half) {
|
|
16941
|
+
const leaves = half.triggers.map(convertTrigger);
|
|
16942
|
+
const first = leaves[0];
|
|
16943
|
+
const triggers = leaves.length === 1 && first !== void 0 ? first : {
|
|
16944
|
+
kind: "any",
|
|
16945
|
+
children: leaves
|
|
16946
|
+
};
|
|
16947
|
+
if (half.conditions === void 0) return triggers;
|
|
16948
|
+
return {
|
|
16949
|
+
kind: "all",
|
|
16950
|
+
children: [triggers, convertLegacyCondition(half.conditions)]
|
|
16951
|
+
};
|
|
16952
|
+
}
|
|
16953
|
+
/**
|
|
16954
|
+
* The automation RECIPE — what a visually-authored automation is, before it
|
|
16955
|
+
* becomes code.
|
|
16956
|
+
*
|
|
16957
|
+
* This is the data half of `docs/design/2026-08-08-automation-scripts-custom-devices.md`
|
|
16958
|
+
* Phase 6. A recipe is not an engine and never evaluates anything: it is a
|
|
16959
|
+
* declarative description that {@link generateAutomationBlock} compiles to the
|
|
16960
|
+
* TypeScript of an ordinary core block. There is therefore no second rule
|
|
16961
|
+
* engine, no second store and no second runtime — the block runner is the
|
|
16962
|
+
* runtime, `core_blocks` is the store (§2.1).
|
|
16963
|
+
*
|
|
16964
|
+
* Two parts:
|
|
16965
|
+
*
|
|
16966
|
+
* - **`when`** — ONE tree (`./when.ts`) of all/any/not over event leaves
|
|
16967
|
+
* (instants) and state leaves (durations). It replaces the flat trigger list
|
|
16968
|
+
* plus the separate condition tree; the firing rule is in that file and in
|
|
16969
|
+
* ADR "one tree".
|
|
16970
|
+
* - **Actions** — `NcRuleActionSchema`'s two steps, plus the `{kind:'code'}`
|
|
16971
|
+
* fragment of §3.2.2. See {@link AutomationActionSchema} for the ONE place
|
|
16972
|
+
* this had to diverge from "verbatim", and why.
|
|
16973
|
+
*
|
|
16974
|
+
* A recipe stored in the old shape (`triggers` + `conditions`) still LOADS:
|
|
16975
|
+
* {@link AutomationRecipeSchema} (the parse-anywhere schema) converts it on parse. Nothing writes it.
|
|
16976
|
+
*/
|
|
16500
16977
|
/**
|
|
16501
16978
|
* One action step.
|
|
16502
16979
|
*
|
|
@@ -16599,69 +17076,49 @@ var AutomationRunModeSchema = discriminatedUnion("mode", [
|
|
|
16599
17076
|
minutes: number().int().min(1).max(1440)
|
|
16600
17077
|
})
|
|
16601
17078
|
]);
|
|
16602
|
-
var
|
|
16603
|
-
|
|
16604
|
-
conditions: AutomationConditionSchema.optional(),
|
|
17079
|
+
var CurrentAutomationRecipeSchema = object({
|
|
17080
|
+
when: AutomationWhenSchema,
|
|
16605
17081
|
actions: array(AutomationActionSchema),
|
|
16606
17082
|
runMode: AutomationRunModeSchema.optional(),
|
|
16607
17083
|
/** How many run traces the automation keeps (default {@link DEFAULT_TRACE_KEEP}). */
|
|
16608
17084
|
keepTraces: number().int().min(1).max(100).optional()
|
|
16609
17085
|
});
|
|
16610
|
-
|
|
16611
|
-
|
|
16612
|
-
|
|
16613
|
-
|
|
16614
|
-
|
|
16615
|
-
|
|
16616
|
-
|
|
16617
|
-
|
|
16618
|
-
|
|
16619
|
-
|
|
16620
|
-
|
|
16621
|
-
|
|
16622
|
-
|
|
16623
|
-
|
|
16624
|
-
|
|
16625
|
-
|
|
16626
|
-
|
|
16627
|
-
|
|
16628
|
-
|
|
16629
|
-
|
|
16630
|
-
|
|
16631
|
-
|
|
16632
|
-
|
|
16633
|
-
|
|
16634
|
-
|
|
16635
|
-
bus: "vehicle",
|
|
16636
|
-
train: "vehicle",
|
|
16637
|
-
truck: "vehicle",
|
|
16638
|
-
boat: "vehicle",
|
|
16639
|
-
bird: "animal",
|
|
16640
|
-
cat: "animal",
|
|
16641
|
-
dog: "animal",
|
|
16642
|
-
horse: "animal",
|
|
16643
|
-
sheep: "animal",
|
|
16644
|
-
cow: "animal",
|
|
16645
|
-
elephant: "animal",
|
|
16646
|
-
bear: "animal",
|
|
16647
|
-
zebra: "animal",
|
|
16648
|
-
giraffe: "animal",
|
|
16649
|
-
suitcase: "package",
|
|
16650
|
-
backpack: "package",
|
|
16651
|
-
handbag: "package"
|
|
16652
|
-
},
|
|
16653
|
-
preserveOriginal: false
|
|
16654
|
-
};
|
|
17086
|
+
/** A recipe as it was stored before the single tree. Read-only: nothing writes this. */
|
|
17087
|
+
var LegacyAutomationRecipeSchema = object({
|
|
17088
|
+
triggers: array(LegacyTriggerSchema),
|
|
17089
|
+
conditions: LegacyConditionSchema.optional(),
|
|
17090
|
+
actions: array(AutomationActionSchema),
|
|
17091
|
+
runMode: AutomationRunModeSchema.optional(),
|
|
17092
|
+
keepTraces: number().int().min(1).max(100).optional()
|
|
17093
|
+
});
|
|
17094
|
+
/** The old shape as the new one, lossless. */
|
|
17095
|
+
function convertLegacyRecipe(legacy) {
|
|
17096
|
+
const { triggers, conditions, ...rest } = legacy;
|
|
17097
|
+
return {
|
|
17098
|
+
when: convertLegacyWhen({
|
|
17099
|
+
triggers,
|
|
17100
|
+
conditions
|
|
17101
|
+
}),
|
|
17102
|
+
...rest
|
|
17103
|
+
};
|
|
17104
|
+
}
|
|
17105
|
+
/**
|
|
17106
|
+
* Where recipes are PARSED (the core-blocks cap, the MCP tool, the literal a
|
|
17107
|
+
* saved block carries): the new shape as it is, or the old shape converted to
|
|
17108
|
+
* it. The output type is always the new one.
|
|
17109
|
+
*/
|
|
17110
|
+
var AutomationRecipeSchema = union([CurrentAutomationRecipeSchema, LegacyAutomationRecipeSchema.transform(convertLegacyRecipe)]);
|
|
16655
17111
|
new Set(Object.values(COCO_TO_MACRO.mapping));
|
|
16656
17112
|
/**
|
|
16657
17113
|
* Run traces — what an automation (or, later, an Action run) DID, node by node,
|
|
16658
17114
|
* so the editor can paint every block green or red with its reason.
|
|
16659
17115
|
*
|
|
16660
|
-
* A trace is data, not a log: it covers the WHOLE tree — the
|
|
16661
|
-
* every
|
|
16662
|
-
* short-circuit never reached), the run-mode verdict and every action
|
|
16663
|
-
* each addressed by a stable path (`
|
|
16664
|
-
* for an Action run).
|
|
17116
|
+
* A trace is data, not a log: it covers the WHOLE tree — the event leaf that
|
|
17117
|
+
* fired, every node of the `when` tree (containers and leaves, including the
|
|
17118
|
+
* ones a short-circuit never reached), the run-mode verdict and every action
|
|
17119
|
+
* step — each addressed by a stable path (`when.0.1`, `actions.2`, or `steps.N`
|
|
17120
|
+
* for an Action run). Traces written before the single tree carry
|
|
17121
|
+
* `conditions.*` paths and still parse. Skipped
|
|
16665
17122
|
* evaluations are traced too, and identical consecutive skips collapse into a
|
|
16666
17123
|
* repeat count so a trigger that fails every 5 minutes cannot flush real runs.
|
|
16667
17124
|
*
|
|
@@ -16693,6 +17150,8 @@ var TraceNodeOutcomeSchema = _enum([
|
|
|
16693
17150
|
]);
|
|
16694
17151
|
var AutomationTriggerTraceSchema = object({
|
|
16695
17152
|
kind: string().min(1),
|
|
17153
|
+
/** The event leaf that fired (`when`, `when.0.1`); absent on traces written before the single tree. */
|
|
17154
|
+
path: string().min(1).optional(),
|
|
16696
17155
|
outcome: _enum(["pass", "fail"]),
|
|
16697
17156
|
/** Why an event was rejected ("class person not in [dog]") or what fired. */
|
|
16698
17157
|
reason: string(),
|
|
@@ -16700,15 +17159,23 @@ var AutomationTriggerTraceSchema = object({
|
|
|
16700
17159
|
vars: record(string(), TraceValueSchema)
|
|
16701
17160
|
});
|
|
16702
17161
|
var AutomationConditionTraceNodeSchema = object({
|
|
16703
|
-
/** `
|
|
17162
|
+
/** `when` for the root, `when.<i>` for its i-th child, … (`conditions…` on an old trace). */
|
|
16704
17163
|
path: string().min(1),
|
|
16705
17164
|
kind: _enum([
|
|
16706
17165
|
"all",
|
|
16707
17166
|
"any",
|
|
16708
17167
|
"not",
|
|
16709
17168
|
"condition",
|
|
17169
|
+
"device-is",
|
|
16710
17170
|
"expression",
|
|
16711
|
-
"stationary-present"
|
|
17171
|
+
"stationary-present",
|
|
17172
|
+
"device-becomes",
|
|
17173
|
+
"device-event",
|
|
17174
|
+
"schedule",
|
|
17175
|
+
"interval",
|
|
17176
|
+
"manual",
|
|
17177
|
+
"detection",
|
|
17178
|
+
"stationary"
|
|
16712
17179
|
]),
|
|
16713
17180
|
outcome: TraceNodeOutcomeSchema,
|
|
16714
17181
|
reason: string(),
|
|
@@ -16742,6 +17209,12 @@ var AutomationStepTraceSchema = object({
|
|
|
16742
17209
|
deviceId: number().int().optional(),
|
|
16743
17210
|
startedAt: number().optional(),
|
|
16744
17211
|
endedAt: number().optional(),
|
|
17212
|
+
/**
|
|
17213
|
+
* The parameters the step SENT, after `{{vars.x}}` substitution — what the
|
|
17214
|
+
* device was actually asked. Bounded like trigger `vars`; a secret parameter
|
|
17215
|
+
* is shown as {@link REDACTED_ARG}. Absent on traces written before it existed.
|
|
17216
|
+
*/
|
|
17217
|
+
args: record(string(), TraceValueSchema).optional(),
|
|
16745
17218
|
outcome: _enum([
|
|
16746
17219
|
"ok",
|
|
16747
17220
|
"error",
|
|
@@ -17491,6 +17964,23 @@ method(object({
|
|
|
17491
17964
|
addonId: string(),
|
|
17492
17965
|
nodeId: string().optional()
|
|
17493
17966
|
}), SettingsSchemaWithValuesSchema.nullable()), method(object({
|
|
17967
|
+
addonId: string(),
|
|
17968
|
+
nodeId: string().optional()
|
|
17969
|
+
}), object({ keys: array(string()) }).nullable(), { auth: "admin" }), method(object({
|
|
17970
|
+
addonId: string(),
|
|
17971
|
+
nodeId: string().optional(),
|
|
17972
|
+
keys: array(string())
|
|
17973
|
+
}), object({
|
|
17974
|
+
removed: array(string()),
|
|
17975
|
+
refused: array(object({
|
|
17976
|
+
key: string(),
|
|
17977
|
+
reason: string()
|
|
17978
|
+
})),
|
|
17979
|
+
secretsCleared: number()
|
|
17980
|
+
}), {
|
|
17981
|
+
kind: "mutation",
|
|
17982
|
+
auth: "admin"
|
|
17983
|
+
}), method(object({
|
|
17494
17984
|
addonId: string(),
|
|
17495
17985
|
deviceId: number(),
|
|
17496
17986
|
nodeId: string().optional()
|
|
@@ -18926,8 +19416,21 @@ var BrokerProviderInfoSchema = object({
|
|
|
18926
19416
|
* (`{ sessionId }`) drops the session. The dialog that edits a broker
|
|
18927
19417
|
* offers it as the default path; the fields the session fills are
|
|
18928
19418
|
* marked `filledBySignIn` in the kind's settings schema.
|
|
19419
|
+
*
|
|
19420
|
+
* `methods` lists the ways the session can sign in, recommended first:
|
|
19421
|
+
* `'device-code'` adds `POST {basePath}/code/start` (body: the form's
|
|
19422
|
+
* settings) answering `{ sessionId, userCode, verificationUrl,
|
|
19423
|
+
* expiresAt }` — the operator types `userCode` at `verificationUrl` on
|
|
19424
|
+
* any device, and the same `status` / `commit` / `cancel` routes follow
|
|
19425
|
+
* (status then carries `method`, `userCode`, `verificationUrl`,
|
|
19426
|
+
* `expiresAt` and the phases `'awaiting-code'` / `'expired'`);
|
|
19427
|
+
* `'proxy'` is the `{basePath}/start` flow above. Absent means
|
|
19428
|
+
* `['proxy']`.
|
|
18929
19429
|
*/
|
|
18930
|
-
signIn: object({
|
|
19430
|
+
signIn: object({
|
|
19431
|
+
basePath: string().min(1),
|
|
19432
|
+
methods: array(_enum(["device-code", "proxy"])).optional()
|
|
19433
|
+
}).optional()
|
|
18931
19434
|
}))
|
|
18932
19435
|
});
|
|
18933
19436
|
var ListInputSchema = object({
|
|
@@ -21401,6 +21904,8 @@ var StorageKindSchema = object({
|
|
|
21401
21904
|
"orphaned",
|
|
21402
21905
|
"dropped"
|
|
21403
21906
|
]),
|
|
21907
|
+
/** An owner declared this kind since the engine started. */
|
|
21908
|
+
declaredThisBoot: boolean(),
|
|
21404
21909
|
/** `null` when the caller asked for no counts (`counts: false`). */
|
|
21405
21910
|
rows: number().int().nonnegative().nullable(),
|
|
21406
21911
|
/** Rows x the average size of a sample; null for an empty kind. */
|
|
@@ -21412,6 +21917,51 @@ var StorageKindSchema = object({
|
|
|
21412
21917
|
/** The whole declaration, secret paths and entry descriptor included (the entries service builds its handle from it). */
|
|
21413
21918
|
declaration: RecordKindDeclarationSchema
|
|
21414
21919
|
});
|
|
21920
|
+
_enum([
|
|
21921
|
+
"orphaned",
|
|
21922
|
+
"stale",
|
|
21923
|
+
"unknown"
|
|
21924
|
+
]);
|
|
21925
|
+
/** A record kind nobody declared since its owner started, or whose owner is gone. */
|
|
21926
|
+
var StaleKindFindingSchema = object({
|
|
21927
|
+
owner: string(),
|
|
21928
|
+
kind: string(),
|
|
21929
|
+
/** `unknown`: the owner could not be judged (not running, or its state unreadable) - never dropped. */
|
|
21930
|
+
verdict: _enum(["orphaned", "unknown"]),
|
|
21931
|
+
reason: string(),
|
|
21932
|
+
rows: number().int().nonnegative().nullable(),
|
|
21933
|
+
/** local-auth's tables are never dropped by this audit. */
|
|
21934
|
+
protected: boolean()
|
|
21935
|
+
});
|
|
21936
|
+
/** A stored global-settings key the running addon no longer declares. */
|
|
21937
|
+
var StaleSettingsKeyFindingSchema = object({
|
|
21938
|
+
addonId: string(),
|
|
21939
|
+
/** The stored key as written, a per-node `<key>@<nodeId>` suffix included. */
|
|
21940
|
+
key: string(),
|
|
21941
|
+
/**
|
|
21942
|
+
* Keychain refs whose ONLY marker sits under this key: in use by stale data,
|
|
21943
|
+
* not orphans yet. Removing the key releases them.
|
|
21944
|
+
*/
|
|
21945
|
+
secretRefs: array(string())
|
|
21946
|
+
});
|
|
21947
|
+
/** An addon the audit could not judge, with the reason. Never reported as stale. */
|
|
21948
|
+
var StaleUnknownSchema = object({
|
|
21949
|
+
subject: string(),
|
|
21950
|
+
reason: string()
|
|
21951
|
+
});
|
|
21952
|
+
var StaleDataReportSchema = object({
|
|
21953
|
+
/** Epoch ms of the last finished scan; `null` before the first. */
|
|
21954
|
+
scannedAt: number().nullable(),
|
|
21955
|
+
scanning: boolean(),
|
|
21956
|
+
trigger: _enum([
|
|
21957
|
+
"boot",
|
|
21958
|
+
"interval",
|
|
21959
|
+
"manual"
|
|
21960
|
+
]).nullable(),
|
|
21961
|
+
kinds: array(StaleKindFindingSchema),
|
|
21962
|
+
settingsKeys: array(StaleSettingsKeyFindingSchema),
|
|
21963
|
+
unknown: array(StaleUnknownSchema)
|
|
21964
|
+
});
|
|
21415
21965
|
method(object({ ownerPrefix: string().min(1).optional() }), object({ tables: array(StorageTableSchema) }), { auth: "admin" }), method(object({
|
|
21416
21966
|
owner: string().min(1).optional(),
|
|
21417
21967
|
/** `false` skips the per-kind row count (a catalog read only). Default true. */
|
|
@@ -21419,7 +21969,30 @@ method(object({ ownerPrefix: string().min(1).optional() }), object({ tables: arr
|
|
|
21419
21969
|
}), object({ kinds: array(StorageKindSchema) }), { auth: "admin" }), method(RecordQueryInputSchema.extend({
|
|
21420
21970
|
owner: string().min(1),
|
|
21421
21971
|
kind: RecordKindNameSchema
|
|
21422
|
-
}), RecordQueryResultSchema, { auth: "admin" })
|
|
21972
|
+
}), RecordQueryResultSchema, { auth: "admin" }), method(object({}), StaleDataReportSchema, { auth: "admin" }), method(object({}), StaleDataReportSchema, {
|
|
21973
|
+
kind: "mutation",
|
|
21974
|
+
auth: "admin"
|
|
21975
|
+
}), method(object({
|
|
21976
|
+
owner: string().min(1),
|
|
21977
|
+
kind: RecordKindNameSchema
|
|
21978
|
+
}), object({
|
|
21979
|
+
deleted: number().int().nonnegative(),
|
|
21980
|
+
secretsReleased: number().int().nonnegative()
|
|
21981
|
+
}), {
|
|
21982
|
+
kind: "mutation",
|
|
21983
|
+
auth: "admin",
|
|
21984
|
+
caller: "required"
|
|
21985
|
+
}), method(object({
|
|
21986
|
+
addonId: string().min(1),
|
|
21987
|
+
key: string().min(1)
|
|
21988
|
+
}), object({
|
|
21989
|
+
removed: boolean(),
|
|
21990
|
+
secretsCleared: number().int().nonnegative()
|
|
21991
|
+
}), {
|
|
21992
|
+
kind: "mutation",
|
|
21993
|
+
auth: "admin",
|
|
21994
|
+
caller: "required"
|
|
21995
|
+
});
|
|
21423
21996
|
/**
|
|
21424
21997
|
* record-entries - the ONE admin surface for every list of user-created entries
|
|
21425
21998
|
* (D881): notification targets, brokers, LLM profiles, anything an addon
|
|
@@ -21558,6 +22131,12 @@ method(object({
|
|
|
21558
22131
|
}), method(object({ deviceId: number().int() }), object({ refs: array(string()).readonly() }), {
|
|
21559
22132
|
kind: "mutation",
|
|
21560
22133
|
auth: "admin"
|
|
22134
|
+
}), method(object({
|
|
22135
|
+
refs: RefListSchema,
|
|
22136
|
+
onBehalfOf: string().min(1)
|
|
22137
|
+
}), object({ released: number().int() }), {
|
|
22138
|
+
kind: "mutation",
|
|
22139
|
+
auth: "admin"
|
|
21561
22140
|
}), method(object({}), array(KeychainSecretMetaSchema).readonly(), { auth: "admin" }), method(object({}), KeychainStateSchema, { auth: "admin" });
|
|
21562
22141
|
/**
|
|
21563
22142
|
* device-history — the ONE mechanism for numeric history (D850).
|
|
@@ -22347,7 +22926,9 @@ var DiscoveredChildDeviceSchema = lazy(() => object({
|
|
|
22347
22926
|
/** Upstream-system identity + rendering hints for the candidate. */
|
|
22348
22927
|
sourceInfo: SourceInfoSchema.optional(),
|
|
22349
22928
|
/** Nested entity-children for accordion preview — integrations populate, Hubs omit. */
|
|
22350
|
-
children: array(DiscoveredChildDeviceSchema).readonly().optional()
|
|
22929
|
+
children: array(DiscoveredChildDeviceSchema).readonly().optional(),
|
|
22930
|
+
/** `childNativeId` of the gateway candidate in the same list this device sits behind. */
|
|
22931
|
+
parentStableId: string().optional()
|
|
22351
22932
|
}));
|
|
22352
22933
|
/**
|
|
22353
22934
|
* Status block — populated by the integration after each
|
|
@@ -42662,6 +43243,79 @@ object({
|
|
|
42662
43243
|
metadata: record(string(), unknown()).optional()
|
|
42663
43244
|
});
|
|
42664
43245
|
/**
|
|
43246
|
+
* HOW a provider can be found on the network. The provider only DECLARES the
|
|
43247
|
+
* mode; the `local-network` scan engine runs it once for every provider that
|
|
43248
|
+
* asked (one multicast browse, one host sweep) and hands the raw hits back to
|
|
43249
|
+
* `classifyDiscoveryHits`. A provider never opens a discovery socket itself,
|
|
43250
|
+
* except through a `custom` mode.
|
|
43251
|
+
*/
|
|
43252
|
+
var DiscoveryModeSchema = discriminatedUnion("kind", [
|
|
43253
|
+
object({
|
|
43254
|
+
kind: literal("mdns"),
|
|
43255
|
+
/** DNS-SD service type, e.g. `_esphomelib._tcp`. */
|
|
43256
|
+
serviceType: string().min(1),
|
|
43257
|
+
/** Regular expression an instance name must match (case-insensitive). */
|
|
43258
|
+
instancePattern: string().optional()
|
|
43259
|
+
}),
|
|
43260
|
+
object({
|
|
43261
|
+
kind: literal("ws-discovery"),
|
|
43262
|
+
/** Probe types (e.g. `dn:NetworkVideoTransmitter`); omitted = any device. */
|
|
43263
|
+
types: array(string()).optional()
|
|
43264
|
+
}),
|
|
43265
|
+
object({
|
|
43266
|
+
kind: literal("ssdp"),
|
|
43267
|
+
searchTarget: string().min(1)
|
|
43268
|
+
}),
|
|
43269
|
+
object({
|
|
43270
|
+
kind: literal("udp-broadcast"),
|
|
43271
|
+
port: number().int().min(1).max(65535),
|
|
43272
|
+
payloadHex: string().regex(/^([0-9a-fA-F]{2})*$/),
|
|
43273
|
+
/** Port replies arrive on, when the device answers somewhere other than the source port. */
|
|
43274
|
+
listenPort: number().int().min(1).max(65535).optional()
|
|
43275
|
+
}),
|
|
43276
|
+
object({
|
|
43277
|
+
kind: literal("http-probe"),
|
|
43278
|
+
port: number().int().min(1).max(65535),
|
|
43279
|
+
path: string().startsWith("/"),
|
|
43280
|
+
method: _enum(["GET", "POST"]).default("GET"),
|
|
43281
|
+
bodyText: string().optional(),
|
|
43282
|
+
contentType: string().optional(),
|
|
43283
|
+
tls: boolean().default(false),
|
|
43284
|
+
/** Status codes that count as a hit. Omitted = any HTTP answer. */
|
|
43285
|
+
acceptStatus: array(number().int()).optional()
|
|
43286
|
+
}),
|
|
43287
|
+
object({
|
|
43288
|
+
kind: literal("custom"),
|
|
43289
|
+
/** Why the engine cannot do it (an allowlisted exception, e.g. a vendor UDP protocol). */
|
|
43290
|
+
reason: string().min(1)
|
|
43291
|
+
})
|
|
43292
|
+
]);
|
|
43293
|
+
/** What the engine saw on the wire for ONE declared mode; the provider classifies it. */
|
|
43294
|
+
var DiscoveryHitSchema = object({
|
|
43295
|
+
/** Index into the provider's own `getDiscoveryModes()` array. */
|
|
43296
|
+
modeIndex: number().int().min(0),
|
|
43297
|
+
host: string(),
|
|
43298
|
+
port: number().int(),
|
|
43299
|
+
http: object({
|
|
43300
|
+
status: number().int(),
|
|
43301
|
+
headers: record(string(), string()),
|
|
43302
|
+
/** At most 16 KiB. */
|
|
43303
|
+
bodyText: string()
|
|
43304
|
+
}).optional(),
|
|
43305
|
+
mdns: object({
|
|
43306
|
+
instance: string(),
|
|
43307
|
+
host: string(),
|
|
43308
|
+
port: number().int(),
|
|
43309
|
+
txt: record(string(), string())
|
|
43310
|
+
}).optional(),
|
|
43311
|
+
udp: object({ payloadHex: string() }).optional(),
|
|
43312
|
+
wsd: object({
|
|
43313
|
+
xaddrs: array(string()),
|
|
43314
|
+
types: array(string()),
|
|
43315
|
+
scopes: array(string())
|
|
43316
|
+
}).optional()
|
|
43317
|
+
});
|
|
43318
|
+
/**
|
|
42665
43319
|
* Candidate handed back from discovery and accepted by
|
|
42666
43320
|
* `adoptDiscoveredDevice`. Shape mirrors the in-process
|
|
42667
43321
|
* `DiscoveredDevice` interface used by `DeviceDiscovery`.
|
|
@@ -42696,7 +43350,17 @@ var DiscoveryCandidateSchema = object({
|
|
|
42696
43350
|
/** Numeric id of the device this candidate was matched to. Set with `alreadyOnboarded`. */
|
|
42697
43351
|
onboardedDeviceId: number().optional(),
|
|
42698
43352
|
/** Operator-facing name of the matched device, so the UI can say WHICH one it is. */
|
|
42699
|
-
onboardedName: string().optional()
|
|
43353
|
+
onboardedName: string().optional(),
|
|
43354
|
+
/**
|
|
43355
|
+
* This candidate is an INTEGRATION to set up (a gateway, a hub, a controller),
|
|
43356
|
+
* not a device to adopt. Replaces the `prefilledConfig.setupIntegration` marker,
|
|
43357
|
+
* which is still read for one release.
|
|
43358
|
+
*/
|
|
43359
|
+
setupIntegration: boolean().optional(),
|
|
43360
|
+
/** `stableId` of the gateway candidate this device was found BEHIND. */
|
|
43361
|
+
parentStableId: string().optional(),
|
|
43362
|
+
/** `false` = shown for context but not pickable. Default true. */
|
|
43363
|
+
selectable: boolean().optional()
|
|
42700
43364
|
});
|
|
42701
43365
|
/**
|
|
42702
43366
|
* Flat device summary returned by `createDevice` / `adoptDiscoveredDevice`.
|
|
@@ -42749,6 +43413,12 @@ method(_void(), _void(), { kind: "mutation" }), method(_void(), _void(), { kind:
|
|
|
42749
43413
|
}), method(object({}), boolean()), method(object({ params: record(string(), unknown()).optional() }), array(DiscoveryCandidateSchema), {
|
|
42750
43414
|
kind: "mutation",
|
|
42751
43415
|
auth: "admin"
|
|
43416
|
+
}), method(object({}), array(DiscoveryModeSchema)), method(object({
|
|
43417
|
+
hits: array(DiscoveryHitSchema),
|
|
43418
|
+
params: record(string(), unknown()).optional()
|
|
43419
|
+
}), array(DiscoveryCandidateSchema), {
|
|
43420
|
+
kind: "mutation",
|
|
43421
|
+
auth: "admin"
|
|
42752
43422
|
}), method(object({}), CreationSchemaOutputSchema), method(object({}), object({ deviceType: _enum(DeviceType).nullable() })), method(object({ candidate: DiscoveryCandidateSchema }), DeviceSummarySchema, {
|
|
42753
43423
|
kind: "mutation",
|
|
42754
43424
|
auth: "admin"
|
|
@@ -43476,17 +44146,23 @@ method(object({
|
|
|
43476
44146
|
}), method(object({}), object({ providers: array(object({
|
|
43477
44147
|
addonId: string(),
|
|
43478
44148
|
label: string()
|
|
43479
|
-
})).readonly() }), { auth: "admin" }), method(
|
|
43480
|
-
|
|
43481
|
-
|
|
43482
|
-
|
|
43483
|
-
|
|
43484
|
-
|
|
43485
|
-
|
|
43486
|
-
|
|
43487
|
-
}),
|
|
44149
|
+
})).readonly() }), { auth: "admin" }), method(
|
|
44150
|
+
/** `subnets`: extra CIDRs for THIS scan, on top of the hub's own and the saved list. */
|
|
44151
|
+
object({ subnets: array(string()).optional() }),
|
|
44152
|
+
object({ groups: array(object({
|
|
44153
|
+
addonId: string(),
|
|
44154
|
+
label: string(),
|
|
44155
|
+
candidates: array(DiscoveryCandidateSchema).readonly(),
|
|
44156
|
+
error: string().nullable()
|
|
44157
|
+
})).readonly() }),
|
|
44158
|
+
{
|
|
44159
|
+
kind: "mutation",
|
|
44160
|
+
auth: "admin"
|
|
44161
|
+
}
|
|
44162
|
+
), method(object({
|
|
43488
44163
|
addonId: string(),
|
|
43489
|
-
params: record(string(), unknown()).optional()
|
|
44164
|
+
params: record(string(), unknown()).optional(),
|
|
44165
|
+
subnets: array(string()).optional()
|
|
43490
44166
|
}), object({ candidates: array(DiscoveryCandidateSchema).readonly() }), {
|
|
43491
44167
|
kind: "mutation",
|
|
43492
44168
|
auth: "admin"
|
|
@@ -61106,7 +61782,9 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
|
|
|
61106
61782
|
id: string(),
|
|
61107
61783
|
settings: record(string(), unknown()),
|
|
61108
61784
|
/** Keys the form's schema declares secret (by type) — keychain-stored (D787). */
|
|
61109
|
-
secretKeys: array(string()).optional()
|
|
61785
|
+
secretKeys: array(string()).optional(),
|
|
61786
|
+
/** The integration's own addon applies the change in-process and asks the hub not to respawn it. */
|
|
61787
|
+
skipRestart: boolean().optional()
|
|
61110
61788
|
}), object({ success: boolean() }), {
|
|
61111
61789
|
kind: "mutation",
|
|
61112
61790
|
auth: "admin"
|
|
@@ -61583,6 +62261,39 @@ var TlsStatusSchema = object({
|
|
|
61583
62261
|
hasCa: boolean()
|
|
61584
62262
|
}).nullable()
|
|
61585
62263
|
});
|
|
62264
|
+
/** One provider's ask of the scan engine: the modes it declared. */
|
|
62265
|
+
var ScanRequestSchema = object({
|
|
62266
|
+
providerId: string().min(1),
|
|
62267
|
+
modes: array(DiscoveryModeSchema)
|
|
62268
|
+
});
|
|
62269
|
+
var ScanErrorSchema = object({
|
|
62270
|
+
/** Absent for a failure that is the scan's own (a bad subnet, a socket that would not open). */
|
|
62271
|
+
providerId: string().optional(),
|
|
62272
|
+
/** The mode family that failed (`mdns`, `http-probe`, …). */
|
|
62273
|
+
mode: string().optional(),
|
|
62274
|
+
message: string()
|
|
62275
|
+
});
|
|
62276
|
+
/** What one scan did, per mode family; the engine logs the same numbers once. */
|
|
62277
|
+
var ScanStatsSchema = object({
|
|
62278
|
+
hostsProbed: number().int(),
|
|
62279
|
+
hits: number().int(),
|
|
62280
|
+
timeouts: number().int(),
|
|
62281
|
+
byMode: record(string(), object({
|
|
62282
|
+
probed: number().int(),
|
|
62283
|
+
hits: number().int(),
|
|
62284
|
+
timeouts: number().int()
|
|
62285
|
+
}))
|
|
62286
|
+
});
|
|
62287
|
+
var ScanNetworkResultSchema = object({
|
|
62288
|
+
hits: array(object({
|
|
62289
|
+
providerId: string(),
|
|
62290
|
+
hit: DiscoveryHitSchema
|
|
62291
|
+
})),
|
|
62292
|
+
/** CIDRs actually covered: the hub's own /24s, the saved list and the per-call list. */
|
|
62293
|
+
subnetsScanned: array(string()),
|
|
62294
|
+
errors: array(ScanErrorSchema),
|
|
62295
|
+
stats: ScanStatsSchema
|
|
62296
|
+
});
|
|
61586
62297
|
method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
|
|
61587
62298
|
/**
|
|
61588
62299
|
* LEGACY HINT — do not send from new code. Kept optional so clients
|
|
@@ -61637,6 +62348,16 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
|
|
|
61637
62348
|
}), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
|
|
61638
62349
|
kind: "mutation",
|
|
61639
62350
|
auth: "admin"
|
|
62351
|
+
}), method(object({
|
|
62352
|
+
requests: array(ScanRequestSchema),
|
|
62353
|
+
subnets: array(string()).optional()
|
|
62354
|
+
}), ScanNetworkResultSchema, {
|
|
62355
|
+
kind: "mutation",
|
|
62356
|
+
auth: "admin",
|
|
62357
|
+
timeoutMs: 9e4
|
|
62358
|
+
}), method(_void(), object({ subnets: array(string()) }), { auth: "admin" }), method(object({ subnets: array(string()) }), object({ subnets: array(string()) }), {
|
|
62359
|
+
kind: "mutation",
|
|
62360
|
+
auth: "admin"
|
|
61640
62361
|
}), method(_void(), TlsStatusSchema, {
|
|
61641
62362
|
kind: "mutation",
|
|
61642
62363
|
auth: "admin"
|
|
@@ -64179,7 +64900,7 @@ object({
|
|
|
64179
64900
|
});
|
|
64180
64901
|
Object.values(DeviceType);
|
|
64181
64902
|
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
|
|
64182
|
-
DeviceType.Camera, DeviceType.Sensor, DeviceType.Switch, method(object({ deviceId: number().int().nonnegative() }), object({ success: literal(true) }), {
|
|
64903
|
+
DeviceType.Camera, DeviceType.Sensor, DeviceType.Switch, DeviceType.Gateway, method(object({ deviceId: number().int().nonnegative() }), object({ success: literal(true) }), {
|
|
64183
64904
|
kind: "mutation",
|
|
64184
64905
|
auth: "admin"
|
|
64185
64906
|
});
|
|
@@ -68139,7 +68860,7 @@ discriminatedUnion("kind", [object({
|
|
|
68139
68860
|
})]);
|
|
68140
68861
|
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
68141
68862
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
68142
|
-
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;
|
|
68863
|
+
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;
|
|
68143
68864
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
68144
68865
|
DeviceRole.BinarySensor, DeviceRole.MotionSensor;
|
|
68145
68866
|
new Set(CAP_NAMES);
|
|
@@ -68192,7 +68913,7 @@ var AccessoryKind = {
|
|
|
68192
68913
|
PrivacyMask: DeviceRole.PrivacyMask
|
|
68193
68914
|
};
|
|
68194
68915
|
AccessoryKind.Siren, AccessoryKind.Floodlight, AccessoryKind.Spotlight, AccessoryKind.PirSensor, AccessoryKind.Chime, AccessoryKind.Autotrack, AccessoryKind.Nightvision, AccessoryKind.PrivacyMask;
|
|
68195
|
-
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;
|
|
68916
|
+
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;
|
|
68196
68917
|
new Set(Object.values(DeviceType));
|
|
68197
68918
|
Object.freeze({
|
|
68198
68919
|
"accessories.getStatus": {
|
|
@@ -68580,6 +69301,13 @@ Object.freeze({
|
|
|
68580
69301
|
access: "create",
|
|
68581
69302
|
role: "admin"
|
|
68582
69303
|
},
|
|
69304
|
+
"addonSettings.getDeclaredSettingsKeys": {
|
|
69305
|
+
capName: "addon-settings",
|
|
69306
|
+
capScope: "system",
|
|
69307
|
+
addonId: null,
|
|
69308
|
+
access: "view",
|
|
69309
|
+
role: "admin"
|
|
69310
|
+
},
|
|
68583
69311
|
"addonSettings.getDeviceSettings": {
|
|
68584
69312
|
capName: "addon-settings",
|
|
68585
69313
|
capScope: "system",
|
|
@@ -68601,6 +69329,13 @@ Object.freeze({
|
|
|
68601
69329
|
access: "view",
|
|
68602
69330
|
role: "admin"
|
|
68603
69331
|
},
|
|
69332
|
+
"addonSettings.unsetStoredSettingsKeys": {
|
|
69333
|
+
capName: "addon-settings",
|
|
69334
|
+
capScope: "system",
|
|
69335
|
+
addonId: null,
|
|
69336
|
+
access: "create",
|
|
69337
|
+
role: "admin"
|
|
69338
|
+
},
|
|
68604
69339
|
"addonSettings.updateDeviceSettings": {
|
|
68605
69340
|
capName: "addon-settings",
|
|
68606
69341
|
capScope: "system",
|
|
@@ -71016,6 +71751,13 @@ Object.freeze({
|
|
|
71016
71751
|
access: "create",
|
|
71017
71752
|
role: "admin"
|
|
71018
71753
|
},
|
|
71754
|
+
"deviceProvider.classifyDiscoveryHits": {
|
|
71755
|
+
capName: "device-provider",
|
|
71756
|
+
capScope: "system",
|
|
71757
|
+
addonId: null,
|
|
71758
|
+
access: "create",
|
|
71759
|
+
role: "admin"
|
|
71760
|
+
},
|
|
71019
71761
|
"deviceProvider.createDevice": {
|
|
71020
71762
|
capName: "device-provider",
|
|
71021
71763
|
capScope: "system",
|
|
@@ -71044,6 +71786,13 @@ Object.freeze({
|
|
|
71044
71786
|
access: "view",
|
|
71045
71787
|
role: "admin"
|
|
71046
71788
|
},
|
|
71789
|
+
"deviceProvider.getDiscoveryModes": {
|
|
71790
|
+
capName: "device-provider",
|
|
71791
|
+
capScope: "system",
|
|
71792
|
+
addonId: null,
|
|
71793
|
+
access: "view",
|
|
71794
|
+
role: "admin"
|
|
71795
|
+
},
|
|
71047
71796
|
"deviceProvider.getDiscoveryParamsSchema": {
|
|
71048
71797
|
capName: "device-provider",
|
|
71049
71798
|
capScope: "system",
|
|
@@ -71821,6 +72570,13 @@ Object.freeze({
|
|
|
71821
72570
|
access: "create",
|
|
71822
72571
|
role: "admin"
|
|
71823
72572
|
},
|
|
72573
|
+
"keychain.releaseRecordRefs": {
|
|
72574
|
+
capName: "keychain",
|
|
72575
|
+
capScope: "system",
|
|
72576
|
+
addonId: null,
|
|
72577
|
+
access: "create",
|
|
72578
|
+
role: "admin"
|
|
72579
|
+
},
|
|
71824
72580
|
"keychain.resolve": {
|
|
71825
72581
|
capName: "keychain",
|
|
71826
72582
|
capScope: "system",
|
|
@@ -72241,6 +72997,13 @@ Object.freeze({
|
|
|
72241
72997
|
access: "view",
|
|
72242
72998
|
role: "admin"
|
|
72243
72999
|
},
|
|
73000
|
+
"localNetwork.getScanSubnets": {
|
|
73001
|
+
capName: "local-network",
|
|
73002
|
+
capScope: "system",
|
|
73003
|
+
addonId: null,
|
|
73004
|
+
access: "view",
|
|
73005
|
+
role: "admin"
|
|
73006
|
+
},
|
|
72244
73007
|
"localNetwork.getTlsStatus": {
|
|
72245
73008
|
capName: "local-network",
|
|
72246
73009
|
capScope: "system",
|
|
@@ -72290,6 +73053,13 @@ Object.freeze({
|
|
|
72290
73053
|
access: "create",
|
|
72291
73054
|
role: "admin"
|
|
72292
73055
|
},
|
|
73056
|
+
"localNetwork.scanNetwork": {
|
|
73057
|
+
capName: "local-network",
|
|
73058
|
+
capScope: "system",
|
|
73059
|
+
addonId: null,
|
|
73060
|
+
access: "create",
|
|
73061
|
+
role: "admin"
|
|
73062
|
+
},
|
|
72293
73063
|
"localNetwork.setAddressRole": {
|
|
72294
73064
|
capName: "local-network",
|
|
72295
73065
|
capScope: "system",
|
|
@@ -72311,6 +73081,13 @@ Object.freeze({
|
|
|
72311
73081
|
access: "create",
|
|
72312
73082
|
role: "admin"
|
|
72313
73083
|
},
|
|
73084
|
+
"localNetwork.setScanSubnets": {
|
|
73085
|
+
capName: "local-network",
|
|
73086
|
+
capScope: "system",
|
|
73087
|
+
addonId: null,
|
|
73088
|
+
access: "create",
|
|
73089
|
+
role: "admin"
|
|
73090
|
+
},
|
|
72314
73091
|
"localNetwork.setViewerEndpoints": {
|
|
72315
73092
|
capName: "local-network",
|
|
72316
73093
|
capScope: "system",
|
|
@@ -76532,6 +77309,13 @@ Object.freeze({
|
|
|
76532
77309
|
access: "create",
|
|
76533
77310
|
role: "admin"
|
|
76534
77311
|
},
|
|
77312
|
+
"storageAdmin.dropOrphanedKind": {
|
|
77313
|
+
capName: "storage-admin",
|
|
77314
|
+
capScope: "system",
|
|
77315
|
+
addonId: null,
|
|
77316
|
+
access: "delete",
|
|
77317
|
+
role: "admin"
|
|
77318
|
+
},
|
|
76535
77319
|
"storageAdmin.listKinds": {
|
|
76536
77320
|
capName: "storage-admin",
|
|
76537
77321
|
capScope: "system",
|
|
@@ -76553,6 +77337,27 @@ Object.freeze({
|
|
|
76553
77337
|
access: "view",
|
|
76554
77338
|
role: "admin"
|
|
76555
77339
|
},
|
|
77340
|
+
"storageAdmin.scanStaleData": {
|
|
77341
|
+
capName: "storage-admin",
|
|
77342
|
+
capScope: "system",
|
|
77343
|
+
addonId: null,
|
|
77344
|
+
access: "create",
|
|
77345
|
+
role: "admin"
|
|
77346
|
+
},
|
|
77347
|
+
"storageAdmin.staleDataReport": {
|
|
77348
|
+
capName: "storage-admin",
|
|
77349
|
+
capScope: "system",
|
|
77350
|
+
addonId: null,
|
|
77351
|
+
access: "view",
|
|
77352
|
+
role: "admin"
|
|
77353
|
+
},
|
|
77354
|
+
"storageAdmin.unsetStaleSettingsKey": {
|
|
77355
|
+
capName: "storage-admin",
|
|
77356
|
+
capScope: "system",
|
|
77357
|
+
addonId: null,
|
|
77358
|
+
access: "create",
|
|
77359
|
+
role: "admin"
|
|
77360
|
+
},
|
|
76556
77361
|
"storageEvictable.evict": {
|
|
76557
77362
|
capName: "storage-evictable",
|
|
76558
77363
|
capScope: "system",
|
|
@@ -82249,7 +83054,7 @@ function projectAction(cap, action) {
|
|
|
82249
83054
|
};
|
|
82250
83055
|
}
|
|
82251
83056
|
Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
|
|
82252
|
-
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;
|
|
83057
|
+
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;
|
|
82253
83058
|
DeviceFeature.BatteryOperated;
|
|
82254
83059
|
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
82255
83060
|
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.";
|