@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
|
@@ -6021,7 +6021,7 @@ var ZodIssueCode = {
|
|
|
6021
6021
|
var ZodFirstPartyTypeKind;
|
|
6022
6022
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
6023
6023
|
//#endregion
|
|
6024
|
-
//#region ../types/dist/sleep-
|
|
6024
|
+
//#region ../types/dist/sleep-DEZaLbiu.mjs
|
|
6025
6025
|
/**
|
|
6026
6026
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
6027
6027
|
* window to float samples (D455).
|
|
@@ -6621,7 +6621,7 @@ var NOT_RUN = {
|
|
|
6621
6621
|
* throws: an unreachable or refusing engine is logged and reported `declared: false`,
|
|
6622
6622
|
* because the hub (and this addon's boot) must not hang on a migration.
|
|
6623
6623
|
*/
|
|
6624
|
-
async function runAddonMigrations(host, migrations, logger) {
|
|
6624
|
+
async function runAddonMigrations(host, migrations, logger, options = {}) {
|
|
6625
6625
|
const first = migrations[0];
|
|
6626
6626
|
if (first === void 0) return NOT_RUN;
|
|
6627
6627
|
const owner = first.meta.owner;
|
|
@@ -6650,7 +6650,9 @@ async function runAddonMigrations(host, migrations, logger) {
|
|
|
6650
6650
|
else if (status === "blocked") blocked.push(id);
|
|
6651
6651
|
else skipped.push(id);
|
|
6652
6652
|
};
|
|
6653
|
+
const selected = options.only === void 0 ? null : new Set(options.only);
|
|
6653
6654
|
for (const step of declaration.steps) {
|
|
6655
|
+
if (selected !== null && !selected.has(step.id)) continue;
|
|
6654
6656
|
const bound = byId.get(step.id);
|
|
6655
6657
|
if (bound === void 0 || step.action === "skip") {
|
|
6656
6658
|
skipped.push(step.id);
|
|
@@ -6828,6 +6830,12 @@ function projectStore(store, perNodeKeys, nodeId) {
|
|
|
6828
6830
|
}
|
|
6829
6831
|
return out;
|
|
6830
6832
|
}
|
|
6833
|
+
/** Every scope, in the order the guard and the docs list them. */
|
|
6834
|
+
var SCOPED_SETTINGS_SCOPES = [
|
|
6835
|
+
"cluster",
|
|
6836
|
+
"device-defaults",
|
|
6837
|
+
"node"
|
|
6838
|
+
];
|
|
6831
6839
|
/**
|
|
6832
6840
|
* The address of one keychain value: `secret://<owner>/<scope>/<fieldPath>`.
|
|
6833
6841
|
*
|
|
@@ -7719,6 +7727,13 @@ var BaseAddon = class {
|
|
|
7719
7727
|
*/
|
|
7720
7728
|
secretStatePaths = [];
|
|
7721
7729
|
_addonSecretTree = null;
|
|
7730
|
+
/**
|
|
7731
|
+
* D986 M4: the scoped schemas this addon declared, resolved once per capability
|
|
7732
|
+
* (`loadScopedSchemas`) so the sync readers (`perNodeKeys`, the secret tree, the restart check)
|
|
7733
|
+
* see them. Empty until loaded: the readers then fall back to {@link globalSettingsSchema}.
|
|
7734
|
+
*/
|
|
7735
|
+
_scopedByCap = /* @__PURE__ */ new Map();
|
|
7736
|
+
_nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
7722
7737
|
_deviceSecretTree = null;
|
|
7723
7738
|
/** Refs the last resolved config pointed at — a change to one re-resolves. */
|
|
7724
7739
|
_configSecretRefs = /* @__PURE__ */ new Set();
|
|
@@ -7788,6 +7803,7 @@ var BaseAddon = class {
|
|
|
7788
7803
|
this._ctx = context;
|
|
7789
7804
|
this._secretSettings = null;
|
|
7790
7805
|
}
|
|
7806
|
+
await this.loadScopedSchemas();
|
|
7791
7807
|
await this.resolveConfig();
|
|
7792
7808
|
if (secrets !== void 0) this._secretChangeUnsub = secrets.onChange((ref) => {
|
|
7793
7809
|
if (this._configSecretRefs.has(ref)) this.scheduleSecretReresolve(SECRET_CHANGE_DEBOUNCE_MS);
|
|
@@ -7797,6 +7813,7 @@ var BaseAddon = class {
|
|
|
7797
7813
|
const normalized = normalizeAddonInitResult(result);
|
|
7798
7814
|
const providers = normalized && "providers" in normalized && normalized.providers ? normalized.providers : [];
|
|
7799
7815
|
this._registeredCapNames = providers.map((p) => p.capability.name);
|
|
7816
|
+
await this.loadScopedSchemas();
|
|
7800
7817
|
return normalized;
|
|
7801
7818
|
}
|
|
7802
7819
|
/**
|
|
@@ -7810,14 +7827,18 @@ var BaseAddon = class {
|
|
|
7810
7827
|
* nothing; the boot continues. A migration that FAILED (or was blocked by one)
|
|
7811
7828
|
* and declares `onFailure: 'block-owner'` makes this throw, so the addon does not
|
|
7812
7829
|
* serve data whose shape it cannot trust; `tolerate` migrations never do.
|
|
7830
|
+
*
|
|
7831
|
+
* ALWAYS pass the whole catalogue; a phase that must run only some of it passes
|
|
7832
|
+
* `{ only: [ids] }` — never a filtered catalogue (the engine would judge the
|
|
7833
|
+
* omitted applied ids "newer").
|
|
7813
7834
|
*/
|
|
7814
|
-
async runMigrations(migrations) {
|
|
7835
|
+
async runMigrations(migrations, options) {
|
|
7815
7836
|
const api = this.ctx.api.migrations;
|
|
7816
7837
|
const report = await runAddonMigrations({
|
|
7817
7838
|
declareOwner: (input) => api.declareOwner.mutate(input),
|
|
7818
7839
|
startMigration: (input) => api.startMigration.mutate(input),
|
|
7819
7840
|
finishMigration: (input) => api.finishMigration.mutate(input)
|
|
7820
|
-
}, migrations, this.ctx.logger);
|
|
7841
|
+
}, migrations, this.ctx.logger, options);
|
|
7821
7842
|
if (report.blockOwner.length > 0) throw new AddonMigrationsBlockedError(report.blockOwner);
|
|
7822
7843
|
return report;
|
|
7823
7844
|
}
|
|
@@ -7881,6 +7902,16 @@ var BaseAddon = class {
|
|
|
7881
7902
|
schema(schema) {
|
|
7882
7903
|
return schema;
|
|
7883
7904
|
}
|
|
7905
|
+
/**
|
|
7906
|
+
* The capability names whose scoped settings schemas ({@link clusterSettingsSchema},
|
|
7907
|
+
* {@link nodeSettingsSchema}, {@link deviceDefaultsSchema}) this addon declares. Read BEFORE
|
|
7908
|
+
* `onInitialize`, when the providers it registers are not known yet: the first config resolve
|
|
7909
|
+
* must already know which keys are per node. Names the providers register at init are added
|
|
7910
|
+
* once known.
|
|
7911
|
+
*/
|
|
7912
|
+
scopedSettingsCapabilities() {
|
|
7913
|
+
return [];
|
|
7914
|
+
}
|
|
7884
7915
|
/** Override to provide global-level settings UI schema. */
|
|
7885
7916
|
globalSettingsSchema(_cap) {
|
|
7886
7917
|
return null;
|
|
@@ -7924,7 +7955,8 @@ var BaseAddon = class {
|
|
|
7924
7955
|
return [];
|
|
7925
7956
|
}
|
|
7926
7957
|
async getGlobalSettings(overlay, cap, nodeId) {
|
|
7927
|
-
|
|
7958
|
+
await this.loadScopedSchemas(cap);
|
|
7959
|
+
const schema = this.globalSettingsSchema(cap) ?? this.scopedFormSchema(cap);
|
|
7928
7960
|
if (!schema) return { sections: [] };
|
|
7929
7961
|
const projected = await this.readRawProjectedStore(nodeId, cap);
|
|
7930
7962
|
return hydrateSchema(schema, overlay ? await this.overlayKeepingMarkers(projected, overlay) : projected, { inlineSecretsAsViews: true });
|
|
@@ -7959,10 +7991,9 @@ var BaseAddon = class {
|
|
|
7959
7991
|
*
|
|
7960
7992
|
* Override it only when an `instanceMode: multiple` addon's instances differ: an NVR integration
|
|
7961
7993
|
* needs its connection fields, the default integration the addon's own sections. It is the
|
|
7962
|
-
* SHAPE of the form only — the values live
|
|
7963
|
-
* unchanged (`integrations.setSettings` for an account integration).
|
|
7994
|
+
* SHAPE of the form only — the values live on the instance record.
|
|
7964
7995
|
*/
|
|
7965
|
-
async
|
|
7996
|
+
async integrationFormSchema(_instance) {
|
|
7966
7997
|
return null;
|
|
7967
7998
|
}
|
|
7968
7999
|
/**
|
|
@@ -8008,7 +8039,7 @@ var BaseAddon = class {
|
|
|
8008
8039
|
* per-instance schema when it declares one, else its global schema (what they always showed).
|
|
8009
8040
|
*/
|
|
8010
8041
|
async getIntegrationFormSchema(instance, nodeId) {
|
|
8011
|
-
const scoped = await this.
|
|
8042
|
+
const scoped = await this.integrationFormSchema(instance);
|
|
8012
8043
|
if (scoped === null) return this.getGlobalSettings(void 0, void 0, nodeId);
|
|
8013
8044
|
return hydrateSchema(scoped, {}, { inlineSecretsAsViews: true });
|
|
8014
8045
|
}
|
|
@@ -8056,6 +8087,7 @@ var BaseAddon = class {
|
|
|
8056
8087
|
* resolved blob back is a no-op for every unchanged secret.
|
|
8057
8088
|
*/
|
|
8058
8089
|
async resolveGlobalStore(nodeId, cap) {
|
|
8090
|
+
await this.loadScopedSchemas(cap);
|
|
8059
8091
|
const raw = await this._ctx?.settings?.readAddonStore() ?? {};
|
|
8060
8092
|
return this.projectForNode(raw, nodeId, cap);
|
|
8061
8093
|
}
|
|
@@ -8066,6 +8098,7 @@ var BaseAddon = class {
|
|
|
8066
8098
|
}
|
|
8067
8099
|
/** The PROJECTED store with its markers — the form hydrate only (D798). */
|
|
8068
8100
|
async readRawProjectedStore(nodeId, cap) {
|
|
8101
|
+
await this.loadScopedSchemas(cap);
|
|
8069
8102
|
const raw = await this.rawSettings()?.readAddonStore() ?? {};
|
|
8070
8103
|
return this.projectForNode(raw, nodeId, cap);
|
|
8071
8104
|
}
|
|
@@ -8158,12 +8191,17 @@ var BaseAddon = class {
|
|
|
8158
8191
|
const stored = keychain ? await this.readAddonStoreWithRetry() : {};
|
|
8159
8192
|
const patch = {};
|
|
8160
8193
|
for (const key of keys) {
|
|
8194
|
+
if (tree.get(key)?.kind === "array") {
|
|
8195
|
+
patch[key] = [];
|
|
8196
|
+
continue;
|
|
8197
|
+
}
|
|
8161
8198
|
const keychainStored = tree.has(key) || collectSecretMarkerRefs(stored[key]).length > 0;
|
|
8162
8199
|
patch[key] = keychain && keychainStored ? SECRET_CLEAR : "";
|
|
8163
8200
|
}
|
|
8164
8201
|
await this.applyGlobalPatch(patch, nodeId);
|
|
8165
8202
|
}
|
|
8166
8203
|
async applyGlobalPatch(barePatch, nodeId) {
|
|
8204
|
+
await this.loadScopedSchemas();
|
|
8167
8205
|
const keys = this.perNodeKeys();
|
|
8168
8206
|
const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
|
|
8169
8207
|
const target = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
|
|
@@ -8177,7 +8215,8 @@ var BaseAddon = class {
|
|
|
8177
8215
|
await this.resolveConfig();
|
|
8178
8216
|
await this.onConfigChanged();
|
|
8179
8217
|
this.emitLifecycle(EventCategory.AddonUpdated, { level: "global" });
|
|
8180
|
-
|
|
8218
|
+
const legacy = this.globalSettingsSchema();
|
|
8219
|
+
this.maybeAutoRestart(Object.keys(barePatch), [...legacy === null ? [] : [legacy], ...this.loadedScopedSchemas().map((entry) => entry.schema)]);
|
|
8181
8220
|
}
|
|
8182
8221
|
/**
|
|
8183
8222
|
* The set of field keys the global settings schema declares `perNode: true`
|
|
@@ -8185,14 +8224,67 @@ var BaseAddon = class {
|
|
|
8185
8224
|
* declarations). Empty set ⇒ every per-node code path is bypassed and the
|
|
8186
8225
|
* settings API behaves exactly like the legacy node-agnostic one.
|
|
8187
8226
|
*/
|
|
8188
|
-
|
|
8227
|
+
/**
|
|
8228
|
+
* Load the scoped schemas of every capability this addon declares (the override and the
|
|
8229
|
+
* providers registered at init, plus `extraCap`) that is not loaded yet, then drop what was
|
|
8230
|
+
* derived from the previous set. Idempotent.
|
|
8231
|
+
*/
|
|
8232
|
+
async loadScopedSchemas(extraCap) {
|
|
8233
|
+
const caps = new Set([
|
|
8234
|
+
...this.scopedSettingsCapabilities(),
|
|
8235
|
+
...this._registeredCapNames,
|
|
8236
|
+
...extraCap === void 0 ? [] : [extraCap]
|
|
8237
|
+
]);
|
|
8238
|
+
let added = false;
|
|
8239
|
+
for (const cap of caps) {
|
|
8240
|
+
if (this._scopedByCap.has(cap)) continue;
|
|
8241
|
+
const loaded = [];
|
|
8242
|
+
for (const scope of SCOPED_SETTINGS_SCOPES) {
|
|
8243
|
+
const schema = await this.getScopedSettingsSchema(scope, cap);
|
|
8244
|
+
if (schema !== null) loaded.push({
|
|
8245
|
+
scope,
|
|
8246
|
+
schema
|
|
8247
|
+
});
|
|
8248
|
+
}
|
|
8249
|
+
this._scopedByCap.set(cap, loaded);
|
|
8250
|
+
added = true;
|
|
8251
|
+
}
|
|
8252
|
+
if (!added) return;
|
|
8253
|
+
this._nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
8254
|
+
this._addonSecretTree = null;
|
|
8255
|
+
}
|
|
8256
|
+
/**
|
|
8257
|
+
* The form the settings API serves while an addon declares no {@link globalSettingsSchema}: the
|
|
8258
|
+
* sections of its scoped schemas, so dropping the old hook does not blank the form.
|
|
8259
|
+
*/
|
|
8260
|
+
scopedFormSchema(cap) {
|
|
8261
|
+
const byId = /* @__PURE__ */ new Map();
|
|
8262
|
+
for (const entry of this.loadedScopedSchemas(cap)) for (const section of entry.schema.sections) if (!byId.has(section.id)) byId.set(section.id, section);
|
|
8263
|
+
return byId.size === 0 ? null : { sections: [...byId.values()] };
|
|
8264
|
+
}
|
|
8265
|
+
/** Every loaded scoped schema of `cap`, or of every loaded capability. */
|
|
8266
|
+
loadedScopedSchemas(cap) {
|
|
8267
|
+
if (cap !== void 0) return this._scopedByCap.get(cap) ?? [];
|
|
8268
|
+
return [...this._scopedByCap.values()].flat();
|
|
8269
|
+
}
|
|
8270
|
+
/**
|
|
8271
|
+
* The field keys that are stored per node: every field of a {@link nodeSettingsSchema}, plus
|
|
8272
|
+
* the fields the legacy {@link globalSettingsSchema} still marks `perNode: true`. Derived once
|
|
8273
|
+
* per `cap` argument and memoized (schemas are static declarations). Empty set ⇒ every
|
|
8274
|
+
* per-node code path is bypassed and the settings API behaves like the node-agnostic one.
|
|
8275
|
+
* Call {@link loadScopedSchemas} first (the async seams do).
|
|
8276
|
+
*/
|
|
8189
8277
|
perNodeKeys(cap) {
|
|
8190
8278
|
const cacheKey = cap ?? "";
|
|
8191
|
-
const cached = this.
|
|
8279
|
+
const cached = this._nodeKeysByCap.get(cacheKey);
|
|
8192
8280
|
if (cached) return cached;
|
|
8193
|
-
const
|
|
8194
|
-
const keys = new Set(
|
|
8195
|
-
this.
|
|
8281
|
+
const legacy = this.globalSettingsSchema(cap);
|
|
8282
|
+
const keys = new Set(legacy ? legacy.sections.flatMap((section) => collectPerNodeFieldKeys(section.fields)) : []);
|
|
8283
|
+
for (const { scope, schema } of this.loadedScopedSchemas(cap)) {
|
|
8284
|
+
if (scope !== "node") continue;
|
|
8285
|
+
for (const section of schema.sections) for (const key of collectValueFieldKeys(section.fields)) keys.add(key);
|
|
8286
|
+
}
|
|
8287
|
+
this._nodeKeysByCap.set(cacheKey, keys);
|
|
8196
8288
|
return keys;
|
|
8197
8289
|
}
|
|
8198
8290
|
/**
|
|
@@ -8202,10 +8294,9 @@ var BaseAddon = class {
|
|
|
8202
8294
|
* time to return its response before the addon is torn down and
|
|
8203
8295
|
* re-initialised by `AddonRegistryService.restartAddon`.
|
|
8204
8296
|
*/
|
|
8205
|
-
maybeAutoRestart(patchKeys,
|
|
8206
|
-
if (!schema) return;
|
|
8297
|
+
maybeAutoRestart(patchKeys, schemas) {
|
|
8207
8298
|
const restartKeys = /* @__PURE__ */ new Set();
|
|
8208
|
-
for (const section of schema.sections) for (const field of section.fields) {
|
|
8299
|
+
for (const schema of schemas) for (const section of schema.sections) for (const field of section.fields) {
|
|
8209
8300
|
if (field.type === "separator" || field.type === "info") continue;
|
|
8210
8301
|
if (field.requiresRestart) restartKeys.add(field.key);
|
|
8211
8302
|
}
|
|
@@ -8401,12 +8492,14 @@ var BaseAddon = class {
|
|
|
8401
8492
|
* here, in the addon's own process, so the hub keeps no copy of it.
|
|
8402
8493
|
*/
|
|
8403
8494
|
async getDeclaredSettingsKeys() {
|
|
8495
|
+
await this.loadScopedSchemas();
|
|
8404
8496
|
return { keys: [...this.declaredKeySet()].toSorted() };
|
|
8405
8497
|
}
|
|
8406
8498
|
declaredKeySet() {
|
|
8407
8499
|
const keys = new Set(Object.keys(this.defaults));
|
|
8408
8500
|
const schema = this.globalSettingsSchema();
|
|
8409
8501
|
if (schema) for (const section of schema.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
8502
|
+
for (const { schema: scoped } of this.loadedScopedSchemas()) for (const section of scoped.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
8410
8503
|
for (const key of this._declaredStateKeys) keys.add(key);
|
|
8411
8504
|
for (const path of this.secretStatePaths) {
|
|
8412
8505
|
const root = path.split(/[.[]/, 1)[0];
|
|
@@ -8502,7 +8595,11 @@ var BaseAddon = class {
|
|
|
8502
8595
|
}
|
|
8503
8596
|
/** The addon store's secret tree: schema fields by TYPE, plus `secretStatePaths`. */
|
|
8504
8597
|
addonSecretTree() {
|
|
8505
|
-
this._addonSecretTree
|
|
8598
|
+
if (this._addonSecretTree === null) {
|
|
8599
|
+
const tree = new Map(secretTreeFromSchema(this.globalSettingsSchema()));
|
|
8600
|
+
for (const { schema } of this.loadedScopedSchemas()) for (const [key, node] of secretTreeFromSchema(schema)) tree.set(key, node);
|
|
8601
|
+
this._addonSecretTree = mergeSecretStatePaths(tree, this.secretStatePaths);
|
|
8602
|
+
}
|
|
8506
8603
|
return this._addonSecretTree;
|
|
8507
8604
|
}
|
|
8508
8605
|
deviceSecretTree() {
|
|
@@ -8752,6 +8849,23 @@ function dropPerNodeFields(fields) {
|
|
|
8752
8849
|
}
|
|
8753
8850
|
return kept;
|
|
8754
8851
|
}
|
|
8852
|
+
/** The keys of the value-holding fields, recursing into layout containers. */
|
|
8853
|
+
function collectValueFieldKeys(fields) {
|
|
8854
|
+
const collected = [];
|
|
8855
|
+
for (const field of fields) {
|
|
8856
|
+
if (field.type === "group") {
|
|
8857
|
+
collected.push(...collectValueFieldKeys(field.fields));
|
|
8858
|
+
continue;
|
|
8859
|
+
}
|
|
8860
|
+
if (field.type === "sub-tabs") {
|
|
8861
|
+
for (const tab of field.tabs) collected.push(...collectValueFieldKeys(tab.fields));
|
|
8862
|
+
continue;
|
|
8863
|
+
}
|
|
8864
|
+
if (field.type === "separator" || field.type === "info") continue;
|
|
8865
|
+
if ("key" in field && typeof field.key === "string") collected.push(field.key);
|
|
8866
|
+
}
|
|
8867
|
+
return collected;
|
|
8868
|
+
}
|
|
8755
8869
|
function collectFieldKeys(fields) {
|
|
8756
8870
|
const collected = [];
|
|
8757
8871
|
for (const field of fields) {
|
|
@@ -8853,6 +8967,10 @@ var RECORD_SECRET_COLLECTION_PREFIX = "records:";
|
|
|
8853
8967
|
* (`collectZodSecretPaths`): dotted keys, `[]` for "each element of this array"
|
|
8854
8968
|
* (`auth.accessToken`, `headers[].value`).
|
|
8855
8969
|
*
|
|
8970
|
+
* `{}` after a key means "each value of this map" (`applianceSecrets{}`): the map's own key
|
|
8971
|
+
* names the value (`applianceSecrets{fridge}`), so no id key is declared and a body that drops a
|
|
8972
|
+
* key releases exactly that key's secret.
|
|
8973
|
+
*
|
|
8856
8974
|
* Array elements are matched by a DECLARED id key (`secretArrayKeys`:
|
|
8857
8975
|
* `{ headers: 'name' }`), never by index, so reordering a list keeps every
|
|
8858
8976
|
* keychain ref (the same rule as `arrayItemFieldPath`). The concrete path of a
|
|
@@ -8865,11 +8983,13 @@ function parseSecretPath(path) {
|
|
|
8865
8983
|
if (path.length === 0) throw new InvalidRecordError("an empty secret path");
|
|
8866
8984
|
return path.split(".").map((raw) => {
|
|
8867
8985
|
const each = raw.endsWith("[]");
|
|
8868
|
-
const
|
|
8986
|
+
const map = raw.endsWith("{}");
|
|
8987
|
+
const key = each || map ? raw.slice(0, -2) : raw;
|
|
8869
8988
|
if (key.length === 0) throw new InvalidRecordError(`secret path "${path}" has an empty segment`);
|
|
8870
8989
|
return {
|
|
8871
8990
|
key,
|
|
8872
|
-
each
|
|
8991
|
+
each,
|
|
8992
|
+
map
|
|
8873
8993
|
};
|
|
8874
8994
|
});
|
|
8875
8995
|
}
|
|
@@ -8921,6 +9041,22 @@ function mapAt(node, segments, index, walkedKeys, concretePrefix, declared, arra
|
|
|
8921
9041
|
[segment.key]: mapped
|
|
8922
9042
|
};
|
|
8923
9043
|
}
|
|
9044
|
+
if (segment.map) {
|
|
9045
|
+
if (!isRecord$4(child)) return node;
|
|
9046
|
+
const kept = Object.entries(child).map(([mapKey, value]) => {
|
|
9047
|
+
const concrete = `${concretePrefix}${concretePrefix.length > 0 ? "." : ""}${segment.key}{${encodeURIComponent(mapKey)}}`;
|
|
9048
|
+
if (!last) return [mapKey, mapAt(value, segments, index + 1, keys, concrete, declared, arrayKeys, visit)];
|
|
9049
|
+
return [mapKey, visit({
|
|
9050
|
+
declared,
|
|
9051
|
+
concrete,
|
|
9052
|
+
value
|
|
9053
|
+
})];
|
|
9054
|
+
}).filter(([, value]) => value !== REMOVE_LEAF);
|
|
9055
|
+
return {
|
|
9056
|
+
...node,
|
|
9057
|
+
[segment.key]: Object.fromEntries(kept)
|
|
9058
|
+
};
|
|
9059
|
+
}
|
|
8924
9060
|
const concrete = `${concretePrefix}${concretePrefix.length > 0 ? "." : ""}${segment.key}`;
|
|
8925
9061
|
if (last) {
|
|
8926
9062
|
const replacement = visit({
|
|
@@ -9062,7 +9198,8 @@ function isZodSecret(schema) {
|
|
|
9062
9198
|
}
|
|
9063
9199
|
/**
|
|
9064
9200
|
* Every declared secret path in a config schema: dotted keys, `[]` for "each
|
|
9065
|
-
* element of this array" (`connection.password`, `rows[].token`)
|
|
9201
|
+
* element of this array" (`connection.password`, `rows[].token`), `{}` for "each value of this
|
|
9202
|
+
* map" (`applianceSecrets{}`). A declared
|
|
9066
9203
|
* object or array is reported as one path and not descended into — everything
|
|
9067
9204
|
* under it is secret.
|
|
9068
9205
|
*/
|
|
@@ -9073,6 +9210,7 @@ function collectZodSecretPaths(schema, prefix = "") {
|
|
|
9073
9210
|
const def = defOf(layer);
|
|
9074
9211
|
if (def.type === "object" && def.shape) for (const [key, child] of Object.entries(def.shape)) paths.push(...collectZodSecretPaths(child, prefix ? `${prefix}.${key}` : key));
|
|
9075
9212
|
else if (def.type === "array" && def.element) paths.push(...collectZodSecretPaths(def.element, `${prefix}[]`));
|
|
9213
|
+
else if (def.type === "record" && def.valueType) paths.push(...collectZodSecretPaths(def.valueType, `${prefix}{}`));
|
|
9076
9214
|
else if (def.type === "union" && def.options) for (const option of def.options) paths.push(...collectZodSecretPaths(option, prefix));
|
|
9077
9215
|
else if (def.type === "intersection") {
|
|
9078
9216
|
if (def.left) paths.push(...collectZodSecretPaths(def.left, prefix));
|
|
@@ -9212,7 +9350,7 @@ function defineRecordKinds(specs) {
|
|
|
9212
9350
|
retention: spec.retention,
|
|
9213
9351
|
deviceRemoval: spec.deviceRemoval,
|
|
9214
9352
|
maxDataBytes: spec.maxDataBytes,
|
|
9215
|
-
secretPaths: [...secretPaths],
|
|
9353
|
+
secretPaths: [...new Set([...secretPaths, ...spec.retiredSecretPaths ?? []])],
|
|
9216
9354
|
secretArrayKeys: spec.secretArrayKeys === void 0 ? void 0 : { ...spec.secretArrayKeys },
|
|
9217
9355
|
transactFamily: spec.transactFamily,
|
|
9218
9356
|
entry: spec.entry === void 0 ? void 0 : {
|
|
@@ -9254,9 +9392,11 @@ function defineEntryKind(name, spec) {
|
|
|
9254
9392
|
class: "config",
|
|
9255
9393
|
writeTrigger: spec.writeTrigger ?? "an operator adds or edits an entry in the entries UI",
|
|
9256
9394
|
retention: { none: "an entry goes only when the operator deletes it" },
|
|
9395
|
+
...spec.maxDataBytes === void 0 ? {} : { maxDataBytes: spec.maxDataBytes },
|
|
9257
9396
|
...spec.database === void 0 ? {} : { database: spec.database },
|
|
9258
9397
|
...spec.indexes === void 0 ? {} : { indexes: spec.indexes },
|
|
9259
9398
|
...spec.secretArrayKeys === void 0 ? {} : { secretArrayKeys: spec.secretArrayKeys },
|
|
9399
|
+
...spec.retiredSecretPaths === void 0 ? {} : { retiredSecretPaths: spec.retiredSecretPaths },
|
|
9260
9400
|
entry: {
|
|
9261
9401
|
title: spec.title,
|
|
9262
9402
|
...spec.nameField === void 0 ? {} : { nameField: spec.nameField }
|
|
@@ -11918,7 +12058,7 @@ object({
|
|
|
11918
12058
|
params: record(string(), union([string(), number()])).optional()
|
|
11919
12059
|
});
|
|
11920
12060
|
//#endregion
|
|
11921
|
-
//#region ../types/dist/composition-
|
|
12061
|
+
//#region ../types/dist/composition-pOHbTzRB.mjs
|
|
11922
12062
|
/**
|
|
11923
12063
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
11924
12064
|
*
|
|
@@ -12681,38 +12821,6 @@ function validateExpressionSource(src) {
|
|
|
12681
12821
|
return null;
|
|
12682
12822
|
}
|
|
12683
12823
|
/**
|
|
12684
|
-
* One power reading becomes a washer, dryer or dishwasher (D944 helper).
|
|
12685
|
-
*
|
|
12686
|
-
* Standby of the control board is under 5 W; a motor or a heater is far
|
|
12687
|
-
* above that, so 5 W is the line. A door lock or a short pump spike is not
|
|
12688
|
-
* a cycle, so running starts only after 30 s above the line.
|
|
12689
|
-
*
|
|
12690
|
-
* A wash, dry or dishwasher pause (soak, heat-wait, heater off between
|
|
12691
|
-
* bursts) lasts several minutes. Ending the cycle on the first dip would
|
|
12692
|
-
* report "finished" in the middle of the program, so the stop dwell is
|
|
12693
|
-
* 10 minutes on every type. The number is stored per type so one machine
|
|
12694
|
-
* can move later without moving the others.
|
|
12695
|
-
*/
|
|
12696
|
-
var APPLIANCE_TEMPLATE_ID = "appliance";
|
|
12697
|
-
var APPLIANCE_KINDS = [
|
|
12698
|
-
"washer",
|
|
12699
|
-
"dryer",
|
|
12700
|
-
"dishwasher"
|
|
12701
|
-
];
|
|
12702
|
-
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
12703
|
-
/**
|
|
12704
|
-
* A dumb plug-in stove or heater, built from a smart plug (a `Heater`).
|
|
12705
|
-
*
|
|
12706
|
-
* The plug is the actuator: `switch.setState` is a FORWARD to the plug's own
|
|
12707
|
-
* (D967), so the heater switches exactly as the plug does. Running is the
|
|
12708
|
-
* plug being on; when a power reading is also named, running is the draw
|
|
12709
|
-
* staying above the line instead (a plug left on with the stove's own
|
|
12710
|
-
* thermostat open is not heating). 50 W is far above any standby and below
|
|
12711
|
-
* the smallest radiator.
|
|
12712
|
-
*/
|
|
12713
|
-
var HEATER_TEMPLATE_ID = "heater";
|
|
12714
|
-
DeviceType.Heater;
|
|
12715
|
-
/**
|
|
12716
12824
|
* Statistics and integrals over a history read (D944).
|
|
12717
12825
|
*
|
|
12718
12826
|
* Pure: the same points always give the same answer, so a restart recomputes
|
|
@@ -12856,34 +12964,6 @@ var HelperSpecSchema = discriminatedUnion("kind", [
|
|
|
12856
12964
|
/** Actuator groups only. Readings leave this unset. */
|
|
12857
12965
|
active: ActiveCriterionSchema.optional()
|
|
12858
12966
|
}),
|
|
12859
|
-
object({
|
|
12860
|
-
kind: literal("appliance"),
|
|
12861
|
-
templateId: literal(APPLIANCE_TEMPLATE_ID),
|
|
12862
|
-
appliance: _enum(APPLIANCE_KINDS),
|
|
12863
|
-
thresholdWatts: number().positive(),
|
|
12864
|
-
startDwellMs: number().int().nonnegative(),
|
|
12865
|
-
stopDwellMs: number().int().nonnegative(),
|
|
12866
|
-
member: HelperMemberSchema,
|
|
12867
|
-
/**
|
|
12868
|
-
* The plug that powers the appliance (`switch.on`). Optional: when set, the
|
|
12869
|
-
* composition carries a `power-source` feature, so the hub can cut and restore
|
|
12870
|
-
* that plug (power-source link of a NEW-target composition).
|
|
12871
|
-
*/
|
|
12872
|
-
powerSwitch: HelperMemberSchema.optional(),
|
|
12873
|
-
/** How long a forced restart keeps the plug off; the cap's default when unset. */
|
|
12874
|
-
offSeconds: number().optional()
|
|
12875
|
-
}),
|
|
12876
|
-
object({
|
|
12877
|
-
kind: literal("heater"),
|
|
12878
|
-
templateId: literal(HEATER_TEMPLATE_ID),
|
|
12879
|
-
/** The plug, read and commanded at `switch.on`. */
|
|
12880
|
-
plug: HelperMemberSchema,
|
|
12881
|
-
/** The plug's power reading, when it has one: running is then the draw, not the switch. */
|
|
12882
|
-
meter: HelperMemberSchema.optional(),
|
|
12883
|
-
thresholdWatts: number().positive(),
|
|
12884
|
-
startDwellMs: number().int().nonnegative(),
|
|
12885
|
-
stopDwellMs: number().int().nonnegative()
|
|
12886
|
-
}),
|
|
12887
12967
|
object({
|
|
12888
12968
|
kind: literal("statistic"),
|
|
12889
12969
|
stat: _enum(STATISTIC_STATS),
|
|
@@ -13107,7 +13187,7 @@ var CompositionSchema = object({
|
|
|
13107
13187
|
kind: literal("existing"),
|
|
13108
13188
|
device: CompositionSourceRefSchema
|
|
13109
13189
|
})]),
|
|
13110
|
-
features: array(CompositionFeatureSchema).min(
|
|
13190
|
+
features: array(CompositionFeatureSchema).min(0).max(16),
|
|
13111
13191
|
/**
|
|
13112
13192
|
* Present when the block is a helper template. Absent on a free
|
|
13113
13193
|
* composition (the room example, device 4603). The editor reads this,
|
|
@@ -13325,11 +13405,49 @@ var IntegrationInstanceRefSchema = object({
|
|
|
13325
13405
|
]),
|
|
13326
13406
|
candidateStableId: string().optional()
|
|
13327
13407
|
});
|
|
13408
|
+
/**
|
|
13409
|
+
* `RecordIntegrationInstances` - the `integration-instances` provider over ONE
|
|
13410
|
+
* entry kind, so an addon that holds a connection serves it in ~30 lines.
|
|
13411
|
+
*
|
|
13412
|
+
* Every instance is one record of the addon's own kind, keyed by the
|
|
13413
|
+
* `integrations` registry id. The records door does the secret work: a field the
|
|
13414
|
+
* kind marks `.meta({ secret: true })` is a keychain marker bound to the row, so
|
|
13415
|
+
* deleting the row deletes the secret, and one row's value is never another's.
|
|
13416
|
+
*
|
|
13417
|
+
* Patch rules for a secret (`updateInstance`): omitted, `''`, the redaction
|
|
13418
|
+
* token or the view the form was given KEEP the stored secret;
|
|
13419
|
+
* `{ $secretClear: true }` clears it; any other string replaces it.
|
|
13420
|
+
*/
|
|
13328
13421
|
/** The most instances one addon lists (an integrations page, not a table). */
|
|
13329
13422
|
var INSTANCES_LIST_MAX = 1e3;
|
|
13330
13423
|
/** The keys of the kind's secret fields that sit at the top level of the body. */
|
|
13331
13424
|
function topLevelSecretKeys(paths) {
|
|
13332
|
-
return new Set(paths.filter((p) => !p.includes(".") && !p.includes("[")));
|
|
13425
|
+
return new Set(paths.filter((p) => !p.includes(".") && !p.includes("[") && !p.includes("{")));
|
|
13426
|
+
}
|
|
13427
|
+
/** The keys of the kind's top-level MAPS whose values are secrets (`applianceSecrets{}`). */
|
|
13428
|
+
function secretMapKeys(paths) {
|
|
13429
|
+
return new Set(paths.filter((p) => p.endsWith("{}") && !p.slice(0, -2).match(/[.[{]/)).map((p) => p.slice(0, -2)));
|
|
13430
|
+
}
|
|
13431
|
+
/**
|
|
13432
|
+
* A patch value for a map of secrets: an entry that is omitted, `''`, the redaction token or the
|
|
13433
|
+
* view the form was given KEEPS its stored secret (it is left out of the merge); `{ $secretClear }`
|
|
13434
|
+
* or `null` removes the entry; any other string replaces it. Null when nothing is left to merge.
|
|
13435
|
+
*/
|
|
13436
|
+
function secretMapPatch(value) {
|
|
13437
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
|
|
13438
|
+
const out = {};
|
|
13439
|
+
for (const [key, entry] of Object.entries(value)) if (entry === null || isSecretClear(entry)) out[key] = null;
|
|
13440
|
+
else if (typeof entry === "string" && entry !== "" && entry !== "__camstack_redacted__" && !isSecretFieldView(entry)) out[key] = entry;
|
|
13441
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
13442
|
+
}
|
|
13443
|
+
/** Does the kind's body schema carry an `enabled` field? */
|
|
13444
|
+
function declaresEnabled(kind) {
|
|
13445
|
+
const json = toJSONSchema(kind.schema, {
|
|
13446
|
+
unrepresentable: "any",
|
|
13447
|
+
io: "input"
|
|
13448
|
+
});
|
|
13449
|
+
const properties = Reflect.get(json, "properties");
|
|
13450
|
+
return typeof properties === "object" && properties !== null && "enabled" in properties;
|
|
13333
13451
|
}
|
|
13334
13452
|
var RecordIntegrationInstances = class {
|
|
13335
13453
|
records;
|
|
@@ -13337,14 +13455,20 @@ var RecordIntegrationInstances = class {
|
|
|
13337
13455
|
hooks;
|
|
13338
13456
|
handle;
|
|
13339
13457
|
secretKeys;
|
|
13458
|
+
secretMaps;
|
|
13340
13459
|
nameField;
|
|
13460
|
+
/** The `integrations` registry owns the on/off switch (`role: devices`); the body has none. */
|
|
13461
|
+
registryOwnsEnabled;
|
|
13341
13462
|
constructor(records, kind, hooks) {
|
|
13342
13463
|
this.records = records;
|
|
13343
13464
|
this.kind = kind;
|
|
13344
13465
|
this.hooks = hooks;
|
|
13345
13466
|
this.handle = records.kind(kind);
|
|
13346
13467
|
this.secretKeys = topLevelSecretKeys(kind.declaration.secretPaths);
|
|
13468
|
+
this.secretMaps = secretMapKeys(kind.declaration.secretPaths);
|
|
13347
13469
|
this.nameField = kind.declaration.entry?.nameField ?? "name";
|
|
13470
|
+
this.registryOwnsEnabled = hooks.description.role === "devices";
|
|
13471
|
+
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"`);
|
|
13348
13472
|
}
|
|
13349
13473
|
/**
|
|
13350
13474
|
* One instance read for the addon's own runtime; null when absent. When the keychain is
|
|
@@ -13389,15 +13513,23 @@ var RecordIntegrationInstances = class {
|
|
|
13389
13513
|
viewOf(record) {
|
|
13390
13514
|
const shown = secretViewsOf(this.records.owner, this.kind.name, record.id, this.kind.declaration, record.data);
|
|
13391
13515
|
const rawName = record.data[this.nameField];
|
|
13516
|
+
const kind = this.hooks.kindOf?.(record.data);
|
|
13517
|
+
const health = this.hooks.health?.(record.data, record.id);
|
|
13392
13518
|
return {
|
|
13393
13519
|
id: record.id,
|
|
13394
13520
|
name: typeof rawName === "string" ? rawName : record.id,
|
|
13395
13521
|
configured: this.hooks.configured(record.data),
|
|
13396
|
-
enabled: record.data.enabled !== false,
|
|
13397
|
-
formValues: shown
|
|
13522
|
+
...this.registryOwnsEnabled ? {} : { enabled: record.data.enabled !== false },
|
|
13523
|
+
formValues: shown,
|
|
13524
|
+
...kind !== void 0 ? { kind } : {},
|
|
13525
|
+
...health?.status !== void 0 ? { status: health.status } : {},
|
|
13526
|
+
...health?.lastCheckedAt !== void 0 ? { lastCheckedAt: health.lastCheckedAt } : {}
|
|
13398
13527
|
};
|
|
13399
13528
|
}
|
|
13400
|
-
describe = async () => this.hooks.description
|
|
13529
|
+
describe = async () => this.hooks.mintId === void 0 ? this.hooks.description : {
|
|
13530
|
+
...this.hooks.description,
|
|
13531
|
+
mintsId: true
|
|
13532
|
+
};
|
|
13401
13533
|
instanceFormSchema = async ({ id }) => this.hooks.formSchema(id);
|
|
13402
13534
|
listInstances = async () => {
|
|
13403
13535
|
return (await this.handle.query({ limit: INSTANCES_LIST_MAX })).records.map((record) => this.viewOf(record));
|
|
@@ -13407,37 +13539,46 @@ var RecordIntegrationInstances = class {
|
|
|
13407
13539
|
return record === null ? null : this.viewOf(record);
|
|
13408
13540
|
};
|
|
13409
13541
|
/** Insert ONE row (`ifRev: 0`: an existing id is refused, never overwritten). */
|
|
13410
|
-
createInstance = async ({ id, name }) => {
|
|
13542
|
+
createInstance = async ({ id, name, kind }) => {
|
|
13543
|
+
const minted = id ?? this.hooks.mintId?.({
|
|
13544
|
+
name,
|
|
13545
|
+
...kind !== void 0 ? { kind } : {}
|
|
13546
|
+
});
|
|
13547
|
+
if (minted === void 0) throw new Error("createInstance needs an id: this provider does not mint one");
|
|
13411
13548
|
await this.handle.put({
|
|
13412
|
-
id,
|
|
13413
|
-
data: this.hooks.initialBody(name)
|
|
13549
|
+
id: minted,
|
|
13550
|
+
data: this.hooks.initialBody(name, kind)
|
|
13414
13551
|
}, { ifRev: 0 });
|
|
13415
|
-
await this.hooks.onChanged?.(
|
|
13416
|
-
return { id };
|
|
13552
|
+
await this.hooks.onChanged?.(minted, "created");
|
|
13553
|
+
return { id: minted };
|
|
13417
13554
|
};
|
|
13418
13555
|
updateInstance = async ({ id, name, values }) => {
|
|
13419
13556
|
const patch = {};
|
|
13420
13557
|
for (const [key, value] of Object.entries(values)) {
|
|
13421
13558
|
if (value === void 0) continue;
|
|
13422
|
-
if (
|
|
13559
|
+
if (this.secretMaps.has(key)) {
|
|
13560
|
+
const entries = secretMapPatch(value);
|
|
13561
|
+
if (entries !== null) patch[key] = entries;
|
|
13562
|
+
} else if (!this.secretKeys.has(key)) patch[key] = value;
|
|
13423
13563
|
else if (isSecretClear(value)) patch[key] = null;
|
|
13424
13564
|
else if (typeof value === "string" && value !== "" && value !== "__camstack_redacted__" && !isSecretFieldView(value)) patch[key] = value;
|
|
13425
13565
|
}
|
|
13426
13566
|
if (name !== void 0) patch[this.nameField] = name;
|
|
13427
13567
|
await this.handle.patch(id, patch);
|
|
13428
|
-
await this.hooks.onChanged?.(id);
|
|
13568
|
+
await this.hooks.onChanged?.(id, "updated");
|
|
13429
13569
|
return { ok: true };
|
|
13430
13570
|
};
|
|
13431
13571
|
/** The switch lives in the record body; the addon is told so it applies it (D996). */
|
|
13432
13572
|
setInstanceEnabled = async ({ id, enabled }) => {
|
|
13573
|
+
if (this.registryOwnsEnabled) throw new Error(`instance "${id}": the integrations registry owns the on/off switch of this addon; set it with integrations.update`);
|
|
13433
13574
|
await this.handle.patch(id, { enabled });
|
|
13434
|
-
await this.hooks.onChanged?.(id);
|
|
13575
|
+
await this.hooks.onChanged?.(id, "enabled");
|
|
13435
13576
|
return { ok: true };
|
|
13436
13577
|
};
|
|
13437
13578
|
/** The row and its keychain refs go together (the records door). */
|
|
13438
13579
|
deleteInstance = async ({ id }) => {
|
|
13439
13580
|
await this.handle.delete(id);
|
|
13440
|
-
await this.hooks.onChanged?.(id);
|
|
13581
|
+
await this.hooks.onChanged?.(id, "deleted");
|
|
13441
13582
|
return { ok: true };
|
|
13442
13583
|
};
|
|
13443
13584
|
testInstance = async (input) => {
|
|
@@ -14508,7 +14649,13 @@ var MigrationLedgerEntrySchema = object({
|
|
|
14508
14649
|
* joined by the engine from the catalogues it knows this boot. Absent when no
|
|
14509
14650
|
* catalogue lists the id (a newer database).
|
|
14510
14651
|
*/
|
|
14511
|
-
meta: MigrationMetaSchema.optional()
|
|
14652
|
+
meta: MigrationMetaSchema.optional(),
|
|
14653
|
+
/**
|
|
14654
|
+
* Set when this is an applied row of an addon owner whose catalogue the answering
|
|
14655
|
+
* process has not loaded (the owner has not declared yet): the row is applied, the
|
|
14656
|
+
* process just cannot judge it. Never `skipped-newer` — that needs the owner loaded.
|
|
14657
|
+
*/
|
|
14658
|
+
ownerNotLoaded: boolean().optional()
|
|
14512
14659
|
});
|
|
14513
14660
|
/** A catalogued migration that is not applied, with where it stands. */
|
|
14514
14661
|
var MigrationPendingEntrySchema = object({
|
|
@@ -48887,49 +49034,6 @@ function actuator(input) {
|
|
|
48887
49034
|
};
|
|
48888
49035
|
}
|
|
48889
49036
|
new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
|
|
48890
|
-
var PowerSourceRefusalReasonSchema = _enum([
|
|
48891
|
-
"no-power-source",
|
|
48892
|
-
"switch-missing",
|
|
48893
|
-
"switch-offline",
|
|
48894
|
-
"switch-unknown",
|
|
48895
|
-
"restart-in-progress",
|
|
48896
|
-
"switch-call-failed"
|
|
48897
|
-
]);
|
|
48898
|
-
var PowerSourceActionResultSchema = discriminatedUnion("accepted", [object({ accepted: literal(true) }), object({
|
|
48899
|
-
accepted: literal(false),
|
|
48900
|
-
reason: PowerSourceRefusalReasonSchema,
|
|
48901
|
-
/** One line for the operator, naming the switch. */
|
|
48902
|
-
message: string()
|
|
48903
|
-
})]);
|
|
48904
|
-
var PowerSourceInfoSchema = object({
|
|
48905
|
-
/** False when the device has no power source linked; the rest is then empty. */
|
|
48906
|
-
linked: boolean(),
|
|
48907
|
-
switchDeviceId: number().nullable(),
|
|
48908
|
-
switchName: string().nullable(),
|
|
48909
|
-
/** The switch's on/off state, or null when it is unknown. Never false for unknown (D393). */
|
|
48910
|
-
switchOn: boolean().nullable(),
|
|
48911
|
-
switchOnline: boolean().nullable(),
|
|
48912
|
-
restartInProgress: boolean(),
|
|
48913
|
-
offSeconds: number().int()
|
|
48914
|
-
});
|
|
48915
|
-
object({
|
|
48916
|
-
/** Null while the switch has not reported: unknown is never `false` (D393). */
|
|
48917
|
-
switchOn: boolean().nullable(),
|
|
48918
|
-
offSeconds: number().int(),
|
|
48919
|
-
lastFetchedAt: number()
|
|
48920
|
-
});
|
|
48921
|
-
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;
|
|
48922
|
-
var deviceInput$2 = object({ deviceId: number().int().nonnegative() });
|
|
48923
|
-
systemMethod(deviceInput$2, PowerSourceInfoSchema, { auth: "admin" }), systemMethod(deviceInput$2, PowerSourceActionResultSchema, {
|
|
48924
|
-
kind: "mutation",
|
|
48925
|
-
auth: "admin"
|
|
48926
|
-
}), systemMethod(deviceInput$2, PowerSourceActionResultSchema, {
|
|
48927
|
-
kind: "mutation",
|
|
48928
|
-
auth: "admin"
|
|
48929
|
-
}), systemMethod(deviceInput$2, PowerSourceActionResultSchema, {
|
|
48930
|
-
kind: "mutation",
|
|
48931
|
-
auth: "admin"
|
|
48932
|
-
});
|
|
48933
49037
|
DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
|
|
48934
49038
|
DeviceType.Camera;
|
|
48935
49039
|
`${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
|
|
@@ -48957,6 +49061,168 @@ var ComposableCapabilitiesSchema = object({ caps: array(object({
|
|
|
48957
49061
|
}).optional()
|
|
48958
49062
|
})) });
|
|
48959
49063
|
/**
|
|
49064
|
+
* A saved Customize assignment (D936). The schema is the type: callers use
|
|
49065
|
+
* `z.infer`, they do not restate the shape.
|
|
49066
|
+
*/
|
|
49067
|
+
var OverrideSourceSchema = object({
|
|
49068
|
+
addonId: string().min(1),
|
|
49069
|
+
stableId: string().min(1),
|
|
49070
|
+
cap: string().min(1),
|
|
49071
|
+
fieldPath: string().min(1)
|
|
49072
|
+
});
|
|
49073
|
+
var IdentityTransformDraftSchema = object({ kind: literal("identity") });
|
|
49074
|
+
var InvertTransformDraftSchema = object({ kind: literal("invert") });
|
|
49075
|
+
var ThresholdTransformDraftSchema = object({
|
|
49076
|
+
kind: literal("threshold"),
|
|
49077
|
+
comparison: _enum(["gte", "lte"]),
|
|
49078
|
+
threshold: number(),
|
|
49079
|
+
startDwellMs: number().nonnegative(),
|
|
49080
|
+
stopDwellMs: number().nonnegative()
|
|
49081
|
+
});
|
|
49082
|
+
var CustomizeTransformDraftSchema = discriminatedUnion("kind", [
|
|
49083
|
+
IdentityTransformDraftSchema,
|
|
49084
|
+
InvertTransformDraftSchema,
|
|
49085
|
+
ThresholdTransformDraftSchema,
|
|
49086
|
+
object({
|
|
49087
|
+
kind: literal("linear"),
|
|
49088
|
+
scale: number(),
|
|
49089
|
+
offset: number(),
|
|
49090
|
+
clampMin: number().optional(),
|
|
49091
|
+
clampMax: number().optional()
|
|
49092
|
+
}),
|
|
49093
|
+
object({
|
|
49094
|
+
kind: literal("enum-map"),
|
|
49095
|
+
mapping: record(string(), string().nullable())
|
|
49096
|
+
}),
|
|
49097
|
+
object({ kind: literal("number") })
|
|
49098
|
+
]);
|
|
49099
|
+
var ScalarOverrideDraftSchema = object({
|
|
49100
|
+
kind: literal("scalar"),
|
|
49101
|
+
cap: string().min(1),
|
|
49102
|
+
targetId: string().min(1),
|
|
49103
|
+
source: OverrideSourceSchema,
|
|
49104
|
+
transform: CustomizeTransformDraftSchema
|
|
49105
|
+
});
|
|
49106
|
+
var KeyedArrayOverrideDraftSchema = object({
|
|
49107
|
+
kind: literal("keyedArray"),
|
|
49108
|
+
cap: string().min(1),
|
|
49109
|
+
targetId: string().min(1),
|
|
49110
|
+
key: string().min(1),
|
|
49111
|
+
label: string().min(1),
|
|
49112
|
+
fieldPath: string().min(1),
|
|
49113
|
+
source: OverrideSourceSchema,
|
|
49114
|
+
transform: CustomizeTransformDraftSchema
|
|
49115
|
+
});
|
|
49116
|
+
var PulseOverrideDraftSchema = object({
|
|
49117
|
+
kind: literal("pulse"),
|
|
49118
|
+
cap: string().min(1),
|
|
49119
|
+
targetId: string().min(1),
|
|
49120
|
+
enabled: boolean(),
|
|
49121
|
+
debounceMs: number().nonnegative(),
|
|
49122
|
+
edges: array(_enum([
|
|
49123
|
+
"rising",
|
|
49124
|
+
"button",
|
|
49125
|
+
"native-event"
|
|
49126
|
+
])).min(1).optional(),
|
|
49127
|
+
sources: array(OverrideSourceSchema).min(1)
|
|
49128
|
+
});
|
|
49129
|
+
var SpanSourceDraftSchema = OverrideSourceSchema.extend({ mode: _enum([
|
|
49130
|
+
"in-addition",
|
|
49131
|
+
"confirm",
|
|
49132
|
+
"replace"
|
|
49133
|
+
]).optional() });
|
|
49134
|
+
discriminatedUnion("kind", [
|
|
49135
|
+
ScalarOverrideDraftSchema,
|
|
49136
|
+
KeyedArrayOverrideDraftSchema,
|
|
49137
|
+
PulseOverrideDraftSchema,
|
|
49138
|
+
object({
|
|
49139
|
+
kind: literal("span"),
|
|
49140
|
+
cap: string().min(1),
|
|
49141
|
+
targetId: string().min(1),
|
|
49142
|
+
timeoutMs: number().nonnegative(),
|
|
49143
|
+
cooldownMs: number().nonnegative().optional(),
|
|
49144
|
+
sources: array(SpanSourceDraftSchema)
|
|
49145
|
+
})
|
|
49146
|
+
]);
|
|
49147
|
+
object({
|
|
49148
|
+
targetId: string().min(1),
|
|
49149
|
+
configured: boolean(),
|
|
49150
|
+
source: _enum(["native", "assigned"]),
|
|
49151
|
+
state: _enum([
|
|
49152
|
+
"active",
|
|
49153
|
+
"stale",
|
|
49154
|
+
"released",
|
|
49155
|
+
"unavailable"
|
|
49156
|
+
]),
|
|
49157
|
+
reason: string().nullable()
|
|
49158
|
+
});
|
|
49159
|
+
/**
|
|
49160
|
+
* What a running-threshold proposal says (planning#405). The schema is the
|
|
49161
|
+
* type: the algorithm, the capability method and the UI all read `z.infer`.
|
|
49162
|
+
*
|
|
49163
|
+
* A proposal is read-only advice: it carries the numbers it was derived from
|
|
49164
|
+
* (`evidence`) so the operator can judge it, and an `insufficient` answer names
|
|
49165
|
+
* WHY with a typed reason and the catalog defaults, never an invented number.
|
|
49166
|
+
*/
|
|
49167
|
+
var RunningProposalTierSchema = _enum(["raw", "5m"]);
|
|
49168
|
+
var RunningProposalConfidenceSchema = _enum([
|
|
49169
|
+
"high",
|
|
49170
|
+
"medium",
|
|
49171
|
+
"low"
|
|
49172
|
+
]);
|
|
49173
|
+
var RunningProposalInsufficientReasonSchema = _enum([
|
|
49174
|
+
"no-history",
|
|
49175
|
+
"span-too-short",
|
|
49176
|
+
"truncated",
|
|
49177
|
+
"no-idle-floor",
|
|
49178
|
+
"no-runs",
|
|
49179
|
+
"no-separation"
|
|
49180
|
+
]);
|
|
49181
|
+
var RunningProposalReplaySchema = object({
|
|
49182
|
+
/** Intervals the real dwell expression held true over the window. */
|
|
49183
|
+
intervals: number().int().nonnegative(),
|
|
49184
|
+
/** Intervals outside every run, or a second interval inside one. */
|
|
49185
|
+
extra: number().int().nonnegative(),
|
|
49186
|
+
/** Runs without an interval of their own (none, or merged into a neighbour). */
|
|
49187
|
+
missed: number().int().nonnegative()
|
|
49188
|
+
});
|
|
49189
|
+
var RunningProposalEvidenceSchema = object({
|
|
49190
|
+
tier: RunningProposalTierSchema,
|
|
49191
|
+
spanMs: number().nonnegative(),
|
|
49192
|
+
from: number(),
|
|
49193
|
+
to: number(),
|
|
49194
|
+
points: number().int().nonnegative(),
|
|
49195
|
+
runsFound: number().int().nonnegative(),
|
|
49196
|
+
pausesFound: number().int().nonnegative(),
|
|
49197
|
+
longestPauseMs: number().nonnegative(),
|
|
49198
|
+
/** Episodes shorter than ~90 s (an anti-crease pulse, a display waking): never a run, never a pause. */
|
|
49199
|
+
pulsesFound: number().int().nonnegative(),
|
|
49200
|
+
longestPulseMs: number().nonnegative(),
|
|
49201
|
+
idleFloorW: number(),
|
|
49202
|
+
noiseCeilingW: number(),
|
|
49203
|
+
plateauW: number(),
|
|
49204
|
+
replay: RunningProposalReplaySchema
|
|
49205
|
+
});
|
|
49206
|
+
var RunningProposalInsufficientEvidenceSchema = object({
|
|
49207
|
+
spanMs: number().nonnegative(),
|
|
49208
|
+
points: number().int().nonnegative(),
|
|
49209
|
+
runsFound: number().int().nonnegative()
|
|
49210
|
+
});
|
|
49211
|
+
var RunningThresholdProposalSchema = discriminatedUnion("state", [object({
|
|
49212
|
+
state: literal("proposal"),
|
|
49213
|
+
transform: ThresholdTransformDraftSchema,
|
|
49214
|
+
evidence: RunningProposalEvidenceSchema,
|
|
49215
|
+
confidence: RunningProposalConfidenceSchema,
|
|
49216
|
+
/** Why the confidence is not higher; empty when nothing held it back. */
|
|
49217
|
+
notes: array(string())
|
|
49218
|
+
}), object({
|
|
49219
|
+
state: literal("insufficient"),
|
|
49220
|
+
reason: RunningProposalInsufficientReasonSchema,
|
|
49221
|
+
evidence: RunningProposalInsufficientEvidenceSchema,
|
|
49222
|
+
/** The catalog defaults, offered as what stands when nothing better is known. */
|
|
49223
|
+
fallback: ThresholdTransformDraftSchema
|
|
49224
|
+
})]);
|
|
49225
|
+
/**
|
|
48960
49226
|
* The report of a one-shot "move this owner's plaintext secrets into the
|
|
48961
49227
|
* keychain" run (D878). COUNTS only - never a value, never a ref.
|
|
48962
49228
|
*
|
|
@@ -49246,6 +49512,14 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
|
|
|
49246
49512
|
kind: "mutation",
|
|
49247
49513
|
auth: "admin",
|
|
49248
49514
|
access: "view"
|
|
49515
|
+
}), method(object({
|
|
49516
|
+
deviceId: number().int(),
|
|
49517
|
+
cap: string().min(1),
|
|
49518
|
+
fieldPath: string().min(1),
|
|
49519
|
+
deviceType: _enum(DeviceType).optional()
|
|
49520
|
+
}), RunningThresholdProposalSchema, {
|
|
49521
|
+
auth: "admin",
|
|
49522
|
+
access: "view"
|
|
49249
49523
|
}), method(object({
|
|
49250
49524
|
blockId: string().min(1),
|
|
49251
49525
|
trace: AutomationRunTraceSchema.optional(),
|
|
@@ -50054,7 +50328,27 @@ method(object({
|
|
|
50054
50328
|
* exposes only the COMMON surface: link state, supported device
|
|
50055
50329
|
* kinds (so the UI can filter the device picker), and per-device
|
|
50056
50330
|
* expose/unexpose.
|
|
50057
|
-
|
|
50331
|
+
*
|
|
50332
|
+
* INSTANCES. One export addon can serve several independent targets (a
|
|
50333
|
+
* HomeKit bridge, a Home Assistant broker). `listInstances` names them.
|
|
50334
|
+
* `instanceId` on a READ is optional: absent = the whole addon (linked if any
|
|
50335
|
+
* enabled instance is, `exposedDeviceCount` = distinct devices, `setup` = the
|
|
50336
|
+
* default instance's); present = that instance; an id the provider does not
|
|
50337
|
+
* serve is refused with `ExportInstanceUnknownError` (`export-instance-unknown`),
|
|
50338
|
+
* never answered empty. `instanceId` on a WRITE is REQUIRED: a membership
|
|
50339
|
+
* change always names the target it changes. A single-instance provider
|
|
50340
|
+
* accepts an absent id on a read or its own id, and refuses any other.
|
|
50341
|
+
*/
|
|
50342
|
+
/** Which instance of an export addon a call addresses. Absent on a read = the whole addon. */
|
|
50343
|
+
var InstanceIdSchema$1 = string().min(1);
|
|
50344
|
+
var InstancePinSchema = object({ instanceId: InstanceIdSchema$1.optional() });
|
|
50345
|
+
/** One independent export target of an addon. */
|
|
50346
|
+
var ExportInstanceSchema = object({
|
|
50347
|
+
instanceId: InstanceIdSchema$1,
|
|
50348
|
+
name: string(),
|
|
50349
|
+
enabled: boolean(),
|
|
50350
|
+
kind: string().optional()
|
|
50351
|
+
});
|
|
50058
50352
|
var LinkStateSchema = _enum([
|
|
50059
50353
|
"unlinked",
|
|
50060
50354
|
"linked",
|
|
@@ -50105,10 +50399,14 @@ var ExposedDeviceSchema = object({
|
|
|
50105
50399
|
capabilities: array(string()).optional()
|
|
50106
50400
|
});
|
|
50107
50401
|
var ExposeInputSchema = object({
|
|
50402
|
+
instanceId: InstanceIdSchema$1,
|
|
50108
50403
|
deviceId: string(),
|
|
50109
50404
|
capabilities: array(string()).optional()
|
|
50110
50405
|
});
|
|
50111
|
-
var UnexposeInputSchema = object({
|
|
50406
|
+
var UnexposeInputSchema = object({
|
|
50407
|
+
instanceId: InstanceIdSchema$1,
|
|
50408
|
+
deviceId: string()
|
|
50409
|
+
});
|
|
50112
50410
|
/**
|
|
50113
50411
|
* What an exporter may expose besides devices (D968). An action and a
|
|
50114
50412
|
* notification target are LIST ENTRIES, never devices (D954): they enter an
|
|
@@ -50137,15 +50435,21 @@ var ExposedEntitySchema = object({
|
|
|
50137
50435
|
entity: ExportEntityRefSchema,
|
|
50138
50436
|
exposedAs: string().optional()
|
|
50139
50437
|
});
|
|
50140
|
-
var ExposeEntityInputSchema = object({
|
|
50141
|
-
|
|
50142
|
-
|
|
50438
|
+
var ExposeEntityInputSchema = object({
|
|
50439
|
+
instanceId: InstanceIdSchema$1,
|
|
50440
|
+
entity: ExportEntityRefSchema
|
|
50441
|
+
});
|
|
50442
|
+
var UnexposeEntityInputSchema = object({
|
|
50443
|
+
instanceId: InstanceIdSchema$1,
|
|
50444
|
+
entity: ExportEntityRefSchema
|
|
50445
|
+
});
|
|
50446
|
+
method(InstancePinSchema, DeviceExportStatusSchema), method(_void(), array(ExportInstanceSchema)), method(InstancePinSchema, array(DeviceKindSchema)), method(InstancePinSchema, array(ExposedDeviceSchema)), method(ExposeInputSchema, _void(), {
|
|
50143
50447
|
kind: "mutation",
|
|
50144
50448
|
auth: "admin"
|
|
50145
50449
|
}), method(UnexposeInputSchema, _void(), {
|
|
50146
50450
|
kind: "mutation",
|
|
50147
50451
|
auth: "admin"
|
|
50148
|
-
}), optionalMethod(_void(), array(ExportEntityKindSchema)), optionalMethod(
|
|
50452
|
+
}), optionalMethod(_void(), array(ExportEntityKindSchema)), optionalMethod(InstancePinSchema, array(ExposedEntitySchema)), optionalMethod(ExposeEntityInputSchema, _void(), {
|
|
50149
50453
|
kind: "mutation",
|
|
50150
50454
|
auth: "admin"
|
|
50151
50455
|
}), optionalMethod(UnexposeEntityInputSchema, _void(), {
|
|
@@ -53346,15 +53650,15 @@ var FanPresetCaptureResultSchema = object({
|
|
|
53346
53650
|
items: array(FanCaptureItemSchema),
|
|
53347
53651
|
omitted: array(FanCaptureOmissionSchema)
|
|
53348
53652
|
});
|
|
53349
|
-
var deviceInput$
|
|
53653
|
+
var deviceInput$2 = object({ deviceId: number().int().nonnegative() });
|
|
53350
53654
|
DeviceType.Fan;
|
|
53351
|
-
method(deviceInput$
|
|
53655
|
+
method(deviceInput$2, array(FanPresetSchema), { auth: "protected" }), method(deviceInput$2.extend({ presets: array(FanPresetSchema).max(12) }), FanPresetsSetResultSchema, {
|
|
53352
53656
|
kind: "mutation",
|
|
53353
53657
|
auth: "admin"
|
|
53354
|
-
}), method(deviceInput$
|
|
53658
|
+
}), method(deviceInput$2.extend({ presetId: string().min(1) }), FanPresetApplyResultSchema, {
|
|
53355
53659
|
kind: "mutation",
|
|
53356
53660
|
auth: "protected"
|
|
53357
|
-
}), method(deviceInput$
|
|
53661
|
+
}), method(deviceInput$2.extend({
|
|
53358
53662
|
fields: array(FanCaptureFieldSchema).optional(),
|
|
53359
53663
|
steps: array(ActionStepSchema).max(20).default([])
|
|
53360
53664
|
}), FanPresetCaptureResultSchema, { auth: "protected" });
|
|
@@ -53815,11 +54119,7 @@ var CreateIntegrationInputSchema = object({
|
|
|
53815
54119
|
info: record(string(), unknown()).optional(),
|
|
53816
54120
|
/** `stableId` of the discovery candidate the form was seeded from (D984). */
|
|
53817
54121
|
originStableId: string().optional(),
|
|
53818
|
-
settings: record(string(), unknown()).optional()
|
|
53819
|
-
/** Keys of `settings` the form's schema declares secret (by type) — kept in
|
|
53820
|
-
* the keychain, not the table (D787). */
|
|
53821
|
-
secretKeys: array(string()).optional(),
|
|
53822
|
-
skipRestart: boolean().optional()
|
|
54122
|
+
settings: record(string(), unknown()).optional()
|
|
53823
54123
|
});
|
|
53824
54124
|
var UpdateIntegrationInputSchema = object({
|
|
53825
54125
|
id: string(),
|
|
@@ -53840,16 +54140,6 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
|
|
|
53840
54140
|
}), method(object({ id: string() }), DeleteIntegrationResultSchema, {
|
|
53841
54141
|
kind: "mutation",
|
|
53842
54142
|
auth: "admin"
|
|
53843
|
-
}), method(object({ id: string() }), record(string(), unknown()), { auth: "admin" }), method(object({
|
|
53844
|
-
id: string(),
|
|
53845
|
-
settings: record(string(), unknown()),
|
|
53846
|
-
/** Keys the form's schema declares secret (by type) — keychain-stored (D787). */
|
|
53847
|
-
secretKeys: array(string()).optional(),
|
|
53848
|
-
/** The integration's own addon applies the change in-process and asks the hub not to respawn it. */
|
|
53849
|
-
skipRestart: boolean().optional()
|
|
53850
|
-
}), object({ success: boolean() }), {
|
|
53851
|
-
kind: "mutation",
|
|
53852
|
-
auth: "admin"
|
|
53853
54143
|
}), method(_void(), array(AvailableIntegrationTypeSchema), { auth: "admin" }), method(object({
|
|
53854
54144
|
addonId: string(),
|
|
53855
54145
|
settings: record(string(), unknown())
|
|
@@ -53878,13 +54168,65 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
|
|
|
53878
54168
|
*/
|
|
53879
54169
|
var ConfigSchemaPassthrough$3 = custom();
|
|
53880
54170
|
var IntegrationInstanceIdSchema = string().min(1);
|
|
54171
|
+
var IntegrationInstanceModeSchema = _enum(["single", "multiple"]);
|
|
54172
|
+
/**
|
|
54173
|
+
* One kind of instance a provider can create (an adapter, an account type). The sign-in and
|
|
54174
|
+
* connect fields follow the `broker` contract (`broker.cap.ts`): `signIn.basePath` is a
|
|
54175
|
+
* hub-relative route set speaking the sign-in contract, `connectPath` / `disconnectPath` open an
|
|
54176
|
+
* interactive account link (popup) and may carry `{{brokerId}}`, which the UI replaces with the
|
|
54177
|
+
* instance id.
|
|
54178
|
+
*/
|
|
54179
|
+
var IntegrationInstanceKindSchema = object({
|
|
54180
|
+
/** Stable id of the kind, sent back as `createInstance.kind`. */
|
|
54181
|
+
id: string().min(1),
|
|
54182
|
+
label: string().min(1),
|
|
54183
|
+
/** The kind signs in through a server-held interactive session. */
|
|
54184
|
+
signIn: object({
|
|
54185
|
+
basePath: string().min(1),
|
|
54186
|
+
methods: array(_enum(["device-code", "proxy"])).optional()
|
|
54187
|
+
}).optional(),
|
|
54188
|
+
/** Relative hub path to start an interactive account link of an instance of this kind. */
|
|
54189
|
+
connectPath: string().min(1).optional(),
|
|
54190
|
+
/** Relative hub path to disconnect that account. */
|
|
54191
|
+
disconnectPath: string().min(1).optional()
|
|
54192
|
+
});
|
|
53881
54193
|
var IntegrationInstanceDescriptionSchema = object({
|
|
54194
|
+
/** What the instances are for the Integrations page; see {@link IntegrationInstanceRoleSchema}. */
|
|
54195
|
+
role: _enum([
|
|
54196
|
+
"devices",
|
|
54197
|
+
"services",
|
|
54198
|
+
"both"
|
|
54199
|
+
]),
|
|
54200
|
+
/** How the devices of an instance are found; absent means `{ mode: 'none' }`. */
|
|
54201
|
+
discovery: object({
|
|
54202
|
+
mode: _enum([
|
|
54203
|
+
"none",
|
|
54204
|
+
"lan",
|
|
54205
|
+
"account"
|
|
54206
|
+
]),
|
|
54207
|
+
scopedToInstance: boolean()
|
|
54208
|
+
}).optional(),
|
|
53882
54209
|
/** `single`: at most one instance can exist (Loki). `multiple`: an account each. */
|
|
53883
|
-
instanceMode:
|
|
54210
|
+
instanceMode: IntegrationInstanceModeSchema,
|
|
53884
54211
|
/** What the instance is, for the UI (`destination`, `relay`, `account`). */
|
|
53885
54212
|
kind: string(),
|
|
53886
54213
|
/** True when `testInstance` really probes the remote. */
|
|
53887
|
-
canTest: boolean()
|
|
54214
|
+
canTest: boolean(),
|
|
54215
|
+
/**
|
|
54216
|
+
* The kinds an instance can be of. Absent or one entry: nothing to choose; more than one: the
|
|
54217
|
+
* Add flow asks which first and sends it as `createInstance.kind`.
|
|
54218
|
+
*/
|
|
54219
|
+
kinds: array(IntegrationInstanceKindSchema).optional(),
|
|
54220
|
+
/**
|
|
54221
|
+
* The provider accepts `createInstance.id` for an id that once existed (an adopted id, because
|
|
54222
|
+
* devices carry it). The UI then offers "restore under its old id" on Add. Absent means no.
|
|
54223
|
+
*/
|
|
54224
|
+
allowRestoreId: boolean().optional(),
|
|
54225
|
+
/**
|
|
54226
|
+
* `createInstance` without an `id` works: the provider mints it. Absent means the caller must
|
|
54227
|
+
* send an id (the UI then mints an opaque one).
|
|
54228
|
+
*/
|
|
54229
|
+
mintsId: boolean().optional()
|
|
53888
54230
|
});
|
|
53889
54231
|
var IntegrationInstanceStatusSchema = object({
|
|
53890
54232
|
state: _enum([
|
|
@@ -53893,7 +54235,9 @@ var IntegrationInstanceStatusSchema = object({
|
|
|
53893
54235
|
"error",
|
|
53894
54236
|
"unknown"
|
|
53895
54237
|
]),
|
|
53896
|
-
detail: string().optional()
|
|
54238
|
+
detail: string().optional(),
|
|
54239
|
+
/** A short human word for the state ("Signed in", "Token expired"); shown instead of `state`. */
|
|
54240
|
+
label: string().optional()
|
|
53897
54241
|
});
|
|
53898
54242
|
var IntegrationInstanceSchema = object({
|
|
53899
54243
|
id: IntegrationInstanceIdSchema,
|
|
@@ -53901,13 +54245,18 @@ var IntegrationInstanceSchema = object({
|
|
|
53901
54245
|
/** The addon's own rule (an URL is present, a client id is present...). */
|
|
53902
54246
|
configured: boolean(),
|
|
53903
54247
|
/**
|
|
53904
|
-
* The instance's own on/off switch (D996)
|
|
53905
|
-
*
|
|
54248
|
+
* The instance's own on/off switch (D996), derived from the record body (`enabled !== false`,
|
|
54249
|
+
* so a row written before the field existed is on). Absent when the `integrations` registry
|
|
54250
|
+
* owns the switch (`describe().role` is `devices`): read it from the registry row.
|
|
53906
54251
|
*/
|
|
53907
|
-
enabled: boolean(),
|
|
54252
|
+
enabled: boolean().optional(),
|
|
54253
|
+
/** The kind of this instance (one of `describe().kinds[].id`), when the provider has kinds. */
|
|
54254
|
+
kind: string().optional(),
|
|
53908
54255
|
/** Values for the instance form; a secret is a `SecretFieldView`, never text. */
|
|
53909
54256
|
formValues: record(string(), unknown()),
|
|
53910
|
-
status: IntegrationInstanceStatusSchema.optional()
|
|
54257
|
+
status: IntegrationInstanceStatusSchema.optional(),
|
|
54258
|
+
/** Epoch ms of the last probe of the remote that produced `status`; absent when never checked. */
|
|
54259
|
+
lastCheckedAt: number().optional()
|
|
53911
54260
|
});
|
|
53912
54261
|
var IntegrationInstancePatchSchema = object({
|
|
53913
54262
|
id: IntegrationInstanceIdSchema,
|
|
@@ -53925,10 +54274,14 @@ var integrationInstancesCapability = {
|
|
|
53925
54274
|
instanceFormSchema: method(object({ id: IntegrationInstanceIdSchema.nullable() }), ConfigSchemaPassthrough$3, { auth: "admin" }),
|
|
53926
54275
|
listInstances: method(_void(), array(IntegrationInstanceSchema).readonly(), { auth: "admin" }),
|
|
53927
54276
|
getInstance: method(object({ id: IntegrationInstanceIdSchema }), IntegrationInstanceSchema.nullable(), { auth: "admin" }),
|
|
53928
|
-
/**
|
|
54277
|
+
/**
|
|
54278
|
+
* Insert the typed record. With `id` it is stored under that id (a duplicate id is refused);
|
|
54279
|
+
* without one the PROVIDER mints it and answers it. `kind` picks one of `describe().kinds`.
|
|
54280
|
+
*/
|
|
53929
54281
|
createInstance: method(object({
|
|
53930
|
-
id: IntegrationInstanceIdSchema,
|
|
53931
|
-
name: string().min(1)
|
|
54282
|
+
id: IntegrationInstanceIdSchema.optional(),
|
|
54283
|
+
name: string().min(1),
|
|
54284
|
+
kind: string().min(1).optional()
|
|
53932
54285
|
}), object({ id: string() }), {
|
|
53933
54286
|
kind: "mutation",
|
|
53934
54287
|
auth: "admin"
|
|
@@ -55760,6 +56113,11 @@ var LogEntrySchema = object({
|
|
|
55760
56113
|
* flight, and not asking for the next slice ends the scan.
|
|
55761
56114
|
*/
|
|
55762
56115
|
var LogReadQuerySchema = object({
|
|
56116
|
+
/**
|
|
56117
|
+
* Which destination INSTANCE answers (a destination addon may hold several records). Absent ⇒
|
|
56118
|
+
* the provider's first readable one, so a caller written before instances existed is unchanged.
|
|
56119
|
+
*/
|
|
56120
|
+
instanceId: string().min(1).max(200).optional(),
|
|
55763
56121
|
/** Correlates `cancelRead`. Caller-chosen, unique per in-flight read. */
|
|
55764
56122
|
requestId: string().min(1).max(64).optional(),
|
|
55765
56123
|
/** The device the line is about (`tags.deviceId`, numeric). */
|
|
@@ -55822,6 +56180,8 @@ var LogReadResultSchema = object({
|
|
|
55822
56180
|
/** True when `cancelRead` ended this slice early. */
|
|
55823
56181
|
cancelled: boolean().optional()
|
|
55824
56182
|
});
|
|
56183
|
+
/** Names one destination instance; absent ⇒ the provider's first readable one. */
|
|
56184
|
+
var LogInstanceSelectorSchema = object({ instanceId: string().min(1).max(200).optional() });
|
|
55825
56185
|
var LogReadCapabilitiesSchema = object({
|
|
55826
56186
|
/** False ⇒ this destination only ships lines (console, forwarder, an unconfigured Loki). */
|
|
55827
56187
|
readable: boolean(),
|
|
@@ -55840,7 +56200,7 @@ var LogReadCapabilitiesSchema = object({
|
|
|
55840
56200
|
/** How far back the store reaches, when the provider knows (days). */
|
|
55841
56201
|
retentionDays: number().nullable()
|
|
55842
56202
|
});
|
|
55843
|
-
method(LogEntrySchema, _void(), { kind: "mutation" }), optionalMethod(
|
|
56203
|
+
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() }), {
|
|
55844
56204
|
kind: "mutation",
|
|
55845
56205
|
auth: "admin"
|
|
55846
56206
|
});
|
|
@@ -56504,6 +56864,27 @@ method(object({}), object({ entries: array(MigrationLedgerEntrySchema) }), { aut
|
|
|
56504
56864
|
}), {
|
|
56505
56865
|
kind: "mutation",
|
|
56506
56866
|
auth: "admin"
|
|
56867
|
+
}), method(object({ integrationId: string().min(1) }), record(string(), unknown()), { auth: "admin" }), method(object({ integrationId: string().min(1) }), object({
|
|
56868
|
+
rows: number().int().nonnegative(),
|
|
56869
|
+
secrets: number().int().nonnegative()
|
|
56870
|
+
}), {
|
|
56871
|
+
kind: "mutation",
|
|
56872
|
+
auth: "admin"
|
|
56873
|
+
}), method(object({
|
|
56874
|
+
integrationId: string().min(1),
|
|
56875
|
+
patch: record(string(), unknown())
|
|
56876
|
+
}), object({ written: boolean() }), {
|
|
56877
|
+
kind: "mutation",
|
|
56878
|
+
auth: "admin"
|
|
56879
|
+
}), method(object({
|
|
56880
|
+
integrationId: string().min(1),
|
|
56881
|
+
keys: array(string().min(1))
|
|
56882
|
+
}), object({
|
|
56883
|
+
present: boolean(),
|
|
56884
|
+
removed: record(string(), unknown())
|
|
56885
|
+
}), {
|
|
56886
|
+
kind: "mutation",
|
|
56887
|
+
auth: "admin"
|
|
56507
56888
|
});
|
|
56508
56889
|
method(object({
|
|
56509
56890
|
sourceUrl: string(),
|
|
@@ -67431,6 +67812,49 @@ object({
|
|
|
67431
67812
|
DeviceType.PetFeeder,
|
|
67432
67813
|
DeviceType.Heater
|
|
67433
67814
|
].filter((type) => type !== DeviceType.Sensor);
|
|
67815
|
+
var PowerSourceRefusalReasonSchema = _enum([
|
|
67816
|
+
"no-power-source",
|
|
67817
|
+
"switch-missing",
|
|
67818
|
+
"switch-offline",
|
|
67819
|
+
"switch-unknown",
|
|
67820
|
+
"restart-in-progress",
|
|
67821
|
+
"switch-call-failed"
|
|
67822
|
+
]);
|
|
67823
|
+
var PowerSourceActionResultSchema = discriminatedUnion("accepted", [object({ accepted: literal(true) }), object({
|
|
67824
|
+
accepted: literal(false),
|
|
67825
|
+
reason: PowerSourceRefusalReasonSchema,
|
|
67826
|
+
/** One line for the operator, naming the switch. */
|
|
67827
|
+
message: string()
|
|
67828
|
+
})]);
|
|
67829
|
+
var PowerSourceInfoSchema = object({
|
|
67830
|
+
/** False when the device has no power source linked; the rest is then empty. */
|
|
67831
|
+
linked: boolean(),
|
|
67832
|
+
switchDeviceId: number().nullable(),
|
|
67833
|
+
switchName: string().nullable(),
|
|
67834
|
+
/** The switch's on/off state, or null when it is unknown. Never false for unknown (D393). */
|
|
67835
|
+
switchOn: boolean().nullable(),
|
|
67836
|
+
switchOnline: boolean().nullable(),
|
|
67837
|
+
restartInProgress: boolean(),
|
|
67838
|
+
offSeconds: number().int()
|
|
67839
|
+
});
|
|
67840
|
+
object({
|
|
67841
|
+
/** Null while the switch has not reported: unknown is never `false` (D393). */
|
|
67842
|
+
switchOn: boolean().nullable(),
|
|
67843
|
+
offSeconds: number().int(),
|
|
67844
|
+
lastFetchedAt: number()
|
|
67845
|
+
});
|
|
67846
|
+
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;
|
|
67847
|
+
var deviceInput$1 = object({ deviceId: number().int().nonnegative() });
|
|
67848
|
+
systemMethod(deviceInput$1, PowerSourceInfoSchema, { auth: "admin" }), systemMethod(deviceInput$1, PowerSourceActionResultSchema, {
|
|
67849
|
+
kind: "mutation",
|
|
67850
|
+
auth: "admin"
|
|
67851
|
+
}), systemMethod(deviceInput$1, PowerSourceActionResultSchema, {
|
|
67852
|
+
kind: "mutation",
|
|
67853
|
+
auth: "admin"
|
|
67854
|
+
}), systemMethod(deviceInput$1, PowerSourceActionResultSchema, {
|
|
67855
|
+
kind: "mutation",
|
|
67856
|
+
auth: "admin"
|
|
67857
|
+
});
|
|
67434
67858
|
/**
|
|
67435
67859
|
* Presence cap. Models HA `person.*` and `device_tracker.*` entities
|
|
67436
67860
|
* on `DeviceType.Presence`. Read-only — no setters: presence is
|
|
@@ -75484,6 +75908,25 @@ function buildDescriptor(capName, kind) {
|
|
|
75484
75908
|
* contributes one entry per bound cap present in this map.
|
|
75485
75909
|
*/
|
|
75486
75910
|
var EVENT_KIND_BY_CAP = Object.freeze(Object.fromEntries(Object.entries(CAP_TO_KIND).map(([capName, kind]) => [capName, buildDescriptor(capName, kind)])));
|
|
75911
|
+
/**
|
|
75912
|
+
* What a remote key is CALLED and what it LOOKS like, for every role (D963),
|
|
75913
|
+
* shared by the admin UI and the viewer. A role is data on the wire; each
|
|
75914
|
+
* client resolves its name from `label` (English and Italian side by side) and
|
|
75915
|
+
* its glyph from `icon`, a lucide id from the shared icon catalogue — the
|
|
75916
|
+
* server sends an id, a client maps it to its own icon set.
|
|
75917
|
+
*
|
|
75918
|
+
* `legend` is text printed on the key INSTEAD of a glyph (a digit has no
|
|
75919
|
+
* pictogram): a client that can draw text shows it, one that cannot falls back
|
|
75920
|
+
* to `icon`.
|
|
75921
|
+
*/
|
|
75922
|
+
function digit(n) {
|
|
75923
|
+
return {
|
|
75924
|
+
label: t(n, n),
|
|
75925
|
+
icon: "hash",
|
|
75926
|
+
legend: n
|
|
75927
|
+
};
|
|
75928
|
+
}
|
|
75929
|
+
digit("0"), digit("1"), digit("2"), digit("3"), digit("4"), digit("5"), digit("6"), digit("7"), digit("8"), digit("9");
|
|
75487
75930
|
DeviceFeature.MediaPlayerPower, DeviceFeature.MediaPlayerVolumeStep, DeviceFeature.MediaPlayerChannelStep, DeviceFeature.MediaPlayerMute, DeviceFeature.MediaPlayerStop, DeviceFeature.MediaPlayerNext, DeviceFeature.MediaPlayerPrevious;
|
|
75488
75931
|
var btn = (idSuffix, row, col, init) => ({
|
|
75489
75932
|
idSuffix,
|
|
@@ -75611,6 +76054,42 @@ discriminatedUnion("kind", [object({
|
|
|
75611
76054
|
kind: literal("pronto"),
|
|
75612
76055
|
code: string()
|
|
75613
76056
|
})]);
|
|
76057
|
+
/**
|
|
76058
|
+
* Propose a running threshold from a device's own power history (planning#405).
|
|
76059
|
+
*
|
|
76060
|
+
* Pure and deterministic: the same series gives the same answer. The series is
|
|
76061
|
+
* a HELD-VALUE step series — a point's value stands until the next point — which
|
|
76062
|
+
* is how device-history stores a gauge, so "time at a level" is exact, not a
|
|
76063
|
+
* count of samples.
|
|
76064
|
+
*
|
|
76065
|
+
* 1. idle floor F = time-weighted median; noise ceiling N = highest value seen
|
|
76066
|
+
* near F. A device that is not near one level most of the time has no floor.
|
|
76067
|
+
* 2. coarse activity = above max(2N, N + 5 W); consecutive stretches are episodes.
|
|
76068
|
+
* 3. an episode shorter than `PULSE_MAX_MS` is a PULSE (an anti-crease tumble, a
|
|
76069
|
+
* display waking): it is neither a run nor a pause. Among the remaining
|
|
76070
|
+
* episodes, gaps over `PAUSE_MAX_MS` always separate runs; the rest split in
|
|
76071
|
+
* two by the largest log-scale jump (>= 3x): the short ones are pauses INSIDE
|
|
76072
|
+
* a run (a soak), the long ones separate runs. A stretch under `MIN_RUN_MS`
|
|
76073
|
+
* is no run.
|
|
76074
|
+
* 4. T = geometric mean of N and the plateau's 10th percentile, snapped to a
|
|
76075
|
+
* grid a person would type; stop dwell covers the longest pause and the
|
|
76076
|
+
* kind's floor; start dwell covers the longest pulse and standby blip above T.
|
|
76077
|
+
* 5. the numbers are proved by REPLAY through the real `dwell` evaluation: each
|
|
76078
|
+
* run must hold exactly one true interval and nothing may flap outside.
|
|
76079
|
+
*/
|
|
76080
|
+
var SECOND_MS = 1e3;
|
|
76081
|
+
var MINUTE_MS$1 = 60 * SECOND_MS;
|
|
76082
|
+
24 * (60 * MINUTE_MS$1);
|
|
76083
|
+
10 * MINUTE_MS$1;
|
|
76084
|
+
30 * SECOND_MS;
|
|
76085
|
+
60 * SECOND_MS;
|
|
76086
|
+
20 * MINUTE_MS$1;
|
|
76087
|
+
90 * SECOND_MS;
|
|
76088
|
+
30 * MINUTE_MS$1;
|
|
76089
|
+
10 * MINUTE_MS$1;
|
|
76090
|
+
DeviceType.Dryer, 3 * MINUTE_MS$1, DeviceType.Dishwasher, 3 * MINUTE_MS$1, DeviceType.Washer, 10 * MINUTE_MS$1;
|
|
76091
|
+
10 * SECOND_MS;
|
|
76092
|
+
10 * SECOND_MS;
|
|
75614
76093
|
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverStoppable, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
75615
76094
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
75616
76095
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
@@ -77152,6 +77631,13 @@ Object.freeze({
|
|
|
77152
77631
|
access: "create",
|
|
77153
77632
|
role: "admin"
|
|
77154
77633
|
},
|
|
77634
|
+
"coreBlocks.proposeRunningThreshold": {
|
|
77635
|
+
capName: "core-blocks",
|
|
77636
|
+
capScope: "system",
|
|
77637
|
+
addonId: null,
|
|
77638
|
+
access: "view",
|
|
77639
|
+
role: "admin"
|
|
77640
|
+
},
|
|
77155
77641
|
"coreBlocks.reportAutomationRun": {
|
|
77156
77642
|
capName: "core-blocks",
|
|
77157
77643
|
capScope: "system",
|
|
@@ -77663,6 +78149,13 @@ Object.freeze({
|
|
|
77663
78149
|
access: "view",
|
|
77664
78150
|
role: "admin"
|
|
77665
78151
|
},
|
|
78152
|
+
"deviceExport.listInstances": {
|
|
78153
|
+
capName: "device-export",
|
|
78154
|
+
capScope: "system",
|
|
78155
|
+
addonId: null,
|
|
78156
|
+
access: "view",
|
|
78157
|
+
role: "admin"
|
|
78158
|
+
},
|
|
77666
78159
|
"deviceExport.listSupportedDeviceKinds": {
|
|
77667
78160
|
capName: "device-export",
|
|
77668
78161
|
capScope: "system",
|
|
@@ -79350,13 +79843,6 @@ Object.freeze({
|
|
|
79350
79843
|
access: "view",
|
|
79351
79844
|
role: "admin"
|
|
79352
79845
|
},
|
|
79353
|
-
"integrations.getSettings": {
|
|
79354
|
-
capName: "integrations",
|
|
79355
|
-
capScope: "system",
|
|
79356
|
-
addonId: null,
|
|
79357
|
-
access: "view",
|
|
79358
|
-
role: "admin"
|
|
79359
|
-
},
|
|
79360
79846
|
"integrations.list": {
|
|
79361
79847
|
capName: "integrations",
|
|
79362
79848
|
capScope: "system",
|
|
@@ -79364,13 +79850,6 @@ Object.freeze({
|
|
|
79364
79850
|
access: "view",
|
|
79365
79851
|
role: "admin"
|
|
79366
79852
|
},
|
|
79367
|
-
"integrations.setSettings": {
|
|
79368
|
-
capName: "integrations",
|
|
79369
|
-
capScope: "system",
|
|
79370
|
-
addonId: null,
|
|
79371
|
-
access: "create",
|
|
79372
|
-
role: "admin"
|
|
79373
|
-
},
|
|
79374
79853
|
"integrations.testConnection": {
|
|
79375
79854
|
capName: "integrations",
|
|
79376
79855
|
capScope: "system",
|
|
@@ -80365,6 +80844,13 @@ Object.freeze({
|
|
|
80365
80844
|
access: "view",
|
|
80366
80845
|
role: "admin"
|
|
80367
80846
|
},
|
|
80847
|
+
"migrations.clearLegacyIntegrationRows": {
|
|
80848
|
+
capName: "migrations",
|
|
80849
|
+
capScope: "system",
|
|
80850
|
+
addonId: null,
|
|
80851
|
+
access: "delete",
|
|
80852
|
+
role: "admin"
|
|
80853
|
+
},
|
|
80368
80854
|
"migrations.declareOwner": {
|
|
80369
80855
|
capName: "migrations",
|
|
80370
80856
|
capScope: "system",
|
|
@@ -80372,6 +80858,13 @@ Object.freeze({
|
|
|
80372
80858
|
access: "create",
|
|
80373
80859
|
role: "admin"
|
|
80374
80860
|
},
|
|
80861
|
+
"migrations.deleteLegacyIntegrationRowKeys": {
|
|
80862
|
+
capName: "migrations",
|
|
80863
|
+
capScope: "system",
|
|
80864
|
+
addonId: null,
|
|
80865
|
+
access: "delete",
|
|
80866
|
+
role: "admin"
|
|
80867
|
+
},
|
|
80375
80868
|
"migrations.dryRun": {
|
|
80376
80869
|
capName: "migrations",
|
|
80377
80870
|
capScope: "system",
|
|
@@ -80407,6 +80900,20 @@ Object.freeze({
|
|
|
80407
80900
|
access: "create",
|
|
80408
80901
|
role: "admin"
|
|
80409
80902
|
},
|
|
80903
|
+
"migrations.patchLegacyIntegrationRows": {
|
|
80904
|
+
capName: "migrations",
|
|
80905
|
+
capScope: "system",
|
|
80906
|
+
addonId: null,
|
|
80907
|
+
access: "create",
|
|
80908
|
+
role: "admin"
|
|
80909
|
+
},
|
|
80910
|
+
"migrations.readLegacyIntegrationRows": {
|
|
80911
|
+
capName: "migrations",
|
|
80912
|
+
capScope: "system",
|
|
80913
|
+
addonId: null,
|
|
80914
|
+
access: "view",
|
|
80915
|
+
role: "admin"
|
|
80916
|
+
},
|
|
80410
80917
|
"migrations.restorePreMigration": {
|
|
80411
80918
|
capName: "migrations",
|
|
80412
80919
|
capScope: "system",
|
|
@@ -86610,6 +87117,11 @@ Object.freeze({
|
|
|
86610
87117
|
form: "single",
|
|
86611
87118
|
optional: false
|
|
86612
87119
|
}],
|
|
87120
|
+
"coreBlocks.proposeRunningThreshold": [{
|
|
87121
|
+
name: "deviceId",
|
|
87122
|
+
form: "single",
|
|
87123
|
+
optional: false
|
|
87124
|
+
}],
|
|
86613
87125
|
"cover.close": [{
|
|
86614
87126
|
name: "deviceId",
|
|
86615
87127
|
form: "single",
|
|
@@ -89273,99 +89785,6 @@ Object.freeze({
|
|
|
89273
89785
|
"network-access": "ingress",
|
|
89274
89786
|
"smtp-provider": "email"
|
|
89275
89787
|
});
|
|
89276
|
-
/**
|
|
89277
|
-
* A saved Customize assignment (D936). The schema is the type: callers use
|
|
89278
|
-
* `z.infer`, they do not restate the shape.
|
|
89279
|
-
*/
|
|
89280
|
-
var OverrideSourceSchema = object({
|
|
89281
|
-
addonId: string().min(1),
|
|
89282
|
-
stableId: string().min(1),
|
|
89283
|
-
cap: string().min(1),
|
|
89284
|
-
fieldPath: string().min(1)
|
|
89285
|
-
});
|
|
89286
|
-
var CustomizeTransformDraftSchema = discriminatedUnion("kind", [
|
|
89287
|
-
object({ kind: literal("identity") }),
|
|
89288
|
-
object({ kind: literal("invert") }),
|
|
89289
|
-
object({
|
|
89290
|
-
kind: literal("threshold"),
|
|
89291
|
-
comparison: _enum(["gte", "lte"]),
|
|
89292
|
-
threshold: number(),
|
|
89293
|
-
startDwellMs: number().nonnegative(),
|
|
89294
|
-
stopDwellMs: number().nonnegative()
|
|
89295
|
-
}),
|
|
89296
|
-
object({
|
|
89297
|
-
kind: literal("linear"),
|
|
89298
|
-
scale: number(),
|
|
89299
|
-
offset: number(),
|
|
89300
|
-
clampMin: number().optional(),
|
|
89301
|
-
clampMax: number().optional()
|
|
89302
|
-
}),
|
|
89303
|
-
object({
|
|
89304
|
-
kind: literal("enum-map"),
|
|
89305
|
-
mapping: record(string(), string().nullable())
|
|
89306
|
-
}),
|
|
89307
|
-
object({ kind: literal("number") })
|
|
89308
|
-
]);
|
|
89309
|
-
var ScalarOverrideDraftSchema = object({
|
|
89310
|
-
kind: literal("scalar"),
|
|
89311
|
-
cap: string().min(1),
|
|
89312
|
-
targetId: string().min(1),
|
|
89313
|
-
source: OverrideSourceSchema,
|
|
89314
|
-
transform: CustomizeTransformDraftSchema
|
|
89315
|
-
});
|
|
89316
|
-
var KeyedArrayOverrideDraftSchema = object({
|
|
89317
|
-
kind: literal("keyedArray"),
|
|
89318
|
-
cap: string().min(1),
|
|
89319
|
-
targetId: string().min(1),
|
|
89320
|
-
key: string().min(1),
|
|
89321
|
-
label: string().min(1),
|
|
89322
|
-
fieldPath: string().min(1),
|
|
89323
|
-
source: OverrideSourceSchema,
|
|
89324
|
-
transform: CustomizeTransformDraftSchema
|
|
89325
|
-
});
|
|
89326
|
-
var PulseOverrideDraftSchema = object({
|
|
89327
|
-
kind: literal("pulse"),
|
|
89328
|
-
cap: string().min(1),
|
|
89329
|
-
targetId: string().min(1),
|
|
89330
|
-
enabled: boolean(),
|
|
89331
|
-
debounceMs: number().nonnegative(),
|
|
89332
|
-
edges: array(_enum([
|
|
89333
|
-
"rising",
|
|
89334
|
-
"button",
|
|
89335
|
-
"native-event"
|
|
89336
|
-
])).min(1).optional(),
|
|
89337
|
-
sources: array(OverrideSourceSchema).min(1)
|
|
89338
|
-
});
|
|
89339
|
-
var SpanSourceDraftSchema = OverrideSourceSchema.extend({ mode: _enum([
|
|
89340
|
-
"in-addition",
|
|
89341
|
-
"confirm",
|
|
89342
|
-
"replace"
|
|
89343
|
-
]).optional() });
|
|
89344
|
-
discriminatedUnion("kind", [
|
|
89345
|
-
ScalarOverrideDraftSchema,
|
|
89346
|
-
KeyedArrayOverrideDraftSchema,
|
|
89347
|
-
PulseOverrideDraftSchema,
|
|
89348
|
-
object({
|
|
89349
|
-
kind: literal("span"),
|
|
89350
|
-
cap: string().min(1),
|
|
89351
|
-
targetId: string().min(1),
|
|
89352
|
-
timeoutMs: number().nonnegative(),
|
|
89353
|
-
cooldownMs: number().nonnegative().optional(),
|
|
89354
|
-
sources: array(SpanSourceDraftSchema)
|
|
89355
|
-
})
|
|
89356
|
-
]);
|
|
89357
|
-
object({
|
|
89358
|
-
targetId: string().min(1),
|
|
89359
|
-
configured: boolean(),
|
|
89360
|
-
source: _enum(["native", "assigned"]),
|
|
89361
|
-
state: _enum([
|
|
89362
|
-
"active",
|
|
89363
|
-
"stale",
|
|
89364
|
-
"released",
|
|
89365
|
-
"unavailable"
|
|
89366
|
-
]),
|
|
89367
|
-
reason: string().nullable()
|
|
89368
|
-
});
|
|
89369
89788
|
var G711_SCALE_CORRECTION_DB = {
|
|
89370
89789
|
PCMU: 20 * Math.log10(4),
|
|
89371
89790
|
PCMA: 20 * Math.log10(8)
|
|
@@ -123190,7 +123609,7 @@ var MqttAclStore = class MqttAclStore {
|
|
|
123190
123609
|
* The addon has NO standalone admin page. All configuration — external
|
|
123191
123610
|
* broker entries, the embedded `aedes` broker, embedded credentials,
|
|
123192
123611
|
* and the topic ACL — is exposed through the standard
|
|
123193
|
-
* `
|
|
123612
|
+
* `clusterSettingsSchema()` and rendered automatically inside the
|
|
123194
123613
|
* Integrations → Brokers tab's docked Settings panel, like every other
|
|
123195
123614
|
* integration-type addon.
|
|
123196
123615
|
*
|
|
@@ -123377,6 +123796,7 @@ var MqttBrokerAddon = class extends BaseAddon {
|
|
|
123377
123796
|
this.aclStore = MqttAclStore.of(records);
|
|
123378
123797
|
this.instances = new RecordIntegrationInstances(records, mqttBrokersEntryKind, {
|
|
123379
123798
|
description: {
|
|
123799
|
+
role: "services",
|
|
123380
123800
|
instanceMode: "multiple",
|
|
123381
123801
|
kind: "brokers",
|
|
123382
123802
|
canTest: false
|
|
@@ -124260,11 +124680,15 @@ var MqttBrokerAddon = class extends BaseAddon {
|
|
|
124260
124680
|
}
|
|
124261
124681
|
};
|
|
124262
124682
|
}
|
|
124683
|
+
scopedSettingsCapabilities() {
|
|
124684
|
+
return ["mqtt-broker", "broker"];
|
|
124685
|
+
}
|
|
124263
124686
|
/**
|
|
124264
124687
|
* The embedded broker, its credentials and the ACL. The external brokers are records, listed
|
|
124265
124688
|
* on the Brokers page and edited as integration instances (`brokerForm`).
|
|
124266
124689
|
*/
|
|
124267
|
-
|
|
124690
|
+
async clusterSettingsSchema(cap) {
|
|
124691
|
+
if (cap !== "mqtt-broker" && cap !== "broker") return null;
|
|
124268
124692
|
return this.schema({ sections: [
|
|
124269
124693
|
{
|
|
124270
124694
|
id: "mqtt-broker-embedded",
|