@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
|
@@ -5588,7 +5588,7 @@ var ZodIssueCode = {
|
|
|
5588
5588
|
var ZodFirstPartyTypeKind;
|
|
5589
5589
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5590
5590
|
//#endregion
|
|
5591
|
-
//#region ../types/dist/sleep-
|
|
5591
|
+
//#region ../types/dist/sleep-CeyhrcOk.mjs
|
|
5592
5592
|
/**
|
|
5593
5593
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5594
5594
|
* window to float samples (D455).
|
|
@@ -7655,6 +7655,7 @@ var BaseAddon = class {
|
|
|
7655
7655
|
* a warning and falls back rather than crashing boot.
|
|
7656
7656
|
*/
|
|
7657
7657
|
state(key, schema, fallback) {
|
|
7658
|
+
this._declaredStateKeys.add(key);
|
|
7658
7659
|
return createDurableState({
|
|
7659
7660
|
key,
|
|
7660
7661
|
schema,
|
|
@@ -7670,6 +7671,96 @@ var BaseAddon = class {
|
|
|
7670
7671
|
} })
|
|
7671
7672
|
});
|
|
7672
7673
|
}
|
|
7674
|
+
/** Durable-state keys this process registered through {@link state}. */
|
|
7675
|
+
_declaredStateKeys = /* @__PURE__ */ new Set();
|
|
7676
|
+
/**
|
|
7677
|
+
* Stale-data audit: every global-settings key this addon still declares —
|
|
7678
|
+
* constructor defaults, settings-schema fields (layout containers included),
|
|
7679
|
+
* durable-state keys and the root of each `secretStatePaths` entry. Answered
|
|
7680
|
+
* here, in the addon's own process, so the hub keeps no copy of it.
|
|
7681
|
+
*/
|
|
7682
|
+
async getDeclaredSettingsKeys() {
|
|
7683
|
+
return { keys: [...this.declaredKeySet()].toSorted() };
|
|
7684
|
+
}
|
|
7685
|
+
declaredKeySet() {
|
|
7686
|
+
const keys = new Set(Object.keys(this.defaults));
|
|
7687
|
+
const schema = this.globalSettingsSchema();
|
|
7688
|
+
if (schema) for (const section of schema.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
7689
|
+
for (const key of this._declaredStateKeys) keys.add(key);
|
|
7690
|
+
for (const path of this.secretStatePaths) {
|
|
7691
|
+
const root = path.split(/[.[]/, 1)[0];
|
|
7692
|
+
if (root) keys.add(root);
|
|
7693
|
+
}
|
|
7694
|
+
return keys;
|
|
7695
|
+
}
|
|
7696
|
+
/**
|
|
7697
|
+
* Stale-data audit: remove stored global-settings keys the addon no longer
|
|
7698
|
+
* declares, and clear the keychain values their markers held — except a value
|
|
7699
|
+
* a surviving key still points at. A declared or absent key is refused.
|
|
7700
|
+
*/
|
|
7701
|
+
async unsetStoredSettingsKeys(keys) {
|
|
7702
|
+
const settings = this._ctx?.settings;
|
|
7703
|
+
const raw = this.rawSettings();
|
|
7704
|
+
if (!settings || !raw) return {
|
|
7705
|
+
removed: [],
|
|
7706
|
+
refused: keys.map((key) => ({
|
|
7707
|
+
key,
|
|
7708
|
+
reason: "no settings store"
|
|
7709
|
+
})),
|
|
7710
|
+
secretsCleared: 0
|
|
7711
|
+
};
|
|
7712
|
+
let stored;
|
|
7713
|
+
try {
|
|
7714
|
+
stored = await raw.readAddonStore();
|
|
7715
|
+
} catch (error) {
|
|
7716
|
+
const reason = `store unreadable: ${error instanceof Error ? error.message : "unknown"}`;
|
|
7717
|
+
return {
|
|
7718
|
+
removed: [],
|
|
7719
|
+
refused: keys.map((key) => ({
|
|
7720
|
+
key,
|
|
7721
|
+
reason
|
|
7722
|
+
})),
|
|
7723
|
+
secretsCleared: 0
|
|
7724
|
+
};
|
|
7725
|
+
}
|
|
7726
|
+
const declared = this.declaredKeySet();
|
|
7727
|
+
const removed = [];
|
|
7728
|
+
const refused = [];
|
|
7729
|
+
for (const key of keys) {
|
|
7730
|
+
const base = key.split("@", 1)[0] ?? key;
|
|
7731
|
+
if (declared.has(base)) refused.push({
|
|
7732
|
+
key,
|
|
7733
|
+
reason: "still declared by the addon"
|
|
7734
|
+
});
|
|
7735
|
+
else if (!(key in stored)) refused.push({
|
|
7736
|
+
key,
|
|
7737
|
+
reason: "not stored"
|
|
7738
|
+
});
|
|
7739
|
+
else removed.push(key);
|
|
7740
|
+
}
|
|
7741
|
+
if (removed.length === 0) return {
|
|
7742
|
+
removed,
|
|
7743
|
+
refused,
|
|
7744
|
+
secretsCleared: 0
|
|
7745
|
+
};
|
|
7746
|
+
const kept = /* @__PURE__ */ new Set();
|
|
7747
|
+
const held = /* @__PURE__ */ new Set();
|
|
7748
|
+
for (const [key, value] of Object.entries(stored)) {
|
|
7749
|
+
const target = removed.includes(key) ? held : kept;
|
|
7750
|
+
for (const ref of collectSecretMarkerRefs(value)) target.add(ref);
|
|
7751
|
+
}
|
|
7752
|
+
const refsToClear = [...held].filter((ref) => !kept.has(ref));
|
|
7753
|
+
const patch = {};
|
|
7754
|
+
for (const key of removed) patch[key] = void 0;
|
|
7755
|
+
await settings.writeAddonStore(patch);
|
|
7756
|
+
const secretsCleared = refsToClear.length > 0 && this._ctx?.secrets ? await this._ctx.secrets.delete(refsToClear) : 0;
|
|
7757
|
+
await this.resolveConfig();
|
|
7758
|
+
return {
|
|
7759
|
+
removed,
|
|
7760
|
+
refused,
|
|
7761
|
+
secretsCleared
|
|
7762
|
+
};
|
|
7763
|
+
}
|
|
7673
7764
|
/** Per-device variant of {@link state}, backed by the per-device store. */
|
|
7674
7765
|
deviceState(deviceId, key, schema, fallback) {
|
|
7675
7766
|
return createDurableState({
|
|
@@ -7940,6 +8031,21 @@ function dropPerNodeFields(fields) {
|
|
|
7940
8031
|
}
|
|
7941
8032
|
return kept;
|
|
7942
8033
|
}
|
|
8034
|
+
function collectFieldKeys(fields) {
|
|
8035
|
+
const collected = [];
|
|
8036
|
+
for (const field of fields) {
|
|
8037
|
+
if (field.type === "group") {
|
|
8038
|
+
collected.push(...collectFieldKeys(field.fields));
|
|
8039
|
+
continue;
|
|
8040
|
+
}
|
|
8041
|
+
if (field.type === "sub-tabs") {
|
|
8042
|
+
for (const tab of field.tabs) collected.push(...collectFieldKeys(tab.fields));
|
|
8043
|
+
continue;
|
|
8044
|
+
}
|
|
8045
|
+
if ("key" in field && typeof field.key === "string") collected.push(field.key);
|
|
8046
|
+
}
|
|
8047
|
+
return collected;
|
|
8048
|
+
}
|
|
7943
8049
|
function collectPerNodeFieldKeys(fields) {
|
|
7944
8050
|
const collected = [];
|
|
7945
8051
|
for (const field of fields) {
|
|
@@ -10962,7 +11068,7 @@ object({
|
|
|
10962
11068
|
params: record(string(), union([string(), number()])).optional()
|
|
10963
11069
|
});
|
|
10964
11070
|
//#endregion
|
|
10965
|
-
//#region ../types/dist/err-msg-
|
|
11071
|
+
//#region ../types/dist/err-msg-BD5buxvT.mjs
|
|
10966
11072
|
var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
10967
11073
|
DeviceType["Camera"] = "camera";
|
|
10968
11074
|
DeviceType["Hub"] = "hub";
|
|
@@ -11068,6 +11174,12 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
|
11068
11174
|
DeviceType["Washer"] = "washer";
|
|
11069
11175
|
/** Tumble dryer. Same plug-built running surface as {@link Washer}. */
|
|
11070
11176
|
DeviceType["Dryer"] = "dryer";
|
|
11177
|
+
/** The physical bridge box of a vendor ecosystem (Velux KLF-200, and later
|
|
11178
|
+
* other radio-to-IP bridges). It carries the box's own facts (firmware,
|
|
11179
|
+
* address, connection state) and the actions the box really supports
|
|
11180
|
+
* (`reboot`, `device-admin-link`). Products are NOT its children — they are
|
|
11181
|
+
* adopted beside it — and its connection settings stay on the integration. */
|
|
11182
|
+
DeviceType["Gateway"] = "gateway";
|
|
11071
11183
|
return DeviceType;
|
|
11072
11184
|
}({});
|
|
11073
11185
|
var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
|
|
@@ -11318,7 +11430,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
|
|
|
11318
11430
|
return DeviceRole;
|
|
11319
11431
|
}({});
|
|
11320
11432
|
//#endregion
|
|
11321
|
-
//#region ../types/dist/composition-
|
|
11433
|
+
//#region ../types/dist/composition-C7WdThmP.mjs
|
|
11322
11434
|
/**
|
|
11323
11435
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
11324
11436
|
*
|
|
@@ -16325,8 +16437,172 @@ object({
|
|
|
16325
16437
|
quantity: ReadingQuantitySchema,
|
|
16326
16438
|
unit: string().min(1)
|
|
16327
16439
|
});
|
|
16328
|
-
|
|
16329
|
-
|
|
16440
|
+
new Set([
|
|
16441
|
+
{
|
|
16442
|
+
id: "person",
|
|
16443
|
+
name: "Person"
|
|
16444
|
+
},
|
|
16445
|
+
{
|
|
16446
|
+
id: "vehicle",
|
|
16447
|
+
name: "Vehicle"
|
|
16448
|
+
},
|
|
16449
|
+
{
|
|
16450
|
+
id: "animal",
|
|
16451
|
+
name: "Animal"
|
|
16452
|
+
},
|
|
16453
|
+
{
|
|
16454
|
+
id: "package",
|
|
16455
|
+
name: "Package"
|
|
16456
|
+
}
|
|
16457
|
+
].map((l) => l.id));
|
|
16458
|
+
var COCO_TO_MACRO = {
|
|
16459
|
+
mapping: {
|
|
16460
|
+
person: "person",
|
|
16461
|
+
bicycle: "vehicle",
|
|
16462
|
+
car: "vehicle",
|
|
16463
|
+
motorcycle: "vehicle",
|
|
16464
|
+
airplane: "vehicle",
|
|
16465
|
+
bus: "vehicle",
|
|
16466
|
+
train: "vehicle",
|
|
16467
|
+
truck: "vehicle",
|
|
16468
|
+
boat: "vehicle",
|
|
16469
|
+
bird: "animal",
|
|
16470
|
+
cat: "animal",
|
|
16471
|
+
dog: "animal",
|
|
16472
|
+
horse: "animal",
|
|
16473
|
+
sheep: "animal",
|
|
16474
|
+
cow: "animal",
|
|
16475
|
+
elephant: "animal",
|
|
16476
|
+
bear: "animal",
|
|
16477
|
+
zebra: "animal",
|
|
16478
|
+
giraffe: "animal",
|
|
16479
|
+
suitcase: "package",
|
|
16480
|
+
backpack: "package",
|
|
16481
|
+
handbag: "package"
|
|
16482
|
+
},
|
|
16483
|
+
preserveOriginal: false
|
|
16484
|
+
};
|
|
16485
|
+
/**
|
|
16486
|
+
* WHAT a detection-shaped leaf is about: one MACRO class (person, vehicle,
|
|
16487
|
+
* animal, package) plus an OPTIONAL label that narrows it.
|
|
16488
|
+
*
|
|
16489
|
+
* The taxonomy is `COCO_TO_MACRO` (and `CLASS_MAP_MACRO_TARGETS`) — never a
|
|
16490
|
+
* second list. The label that fits depends on the macro class:
|
|
16491
|
+
*
|
|
16492
|
+
* - `animal` → a sub-class (`dog`, `cat`, …);
|
|
16493
|
+
* - `person` → a known face (an identity of the `face-gallery`);
|
|
16494
|
+
* - `vehicle` → a plate (its text) or a sub-class (`car`, `truck`, …);
|
|
16495
|
+
* - `package` → a sub-class.
|
|
16496
|
+
*
|
|
16497
|
+
* `other` exists only so a class the taxonomy does not know (a custom model's
|
|
16498
|
+
* `forklift`) survives the conversion of a stored recipe without widening.
|
|
16499
|
+
*
|
|
16500
|
+
* A label is decided LATE (D683): face recognition and LPR land after the
|
|
16501
|
+
* detection starts. The track-lifecycle `update` event carries the newly
|
|
16502
|
+
* resolved `label` / `plateText`, which is why a leaf with a label fires when
|
|
16503
|
+
* the subject GETS it — not at the first detection.
|
|
16504
|
+
*/
|
|
16505
|
+
var SubClassLabelSchema = object({
|
|
16506
|
+
kind: literal("sub-class"),
|
|
16507
|
+
className: string().min(1)
|
|
16508
|
+
});
|
|
16509
|
+
var IdentityLabelSchema = object({
|
|
16510
|
+
kind: literal("identity"),
|
|
16511
|
+
/** The `face-gallery` identity id (stable across a rename). */
|
|
16512
|
+
identityId: string().min(1),
|
|
16513
|
+
/** The identity's name — what a track-lifecycle event carries as `label`. */
|
|
16514
|
+
name: string().min(1)
|
|
16515
|
+
});
|
|
16516
|
+
var PlateLabelSchema = object({
|
|
16517
|
+
kind: literal("plate"),
|
|
16518
|
+
text: string().min(1)
|
|
16519
|
+
});
|
|
16520
|
+
function macroOf$1(className) {
|
|
16521
|
+
return COCO_TO_MACRO.mapping[className.toLowerCase()] ?? className.toLowerCase();
|
|
16522
|
+
}
|
|
16523
|
+
/** A sub-class label must belong to the macro it is under. */
|
|
16524
|
+
function subClassBelongs(macro, label) {
|
|
16525
|
+
return label === void 0 || macroOf$1(label.className) === macro;
|
|
16526
|
+
}
|
|
16527
|
+
var subClassOf = (macro) => SubClassLabelSchema.refine((l) => subClassBelongs(macro, l), { message: `that class is not a kind of ${macro} in the detection taxonomy` });
|
|
16528
|
+
var DetectionSubjectSchema = discriminatedUnion("macro", [
|
|
16529
|
+
object({
|
|
16530
|
+
macro: literal("animal"),
|
|
16531
|
+
label: subClassOf("animal").optional()
|
|
16532
|
+
}),
|
|
16533
|
+
object({
|
|
16534
|
+
macro: literal("person"),
|
|
16535
|
+
label: IdentityLabelSchema.optional()
|
|
16536
|
+
}),
|
|
16537
|
+
object({
|
|
16538
|
+
macro: literal("vehicle"),
|
|
16539
|
+
label: discriminatedUnion("kind", [SubClassLabelSchema.refine((l) => subClassBelongs("vehicle", l), { message: "that class is not a kind of vehicle in the detection taxonomy" }), PlateLabelSchema]).optional()
|
|
16540
|
+
}),
|
|
16541
|
+
object({
|
|
16542
|
+
macro: literal("package"),
|
|
16543
|
+
label: subClassOf("package").optional()
|
|
16544
|
+
}),
|
|
16545
|
+
object({
|
|
16546
|
+
macro: literal("other"),
|
|
16547
|
+
className: string().min(1)
|
|
16548
|
+
})
|
|
16549
|
+
]);
|
|
16550
|
+
/**
|
|
16551
|
+
* The subject of a PARKED object. The stationary registry's event carries a
|
|
16552
|
+
* class and a zone, no face and no plate — an identity or plate label there
|
|
16553
|
+
* could never match, so the schema refuses it instead of letting it sit dead.
|
|
16554
|
+
*/
|
|
16555
|
+
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" });
|
|
16556
|
+
/**
|
|
16557
|
+
* A stored class string as a subject: a macro name stays a macro; a raw class
|
|
16558
|
+
* the taxonomy files under a macro becomes that macro plus the sub-class label
|
|
16559
|
+
* (`dog` → animal / dog); anything else is `other`, kept verbatim.
|
|
16560
|
+
*/
|
|
16561
|
+
function subjectFromClassName(className) {
|
|
16562
|
+
const name = className.trim().toLowerCase();
|
|
16563
|
+
const macro = COCO_TO_MACRO.mapping[name];
|
|
16564
|
+
if (new Set(Object.values(COCO_TO_MACRO.mapping)).has(name)) return macroOnly(name);
|
|
16565
|
+
if (macro === void 0) return {
|
|
16566
|
+
macro: "other",
|
|
16567
|
+
className
|
|
16568
|
+
};
|
|
16569
|
+
const label = {
|
|
16570
|
+
kind: "sub-class",
|
|
16571
|
+
className: name
|
|
16572
|
+
};
|
|
16573
|
+
switch (macro) {
|
|
16574
|
+
case "animal": return {
|
|
16575
|
+
macro: "animal",
|
|
16576
|
+
label
|
|
16577
|
+
};
|
|
16578
|
+
case "vehicle": return {
|
|
16579
|
+
macro: "vehicle",
|
|
16580
|
+
label
|
|
16581
|
+
};
|
|
16582
|
+
case "package": return {
|
|
16583
|
+
macro: "package",
|
|
16584
|
+
label
|
|
16585
|
+
};
|
|
16586
|
+
default: return {
|
|
16587
|
+
macro: "other",
|
|
16588
|
+
className
|
|
16589
|
+
};
|
|
16590
|
+
}
|
|
16591
|
+
}
|
|
16592
|
+
function macroOnly(macro) {
|
|
16593
|
+
switch (macro) {
|
|
16594
|
+
case "animal": return { macro: "animal" };
|
|
16595
|
+
case "person": return { macro: "person" };
|
|
16596
|
+
case "vehicle": return { macro: "vehicle" };
|
|
16597
|
+
case "package": return { macro: "package" };
|
|
16598
|
+
default: return {
|
|
16599
|
+
macro: "other",
|
|
16600
|
+
className: macro
|
|
16601
|
+
};
|
|
16602
|
+
}
|
|
16603
|
+
}
|
|
16604
|
+
/** How a state leaf compares a device field to a value. Derived from the
|
|
16605
|
+
* field's `kind` in `deviceManager.getWireableFields`, never hand-maintained. */
|
|
16330
16606
|
var AutomationConditionOperatorSchema = _enum([
|
|
16331
16607
|
"eq",
|
|
16332
16608
|
"ne",
|
|
@@ -16337,12 +16613,76 @@ var AutomationConditionOperatorSchema = _enum([
|
|
|
16337
16613
|
"contains",
|
|
16338
16614
|
"in"
|
|
16339
16615
|
]);
|
|
16340
|
-
|
|
16341
|
-
|
|
16616
|
+
/**
|
|
16617
|
+
* D8 compliance, and it is the reason a "becomes" leaf is not merely an event
|
|
16618
|
+
* subscription: it evaluates against the **state mirror**, which is reconciled,
|
|
16619
|
+
* and an event only WAKES the evaluation. A dropped event therefore DELAYS a
|
|
16620
|
+
* fire; it does not lose it.
|
|
16621
|
+
*/
|
|
16622
|
+
var DeviceBecomesLeafSchema = object({
|
|
16623
|
+
kind: literal("device-becomes"),
|
|
16624
|
+
deviceId: number().int().nonnegative(),
|
|
16625
|
+
cap: string().min(1),
|
|
16626
|
+
fieldPath: string().min(1),
|
|
16627
|
+
/** Fire when the field takes this value. Omit to fire on any change. */
|
|
16628
|
+
becomes: union([
|
|
16629
|
+
string(),
|
|
16630
|
+
number(),
|
|
16631
|
+
boolean()
|
|
16632
|
+
]).optional(),
|
|
16633
|
+
/** Only on a CHANGE of value, not on every re-report. */
|
|
16634
|
+
edge: boolean().optional(),
|
|
16635
|
+
/** The new value must hold this long before it counts. */
|
|
16636
|
+
forMs: number().int().min(0).max(864e5).optional(),
|
|
16637
|
+
/** Collapse a burst into one occurrence. */
|
|
16638
|
+
debounceMs: number().int().min(0).max(6e5).optional()
|
|
16639
|
+
});
|
|
16640
|
+
var DeviceEventLeafSchema = object({
|
|
16641
|
+
kind: literal("device-event"),
|
|
16642
|
+
/** An `EventCategory` value. */
|
|
16643
|
+
category: string().min(1),
|
|
16644
|
+
deviceId: number().int().nonnegative().optional()
|
|
16645
|
+
});
|
|
16646
|
+
var ScheduleLeafSchema = object({
|
|
16647
|
+
kind: literal("schedule"),
|
|
16648
|
+
cron: string().min(1).max(120)
|
|
16649
|
+
});
|
|
16650
|
+
var ManualLeafSchema = object({ kind: literal("manual") });
|
|
16651
|
+
var IntervalLeafSchema = object({
|
|
16652
|
+
kind: literal("interval"),
|
|
16653
|
+
minutes: number().int().min(1).max(1440)
|
|
16654
|
+
});
|
|
16655
|
+
var DetectionLeafSchema = object({
|
|
16656
|
+
kind: literal("detection"),
|
|
16657
|
+
/** One macro class each, optionally narrowed by a label; a track of any listed subject fires. */
|
|
16658
|
+
subjects: array(DetectionSubjectSchema).min(1),
|
|
16659
|
+
deviceIds: array(number().int().nonnegative()).min(1),
|
|
16660
|
+
/** The track must have visited at least one of these zones. */
|
|
16661
|
+
zones: array(string().min(1)).min(1).optional(),
|
|
16662
|
+
/** Best-so-far confidence the track must reach. */
|
|
16663
|
+
minScore: number().min(0).max(1).optional()
|
|
16664
|
+
});
|
|
16665
|
+
var StationaryLeafSchema = object({
|
|
16666
|
+
kind: literal("stationary"),
|
|
16667
|
+
deviceIds: array(number().int().nonnegative()).min(1),
|
|
16668
|
+
subjects: array(StationarySubjectSchema).min(1).optional(),
|
|
16669
|
+
zones: array(string().min(1)).min(1).optional(),
|
|
16670
|
+
/** `appeared`: it became parked. `departed`: the registry retired it. */
|
|
16671
|
+
phase: _enum(["appeared", "departed"])
|
|
16672
|
+
});
|
|
16673
|
+
/** "is": one device field compared to one value, NOW. */
|
|
16674
|
+
var DeviceIsLeafSchema = object({
|
|
16675
|
+
kind: literal("device-is"),
|
|
16342
16676
|
deviceId: number().int().nonnegative(),
|
|
16343
16677
|
cap: string().min(1),
|
|
16344
16678
|
fieldPath: string().min(1),
|
|
16345
16679
|
operator: AutomationConditionOperatorSchema,
|
|
16680
|
+
/**
|
|
16681
|
+
* The value. A number on a quantity field is stored in the field's base unit
|
|
16682
|
+
* (D944); a quantity value is the WRITE form, which the save boundary
|
|
16683
|
+
* replaces with the converted number and `unit`. A leaf that still carries
|
|
16684
|
+
* one is never evaluated (it fails).
|
|
16685
|
+
*/
|
|
16346
16686
|
value: union([
|
|
16347
16687
|
string(),
|
|
16348
16688
|
number(),
|
|
@@ -16350,24 +16690,26 @@ var AutomationConditionLeafSchema = object({
|
|
|
16350
16690
|
array(union([string(), number()])),
|
|
16351
16691
|
QuantityValueSchema
|
|
16352
16692
|
]),
|
|
16693
|
+
/** The unit the person picked for `value` (display only; `value` is in the base unit). */
|
|
16353
16694
|
unit: string().min(1).optional()
|
|
16354
16695
|
});
|
|
16355
|
-
|
|
16696
|
+
/**
|
|
16697
|
+
* True when the camera currently has a PARKED object matching `subjects` in at
|
|
16698
|
+
* least one of `zones`. Reads the zone-analytics snapshot through the same
|
|
16699
|
+
* mirror as every other state leaf, so a dropped event never decides it (D49).
|
|
16700
|
+
*/
|
|
16701
|
+
var StationaryPresentLeafSchema = object({
|
|
16356
16702
|
kind: literal("stationary-present"),
|
|
16357
16703
|
deviceId: number().int().nonnegative(),
|
|
16358
|
-
|
|
16359
|
-
classes: array(string().min(1)).min(1).optional(),
|
|
16360
|
-
/** The parked object must be in at least one of these zones. */
|
|
16704
|
+
subjects: array(StationarySubjectSchema).min(1).optional(),
|
|
16361
16705
|
zones: array(string().min(1)).min(1).optional()
|
|
16362
16706
|
});
|
|
16363
16707
|
/**
|
|
16364
|
-
* The
|
|
16365
|
-
*
|
|
16366
|
-
*
|
|
16367
|
-
* validation is the SAME function `ExpressionSourceSchema` runs, so the two
|
|
16368
|
-
* cannot drift — an expression that one accepts, the other accepts.
|
|
16708
|
+
* The escape hatch: the bounded expression engine over named device bindings.
|
|
16709
|
+
* Declared as a plain object so a discriminated union can read `kind`; the
|
|
16710
|
+
* author-time validation is the SAME function `ExpressionSourceSchema` runs.
|
|
16369
16711
|
*/
|
|
16370
|
-
var
|
|
16712
|
+
var ExpressionLeafSchema = object({
|
|
16371
16713
|
kind: literal("expression"),
|
|
16372
16714
|
expr: string().min(1).max(MAX_EXPRESSION_SOURCE_LENGTH),
|
|
16373
16715
|
bindings: record(string().regex(EXPRESSION_IDENTIFIER_RE), ExpressionBindingSourceSchema)
|
|
@@ -16379,55 +16721,62 @@ var AutomationConditionExpressionSchema = object({
|
|
|
16379
16721
|
path: ["expr"]
|
|
16380
16722
|
});
|
|
16381
16723
|
});
|
|
16382
|
-
var
|
|
16724
|
+
var AutomationWhenSchema = lazy(() => discriminatedUnion("kind", [
|
|
16383
16725
|
object({
|
|
16384
16726
|
kind: literal("all"),
|
|
16385
|
-
children: array(
|
|
16727
|
+
children: array(AutomationWhenSchema)
|
|
16386
16728
|
}),
|
|
16387
16729
|
object({
|
|
16388
16730
|
kind: literal("any"),
|
|
16389
|
-
children: array(
|
|
16731
|
+
children: array(AutomationWhenSchema)
|
|
16390
16732
|
}),
|
|
16391
16733
|
object({
|
|
16392
16734
|
kind: literal("not"),
|
|
16393
|
-
child:
|
|
16735
|
+
child: AutomationWhenSchema
|
|
16394
16736
|
}),
|
|
16395
|
-
|
|
16396
|
-
|
|
16397
|
-
|
|
16737
|
+
DeviceBecomesLeafSchema,
|
|
16738
|
+
DeviceEventLeafSchema,
|
|
16739
|
+
ScheduleLeafSchema,
|
|
16740
|
+
ManualLeafSchema,
|
|
16741
|
+
IntervalLeafSchema,
|
|
16742
|
+
DetectionLeafSchema,
|
|
16743
|
+
StationaryLeafSchema,
|
|
16744
|
+
DeviceIsLeafSchema,
|
|
16745
|
+
StationaryPresentLeafSchema,
|
|
16746
|
+
ExpressionLeafSchema
|
|
16398
16747
|
]));
|
|
16399
16748
|
/**
|
|
16400
|
-
*
|
|
16401
|
-
*
|
|
16402
|
-
*
|
|
16403
|
-
*
|
|
16404
|
-
*
|
|
16405
|
-
*
|
|
16406
|
-
*
|
|
16407
|
-
*
|
|
16408
|
-
|
|
16409
|
-
|
|
16749
|
+
* The recipe shape before the single `when` tree: a flat list of triggers
|
|
16750
|
+
* (OR'd, each an instant) and an optional condition tree. Stored recipes —
|
|
16751
|
+
* the `AUTOMATION_RECIPE` literal of every block saved so far, and whatever an
|
|
16752
|
+
* older client still posts — are in this shape, so it stays READABLE here, and
|
|
16753
|
+
* {@link convertLegacyRecipe} turns it into the new one, losslessly and one
|
|
16754
|
+
* way. Nothing writes this shape any more.
|
|
16755
|
+
*
|
|
16756
|
+
* `{ triggers: [t1…tn], conditions?: C }`
|
|
16757
|
+
* → `when = all(any(t1…tn), C)`, or `any(t1…tn)` without `C`;
|
|
16758
|
+
* a single trigger is not wrapped in `any`.
|
|
16759
|
+
* a `condition` leaf → a `device-is` leaf (the "is" verb);
|
|
16760
|
+
* a `device-state` trigger → a `device-becomes` leaf (the "becomes" verb);
|
|
16761
|
+
* `classes: ['dog']` → the macro `animal` with the label `dog`.
|
|
16762
|
+
*/
|
|
16763
|
+
var LegacyTriggerSchema = discriminatedUnion("kind", [
|
|
16410
16764
|
object({
|
|
16411
16765
|
kind: literal("device-state"),
|
|
16412
16766
|
deviceId: number().int().nonnegative(),
|
|
16413
16767
|
cap: string().min(1),
|
|
16414
16768
|
fieldPath: string().min(1),
|
|
16415
|
-
/** Fire when the field takes this value. Omit to fire on any change. */
|
|
16416
16769
|
becomes: union([
|
|
16417
16770
|
string(),
|
|
16418
16771
|
number(),
|
|
16419
16772
|
boolean()
|
|
16420
16773
|
]).optional(),
|
|
16421
|
-
/** Only on a CHANGE of value, not on every re-report. */
|
|
16422
16774
|
edge: boolean().optional(),
|
|
16423
|
-
/** The condition must hold this long before the run starts. */
|
|
16424
16775
|
forMs: number().int().min(0).max(864e5).optional(),
|
|
16425
|
-
/** Collapse a burst into one run. */
|
|
16426
16776
|
debounceMs: number().int().min(0).max(6e5).optional()
|
|
16427
16777
|
}),
|
|
16428
16778
|
object({
|
|
16429
16779
|
kind: literal("device-event"),
|
|
16430
|
-
/** An `EventCategory` value. */
|
|
16431
16780
|
category: string().min(1),
|
|
16432
16781
|
deviceId: number().int().nonnegative().optional()
|
|
16433
16782
|
}),
|
|
@@ -16438,25 +16787,16 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
|
|
|
16438
16787
|
object({ kind: literal("manual") }),
|
|
16439
16788
|
object({
|
|
16440
16789
|
kind: literal("detection"),
|
|
16441
|
-
/** Class names. A macro class (`animal`, `vehicle`, `person`, `package`)
|
|
16442
|
-
* matches every raw class that maps to it; any other name matches
|
|
16443
|
-
* exactly. */
|
|
16444
16790
|
classes: array(string().min(1)).min(1),
|
|
16445
16791
|
deviceIds: array(number().int().nonnegative()).min(1),
|
|
16446
|
-
/** The track must have visited at least one of these zones. */
|
|
16447
16792
|
zones: array(string().min(1)).min(1).optional(),
|
|
16448
|
-
/** Best-so-far confidence the track must reach. */
|
|
16449
16793
|
minScore: number().min(0).max(1).optional()
|
|
16450
16794
|
}),
|
|
16451
16795
|
object({
|
|
16452
16796
|
kind: literal("stationary"),
|
|
16453
16797
|
deviceIds: array(number().int().nonnegative()).min(1),
|
|
16454
|
-
/** Class names; a macro class matches like the detection trigger. */
|
|
16455
16798
|
classes: array(string().min(1)).min(1).optional(),
|
|
16456
|
-
/** The parked object must be in at least one of these zones. */
|
|
16457
16799
|
zones: array(string().min(1)).min(1).optional(),
|
|
16458
|
-
/** `appeared`: it became parked. `departed`: the registry retired it.
|
|
16459
|
-
* The vocabulary of `PipelineAnalyticsStationaryChanged`. */
|
|
16460
16800
|
phase: _enum(["appeared", "departed"])
|
|
16461
16801
|
}),
|
|
16462
16802
|
object({
|
|
@@ -16464,6 +16804,143 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
|
|
|
16464
16804
|
minutes: number().int().min(1).max(1440)
|
|
16465
16805
|
})
|
|
16466
16806
|
]);
|
|
16807
|
+
var LegacyConditionLeafSchema = object({
|
|
16808
|
+
kind: literal("condition"),
|
|
16809
|
+
deviceId: number().int().nonnegative(),
|
|
16810
|
+
cap: string().min(1),
|
|
16811
|
+
fieldPath: string().min(1),
|
|
16812
|
+
operator: AutomationConditionOperatorSchema,
|
|
16813
|
+
value: union([
|
|
16814
|
+
string(),
|
|
16815
|
+
number(),
|
|
16816
|
+
boolean(),
|
|
16817
|
+
array(union([string(), number()])),
|
|
16818
|
+
QuantityValueSchema
|
|
16819
|
+
]),
|
|
16820
|
+
unit: string().min(1).optional()
|
|
16821
|
+
});
|
|
16822
|
+
var LegacyStationaryPresentSchema = object({
|
|
16823
|
+
kind: literal("stationary-present"),
|
|
16824
|
+
deviceId: number().int().nonnegative(),
|
|
16825
|
+
classes: array(string().min(1)).min(1).optional(),
|
|
16826
|
+
zones: array(string().min(1)).min(1).optional()
|
|
16827
|
+
});
|
|
16828
|
+
var LegacyExpressionSchema = object({
|
|
16829
|
+
kind: literal("expression"),
|
|
16830
|
+
expr: string().min(1).max(MAX_EXPRESSION_SOURCE_LENGTH),
|
|
16831
|
+
bindings: record(string().regex(EXPRESSION_IDENTIFIER_RE), ExpressionBindingSourceSchema)
|
|
16832
|
+
}).superRefine((src, ctx) => {
|
|
16833
|
+
const err = validateExpressionSource(src);
|
|
16834
|
+
if (err !== null) ctx.addIssue({
|
|
16835
|
+
code: "custom",
|
|
16836
|
+
message: err,
|
|
16837
|
+
path: ["expr"]
|
|
16838
|
+
});
|
|
16839
|
+
});
|
|
16840
|
+
var LegacyConditionSchema = lazy(() => discriminatedUnion("kind", [
|
|
16841
|
+
object({
|
|
16842
|
+
kind: literal("all"),
|
|
16843
|
+
children: array(LegacyConditionSchema)
|
|
16844
|
+
}),
|
|
16845
|
+
object({
|
|
16846
|
+
kind: literal("any"),
|
|
16847
|
+
children: array(LegacyConditionSchema)
|
|
16848
|
+
}),
|
|
16849
|
+
object({
|
|
16850
|
+
kind: literal("not"),
|
|
16851
|
+
child: LegacyConditionSchema
|
|
16852
|
+
}),
|
|
16853
|
+
LegacyConditionLeafSchema,
|
|
16854
|
+
LegacyExpressionSchema,
|
|
16855
|
+
LegacyStationaryPresentSchema
|
|
16856
|
+
]));
|
|
16857
|
+
function convertTrigger(trigger) {
|
|
16858
|
+
switch (trigger.kind) {
|
|
16859
|
+
case "device-state": return {
|
|
16860
|
+
...trigger,
|
|
16861
|
+
kind: "device-becomes"
|
|
16862
|
+
};
|
|
16863
|
+
case "detection": {
|
|
16864
|
+
const { classes, ...rest } = trigger;
|
|
16865
|
+
return {
|
|
16866
|
+
...rest,
|
|
16867
|
+
subjects: classes.map(subjectFromClassName)
|
|
16868
|
+
};
|
|
16869
|
+
}
|
|
16870
|
+
case "stationary": {
|
|
16871
|
+
const { classes, ...rest } = trigger;
|
|
16872
|
+
return {
|
|
16873
|
+
...rest,
|
|
16874
|
+
...classes === void 0 ? {} : { subjects: classes.map(subjectFromClassName) }
|
|
16875
|
+
};
|
|
16876
|
+
}
|
|
16877
|
+
default: return trigger;
|
|
16878
|
+
}
|
|
16879
|
+
}
|
|
16880
|
+
/** An old condition tree as a `when` subtree: `condition` → "is", classes → subjects. */
|
|
16881
|
+
function convertLegacyCondition(condition) {
|
|
16882
|
+
switch (condition.kind) {
|
|
16883
|
+
case "condition": return {
|
|
16884
|
+
...condition,
|
|
16885
|
+
kind: "device-is"
|
|
16886
|
+
};
|
|
16887
|
+
case "stationary-present": {
|
|
16888
|
+
const { classes, ...rest } = condition;
|
|
16889
|
+
return {
|
|
16890
|
+
...rest,
|
|
16891
|
+
...classes === void 0 ? {} : { subjects: classes.map(subjectFromClassName) }
|
|
16892
|
+
};
|
|
16893
|
+
}
|
|
16894
|
+
case "expression": return condition;
|
|
16895
|
+
case "not": return {
|
|
16896
|
+
kind: "not",
|
|
16897
|
+
child: convertLegacyCondition(condition.child)
|
|
16898
|
+
};
|
|
16899
|
+
case "all":
|
|
16900
|
+
case "any": return {
|
|
16901
|
+
kind: condition.kind,
|
|
16902
|
+
children: condition.children.map(convertLegacyCondition)
|
|
16903
|
+
};
|
|
16904
|
+
}
|
|
16905
|
+
}
|
|
16906
|
+
/** `all(any(t1…tn), C)`, or `any(t1…tn)`; a single trigger is not wrapped in `any`. */
|
|
16907
|
+
function convertLegacyWhen(half) {
|
|
16908
|
+
const leaves = half.triggers.map(convertTrigger);
|
|
16909
|
+
const first = leaves[0];
|
|
16910
|
+
const triggers = leaves.length === 1 && first !== void 0 ? first : {
|
|
16911
|
+
kind: "any",
|
|
16912
|
+
children: leaves
|
|
16913
|
+
};
|
|
16914
|
+
if (half.conditions === void 0) return triggers;
|
|
16915
|
+
return {
|
|
16916
|
+
kind: "all",
|
|
16917
|
+
children: [triggers, convertLegacyCondition(half.conditions)]
|
|
16918
|
+
};
|
|
16919
|
+
}
|
|
16920
|
+
/**
|
|
16921
|
+
* The automation RECIPE — what a visually-authored automation is, before it
|
|
16922
|
+
* becomes code.
|
|
16923
|
+
*
|
|
16924
|
+
* This is the data half of `docs/design/2026-08-08-automation-scripts-custom-devices.md`
|
|
16925
|
+
* Phase 6. A recipe is not an engine and never evaluates anything: it is a
|
|
16926
|
+
* declarative description that {@link generateAutomationBlock} compiles to the
|
|
16927
|
+
* TypeScript of an ordinary core block. There is therefore no second rule
|
|
16928
|
+
* engine, no second store and no second runtime — the block runner is the
|
|
16929
|
+
* runtime, `core_blocks` is the store (§2.1).
|
|
16930
|
+
*
|
|
16931
|
+
* Two parts:
|
|
16932
|
+
*
|
|
16933
|
+
* - **`when`** — ONE tree (`./when.ts`) of all/any/not over event leaves
|
|
16934
|
+
* (instants) and state leaves (durations). It replaces the flat trigger list
|
|
16935
|
+
* plus the separate condition tree; the firing rule is in that file and in
|
|
16936
|
+
* ADR "one tree".
|
|
16937
|
+
* - **Actions** — `NcRuleActionSchema`'s two steps, plus the `{kind:'code'}`
|
|
16938
|
+
* fragment of §3.2.2. See {@link AutomationActionSchema} for the ONE place
|
|
16939
|
+
* this had to diverge from "verbatim", and why.
|
|
16940
|
+
*
|
|
16941
|
+
* A recipe stored in the old shape (`triggers` + `conditions`) still LOADS:
|
|
16942
|
+
* {@link AutomationRecipeSchema} (the parse-anywhere schema) converts it on parse. Nothing writes it.
|
|
16943
|
+
*/
|
|
16467
16944
|
/**
|
|
16468
16945
|
* One action step.
|
|
16469
16946
|
*
|
|
@@ -16566,69 +17043,49 @@ var AutomationRunModeSchema = discriminatedUnion("mode", [
|
|
|
16566
17043
|
minutes: number().int().min(1).max(1440)
|
|
16567
17044
|
})
|
|
16568
17045
|
]);
|
|
16569
|
-
var
|
|
16570
|
-
|
|
16571
|
-
conditions: AutomationConditionSchema.optional(),
|
|
17046
|
+
var CurrentAutomationRecipeSchema = object({
|
|
17047
|
+
when: AutomationWhenSchema,
|
|
16572
17048
|
actions: array(AutomationActionSchema),
|
|
16573
17049
|
runMode: AutomationRunModeSchema.optional(),
|
|
16574
17050
|
/** How many run traces the automation keeps (default {@link DEFAULT_TRACE_KEEP}). */
|
|
16575
17051
|
keepTraces: number().int().min(1).max(100).optional()
|
|
16576
17052
|
});
|
|
16577
|
-
|
|
16578
|
-
|
|
16579
|
-
|
|
16580
|
-
|
|
16581
|
-
|
|
16582
|
-
|
|
16583
|
-
|
|
16584
|
-
|
|
16585
|
-
|
|
16586
|
-
|
|
16587
|
-
|
|
16588
|
-
|
|
16589
|
-
|
|
16590
|
-
|
|
16591
|
-
|
|
16592
|
-
|
|
16593
|
-
|
|
16594
|
-
|
|
16595
|
-
|
|
16596
|
-
|
|
16597
|
-
|
|
16598
|
-
|
|
16599
|
-
|
|
16600
|
-
|
|
16601
|
-
|
|
16602
|
-
bus: "vehicle",
|
|
16603
|
-
train: "vehicle",
|
|
16604
|
-
truck: "vehicle",
|
|
16605
|
-
boat: "vehicle",
|
|
16606
|
-
bird: "animal",
|
|
16607
|
-
cat: "animal",
|
|
16608
|
-
dog: "animal",
|
|
16609
|
-
horse: "animal",
|
|
16610
|
-
sheep: "animal",
|
|
16611
|
-
cow: "animal",
|
|
16612
|
-
elephant: "animal",
|
|
16613
|
-
bear: "animal",
|
|
16614
|
-
zebra: "animal",
|
|
16615
|
-
giraffe: "animal",
|
|
16616
|
-
suitcase: "package",
|
|
16617
|
-
backpack: "package",
|
|
16618
|
-
handbag: "package"
|
|
16619
|
-
},
|
|
16620
|
-
preserveOriginal: false
|
|
16621
|
-
};
|
|
17053
|
+
/** A recipe as it was stored before the single tree. Read-only: nothing writes this. */
|
|
17054
|
+
var LegacyAutomationRecipeSchema = object({
|
|
17055
|
+
triggers: array(LegacyTriggerSchema),
|
|
17056
|
+
conditions: LegacyConditionSchema.optional(),
|
|
17057
|
+
actions: array(AutomationActionSchema),
|
|
17058
|
+
runMode: AutomationRunModeSchema.optional(),
|
|
17059
|
+
keepTraces: number().int().min(1).max(100).optional()
|
|
17060
|
+
});
|
|
17061
|
+
/** The old shape as the new one, lossless. */
|
|
17062
|
+
function convertLegacyRecipe(legacy) {
|
|
17063
|
+
const { triggers, conditions, ...rest } = legacy;
|
|
17064
|
+
return {
|
|
17065
|
+
when: convertLegacyWhen({
|
|
17066
|
+
triggers,
|
|
17067
|
+
conditions
|
|
17068
|
+
}),
|
|
17069
|
+
...rest
|
|
17070
|
+
};
|
|
17071
|
+
}
|
|
17072
|
+
/**
|
|
17073
|
+
* Where recipes are PARSED (the core-blocks cap, the MCP tool, the literal a
|
|
17074
|
+
* saved block carries): the new shape as it is, or the old shape converted to
|
|
17075
|
+
* it. The output type is always the new one.
|
|
17076
|
+
*/
|
|
17077
|
+
var AutomationRecipeSchema = union([CurrentAutomationRecipeSchema, LegacyAutomationRecipeSchema.transform(convertLegacyRecipe)]);
|
|
16622
17078
|
new Set(Object.values(COCO_TO_MACRO.mapping));
|
|
16623
17079
|
/**
|
|
16624
17080
|
* Run traces — what an automation (or, later, an Action run) DID, node by node,
|
|
16625
17081
|
* so the editor can paint every block green or red with its reason.
|
|
16626
17082
|
*
|
|
16627
|
-
* A trace is data, not a log: it covers the WHOLE tree — the
|
|
16628
|
-
* every
|
|
16629
|
-
* short-circuit never reached), the run-mode verdict and every action
|
|
16630
|
-
* each addressed by a stable path (`
|
|
16631
|
-
* for an Action run).
|
|
17083
|
+
* A trace is data, not a log: it covers the WHOLE tree — the event leaf that
|
|
17084
|
+
* fired, every node of the `when` tree (containers and leaves, including the
|
|
17085
|
+
* ones a short-circuit never reached), the run-mode verdict and every action
|
|
17086
|
+
* step — each addressed by a stable path (`when.0.1`, `actions.2`, or `steps.N`
|
|
17087
|
+
* for an Action run). Traces written before the single tree carry
|
|
17088
|
+
* `conditions.*` paths and still parse. Skipped
|
|
16632
17089
|
* evaluations are traced too, and identical consecutive skips collapse into a
|
|
16633
17090
|
* repeat count so a trigger that fails every 5 minutes cannot flush real runs.
|
|
16634
17091
|
*
|
|
@@ -16660,6 +17117,8 @@ var TraceNodeOutcomeSchema = _enum([
|
|
|
16660
17117
|
]);
|
|
16661
17118
|
var AutomationTriggerTraceSchema = object({
|
|
16662
17119
|
kind: string().min(1),
|
|
17120
|
+
/** The event leaf that fired (`when`, `when.0.1`); absent on traces written before the single tree. */
|
|
17121
|
+
path: string().min(1).optional(),
|
|
16663
17122
|
outcome: _enum(["pass", "fail"]),
|
|
16664
17123
|
/** Why an event was rejected ("class person not in [dog]") or what fired. */
|
|
16665
17124
|
reason: string(),
|
|
@@ -16667,15 +17126,23 @@ var AutomationTriggerTraceSchema = object({
|
|
|
16667
17126
|
vars: record(string(), TraceValueSchema)
|
|
16668
17127
|
});
|
|
16669
17128
|
var AutomationConditionTraceNodeSchema = object({
|
|
16670
|
-
/** `
|
|
17129
|
+
/** `when` for the root, `when.<i>` for its i-th child, … (`conditions…` on an old trace). */
|
|
16671
17130
|
path: string().min(1),
|
|
16672
17131
|
kind: _enum([
|
|
16673
17132
|
"all",
|
|
16674
17133
|
"any",
|
|
16675
17134
|
"not",
|
|
16676
17135
|
"condition",
|
|
17136
|
+
"device-is",
|
|
16677
17137
|
"expression",
|
|
16678
|
-
"stationary-present"
|
|
17138
|
+
"stationary-present",
|
|
17139
|
+
"device-becomes",
|
|
17140
|
+
"device-event",
|
|
17141
|
+
"schedule",
|
|
17142
|
+
"interval",
|
|
17143
|
+
"manual",
|
|
17144
|
+
"detection",
|
|
17145
|
+
"stationary"
|
|
16679
17146
|
]),
|
|
16680
17147
|
outcome: TraceNodeOutcomeSchema,
|
|
16681
17148
|
reason: string(),
|
|
@@ -16709,6 +17176,12 @@ var AutomationStepTraceSchema = object({
|
|
|
16709
17176
|
deviceId: number().int().optional(),
|
|
16710
17177
|
startedAt: number().optional(),
|
|
16711
17178
|
endedAt: number().optional(),
|
|
17179
|
+
/**
|
|
17180
|
+
* The parameters the step SENT, after `{{vars.x}}` substitution — what the
|
|
17181
|
+
* device was actually asked. Bounded like trigger `vars`; a secret parameter
|
|
17182
|
+
* is shown as {@link REDACTED_ARG}. Absent on traces written before it existed.
|
|
17183
|
+
*/
|
|
17184
|
+
args: record(string(), TraceValueSchema).optional(),
|
|
16712
17185
|
outcome: _enum([
|
|
16713
17186
|
"ok",
|
|
16714
17187
|
"error",
|
|
@@ -17458,6 +17931,23 @@ method(object({
|
|
|
17458
17931
|
addonId: string(),
|
|
17459
17932
|
nodeId: string().optional()
|
|
17460
17933
|
}), SettingsSchemaWithValuesSchema.nullable()), method(object({
|
|
17934
|
+
addonId: string(),
|
|
17935
|
+
nodeId: string().optional()
|
|
17936
|
+
}), object({ keys: array(string()) }).nullable(), { auth: "admin" }), method(object({
|
|
17937
|
+
addonId: string(),
|
|
17938
|
+
nodeId: string().optional(),
|
|
17939
|
+
keys: array(string())
|
|
17940
|
+
}), object({
|
|
17941
|
+
removed: array(string()),
|
|
17942
|
+
refused: array(object({
|
|
17943
|
+
key: string(),
|
|
17944
|
+
reason: string()
|
|
17945
|
+
})),
|
|
17946
|
+
secretsCleared: number()
|
|
17947
|
+
}), {
|
|
17948
|
+
kind: "mutation",
|
|
17949
|
+
auth: "admin"
|
|
17950
|
+
}), method(object({
|
|
17461
17951
|
addonId: string(),
|
|
17462
17952
|
deviceId: number(),
|
|
17463
17953
|
nodeId: string().optional()
|
|
@@ -18893,8 +19383,21 @@ var BrokerProviderInfoSchema = object({
|
|
|
18893
19383
|
* (`{ sessionId }`) drops the session. The dialog that edits a broker
|
|
18894
19384
|
* offers it as the default path; the fields the session fills are
|
|
18895
19385
|
* marked `filledBySignIn` in the kind's settings schema.
|
|
19386
|
+
*
|
|
19387
|
+
* `methods` lists the ways the session can sign in, recommended first:
|
|
19388
|
+
* `'device-code'` adds `POST {basePath}/code/start` (body: the form's
|
|
19389
|
+
* settings) answering `{ sessionId, userCode, verificationUrl,
|
|
19390
|
+
* expiresAt }` — the operator types `userCode` at `verificationUrl` on
|
|
19391
|
+
* any device, and the same `status` / `commit` / `cancel` routes follow
|
|
19392
|
+
* (status then carries `method`, `userCode`, `verificationUrl`,
|
|
19393
|
+
* `expiresAt` and the phases `'awaiting-code'` / `'expired'`);
|
|
19394
|
+
* `'proxy'` is the `{basePath}/start` flow above. Absent means
|
|
19395
|
+
* `['proxy']`.
|
|
18896
19396
|
*/
|
|
18897
|
-
signIn: object({
|
|
19397
|
+
signIn: object({
|
|
19398
|
+
basePath: string().min(1),
|
|
19399
|
+
methods: array(_enum(["device-code", "proxy"])).optional()
|
|
19400
|
+
}).optional()
|
|
18898
19401
|
}))
|
|
18899
19402
|
});
|
|
18900
19403
|
var ListInputSchema = object({
|
|
@@ -21368,6 +21871,8 @@ var StorageKindSchema = object({
|
|
|
21368
21871
|
"orphaned",
|
|
21369
21872
|
"dropped"
|
|
21370
21873
|
]),
|
|
21874
|
+
/** An owner declared this kind since the engine started. */
|
|
21875
|
+
declaredThisBoot: boolean(),
|
|
21371
21876
|
/** `null` when the caller asked for no counts (`counts: false`). */
|
|
21372
21877
|
rows: number().int().nonnegative().nullable(),
|
|
21373
21878
|
/** Rows x the average size of a sample; null for an empty kind. */
|
|
@@ -21379,6 +21884,51 @@ var StorageKindSchema = object({
|
|
|
21379
21884
|
/** The whole declaration, secret paths and entry descriptor included (the entries service builds its handle from it). */
|
|
21380
21885
|
declaration: RecordKindDeclarationSchema
|
|
21381
21886
|
});
|
|
21887
|
+
_enum([
|
|
21888
|
+
"orphaned",
|
|
21889
|
+
"stale",
|
|
21890
|
+
"unknown"
|
|
21891
|
+
]);
|
|
21892
|
+
/** A record kind nobody declared since its owner started, or whose owner is gone. */
|
|
21893
|
+
var StaleKindFindingSchema = object({
|
|
21894
|
+
owner: string(),
|
|
21895
|
+
kind: string(),
|
|
21896
|
+
/** `unknown`: the owner could not be judged (not running, or its state unreadable) - never dropped. */
|
|
21897
|
+
verdict: _enum(["orphaned", "unknown"]),
|
|
21898
|
+
reason: string(),
|
|
21899
|
+
rows: number().int().nonnegative().nullable(),
|
|
21900
|
+
/** local-auth's tables are never dropped by this audit. */
|
|
21901
|
+
protected: boolean()
|
|
21902
|
+
});
|
|
21903
|
+
/** A stored global-settings key the running addon no longer declares. */
|
|
21904
|
+
var StaleSettingsKeyFindingSchema = object({
|
|
21905
|
+
addonId: string(),
|
|
21906
|
+
/** The stored key as written, a per-node `<key>@<nodeId>` suffix included. */
|
|
21907
|
+
key: string(),
|
|
21908
|
+
/**
|
|
21909
|
+
* Keychain refs whose ONLY marker sits under this key: in use by stale data,
|
|
21910
|
+
* not orphans yet. Removing the key releases them.
|
|
21911
|
+
*/
|
|
21912
|
+
secretRefs: array(string())
|
|
21913
|
+
});
|
|
21914
|
+
/** An addon the audit could not judge, with the reason. Never reported as stale. */
|
|
21915
|
+
var StaleUnknownSchema = object({
|
|
21916
|
+
subject: string(),
|
|
21917
|
+
reason: string()
|
|
21918
|
+
});
|
|
21919
|
+
var StaleDataReportSchema = object({
|
|
21920
|
+
/** Epoch ms of the last finished scan; `null` before the first. */
|
|
21921
|
+
scannedAt: number().nullable(),
|
|
21922
|
+
scanning: boolean(),
|
|
21923
|
+
trigger: _enum([
|
|
21924
|
+
"boot",
|
|
21925
|
+
"interval",
|
|
21926
|
+
"manual"
|
|
21927
|
+
]).nullable(),
|
|
21928
|
+
kinds: array(StaleKindFindingSchema),
|
|
21929
|
+
settingsKeys: array(StaleSettingsKeyFindingSchema),
|
|
21930
|
+
unknown: array(StaleUnknownSchema)
|
|
21931
|
+
});
|
|
21382
21932
|
method(object({ ownerPrefix: string().min(1).optional() }), object({ tables: array(StorageTableSchema) }), { auth: "admin" }), method(object({
|
|
21383
21933
|
owner: string().min(1).optional(),
|
|
21384
21934
|
/** `false` skips the per-kind row count (a catalog read only). Default true. */
|
|
@@ -21386,7 +21936,30 @@ method(object({ ownerPrefix: string().min(1).optional() }), object({ tables: arr
|
|
|
21386
21936
|
}), object({ kinds: array(StorageKindSchema) }), { auth: "admin" }), method(RecordQueryInputSchema.extend({
|
|
21387
21937
|
owner: string().min(1),
|
|
21388
21938
|
kind: RecordKindNameSchema
|
|
21389
|
-
}), RecordQueryResultSchema, { auth: "admin" })
|
|
21939
|
+
}), RecordQueryResultSchema, { auth: "admin" }), method(object({}), StaleDataReportSchema, { auth: "admin" }), method(object({}), StaleDataReportSchema, {
|
|
21940
|
+
kind: "mutation",
|
|
21941
|
+
auth: "admin"
|
|
21942
|
+
}), method(object({
|
|
21943
|
+
owner: string().min(1),
|
|
21944
|
+
kind: RecordKindNameSchema
|
|
21945
|
+
}), object({
|
|
21946
|
+
deleted: number().int().nonnegative(),
|
|
21947
|
+
secretsReleased: number().int().nonnegative()
|
|
21948
|
+
}), {
|
|
21949
|
+
kind: "mutation",
|
|
21950
|
+
auth: "admin",
|
|
21951
|
+
caller: "required"
|
|
21952
|
+
}), method(object({
|
|
21953
|
+
addonId: string().min(1),
|
|
21954
|
+
key: string().min(1)
|
|
21955
|
+
}), object({
|
|
21956
|
+
removed: boolean(),
|
|
21957
|
+
secretsCleared: number().int().nonnegative()
|
|
21958
|
+
}), {
|
|
21959
|
+
kind: "mutation",
|
|
21960
|
+
auth: "admin",
|
|
21961
|
+
caller: "required"
|
|
21962
|
+
});
|
|
21390
21963
|
/**
|
|
21391
21964
|
* record-entries - the ONE admin surface for every list of user-created entries
|
|
21392
21965
|
* (D881): notification targets, brokers, LLM profiles, anything an addon
|
|
@@ -21525,6 +22098,12 @@ method(object({
|
|
|
21525
22098
|
}), method(object({ deviceId: number().int() }), object({ refs: array(string()).readonly() }), {
|
|
21526
22099
|
kind: "mutation",
|
|
21527
22100
|
auth: "admin"
|
|
22101
|
+
}), method(object({
|
|
22102
|
+
refs: RefListSchema,
|
|
22103
|
+
onBehalfOf: string().min(1)
|
|
22104
|
+
}), object({ released: number().int() }), {
|
|
22105
|
+
kind: "mutation",
|
|
22106
|
+
auth: "admin"
|
|
21528
22107
|
}), method(object({}), array(KeychainSecretMetaSchema).readonly(), { auth: "admin" }), method(object({}), KeychainStateSchema, { auth: "admin" });
|
|
21529
22108
|
/**
|
|
21530
22109
|
* device-history — the ONE mechanism for numeric history (D850).
|
|
@@ -22314,7 +22893,9 @@ var DiscoveredChildDeviceSchema = lazy(() => object({
|
|
|
22314
22893
|
/** Upstream-system identity + rendering hints for the candidate. */
|
|
22315
22894
|
sourceInfo: SourceInfoSchema.optional(),
|
|
22316
22895
|
/** Nested entity-children for accordion preview — integrations populate, Hubs omit. */
|
|
22317
|
-
children: array(DiscoveredChildDeviceSchema).readonly().optional()
|
|
22896
|
+
children: array(DiscoveredChildDeviceSchema).readonly().optional(),
|
|
22897
|
+
/** `childNativeId` of the gateway candidate in the same list this device sits behind. */
|
|
22898
|
+
parentStableId: string().optional()
|
|
22318
22899
|
}));
|
|
22319
22900
|
/**
|
|
22320
22901
|
* Status block — populated by the integration after each
|
|
@@ -42629,6 +43210,79 @@ object({
|
|
|
42629
43210
|
metadata: record(string(), unknown()).optional()
|
|
42630
43211
|
});
|
|
42631
43212
|
/**
|
|
43213
|
+
* HOW a provider can be found on the network. The provider only DECLARES the
|
|
43214
|
+
* mode; the `local-network` scan engine runs it once for every provider that
|
|
43215
|
+
* asked (one multicast browse, one host sweep) and hands the raw hits back to
|
|
43216
|
+
* `classifyDiscoveryHits`. A provider never opens a discovery socket itself,
|
|
43217
|
+
* except through a `custom` mode.
|
|
43218
|
+
*/
|
|
43219
|
+
var DiscoveryModeSchema = discriminatedUnion("kind", [
|
|
43220
|
+
object({
|
|
43221
|
+
kind: literal("mdns"),
|
|
43222
|
+
/** DNS-SD service type, e.g. `_esphomelib._tcp`. */
|
|
43223
|
+
serviceType: string().min(1),
|
|
43224
|
+
/** Regular expression an instance name must match (case-insensitive). */
|
|
43225
|
+
instancePattern: string().optional()
|
|
43226
|
+
}),
|
|
43227
|
+
object({
|
|
43228
|
+
kind: literal("ws-discovery"),
|
|
43229
|
+
/** Probe types (e.g. `dn:NetworkVideoTransmitter`); omitted = any device. */
|
|
43230
|
+
types: array(string()).optional()
|
|
43231
|
+
}),
|
|
43232
|
+
object({
|
|
43233
|
+
kind: literal("ssdp"),
|
|
43234
|
+
searchTarget: string().min(1)
|
|
43235
|
+
}),
|
|
43236
|
+
object({
|
|
43237
|
+
kind: literal("udp-broadcast"),
|
|
43238
|
+
port: number().int().min(1).max(65535),
|
|
43239
|
+
payloadHex: string().regex(/^([0-9a-fA-F]{2})*$/),
|
|
43240
|
+
/** Port replies arrive on, when the device answers somewhere other than the source port. */
|
|
43241
|
+
listenPort: number().int().min(1).max(65535).optional()
|
|
43242
|
+
}),
|
|
43243
|
+
object({
|
|
43244
|
+
kind: literal("http-probe"),
|
|
43245
|
+
port: number().int().min(1).max(65535),
|
|
43246
|
+
path: string().startsWith("/"),
|
|
43247
|
+
method: _enum(["GET", "POST"]).default("GET"),
|
|
43248
|
+
bodyText: string().optional(),
|
|
43249
|
+
contentType: string().optional(),
|
|
43250
|
+
tls: boolean().default(false),
|
|
43251
|
+
/** Status codes that count as a hit. Omitted = any HTTP answer. */
|
|
43252
|
+
acceptStatus: array(number().int()).optional()
|
|
43253
|
+
}),
|
|
43254
|
+
object({
|
|
43255
|
+
kind: literal("custom"),
|
|
43256
|
+
/** Why the engine cannot do it (an allowlisted exception, e.g. a vendor UDP protocol). */
|
|
43257
|
+
reason: string().min(1)
|
|
43258
|
+
})
|
|
43259
|
+
]);
|
|
43260
|
+
/** What the engine saw on the wire for ONE declared mode; the provider classifies it. */
|
|
43261
|
+
var DiscoveryHitSchema = object({
|
|
43262
|
+
/** Index into the provider's own `getDiscoveryModes()` array. */
|
|
43263
|
+
modeIndex: number().int().min(0),
|
|
43264
|
+
host: string(),
|
|
43265
|
+
port: number().int(),
|
|
43266
|
+
http: object({
|
|
43267
|
+
status: number().int(),
|
|
43268
|
+
headers: record(string(), string()),
|
|
43269
|
+
/** At most 16 KiB. */
|
|
43270
|
+
bodyText: string()
|
|
43271
|
+
}).optional(),
|
|
43272
|
+
mdns: object({
|
|
43273
|
+
instance: string(),
|
|
43274
|
+
host: string(),
|
|
43275
|
+
port: number().int(),
|
|
43276
|
+
txt: record(string(), string())
|
|
43277
|
+
}).optional(),
|
|
43278
|
+
udp: object({ payloadHex: string() }).optional(),
|
|
43279
|
+
wsd: object({
|
|
43280
|
+
xaddrs: array(string()),
|
|
43281
|
+
types: array(string()),
|
|
43282
|
+
scopes: array(string())
|
|
43283
|
+
}).optional()
|
|
43284
|
+
});
|
|
43285
|
+
/**
|
|
42632
43286
|
* Candidate handed back from discovery and accepted by
|
|
42633
43287
|
* `adoptDiscoveredDevice`. Shape mirrors the in-process
|
|
42634
43288
|
* `DiscoveredDevice` interface used by `DeviceDiscovery`.
|
|
@@ -42663,7 +43317,17 @@ var DiscoveryCandidateSchema = object({
|
|
|
42663
43317
|
/** Numeric id of the device this candidate was matched to. Set with `alreadyOnboarded`. */
|
|
42664
43318
|
onboardedDeviceId: number().optional(),
|
|
42665
43319
|
/** Operator-facing name of the matched device, so the UI can say WHICH one it is. */
|
|
42666
|
-
onboardedName: string().optional()
|
|
43320
|
+
onboardedName: string().optional(),
|
|
43321
|
+
/**
|
|
43322
|
+
* This candidate is an INTEGRATION to set up (a gateway, a hub, a controller),
|
|
43323
|
+
* not a device to adopt. Replaces the `prefilledConfig.setupIntegration` marker,
|
|
43324
|
+
* which is still read for one release.
|
|
43325
|
+
*/
|
|
43326
|
+
setupIntegration: boolean().optional(),
|
|
43327
|
+
/** `stableId` of the gateway candidate this device was found BEHIND. */
|
|
43328
|
+
parentStableId: string().optional(),
|
|
43329
|
+
/** `false` = shown for context but not pickable. Default true. */
|
|
43330
|
+
selectable: boolean().optional()
|
|
42667
43331
|
});
|
|
42668
43332
|
/**
|
|
42669
43333
|
* Flat device summary returned by `createDevice` / `adoptDiscoveredDevice`.
|
|
@@ -42716,6 +43380,12 @@ method(_void(), _void(), { kind: "mutation" }), method(_void(), _void(), { kind:
|
|
|
42716
43380
|
}), method(object({}), boolean()), method(object({ params: record(string(), unknown()).optional() }), array(DiscoveryCandidateSchema), {
|
|
42717
43381
|
kind: "mutation",
|
|
42718
43382
|
auth: "admin"
|
|
43383
|
+
}), method(object({}), array(DiscoveryModeSchema)), method(object({
|
|
43384
|
+
hits: array(DiscoveryHitSchema),
|
|
43385
|
+
params: record(string(), unknown()).optional()
|
|
43386
|
+
}), array(DiscoveryCandidateSchema), {
|
|
43387
|
+
kind: "mutation",
|
|
43388
|
+
auth: "admin"
|
|
42719
43389
|
}), method(object({}), CreationSchemaOutputSchema), method(object({}), object({ deviceType: _enum(DeviceType).nullable() })), method(object({ candidate: DiscoveryCandidateSchema }), DeviceSummarySchema, {
|
|
42720
43390
|
kind: "mutation",
|
|
42721
43391
|
auth: "admin"
|
|
@@ -43443,17 +44113,23 @@ method(object({
|
|
|
43443
44113
|
}), method(object({}), object({ providers: array(object({
|
|
43444
44114
|
addonId: string(),
|
|
43445
44115
|
label: string()
|
|
43446
|
-
})).readonly() }), { auth: "admin" }), method(
|
|
43447
|
-
|
|
43448
|
-
|
|
43449
|
-
|
|
43450
|
-
|
|
43451
|
-
|
|
43452
|
-
|
|
43453
|
-
|
|
43454
|
-
}),
|
|
44116
|
+
})).readonly() }), { auth: "admin" }), method(
|
|
44117
|
+
/** `subnets`: extra CIDRs for THIS scan, on top of the hub's own and the saved list. */
|
|
44118
|
+
object({ subnets: array(string()).optional() }),
|
|
44119
|
+
object({ groups: array(object({
|
|
44120
|
+
addonId: string(),
|
|
44121
|
+
label: string(),
|
|
44122
|
+
candidates: array(DiscoveryCandidateSchema).readonly(),
|
|
44123
|
+
error: string().nullable()
|
|
44124
|
+
})).readonly() }),
|
|
44125
|
+
{
|
|
44126
|
+
kind: "mutation",
|
|
44127
|
+
auth: "admin"
|
|
44128
|
+
}
|
|
44129
|
+
), method(object({
|
|
43455
44130
|
addonId: string(),
|
|
43456
|
-
params: record(string(), unknown()).optional()
|
|
44131
|
+
params: record(string(), unknown()).optional(),
|
|
44132
|
+
subnets: array(string()).optional()
|
|
43457
44133
|
}), object({ candidates: array(DiscoveryCandidateSchema).readonly() }), {
|
|
43458
44134
|
kind: "mutation",
|
|
43459
44135
|
auth: "admin"
|
|
@@ -61073,7 +61749,9 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
|
|
|
61073
61749
|
id: string(),
|
|
61074
61750
|
settings: record(string(), unknown()),
|
|
61075
61751
|
/** Keys the form's schema declares secret (by type) — keychain-stored (D787). */
|
|
61076
|
-
secretKeys: array(string()).optional()
|
|
61752
|
+
secretKeys: array(string()).optional(),
|
|
61753
|
+
/** The integration's own addon applies the change in-process and asks the hub not to respawn it. */
|
|
61754
|
+
skipRestart: boolean().optional()
|
|
61077
61755
|
}), object({ success: boolean() }), {
|
|
61078
61756
|
kind: "mutation",
|
|
61079
61757
|
auth: "admin"
|
|
@@ -61550,6 +62228,39 @@ var TlsStatusSchema = object({
|
|
|
61550
62228
|
hasCa: boolean()
|
|
61551
62229
|
}).nullable()
|
|
61552
62230
|
});
|
|
62231
|
+
/** One provider's ask of the scan engine: the modes it declared. */
|
|
62232
|
+
var ScanRequestSchema = object({
|
|
62233
|
+
providerId: string().min(1),
|
|
62234
|
+
modes: array(DiscoveryModeSchema)
|
|
62235
|
+
});
|
|
62236
|
+
var ScanErrorSchema = object({
|
|
62237
|
+
/** Absent for a failure that is the scan's own (a bad subnet, a socket that would not open). */
|
|
62238
|
+
providerId: string().optional(),
|
|
62239
|
+
/** The mode family that failed (`mdns`, `http-probe`, …). */
|
|
62240
|
+
mode: string().optional(),
|
|
62241
|
+
message: string()
|
|
62242
|
+
});
|
|
62243
|
+
/** What one scan did, per mode family; the engine logs the same numbers once. */
|
|
62244
|
+
var ScanStatsSchema = object({
|
|
62245
|
+
hostsProbed: number().int(),
|
|
62246
|
+
hits: number().int(),
|
|
62247
|
+
timeouts: number().int(),
|
|
62248
|
+
byMode: record(string(), object({
|
|
62249
|
+
probed: number().int(),
|
|
62250
|
+
hits: number().int(),
|
|
62251
|
+
timeouts: number().int()
|
|
62252
|
+
}))
|
|
62253
|
+
});
|
|
62254
|
+
var ScanNetworkResultSchema = object({
|
|
62255
|
+
hits: array(object({
|
|
62256
|
+
providerId: string(),
|
|
62257
|
+
hit: DiscoveryHitSchema
|
|
62258
|
+
})),
|
|
62259
|
+
/** CIDRs actually covered: the hub's own /24s, the saved list and the per-call list. */
|
|
62260
|
+
subnetsScanned: array(string()),
|
|
62261
|
+
errors: array(ScanErrorSchema),
|
|
62262
|
+
stats: ScanStatsSchema
|
|
62263
|
+
});
|
|
61553
62264
|
method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
|
|
61554
62265
|
/**
|
|
61555
62266
|
* LEGACY HINT — do not send from new code. Kept optional so clients
|
|
@@ -61604,6 +62315,16 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
|
|
|
61604
62315
|
}), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
|
|
61605
62316
|
kind: "mutation",
|
|
61606
62317
|
auth: "admin"
|
|
62318
|
+
}), method(object({
|
|
62319
|
+
requests: array(ScanRequestSchema),
|
|
62320
|
+
subnets: array(string()).optional()
|
|
62321
|
+
}), ScanNetworkResultSchema, {
|
|
62322
|
+
kind: "mutation",
|
|
62323
|
+
auth: "admin",
|
|
62324
|
+
timeoutMs: 9e4
|
|
62325
|
+
}), method(_void(), object({ subnets: array(string()) }), { auth: "admin" }), method(object({ subnets: array(string()) }), object({ subnets: array(string()) }), {
|
|
62326
|
+
kind: "mutation",
|
|
62327
|
+
auth: "admin"
|
|
61607
62328
|
}), method(_void(), TlsStatusSchema, {
|
|
61608
62329
|
kind: "mutation",
|
|
61609
62330
|
auth: "admin"
|
|
@@ -64146,7 +64867,7 @@ object({
|
|
|
64146
64867
|
});
|
|
64147
64868
|
Object.values(DeviceType);
|
|
64148
64869
|
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
|
|
64149
|
-
DeviceType.Camera, DeviceType.Sensor, DeviceType.Switch, method(object({ deviceId: number().int().nonnegative() }), object({ success: literal(true) }), {
|
|
64870
|
+
DeviceType.Camera, DeviceType.Sensor, DeviceType.Switch, DeviceType.Gateway, method(object({ deviceId: number().int().nonnegative() }), object({ success: literal(true) }), {
|
|
64150
64871
|
kind: "mutation",
|
|
64151
64872
|
auth: "admin"
|
|
64152
64873
|
});
|
|
@@ -68106,7 +68827,7 @@ discriminatedUnion("kind", [object({
|
|
|
68106
68827
|
})]);
|
|
68107
68828
|
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
68108
68829
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
68109
|
-
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;
|
|
68830
|
+
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;
|
|
68110
68831
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
68111
68832
|
DeviceRole.BinarySensor, DeviceRole.MotionSensor;
|
|
68112
68833
|
new Set(CAP_NAMES);
|
|
@@ -68159,7 +68880,7 @@ var AccessoryKind = {
|
|
|
68159
68880
|
PrivacyMask: DeviceRole.PrivacyMask
|
|
68160
68881
|
};
|
|
68161
68882
|
AccessoryKind.Siren, AccessoryKind.Floodlight, AccessoryKind.Spotlight, AccessoryKind.PirSensor, AccessoryKind.Chime, AccessoryKind.Autotrack, AccessoryKind.Nightvision, AccessoryKind.PrivacyMask;
|
|
68162
|
-
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;
|
|
68883
|
+
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;
|
|
68163
68884
|
new Set(Object.values(DeviceType));
|
|
68164
68885
|
Object.freeze({
|
|
68165
68886
|
"accessories.getStatus": {
|
|
@@ -68547,6 +69268,13 @@ Object.freeze({
|
|
|
68547
69268
|
access: "create",
|
|
68548
69269
|
role: "admin"
|
|
68549
69270
|
},
|
|
69271
|
+
"addonSettings.getDeclaredSettingsKeys": {
|
|
69272
|
+
capName: "addon-settings",
|
|
69273
|
+
capScope: "system",
|
|
69274
|
+
addonId: null,
|
|
69275
|
+
access: "view",
|
|
69276
|
+
role: "admin"
|
|
69277
|
+
},
|
|
68550
69278
|
"addonSettings.getDeviceSettings": {
|
|
68551
69279
|
capName: "addon-settings",
|
|
68552
69280
|
capScope: "system",
|
|
@@ -68568,6 +69296,13 @@ Object.freeze({
|
|
|
68568
69296
|
access: "view",
|
|
68569
69297
|
role: "admin"
|
|
68570
69298
|
},
|
|
69299
|
+
"addonSettings.unsetStoredSettingsKeys": {
|
|
69300
|
+
capName: "addon-settings",
|
|
69301
|
+
capScope: "system",
|
|
69302
|
+
addonId: null,
|
|
69303
|
+
access: "create",
|
|
69304
|
+
role: "admin"
|
|
69305
|
+
},
|
|
68571
69306
|
"addonSettings.updateDeviceSettings": {
|
|
68572
69307
|
capName: "addon-settings",
|
|
68573
69308
|
capScope: "system",
|
|
@@ -70983,6 +71718,13 @@ Object.freeze({
|
|
|
70983
71718
|
access: "create",
|
|
70984
71719
|
role: "admin"
|
|
70985
71720
|
},
|
|
71721
|
+
"deviceProvider.classifyDiscoveryHits": {
|
|
71722
|
+
capName: "device-provider",
|
|
71723
|
+
capScope: "system",
|
|
71724
|
+
addonId: null,
|
|
71725
|
+
access: "create",
|
|
71726
|
+
role: "admin"
|
|
71727
|
+
},
|
|
70986
71728
|
"deviceProvider.createDevice": {
|
|
70987
71729
|
capName: "device-provider",
|
|
70988
71730
|
capScope: "system",
|
|
@@ -71011,6 +71753,13 @@ Object.freeze({
|
|
|
71011
71753
|
access: "view",
|
|
71012
71754
|
role: "admin"
|
|
71013
71755
|
},
|
|
71756
|
+
"deviceProvider.getDiscoveryModes": {
|
|
71757
|
+
capName: "device-provider",
|
|
71758
|
+
capScope: "system",
|
|
71759
|
+
addonId: null,
|
|
71760
|
+
access: "view",
|
|
71761
|
+
role: "admin"
|
|
71762
|
+
},
|
|
71014
71763
|
"deviceProvider.getDiscoveryParamsSchema": {
|
|
71015
71764
|
capName: "device-provider",
|
|
71016
71765
|
capScope: "system",
|
|
@@ -71788,6 +72537,13 @@ Object.freeze({
|
|
|
71788
72537
|
access: "create",
|
|
71789
72538
|
role: "admin"
|
|
71790
72539
|
},
|
|
72540
|
+
"keychain.releaseRecordRefs": {
|
|
72541
|
+
capName: "keychain",
|
|
72542
|
+
capScope: "system",
|
|
72543
|
+
addonId: null,
|
|
72544
|
+
access: "create",
|
|
72545
|
+
role: "admin"
|
|
72546
|
+
},
|
|
71791
72547
|
"keychain.resolve": {
|
|
71792
72548
|
capName: "keychain",
|
|
71793
72549
|
capScope: "system",
|
|
@@ -72208,6 +72964,13 @@ Object.freeze({
|
|
|
72208
72964
|
access: "view",
|
|
72209
72965
|
role: "admin"
|
|
72210
72966
|
},
|
|
72967
|
+
"localNetwork.getScanSubnets": {
|
|
72968
|
+
capName: "local-network",
|
|
72969
|
+
capScope: "system",
|
|
72970
|
+
addonId: null,
|
|
72971
|
+
access: "view",
|
|
72972
|
+
role: "admin"
|
|
72973
|
+
},
|
|
72211
72974
|
"localNetwork.getTlsStatus": {
|
|
72212
72975
|
capName: "local-network",
|
|
72213
72976
|
capScope: "system",
|
|
@@ -72257,6 +73020,13 @@ Object.freeze({
|
|
|
72257
73020
|
access: "create",
|
|
72258
73021
|
role: "admin"
|
|
72259
73022
|
},
|
|
73023
|
+
"localNetwork.scanNetwork": {
|
|
73024
|
+
capName: "local-network",
|
|
73025
|
+
capScope: "system",
|
|
73026
|
+
addonId: null,
|
|
73027
|
+
access: "create",
|
|
73028
|
+
role: "admin"
|
|
73029
|
+
},
|
|
72260
73030
|
"localNetwork.setAddressRole": {
|
|
72261
73031
|
capName: "local-network",
|
|
72262
73032
|
capScope: "system",
|
|
@@ -72278,6 +73048,13 @@ Object.freeze({
|
|
|
72278
73048
|
access: "create",
|
|
72279
73049
|
role: "admin"
|
|
72280
73050
|
},
|
|
73051
|
+
"localNetwork.setScanSubnets": {
|
|
73052
|
+
capName: "local-network",
|
|
73053
|
+
capScope: "system",
|
|
73054
|
+
addonId: null,
|
|
73055
|
+
access: "create",
|
|
73056
|
+
role: "admin"
|
|
73057
|
+
},
|
|
72281
73058
|
"localNetwork.setViewerEndpoints": {
|
|
72282
73059
|
capName: "local-network",
|
|
72283
73060
|
capScope: "system",
|
|
@@ -76499,6 +77276,13 @@ Object.freeze({
|
|
|
76499
77276
|
access: "create",
|
|
76500
77277
|
role: "admin"
|
|
76501
77278
|
},
|
|
77279
|
+
"storageAdmin.dropOrphanedKind": {
|
|
77280
|
+
capName: "storage-admin",
|
|
77281
|
+
capScope: "system",
|
|
77282
|
+
addonId: null,
|
|
77283
|
+
access: "delete",
|
|
77284
|
+
role: "admin"
|
|
77285
|
+
},
|
|
76502
77286
|
"storageAdmin.listKinds": {
|
|
76503
77287
|
capName: "storage-admin",
|
|
76504
77288
|
capScope: "system",
|
|
@@ -76520,6 +77304,27 @@ Object.freeze({
|
|
|
76520
77304
|
access: "view",
|
|
76521
77305
|
role: "admin"
|
|
76522
77306
|
},
|
|
77307
|
+
"storageAdmin.scanStaleData": {
|
|
77308
|
+
capName: "storage-admin",
|
|
77309
|
+
capScope: "system",
|
|
77310
|
+
addonId: null,
|
|
77311
|
+
access: "create",
|
|
77312
|
+
role: "admin"
|
|
77313
|
+
},
|
|
77314
|
+
"storageAdmin.staleDataReport": {
|
|
77315
|
+
capName: "storage-admin",
|
|
77316
|
+
capScope: "system",
|
|
77317
|
+
addonId: null,
|
|
77318
|
+
access: "view",
|
|
77319
|
+
role: "admin"
|
|
77320
|
+
},
|
|
77321
|
+
"storageAdmin.unsetStaleSettingsKey": {
|
|
77322
|
+
capName: "storage-admin",
|
|
77323
|
+
capScope: "system",
|
|
77324
|
+
addonId: null,
|
|
77325
|
+
access: "create",
|
|
77326
|
+
role: "admin"
|
|
77327
|
+
},
|
|
76523
77328
|
"storageEvictable.evict": {
|
|
76524
77329
|
capName: "storage-evictable",
|
|
76525
77330
|
capScope: "system",
|
|
@@ -82216,7 +83021,7 @@ function projectAction(cap, action) {
|
|
|
82216
83021
|
};
|
|
82217
83022
|
}
|
|
82218
83023
|
Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
|
|
82219
|
-
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;
|
|
83024
|
+
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;
|
|
82220
83025
|
DeviceFeature.BatteryOperated;
|
|
82221
83026
|
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
82222
83027
|
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.";
|