@camstack/addon-mqtt-broker 1.2.149 → 1.2.151
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/mqtt-broker.addon.js +711 -287
- package/dist/mqtt-broker.addon.mjs +711 -287
- package/package.json +2 -2
|
@@ -6028,7 +6028,7 @@ var ZodIssueCode = {
|
|
|
6028
6028
|
var ZodFirstPartyTypeKind;
|
|
6029
6029
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
6030
6030
|
//#endregion
|
|
6031
|
-
//#region ../types/dist/sleep-
|
|
6031
|
+
//#region ../types/dist/sleep-DEZaLbiu.mjs
|
|
6032
6032
|
/**
|
|
6033
6033
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
6034
6034
|
* window to float samples (D455).
|
|
@@ -6628,7 +6628,7 @@ var NOT_RUN = {
|
|
|
6628
6628
|
* throws: an unreachable or refusing engine is logged and reported `declared: false`,
|
|
6629
6629
|
* because the hub (and this addon's boot) must not hang on a migration.
|
|
6630
6630
|
*/
|
|
6631
|
-
async function runAddonMigrations(host, migrations, logger) {
|
|
6631
|
+
async function runAddonMigrations(host, migrations, logger, options = {}) {
|
|
6632
6632
|
const first = migrations[0];
|
|
6633
6633
|
if (first === void 0) return NOT_RUN;
|
|
6634
6634
|
const owner = first.meta.owner;
|
|
@@ -6657,7 +6657,9 @@ async function runAddonMigrations(host, migrations, logger) {
|
|
|
6657
6657
|
else if (status === "blocked") blocked.push(id);
|
|
6658
6658
|
else skipped.push(id);
|
|
6659
6659
|
};
|
|
6660
|
+
const selected = options.only === void 0 ? null : new Set(options.only);
|
|
6660
6661
|
for (const step of declaration.steps) {
|
|
6662
|
+
if (selected !== null && !selected.has(step.id)) continue;
|
|
6661
6663
|
const bound = byId.get(step.id);
|
|
6662
6664
|
if (bound === void 0 || step.action === "skip") {
|
|
6663
6665
|
skipped.push(step.id);
|
|
@@ -6835,6 +6837,12 @@ function projectStore(store, perNodeKeys, nodeId) {
|
|
|
6835
6837
|
}
|
|
6836
6838
|
return out;
|
|
6837
6839
|
}
|
|
6840
|
+
/** Every scope, in the order the guard and the docs list them. */
|
|
6841
|
+
var SCOPED_SETTINGS_SCOPES = [
|
|
6842
|
+
"cluster",
|
|
6843
|
+
"device-defaults",
|
|
6844
|
+
"node"
|
|
6845
|
+
];
|
|
6838
6846
|
/**
|
|
6839
6847
|
* The address of one keychain value: `secret://<owner>/<scope>/<fieldPath>`.
|
|
6840
6848
|
*
|
|
@@ -7726,6 +7734,13 @@ var BaseAddon = class {
|
|
|
7726
7734
|
*/
|
|
7727
7735
|
secretStatePaths = [];
|
|
7728
7736
|
_addonSecretTree = null;
|
|
7737
|
+
/**
|
|
7738
|
+
* D986 M4: the scoped schemas this addon declared, resolved once per capability
|
|
7739
|
+
* (`loadScopedSchemas`) so the sync readers (`perNodeKeys`, the secret tree, the restart check)
|
|
7740
|
+
* see them. Empty until loaded: the readers then fall back to {@link globalSettingsSchema}.
|
|
7741
|
+
*/
|
|
7742
|
+
_scopedByCap = /* @__PURE__ */ new Map();
|
|
7743
|
+
_nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
7729
7744
|
_deviceSecretTree = null;
|
|
7730
7745
|
/** Refs the last resolved config pointed at — a change to one re-resolves. */
|
|
7731
7746
|
_configSecretRefs = /* @__PURE__ */ new Set();
|
|
@@ -7795,6 +7810,7 @@ var BaseAddon = class {
|
|
|
7795
7810
|
this._ctx = context;
|
|
7796
7811
|
this._secretSettings = null;
|
|
7797
7812
|
}
|
|
7813
|
+
await this.loadScopedSchemas();
|
|
7798
7814
|
await this.resolveConfig();
|
|
7799
7815
|
if (secrets !== void 0) this._secretChangeUnsub = secrets.onChange((ref) => {
|
|
7800
7816
|
if (this._configSecretRefs.has(ref)) this.scheduleSecretReresolve(SECRET_CHANGE_DEBOUNCE_MS);
|
|
@@ -7804,6 +7820,7 @@ var BaseAddon = class {
|
|
|
7804
7820
|
const normalized = normalizeAddonInitResult(result);
|
|
7805
7821
|
const providers = normalized && "providers" in normalized && normalized.providers ? normalized.providers : [];
|
|
7806
7822
|
this._registeredCapNames = providers.map((p) => p.capability.name);
|
|
7823
|
+
await this.loadScopedSchemas();
|
|
7807
7824
|
return normalized;
|
|
7808
7825
|
}
|
|
7809
7826
|
/**
|
|
@@ -7817,14 +7834,18 @@ var BaseAddon = class {
|
|
|
7817
7834
|
* nothing; the boot continues. A migration that FAILED (or was blocked by one)
|
|
7818
7835
|
* and declares `onFailure: 'block-owner'` makes this throw, so the addon does not
|
|
7819
7836
|
* serve data whose shape it cannot trust; `tolerate` migrations never do.
|
|
7837
|
+
*
|
|
7838
|
+
* ALWAYS pass the whole catalogue; a phase that must run only some of it passes
|
|
7839
|
+
* `{ only: [ids] }` — never a filtered catalogue (the engine would judge the
|
|
7840
|
+
* omitted applied ids "newer").
|
|
7820
7841
|
*/
|
|
7821
|
-
async runMigrations(migrations) {
|
|
7842
|
+
async runMigrations(migrations, options) {
|
|
7822
7843
|
const api = this.ctx.api.migrations;
|
|
7823
7844
|
const report = await runAddonMigrations({
|
|
7824
7845
|
declareOwner: (input) => api.declareOwner.mutate(input),
|
|
7825
7846
|
startMigration: (input) => api.startMigration.mutate(input),
|
|
7826
7847
|
finishMigration: (input) => api.finishMigration.mutate(input)
|
|
7827
|
-
}, migrations, this.ctx.logger);
|
|
7848
|
+
}, migrations, this.ctx.logger, options);
|
|
7828
7849
|
if (report.blockOwner.length > 0) throw new AddonMigrationsBlockedError(report.blockOwner);
|
|
7829
7850
|
return report;
|
|
7830
7851
|
}
|
|
@@ -7888,6 +7909,16 @@ var BaseAddon = class {
|
|
|
7888
7909
|
schema(schema) {
|
|
7889
7910
|
return schema;
|
|
7890
7911
|
}
|
|
7912
|
+
/**
|
|
7913
|
+
* The capability names whose scoped settings schemas ({@link clusterSettingsSchema},
|
|
7914
|
+
* {@link nodeSettingsSchema}, {@link deviceDefaultsSchema}) this addon declares. Read BEFORE
|
|
7915
|
+
* `onInitialize`, when the providers it registers are not known yet: the first config resolve
|
|
7916
|
+
* must already know which keys are per node. Names the providers register at init are added
|
|
7917
|
+
* once known.
|
|
7918
|
+
*/
|
|
7919
|
+
scopedSettingsCapabilities() {
|
|
7920
|
+
return [];
|
|
7921
|
+
}
|
|
7891
7922
|
/** Override to provide global-level settings UI schema. */
|
|
7892
7923
|
globalSettingsSchema(_cap) {
|
|
7893
7924
|
return null;
|
|
@@ -7931,7 +7962,8 @@ var BaseAddon = class {
|
|
|
7931
7962
|
return [];
|
|
7932
7963
|
}
|
|
7933
7964
|
async getGlobalSettings(overlay, cap, nodeId) {
|
|
7934
|
-
|
|
7965
|
+
await this.loadScopedSchemas(cap);
|
|
7966
|
+
const schema = this.globalSettingsSchema(cap) ?? this.scopedFormSchema(cap);
|
|
7935
7967
|
if (!schema) return { sections: [] };
|
|
7936
7968
|
const projected = await this.readRawProjectedStore(nodeId, cap);
|
|
7937
7969
|
return hydrateSchema(schema, overlay ? await this.overlayKeepingMarkers(projected, overlay) : projected, { inlineSecretsAsViews: true });
|
|
@@ -7966,10 +7998,9 @@ var BaseAddon = class {
|
|
|
7966
7998
|
*
|
|
7967
7999
|
* Override it only when an `instanceMode: multiple` addon's instances differ: an NVR integration
|
|
7968
8000
|
* needs its connection fields, the default integration the addon's own sections. It is the
|
|
7969
|
-
* SHAPE of the form only — the values live
|
|
7970
|
-
* unchanged (`integrations.setSettings` for an account integration).
|
|
8001
|
+
* SHAPE of the form only — the values live on the instance record.
|
|
7971
8002
|
*/
|
|
7972
|
-
async
|
|
8003
|
+
async integrationFormSchema(_instance) {
|
|
7973
8004
|
return null;
|
|
7974
8005
|
}
|
|
7975
8006
|
/**
|
|
@@ -8015,7 +8046,7 @@ var BaseAddon = class {
|
|
|
8015
8046
|
* per-instance schema when it declares one, else its global schema (what they always showed).
|
|
8016
8047
|
*/
|
|
8017
8048
|
async getIntegrationFormSchema(instance, nodeId) {
|
|
8018
|
-
const scoped = await this.
|
|
8049
|
+
const scoped = await this.integrationFormSchema(instance);
|
|
8019
8050
|
if (scoped === null) return this.getGlobalSettings(void 0, void 0, nodeId);
|
|
8020
8051
|
return hydrateSchema(scoped, {}, { inlineSecretsAsViews: true });
|
|
8021
8052
|
}
|
|
@@ -8063,6 +8094,7 @@ var BaseAddon = class {
|
|
|
8063
8094
|
* resolved blob back is a no-op for every unchanged secret.
|
|
8064
8095
|
*/
|
|
8065
8096
|
async resolveGlobalStore(nodeId, cap) {
|
|
8097
|
+
await this.loadScopedSchemas(cap);
|
|
8066
8098
|
const raw = await this._ctx?.settings?.readAddonStore() ?? {};
|
|
8067
8099
|
return this.projectForNode(raw, nodeId, cap);
|
|
8068
8100
|
}
|
|
@@ -8073,6 +8105,7 @@ var BaseAddon = class {
|
|
|
8073
8105
|
}
|
|
8074
8106
|
/** The PROJECTED store with its markers — the form hydrate only (D798). */
|
|
8075
8107
|
async readRawProjectedStore(nodeId, cap) {
|
|
8108
|
+
await this.loadScopedSchemas(cap);
|
|
8076
8109
|
const raw = await this.rawSettings()?.readAddonStore() ?? {};
|
|
8077
8110
|
return this.projectForNode(raw, nodeId, cap);
|
|
8078
8111
|
}
|
|
@@ -8165,12 +8198,17 @@ var BaseAddon = class {
|
|
|
8165
8198
|
const stored = keychain ? await this.readAddonStoreWithRetry() : {};
|
|
8166
8199
|
const patch = {};
|
|
8167
8200
|
for (const key of keys) {
|
|
8201
|
+
if (tree.get(key)?.kind === "array") {
|
|
8202
|
+
patch[key] = [];
|
|
8203
|
+
continue;
|
|
8204
|
+
}
|
|
8168
8205
|
const keychainStored = tree.has(key) || collectSecretMarkerRefs(stored[key]).length > 0;
|
|
8169
8206
|
patch[key] = keychain && keychainStored ? SECRET_CLEAR : "";
|
|
8170
8207
|
}
|
|
8171
8208
|
await this.applyGlobalPatch(patch, nodeId);
|
|
8172
8209
|
}
|
|
8173
8210
|
async applyGlobalPatch(barePatch, nodeId) {
|
|
8211
|
+
await this.loadScopedSchemas();
|
|
8174
8212
|
const keys = this.perNodeKeys();
|
|
8175
8213
|
const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
|
|
8176
8214
|
const target = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
|
|
@@ -8184,7 +8222,8 @@ var BaseAddon = class {
|
|
|
8184
8222
|
await this.resolveConfig();
|
|
8185
8223
|
await this.onConfigChanged();
|
|
8186
8224
|
this.emitLifecycle(EventCategory.AddonUpdated, { level: "global" });
|
|
8187
|
-
|
|
8225
|
+
const legacy = this.globalSettingsSchema();
|
|
8226
|
+
this.maybeAutoRestart(Object.keys(barePatch), [...legacy === null ? [] : [legacy], ...this.loadedScopedSchemas().map((entry) => entry.schema)]);
|
|
8188
8227
|
}
|
|
8189
8228
|
/**
|
|
8190
8229
|
* The set of field keys the global settings schema declares `perNode: true`
|
|
@@ -8192,14 +8231,67 @@ var BaseAddon = class {
|
|
|
8192
8231
|
* declarations). Empty set ⇒ every per-node code path is bypassed and the
|
|
8193
8232
|
* settings API behaves exactly like the legacy node-agnostic one.
|
|
8194
8233
|
*/
|
|
8195
|
-
|
|
8234
|
+
/**
|
|
8235
|
+
* Load the scoped schemas of every capability this addon declares (the override and the
|
|
8236
|
+
* providers registered at init, plus `extraCap`) that is not loaded yet, then drop what was
|
|
8237
|
+
* derived from the previous set. Idempotent.
|
|
8238
|
+
*/
|
|
8239
|
+
async loadScopedSchemas(extraCap) {
|
|
8240
|
+
const caps = new Set([
|
|
8241
|
+
...this.scopedSettingsCapabilities(),
|
|
8242
|
+
...this._registeredCapNames,
|
|
8243
|
+
...extraCap === void 0 ? [] : [extraCap]
|
|
8244
|
+
]);
|
|
8245
|
+
let added = false;
|
|
8246
|
+
for (const cap of caps) {
|
|
8247
|
+
if (this._scopedByCap.has(cap)) continue;
|
|
8248
|
+
const loaded = [];
|
|
8249
|
+
for (const scope of SCOPED_SETTINGS_SCOPES) {
|
|
8250
|
+
const schema = await this.getScopedSettingsSchema(scope, cap);
|
|
8251
|
+
if (schema !== null) loaded.push({
|
|
8252
|
+
scope,
|
|
8253
|
+
schema
|
|
8254
|
+
});
|
|
8255
|
+
}
|
|
8256
|
+
this._scopedByCap.set(cap, loaded);
|
|
8257
|
+
added = true;
|
|
8258
|
+
}
|
|
8259
|
+
if (!added) return;
|
|
8260
|
+
this._nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
8261
|
+
this._addonSecretTree = null;
|
|
8262
|
+
}
|
|
8263
|
+
/**
|
|
8264
|
+
* The form the settings API serves while an addon declares no {@link globalSettingsSchema}: the
|
|
8265
|
+
* sections of its scoped schemas, so dropping the old hook does not blank the form.
|
|
8266
|
+
*/
|
|
8267
|
+
scopedFormSchema(cap) {
|
|
8268
|
+
const byId = /* @__PURE__ */ new Map();
|
|
8269
|
+
for (const entry of this.loadedScopedSchemas(cap)) for (const section of entry.schema.sections) if (!byId.has(section.id)) byId.set(section.id, section);
|
|
8270
|
+
return byId.size === 0 ? null : { sections: [...byId.values()] };
|
|
8271
|
+
}
|
|
8272
|
+
/** Every loaded scoped schema of `cap`, or of every loaded capability. */
|
|
8273
|
+
loadedScopedSchemas(cap) {
|
|
8274
|
+
if (cap !== void 0) return this._scopedByCap.get(cap) ?? [];
|
|
8275
|
+
return [...this._scopedByCap.values()].flat();
|
|
8276
|
+
}
|
|
8277
|
+
/**
|
|
8278
|
+
* The field keys that are stored per node: every field of a {@link nodeSettingsSchema}, plus
|
|
8279
|
+
* the fields the legacy {@link globalSettingsSchema} still marks `perNode: true`. Derived once
|
|
8280
|
+
* per `cap` argument and memoized (schemas are static declarations). Empty set ⇒ every
|
|
8281
|
+
* per-node code path is bypassed and the settings API behaves like the node-agnostic one.
|
|
8282
|
+
* Call {@link loadScopedSchemas} first (the async seams do).
|
|
8283
|
+
*/
|
|
8196
8284
|
perNodeKeys(cap) {
|
|
8197
8285
|
const cacheKey = cap ?? "";
|
|
8198
|
-
const cached = this.
|
|
8286
|
+
const cached = this._nodeKeysByCap.get(cacheKey);
|
|
8199
8287
|
if (cached) return cached;
|
|
8200
|
-
const
|
|
8201
|
-
const keys = new Set(
|
|
8202
|
-
this.
|
|
8288
|
+
const legacy = this.globalSettingsSchema(cap);
|
|
8289
|
+
const keys = new Set(legacy ? legacy.sections.flatMap((section) => collectPerNodeFieldKeys(section.fields)) : []);
|
|
8290
|
+
for (const { scope, schema } of this.loadedScopedSchemas(cap)) {
|
|
8291
|
+
if (scope !== "node") continue;
|
|
8292
|
+
for (const section of schema.sections) for (const key of collectValueFieldKeys(section.fields)) keys.add(key);
|
|
8293
|
+
}
|
|
8294
|
+
this._nodeKeysByCap.set(cacheKey, keys);
|
|
8203
8295
|
return keys;
|
|
8204
8296
|
}
|
|
8205
8297
|
/**
|
|
@@ -8209,10 +8301,9 @@ var BaseAddon = class {
|
|
|
8209
8301
|
* time to return its response before the addon is torn down and
|
|
8210
8302
|
* re-initialised by `AddonRegistryService.restartAddon`.
|
|
8211
8303
|
*/
|
|
8212
|
-
maybeAutoRestart(patchKeys,
|
|
8213
|
-
if (!schema) return;
|
|
8304
|
+
maybeAutoRestart(patchKeys, schemas) {
|
|
8214
8305
|
const restartKeys = /* @__PURE__ */ new Set();
|
|
8215
|
-
for (const section of schema.sections) for (const field of section.fields) {
|
|
8306
|
+
for (const schema of schemas) for (const section of schema.sections) for (const field of section.fields) {
|
|
8216
8307
|
if (field.type === "separator" || field.type === "info") continue;
|
|
8217
8308
|
if (field.requiresRestart) restartKeys.add(field.key);
|
|
8218
8309
|
}
|
|
@@ -8408,12 +8499,14 @@ var BaseAddon = class {
|
|
|
8408
8499
|
* here, in the addon's own process, so the hub keeps no copy of it.
|
|
8409
8500
|
*/
|
|
8410
8501
|
async getDeclaredSettingsKeys() {
|
|
8502
|
+
await this.loadScopedSchemas();
|
|
8411
8503
|
return { keys: [...this.declaredKeySet()].toSorted() };
|
|
8412
8504
|
}
|
|
8413
8505
|
declaredKeySet() {
|
|
8414
8506
|
const keys = new Set(Object.keys(this.defaults));
|
|
8415
8507
|
const schema = this.globalSettingsSchema();
|
|
8416
8508
|
if (schema) for (const section of schema.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
8509
|
+
for (const { schema: scoped } of this.loadedScopedSchemas()) for (const section of scoped.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
8417
8510
|
for (const key of this._declaredStateKeys) keys.add(key);
|
|
8418
8511
|
for (const path of this.secretStatePaths) {
|
|
8419
8512
|
const root = path.split(/[.[]/, 1)[0];
|
|
@@ -8509,7 +8602,11 @@ var BaseAddon = class {
|
|
|
8509
8602
|
}
|
|
8510
8603
|
/** The addon store's secret tree: schema fields by TYPE, plus `secretStatePaths`. */
|
|
8511
8604
|
addonSecretTree() {
|
|
8512
|
-
this._addonSecretTree
|
|
8605
|
+
if (this._addonSecretTree === null) {
|
|
8606
|
+
const tree = new Map(secretTreeFromSchema(this.globalSettingsSchema()));
|
|
8607
|
+
for (const { schema } of this.loadedScopedSchemas()) for (const [key, node] of secretTreeFromSchema(schema)) tree.set(key, node);
|
|
8608
|
+
this._addonSecretTree = mergeSecretStatePaths(tree, this.secretStatePaths);
|
|
8609
|
+
}
|
|
8513
8610
|
return this._addonSecretTree;
|
|
8514
8611
|
}
|
|
8515
8612
|
deviceSecretTree() {
|
|
@@ -8759,6 +8856,23 @@ function dropPerNodeFields(fields) {
|
|
|
8759
8856
|
}
|
|
8760
8857
|
return kept;
|
|
8761
8858
|
}
|
|
8859
|
+
/** The keys of the value-holding fields, recursing into layout containers. */
|
|
8860
|
+
function collectValueFieldKeys(fields) {
|
|
8861
|
+
const collected = [];
|
|
8862
|
+
for (const field of fields) {
|
|
8863
|
+
if (field.type === "group") {
|
|
8864
|
+
collected.push(...collectValueFieldKeys(field.fields));
|
|
8865
|
+
continue;
|
|
8866
|
+
}
|
|
8867
|
+
if (field.type === "sub-tabs") {
|
|
8868
|
+
for (const tab of field.tabs) collected.push(...collectValueFieldKeys(tab.fields));
|
|
8869
|
+
continue;
|
|
8870
|
+
}
|
|
8871
|
+
if (field.type === "separator" || field.type === "info") continue;
|
|
8872
|
+
if ("key" in field && typeof field.key === "string") collected.push(field.key);
|
|
8873
|
+
}
|
|
8874
|
+
return collected;
|
|
8875
|
+
}
|
|
8762
8876
|
function collectFieldKeys(fields) {
|
|
8763
8877
|
const collected = [];
|
|
8764
8878
|
for (const field of fields) {
|
|
@@ -8860,6 +8974,10 @@ var RECORD_SECRET_COLLECTION_PREFIX = "records:";
|
|
|
8860
8974
|
* (`collectZodSecretPaths`): dotted keys, `[]` for "each element of this array"
|
|
8861
8975
|
* (`auth.accessToken`, `headers[].value`).
|
|
8862
8976
|
*
|
|
8977
|
+
* `{}` after a key means "each value of this map" (`applianceSecrets{}`): the map's own key
|
|
8978
|
+
* names the value (`applianceSecrets{fridge}`), so no id key is declared and a body that drops a
|
|
8979
|
+
* key releases exactly that key's secret.
|
|
8980
|
+
*
|
|
8863
8981
|
* Array elements are matched by a DECLARED id key (`secretArrayKeys`:
|
|
8864
8982
|
* `{ headers: 'name' }`), never by index, so reordering a list keeps every
|
|
8865
8983
|
* keychain ref (the same rule as `arrayItemFieldPath`). The concrete path of a
|
|
@@ -8872,11 +8990,13 @@ function parseSecretPath(path) {
|
|
|
8872
8990
|
if (path.length === 0) throw new InvalidRecordError("an empty secret path");
|
|
8873
8991
|
return path.split(".").map((raw) => {
|
|
8874
8992
|
const each = raw.endsWith("[]");
|
|
8875
|
-
const
|
|
8993
|
+
const map = raw.endsWith("{}");
|
|
8994
|
+
const key = each || map ? raw.slice(0, -2) : raw;
|
|
8876
8995
|
if (key.length === 0) throw new InvalidRecordError(`secret path "${path}" has an empty segment`);
|
|
8877
8996
|
return {
|
|
8878
8997
|
key,
|
|
8879
|
-
each
|
|
8998
|
+
each,
|
|
8999
|
+
map
|
|
8880
9000
|
};
|
|
8881
9001
|
});
|
|
8882
9002
|
}
|
|
@@ -8928,6 +9048,22 @@ function mapAt(node, segments, index, walkedKeys, concretePrefix, declared, arra
|
|
|
8928
9048
|
[segment.key]: mapped
|
|
8929
9049
|
};
|
|
8930
9050
|
}
|
|
9051
|
+
if (segment.map) {
|
|
9052
|
+
if (!isRecord$4(child)) return node;
|
|
9053
|
+
const kept = Object.entries(child).map(([mapKey, value]) => {
|
|
9054
|
+
const concrete = `${concretePrefix}${concretePrefix.length > 0 ? "." : ""}${segment.key}{${encodeURIComponent(mapKey)}}`;
|
|
9055
|
+
if (!last) return [mapKey, mapAt(value, segments, index + 1, keys, concrete, declared, arrayKeys, visit)];
|
|
9056
|
+
return [mapKey, visit({
|
|
9057
|
+
declared,
|
|
9058
|
+
concrete,
|
|
9059
|
+
value
|
|
9060
|
+
})];
|
|
9061
|
+
}).filter(([, value]) => value !== REMOVE_LEAF);
|
|
9062
|
+
return {
|
|
9063
|
+
...node,
|
|
9064
|
+
[segment.key]: Object.fromEntries(kept)
|
|
9065
|
+
};
|
|
9066
|
+
}
|
|
8931
9067
|
const concrete = `${concretePrefix}${concretePrefix.length > 0 ? "." : ""}${segment.key}`;
|
|
8932
9068
|
if (last) {
|
|
8933
9069
|
const replacement = visit({
|
|
@@ -9069,7 +9205,8 @@ function isZodSecret(schema) {
|
|
|
9069
9205
|
}
|
|
9070
9206
|
/**
|
|
9071
9207
|
* Every declared secret path in a config schema: dotted keys, `[]` for "each
|
|
9072
|
-
* element of this array" (`connection.password`, `rows[].token`)
|
|
9208
|
+
* element of this array" (`connection.password`, `rows[].token`), `{}` for "each value of this
|
|
9209
|
+
* map" (`applianceSecrets{}`). A declared
|
|
9073
9210
|
* object or array is reported as one path and not descended into — everything
|
|
9074
9211
|
* under it is secret.
|
|
9075
9212
|
*/
|
|
@@ -9080,6 +9217,7 @@ function collectZodSecretPaths(schema, prefix = "") {
|
|
|
9080
9217
|
const def = defOf(layer);
|
|
9081
9218
|
if (def.type === "object" && def.shape) for (const [key, child] of Object.entries(def.shape)) paths.push(...collectZodSecretPaths(child, prefix ? `${prefix}.${key}` : key));
|
|
9082
9219
|
else if (def.type === "array" && def.element) paths.push(...collectZodSecretPaths(def.element, `${prefix}[]`));
|
|
9220
|
+
else if (def.type === "record" && def.valueType) paths.push(...collectZodSecretPaths(def.valueType, `${prefix}{}`));
|
|
9083
9221
|
else if (def.type === "union" && def.options) for (const option of def.options) paths.push(...collectZodSecretPaths(option, prefix));
|
|
9084
9222
|
else if (def.type === "intersection") {
|
|
9085
9223
|
if (def.left) paths.push(...collectZodSecretPaths(def.left, prefix));
|
|
@@ -9219,7 +9357,7 @@ function defineRecordKinds(specs) {
|
|
|
9219
9357
|
retention: spec.retention,
|
|
9220
9358
|
deviceRemoval: spec.deviceRemoval,
|
|
9221
9359
|
maxDataBytes: spec.maxDataBytes,
|
|
9222
|
-
secretPaths: [...secretPaths],
|
|
9360
|
+
secretPaths: [...new Set([...secretPaths, ...spec.retiredSecretPaths ?? []])],
|
|
9223
9361
|
secretArrayKeys: spec.secretArrayKeys === void 0 ? void 0 : { ...spec.secretArrayKeys },
|
|
9224
9362
|
transactFamily: spec.transactFamily,
|
|
9225
9363
|
entry: spec.entry === void 0 ? void 0 : {
|
|
@@ -9261,9 +9399,11 @@ function defineEntryKind(name, spec) {
|
|
|
9261
9399
|
class: "config",
|
|
9262
9400
|
writeTrigger: spec.writeTrigger ?? "an operator adds or edits an entry in the entries UI",
|
|
9263
9401
|
retention: { none: "an entry goes only when the operator deletes it" },
|
|
9402
|
+
...spec.maxDataBytes === void 0 ? {} : { maxDataBytes: spec.maxDataBytes },
|
|
9264
9403
|
...spec.database === void 0 ? {} : { database: spec.database },
|
|
9265
9404
|
...spec.indexes === void 0 ? {} : { indexes: spec.indexes },
|
|
9266
9405
|
...spec.secretArrayKeys === void 0 ? {} : { secretArrayKeys: spec.secretArrayKeys },
|
|
9406
|
+
...spec.retiredSecretPaths === void 0 ? {} : { retiredSecretPaths: spec.retiredSecretPaths },
|
|
9267
9407
|
entry: {
|
|
9268
9408
|
title: spec.title,
|
|
9269
9409
|
...spec.nameField === void 0 ? {} : { nameField: spec.nameField }
|
|
@@ -11925,7 +12065,7 @@ object({
|
|
|
11925
12065
|
params: record(string(), union([string(), number()])).optional()
|
|
11926
12066
|
});
|
|
11927
12067
|
//#endregion
|
|
11928
|
-
//#region ../types/dist/composition-
|
|
12068
|
+
//#region ../types/dist/composition-pOHbTzRB.mjs
|
|
11929
12069
|
/**
|
|
11930
12070
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
11931
12071
|
*
|
|
@@ -12688,38 +12828,6 @@ function validateExpressionSource(src) {
|
|
|
12688
12828
|
return null;
|
|
12689
12829
|
}
|
|
12690
12830
|
/**
|
|
12691
|
-
* One power reading becomes a washer, dryer or dishwasher (D944 helper).
|
|
12692
|
-
*
|
|
12693
|
-
* Standby of the control board is under 5 W; a motor or a heater is far
|
|
12694
|
-
* above that, so 5 W is the line. A door lock or a short pump spike is not
|
|
12695
|
-
* a cycle, so running starts only after 30 s above the line.
|
|
12696
|
-
*
|
|
12697
|
-
* A wash, dry or dishwasher pause (soak, heat-wait, heater off between
|
|
12698
|
-
* bursts) lasts several minutes. Ending the cycle on the first dip would
|
|
12699
|
-
* report "finished" in the middle of the program, so the stop dwell is
|
|
12700
|
-
* 10 minutes on every type. The number is stored per type so one machine
|
|
12701
|
-
* can move later without moving the others.
|
|
12702
|
-
*/
|
|
12703
|
-
var APPLIANCE_TEMPLATE_ID = "appliance";
|
|
12704
|
-
var APPLIANCE_KINDS = [
|
|
12705
|
-
"washer",
|
|
12706
|
-
"dryer",
|
|
12707
|
-
"dishwasher"
|
|
12708
|
-
];
|
|
12709
|
-
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
12710
|
-
/**
|
|
12711
|
-
* A dumb plug-in stove or heater, built from a smart plug (a `Heater`).
|
|
12712
|
-
*
|
|
12713
|
-
* The plug is the actuator: `switch.setState` is a FORWARD to the plug's own
|
|
12714
|
-
* (D967), so the heater switches exactly as the plug does. Running is the
|
|
12715
|
-
* plug being on; when a power reading is also named, running is the draw
|
|
12716
|
-
* staying above the line instead (a plug left on with the stove's own
|
|
12717
|
-
* thermostat open is not heating). 50 W is far above any standby and below
|
|
12718
|
-
* the smallest radiator.
|
|
12719
|
-
*/
|
|
12720
|
-
var HEATER_TEMPLATE_ID = "heater";
|
|
12721
|
-
DeviceType.Heater;
|
|
12722
|
-
/**
|
|
12723
12831
|
* Statistics and integrals over a history read (D944).
|
|
12724
12832
|
*
|
|
12725
12833
|
* Pure: the same points always give the same answer, so a restart recomputes
|
|
@@ -12863,34 +12971,6 @@ var HelperSpecSchema = discriminatedUnion("kind", [
|
|
|
12863
12971
|
/** Actuator groups only. Readings leave this unset. */
|
|
12864
12972
|
active: ActiveCriterionSchema.optional()
|
|
12865
12973
|
}),
|
|
12866
|
-
object({
|
|
12867
|
-
kind: literal("appliance"),
|
|
12868
|
-
templateId: literal(APPLIANCE_TEMPLATE_ID),
|
|
12869
|
-
appliance: _enum(APPLIANCE_KINDS),
|
|
12870
|
-
thresholdWatts: number().positive(),
|
|
12871
|
-
startDwellMs: number().int().nonnegative(),
|
|
12872
|
-
stopDwellMs: number().int().nonnegative(),
|
|
12873
|
-
member: HelperMemberSchema,
|
|
12874
|
-
/**
|
|
12875
|
-
* The plug that powers the appliance (`switch.on`). Optional: when set, the
|
|
12876
|
-
* composition carries a `power-source` feature, so the hub can cut and restore
|
|
12877
|
-
* that plug (power-source link of a NEW-target composition).
|
|
12878
|
-
*/
|
|
12879
|
-
powerSwitch: HelperMemberSchema.optional(),
|
|
12880
|
-
/** How long a forced restart keeps the plug off; the cap's default when unset. */
|
|
12881
|
-
offSeconds: number().optional()
|
|
12882
|
-
}),
|
|
12883
|
-
object({
|
|
12884
|
-
kind: literal("heater"),
|
|
12885
|
-
templateId: literal(HEATER_TEMPLATE_ID),
|
|
12886
|
-
/** The plug, read and commanded at `switch.on`. */
|
|
12887
|
-
plug: HelperMemberSchema,
|
|
12888
|
-
/** The plug's power reading, when it has one: running is then the draw, not the switch. */
|
|
12889
|
-
meter: HelperMemberSchema.optional(),
|
|
12890
|
-
thresholdWatts: number().positive(),
|
|
12891
|
-
startDwellMs: number().int().nonnegative(),
|
|
12892
|
-
stopDwellMs: number().int().nonnegative()
|
|
12893
|
-
}),
|
|
12894
12974
|
object({
|
|
12895
12975
|
kind: literal("statistic"),
|
|
12896
12976
|
stat: _enum(STATISTIC_STATS),
|
|
@@ -13114,7 +13194,7 @@ var CompositionSchema = object({
|
|
|
13114
13194
|
kind: literal("existing"),
|
|
13115
13195
|
device: CompositionSourceRefSchema
|
|
13116
13196
|
})]),
|
|
13117
|
-
features: array(CompositionFeatureSchema).min(
|
|
13197
|
+
features: array(CompositionFeatureSchema).min(0).max(16),
|
|
13118
13198
|
/**
|
|
13119
13199
|
* Present when the block is a helper template. Absent on a free
|
|
13120
13200
|
* composition (the room example, device 4603). The editor reads this,
|
|
@@ -13332,11 +13412,49 @@ var IntegrationInstanceRefSchema = object({
|
|
|
13332
13412
|
]),
|
|
13333
13413
|
candidateStableId: string().optional()
|
|
13334
13414
|
});
|
|
13415
|
+
/**
|
|
13416
|
+
* `RecordIntegrationInstances` - the `integration-instances` provider over ONE
|
|
13417
|
+
* entry kind, so an addon that holds a connection serves it in ~30 lines.
|
|
13418
|
+
*
|
|
13419
|
+
* Every instance is one record of the addon's own kind, keyed by the
|
|
13420
|
+
* `integrations` registry id. The records door does the secret work: a field the
|
|
13421
|
+
* kind marks `.meta({ secret: true })` is a keychain marker bound to the row, so
|
|
13422
|
+
* deleting the row deletes the secret, and one row's value is never another's.
|
|
13423
|
+
*
|
|
13424
|
+
* Patch rules for a secret (`updateInstance`): omitted, `''`, the redaction
|
|
13425
|
+
* token or the view the form was given KEEP the stored secret;
|
|
13426
|
+
* `{ $secretClear: true }` clears it; any other string replaces it.
|
|
13427
|
+
*/
|
|
13335
13428
|
/** The most instances one addon lists (an integrations page, not a table). */
|
|
13336
13429
|
var INSTANCES_LIST_MAX = 1e3;
|
|
13337
13430
|
/** The keys of the kind's secret fields that sit at the top level of the body. */
|
|
13338
13431
|
function topLevelSecretKeys(paths) {
|
|
13339
|
-
return new Set(paths.filter((p) => !p.includes(".") && !p.includes("[")));
|
|
13432
|
+
return new Set(paths.filter((p) => !p.includes(".") && !p.includes("[") && !p.includes("{")));
|
|
13433
|
+
}
|
|
13434
|
+
/** The keys of the kind's top-level MAPS whose values are secrets (`applianceSecrets{}`). */
|
|
13435
|
+
function secretMapKeys(paths) {
|
|
13436
|
+
return new Set(paths.filter((p) => p.endsWith("{}") && !p.slice(0, -2).match(/[.[{]/)).map((p) => p.slice(0, -2)));
|
|
13437
|
+
}
|
|
13438
|
+
/**
|
|
13439
|
+
* A patch value for a map of secrets: an entry that is omitted, `''`, the redaction token or the
|
|
13440
|
+
* view the form was given KEEPS its stored secret (it is left out of the merge); `{ $secretClear }`
|
|
13441
|
+
* or `null` removes the entry; any other string replaces it. Null when nothing is left to merge.
|
|
13442
|
+
*/
|
|
13443
|
+
function secretMapPatch(value) {
|
|
13444
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
|
|
13445
|
+
const out = {};
|
|
13446
|
+
for (const [key, entry] of Object.entries(value)) if (entry === null || isSecretClear(entry)) out[key] = null;
|
|
13447
|
+
else if (typeof entry === "string" && entry !== "" && entry !== "__camstack_redacted__" && !isSecretFieldView(entry)) out[key] = entry;
|
|
13448
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
13449
|
+
}
|
|
13450
|
+
/** Does the kind's body schema carry an `enabled` field? */
|
|
13451
|
+
function declaresEnabled(kind) {
|
|
13452
|
+
const json = toJSONSchema(kind.schema, {
|
|
13453
|
+
unrepresentable: "any",
|
|
13454
|
+
io: "input"
|
|
13455
|
+
});
|
|
13456
|
+
const properties = Reflect.get(json, "properties");
|
|
13457
|
+
return typeof properties === "object" && properties !== null && "enabled" in properties;
|
|
13340
13458
|
}
|
|
13341
13459
|
var RecordIntegrationInstances = class {
|
|
13342
13460
|
records;
|
|
@@ -13344,14 +13462,20 @@ var RecordIntegrationInstances = class {
|
|
|
13344
13462
|
hooks;
|
|
13345
13463
|
handle;
|
|
13346
13464
|
secretKeys;
|
|
13465
|
+
secretMaps;
|
|
13347
13466
|
nameField;
|
|
13467
|
+
/** The `integrations` registry owns the on/off switch (`role: devices`); the body has none. */
|
|
13468
|
+
registryOwnsEnabled;
|
|
13348
13469
|
constructor(records, kind, hooks) {
|
|
13349
13470
|
this.records = records;
|
|
13350
13471
|
this.kind = kind;
|
|
13351
13472
|
this.hooks = hooks;
|
|
13352
13473
|
this.handle = records.kind(kind);
|
|
13353
13474
|
this.secretKeys = topLevelSecretKeys(kind.declaration.secretPaths);
|
|
13475
|
+
this.secretMaps = secretMapKeys(kind.declaration.secretPaths);
|
|
13354
13476
|
this.nameField = kind.declaration.entry?.nameField ?? "name";
|
|
13477
|
+
this.registryOwnsEnabled = hooks.description.role === "devices";
|
|
13478
|
+
if (this.registryOwnsEnabled && declaresEnabled(kind)) throw new Error(`kind "${kind.name}": the integrations registry owns the on/off switch of a role "devices" addon, so the body must not declare "enabled"`);
|
|
13355
13479
|
}
|
|
13356
13480
|
/**
|
|
13357
13481
|
* One instance read for the addon's own runtime; null when absent. When the keychain is
|
|
@@ -13396,15 +13520,23 @@ var RecordIntegrationInstances = class {
|
|
|
13396
13520
|
viewOf(record) {
|
|
13397
13521
|
const shown = secretViewsOf(this.records.owner, this.kind.name, record.id, this.kind.declaration, record.data);
|
|
13398
13522
|
const rawName = record.data[this.nameField];
|
|
13523
|
+
const kind = this.hooks.kindOf?.(record.data);
|
|
13524
|
+
const health = this.hooks.health?.(record.data, record.id);
|
|
13399
13525
|
return {
|
|
13400
13526
|
id: record.id,
|
|
13401
13527
|
name: typeof rawName === "string" ? rawName : record.id,
|
|
13402
13528
|
configured: this.hooks.configured(record.data),
|
|
13403
|
-
enabled: record.data.enabled !== false,
|
|
13404
|
-
formValues: shown
|
|
13529
|
+
...this.registryOwnsEnabled ? {} : { enabled: record.data.enabled !== false },
|
|
13530
|
+
formValues: shown,
|
|
13531
|
+
...kind !== void 0 ? { kind } : {},
|
|
13532
|
+
...health?.status !== void 0 ? { status: health.status } : {},
|
|
13533
|
+
...health?.lastCheckedAt !== void 0 ? { lastCheckedAt: health.lastCheckedAt } : {}
|
|
13405
13534
|
};
|
|
13406
13535
|
}
|
|
13407
|
-
describe = async () => this.hooks.description
|
|
13536
|
+
describe = async () => this.hooks.mintId === void 0 ? this.hooks.description : {
|
|
13537
|
+
...this.hooks.description,
|
|
13538
|
+
mintsId: true
|
|
13539
|
+
};
|
|
13408
13540
|
instanceFormSchema = async ({ id }) => this.hooks.formSchema(id);
|
|
13409
13541
|
listInstances = async () => {
|
|
13410
13542
|
return (await this.handle.query({ limit: INSTANCES_LIST_MAX })).records.map((record) => this.viewOf(record));
|
|
@@ -13414,37 +13546,46 @@ var RecordIntegrationInstances = class {
|
|
|
13414
13546
|
return record === null ? null : this.viewOf(record);
|
|
13415
13547
|
};
|
|
13416
13548
|
/** Insert ONE row (`ifRev: 0`: an existing id is refused, never overwritten). */
|
|
13417
|
-
createInstance = async ({ id, name }) => {
|
|
13549
|
+
createInstance = async ({ id, name, kind }) => {
|
|
13550
|
+
const minted = id ?? this.hooks.mintId?.({
|
|
13551
|
+
name,
|
|
13552
|
+
...kind !== void 0 ? { kind } : {}
|
|
13553
|
+
});
|
|
13554
|
+
if (minted === void 0) throw new Error("createInstance needs an id: this provider does not mint one");
|
|
13418
13555
|
await this.handle.put({
|
|
13419
|
-
id,
|
|
13420
|
-
data: this.hooks.initialBody(name)
|
|
13556
|
+
id: minted,
|
|
13557
|
+
data: this.hooks.initialBody(name, kind)
|
|
13421
13558
|
}, { ifRev: 0 });
|
|
13422
|
-
await this.hooks.onChanged?.(
|
|
13423
|
-
return { id };
|
|
13559
|
+
await this.hooks.onChanged?.(minted, "created");
|
|
13560
|
+
return { id: minted };
|
|
13424
13561
|
};
|
|
13425
13562
|
updateInstance = async ({ id, name, values }) => {
|
|
13426
13563
|
const patch = {};
|
|
13427
13564
|
for (const [key, value] of Object.entries(values)) {
|
|
13428
13565
|
if (value === void 0) continue;
|
|
13429
|
-
if (
|
|
13566
|
+
if (this.secretMaps.has(key)) {
|
|
13567
|
+
const entries = secretMapPatch(value);
|
|
13568
|
+
if (entries !== null) patch[key] = entries;
|
|
13569
|
+
} else if (!this.secretKeys.has(key)) patch[key] = value;
|
|
13430
13570
|
else if (isSecretClear(value)) patch[key] = null;
|
|
13431
13571
|
else if (typeof value === "string" && value !== "" && value !== "__camstack_redacted__" && !isSecretFieldView(value)) patch[key] = value;
|
|
13432
13572
|
}
|
|
13433
13573
|
if (name !== void 0) patch[this.nameField] = name;
|
|
13434
13574
|
await this.handle.patch(id, patch);
|
|
13435
|
-
await this.hooks.onChanged?.(id);
|
|
13575
|
+
await this.hooks.onChanged?.(id, "updated");
|
|
13436
13576
|
return { ok: true };
|
|
13437
13577
|
};
|
|
13438
13578
|
/** The switch lives in the record body; the addon is told so it applies it (D996). */
|
|
13439
13579
|
setInstanceEnabled = async ({ id, enabled }) => {
|
|
13580
|
+
if (this.registryOwnsEnabled) throw new Error(`instance "${id}": the integrations registry owns the on/off switch of this addon; set it with integrations.update`);
|
|
13440
13581
|
await this.handle.patch(id, { enabled });
|
|
13441
|
-
await this.hooks.onChanged?.(id);
|
|
13582
|
+
await this.hooks.onChanged?.(id, "enabled");
|
|
13442
13583
|
return { ok: true };
|
|
13443
13584
|
};
|
|
13444
13585
|
/** The row and its keychain refs go together (the records door). */
|
|
13445
13586
|
deleteInstance = async ({ id }) => {
|
|
13446
13587
|
await this.handle.delete(id);
|
|
13447
|
-
await this.hooks.onChanged?.(id);
|
|
13588
|
+
await this.hooks.onChanged?.(id, "deleted");
|
|
13448
13589
|
return { ok: true };
|
|
13449
13590
|
};
|
|
13450
13591
|
testInstance = async (input) => {
|
|
@@ -14515,7 +14656,13 @@ var MigrationLedgerEntrySchema = object({
|
|
|
14515
14656
|
* joined by the engine from the catalogues it knows this boot. Absent when no
|
|
14516
14657
|
* catalogue lists the id (a newer database).
|
|
14517
14658
|
*/
|
|
14518
|
-
meta: MigrationMetaSchema.optional()
|
|
14659
|
+
meta: MigrationMetaSchema.optional(),
|
|
14660
|
+
/**
|
|
14661
|
+
* Set when this is an applied row of an addon owner whose catalogue the answering
|
|
14662
|
+
* process has not loaded (the owner has not declared yet): the row is applied, the
|
|
14663
|
+
* process just cannot judge it. Never `skipped-newer` — that needs the owner loaded.
|
|
14664
|
+
*/
|
|
14665
|
+
ownerNotLoaded: boolean().optional()
|
|
14519
14666
|
});
|
|
14520
14667
|
/** A catalogued migration that is not applied, with where it stands. */
|
|
14521
14668
|
var MigrationPendingEntrySchema = object({
|
|
@@ -48894,49 +49041,6 @@ function actuator(input) {
|
|
|
48894
49041
|
};
|
|
48895
49042
|
}
|
|
48896
49043
|
new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
|
|
48897
|
-
var PowerSourceRefusalReasonSchema = _enum([
|
|
48898
|
-
"no-power-source",
|
|
48899
|
-
"switch-missing",
|
|
48900
|
-
"switch-offline",
|
|
48901
|
-
"switch-unknown",
|
|
48902
|
-
"restart-in-progress",
|
|
48903
|
-
"switch-call-failed"
|
|
48904
|
-
]);
|
|
48905
|
-
var PowerSourceActionResultSchema = discriminatedUnion("accepted", [object({ accepted: literal(true) }), object({
|
|
48906
|
-
accepted: literal(false),
|
|
48907
|
-
reason: PowerSourceRefusalReasonSchema,
|
|
48908
|
-
/** One line for the operator, naming the switch. */
|
|
48909
|
-
message: string()
|
|
48910
|
-
})]);
|
|
48911
|
-
var PowerSourceInfoSchema = object({
|
|
48912
|
-
/** False when the device has no power source linked; the rest is then empty. */
|
|
48913
|
-
linked: boolean(),
|
|
48914
|
-
switchDeviceId: number().nullable(),
|
|
48915
|
-
switchName: string().nullable(),
|
|
48916
|
-
/** The switch's on/off state, or null when it is unknown. Never false for unknown (D393). */
|
|
48917
|
-
switchOn: boolean().nullable(),
|
|
48918
|
-
switchOnline: boolean().nullable(),
|
|
48919
|
-
restartInProgress: boolean(),
|
|
48920
|
-
offSeconds: number().int()
|
|
48921
|
-
});
|
|
48922
|
-
object({
|
|
48923
|
-
/** Null while the switch has not reported: unknown is never `false` (D393). */
|
|
48924
|
-
switchOn: boolean().nullable(),
|
|
48925
|
-
offSeconds: number().int(),
|
|
48926
|
-
lastFetchedAt: number()
|
|
48927
|
-
});
|
|
48928
|
-
DeviceType.Gateway, DeviceType.Hub, DeviceType.Camera, DeviceType.Sensor, DeviceType.MediaPlayer, DeviceType.Vacuum, DeviceType.Generic, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.PetFountain, DeviceType.Fan, DeviceType.Humidifier, DeviceType.Climate, DeviceType.WaterHeater;
|
|
48929
|
-
var deviceInput$2 = object({ deviceId: number().int().nonnegative() });
|
|
48930
|
-
systemMethod(deviceInput$2, PowerSourceInfoSchema, { auth: "admin" }), systemMethod(deviceInput$2, PowerSourceActionResultSchema, {
|
|
48931
|
-
kind: "mutation",
|
|
48932
|
-
auth: "admin"
|
|
48933
|
-
}), systemMethod(deviceInput$2, PowerSourceActionResultSchema, {
|
|
48934
|
-
kind: "mutation",
|
|
48935
|
-
auth: "admin"
|
|
48936
|
-
}), systemMethod(deviceInput$2, PowerSourceActionResultSchema, {
|
|
48937
|
-
kind: "mutation",
|
|
48938
|
-
auth: "admin"
|
|
48939
|
-
});
|
|
48940
49044
|
DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
|
|
48941
49045
|
DeviceType.Camera;
|
|
48942
49046
|
`${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
|
|
@@ -48964,6 +49068,168 @@ var ComposableCapabilitiesSchema = object({ caps: array(object({
|
|
|
48964
49068
|
}).optional()
|
|
48965
49069
|
})) });
|
|
48966
49070
|
/**
|
|
49071
|
+
* A saved Customize assignment (D936). The schema is the type: callers use
|
|
49072
|
+
* `z.infer`, they do not restate the shape.
|
|
49073
|
+
*/
|
|
49074
|
+
var OverrideSourceSchema = object({
|
|
49075
|
+
addonId: string().min(1),
|
|
49076
|
+
stableId: string().min(1),
|
|
49077
|
+
cap: string().min(1),
|
|
49078
|
+
fieldPath: string().min(1)
|
|
49079
|
+
});
|
|
49080
|
+
var IdentityTransformDraftSchema = object({ kind: literal("identity") });
|
|
49081
|
+
var InvertTransformDraftSchema = object({ kind: literal("invert") });
|
|
49082
|
+
var ThresholdTransformDraftSchema = object({
|
|
49083
|
+
kind: literal("threshold"),
|
|
49084
|
+
comparison: _enum(["gte", "lte"]),
|
|
49085
|
+
threshold: number(),
|
|
49086
|
+
startDwellMs: number().nonnegative(),
|
|
49087
|
+
stopDwellMs: number().nonnegative()
|
|
49088
|
+
});
|
|
49089
|
+
var CustomizeTransformDraftSchema = discriminatedUnion("kind", [
|
|
49090
|
+
IdentityTransformDraftSchema,
|
|
49091
|
+
InvertTransformDraftSchema,
|
|
49092
|
+
ThresholdTransformDraftSchema,
|
|
49093
|
+
object({
|
|
49094
|
+
kind: literal("linear"),
|
|
49095
|
+
scale: number(),
|
|
49096
|
+
offset: number(),
|
|
49097
|
+
clampMin: number().optional(),
|
|
49098
|
+
clampMax: number().optional()
|
|
49099
|
+
}),
|
|
49100
|
+
object({
|
|
49101
|
+
kind: literal("enum-map"),
|
|
49102
|
+
mapping: record(string(), string().nullable())
|
|
49103
|
+
}),
|
|
49104
|
+
object({ kind: literal("number") })
|
|
49105
|
+
]);
|
|
49106
|
+
var ScalarOverrideDraftSchema = object({
|
|
49107
|
+
kind: literal("scalar"),
|
|
49108
|
+
cap: string().min(1),
|
|
49109
|
+
targetId: string().min(1),
|
|
49110
|
+
source: OverrideSourceSchema,
|
|
49111
|
+
transform: CustomizeTransformDraftSchema
|
|
49112
|
+
});
|
|
49113
|
+
var KeyedArrayOverrideDraftSchema = object({
|
|
49114
|
+
kind: literal("keyedArray"),
|
|
49115
|
+
cap: string().min(1),
|
|
49116
|
+
targetId: string().min(1),
|
|
49117
|
+
key: string().min(1),
|
|
49118
|
+
label: string().min(1),
|
|
49119
|
+
fieldPath: string().min(1),
|
|
49120
|
+
source: OverrideSourceSchema,
|
|
49121
|
+
transform: CustomizeTransformDraftSchema
|
|
49122
|
+
});
|
|
49123
|
+
var PulseOverrideDraftSchema = object({
|
|
49124
|
+
kind: literal("pulse"),
|
|
49125
|
+
cap: string().min(1),
|
|
49126
|
+
targetId: string().min(1),
|
|
49127
|
+
enabled: boolean(),
|
|
49128
|
+
debounceMs: number().nonnegative(),
|
|
49129
|
+
edges: array(_enum([
|
|
49130
|
+
"rising",
|
|
49131
|
+
"button",
|
|
49132
|
+
"native-event"
|
|
49133
|
+
])).min(1).optional(),
|
|
49134
|
+
sources: array(OverrideSourceSchema).min(1)
|
|
49135
|
+
});
|
|
49136
|
+
var SpanSourceDraftSchema = OverrideSourceSchema.extend({ mode: _enum([
|
|
49137
|
+
"in-addition",
|
|
49138
|
+
"confirm",
|
|
49139
|
+
"replace"
|
|
49140
|
+
]).optional() });
|
|
49141
|
+
discriminatedUnion("kind", [
|
|
49142
|
+
ScalarOverrideDraftSchema,
|
|
49143
|
+
KeyedArrayOverrideDraftSchema,
|
|
49144
|
+
PulseOverrideDraftSchema,
|
|
49145
|
+
object({
|
|
49146
|
+
kind: literal("span"),
|
|
49147
|
+
cap: string().min(1),
|
|
49148
|
+
targetId: string().min(1),
|
|
49149
|
+
timeoutMs: number().nonnegative(),
|
|
49150
|
+
cooldownMs: number().nonnegative().optional(),
|
|
49151
|
+
sources: array(SpanSourceDraftSchema)
|
|
49152
|
+
})
|
|
49153
|
+
]);
|
|
49154
|
+
object({
|
|
49155
|
+
targetId: string().min(1),
|
|
49156
|
+
configured: boolean(),
|
|
49157
|
+
source: _enum(["native", "assigned"]),
|
|
49158
|
+
state: _enum([
|
|
49159
|
+
"active",
|
|
49160
|
+
"stale",
|
|
49161
|
+
"released",
|
|
49162
|
+
"unavailable"
|
|
49163
|
+
]),
|
|
49164
|
+
reason: string().nullable()
|
|
49165
|
+
});
|
|
49166
|
+
/**
|
|
49167
|
+
* What a running-threshold proposal says (planning#405). The schema is the
|
|
49168
|
+
* type: the algorithm, the capability method and the UI all read `z.infer`.
|
|
49169
|
+
*
|
|
49170
|
+
* A proposal is read-only advice: it carries the numbers it was derived from
|
|
49171
|
+
* (`evidence`) so the operator can judge it, and an `insufficient` answer names
|
|
49172
|
+
* WHY with a typed reason and the catalog defaults, never an invented number.
|
|
49173
|
+
*/
|
|
49174
|
+
var RunningProposalTierSchema = _enum(["raw", "5m"]);
|
|
49175
|
+
var RunningProposalConfidenceSchema = _enum([
|
|
49176
|
+
"high",
|
|
49177
|
+
"medium",
|
|
49178
|
+
"low"
|
|
49179
|
+
]);
|
|
49180
|
+
var RunningProposalInsufficientReasonSchema = _enum([
|
|
49181
|
+
"no-history",
|
|
49182
|
+
"span-too-short",
|
|
49183
|
+
"truncated",
|
|
49184
|
+
"no-idle-floor",
|
|
49185
|
+
"no-runs",
|
|
49186
|
+
"no-separation"
|
|
49187
|
+
]);
|
|
49188
|
+
var RunningProposalReplaySchema = object({
|
|
49189
|
+
/** Intervals the real dwell expression held true over the window. */
|
|
49190
|
+
intervals: number().int().nonnegative(),
|
|
49191
|
+
/** Intervals outside every run, or a second interval inside one. */
|
|
49192
|
+
extra: number().int().nonnegative(),
|
|
49193
|
+
/** Runs without an interval of their own (none, or merged into a neighbour). */
|
|
49194
|
+
missed: number().int().nonnegative()
|
|
49195
|
+
});
|
|
49196
|
+
var RunningProposalEvidenceSchema = object({
|
|
49197
|
+
tier: RunningProposalTierSchema,
|
|
49198
|
+
spanMs: number().nonnegative(),
|
|
49199
|
+
from: number(),
|
|
49200
|
+
to: number(),
|
|
49201
|
+
points: number().int().nonnegative(),
|
|
49202
|
+
runsFound: number().int().nonnegative(),
|
|
49203
|
+
pausesFound: number().int().nonnegative(),
|
|
49204
|
+
longestPauseMs: number().nonnegative(),
|
|
49205
|
+
/** Episodes shorter than ~90 s (an anti-crease pulse, a display waking): never a run, never a pause. */
|
|
49206
|
+
pulsesFound: number().int().nonnegative(),
|
|
49207
|
+
longestPulseMs: number().nonnegative(),
|
|
49208
|
+
idleFloorW: number(),
|
|
49209
|
+
noiseCeilingW: number(),
|
|
49210
|
+
plateauW: number(),
|
|
49211
|
+
replay: RunningProposalReplaySchema
|
|
49212
|
+
});
|
|
49213
|
+
var RunningProposalInsufficientEvidenceSchema = object({
|
|
49214
|
+
spanMs: number().nonnegative(),
|
|
49215
|
+
points: number().int().nonnegative(),
|
|
49216
|
+
runsFound: number().int().nonnegative()
|
|
49217
|
+
});
|
|
49218
|
+
var RunningThresholdProposalSchema = discriminatedUnion("state", [object({
|
|
49219
|
+
state: literal("proposal"),
|
|
49220
|
+
transform: ThresholdTransformDraftSchema,
|
|
49221
|
+
evidence: RunningProposalEvidenceSchema,
|
|
49222
|
+
confidence: RunningProposalConfidenceSchema,
|
|
49223
|
+
/** Why the confidence is not higher; empty when nothing held it back. */
|
|
49224
|
+
notes: array(string())
|
|
49225
|
+
}), object({
|
|
49226
|
+
state: literal("insufficient"),
|
|
49227
|
+
reason: RunningProposalInsufficientReasonSchema,
|
|
49228
|
+
evidence: RunningProposalInsufficientEvidenceSchema,
|
|
49229
|
+
/** The catalog defaults, offered as what stands when nothing better is known. */
|
|
49230
|
+
fallback: ThresholdTransformDraftSchema
|
|
49231
|
+
})]);
|
|
49232
|
+
/**
|
|
48967
49233
|
* The report of a one-shot "move this owner's plaintext secrets into the
|
|
48968
49234
|
* keychain" run (D878). COUNTS only - never a value, never a ref.
|
|
48969
49235
|
*
|
|
@@ -49253,6 +49519,14 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
|
|
|
49253
49519
|
kind: "mutation",
|
|
49254
49520
|
auth: "admin",
|
|
49255
49521
|
access: "view"
|
|
49522
|
+
}), method(object({
|
|
49523
|
+
deviceId: number().int(),
|
|
49524
|
+
cap: string().min(1),
|
|
49525
|
+
fieldPath: string().min(1),
|
|
49526
|
+
deviceType: _enum(DeviceType).optional()
|
|
49527
|
+
}), RunningThresholdProposalSchema, {
|
|
49528
|
+
auth: "admin",
|
|
49529
|
+
access: "view"
|
|
49256
49530
|
}), method(object({
|
|
49257
49531
|
blockId: string().min(1),
|
|
49258
49532
|
trace: AutomationRunTraceSchema.optional(),
|
|
@@ -50061,7 +50335,27 @@ method(object({
|
|
|
50061
50335
|
* exposes only the COMMON surface: link state, supported device
|
|
50062
50336
|
* kinds (so the UI can filter the device picker), and per-device
|
|
50063
50337
|
* expose/unexpose.
|
|
50064
|
-
|
|
50338
|
+
*
|
|
50339
|
+
* INSTANCES. One export addon can serve several independent targets (a
|
|
50340
|
+
* HomeKit bridge, a Home Assistant broker). `listInstances` names them.
|
|
50341
|
+
* `instanceId` on a READ is optional: absent = the whole addon (linked if any
|
|
50342
|
+
* enabled instance is, `exposedDeviceCount` = distinct devices, `setup` = the
|
|
50343
|
+
* default instance's); present = that instance; an id the provider does not
|
|
50344
|
+
* serve is refused with `ExportInstanceUnknownError` (`export-instance-unknown`),
|
|
50345
|
+
* never answered empty. `instanceId` on a WRITE is REQUIRED: a membership
|
|
50346
|
+
* change always names the target it changes. A single-instance provider
|
|
50347
|
+
* accepts an absent id on a read or its own id, and refuses any other.
|
|
50348
|
+
*/
|
|
50349
|
+
/** Which instance of an export addon a call addresses. Absent on a read = the whole addon. */
|
|
50350
|
+
var InstanceIdSchema$1 = string().min(1);
|
|
50351
|
+
var InstancePinSchema = object({ instanceId: InstanceIdSchema$1.optional() });
|
|
50352
|
+
/** One independent export target of an addon. */
|
|
50353
|
+
var ExportInstanceSchema = object({
|
|
50354
|
+
instanceId: InstanceIdSchema$1,
|
|
50355
|
+
name: string(),
|
|
50356
|
+
enabled: boolean(),
|
|
50357
|
+
kind: string().optional()
|
|
50358
|
+
});
|
|
50065
50359
|
var LinkStateSchema = _enum([
|
|
50066
50360
|
"unlinked",
|
|
50067
50361
|
"linked",
|
|
@@ -50112,10 +50406,14 @@ var ExposedDeviceSchema = object({
|
|
|
50112
50406
|
capabilities: array(string()).optional()
|
|
50113
50407
|
});
|
|
50114
50408
|
var ExposeInputSchema = object({
|
|
50409
|
+
instanceId: InstanceIdSchema$1,
|
|
50115
50410
|
deviceId: string(),
|
|
50116
50411
|
capabilities: array(string()).optional()
|
|
50117
50412
|
});
|
|
50118
|
-
var UnexposeInputSchema = object({
|
|
50413
|
+
var UnexposeInputSchema = object({
|
|
50414
|
+
instanceId: InstanceIdSchema$1,
|
|
50415
|
+
deviceId: string()
|
|
50416
|
+
});
|
|
50119
50417
|
/**
|
|
50120
50418
|
* What an exporter may expose besides devices (D968). An action and a
|
|
50121
50419
|
* notification target are LIST ENTRIES, never devices (D954): they enter an
|
|
@@ -50144,15 +50442,21 @@ var ExposedEntitySchema = object({
|
|
|
50144
50442
|
entity: ExportEntityRefSchema,
|
|
50145
50443
|
exposedAs: string().optional()
|
|
50146
50444
|
});
|
|
50147
|
-
var ExposeEntityInputSchema = object({
|
|
50148
|
-
|
|
50149
|
-
|
|
50445
|
+
var ExposeEntityInputSchema = object({
|
|
50446
|
+
instanceId: InstanceIdSchema$1,
|
|
50447
|
+
entity: ExportEntityRefSchema
|
|
50448
|
+
});
|
|
50449
|
+
var UnexposeEntityInputSchema = object({
|
|
50450
|
+
instanceId: InstanceIdSchema$1,
|
|
50451
|
+
entity: ExportEntityRefSchema
|
|
50452
|
+
});
|
|
50453
|
+
method(InstancePinSchema, DeviceExportStatusSchema), method(_void(), array(ExportInstanceSchema)), method(InstancePinSchema, array(DeviceKindSchema)), method(InstancePinSchema, array(ExposedDeviceSchema)), method(ExposeInputSchema, _void(), {
|
|
50150
50454
|
kind: "mutation",
|
|
50151
50455
|
auth: "admin"
|
|
50152
50456
|
}), method(UnexposeInputSchema, _void(), {
|
|
50153
50457
|
kind: "mutation",
|
|
50154
50458
|
auth: "admin"
|
|
50155
|
-
}), optionalMethod(_void(), array(ExportEntityKindSchema)), optionalMethod(
|
|
50459
|
+
}), optionalMethod(_void(), array(ExportEntityKindSchema)), optionalMethod(InstancePinSchema, array(ExposedEntitySchema)), optionalMethod(ExposeEntityInputSchema, _void(), {
|
|
50156
50460
|
kind: "mutation",
|
|
50157
50461
|
auth: "admin"
|
|
50158
50462
|
}), optionalMethod(UnexposeEntityInputSchema, _void(), {
|
|
@@ -53353,15 +53657,15 @@ var FanPresetCaptureResultSchema = object({
|
|
|
53353
53657
|
items: array(FanCaptureItemSchema),
|
|
53354
53658
|
omitted: array(FanCaptureOmissionSchema)
|
|
53355
53659
|
});
|
|
53356
|
-
var deviceInput$
|
|
53660
|
+
var deviceInput$2 = object({ deviceId: number().int().nonnegative() });
|
|
53357
53661
|
DeviceType.Fan;
|
|
53358
|
-
method(deviceInput$
|
|
53662
|
+
method(deviceInput$2, array(FanPresetSchema), { auth: "protected" }), method(deviceInput$2.extend({ presets: array(FanPresetSchema).max(12) }), FanPresetsSetResultSchema, {
|
|
53359
53663
|
kind: "mutation",
|
|
53360
53664
|
auth: "admin"
|
|
53361
|
-
}), method(deviceInput$
|
|
53665
|
+
}), method(deviceInput$2.extend({ presetId: string().min(1) }), FanPresetApplyResultSchema, {
|
|
53362
53666
|
kind: "mutation",
|
|
53363
53667
|
auth: "protected"
|
|
53364
|
-
}), method(deviceInput$
|
|
53668
|
+
}), method(deviceInput$2.extend({
|
|
53365
53669
|
fields: array(FanCaptureFieldSchema).optional(),
|
|
53366
53670
|
steps: array(ActionStepSchema).max(20).default([])
|
|
53367
53671
|
}), FanPresetCaptureResultSchema, { auth: "protected" });
|
|
@@ -53822,11 +54126,7 @@ var CreateIntegrationInputSchema = object({
|
|
|
53822
54126
|
info: record(string(), unknown()).optional(),
|
|
53823
54127
|
/** `stableId` of the discovery candidate the form was seeded from (D984). */
|
|
53824
54128
|
originStableId: string().optional(),
|
|
53825
|
-
settings: record(string(), unknown()).optional()
|
|
53826
|
-
/** Keys of `settings` the form's schema declares secret (by type) — kept in
|
|
53827
|
-
* the keychain, not the table (D787). */
|
|
53828
|
-
secretKeys: array(string()).optional(),
|
|
53829
|
-
skipRestart: boolean().optional()
|
|
54129
|
+
settings: record(string(), unknown()).optional()
|
|
53830
54130
|
});
|
|
53831
54131
|
var UpdateIntegrationInputSchema = object({
|
|
53832
54132
|
id: string(),
|
|
@@ -53847,16 +54147,6 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
|
|
|
53847
54147
|
}), method(object({ id: string() }), DeleteIntegrationResultSchema, {
|
|
53848
54148
|
kind: "mutation",
|
|
53849
54149
|
auth: "admin"
|
|
53850
|
-
}), method(object({ id: string() }), record(string(), unknown()), { auth: "admin" }), method(object({
|
|
53851
|
-
id: string(),
|
|
53852
|
-
settings: record(string(), unknown()),
|
|
53853
|
-
/** Keys the form's schema declares secret (by type) — keychain-stored (D787). */
|
|
53854
|
-
secretKeys: array(string()).optional(),
|
|
53855
|
-
/** The integration's own addon applies the change in-process and asks the hub not to respawn it. */
|
|
53856
|
-
skipRestart: boolean().optional()
|
|
53857
|
-
}), object({ success: boolean() }), {
|
|
53858
|
-
kind: "mutation",
|
|
53859
|
-
auth: "admin"
|
|
53860
54150
|
}), method(_void(), array(AvailableIntegrationTypeSchema), { auth: "admin" }), method(object({
|
|
53861
54151
|
addonId: string(),
|
|
53862
54152
|
settings: record(string(), unknown())
|
|
@@ -53885,13 +54175,65 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
|
|
|
53885
54175
|
*/
|
|
53886
54176
|
var ConfigSchemaPassthrough$3 = custom();
|
|
53887
54177
|
var IntegrationInstanceIdSchema = string().min(1);
|
|
54178
|
+
var IntegrationInstanceModeSchema = _enum(["single", "multiple"]);
|
|
54179
|
+
/**
|
|
54180
|
+
* One kind of instance a provider can create (an adapter, an account type). The sign-in and
|
|
54181
|
+
* connect fields follow the `broker` contract (`broker.cap.ts`): `signIn.basePath` is a
|
|
54182
|
+
* hub-relative route set speaking the sign-in contract, `connectPath` / `disconnectPath` open an
|
|
54183
|
+
* interactive account link (popup) and may carry `{{brokerId}}`, which the UI replaces with the
|
|
54184
|
+
* instance id.
|
|
54185
|
+
*/
|
|
54186
|
+
var IntegrationInstanceKindSchema = object({
|
|
54187
|
+
/** Stable id of the kind, sent back as `createInstance.kind`. */
|
|
54188
|
+
id: string().min(1),
|
|
54189
|
+
label: string().min(1),
|
|
54190
|
+
/** The kind signs in through a server-held interactive session. */
|
|
54191
|
+
signIn: object({
|
|
54192
|
+
basePath: string().min(1),
|
|
54193
|
+
methods: array(_enum(["device-code", "proxy"])).optional()
|
|
54194
|
+
}).optional(),
|
|
54195
|
+
/** Relative hub path to start an interactive account link of an instance of this kind. */
|
|
54196
|
+
connectPath: string().min(1).optional(),
|
|
54197
|
+
/** Relative hub path to disconnect that account. */
|
|
54198
|
+
disconnectPath: string().min(1).optional()
|
|
54199
|
+
});
|
|
53888
54200
|
var IntegrationInstanceDescriptionSchema = object({
|
|
54201
|
+
/** What the instances are for the Integrations page; see {@link IntegrationInstanceRoleSchema}. */
|
|
54202
|
+
role: _enum([
|
|
54203
|
+
"devices",
|
|
54204
|
+
"services",
|
|
54205
|
+
"both"
|
|
54206
|
+
]),
|
|
54207
|
+
/** How the devices of an instance are found; absent means `{ mode: 'none' }`. */
|
|
54208
|
+
discovery: object({
|
|
54209
|
+
mode: _enum([
|
|
54210
|
+
"none",
|
|
54211
|
+
"lan",
|
|
54212
|
+
"account"
|
|
54213
|
+
]),
|
|
54214
|
+
scopedToInstance: boolean()
|
|
54215
|
+
}).optional(),
|
|
53889
54216
|
/** `single`: at most one instance can exist (Loki). `multiple`: an account each. */
|
|
53890
|
-
instanceMode:
|
|
54217
|
+
instanceMode: IntegrationInstanceModeSchema,
|
|
53891
54218
|
/** What the instance is, for the UI (`destination`, `relay`, `account`). */
|
|
53892
54219
|
kind: string(),
|
|
53893
54220
|
/** True when `testInstance` really probes the remote. */
|
|
53894
|
-
canTest: boolean()
|
|
54221
|
+
canTest: boolean(),
|
|
54222
|
+
/**
|
|
54223
|
+
* The kinds an instance can be of. Absent or one entry: nothing to choose; more than one: the
|
|
54224
|
+
* Add flow asks which first and sends it as `createInstance.kind`.
|
|
54225
|
+
*/
|
|
54226
|
+
kinds: array(IntegrationInstanceKindSchema).optional(),
|
|
54227
|
+
/**
|
|
54228
|
+
* The provider accepts `createInstance.id` for an id that once existed (an adopted id, because
|
|
54229
|
+
* devices carry it). The UI then offers "restore under its old id" on Add. Absent means no.
|
|
54230
|
+
*/
|
|
54231
|
+
allowRestoreId: boolean().optional(),
|
|
54232
|
+
/**
|
|
54233
|
+
* `createInstance` without an `id` works: the provider mints it. Absent means the caller must
|
|
54234
|
+
* send an id (the UI then mints an opaque one).
|
|
54235
|
+
*/
|
|
54236
|
+
mintsId: boolean().optional()
|
|
53895
54237
|
});
|
|
53896
54238
|
var IntegrationInstanceStatusSchema = object({
|
|
53897
54239
|
state: _enum([
|
|
@@ -53900,7 +54242,9 @@ var IntegrationInstanceStatusSchema = object({
|
|
|
53900
54242
|
"error",
|
|
53901
54243
|
"unknown"
|
|
53902
54244
|
]),
|
|
53903
|
-
detail: string().optional()
|
|
54245
|
+
detail: string().optional(),
|
|
54246
|
+
/** A short human word for the state ("Signed in", "Token expired"); shown instead of `state`. */
|
|
54247
|
+
label: string().optional()
|
|
53904
54248
|
});
|
|
53905
54249
|
var IntegrationInstanceSchema = object({
|
|
53906
54250
|
id: IntegrationInstanceIdSchema,
|
|
@@ -53908,13 +54252,18 @@ var IntegrationInstanceSchema = object({
|
|
|
53908
54252
|
/** The addon's own rule (an URL is present, a client id is present...). */
|
|
53909
54253
|
configured: boolean(),
|
|
53910
54254
|
/**
|
|
53911
|
-
* The instance's own on/off switch (D996)
|
|
53912
|
-
*
|
|
54255
|
+
* The instance's own on/off switch (D996), derived from the record body (`enabled !== false`,
|
|
54256
|
+
* so a row written before the field existed is on). Absent when the `integrations` registry
|
|
54257
|
+
* owns the switch (`describe().role` is `devices`): read it from the registry row.
|
|
53913
54258
|
*/
|
|
53914
|
-
enabled: boolean(),
|
|
54259
|
+
enabled: boolean().optional(),
|
|
54260
|
+
/** The kind of this instance (one of `describe().kinds[].id`), when the provider has kinds. */
|
|
54261
|
+
kind: string().optional(),
|
|
53915
54262
|
/** Values for the instance form; a secret is a `SecretFieldView`, never text. */
|
|
53916
54263
|
formValues: record(string(), unknown()),
|
|
53917
|
-
status: IntegrationInstanceStatusSchema.optional()
|
|
54264
|
+
status: IntegrationInstanceStatusSchema.optional(),
|
|
54265
|
+
/** Epoch ms of the last probe of the remote that produced `status`; absent when never checked. */
|
|
54266
|
+
lastCheckedAt: number().optional()
|
|
53918
54267
|
});
|
|
53919
54268
|
var IntegrationInstancePatchSchema = object({
|
|
53920
54269
|
id: IntegrationInstanceIdSchema,
|
|
@@ -53932,10 +54281,14 @@ var integrationInstancesCapability = {
|
|
|
53932
54281
|
instanceFormSchema: method(object({ id: IntegrationInstanceIdSchema.nullable() }), ConfigSchemaPassthrough$3, { auth: "admin" }),
|
|
53933
54282
|
listInstances: method(_void(), array(IntegrationInstanceSchema).readonly(), { auth: "admin" }),
|
|
53934
54283
|
getInstance: method(object({ id: IntegrationInstanceIdSchema }), IntegrationInstanceSchema.nullable(), { auth: "admin" }),
|
|
53935
|
-
/**
|
|
54284
|
+
/**
|
|
54285
|
+
* Insert the typed record. With `id` it is stored under that id (a duplicate id is refused);
|
|
54286
|
+
* without one the PROVIDER mints it and answers it. `kind` picks one of `describe().kinds`.
|
|
54287
|
+
*/
|
|
53936
54288
|
createInstance: method(object({
|
|
53937
|
-
id: IntegrationInstanceIdSchema,
|
|
53938
|
-
name: string().min(1)
|
|
54289
|
+
id: IntegrationInstanceIdSchema.optional(),
|
|
54290
|
+
name: string().min(1),
|
|
54291
|
+
kind: string().min(1).optional()
|
|
53939
54292
|
}), object({ id: string() }), {
|
|
53940
54293
|
kind: "mutation",
|
|
53941
54294
|
auth: "admin"
|
|
@@ -55767,6 +56120,11 @@ var LogEntrySchema = object({
|
|
|
55767
56120
|
* flight, and not asking for the next slice ends the scan.
|
|
55768
56121
|
*/
|
|
55769
56122
|
var LogReadQuerySchema = object({
|
|
56123
|
+
/**
|
|
56124
|
+
* Which destination INSTANCE answers (a destination addon may hold several records). Absent ⇒
|
|
56125
|
+
* the provider's first readable one, so a caller written before instances existed is unchanged.
|
|
56126
|
+
*/
|
|
56127
|
+
instanceId: string().min(1).max(200).optional(),
|
|
55770
56128
|
/** Correlates `cancelRead`. Caller-chosen, unique per in-flight read. */
|
|
55771
56129
|
requestId: string().min(1).max(64).optional(),
|
|
55772
56130
|
/** The device the line is about (`tags.deviceId`, numeric). */
|
|
@@ -55829,6 +56187,8 @@ var LogReadResultSchema = object({
|
|
|
55829
56187
|
/** True when `cancelRead` ended this slice early. */
|
|
55830
56188
|
cancelled: boolean().optional()
|
|
55831
56189
|
});
|
|
56190
|
+
/** Names one destination instance; absent ⇒ the provider's first readable one. */
|
|
56191
|
+
var LogInstanceSelectorSchema = object({ instanceId: string().min(1).max(200).optional() });
|
|
55832
56192
|
var LogReadCapabilitiesSchema = object({
|
|
55833
56193
|
/** False ⇒ this destination only ships lines (console, forwarder, an unconfigured Loki). */
|
|
55834
56194
|
readable: boolean(),
|
|
@@ -55847,7 +56207,7 @@ var LogReadCapabilitiesSchema = object({
|
|
|
55847
56207
|
/** How far back the store reaches, when the provider knows (days). */
|
|
55848
56208
|
retentionDays: number().nullable()
|
|
55849
56209
|
});
|
|
55850
|
-
method(LogEntrySchema, _void(), { kind: "mutation" }), optionalMethod(
|
|
56210
|
+
method(LogEntrySchema, _void(), { kind: "mutation" }), optionalMethod(LogInstanceSelectorSchema, LogReadCapabilitiesSchema, { auth: "admin" }), optionalMethod(LogReadQuerySchema, LogReadResultSchema, { auth: "admin" }), optionalMethod(object({ requestId: string().min(1).max(64) }), object({ cancelled: boolean() }), {
|
|
55851
56211
|
kind: "mutation",
|
|
55852
56212
|
auth: "admin"
|
|
55853
56213
|
});
|
|
@@ -56511,6 +56871,27 @@ method(object({}), object({ entries: array(MigrationLedgerEntrySchema) }), { aut
|
|
|
56511
56871
|
}), {
|
|
56512
56872
|
kind: "mutation",
|
|
56513
56873
|
auth: "admin"
|
|
56874
|
+
}), method(object({ integrationId: string().min(1) }), record(string(), unknown()), { auth: "admin" }), method(object({ integrationId: string().min(1) }), object({
|
|
56875
|
+
rows: number().int().nonnegative(),
|
|
56876
|
+
secrets: number().int().nonnegative()
|
|
56877
|
+
}), {
|
|
56878
|
+
kind: "mutation",
|
|
56879
|
+
auth: "admin"
|
|
56880
|
+
}), method(object({
|
|
56881
|
+
integrationId: string().min(1),
|
|
56882
|
+
patch: record(string(), unknown())
|
|
56883
|
+
}), object({ written: boolean() }), {
|
|
56884
|
+
kind: "mutation",
|
|
56885
|
+
auth: "admin"
|
|
56886
|
+
}), method(object({
|
|
56887
|
+
integrationId: string().min(1),
|
|
56888
|
+
keys: array(string().min(1))
|
|
56889
|
+
}), object({
|
|
56890
|
+
present: boolean(),
|
|
56891
|
+
removed: record(string(), unknown())
|
|
56892
|
+
}), {
|
|
56893
|
+
kind: "mutation",
|
|
56894
|
+
auth: "admin"
|
|
56514
56895
|
});
|
|
56515
56896
|
method(object({
|
|
56516
56897
|
sourceUrl: string(),
|
|
@@ -67438,6 +67819,49 @@ object({
|
|
|
67438
67819
|
DeviceType.PetFeeder,
|
|
67439
67820
|
DeviceType.Heater
|
|
67440
67821
|
].filter((type) => type !== DeviceType.Sensor);
|
|
67822
|
+
var PowerSourceRefusalReasonSchema = _enum([
|
|
67823
|
+
"no-power-source",
|
|
67824
|
+
"switch-missing",
|
|
67825
|
+
"switch-offline",
|
|
67826
|
+
"switch-unknown",
|
|
67827
|
+
"restart-in-progress",
|
|
67828
|
+
"switch-call-failed"
|
|
67829
|
+
]);
|
|
67830
|
+
var PowerSourceActionResultSchema = discriminatedUnion("accepted", [object({ accepted: literal(true) }), object({
|
|
67831
|
+
accepted: literal(false),
|
|
67832
|
+
reason: PowerSourceRefusalReasonSchema,
|
|
67833
|
+
/** One line for the operator, naming the switch. */
|
|
67834
|
+
message: string()
|
|
67835
|
+
})]);
|
|
67836
|
+
var PowerSourceInfoSchema = object({
|
|
67837
|
+
/** False when the device has no power source linked; the rest is then empty. */
|
|
67838
|
+
linked: boolean(),
|
|
67839
|
+
switchDeviceId: number().nullable(),
|
|
67840
|
+
switchName: string().nullable(),
|
|
67841
|
+
/** The switch's on/off state, or null when it is unknown. Never false for unknown (D393). */
|
|
67842
|
+
switchOn: boolean().nullable(),
|
|
67843
|
+
switchOnline: boolean().nullable(),
|
|
67844
|
+
restartInProgress: boolean(),
|
|
67845
|
+
offSeconds: number().int()
|
|
67846
|
+
});
|
|
67847
|
+
object({
|
|
67848
|
+
/** Null while the switch has not reported: unknown is never `false` (D393). */
|
|
67849
|
+
switchOn: boolean().nullable(),
|
|
67850
|
+
offSeconds: number().int(),
|
|
67851
|
+
lastFetchedAt: number()
|
|
67852
|
+
});
|
|
67853
|
+
DeviceType.Gateway, DeviceType.Hub, DeviceType.Camera, DeviceType.Sensor, DeviceType.MediaPlayer, DeviceType.Vacuum, DeviceType.Generic, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.PetFountain, DeviceType.Fan, DeviceType.Humidifier, DeviceType.Climate, DeviceType.WaterHeater;
|
|
67854
|
+
var deviceInput$1 = object({ deviceId: number().int().nonnegative() });
|
|
67855
|
+
systemMethod(deviceInput$1, PowerSourceInfoSchema, { auth: "admin" }), systemMethod(deviceInput$1, PowerSourceActionResultSchema, {
|
|
67856
|
+
kind: "mutation",
|
|
67857
|
+
auth: "admin"
|
|
67858
|
+
}), systemMethod(deviceInput$1, PowerSourceActionResultSchema, {
|
|
67859
|
+
kind: "mutation",
|
|
67860
|
+
auth: "admin"
|
|
67861
|
+
}), systemMethod(deviceInput$1, PowerSourceActionResultSchema, {
|
|
67862
|
+
kind: "mutation",
|
|
67863
|
+
auth: "admin"
|
|
67864
|
+
});
|
|
67441
67865
|
/**
|
|
67442
67866
|
* Presence cap. Models HA `person.*` and `device_tracker.*` entities
|
|
67443
67867
|
* on `DeviceType.Presence`. Read-only — no setters: presence is
|
|
@@ -75491,6 +75915,25 @@ function buildDescriptor(capName, kind) {
|
|
|
75491
75915
|
* contributes one entry per bound cap present in this map.
|
|
75492
75916
|
*/
|
|
75493
75917
|
var EVENT_KIND_BY_CAP = Object.freeze(Object.fromEntries(Object.entries(CAP_TO_KIND).map(([capName, kind]) => [capName, buildDescriptor(capName, kind)])));
|
|
75918
|
+
/**
|
|
75919
|
+
* What a remote key is CALLED and what it LOOKS like, for every role (D963),
|
|
75920
|
+
* shared by the admin UI and the viewer. A role is data on the wire; each
|
|
75921
|
+
* client resolves its name from `label` (English and Italian side by side) and
|
|
75922
|
+
* its glyph from `icon`, a lucide id from the shared icon catalogue — the
|
|
75923
|
+
* server sends an id, a client maps it to its own icon set.
|
|
75924
|
+
*
|
|
75925
|
+
* `legend` is text printed on the key INSTEAD of a glyph (a digit has no
|
|
75926
|
+
* pictogram): a client that can draw text shows it, one that cannot falls back
|
|
75927
|
+
* to `icon`.
|
|
75928
|
+
*/
|
|
75929
|
+
function digit(n) {
|
|
75930
|
+
return {
|
|
75931
|
+
label: t(n, n),
|
|
75932
|
+
icon: "hash",
|
|
75933
|
+
legend: n
|
|
75934
|
+
};
|
|
75935
|
+
}
|
|
75936
|
+
digit("0"), digit("1"), digit("2"), digit("3"), digit("4"), digit("5"), digit("6"), digit("7"), digit("8"), digit("9");
|
|
75494
75937
|
DeviceFeature.MediaPlayerPower, DeviceFeature.MediaPlayerVolumeStep, DeviceFeature.MediaPlayerChannelStep, DeviceFeature.MediaPlayerMute, DeviceFeature.MediaPlayerStop, DeviceFeature.MediaPlayerNext, DeviceFeature.MediaPlayerPrevious;
|
|
75495
75938
|
var btn = (idSuffix, row, col, init) => ({
|
|
75496
75939
|
idSuffix,
|
|
@@ -75618,6 +76061,42 @@ discriminatedUnion("kind", [object({
|
|
|
75618
76061
|
kind: literal("pronto"),
|
|
75619
76062
|
code: string()
|
|
75620
76063
|
})]);
|
|
76064
|
+
/**
|
|
76065
|
+
* Propose a running threshold from a device's own power history (planning#405).
|
|
76066
|
+
*
|
|
76067
|
+
* Pure and deterministic: the same series gives the same answer. The series is
|
|
76068
|
+
* a HELD-VALUE step series — a point's value stands until the next point — which
|
|
76069
|
+
* is how device-history stores a gauge, so "time at a level" is exact, not a
|
|
76070
|
+
* count of samples.
|
|
76071
|
+
*
|
|
76072
|
+
* 1. idle floor F = time-weighted median; noise ceiling N = highest value seen
|
|
76073
|
+
* near F. A device that is not near one level most of the time has no floor.
|
|
76074
|
+
* 2. coarse activity = above max(2N, N + 5 W); consecutive stretches are episodes.
|
|
76075
|
+
* 3. an episode shorter than `PULSE_MAX_MS` is a PULSE (an anti-crease tumble, a
|
|
76076
|
+
* display waking): it is neither a run nor a pause. Among the remaining
|
|
76077
|
+
* episodes, gaps over `PAUSE_MAX_MS` always separate runs; the rest split in
|
|
76078
|
+
* two by the largest log-scale jump (>= 3x): the short ones are pauses INSIDE
|
|
76079
|
+
* a run (a soak), the long ones separate runs. A stretch under `MIN_RUN_MS`
|
|
76080
|
+
* is no run.
|
|
76081
|
+
* 4. T = geometric mean of N and the plateau's 10th percentile, snapped to a
|
|
76082
|
+
* grid a person would type; stop dwell covers the longest pause and the
|
|
76083
|
+
* kind's floor; start dwell covers the longest pulse and standby blip above T.
|
|
76084
|
+
* 5. the numbers are proved by REPLAY through the real `dwell` evaluation: each
|
|
76085
|
+
* run must hold exactly one true interval and nothing may flap outside.
|
|
76086
|
+
*/
|
|
76087
|
+
var SECOND_MS = 1e3;
|
|
76088
|
+
var MINUTE_MS$1 = 60 * SECOND_MS;
|
|
76089
|
+
24 * (60 * MINUTE_MS$1);
|
|
76090
|
+
10 * MINUTE_MS$1;
|
|
76091
|
+
30 * SECOND_MS;
|
|
76092
|
+
60 * SECOND_MS;
|
|
76093
|
+
20 * MINUTE_MS$1;
|
|
76094
|
+
90 * SECOND_MS;
|
|
76095
|
+
30 * MINUTE_MS$1;
|
|
76096
|
+
10 * MINUTE_MS$1;
|
|
76097
|
+
DeviceType.Dryer, 3 * MINUTE_MS$1, DeviceType.Dishwasher, 3 * MINUTE_MS$1, DeviceType.Washer, 10 * MINUTE_MS$1;
|
|
76098
|
+
10 * SECOND_MS;
|
|
76099
|
+
10 * SECOND_MS;
|
|
75621
76100
|
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverStoppable, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
75622
76101
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
75623
76102
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
@@ -77159,6 +77638,13 @@ Object.freeze({
|
|
|
77159
77638
|
access: "create",
|
|
77160
77639
|
role: "admin"
|
|
77161
77640
|
},
|
|
77641
|
+
"coreBlocks.proposeRunningThreshold": {
|
|
77642
|
+
capName: "core-blocks",
|
|
77643
|
+
capScope: "system",
|
|
77644
|
+
addonId: null,
|
|
77645
|
+
access: "view",
|
|
77646
|
+
role: "admin"
|
|
77647
|
+
},
|
|
77162
77648
|
"coreBlocks.reportAutomationRun": {
|
|
77163
77649
|
capName: "core-blocks",
|
|
77164
77650
|
capScope: "system",
|
|
@@ -77670,6 +78156,13 @@ Object.freeze({
|
|
|
77670
78156
|
access: "view",
|
|
77671
78157
|
role: "admin"
|
|
77672
78158
|
},
|
|
78159
|
+
"deviceExport.listInstances": {
|
|
78160
|
+
capName: "device-export",
|
|
78161
|
+
capScope: "system",
|
|
78162
|
+
addonId: null,
|
|
78163
|
+
access: "view",
|
|
78164
|
+
role: "admin"
|
|
78165
|
+
},
|
|
77673
78166
|
"deviceExport.listSupportedDeviceKinds": {
|
|
77674
78167
|
capName: "device-export",
|
|
77675
78168
|
capScope: "system",
|
|
@@ -79357,13 +79850,6 @@ Object.freeze({
|
|
|
79357
79850
|
access: "view",
|
|
79358
79851
|
role: "admin"
|
|
79359
79852
|
},
|
|
79360
|
-
"integrations.getSettings": {
|
|
79361
|
-
capName: "integrations",
|
|
79362
|
-
capScope: "system",
|
|
79363
|
-
addonId: null,
|
|
79364
|
-
access: "view",
|
|
79365
|
-
role: "admin"
|
|
79366
|
-
},
|
|
79367
79853
|
"integrations.list": {
|
|
79368
79854
|
capName: "integrations",
|
|
79369
79855
|
capScope: "system",
|
|
@@ -79371,13 +79857,6 @@ Object.freeze({
|
|
|
79371
79857
|
access: "view",
|
|
79372
79858
|
role: "admin"
|
|
79373
79859
|
},
|
|
79374
|
-
"integrations.setSettings": {
|
|
79375
|
-
capName: "integrations",
|
|
79376
|
-
capScope: "system",
|
|
79377
|
-
addonId: null,
|
|
79378
|
-
access: "create",
|
|
79379
|
-
role: "admin"
|
|
79380
|
-
},
|
|
79381
79860
|
"integrations.testConnection": {
|
|
79382
79861
|
capName: "integrations",
|
|
79383
79862
|
capScope: "system",
|
|
@@ -80372,6 +80851,13 @@ Object.freeze({
|
|
|
80372
80851
|
access: "view",
|
|
80373
80852
|
role: "admin"
|
|
80374
80853
|
},
|
|
80854
|
+
"migrations.clearLegacyIntegrationRows": {
|
|
80855
|
+
capName: "migrations",
|
|
80856
|
+
capScope: "system",
|
|
80857
|
+
addonId: null,
|
|
80858
|
+
access: "delete",
|
|
80859
|
+
role: "admin"
|
|
80860
|
+
},
|
|
80375
80861
|
"migrations.declareOwner": {
|
|
80376
80862
|
capName: "migrations",
|
|
80377
80863
|
capScope: "system",
|
|
@@ -80379,6 +80865,13 @@ Object.freeze({
|
|
|
80379
80865
|
access: "create",
|
|
80380
80866
|
role: "admin"
|
|
80381
80867
|
},
|
|
80868
|
+
"migrations.deleteLegacyIntegrationRowKeys": {
|
|
80869
|
+
capName: "migrations",
|
|
80870
|
+
capScope: "system",
|
|
80871
|
+
addonId: null,
|
|
80872
|
+
access: "delete",
|
|
80873
|
+
role: "admin"
|
|
80874
|
+
},
|
|
80382
80875
|
"migrations.dryRun": {
|
|
80383
80876
|
capName: "migrations",
|
|
80384
80877
|
capScope: "system",
|
|
@@ -80414,6 +80907,20 @@ Object.freeze({
|
|
|
80414
80907
|
access: "create",
|
|
80415
80908
|
role: "admin"
|
|
80416
80909
|
},
|
|
80910
|
+
"migrations.patchLegacyIntegrationRows": {
|
|
80911
|
+
capName: "migrations",
|
|
80912
|
+
capScope: "system",
|
|
80913
|
+
addonId: null,
|
|
80914
|
+
access: "create",
|
|
80915
|
+
role: "admin"
|
|
80916
|
+
},
|
|
80917
|
+
"migrations.readLegacyIntegrationRows": {
|
|
80918
|
+
capName: "migrations",
|
|
80919
|
+
capScope: "system",
|
|
80920
|
+
addonId: null,
|
|
80921
|
+
access: "view",
|
|
80922
|
+
role: "admin"
|
|
80923
|
+
},
|
|
80417
80924
|
"migrations.restorePreMigration": {
|
|
80418
80925
|
capName: "migrations",
|
|
80419
80926
|
capScope: "system",
|
|
@@ -86617,6 +87124,11 @@ Object.freeze({
|
|
|
86617
87124
|
form: "single",
|
|
86618
87125
|
optional: false
|
|
86619
87126
|
}],
|
|
87127
|
+
"coreBlocks.proposeRunningThreshold": [{
|
|
87128
|
+
name: "deviceId",
|
|
87129
|
+
form: "single",
|
|
87130
|
+
optional: false
|
|
87131
|
+
}],
|
|
86620
87132
|
"cover.close": [{
|
|
86621
87133
|
name: "deviceId",
|
|
86622
87134
|
form: "single",
|
|
@@ -89280,99 +89792,6 @@ Object.freeze({
|
|
|
89280
89792
|
"network-access": "ingress",
|
|
89281
89793
|
"smtp-provider": "email"
|
|
89282
89794
|
});
|
|
89283
|
-
/**
|
|
89284
|
-
* A saved Customize assignment (D936). The schema is the type: callers use
|
|
89285
|
-
* `z.infer`, they do not restate the shape.
|
|
89286
|
-
*/
|
|
89287
|
-
var OverrideSourceSchema = object({
|
|
89288
|
-
addonId: string().min(1),
|
|
89289
|
-
stableId: string().min(1),
|
|
89290
|
-
cap: string().min(1),
|
|
89291
|
-
fieldPath: string().min(1)
|
|
89292
|
-
});
|
|
89293
|
-
var CustomizeTransformDraftSchema = discriminatedUnion("kind", [
|
|
89294
|
-
object({ kind: literal("identity") }),
|
|
89295
|
-
object({ kind: literal("invert") }),
|
|
89296
|
-
object({
|
|
89297
|
-
kind: literal("threshold"),
|
|
89298
|
-
comparison: _enum(["gte", "lte"]),
|
|
89299
|
-
threshold: number(),
|
|
89300
|
-
startDwellMs: number().nonnegative(),
|
|
89301
|
-
stopDwellMs: number().nonnegative()
|
|
89302
|
-
}),
|
|
89303
|
-
object({
|
|
89304
|
-
kind: literal("linear"),
|
|
89305
|
-
scale: number(),
|
|
89306
|
-
offset: number(),
|
|
89307
|
-
clampMin: number().optional(),
|
|
89308
|
-
clampMax: number().optional()
|
|
89309
|
-
}),
|
|
89310
|
-
object({
|
|
89311
|
-
kind: literal("enum-map"),
|
|
89312
|
-
mapping: record(string(), string().nullable())
|
|
89313
|
-
}),
|
|
89314
|
-
object({ kind: literal("number") })
|
|
89315
|
-
]);
|
|
89316
|
-
var ScalarOverrideDraftSchema = object({
|
|
89317
|
-
kind: literal("scalar"),
|
|
89318
|
-
cap: string().min(1),
|
|
89319
|
-
targetId: string().min(1),
|
|
89320
|
-
source: OverrideSourceSchema,
|
|
89321
|
-
transform: CustomizeTransformDraftSchema
|
|
89322
|
-
});
|
|
89323
|
-
var KeyedArrayOverrideDraftSchema = object({
|
|
89324
|
-
kind: literal("keyedArray"),
|
|
89325
|
-
cap: string().min(1),
|
|
89326
|
-
targetId: string().min(1),
|
|
89327
|
-
key: string().min(1),
|
|
89328
|
-
label: string().min(1),
|
|
89329
|
-
fieldPath: string().min(1),
|
|
89330
|
-
source: OverrideSourceSchema,
|
|
89331
|
-
transform: CustomizeTransformDraftSchema
|
|
89332
|
-
});
|
|
89333
|
-
var PulseOverrideDraftSchema = object({
|
|
89334
|
-
kind: literal("pulse"),
|
|
89335
|
-
cap: string().min(1),
|
|
89336
|
-
targetId: string().min(1),
|
|
89337
|
-
enabled: boolean(),
|
|
89338
|
-
debounceMs: number().nonnegative(),
|
|
89339
|
-
edges: array(_enum([
|
|
89340
|
-
"rising",
|
|
89341
|
-
"button",
|
|
89342
|
-
"native-event"
|
|
89343
|
-
])).min(1).optional(),
|
|
89344
|
-
sources: array(OverrideSourceSchema).min(1)
|
|
89345
|
-
});
|
|
89346
|
-
var SpanSourceDraftSchema = OverrideSourceSchema.extend({ mode: _enum([
|
|
89347
|
-
"in-addition",
|
|
89348
|
-
"confirm",
|
|
89349
|
-
"replace"
|
|
89350
|
-
]).optional() });
|
|
89351
|
-
discriminatedUnion("kind", [
|
|
89352
|
-
ScalarOverrideDraftSchema,
|
|
89353
|
-
KeyedArrayOverrideDraftSchema,
|
|
89354
|
-
PulseOverrideDraftSchema,
|
|
89355
|
-
object({
|
|
89356
|
-
kind: literal("span"),
|
|
89357
|
-
cap: string().min(1),
|
|
89358
|
-
targetId: string().min(1),
|
|
89359
|
-
timeoutMs: number().nonnegative(),
|
|
89360
|
-
cooldownMs: number().nonnegative().optional(),
|
|
89361
|
-
sources: array(SpanSourceDraftSchema)
|
|
89362
|
-
})
|
|
89363
|
-
]);
|
|
89364
|
-
object({
|
|
89365
|
-
targetId: string().min(1),
|
|
89366
|
-
configured: boolean(),
|
|
89367
|
-
source: _enum(["native", "assigned"]),
|
|
89368
|
-
state: _enum([
|
|
89369
|
-
"active",
|
|
89370
|
-
"stale",
|
|
89371
|
-
"released",
|
|
89372
|
-
"unavailable"
|
|
89373
|
-
]),
|
|
89374
|
-
reason: string().nullable()
|
|
89375
|
-
});
|
|
89376
89795
|
var G711_SCALE_CORRECTION_DB = {
|
|
89377
89796
|
PCMU: 20 * Math.log10(4),
|
|
89378
89797
|
PCMA: 20 * Math.log10(8)
|
|
@@ -123197,7 +123616,7 @@ var MqttAclStore = class MqttAclStore {
|
|
|
123197
123616
|
* The addon has NO standalone admin page. All configuration — external
|
|
123198
123617
|
* broker entries, the embedded `aedes` broker, embedded credentials,
|
|
123199
123618
|
* and the topic ACL — is exposed through the standard
|
|
123200
|
-
* `
|
|
123619
|
+
* `clusterSettingsSchema()` and rendered automatically inside the
|
|
123201
123620
|
* Integrations → Brokers tab's docked Settings panel, like every other
|
|
123202
123621
|
* integration-type addon.
|
|
123203
123622
|
*
|
|
@@ -123384,6 +123803,7 @@ var MqttBrokerAddon = class extends BaseAddon {
|
|
|
123384
123803
|
this.aclStore = MqttAclStore.of(records);
|
|
123385
123804
|
this.instances = new RecordIntegrationInstances(records, mqttBrokersEntryKind, {
|
|
123386
123805
|
description: {
|
|
123806
|
+
role: "services",
|
|
123387
123807
|
instanceMode: "multiple",
|
|
123388
123808
|
kind: "brokers",
|
|
123389
123809
|
canTest: false
|
|
@@ -124267,11 +124687,15 @@ var MqttBrokerAddon = class extends BaseAddon {
|
|
|
124267
124687
|
}
|
|
124268
124688
|
};
|
|
124269
124689
|
}
|
|
124690
|
+
scopedSettingsCapabilities() {
|
|
124691
|
+
return ["mqtt-broker", "broker"];
|
|
124692
|
+
}
|
|
124270
124693
|
/**
|
|
124271
124694
|
* The embedded broker, its credentials and the ACL. The external brokers are records, listed
|
|
124272
124695
|
* on the Brokers page and edited as integration instances (`brokerForm`).
|
|
124273
124696
|
*/
|
|
124274
|
-
|
|
124697
|
+
async clusterSettingsSchema(cap) {
|
|
124698
|
+
if (cap !== "mqtt-broker" && cap !== "broker") return null;
|
|
124275
124699
|
return this.schema({ sections: [
|
|
124276
124700
|
{
|
|
124277
124701
|
id: "mqtt-broker-embedded",
|