@camstack/addon-smtp-nodemailer 1.2.146 → 1.2.148
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/smtp.addon.js +1321 -575
- package/dist/smtp.addon.mjs +1321 -575
- package/package.json +2 -2
package/dist/smtp.addon.mjs
CHANGED
|
@@ -822,7 +822,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
822
822
|
return EventCategory;
|
|
823
823
|
}({});
|
|
824
824
|
//#endregion
|
|
825
|
-
//#region ../types/dist/device-type-
|
|
825
|
+
//#region ../types/dist/device-type-D1fH5kaC.mjs
|
|
826
826
|
/**
|
|
827
827
|
import { errMsg } from '@camstack/types'
|
|
828
828
|
* Extract a human-readable message from an unknown error value.
|
|
@@ -1053,6 +1053,12 @@ var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
|
|
|
1053
1053
|
DeviceFeature["MediaPlayerPrevious"] = "media-player-previous";
|
|
1054
1054
|
/** Media player exposes stop distinct from pause. */
|
|
1055
1055
|
DeviceFeature["MediaPlayerStop"] = "media-player-stop";
|
|
1056
|
+
/** Media player exposes volume up / down in steps (an infrared TV has no level). */
|
|
1057
|
+
DeviceFeature["MediaPlayerVolumeStep"] = "media-player-volume-step";
|
|
1058
|
+
/** Media player exposes channel up / down in steps. */
|
|
1059
|
+
DeviceFeature["MediaPlayerChannelStep"] = "media-player-channel-step";
|
|
1060
|
+
/** Media player exposes power on / off. */
|
|
1061
|
+
DeviceFeature["MediaPlayerPower"] = "media-player-power";
|
|
1056
1062
|
/** Alarm panel requires a PIN code on arm/disarm. */
|
|
1057
1063
|
DeviceFeature["AlarmPinRequired"] = "alarm-pin-required";
|
|
1058
1064
|
/** Presence device carries GPS coordinates (lat/lng/accuracy) in
|
|
@@ -6012,7 +6018,7 @@ var ZodIssueCode = {
|
|
|
6012
6018
|
var ZodFirstPartyTypeKind;
|
|
6013
6019
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
6014
6020
|
//#endregion
|
|
6015
|
-
//#region ../types/dist/sleep-
|
|
6021
|
+
//#region ../types/dist/sleep-DEZaLbiu.mjs
|
|
6016
6022
|
/**
|
|
6017
6023
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
6018
6024
|
* window to float samples (D455).
|
|
@@ -6091,6 +6097,22 @@ function buildAlawTable() {
|
|
|
6091
6097
|
}
|
|
6092
6098
|
buildUlawTable();
|
|
6093
6099
|
buildAlawTable();
|
|
6100
|
+
/**
|
|
6101
|
+
* An addon whose own migrations did not apply refuses to initialise (D987). The error is typed
|
|
6102
|
+
* so a runner hosting several addons can tell "this addon is blocked" from a crash and keep its
|
|
6103
|
+
* co-located siblings running. Recognised by NAME and shape, never `instanceof`: a forked addon
|
|
6104
|
+
* bundles its own copy of this module, so the class the runner sees is not the one that threw.
|
|
6105
|
+
*/
|
|
6106
|
+
var MIGRATIONS_BLOCKED_ERROR_NAME = "AddonMigrationsBlockedError";
|
|
6107
|
+
var AddonMigrationsBlockedError = class extends Error {
|
|
6108
|
+
/** The ledger ids that did not apply and block the owner. */
|
|
6109
|
+
migrationIds;
|
|
6110
|
+
constructor(migrationIds) {
|
|
6111
|
+
super(`migration(s) ${migrationIds.join(", ")} did not apply and block this addon — fix or retry them (migrations.retry), then restart it`);
|
|
6112
|
+
this.name = MIGRATIONS_BLOCKED_ERROR_NAME;
|
|
6113
|
+
this.migrationIds = migrationIds;
|
|
6114
|
+
}
|
|
6115
|
+
};
|
|
6094
6116
|
/** Is this field's stored value a credential? */
|
|
6095
6117
|
function isSecretConfigField(field) {
|
|
6096
6118
|
if (field.type === "password") return true;
|
|
@@ -6596,7 +6618,7 @@ var NOT_RUN = {
|
|
|
6596
6618
|
* throws: an unreachable or refusing engine is logged and reported `declared: false`,
|
|
6597
6619
|
* because the hub (and this addon's boot) must not hang on a migration.
|
|
6598
6620
|
*/
|
|
6599
|
-
async function runAddonMigrations(host, migrations, logger) {
|
|
6621
|
+
async function runAddonMigrations(host, migrations, logger, options = {}) {
|
|
6600
6622
|
const first = migrations[0];
|
|
6601
6623
|
if (first === void 0) return NOT_RUN;
|
|
6602
6624
|
const owner = first.meta.owner;
|
|
@@ -6625,7 +6647,9 @@ async function runAddonMigrations(host, migrations, logger) {
|
|
|
6625
6647
|
else if (status === "blocked") blocked.push(id);
|
|
6626
6648
|
else skipped.push(id);
|
|
6627
6649
|
};
|
|
6650
|
+
const selected = options.only === void 0 ? null : new Set(options.only);
|
|
6628
6651
|
for (const step of declaration.steps) {
|
|
6652
|
+
if (selected !== null && !selected.has(step.id)) continue;
|
|
6629
6653
|
const bound = byId.get(step.id);
|
|
6630
6654
|
if (bound === void 0 || step.action === "skip") {
|
|
6631
6655
|
skipped.push(step.id);
|
|
@@ -6803,6 +6827,12 @@ function projectStore(store, perNodeKeys, nodeId) {
|
|
|
6803
6827
|
}
|
|
6804
6828
|
return out;
|
|
6805
6829
|
}
|
|
6830
|
+
/** Every scope, in the order the guard and the docs list them. */
|
|
6831
|
+
var SCOPED_SETTINGS_SCOPES = [
|
|
6832
|
+
"cluster",
|
|
6833
|
+
"device-defaults",
|
|
6834
|
+
"node"
|
|
6835
|
+
];
|
|
6806
6836
|
/**
|
|
6807
6837
|
* The address of one keychain value: `secret://<owner>/<scope>/<fieldPath>`.
|
|
6808
6838
|
*
|
|
@@ -7694,6 +7724,13 @@ var BaseAddon = class {
|
|
|
7694
7724
|
*/
|
|
7695
7725
|
secretStatePaths = [];
|
|
7696
7726
|
_addonSecretTree = null;
|
|
7727
|
+
/**
|
|
7728
|
+
* D986 M4: the scoped schemas this addon declared, resolved once per capability
|
|
7729
|
+
* (`loadScopedSchemas`) so the sync readers (`perNodeKeys`, the secret tree, the restart check)
|
|
7730
|
+
* see them. Empty until loaded: the readers then fall back to {@link globalSettingsSchema}.
|
|
7731
|
+
*/
|
|
7732
|
+
_scopedByCap = /* @__PURE__ */ new Map();
|
|
7733
|
+
_nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
7697
7734
|
_deviceSecretTree = null;
|
|
7698
7735
|
/** Refs the last resolved config pointed at — a change to one re-resolves. */
|
|
7699
7736
|
_configSecretRefs = /* @__PURE__ */ new Set();
|
|
@@ -7763,6 +7800,7 @@ var BaseAddon = class {
|
|
|
7763
7800
|
this._ctx = context;
|
|
7764
7801
|
this._secretSettings = null;
|
|
7765
7802
|
}
|
|
7803
|
+
await this.loadScopedSchemas();
|
|
7766
7804
|
await this.resolveConfig();
|
|
7767
7805
|
if (secrets !== void 0) this._secretChangeUnsub = secrets.onChange((ref) => {
|
|
7768
7806
|
if (this._configSecretRefs.has(ref)) this.scheduleSecretReresolve(SECRET_CHANGE_DEBOUNCE_MS);
|
|
@@ -7772,6 +7810,7 @@ var BaseAddon = class {
|
|
|
7772
7810
|
const normalized = normalizeAddonInitResult(result);
|
|
7773
7811
|
const providers = normalized && "providers" in normalized && normalized.providers ? normalized.providers : [];
|
|
7774
7812
|
this._registeredCapNames = providers.map((p) => p.capability.name);
|
|
7813
|
+
await this.loadScopedSchemas();
|
|
7775
7814
|
return normalized;
|
|
7776
7815
|
}
|
|
7777
7816
|
/**
|
|
@@ -7785,15 +7824,19 @@ var BaseAddon = class {
|
|
|
7785
7824
|
* nothing; the boot continues. A migration that FAILED (or was blocked by one)
|
|
7786
7825
|
* and declares `onFailure: 'block-owner'` makes this throw, so the addon does not
|
|
7787
7826
|
* serve data whose shape it cannot trust; `tolerate` migrations never do.
|
|
7827
|
+
*
|
|
7828
|
+
* ALWAYS pass the whole catalogue; a phase that must run only some of it passes
|
|
7829
|
+
* `{ only: [ids] }` — never a filtered catalogue (the engine would judge the
|
|
7830
|
+
* omitted applied ids "newer").
|
|
7788
7831
|
*/
|
|
7789
|
-
async runMigrations(migrations) {
|
|
7832
|
+
async runMigrations(migrations, options) {
|
|
7790
7833
|
const api = this.ctx.api.migrations;
|
|
7791
7834
|
const report = await runAddonMigrations({
|
|
7792
7835
|
declareOwner: (input) => api.declareOwner.mutate(input),
|
|
7793
7836
|
startMigration: (input) => api.startMigration.mutate(input),
|
|
7794
7837
|
finishMigration: (input) => api.finishMigration.mutate(input)
|
|
7795
|
-
}, migrations, this.ctx.logger);
|
|
7796
|
-
if (report.blockOwner.length > 0) throw new
|
|
7838
|
+
}, migrations, this.ctx.logger, options);
|
|
7839
|
+
if (report.blockOwner.length > 0) throw new AddonMigrationsBlockedError(report.blockOwner);
|
|
7797
7840
|
return report;
|
|
7798
7841
|
}
|
|
7799
7842
|
/**
|
|
@@ -7856,6 +7899,16 @@ var BaseAddon = class {
|
|
|
7856
7899
|
schema(schema) {
|
|
7857
7900
|
return schema;
|
|
7858
7901
|
}
|
|
7902
|
+
/**
|
|
7903
|
+
* The capability names whose scoped settings schemas ({@link clusterSettingsSchema},
|
|
7904
|
+
* {@link nodeSettingsSchema}, {@link deviceDefaultsSchema}) this addon declares. Read BEFORE
|
|
7905
|
+
* `onInitialize`, when the providers it registers are not known yet: the first config resolve
|
|
7906
|
+
* must already know which keys are per node. Names the providers register at init are added
|
|
7907
|
+
* once known.
|
|
7908
|
+
*/
|
|
7909
|
+
scopedSettingsCapabilities() {
|
|
7910
|
+
return [];
|
|
7911
|
+
}
|
|
7859
7912
|
/** Override to provide global-level settings UI schema. */
|
|
7860
7913
|
globalSettingsSchema(_cap) {
|
|
7861
7914
|
return null;
|
|
@@ -7899,7 +7952,8 @@ var BaseAddon = class {
|
|
|
7899
7952
|
return [];
|
|
7900
7953
|
}
|
|
7901
7954
|
async getGlobalSettings(overlay, cap, nodeId) {
|
|
7902
|
-
|
|
7955
|
+
await this.loadScopedSchemas(cap);
|
|
7956
|
+
const schema = this.globalSettingsSchema(cap) ?? this.scopedFormSchema(cap);
|
|
7903
7957
|
if (!schema) return { sections: [] };
|
|
7904
7958
|
const projected = await this.readRawProjectedStore(nodeId, cap);
|
|
7905
7959
|
return hydrateSchema(schema, overlay ? await this.overlayKeepingMarkers(projected, overlay) : projected, { inlineSecretsAsViews: true });
|
|
@@ -7934,10 +7988,9 @@ var BaseAddon = class {
|
|
|
7934
7988
|
*
|
|
7935
7989
|
* Override it only when an `instanceMode: multiple` addon's instances differ: an NVR integration
|
|
7936
7990
|
* needs its connection fields, the default integration the addon's own sections. It is the
|
|
7937
|
-
* SHAPE of the form only — the values live
|
|
7938
|
-
* unchanged (`integrations.setSettings` for an account integration).
|
|
7991
|
+
* SHAPE of the form only — the values live on the instance record.
|
|
7939
7992
|
*/
|
|
7940
|
-
async
|
|
7993
|
+
async integrationFormSchema(_instance) {
|
|
7941
7994
|
return null;
|
|
7942
7995
|
}
|
|
7943
7996
|
/**
|
|
@@ -7983,7 +8036,7 @@ var BaseAddon = class {
|
|
|
7983
8036
|
* per-instance schema when it declares one, else its global schema (what they always showed).
|
|
7984
8037
|
*/
|
|
7985
8038
|
async getIntegrationFormSchema(instance, nodeId) {
|
|
7986
|
-
const scoped = await this.
|
|
8039
|
+
const scoped = await this.integrationFormSchema(instance);
|
|
7987
8040
|
if (scoped === null) return this.getGlobalSettings(void 0, void 0, nodeId);
|
|
7988
8041
|
return hydrateSchema(scoped, {}, { inlineSecretsAsViews: true });
|
|
7989
8042
|
}
|
|
@@ -8031,6 +8084,7 @@ var BaseAddon = class {
|
|
|
8031
8084
|
* resolved blob back is a no-op for every unchanged secret.
|
|
8032
8085
|
*/
|
|
8033
8086
|
async resolveGlobalStore(nodeId, cap) {
|
|
8087
|
+
await this.loadScopedSchemas(cap);
|
|
8034
8088
|
const raw = await this._ctx?.settings?.readAddonStore() ?? {};
|
|
8035
8089
|
return this.projectForNode(raw, nodeId, cap);
|
|
8036
8090
|
}
|
|
@@ -8041,6 +8095,7 @@ var BaseAddon = class {
|
|
|
8041
8095
|
}
|
|
8042
8096
|
/** The PROJECTED store with its markers — the form hydrate only (D798). */
|
|
8043
8097
|
async readRawProjectedStore(nodeId, cap) {
|
|
8098
|
+
await this.loadScopedSchemas(cap);
|
|
8044
8099
|
const raw = await this.rawSettings()?.readAddonStore() ?? {};
|
|
8045
8100
|
return this.projectForNode(raw, nodeId, cap);
|
|
8046
8101
|
}
|
|
@@ -8133,12 +8188,17 @@ var BaseAddon = class {
|
|
|
8133
8188
|
const stored = keychain ? await this.readAddonStoreWithRetry() : {};
|
|
8134
8189
|
const patch = {};
|
|
8135
8190
|
for (const key of keys) {
|
|
8191
|
+
if (tree.get(key)?.kind === "array") {
|
|
8192
|
+
patch[key] = [];
|
|
8193
|
+
continue;
|
|
8194
|
+
}
|
|
8136
8195
|
const keychainStored = tree.has(key) || collectSecretMarkerRefs(stored[key]).length > 0;
|
|
8137
8196
|
patch[key] = keychain && keychainStored ? SECRET_CLEAR : "";
|
|
8138
8197
|
}
|
|
8139
8198
|
await this.applyGlobalPatch(patch, nodeId);
|
|
8140
8199
|
}
|
|
8141
8200
|
async applyGlobalPatch(barePatch, nodeId) {
|
|
8201
|
+
await this.loadScopedSchemas();
|
|
8142
8202
|
const keys = this.perNodeKeys();
|
|
8143
8203
|
const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
|
|
8144
8204
|
const target = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
|
|
@@ -8152,7 +8212,8 @@ var BaseAddon = class {
|
|
|
8152
8212
|
await this.resolveConfig();
|
|
8153
8213
|
await this.onConfigChanged();
|
|
8154
8214
|
this.emitLifecycle(EventCategory.AddonUpdated, { level: "global" });
|
|
8155
|
-
|
|
8215
|
+
const legacy = this.globalSettingsSchema();
|
|
8216
|
+
this.maybeAutoRestart(Object.keys(barePatch), [...legacy === null ? [] : [legacy], ...this.loadedScopedSchemas().map((entry) => entry.schema)]);
|
|
8156
8217
|
}
|
|
8157
8218
|
/**
|
|
8158
8219
|
* The set of field keys the global settings schema declares `perNode: true`
|
|
@@ -8160,14 +8221,67 @@ var BaseAddon = class {
|
|
|
8160
8221
|
* declarations). Empty set ⇒ every per-node code path is bypassed and the
|
|
8161
8222
|
* settings API behaves exactly like the legacy node-agnostic one.
|
|
8162
8223
|
*/
|
|
8163
|
-
|
|
8224
|
+
/**
|
|
8225
|
+
* Load the scoped schemas of every capability this addon declares (the override and the
|
|
8226
|
+
* providers registered at init, plus `extraCap`) that is not loaded yet, then drop what was
|
|
8227
|
+
* derived from the previous set. Idempotent.
|
|
8228
|
+
*/
|
|
8229
|
+
async loadScopedSchemas(extraCap) {
|
|
8230
|
+
const caps = new Set([
|
|
8231
|
+
...this.scopedSettingsCapabilities(),
|
|
8232
|
+
...this._registeredCapNames,
|
|
8233
|
+
...extraCap === void 0 ? [] : [extraCap]
|
|
8234
|
+
]);
|
|
8235
|
+
let added = false;
|
|
8236
|
+
for (const cap of caps) {
|
|
8237
|
+
if (this._scopedByCap.has(cap)) continue;
|
|
8238
|
+
const loaded = [];
|
|
8239
|
+
for (const scope of SCOPED_SETTINGS_SCOPES) {
|
|
8240
|
+
const schema = await this.getScopedSettingsSchema(scope, cap);
|
|
8241
|
+
if (schema !== null) loaded.push({
|
|
8242
|
+
scope,
|
|
8243
|
+
schema
|
|
8244
|
+
});
|
|
8245
|
+
}
|
|
8246
|
+
this._scopedByCap.set(cap, loaded);
|
|
8247
|
+
added = true;
|
|
8248
|
+
}
|
|
8249
|
+
if (!added) return;
|
|
8250
|
+
this._nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
8251
|
+
this._addonSecretTree = null;
|
|
8252
|
+
}
|
|
8253
|
+
/**
|
|
8254
|
+
* The form the settings API serves while an addon declares no {@link globalSettingsSchema}: the
|
|
8255
|
+
* sections of its scoped schemas, so dropping the old hook does not blank the form.
|
|
8256
|
+
*/
|
|
8257
|
+
scopedFormSchema(cap) {
|
|
8258
|
+
const byId = /* @__PURE__ */ new Map();
|
|
8259
|
+
for (const entry of this.loadedScopedSchemas(cap)) for (const section of entry.schema.sections) if (!byId.has(section.id)) byId.set(section.id, section);
|
|
8260
|
+
return byId.size === 0 ? null : { sections: [...byId.values()] };
|
|
8261
|
+
}
|
|
8262
|
+
/** Every loaded scoped schema of `cap`, or of every loaded capability. */
|
|
8263
|
+
loadedScopedSchemas(cap) {
|
|
8264
|
+
if (cap !== void 0) return this._scopedByCap.get(cap) ?? [];
|
|
8265
|
+
return [...this._scopedByCap.values()].flat();
|
|
8266
|
+
}
|
|
8267
|
+
/**
|
|
8268
|
+
* The field keys that are stored per node: every field of a {@link nodeSettingsSchema}, plus
|
|
8269
|
+
* the fields the legacy {@link globalSettingsSchema} still marks `perNode: true`. Derived once
|
|
8270
|
+
* per `cap` argument and memoized (schemas are static declarations). Empty set ⇒ every
|
|
8271
|
+
* per-node code path is bypassed and the settings API behaves like the node-agnostic one.
|
|
8272
|
+
* Call {@link loadScopedSchemas} first (the async seams do).
|
|
8273
|
+
*/
|
|
8164
8274
|
perNodeKeys(cap) {
|
|
8165
8275
|
const cacheKey = cap ?? "";
|
|
8166
|
-
const cached = this.
|
|
8276
|
+
const cached = this._nodeKeysByCap.get(cacheKey);
|
|
8167
8277
|
if (cached) return cached;
|
|
8168
|
-
const
|
|
8169
|
-
const keys = new Set(
|
|
8170
|
-
this.
|
|
8278
|
+
const legacy = this.globalSettingsSchema(cap);
|
|
8279
|
+
const keys = new Set(legacy ? legacy.sections.flatMap((section) => collectPerNodeFieldKeys(section.fields)) : []);
|
|
8280
|
+
for (const { scope, schema } of this.loadedScopedSchemas(cap)) {
|
|
8281
|
+
if (scope !== "node") continue;
|
|
8282
|
+
for (const section of schema.sections) for (const key of collectValueFieldKeys(section.fields)) keys.add(key);
|
|
8283
|
+
}
|
|
8284
|
+
this._nodeKeysByCap.set(cacheKey, keys);
|
|
8171
8285
|
return keys;
|
|
8172
8286
|
}
|
|
8173
8287
|
/**
|
|
@@ -8177,10 +8291,9 @@ var BaseAddon = class {
|
|
|
8177
8291
|
* time to return its response before the addon is torn down and
|
|
8178
8292
|
* re-initialised by `AddonRegistryService.restartAddon`.
|
|
8179
8293
|
*/
|
|
8180
|
-
maybeAutoRestart(patchKeys,
|
|
8181
|
-
if (!schema) return;
|
|
8294
|
+
maybeAutoRestart(patchKeys, schemas) {
|
|
8182
8295
|
const restartKeys = /* @__PURE__ */ new Set();
|
|
8183
|
-
for (const section of schema.sections) for (const field of section.fields) {
|
|
8296
|
+
for (const schema of schemas) for (const section of schema.sections) for (const field of section.fields) {
|
|
8184
8297
|
if (field.type === "separator" || field.type === "info") continue;
|
|
8185
8298
|
if (field.requiresRestart) restartKeys.add(field.key);
|
|
8186
8299
|
}
|
|
@@ -8376,12 +8489,14 @@ var BaseAddon = class {
|
|
|
8376
8489
|
* here, in the addon's own process, so the hub keeps no copy of it.
|
|
8377
8490
|
*/
|
|
8378
8491
|
async getDeclaredSettingsKeys() {
|
|
8492
|
+
await this.loadScopedSchemas();
|
|
8379
8493
|
return { keys: [...this.declaredKeySet()].toSorted() };
|
|
8380
8494
|
}
|
|
8381
8495
|
declaredKeySet() {
|
|
8382
8496
|
const keys = new Set(Object.keys(this.defaults));
|
|
8383
8497
|
const schema = this.globalSettingsSchema();
|
|
8384
8498
|
if (schema) for (const section of schema.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
8499
|
+
for (const { schema: scoped } of this.loadedScopedSchemas()) for (const section of scoped.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
8385
8500
|
for (const key of this._declaredStateKeys) keys.add(key);
|
|
8386
8501
|
for (const path of this.secretStatePaths) {
|
|
8387
8502
|
const root = path.split(/[.[]/, 1)[0];
|
|
@@ -8477,7 +8592,11 @@ var BaseAddon = class {
|
|
|
8477
8592
|
}
|
|
8478
8593
|
/** The addon store's secret tree: schema fields by TYPE, plus `secretStatePaths`. */
|
|
8479
8594
|
addonSecretTree() {
|
|
8480
|
-
this._addonSecretTree
|
|
8595
|
+
if (this._addonSecretTree === null) {
|
|
8596
|
+
const tree = new Map(secretTreeFromSchema(this.globalSettingsSchema()));
|
|
8597
|
+
for (const { schema } of this.loadedScopedSchemas()) for (const [key, node] of secretTreeFromSchema(schema)) tree.set(key, node);
|
|
8598
|
+
this._addonSecretTree = mergeSecretStatePaths(tree, this.secretStatePaths);
|
|
8599
|
+
}
|
|
8481
8600
|
return this._addonSecretTree;
|
|
8482
8601
|
}
|
|
8483
8602
|
deviceSecretTree() {
|
|
@@ -8727,6 +8846,23 @@ function dropPerNodeFields(fields) {
|
|
|
8727
8846
|
}
|
|
8728
8847
|
return kept;
|
|
8729
8848
|
}
|
|
8849
|
+
/** The keys of the value-holding fields, recursing into layout containers. */
|
|
8850
|
+
function collectValueFieldKeys(fields) {
|
|
8851
|
+
const collected = [];
|
|
8852
|
+
for (const field of fields) {
|
|
8853
|
+
if (field.type === "group") {
|
|
8854
|
+
collected.push(...collectValueFieldKeys(field.fields));
|
|
8855
|
+
continue;
|
|
8856
|
+
}
|
|
8857
|
+
if (field.type === "sub-tabs") {
|
|
8858
|
+
for (const tab of field.tabs) collected.push(...collectValueFieldKeys(tab.fields));
|
|
8859
|
+
continue;
|
|
8860
|
+
}
|
|
8861
|
+
if (field.type === "separator" || field.type === "info") continue;
|
|
8862
|
+
if ("key" in field && typeof field.key === "string") collected.push(field.key);
|
|
8863
|
+
}
|
|
8864
|
+
return collected;
|
|
8865
|
+
}
|
|
8730
8866
|
function collectFieldKeys(fields) {
|
|
8731
8867
|
const collected = [];
|
|
8732
8868
|
for (const field of fields) {
|
|
@@ -8835,6 +8971,10 @@ var RECORD_SECRET_COLLECTION_PREFIX = "records:";
|
|
|
8835
8971
|
* (`collectZodSecretPaths`): dotted keys, `[]` for "each element of this array"
|
|
8836
8972
|
* (`auth.accessToken`, `headers[].value`).
|
|
8837
8973
|
*
|
|
8974
|
+
* `{}` after a key means "each value of this map" (`applianceSecrets{}`): the map's own key
|
|
8975
|
+
* names the value (`applianceSecrets{fridge}`), so no id key is declared and a body that drops a
|
|
8976
|
+
* key releases exactly that key's secret.
|
|
8977
|
+
*
|
|
8838
8978
|
* Array elements are matched by a DECLARED id key (`secretArrayKeys`:
|
|
8839
8979
|
* `{ headers: 'name' }`), never by index, so reordering a list keeps every
|
|
8840
8980
|
* keychain ref (the same rule as `arrayItemFieldPath`). The concrete path of a
|
|
@@ -8847,11 +8987,13 @@ function parseSecretPath(path) {
|
|
|
8847
8987
|
if (path.length === 0) throw new InvalidRecordError("an empty secret path");
|
|
8848
8988
|
return path.split(".").map((raw) => {
|
|
8849
8989
|
const each = raw.endsWith("[]");
|
|
8850
|
-
const
|
|
8990
|
+
const map = raw.endsWith("{}");
|
|
8991
|
+
const key = each || map ? raw.slice(0, -2) : raw;
|
|
8851
8992
|
if (key.length === 0) throw new InvalidRecordError(`secret path "${path}" has an empty segment`);
|
|
8852
8993
|
return {
|
|
8853
8994
|
key,
|
|
8854
|
-
each
|
|
8995
|
+
each,
|
|
8996
|
+
map
|
|
8855
8997
|
};
|
|
8856
8998
|
});
|
|
8857
8999
|
}
|
|
@@ -8903,6 +9045,22 @@ function mapAt(node, segments, index, walkedKeys, concretePrefix, declared, arra
|
|
|
8903
9045
|
[segment.key]: mapped
|
|
8904
9046
|
};
|
|
8905
9047
|
}
|
|
9048
|
+
if (segment.map) {
|
|
9049
|
+
if (!isRecord$4(child)) return node;
|
|
9050
|
+
const kept = Object.entries(child).map(([mapKey, value]) => {
|
|
9051
|
+
const concrete = `${concretePrefix}${concretePrefix.length > 0 ? "." : ""}${segment.key}{${encodeURIComponent(mapKey)}}`;
|
|
9052
|
+
if (!last) return [mapKey, mapAt(value, segments, index + 1, keys, concrete, declared, arrayKeys, visit)];
|
|
9053
|
+
return [mapKey, visit({
|
|
9054
|
+
declared,
|
|
9055
|
+
concrete,
|
|
9056
|
+
value
|
|
9057
|
+
})];
|
|
9058
|
+
}).filter(([, value]) => value !== REMOVE_LEAF);
|
|
9059
|
+
return {
|
|
9060
|
+
...node,
|
|
9061
|
+
[segment.key]: Object.fromEntries(kept)
|
|
9062
|
+
};
|
|
9063
|
+
}
|
|
8906
9064
|
const concrete = `${concretePrefix}${concretePrefix.length > 0 ? "." : ""}${segment.key}`;
|
|
8907
9065
|
if (last) {
|
|
8908
9066
|
const replacement = visit({
|
|
@@ -9044,7 +9202,8 @@ function isZodSecret(schema) {
|
|
|
9044
9202
|
}
|
|
9045
9203
|
/**
|
|
9046
9204
|
* Every declared secret path in a config schema: dotted keys, `[]` for "each
|
|
9047
|
-
* element of this array" (`connection.password`, `rows[].token`)
|
|
9205
|
+
* element of this array" (`connection.password`, `rows[].token`), `{}` for "each value of this
|
|
9206
|
+
* map" (`applianceSecrets{}`). A declared
|
|
9048
9207
|
* object or array is reported as one path and not descended into — everything
|
|
9049
9208
|
* under it is secret.
|
|
9050
9209
|
*/
|
|
@@ -9055,6 +9214,7 @@ function collectZodSecretPaths(schema, prefix = "") {
|
|
|
9055
9214
|
const def = defOf(layer);
|
|
9056
9215
|
if (def.type === "object" && def.shape) for (const [key, child] of Object.entries(def.shape)) paths.push(...collectZodSecretPaths(child, prefix ? `${prefix}.${key}` : key));
|
|
9057
9216
|
else if (def.type === "array" && def.element) paths.push(...collectZodSecretPaths(def.element, `${prefix}[]`));
|
|
9217
|
+
else if (def.type === "record" && def.valueType) paths.push(...collectZodSecretPaths(def.valueType, `${prefix}{}`));
|
|
9058
9218
|
else if (def.type === "union" && def.options) for (const option of def.options) paths.push(...collectZodSecretPaths(option, prefix));
|
|
9059
9219
|
else if (def.type === "intersection") {
|
|
9060
9220
|
if (def.left) paths.push(...collectZodSecretPaths(def.left, prefix));
|
|
@@ -9194,7 +9354,7 @@ function defineRecordKinds(specs) {
|
|
|
9194
9354
|
retention: spec.retention,
|
|
9195
9355
|
deviceRemoval: spec.deviceRemoval,
|
|
9196
9356
|
maxDataBytes: spec.maxDataBytes,
|
|
9197
|
-
secretPaths: [...secretPaths],
|
|
9357
|
+
secretPaths: [...new Set([...secretPaths, ...spec.retiredSecretPaths ?? []])],
|
|
9198
9358
|
secretArrayKeys: spec.secretArrayKeys === void 0 ? void 0 : { ...spec.secretArrayKeys },
|
|
9199
9359
|
transactFamily: spec.transactFamily,
|
|
9200
9360
|
entry: spec.entry === void 0 ? void 0 : {
|
|
@@ -9236,9 +9396,11 @@ function defineEntryKind(name, spec) {
|
|
|
9236
9396
|
class: "config",
|
|
9237
9397
|
writeTrigger: spec.writeTrigger ?? "an operator adds or edits an entry in the entries UI",
|
|
9238
9398
|
retention: { none: "an entry goes only when the operator deletes it" },
|
|
9399
|
+
...spec.maxDataBytes === void 0 ? {} : { maxDataBytes: spec.maxDataBytes },
|
|
9239
9400
|
...spec.database === void 0 ? {} : { database: spec.database },
|
|
9240
9401
|
...spec.indexes === void 0 ? {} : { indexes: spec.indexes },
|
|
9241
9402
|
...spec.secretArrayKeys === void 0 ? {} : { secretArrayKeys: spec.secretArrayKeys },
|
|
9403
|
+
...spec.retiredSecretPaths === void 0 ? {} : { retiredSecretPaths: spec.retiredSecretPaths },
|
|
9242
9404
|
entry: {
|
|
9243
9405
|
title: spec.title,
|
|
9244
9406
|
...spec.nameField === void 0 ? {} : { nameField: spec.nameField }
|
|
@@ -9899,6 +10061,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
9899
10061
|
"backup",
|
|
9900
10062
|
"battery",
|
|
9901
10063
|
"binary",
|
|
10064
|
+
"board-key-source",
|
|
9902
10065
|
"brightness",
|
|
9903
10066
|
"broker",
|
|
9904
10067
|
"button",
|
|
@@ -10020,6 +10183,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
10020
10183
|
"privacy-mask",
|
|
10021
10184
|
"ptz",
|
|
10022
10185
|
"ptz-autotrack",
|
|
10186
|
+
"pump-cycle",
|
|
10023
10187
|
"reboot",
|
|
10024
10188
|
"record-entries",
|
|
10025
10189
|
"recording",
|
|
@@ -11312,18 +11476,6 @@ Object.freeze({
|
|
|
11312
11476
|
"path": "pumpRunning",
|
|
11313
11477
|
"valueType": "boolean"
|
|
11314
11478
|
},
|
|
11315
|
-
{
|
|
11316
|
-
"path": "pumpIntervalEnabled",
|
|
11317
|
-
"valueType": "boolean"
|
|
11318
|
-
},
|
|
11319
|
-
{
|
|
11320
|
-
"path": "pumpOnTime",
|
|
11321
|
-
"valueType": "number"
|
|
11322
|
-
},
|
|
11323
|
-
{
|
|
11324
|
-
"path": "pumpOffTime",
|
|
11325
|
-
"valueType": "number"
|
|
11326
|
-
},
|
|
11327
11479
|
{
|
|
11328
11480
|
"path": "lastDrink",
|
|
11329
11481
|
"valueType": "number"
|
|
@@ -11447,6 +11599,20 @@ Object.freeze({
|
|
|
11447
11599
|
"valueType": "number"
|
|
11448
11600
|
}
|
|
11449
11601
|
],
|
|
11602
|
+
"pump-cycle": [
|
|
11603
|
+
{
|
|
11604
|
+
"path": "enabled",
|
|
11605
|
+
"valueType": "boolean"
|
|
11606
|
+
},
|
|
11607
|
+
{
|
|
11608
|
+
"path": "onTime",
|
|
11609
|
+
"valueType": "number"
|
|
11610
|
+
},
|
|
11611
|
+
{
|
|
11612
|
+
"path": "offTime",
|
|
11613
|
+
"valueType": "number"
|
|
11614
|
+
}
|
|
11615
|
+
],
|
|
11450
11616
|
"recording-archive": [
|
|
11451
11617
|
{
|
|
11452
11618
|
"path": "deviceId",
|
|
@@ -11910,7 +12076,7 @@ function userError(code, params, englishFallback) {
|
|
|
11910
12076
|
return new CamstackError(code, params, englishFallback);
|
|
11911
12077
|
}
|
|
11912
12078
|
//#endregion
|
|
11913
|
-
//#region ../types/dist/composition-
|
|
12079
|
+
//#region ../types/dist/composition-pOHbTzRB.mjs
|
|
11914
12080
|
/**
|
|
11915
12081
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
11916
12082
|
*
|
|
@@ -12673,38 +12839,6 @@ function validateExpressionSource(src) {
|
|
|
12673
12839
|
return null;
|
|
12674
12840
|
}
|
|
12675
12841
|
/**
|
|
12676
|
-
* One power reading becomes a washer, dryer or dishwasher (D944 helper).
|
|
12677
|
-
*
|
|
12678
|
-
* Standby of the control board is under 5 W; a motor or a heater is far
|
|
12679
|
-
* above that, so 5 W is the line. A door lock or a short pump spike is not
|
|
12680
|
-
* a cycle, so running starts only after 30 s above the line.
|
|
12681
|
-
*
|
|
12682
|
-
* A wash, dry or dishwasher pause (soak, heat-wait, heater off between
|
|
12683
|
-
* bursts) lasts several minutes. Ending the cycle on the first dip would
|
|
12684
|
-
* report "finished" in the middle of the program, so the stop dwell is
|
|
12685
|
-
* 10 minutes on every type. The number is stored per type so one machine
|
|
12686
|
-
* can move later without moving the others.
|
|
12687
|
-
*/
|
|
12688
|
-
var APPLIANCE_TEMPLATE_ID = "appliance";
|
|
12689
|
-
var APPLIANCE_KINDS = [
|
|
12690
|
-
"washer",
|
|
12691
|
-
"dryer",
|
|
12692
|
-
"dishwasher"
|
|
12693
|
-
];
|
|
12694
|
-
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
12695
|
-
/**
|
|
12696
|
-
* A dumb plug-in stove or heater, built from a smart plug (a `Heater`).
|
|
12697
|
-
*
|
|
12698
|
-
* The plug is the actuator: `switch.setState` is a FORWARD to the plug's own
|
|
12699
|
-
* (D967), so the heater switches exactly as the plug does. Running is the
|
|
12700
|
-
* plug being on; when a power reading is also named, running is the draw
|
|
12701
|
-
* staying above the line instead (a plug left on with the stove's own
|
|
12702
|
-
* thermostat open is not heating). 50 W is far above any standby and below
|
|
12703
|
-
* the smallest radiator.
|
|
12704
|
-
*/
|
|
12705
|
-
var HEATER_TEMPLATE_ID = "heater";
|
|
12706
|
-
DeviceType.Heater;
|
|
12707
|
-
/**
|
|
12708
12842
|
* Statistics and integrals over a history read (D944).
|
|
12709
12843
|
*
|
|
12710
12844
|
* Pure: the same points always give the same answer, so a restart recomputes
|
|
@@ -12848,34 +12982,6 @@ var HelperSpecSchema = discriminatedUnion("kind", [
|
|
|
12848
12982
|
/** Actuator groups only. Readings leave this unset. */
|
|
12849
12983
|
active: ActiveCriterionSchema.optional()
|
|
12850
12984
|
}),
|
|
12851
|
-
object({
|
|
12852
|
-
kind: literal("appliance"),
|
|
12853
|
-
templateId: literal(APPLIANCE_TEMPLATE_ID),
|
|
12854
|
-
appliance: _enum(APPLIANCE_KINDS),
|
|
12855
|
-
thresholdWatts: number().positive(),
|
|
12856
|
-
startDwellMs: number().int().nonnegative(),
|
|
12857
|
-
stopDwellMs: number().int().nonnegative(),
|
|
12858
|
-
member: HelperMemberSchema,
|
|
12859
|
-
/**
|
|
12860
|
-
* The plug that powers the appliance (`switch.on`). Optional: when set, the
|
|
12861
|
-
* composition carries a `power-source` feature, so the hub can cut and restore
|
|
12862
|
-
* that plug (power-source link of a NEW-target composition).
|
|
12863
|
-
*/
|
|
12864
|
-
powerSwitch: HelperMemberSchema.optional(),
|
|
12865
|
-
/** How long a forced restart keeps the plug off; the cap's default when unset. */
|
|
12866
|
-
offSeconds: number().optional()
|
|
12867
|
-
}),
|
|
12868
|
-
object({
|
|
12869
|
-
kind: literal("heater"),
|
|
12870
|
-
templateId: literal(HEATER_TEMPLATE_ID),
|
|
12871
|
-
/** The plug, read and commanded at `switch.on`. */
|
|
12872
|
-
plug: HelperMemberSchema,
|
|
12873
|
-
/** The plug's power reading, when it has one: running is then the draw, not the switch. */
|
|
12874
|
-
meter: HelperMemberSchema.optional(),
|
|
12875
|
-
thresholdWatts: number().positive(),
|
|
12876
|
-
startDwellMs: number().int().nonnegative(),
|
|
12877
|
-
stopDwellMs: number().int().nonnegative()
|
|
12878
|
-
}),
|
|
12879
12985
|
object({
|
|
12880
12986
|
kind: literal("statistic"),
|
|
12881
12987
|
stat: _enum(STATISTIC_STATS),
|
|
@@ -13047,7 +13153,14 @@ var CompositionForwardCommandSchema = object({
|
|
|
13047
13153
|
method: string().min(1),
|
|
13048
13154
|
args: record(string().min(1), CompositionForwardArgSchema).optional()
|
|
13049
13155
|
});
|
|
13050
|
-
var CompositionCommandTargetSchema = discriminatedUnion("kind", [
|
|
13156
|
+
var CompositionCommandTargetSchema = discriminatedUnion("kind", [
|
|
13157
|
+
CompositionForwardCommandSchema,
|
|
13158
|
+
CompositionCodeSourceSchema,
|
|
13159
|
+
object({
|
|
13160
|
+
kind: literal("extension"),
|
|
13161
|
+
target: literal("remote")
|
|
13162
|
+
})
|
|
13163
|
+
]);
|
|
13051
13164
|
/** An item key inside an item-array cap (`consumables.items[<key>]`). */
|
|
13052
13165
|
var COMPOSITION_ITEM_KEY_RE = /^[a-z0-9][a-z0-9-]{0,47}$/;
|
|
13053
13166
|
/**
|
|
@@ -13092,7 +13205,7 @@ var CompositionSchema = object({
|
|
|
13092
13205
|
kind: literal("existing"),
|
|
13093
13206
|
device: CompositionSourceRefSchema
|
|
13094
13207
|
})]),
|
|
13095
|
-
features: array(CompositionFeatureSchema).min(
|
|
13208
|
+
features: array(CompositionFeatureSchema).min(0).max(16),
|
|
13096
13209
|
/**
|
|
13097
13210
|
* Present when the block is a helper template. Absent on a free
|
|
13098
13211
|
* composition (the room example, device 4603). The editor reads this,
|
|
@@ -13327,11 +13440,49 @@ var IntegrationInstanceRefSchema = object({
|
|
|
13327
13440
|
]),
|
|
13328
13441
|
candidateStableId: string().optional()
|
|
13329
13442
|
});
|
|
13443
|
+
/**
|
|
13444
|
+
* `RecordIntegrationInstances` - the `integration-instances` provider over ONE
|
|
13445
|
+
* entry kind, so an addon that holds a connection serves it in ~30 lines.
|
|
13446
|
+
*
|
|
13447
|
+
* Every instance is one record of the addon's own kind, keyed by the
|
|
13448
|
+
* `integrations` registry id. The records door does the secret work: a field the
|
|
13449
|
+
* kind marks `.meta({ secret: true })` is a keychain marker bound to the row, so
|
|
13450
|
+
* deleting the row deletes the secret, and one row's value is never another's.
|
|
13451
|
+
*
|
|
13452
|
+
* Patch rules for a secret (`updateInstance`): omitted, `''`, the redaction
|
|
13453
|
+
* token or the view the form was given KEEP the stored secret;
|
|
13454
|
+
* `{ $secretClear: true }` clears it; any other string replaces it.
|
|
13455
|
+
*/
|
|
13330
13456
|
/** The most instances one addon lists (an integrations page, not a table). */
|
|
13331
13457
|
var INSTANCES_LIST_MAX = 1e3;
|
|
13332
13458
|
/** The keys of the kind's secret fields that sit at the top level of the body. */
|
|
13333
13459
|
function topLevelSecretKeys(paths) {
|
|
13334
|
-
return new Set(paths.filter((p) => !p.includes(".") && !p.includes("[")));
|
|
13460
|
+
return new Set(paths.filter((p) => !p.includes(".") && !p.includes("[") && !p.includes("{")));
|
|
13461
|
+
}
|
|
13462
|
+
/** The keys of the kind's top-level MAPS whose values are secrets (`applianceSecrets{}`). */
|
|
13463
|
+
function secretMapKeys(paths) {
|
|
13464
|
+
return new Set(paths.filter((p) => p.endsWith("{}") && !p.slice(0, -2).match(/[.[{]/)).map((p) => p.slice(0, -2)));
|
|
13465
|
+
}
|
|
13466
|
+
/**
|
|
13467
|
+
* A patch value for a map of secrets: an entry that is omitted, `''`, the redaction token or the
|
|
13468
|
+
* view the form was given KEEPS its stored secret (it is left out of the merge); `{ $secretClear }`
|
|
13469
|
+
* or `null` removes the entry; any other string replaces it. Null when nothing is left to merge.
|
|
13470
|
+
*/
|
|
13471
|
+
function secretMapPatch(value) {
|
|
13472
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
|
|
13473
|
+
const out = {};
|
|
13474
|
+
for (const [key, entry] of Object.entries(value)) if (entry === null || isSecretClear(entry)) out[key] = null;
|
|
13475
|
+
else if (typeof entry === "string" && entry !== "" && entry !== "__camstack_redacted__" && !isSecretFieldView(entry)) out[key] = entry;
|
|
13476
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
13477
|
+
}
|
|
13478
|
+
/** Does the kind's body schema carry an `enabled` field? */
|
|
13479
|
+
function declaresEnabled(kind) {
|
|
13480
|
+
const json = toJSONSchema(kind.schema, {
|
|
13481
|
+
unrepresentable: "any",
|
|
13482
|
+
io: "input"
|
|
13483
|
+
});
|
|
13484
|
+
const properties = Reflect.get(json, "properties");
|
|
13485
|
+
return typeof properties === "object" && properties !== null && "enabled" in properties;
|
|
13335
13486
|
}
|
|
13336
13487
|
var RecordIntegrationInstances = class {
|
|
13337
13488
|
records;
|
|
@@ -13339,14 +13490,20 @@ var RecordIntegrationInstances = class {
|
|
|
13339
13490
|
hooks;
|
|
13340
13491
|
handle;
|
|
13341
13492
|
secretKeys;
|
|
13493
|
+
secretMaps;
|
|
13342
13494
|
nameField;
|
|
13495
|
+
/** The `integrations` registry owns the on/off switch (`role: devices`); the body has none. */
|
|
13496
|
+
registryOwnsEnabled;
|
|
13343
13497
|
constructor(records, kind, hooks) {
|
|
13344
13498
|
this.records = records;
|
|
13345
13499
|
this.kind = kind;
|
|
13346
13500
|
this.hooks = hooks;
|
|
13347
13501
|
this.handle = records.kind(kind);
|
|
13348
13502
|
this.secretKeys = topLevelSecretKeys(kind.declaration.secretPaths);
|
|
13503
|
+
this.secretMaps = secretMapKeys(kind.declaration.secretPaths);
|
|
13349
13504
|
this.nameField = kind.declaration.entry?.nameField ?? "name";
|
|
13505
|
+
this.registryOwnsEnabled = hooks.description.role === "devices";
|
|
13506
|
+
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"`);
|
|
13350
13507
|
}
|
|
13351
13508
|
/**
|
|
13352
13509
|
* One instance read for the addon's own runtime; null when absent. When the keychain is
|
|
@@ -13391,55 +13548,83 @@ var RecordIntegrationInstances = class {
|
|
|
13391
13548
|
viewOf(record) {
|
|
13392
13549
|
const shown = secretViewsOf(this.records.owner, this.kind.name, record.id, this.kind.declaration, record.data);
|
|
13393
13550
|
const rawName = record.data[this.nameField];
|
|
13551
|
+
const kind = this.hooks.kindOf?.(record.data);
|
|
13552
|
+
const health = this.hooks.health?.(record.data, record.id);
|
|
13394
13553
|
return {
|
|
13395
13554
|
id: record.id,
|
|
13396
13555
|
name: typeof rawName === "string" ? rawName : record.id,
|
|
13397
13556
|
configured: this.hooks.configured(record.data),
|
|
13398
|
-
enabled: record.data.enabled !== false,
|
|
13399
|
-
formValues: shown
|
|
13557
|
+
...this.registryOwnsEnabled ? {} : { enabled: record.data.enabled !== false },
|
|
13558
|
+
formValues: shown,
|
|
13559
|
+
...kind !== void 0 ? { kind } : {},
|
|
13560
|
+
...health?.status !== void 0 ? { status: health.status } : {},
|
|
13561
|
+
...health?.lastCheckedAt !== void 0 ? { lastCheckedAt: health.lastCheckedAt } : {}
|
|
13400
13562
|
};
|
|
13401
13563
|
}
|
|
13402
|
-
describe = async () => this.hooks.description
|
|
13403
|
-
|
|
13564
|
+
describe = async () => this.hooks.mintId === void 0 ? this.hooks.description : {
|
|
13565
|
+
...this.hooks.description,
|
|
13566
|
+
mintsId: true
|
|
13567
|
+
};
|
|
13568
|
+
/**
|
|
13569
|
+
* The record an id names. A caller holding the `integrations` registry id (the Integrations
|
|
13570
|
+
* page) reaches a kind whose body links to it through `integrationId`; an id that is itself a
|
|
13571
|
+
* record id answers as it is. An id that names nothing comes back unchanged.
|
|
13572
|
+
*/
|
|
13573
|
+
async resolveRecordId(id) {
|
|
13574
|
+
if (await this.handle.get(id) !== null) return id;
|
|
13575
|
+
const linked = (await this.handle.query({ limit: INSTANCES_LIST_MAX })).records.find((record) => record.data["integrationId"] === id);
|
|
13576
|
+
return linked === void 0 ? id : linked.id;
|
|
13577
|
+
}
|
|
13578
|
+
instanceFormSchema = async ({ id }) => this.hooks.formSchema(id === null ? null : await this.resolveRecordId(id));
|
|
13404
13579
|
listInstances = async () => {
|
|
13405
13580
|
return (await this.handle.query({ limit: INSTANCES_LIST_MAX })).records.map((record) => this.viewOf(record));
|
|
13406
13581
|
};
|
|
13407
13582
|
getInstance = async ({ id }) => {
|
|
13408
|
-
const record = await this.handle.get(id);
|
|
13583
|
+
const record = await this.handle.get(await this.resolveRecordId(id));
|
|
13409
13584
|
return record === null ? null : this.viewOf(record);
|
|
13410
13585
|
};
|
|
13411
13586
|
/** Insert ONE row (`ifRev: 0`: an existing id is refused, never overwritten). */
|
|
13412
|
-
createInstance = async ({ id, name }) => {
|
|
13587
|
+
createInstance = async ({ id, name, kind }) => {
|
|
13588
|
+
const minted = id ?? this.hooks.mintId?.({
|
|
13589
|
+
name,
|
|
13590
|
+
...kind !== void 0 ? { kind } : {}
|
|
13591
|
+
});
|
|
13592
|
+
if (minted === void 0) throw new Error("createInstance needs an id: this provider does not mint one");
|
|
13413
13593
|
await this.handle.put({
|
|
13414
|
-
id,
|
|
13415
|
-
data: this.hooks.initialBody(name)
|
|
13594
|
+
id: minted,
|
|
13595
|
+
data: this.hooks.initialBody(name, kind)
|
|
13416
13596
|
}, { ifRev: 0 });
|
|
13417
|
-
await this.hooks.onChanged?.(
|
|
13418
|
-
return { id };
|
|
13597
|
+
await this.hooks.onChanged?.(minted, "created");
|
|
13598
|
+
return { id: minted };
|
|
13419
13599
|
};
|
|
13420
|
-
updateInstance = async ({ id, name, values }) => {
|
|
13600
|
+
updateInstance = async ({ id: askedId, name, values }) => {
|
|
13601
|
+
const id = await this.resolveRecordId(askedId);
|
|
13421
13602
|
const patch = {};
|
|
13422
13603
|
for (const [key, value] of Object.entries(values)) {
|
|
13423
13604
|
if (value === void 0) continue;
|
|
13424
|
-
if (
|
|
13605
|
+
if (this.secretMaps.has(key)) {
|
|
13606
|
+
const entries = secretMapPatch(value);
|
|
13607
|
+
if (entries !== null) patch[key] = entries;
|
|
13608
|
+
} else if (!this.secretKeys.has(key)) patch[key] = value;
|
|
13425
13609
|
else if (isSecretClear(value)) patch[key] = null;
|
|
13426
13610
|
else if (typeof value === "string" && value !== "" && value !== "__camstack_redacted__" && !isSecretFieldView(value)) patch[key] = value;
|
|
13427
13611
|
}
|
|
13428
13612
|
if (name !== void 0) patch[this.nameField] = name;
|
|
13429
13613
|
await this.handle.patch(id, patch);
|
|
13430
|
-
await this.hooks.onChanged?.(id);
|
|
13614
|
+
await this.hooks.onChanged?.(id, "updated");
|
|
13431
13615
|
return { ok: true };
|
|
13432
13616
|
};
|
|
13433
13617
|
/** The switch lives in the record body; the addon is told so it applies it (D996). */
|
|
13434
13618
|
setInstanceEnabled = async ({ id, enabled }) => {
|
|
13619
|
+
if (this.registryOwnsEnabled) throw new Error(`instance "${id}": the integrations registry owns the on/off switch of this addon; set it with integrations.update`);
|
|
13435
13620
|
await this.handle.patch(id, { enabled });
|
|
13436
|
-
await this.hooks.onChanged?.(id);
|
|
13621
|
+
await this.hooks.onChanged?.(id, "enabled");
|
|
13437
13622
|
return { ok: true };
|
|
13438
13623
|
};
|
|
13439
13624
|
/** The row and its keychain refs go together (the records door). */
|
|
13440
13625
|
deleteInstance = async ({ id }) => {
|
|
13441
13626
|
await this.handle.delete(id);
|
|
13442
|
-
await this.hooks.onChanged?.(id);
|
|
13627
|
+
await this.hooks.onChanged?.(id, "deleted");
|
|
13443
13628
|
return { ok: true };
|
|
13444
13629
|
};
|
|
13445
13630
|
testInstance = async (input) => {
|
|
@@ -13448,7 +13633,10 @@ var RecordIntegrationInstances = class {
|
|
|
13448
13633
|
status: "unsupported",
|
|
13449
13634
|
error: "This integration declares no connection test"
|
|
13450
13635
|
};
|
|
13451
|
-
return this.hooks.test(input
|
|
13636
|
+
return this.hooks.test(input.id === null ? input : {
|
|
13637
|
+
...input,
|
|
13638
|
+
id: await this.resolveRecordId(input.id)
|
|
13639
|
+
});
|
|
13452
13640
|
};
|
|
13453
13641
|
/** The flat provider object to hand to `registerProvider`. */
|
|
13454
13642
|
toProvider() {
|
|
@@ -14510,7 +14698,13 @@ var MigrationLedgerEntrySchema = object({
|
|
|
14510
14698
|
* joined by the engine from the catalogues it knows this boot. Absent when no
|
|
14511
14699
|
* catalogue lists the id (a newer database).
|
|
14512
14700
|
*/
|
|
14513
|
-
meta: MigrationMetaSchema.optional()
|
|
14701
|
+
meta: MigrationMetaSchema.optional(),
|
|
14702
|
+
/**
|
|
14703
|
+
* Set when this is an applied row of an addon owner whose catalogue the answering
|
|
14704
|
+
* process has not loaded (the owner has not declared yet): the row is applied, the
|
|
14705
|
+
* process just cannot judge it. Never `skipped-newer` — that needs the owner loaded.
|
|
14706
|
+
*/
|
|
14707
|
+
ownerNotLoaded: boolean().optional()
|
|
14514
14708
|
});
|
|
14515
14709
|
/** A catalogued migration that is not applied, with where it stands. */
|
|
14516
14710
|
var MigrationPendingEntrySchema = object({
|
|
@@ -19628,24 +19822,52 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
19628
19822
|
args: { on: false }
|
|
19629
19823
|
},
|
|
19630
19824
|
{
|
|
19631
|
-
id: "petFountain.
|
|
19632
|
-
method: "
|
|
19825
|
+
id: "petFountain.resetDailyConsumption",
|
|
19826
|
+
method: "resetDailyConsumption",
|
|
19827
|
+
label: t("Clear daily consumption", "Azzera il consumo giornaliero"),
|
|
19828
|
+
phrase: t("Clear the daily water consumption of {device}", "Azzera il consumo giornaliero di acqua di {device}"),
|
|
19829
|
+
icon: "rotate-ccw"
|
|
19830
|
+
}
|
|
19831
|
+
],
|
|
19832
|
+
state: [{
|
|
19833
|
+
kind: "boolean",
|
|
19834
|
+
field: "empty",
|
|
19835
|
+
label: t("Tank empty", "Serbatoio vuoto"),
|
|
19836
|
+
whenTrue: t("When the tank of {device} is empty", "Quando il serbatoio di {device} è vuoto"),
|
|
19837
|
+
whenFalse: t("When the tank of {device} is refilled", "Quando il serbatoio di {device} viene riempito"),
|
|
19838
|
+
icon: "droplet"
|
|
19839
|
+
}, {
|
|
19840
|
+
kind: "numeric",
|
|
19841
|
+
field: "waterLevel",
|
|
19842
|
+
quantity: t("the water level", "il livello dell’acqua"),
|
|
19843
|
+
label: t("Water level", "Livello dell’acqua"),
|
|
19844
|
+
unit: "%",
|
|
19845
|
+
icon: "droplets"
|
|
19846
|
+
}]
|
|
19847
|
+
},
|
|
19848
|
+
"pump-cycle": {
|
|
19849
|
+
scope: "device",
|
|
19850
|
+
noun: t("Pump cycle", "Ciclo della pompa"),
|
|
19851
|
+
actions: [
|
|
19852
|
+
{
|
|
19853
|
+
id: "pumpCycle.on",
|
|
19854
|
+
method: "setEnabled",
|
|
19633
19855
|
label: t("Pump cycle on", "Ciclo pompa attivo"),
|
|
19634
19856
|
phrase: t("Run the pump of {device} on its on/off cycle", "Fai lavorare la pompa di {device} a ciclo acceso/spento"),
|
|
19635
19857
|
icon: "timer",
|
|
19636
19858
|
args: { on: true }
|
|
19637
19859
|
},
|
|
19638
19860
|
{
|
|
19639
|
-
id: "
|
|
19640
|
-
method: "
|
|
19861
|
+
id: "pumpCycle.off",
|
|
19862
|
+
method: "setEnabled",
|
|
19641
19863
|
label: t("Pump cycle off", "Ciclo pompa spento"),
|
|
19642
19864
|
phrase: t("Run the pump of {device} continuously", "Fai lavorare la pompa di {device} in continuo"),
|
|
19643
19865
|
icon: "timer-off",
|
|
19644
19866
|
args: { on: false }
|
|
19645
19867
|
},
|
|
19646
19868
|
{
|
|
19647
|
-
id: "
|
|
19648
|
-
method: "
|
|
19869
|
+
id: "pumpCycle.onMinutes",
|
|
19870
|
+
method: "setOnMinutes",
|
|
19649
19871
|
label: t("Set pump run time", "Imposta il tempo di lavoro della pompa"),
|
|
19650
19872
|
phrase: t("Set the pump of {device} to run {minutes} minutes per cycle", "Imposta la pompa di {device} a lavorare {minutes} minuti per ciclo"),
|
|
19651
19873
|
icon: "timer",
|
|
@@ -19661,8 +19883,8 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
19661
19883
|
}]
|
|
19662
19884
|
},
|
|
19663
19885
|
{
|
|
19664
|
-
id: "
|
|
19665
|
-
method: "
|
|
19886
|
+
id: "pumpCycle.offMinutes",
|
|
19887
|
+
method: "setOffMinutes",
|
|
19666
19888
|
label: t("Set pump rest time", "Imposta il tempo di riposo della pompa"),
|
|
19667
19889
|
phrase: t("Set the pump of {device} to rest {minutes} minutes per cycle", "Imposta la pompa di {device} a riposare {minutes} minuti per ciclo"),
|
|
19668
19890
|
icon: "timer-off",
|
|
@@ -19676,35 +19898,63 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
19676
19898
|
unit: "min"
|
|
19677
19899
|
}
|
|
19678
19900
|
}]
|
|
19679
|
-
},
|
|
19680
|
-
{
|
|
19681
|
-
id: "petFountain.resetDailyConsumption",
|
|
19682
|
-
method: "resetDailyConsumption",
|
|
19683
|
-
label: t("Clear daily consumption", "Azzera il consumo giornaliero"),
|
|
19684
|
-
phrase: t("Clear the daily water consumption of {device}", "Azzera il consumo giornaliero di acqua di {device}"),
|
|
19685
|
-
icon: "rotate-ccw"
|
|
19686
19901
|
}
|
|
19687
19902
|
],
|
|
19688
19903
|
state: [{
|
|
19689
19904
|
kind: "boolean",
|
|
19690
|
-
field: "
|
|
19691
|
-
label: t("
|
|
19692
|
-
whenTrue: t("When
|
|
19693
|
-
whenFalse: t("When
|
|
19694
|
-
icon: "
|
|
19695
|
-
}, {
|
|
19696
|
-
kind: "numeric",
|
|
19697
|
-
field: "waterLevel",
|
|
19698
|
-
quantity: t("the water level", "il livello dell’acqua"),
|
|
19699
|
-
label: t("Water level", "Livello dell’acqua"),
|
|
19700
|
-
unit: "%",
|
|
19701
|
-
icon: "droplets"
|
|
19905
|
+
field: "enabled",
|
|
19906
|
+
label: t("Pump cycle", "Ciclo pompa"),
|
|
19907
|
+
whenTrue: t("When {device} starts running its pump on a cycle", "Quando {device} inizia a far lavorare la pompa a ciclo"),
|
|
19908
|
+
whenFalse: t("When {device} runs its pump continuously", "Quando {device} fa lavorare la pompa in continuo"),
|
|
19909
|
+
icon: "timer"
|
|
19702
19910
|
}]
|
|
19703
19911
|
},
|
|
19704
19912
|
"media-player": {
|
|
19705
19913
|
scope: "device",
|
|
19706
19914
|
noun: t("Media player", "Lettore multimediale"),
|
|
19707
19915
|
actions: [
|
|
19916
|
+
{
|
|
19917
|
+
id: "media.turnOn",
|
|
19918
|
+
method: "turnOn",
|
|
19919
|
+
label: t("Turn on", "Accendi"),
|
|
19920
|
+
phrase: t("Turn on {device}", "Accendi {device}"),
|
|
19921
|
+
icon: "power"
|
|
19922
|
+
},
|
|
19923
|
+
{
|
|
19924
|
+
id: "media.turnOff",
|
|
19925
|
+
method: "turnOff",
|
|
19926
|
+
label: t("Turn off", "Spegni"),
|
|
19927
|
+
phrase: t("Turn off {device}", "Spegni {device}"),
|
|
19928
|
+
icon: "power"
|
|
19929
|
+
},
|
|
19930
|
+
{
|
|
19931
|
+
id: "media.volumeUp",
|
|
19932
|
+
method: "volumeUp",
|
|
19933
|
+
label: t("Volume up", "Alza il volume"),
|
|
19934
|
+
phrase: t("Raise the volume of {device}", "Alza il volume di {device}"),
|
|
19935
|
+
icon: "volume-2"
|
|
19936
|
+
},
|
|
19937
|
+
{
|
|
19938
|
+
id: "media.volumeDown",
|
|
19939
|
+
method: "volumeDown",
|
|
19940
|
+
label: t("Volume down", "Abbassa il volume"),
|
|
19941
|
+
phrase: t("Lower the volume of {device}", "Abbassa il volume di {device}"),
|
|
19942
|
+
icon: "volume-2"
|
|
19943
|
+
},
|
|
19944
|
+
{
|
|
19945
|
+
id: "media.channelUp",
|
|
19946
|
+
method: "channelUp",
|
|
19947
|
+
label: t("Channel up", "Canale successivo"),
|
|
19948
|
+
phrase: t("Go to the next channel on {device}", "Passa al canale successivo su {device}"),
|
|
19949
|
+
icon: "skip-forward"
|
|
19950
|
+
},
|
|
19951
|
+
{
|
|
19952
|
+
id: "media.channelDown",
|
|
19953
|
+
method: "channelDown",
|
|
19954
|
+
label: t("Channel down", "Canale precedente"),
|
|
19955
|
+
phrase: t("Go to the previous channel on {device}", "Torna al canale precedente su {device}"),
|
|
19956
|
+
icon: "skip-back"
|
|
19957
|
+
},
|
|
19708
19958
|
{
|
|
19709
19959
|
id: "media.play",
|
|
19710
19960
|
method: "play",
|
|
@@ -21591,6 +21841,7 @@ var SYSTEM_CAP_VOCABULARY = {
|
|
|
21591
21841
|
"audio-analyzer": system(PIPELINE),
|
|
21592
21842
|
"audio-codec": system(INTERNAL),
|
|
21593
21843
|
"auth-provider": system(IDENTITY),
|
|
21844
|
+
"board-key-source": system(INTERNAL),
|
|
21594
21845
|
backup: system("system backups — run and restored from the Backups page"),
|
|
21595
21846
|
broker: system("message-broker connections — configured, not actuated"),
|
|
21596
21847
|
"calendar-source": system(INTERNAL),
|
|
@@ -46034,6 +46285,49 @@ object({
|
|
|
46034
46285
|
lastChangedAt: number()
|
|
46035
46286
|
});
|
|
46036
46287
|
DeviceType.Sensor;
|
|
46288
|
+
/**
|
|
46289
|
+
* Why a board key could not be read. Typed so a caller reports the cause and
|
|
46290
|
+
* never treats "could not read" as "has no key" (D49).
|
|
46291
|
+
*/
|
|
46292
|
+
var BoardKeyFailureReasonSchema = _enum([
|
|
46293
|
+
"unknown-source",
|
|
46294
|
+
"not-admin",
|
|
46295
|
+
"no-builder",
|
|
46296
|
+
"no-ingress-url",
|
|
46297
|
+
"unreachable",
|
|
46298
|
+
"unknown-board",
|
|
46299
|
+
"no-encryption-key",
|
|
46300
|
+
"unreadable-config"
|
|
46301
|
+
]);
|
|
46302
|
+
var BoardKeySourceSchema = object({
|
|
46303
|
+
sourceId: string(),
|
|
46304
|
+
name: string()
|
|
46305
|
+
});
|
|
46306
|
+
var BoardKeyBoardSchema = object({
|
|
46307
|
+
/** The builder's file name, e.g. `kitchen.yaml`: the handle `getBoardKey` takes. */
|
|
46308
|
+
configuration: string(),
|
|
46309
|
+
/** The node name the builder gives the board. */
|
|
46310
|
+
name: string(),
|
|
46311
|
+
/** The address the builder knows the board by, when it has one. */
|
|
46312
|
+
address: string().nullable()
|
|
46313
|
+
});
|
|
46314
|
+
var BoardKeyFailureSchema = object({
|
|
46315
|
+
ok: literal(false),
|
|
46316
|
+
reason: BoardKeyFailureReasonSchema,
|
|
46317
|
+
detail: string().optional()
|
|
46318
|
+
});
|
|
46319
|
+
var ListBoardsResultSchema = discriminatedUnion("ok", [object({
|
|
46320
|
+
ok: literal(true),
|
|
46321
|
+
boards: array(BoardKeyBoardSchema).readonly()
|
|
46322
|
+
}), BoardKeyFailureSchema]);
|
|
46323
|
+
var GetBoardKeyResultSchema = discriminatedUnion("ok", [object({
|
|
46324
|
+
ok: literal(true),
|
|
46325
|
+
key: string()
|
|
46326
|
+
}), BoardKeyFailureSchema]);
|
|
46327
|
+
method(_void(), array(BoardKeySourceSchema).readonly(), { auth: "admin" }), method(object({ sourceId: string().min(1) }), ListBoardsResultSchema, { auth: "admin" }), method(object({
|
|
46328
|
+
sourceId: string().min(1),
|
|
46329
|
+
configuration: string().min(1)
|
|
46330
|
+
}), GetBoardKeyResultSchema, { auth: "admin" });
|
|
46037
46331
|
object({
|
|
46038
46332
|
/** Current level as 0..100 inclusive. Firmware-reported. */
|
|
46039
46333
|
level: number().min(0).max(100).meta(readingMeta("percentage")),
|
|
@@ -48160,6 +48454,188 @@ DeviceType.Control, method(object({
|
|
|
48160
48454
|
kind: "mutation",
|
|
48161
48455
|
auth: "protected"
|
|
48162
48456
|
});
|
|
48457
|
+
/**
|
|
48458
|
+
* Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
|
|
48459
|
+
* Apple TV, Spotify, Roku, generic OTT receivers, …) on
|
|
48460
|
+
* `DeviceType.MediaPlayer`. Disjoint from the camera streaming
|
|
48461
|
+
* surface — those use `DeviceType.Camera` + the broker pipeline.
|
|
48462
|
+
*
|
|
48463
|
+
* The cap declares a wide method surface; not every backend supports
|
|
48464
|
+
* every method. Drivers signal sub-capability via the
|
|
48465
|
+
* `DeviceFeature.MediaPlayerXxx` flags (paired one-to-one with the
|
|
48466
|
+
* corresponding method) so the UI gates buttons without a slice
|
|
48467
|
+
* fetch — same pattern as `PanTiltZoom` for cameras.
|
|
48468
|
+
*/
|
|
48469
|
+
var MediaPlayerStateSchema = _enum([
|
|
48470
|
+
"off",
|
|
48471
|
+
"on",
|
|
48472
|
+
"idle",
|
|
48473
|
+
"playing",
|
|
48474
|
+
"paused",
|
|
48475
|
+
"buffering",
|
|
48476
|
+
"standby"
|
|
48477
|
+
]);
|
|
48478
|
+
var MediaPlayerRepeatSchema = _enum([
|
|
48479
|
+
"off",
|
|
48480
|
+
"all",
|
|
48481
|
+
"one"
|
|
48482
|
+
]);
|
|
48483
|
+
var MediaInfoSchema = object({
|
|
48484
|
+
/** Free-form media kind (`music`, `tvshow`, `movie`, `app`, `channel`,
|
|
48485
|
+
* `podcast`, …). */
|
|
48486
|
+
type: string(),
|
|
48487
|
+
/** Human-readable title. */
|
|
48488
|
+
title: string(),
|
|
48489
|
+
/** Optional artist / channel / station label. */
|
|
48490
|
+
artist: string().optional(),
|
|
48491
|
+
/** Optional album / season / show name. */
|
|
48492
|
+
album: string().optional(),
|
|
48493
|
+
/** Optional cover-art / thumbnail URL. */
|
|
48494
|
+
imageUrl: string().optional()
|
|
48495
|
+
});
|
|
48496
|
+
/**
|
|
48497
|
+
* Where `state`, `volumeLevel`, `isMuted` and `source` come from. `reported`:
|
|
48498
|
+
* the device said so. `assumed`: the driver has no feedback (infrared) and
|
|
48499
|
+
* tracks what it last SENT; it moves only after a send was accepted, and a
|
|
48500
|
+
* human at the real remote makes it wrong. A UI shows an assumed value as a
|
|
48501
|
+
* guess, never as a reading.
|
|
48502
|
+
*/
|
|
48503
|
+
var MediaPlayerStateSourceSchema = _enum(["reported", "assumed"]);
|
|
48504
|
+
/** The fields of a media-player status a driver can only guess. */
|
|
48505
|
+
var MediaPlayerAssumedFieldSchema = _enum([
|
|
48506
|
+
"state",
|
|
48507
|
+
"volumeLevel",
|
|
48508
|
+
"isMuted",
|
|
48509
|
+
"source"
|
|
48510
|
+
]);
|
|
48511
|
+
var MediaPlayerStatusSchema = object({
|
|
48512
|
+
/** Playback lifecycle state. */
|
|
48513
|
+
state: MediaPlayerStateSchema,
|
|
48514
|
+
/**
|
|
48515
|
+
* Whether the state fields are readings or the driver's own bookkeeping.
|
|
48516
|
+
* A slice written before this field existed parses as `reported`.
|
|
48517
|
+
*/
|
|
48518
|
+
stateSource: MediaPlayerStateSourceSchema.default("reported"),
|
|
48519
|
+
/**
|
|
48520
|
+
* Which fields are the driver's own guess. `stateSource` is `assumed` when
|
|
48521
|
+
* this is not empty; a field not listed here is a reading even then (power
|
|
48522
|
+
* from a running-state source, mute guessed from the last send).
|
|
48523
|
+
*/
|
|
48524
|
+
assumedFields: array(MediaPlayerAssumedFieldSchema).default([]),
|
|
48525
|
+
/** Volume as 0..100 inclusive. Null when the device has no volume
|
|
48526
|
+
* surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
|
|
48527
|
+
volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
48528
|
+
/** Mute toggle distinct from volume=0. Null when no mute surface.
|
|
48529
|
+
* Pair with `DeviceFeature.MediaPlayerMute`. */
|
|
48530
|
+
isMuted: boolean().nullable(),
|
|
48531
|
+
/** Active source / input. Empty when no source surface. */
|
|
48532
|
+
source: string(),
|
|
48533
|
+
/** Selectable sources. Empty when no source surface.
|
|
48534
|
+
* Pair with `DeviceFeature.MediaPlayerSelectSource`. */
|
|
48535
|
+
availableSources: array(string()),
|
|
48536
|
+
/** Currently-playing media info. Null when nothing is playing. */
|
|
48537
|
+
currentMedia: MediaInfoSchema.nullable(),
|
|
48538
|
+
/** Current playback position in ms. Null when not seekable or
|
|
48539
|
+
* nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
|
|
48540
|
+
position: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
48541
|
+
/** Total duration of the current media in ms. Null when unknown
|
|
48542
|
+
* (live stream) or nothing is playing. */
|
|
48543
|
+
duration: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
48544
|
+
/** Shuffle toggle. Null when no shuffle surface.
|
|
48545
|
+
* Pair with `DeviceFeature.MediaPlayerShuffle`. */
|
|
48546
|
+
shuffle: boolean().nullable(),
|
|
48547
|
+
/** Repeat mode. Null when no repeat surface.
|
|
48548
|
+
* Pair with `DeviceFeature.MediaPlayerRepeat`. */
|
|
48549
|
+
repeat: MediaPlayerRepeatSchema.nullable(),
|
|
48550
|
+
/** Ms epoch when the slice was last updated. */
|
|
48551
|
+
lastChangedAt: number()
|
|
48552
|
+
});
|
|
48553
|
+
var stepInput = object({
|
|
48554
|
+
deviceId: number().int().nonnegative(),
|
|
48555
|
+
/** How many presses: the caller's count (Alexa `volumeSteps`, `channelCount`). */
|
|
48556
|
+
steps: number().int().min(1).max(50).default(1)
|
|
48557
|
+
});
|
|
48558
|
+
var deviceOnly = object({ deviceId: number().int().nonnegative() });
|
|
48559
|
+
DeviceType.MediaPlayer, method(deviceOnly, _void(), {
|
|
48560
|
+
kind: "mutation",
|
|
48561
|
+
auth: "protected"
|
|
48562
|
+
}), method(deviceOnly, _void(), {
|
|
48563
|
+
kind: "mutation",
|
|
48564
|
+
auth: "protected"
|
|
48565
|
+
}), method(stepInput, _void(), {
|
|
48566
|
+
kind: "mutation",
|
|
48567
|
+
auth: "protected"
|
|
48568
|
+
}), method(stepInput, _void(), {
|
|
48569
|
+
kind: "mutation",
|
|
48570
|
+
auth: "protected"
|
|
48571
|
+
}), method(stepInput, _void(), {
|
|
48572
|
+
kind: "mutation",
|
|
48573
|
+
auth: "protected"
|
|
48574
|
+
}), method(stepInput, _void(), {
|
|
48575
|
+
kind: "mutation",
|
|
48576
|
+
auth: "protected"
|
|
48577
|
+
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
48578
|
+
kind: "mutation",
|
|
48579
|
+
auth: "protected"
|
|
48580
|
+
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
48581
|
+
kind: "mutation",
|
|
48582
|
+
auth: "protected"
|
|
48583
|
+
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
48584
|
+
kind: "mutation",
|
|
48585
|
+
auth: "protected"
|
|
48586
|
+
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
48587
|
+
kind: "mutation",
|
|
48588
|
+
auth: "protected"
|
|
48589
|
+
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
48590
|
+
kind: "mutation",
|
|
48591
|
+
auth: "protected"
|
|
48592
|
+
}), method(object({
|
|
48593
|
+
deviceId: number().int().nonnegative(),
|
|
48594
|
+
position: number().int().nonnegative()
|
|
48595
|
+
}), _void(), {
|
|
48596
|
+
kind: "mutation",
|
|
48597
|
+
auth: "protected"
|
|
48598
|
+
}), method(object({
|
|
48599
|
+
deviceId: number().int().nonnegative(),
|
|
48600
|
+
volumeLevel: number().min(0).max(100)
|
|
48601
|
+
}), _void(), {
|
|
48602
|
+
kind: "mutation",
|
|
48603
|
+
auth: "protected"
|
|
48604
|
+
}), method(object({
|
|
48605
|
+
deviceId: number().int().nonnegative(),
|
|
48606
|
+
muted: boolean()
|
|
48607
|
+
}), _void(), {
|
|
48608
|
+
kind: "mutation",
|
|
48609
|
+
auth: "protected"
|
|
48610
|
+
}), method(object({
|
|
48611
|
+
deviceId: number().int().nonnegative(),
|
|
48612
|
+
shuffle: boolean()
|
|
48613
|
+
}), _void(), {
|
|
48614
|
+
kind: "mutation",
|
|
48615
|
+
auth: "protected"
|
|
48616
|
+
}), method(object({
|
|
48617
|
+
deviceId: number().int().nonnegative(),
|
|
48618
|
+
repeat: MediaPlayerRepeatSchema
|
|
48619
|
+
}), _void(), {
|
|
48620
|
+
kind: "mutation",
|
|
48621
|
+
auth: "protected"
|
|
48622
|
+
}), method(object({
|
|
48623
|
+
deviceId: number().int().nonnegative(),
|
|
48624
|
+
source: string().min(1)
|
|
48625
|
+
}), _void(), {
|
|
48626
|
+
kind: "mutation",
|
|
48627
|
+
auth: "protected"
|
|
48628
|
+
}), method(object({
|
|
48629
|
+
deviceId: number().int().nonnegative(),
|
|
48630
|
+
/** Media identifier / URL. */
|
|
48631
|
+
mediaId: string().min(1),
|
|
48632
|
+
/** Media kind (`music`, `tvshow`, `movie`, `app`, …) — provider
|
|
48633
|
+
* passes it through to the upstream service. */
|
|
48634
|
+
mediaType: string().min(1)
|
|
48635
|
+
}), _void(), {
|
|
48636
|
+
kind: "mutation",
|
|
48637
|
+
auth: "protected"
|
|
48638
|
+
});
|
|
48163
48639
|
DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Heater, DeviceType.Dryer, DeviceType.Gateway, DeviceType.Camera, DeviceType.Fan, DeviceType.Lock, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Heater, 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, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder, DeviceType.Heater;
|
|
48164
48640
|
var NUMBER = {
|
|
48165
48641
|
kind: "fixed",
|
|
@@ -48607,49 +49083,6 @@ function actuator(input) {
|
|
|
48607
49083
|
};
|
|
48608
49084
|
}
|
|
48609
49085
|
new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
|
|
48610
|
-
var PowerSourceRefusalReasonSchema = _enum([
|
|
48611
|
-
"no-power-source",
|
|
48612
|
-
"switch-missing",
|
|
48613
|
-
"switch-offline",
|
|
48614
|
-
"switch-unknown",
|
|
48615
|
-
"restart-in-progress",
|
|
48616
|
-
"switch-call-failed"
|
|
48617
|
-
]);
|
|
48618
|
-
var PowerSourceActionResultSchema = discriminatedUnion("accepted", [object({ accepted: literal(true) }), object({
|
|
48619
|
-
accepted: literal(false),
|
|
48620
|
-
reason: PowerSourceRefusalReasonSchema,
|
|
48621
|
-
/** One line for the operator, naming the switch. */
|
|
48622
|
-
message: string()
|
|
48623
|
-
})]);
|
|
48624
|
-
var PowerSourceInfoSchema = object({
|
|
48625
|
-
/** False when the device has no power source linked; the rest is then empty. */
|
|
48626
|
-
linked: boolean(),
|
|
48627
|
-
switchDeviceId: number().nullable(),
|
|
48628
|
-
switchName: string().nullable(),
|
|
48629
|
-
/** The switch's on/off state, or null when it is unknown. Never false for unknown (D393). */
|
|
48630
|
-
switchOn: boolean().nullable(),
|
|
48631
|
-
switchOnline: boolean().nullable(),
|
|
48632
|
-
restartInProgress: boolean(),
|
|
48633
|
-
offSeconds: number().int()
|
|
48634
|
-
});
|
|
48635
|
-
object({
|
|
48636
|
-
/** Null while the switch has not reported: unknown is never `false` (D393). */
|
|
48637
|
-
switchOn: boolean().nullable(),
|
|
48638
|
-
offSeconds: number().int(),
|
|
48639
|
-
lastFetchedAt: number()
|
|
48640
|
-
});
|
|
48641
|
-
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;
|
|
48642
|
-
var deviceInput$2 = object({ deviceId: number().int().nonnegative() });
|
|
48643
|
-
systemMethod(deviceInput$2, PowerSourceInfoSchema, { auth: "admin" }), systemMethod(deviceInput$2, PowerSourceActionResultSchema, {
|
|
48644
|
-
kind: "mutation",
|
|
48645
|
-
auth: "admin"
|
|
48646
|
-
}), systemMethod(deviceInput$2, PowerSourceActionResultSchema, {
|
|
48647
|
-
kind: "mutation",
|
|
48648
|
-
auth: "admin"
|
|
48649
|
-
}), systemMethod(deviceInput$2, PowerSourceActionResultSchema, {
|
|
48650
|
-
kind: "mutation",
|
|
48651
|
-
auth: "admin"
|
|
48652
|
-
});
|
|
48653
49086
|
DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
|
|
48654
49087
|
DeviceType.Camera;
|
|
48655
49088
|
`${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
|
|
@@ -48677,6 +49110,168 @@ var ComposableCapabilitiesSchema = object({ caps: array(object({
|
|
|
48677
49110
|
}).optional()
|
|
48678
49111
|
})) });
|
|
48679
49112
|
/**
|
|
49113
|
+
* A saved Customize assignment (D936). The schema is the type: callers use
|
|
49114
|
+
* `z.infer`, they do not restate the shape.
|
|
49115
|
+
*/
|
|
49116
|
+
var OverrideSourceSchema = object({
|
|
49117
|
+
addonId: string().min(1),
|
|
49118
|
+
stableId: string().min(1),
|
|
49119
|
+
cap: string().min(1),
|
|
49120
|
+
fieldPath: string().min(1)
|
|
49121
|
+
});
|
|
49122
|
+
var IdentityTransformDraftSchema = object({ kind: literal("identity") });
|
|
49123
|
+
var InvertTransformDraftSchema = object({ kind: literal("invert") });
|
|
49124
|
+
var ThresholdTransformDraftSchema = object({
|
|
49125
|
+
kind: literal("threshold"),
|
|
49126
|
+
comparison: _enum(["gte", "lte"]),
|
|
49127
|
+
threshold: number(),
|
|
49128
|
+
startDwellMs: number().nonnegative(),
|
|
49129
|
+
stopDwellMs: number().nonnegative()
|
|
49130
|
+
});
|
|
49131
|
+
var CustomizeTransformDraftSchema = discriminatedUnion("kind", [
|
|
49132
|
+
IdentityTransformDraftSchema,
|
|
49133
|
+
InvertTransformDraftSchema,
|
|
49134
|
+
ThresholdTransformDraftSchema,
|
|
49135
|
+
object({
|
|
49136
|
+
kind: literal("linear"),
|
|
49137
|
+
scale: number(),
|
|
49138
|
+
offset: number(),
|
|
49139
|
+
clampMin: number().optional(),
|
|
49140
|
+
clampMax: number().optional()
|
|
49141
|
+
}),
|
|
49142
|
+
object({
|
|
49143
|
+
kind: literal("enum-map"),
|
|
49144
|
+
mapping: record(string(), string().nullable())
|
|
49145
|
+
}),
|
|
49146
|
+
object({ kind: literal("number") })
|
|
49147
|
+
]);
|
|
49148
|
+
var ScalarOverrideDraftSchema = object({
|
|
49149
|
+
kind: literal("scalar"),
|
|
49150
|
+
cap: string().min(1),
|
|
49151
|
+
targetId: string().min(1),
|
|
49152
|
+
source: OverrideSourceSchema,
|
|
49153
|
+
transform: CustomizeTransformDraftSchema
|
|
49154
|
+
});
|
|
49155
|
+
var KeyedArrayOverrideDraftSchema = object({
|
|
49156
|
+
kind: literal("keyedArray"),
|
|
49157
|
+
cap: string().min(1),
|
|
49158
|
+
targetId: string().min(1),
|
|
49159
|
+
key: string().min(1),
|
|
49160
|
+
label: string().min(1),
|
|
49161
|
+
fieldPath: string().min(1),
|
|
49162
|
+
source: OverrideSourceSchema,
|
|
49163
|
+
transform: CustomizeTransformDraftSchema
|
|
49164
|
+
});
|
|
49165
|
+
var PulseOverrideDraftSchema = object({
|
|
49166
|
+
kind: literal("pulse"),
|
|
49167
|
+
cap: string().min(1),
|
|
49168
|
+
targetId: string().min(1),
|
|
49169
|
+
enabled: boolean(),
|
|
49170
|
+
debounceMs: number().nonnegative(),
|
|
49171
|
+
edges: array(_enum([
|
|
49172
|
+
"rising",
|
|
49173
|
+
"button",
|
|
49174
|
+
"native-event"
|
|
49175
|
+
])).min(1).optional(),
|
|
49176
|
+
sources: array(OverrideSourceSchema).min(1)
|
|
49177
|
+
});
|
|
49178
|
+
var SpanSourceDraftSchema = OverrideSourceSchema.extend({ mode: _enum([
|
|
49179
|
+
"in-addition",
|
|
49180
|
+
"confirm",
|
|
49181
|
+
"replace"
|
|
49182
|
+
]).optional() });
|
|
49183
|
+
discriminatedUnion("kind", [
|
|
49184
|
+
ScalarOverrideDraftSchema,
|
|
49185
|
+
KeyedArrayOverrideDraftSchema,
|
|
49186
|
+
PulseOverrideDraftSchema,
|
|
49187
|
+
object({
|
|
49188
|
+
kind: literal("span"),
|
|
49189
|
+
cap: string().min(1),
|
|
49190
|
+
targetId: string().min(1),
|
|
49191
|
+
timeoutMs: number().nonnegative(),
|
|
49192
|
+
cooldownMs: number().nonnegative().optional(),
|
|
49193
|
+
sources: array(SpanSourceDraftSchema)
|
|
49194
|
+
})
|
|
49195
|
+
]);
|
|
49196
|
+
object({
|
|
49197
|
+
targetId: string().min(1),
|
|
49198
|
+
configured: boolean(),
|
|
49199
|
+
source: _enum(["native", "assigned"]),
|
|
49200
|
+
state: _enum([
|
|
49201
|
+
"active",
|
|
49202
|
+
"stale",
|
|
49203
|
+
"released",
|
|
49204
|
+
"unavailable"
|
|
49205
|
+
]),
|
|
49206
|
+
reason: string().nullable()
|
|
49207
|
+
});
|
|
49208
|
+
/**
|
|
49209
|
+
* What a running-threshold proposal says (planning#405). The schema is the
|
|
49210
|
+
* type: the algorithm, the capability method and the UI all read `z.infer`.
|
|
49211
|
+
*
|
|
49212
|
+
* A proposal is read-only advice: it carries the numbers it was derived from
|
|
49213
|
+
* (`evidence`) so the operator can judge it, and an `insufficient` answer names
|
|
49214
|
+
* WHY with a typed reason and the catalog defaults, never an invented number.
|
|
49215
|
+
*/
|
|
49216
|
+
var RunningProposalTierSchema = _enum(["raw", "5m"]);
|
|
49217
|
+
var RunningProposalConfidenceSchema = _enum([
|
|
49218
|
+
"high",
|
|
49219
|
+
"medium",
|
|
49220
|
+
"low"
|
|
49221
|
+
]);
|
|
49222
|
+
var RunningProposalInsufficientReasonSchema = _enum([
|
|
49223
|
+
"no-history",
|
|
49224
|
+
"span-too-short",
|
|
49225
|
+
"truncated",
|
|
49226
|
+
"no-idle-floor",
|
|
49227
|
+
"no-runs",
|
|
49228
|
+
"no-separation"
|
|
49229
|
+
]);
|
|
49230
|
+
var RunningProposalReplaySchema = object({
|
|
49231
|
+
/** Intervals the real dwell expression held true over the window. */
|
|
49232
|
+
intervals: number().int().nonnegative(),
|
|
49233
|
+
/** Intervals outside every run, or a second interval inside one. */
|
|
49234
|
+
extra: number().int().nonnegative(),
|
|
49235
|
+
/** Runs without an interval of their own (none, or merged into a neighbour). */
|
|
49236
|
+
missed: number().int().nonnegative()
|
|
49237
|
+
});
|
|
49238
|
+
var RunningProposalEvidenceSchema = object({
|
|
49239
|
+
tier: RunningProposalTierSchema,
|
|
49240
|
+
spanMs: number().nonnegative(),
|
|
49241
|
+
from: number(),
|
|
49242
|
+
to: number(),
|
|
49243
|
+
points: number().int().nonnegative(),
|
|
49244
|
+
runsFound: number().int().nonnegative(),
|
|
49245
|
+
pausesFound: number().int().nonnegative(),
|
|
49246
|
+
longestPauseMs: number().nonnegative(),
|
|
49247
|
+
/** Episodes shorter than ~90 s (an anti-crease pulse, a display waking): never a run, never a pause. */
|
|
49248
|
+
pulsesFound: number().int().nonnegative(),
|
|
49249
|
+
longestPulseMs: number().nonnegative(),
|
|
49250
|
+
idleFloorW: number(),
|
|
49251
|
+
noiseCeilingW: number(),
|
|
49252
|
+
plateauW: number(),
|
|
49253
|
+
replay: RunningProposalReplaySchema
|
|
49254
|
+
});
|
|
49255
|
+
var RunningProposalInsufficientEvidenceSchema = object({
|
|
49256
|
+
spanMs: number().nonnegative(),
|
|
49257
|
+
points: number().int().nonnegative(),
|
|
49258
|
+
runsFound: number().int().nonnegative()
|
|
49259
|
+
});
|
|
49260
|
+
var RunningThresholdProposalSchema = discriminatedUnion("state", [object({
|
|
49261
|
+
state: literal("proposal"),
|
|
49262
|
+
transform: ThresholdTransformDraftSchema,
|
|
49263
|
+
evidence: RunningProposalEvidenceSchema,
|
|
49264
|
+
confidence: RunningProposalConfidenceSchema,
|
|
49265
|
+
/** Why the confidence is not higher; empty when nothing held it back. */
|
|
49266
|
+
notes: array(string())
|
|
49267
|
+
}), object({
|
|
49268
|
+
state: literal("insufficient"),
|
|
49269
|
+
reason: RunningProposalInsufficientReasonSchema,
|
|
49270
|
+
evidence: RunningProposalInsufficientEvidenceSchema,
|
|
49271
|
+
/** The catalog defaults, offered as what stands when nothing better is known. */
|
|
49272
|
+
fallback: ThresholdTransformDraftSchema
|
|
49273
|
+
})]);
|
|
49274
|
+
/**
|
|
48680
49275
|
* The report of a one-shot "move this owner's plaintext secrets into the
|
|
48681
49276
|
* keychain" run (D878). COUNTS only - never a value, never a ref.
|
|
48682
49277
|
*
|
|
@@ -48966,6 +49561,14 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
|
|
|
48966
49561
|
kind: "mutation",
|
|
48967
49562
|
auth: "admin",
|
|
48968
49563
|
access: "view"
|
|
49564
|
+
}), method(object({
|
|
49565
|
+
deviceId: number().int(),
|
|
49566
|
+
cap: string().min(1),
|
|
49567
|
+
fieldPath: string().min(1),
|
|
49568
|
+
deviceType: _enum(DeviceType).optional()
|
|
49569
|
+
}), RunningThresholdProposalSchema, {
|
|
49570
|
+
auth: "admin",
|
|
49571
|
+
access: "view"
|
|
48969
49572
|
}), method(object({
|
|
48970
49573
|
blockId: string().min(1),
|
|
48971
49574
|
trace: AutomationRunTraceSchema.optional(),
|
|
@@ -49774,7 +50377,27 @@ method(object({
|
|
|
49774
50377
|
* exposes only the COMMON surface: link state, supported device
|
|
49775
50378
|
* kinds (so the UI can filter the device picker), and per-device
|
|
49776
50379
|
* expose/unexpose.
|
|
49777
|
-
|
|
50380
|
+
*
|
|
50381
|
+
* INSTANCES. One export addon can serve several independent targets (a
|
|
50382
|
+
* HomeKit bridge, a Home Assistant broker). `listInstances` names them.
|
|
50383
|
+
* `instanceId` on a READ is optional: absent = the whole addon (linked if any
|
|
50384
|
+
* enabled instance is, `exposedDeviceCount` = distinct devices, `setup` = the
|
|
50385
|
+
* default instance's); present = that instance; an id the provider does not
|
|
50386
|
+
* serve is refused with `ExportInstanceUnknownError` (`export-instance-unknown`),
|
|
50387
|
+
* never answered empty. `instanceId` on a WRITE is REQUIRED: a membership
|
|
50388
|
+
* change always names the target it changes. A single-instance provider
|
|
50389
|
+
* accepts an absent id on a read or its own id, and refuses any other.
|
|
50390
|
+
*/
|
|
50391
|
+
/** Which instance of an export addon a call addresses. Absent on a read = the whole addon. */
|
|
50392
|
+
var InstanceIdSchema$1 = string().min(1);
|
|
50393
|
+
var InstancePinSchema = object({ instanceId: InstanceIdSchema$1.optional() });
|
|
50394
|
+
/** One independent export target of an addon. */
|
|
50395
|
+
var ExportInstanceSchema = object({
|
|
50396
|
+
instanceId: InstanceIdSchema$1,
|
|
50397
|
+
name: string(),
|
|
50398
|
+
enabled: boolean(),
|
|
50399
|
+
kind: string().optional()
|
|
50400
|
+
});
|
|
49778
50401
|
var LinkStateSchema = _enum([
|
|
49779
50402
|
"unlinked",
|
|
49780
50403
|
"linked",
|
|
@@ -49825,10 +50448,14 @@ var ExposedDeviceSchema = object({
|
|
|
49825
50448
|
capabilities: array(string()).optional()
|
|
49826
50449
|
});
|
|
49827
50450
|
var ExposeInputSchema = object({
|
|
50451
|
+
instanceId: InstanceIdSchema$1,
|
|
49828
50452
|
deviceId: string(),
|
|
49829
50453
|
capabilities: array(string()).optional()
|
|
49830
50454
|
});
|
|
49831
|
-
var UnexposeInputSchema = object({
|
|
50455
|
+
var UnexposeInputSchema = object({
|
|
50456
|
+
instanceId: InstanceIdSchema$1,
|
|
50457
|
+
deviceId: string()
|
|
50458
|
+
});
|
|
49832
50459
|
/**
|
|
49833
50460
|
* What an exporter may expose besides devices (D968). An action and a
|
|
49834
50461
|
* notification target are LIST ENTRIES, never devices (D954): they enter an
|
|
@@ -49857,15 +50484,21 @@ var ExposedEntitySchema = object({
|
|
|
49857
50484
|
entity: ExportEntityRefSchema,
|
|
49858
50485
|
exposedAs: string().optional()
|
|
49859
50486
|
});
|
|
49860
|
-
var ExposeEntityInputSchema = object({
|
|
49861
|
-
|
|
49862
|
-
|
|
50487
|
+
var ExposeEntityInputSchema = object({
|
|
50488
|
+
instanceId: InstanceIdSchema$1,
|
|
50489
|
+
entity: ExportEntityRefSchema
|
|
50490
|
+
});
|
|
50491
|
+
var UnexposeEntityInputSchema = object({
|
|
50492
|
+
instanceId: InstanceIdSchema$1,
|
|
50493
|
+
entity: ExportEntityRefSchema
|
|
50494
|
+
});
|
|
50495
|
+
method(InstancePinSchema, DeviceExportStatusSchema), method(_void(), array(ExportInstanceSchema)), method(InstancePinSchema, array(DeviceKindSchema)), method(InstancePinSchema, array(ExposedDeviceSchema)), method(ExposeInputSchema, _void(), {
|
|
49863
50496
|
kind: "mutation",
|
|
49864
50497
|
auth: "admin"
|
|
49865
50498
|
}), method(UnexposeInputSchema, _void(), {
|
|
49866
50499
|
kind: "mutation",
|
|
49867
50500
|
auth: "admin"
|
|
49868
|
-
}), optionalMethod(_void(), array(ExportEntityKindSchema)), optionalMethod(
|
|
50501
|
+
}), optionalMethod(_void(), array(ExportEntityKindSchema)), optionalMethod(InstancePinSchema, array(ExposedEntitySchema)), optionalMethod(ExposeEntityInputSchema, _void(), {
|
|
49869
50502
|
kind: "mutation",
|
|
49870
50503
|
auth: "admin"
|
|
49871
50504
|
}), optionalMethod(UnexposeEntityInputSchema, _void(), {
|
|
@@ -51745,7 +52378,7 @@ object({
|
|
|
51745
52378
|
/** Ms epoch of the last value received from the appliance; 0 = never. */
|
|
51746
52379
|
lastFetchedAt: number()
|
|
51747
52380
|
});
|
|
51748
|
-
var DeviceIdSchema$
|
|
52381
|
+
var DeviceIdSchema$2 = number().int().nonnegative();
|
|
51749
52382
|
/** A value a program option or setting can take. */
|
|
51750
52383
|
var DishwasherValueSchema = union([
|
|
51751
52384
|
boolean(),
|
|
@@ -51809,77 +52442,77 @@ var DishwasherOptionsSchema = object({
|
|
|
51809
52442
|
options: array(DishwasherOptionDescriptorSchema)
|
|
51810
52443
|
});
|
|
51811
52444
|
DeviceType.Dishwasher, method(object({
|
|
51812
|
-
deviceId: DeviceIdSchema$
|
|
52445
|
+
deviceId: DeviceIdSchema$2,
|
|
51813
52446
|
program: string().min(1).optional(),
|
|
51814
52447
|
options: record(string(), DishwasherValueSchema).optional()
|
|
51815
52448
|
}), _void(), {
|
|
51816
52449
|
kind: "mutation",
|
|
51817
52450
|
auth: "protected"
|
|
51818
|
-
}), method(object({ deviceId: DeviceIdSchema$
|
|
52451
|
+
}), method(object({ deviceId: DeviceIdSchema$2 }), _void(), {
|
|
51819
52452
|
kind: "mutation",
|
|
51820
52453
|
auth: "protected"
|
|
51821
|
-
}), method(object({ deviceId: DeviceIdSchema$
|
|
52454
|
+
}), method(object({ deviceId: DeviceIdSchema$2 }), _void(), {
|
|
51822
52455
|
kind: "mutation",
|
|
51823
52456
|
auth: "protected"
|
|
51824
|
-
}), method(object({ deviceId: DeviceIdSchema$
|
|
52457
|
+
}), method(object({ deviceId: DeviceIdSchema$2 }), _void(), {
|
|
51825
52458
|
kind: "mutation",
|
|
51826
52459
|
auth: "protected"
|
|
51827
52460
|
}), method(object({
|
|
51828
|
-
deviceId: DeviceIdSchema$
|
|
52461
|
+
deviceId: DeviceIdSchema$2,
|
|
51829
52462
|
program: string().min(1)
|
|
51830
52463
|
}), _void(), {
|
|
51831
52464
|
kind: "mutation",
|
|
51832
52465
|
auth: "protected"
|
|
51833
52466
|
}), method(object({
|
|
51834
|
-
deviceId: DeviceIdSchema$
|
|
52467
|
+
deviceId: DeviceIdSchema$2,
|
|
51835
52468
|
on: boolean()
|
|
51836
52469
|
}), _void(), {
|
|
51837
52470
|
kind: "mutation",
|
|
51838
52471
|
auth: "protected"
|
|
51839
52472
|
}), method(object({
|
|
51840
|
-
deviceId: DeviceIdSchema$
|
|
52473
|
+
deviceId: DeviceIdSchema$2,
|
|
51841
52474
|
on: boolean()
|
|
51842
52475
|
}), _void(), {
|
|
51843
52476
|
kind: "mutation",
|
|
51844
52477
|
auth: "protected"
|
|
51845
52478
|
}), method(object({
|
|
51846
|
-
deviceId: DeviceIdSchema$
|
|
52479
|
+
deviceId: DeviceIdSchema$2,
|
|
51847
52480
|
on: boolean()
|
|
51848
52481
|
}), _void(), {
|
|
51849
52482
|
kind: "mutation",
|
|
51850
52483
|
auth: "protected"
|
|
51851
52484
|
}), method(object({
|
|
51852
|
-
deviceId: DeviceIdSchema$
|
|
52485
|
+
deviceId: DeviceIdSchema$2,
|
|
51853
52486
|
on: boolean()
|
|
51854
52487
|
}), _void(), {
|
|
51855
52488
|
kind: "mutation",
|
|
51856
52489
|
auth: "protected"
|
|
51857
52490
|
}), method(object({
|
|
51858
|
-
deviceId: DeviceIdSchema$
|
|
52491
|
+
deviceId: DeviceIdSchema$2,
|
|
51859
52492
|
on: boolean()
|
|
51860
52493
|
}), _void(), {
|
|
51861
52494
|
kind: "mutation",
|
|
51862
52495
|
auth: "protected"
|
|
51863
52496
|
}), method(object({
|
|
51864
|
-
deviceId: DeviceIdSchema$
|
|
52497
|
+
deviceId: DeviceIdSchema$2,
|
|
51865
52498
|
on: boolean()
|
|
51866
52499
|
}), _void(), {
|
|
51867
52500
|
kind: "mutation",
|
|
51868
52501
|
auth: "protected"
|
|
51869
52502
|
}), method(object({
|
|
51870
|
-
deviceId: DeviceIdSchema$
|
|
52503
|
+
deviceId: DeviceIdSchema$2,
|
|
51871
52504
|
on: boolean()
|
|
51872
52505
|
}), _void(), {
|
|
51873
52506
|
kind: "mutation",
|
|
51874
52507
|
auth: "protected"
|
|
51875
52508
|
}), method(object({
|
|
51876
|
-
deviceId: DeviceIdSchema$
|
|
52509
|
+
deviceId: DeviceIdSchema$2,
|
|
51877
52510
|
key: string().min(1),
|
|
51878
52511
|
value: DishwasherValueSchema
|
|
51879
52512
|
}), _void(), {
|
|
51880
52513
|
kind: "mutation",
|
|
51881
52514
|
auth: "protected"
|
|
51882
|
-
}), method(object({ deviceId: DeviceIdSchema$
|
|
52515
|
+
}), method(object({ deviceId: DeviceIdSchema$2 }), DishwasherOptionsSchema, { auth: "protected" });
|
|
51883
52516
|
/**
|
|
51884
52517
|
* Palette entry ids (D939). Audio is the one gradient; `audio-*` leaves are
|
|
51885
52518
|
* not entries. `heatmapScrub` is a mark, not an event kind.
|
|
@@ -53066,15 +53699,15 @@ var FanPresetCaptureResultSchema = object({
|
|
|
53066
53699
|
items: array(FanCaptureItemSchema),
|
|
53067
53700
|
omitted: array(FanCaptureOmissionSchema)
|
|
53068
53701
|
});
|
|
53069
|
-
var deviceInput$
|
|
53702
|
+
var deviceInput$2 = object({ deviceId: number().int().nonnegative() });
|
|
53070
53703
|
DeviceType.Fan;
|
|
53071
|
-
method(deviceInput$
|
|
53704
|
+
method(deviceInput$2, array(FanPresetSchema), { auth: "protected" }), method(deviceInput$2.extend({ presets: array(FanPresetSchema).max(12) }), FanPresetsSetResultSchema, {
|
|
53072
53705
|
kind: "mutation",
|
|
53073
53706
|
auth: "admin"
|
|
53074
|
-
}), method(deviceInput$
|
|
53707
|
+
}), method(deviceInput$2.extend({ presetId: string().min(1) }), FanPresetApplyResultSchema, {
|
|
53075
53708
|
kind: "mutation",
|
|
53076
53709
|
auth: "protected"
|
|
53077
|
-
}), method(deviceInput$
|
|
53710
|
+
}), method(deviceInput$2.extend({
|
|
53078
53711
|
fields: array(FanCaptureFieldSchema).optional(),
|
|
53079
53712
|
steps: array(ActionStepSchema).max(20).default([])
|
|
53080
53713
|
}), FanPresetCaptureResultSchema, { auth: "protected" });
|
|
@@ -53535,11 +54168,7 @@ var CreateIntegrationInputSchema = object({
|
|
|
53535
54168
|
info: record(string(), unknown()).optional(),
|
|
53536
54169
|
/** `stableId` of the discovery candidate the form was seeded from (D984). */
|
|
53537
54170
|
originStableId: string().optional(),
|
|
53538
|
-
settings: record(string(), unknown()).optional()
|
|
53539
|
-
/** Keys of `settings` the form's schema declares secret (by type) — kept in
|
|
53540
|
-
* the keychain, not the table (D787). */
|
|
53541
|
-
secretKeys: array(string()).optional(),
|
|
53542
|
-
skipRestart: boolean().optional()
|
|
54171
|
+
settings: record(string(), unknown()).optional()
|
|
53543
54172
|
});
|
|
53544
54173
|
var UpdateIntegrationInputSchema = object({
|
|
53545
54174
|
id: string(),
|
|
@@ -53560,16 +54189,6 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
|
|
|
53560
54189
|
}), method(object({ id: string() }), DeleteIntegrationResultSchema, {
|
|
53561
54190
|
kind: "mutation",
|
|
53562
54191
|
auth: "admin"
|
|
53563
|
-
}), method(object({ id: string() }), record(string(), unknown()), { auth: "admin" }), method(object({
|
|
53564
|
-
id: string(),
|
|
53565
|
-
settings: record(string(), unknown()),
|
|
53566
|
-
/** Keys the form's schema declares secret (by type) — keychain-stored (D787). */
|
|
53567
|
-
secretKeys: array(string()).optional(),
|
|
53568
|
-
/** The integration's own addon applies the change in-process and asks the hub not to respawn it. */
|
|
53569
|
-
skipRestart: boolean().optional()
|
|
53570
|
-
}), object({ success: boolean() }), {
|
|
53571
|
-
kind: "mutation",
|
|
53572
|
-
auth: "admin"
|
|
53573
54192
|
}), method(_void(), array(AvailableIntegrationTypeSchema), { auth: "admin" }), method(object({
|
|
53574
54193
|
addonId: string(),
|
|
53575
54194
|
settings: record(string(), unknown())
|
|
@@ -53598,13 +54217,65 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
|
|
|
53598
54217
|
*/
|
|
53599
54218
|
var ConfigSchemaPassthrough$3 = custom();
|
|
53600
54219
|
var IntegrationInstanceIdSchema = string().min(1);
|
|
54220
|
+
var IntegrationInstanceModeSchema = _enum(["single", "multiple"]);
|
|
54221
|
+
/**
|
|
54222
|
+
* One kind of instance a provider can create (an adapter, an account type). The sign-in and
|
|
54223
|
+
* connect fields follow the `broker` contract (`broker.cap.ts`): `signIn.basePath` is a
|
|
54224
|
+
* hub-relative route set speaking the sign-in contract, `connectPath` / `disconnectPath` open an
|
|
54225
|
+
* interactive account link (popup) and may carry `{{brokerId}}`, which the UI replaces with the
|
|
54226
|
+
* instance id.
|
|
54227
|
+
*/
|
|
54228
|
+
var IntegrationInstanceKindSchema = object({
|
|
54229
|
+
/** Stable id of the kind, sent back as `createInstance.kind`. */
|
|
54230
|
+
id: string().min(1),
|
|
54231
|
+
label: string().min(1),
|
|
54232
|
+
/** The kind signs in through a server-held interactive session. */
|
|
54233
|
+
signIn: object({
|
|
54234
|
+
basePath: string().min(1),
|
|
54235
|
+
methods: array(_enum(["device-code", "proxy"])).optional()
|
|
54236
|
+
}).optional(),
|
|
54237
|
+
/** Relative hub path to start an interactive account link of an instance of this kind. */
|
|
54238
|
+
connectPath: string().min(1).optional(),
|
|
54239
|
+
/** Relative hub path to disconnect that account. */
|
|
54240
|
+
disconnectPath: string().min(1).optional()
|
|
54241
|
+
});
|
|
53601
54242
|
var IntegrationInstanceDescriptionSchema = object({
|
|
54243
|
+
/** What the instances are for the Integrations page; see {@link IntegrationInstanceRoleSchema}. */
|
|
54244
|
+
role: _enum([
|
|
54245
|
+
"devices",
|
|
54246
|
+
"services",
|
|
54247
|
+
"both"
|
|
54248
|
+
]),
|
|
54249
|
+
/** How the devices of an instance are found; absent means `{ mode: 'none' }`. */
|
|
54250
|
+
discovery: object({
|
|
54251
|
+
mode: _enum([
|
|
54252
|
+
"none",
|
|
54253
|
+
"lan",
|
|
54254
|
+
"account"
|
|
54255
|
+
]),
|
|
54256
|
+
scopedToInstance: boolean()
|
|
54257
|
+
}).optional(),
|
|
53602
54258
|
/** `single`: at most one instance can exist (Loki). `multiple`: an account each. */
|
|
53603
|
-
instanceMode:
|
|
54259
|
+
instanceMode: IntegrationInstanceModeSchema,
|
|
53604
54260
|
/** What the instance is, for the UI (`destination`, `relay`, `account`). */
|
|
53605
54261
|
kind: string(),
|
|
53606
54262
|
/** True when `testInstance` really probes the remote. */
|
|
53607
|
-
canTest: boolean()
|
|
54263
|
+
canTest: boolean(),
|
|
54264
|
+
/**
|
|
54265
|
+
* The kinds an instance can be of. Absent or one entry: nothing to choose; more than one: the
|
|
54266
|
+
* Add flow asks which first and sends it as `createInstance.kind`.
|
|
54267
|
+
*/
|
|
54268
|
+
kinds: array(IntegrationInstanceKindSchema).optional(),
|
|
54269
|
+
/**
|
|
54270
|
+
* The provider accepts `createInstance.id` for an id that once existed (an adopted id, because
|
|
54271
|
+
* devices carry it). The UI then offers "restore under its old id" on Add. Absent means no.
|
|
54272
|
+
*/
|
|
54273
|
+
allowRestoreId: boolean().optional(),
|
|
54274
|
+
/**
|
|
54275
|
+
* `createInstance` without an `id` works: the provider mints it. Absent means the caller must
|
|
54276
|
+
* send an id (the UI then mints an opaque one).
|
|
54277
|
+
*/
|
|
54278
|
+
mintsId: boolean().optional()
|
|
53608
54279
|
});
|
|
53609
54280
|
var IntegrationInstanceStatusSchema = object({
|
|
53610
54281
|
state: _enum([
|
|
@@ -53613,7 +54284,9 @@ var IntegrationInstanceStatusSchema = object({
|
|
|
53613
54284
|
"error",
|
|
53614
54285
|
"unknown"
|
|
53615
54286
|
]),
|
|
53616
|
-
detail: string().optional()
|
|
54287
|
+
detail: string().optional(),
|
|
54288
|
+
/** A short human word for the state ("Signed in", "Token expired"); shown instead of `state`. */
|
|
54289
|
+
label: string().optional()
|
|
53617
54290
|
});
|
|
53618
54291
|
var IntegrationInstanceSchema = object({
|
|
53619
54292
|
id: IntegrationInstanceIdSchema,
|
|
@@ -53621,13 +54294,18 @@ var IntegrationInstanceSchema = object({
|
|
|
53621
54294
|
/** The addon's own rule (an URL is present, a client id is present...). */
|
|
53622
54295
|
configured: boolean(),
|
|
53623
54296
|
/**
|
|
53624
|
-
* The instance's own on/off switch (D996)
|
|
53625
|
-
*
|
|
54297
|
+
* The instance's own on/off switch (D996), derived from the record body (`enabled !== false`,
|
|
54298
|
+
* so a row written before the field existed is on). Absent when the `integrations` registry
|
|
54299
|
+
* owns the switch (`describe().role` is `devices`): read it from the registry row.
|
|
53626
54300
|
*/
|
|
53627
|
-
enabled: boolean(),
|
|
54301
|
+
enabled: boolean().optional(),
|
|
54302
|
+
/** The kind of this instance (one of `describe().kinds[].id`), when the provider has kinds. */
|
|
54303
|
+
kind: string().optional(),
|
|
53628
54304
|
/** Values for the instance form; a secret is a `SecretFieldView`, never text. */
|
|
53629
54305
|
formValues: record(string(), unknown()),
|
|
53630
|
-
status: IntegrationInstanceStatusSchema.optional()
|
|
54306
|
+
status: IntegrationInstanceStatusSchema.optional(),
|
|
54307
|
+
/** Epoch ms of the last probe of the remote that produced `status`; absent when never checked. */
|
|
54308
|
+
lastCheckedAt: number().optional()
|
|
53631
54309
|
});
|
|
53632
54310
|
var IntegrationInstancePatchSchema = object({
|
|
53633
54311
|
id: IntegrationInstanceIdSchema,
|
|
@@ -53645,10 +54323,14 @@ var integrationInstancesCapability = {
|
|
|
53645
54323
|
instanceFormSchema: method(object({ id: IntegrationInstanceIdSchema.nullable() }), ConfigSchemaPassthrough$3, { auth: "admin" }),
|
|
53646
54324
|
listInstances: method(_void(), array(IntegrationInstanceSchema).readonly(), { auth: "admin" }),
|
|
53647
54325
|
getInstance: method(object({ id: IntegrationInstanceIdSchema }), IntegrationInstanceSchema.nullable(), { auth: "admin" }),
|
|
53648
|
-
/**
|
|
54326
|
+
/**
|
|
54327
|
+
* Insert the typed record. With `id` it is stored under that id (a duplicate id is refused);
|
|
54328
|
+
* without one the PROVIDER mints it and answers it. `kind` picks one of `describe().kinds`.
|
|
54329
|
+
*/
|
|
53649
54330
|
createInstance: method(object({
|
|
53650
|
-
id: IntegrationInstanceIdSchema,
|
|
53651
|
-
name: string().min(1)
|
|
54331
|
+
id: IntegrationInstanceIdSchema.optional(),
|
|
54332
|
+
name: string().min(1),
|
|
54333
|
+
kind: string().min(1).optional()
|
|
53652
54334
|
}), object({ id: string() }), {
|
|
53653
54335
|
kind: "mutation",
|
|
53654
54336
|
auth: "admin"
|
|
@@ -55480,6 +56162,11 @@ var LogEntrySchema = object({
|
|
|
55480
56162
|
* flight, and not asking for the next slice ends the scan.
|
|
55481
56163
|
*/
|
|
55482
56164
|
var LogReadQuerySchema = object({
|
|
56165
|
+
/**
|
|
56166
|
+
* Which destination INSTANCE answers (a destination addon may hold several records). Absent ⇒
|
|
56167
|
+
* the provider's first readable one, so a caller written before instances existed is unchanged.
|
|
56168
|
+
*/
|
|
56169
|
+
instanceId: string().min(1).max(200).optional(),
|
|
55483
56170
|
/** Correlates `cancelRead`. Caller-chosen, unique per in-flight read. */
|
|
55484
56171
|
requestId: string().min(1).max(64).optional(),
|
|
55485
56172
|
/** The device the line is about (`tags.deviceId`, numeric). */
|
|
@@ -55542,6 +56229,8 @@ var LogReadResultSchema = object({
|
|
|
55542
56229
|
/** True when `cancelRead` ended this slice early. */
|
|
55543
56230
|
cancelled: boolean().optional()
|
|
55544
56231
|
});
|
|
56232
|
+
/** Names one destination instance; absent ⇒ the provider's first readable one. */
|
|
56233
|
+
var LogInstanceSelectorSchema = object({ instanceId: string().min(1).max(200).optional() });
|
|
55545
56234
|
var LogReadCapabilitiesSchema = object({
|
|
55546
56235
|
/** False ⇒ this destination only ships lines (console, forwarder, an unconfigured Loki). */
|
|
55547
56236
|
readable: boolean(),
|
|
@@ -55560,7 +56249,7 @@ var LogReadCapabilitiesSchema = object({
|
|
|
55560
56249
|
/** How far back the store reaches, when the provider knows (days). */
|
|
55561
56250
|
retentionDays: number().nullable()
|
|
55562
56251
|
});
|
|
55563
|
-
method(LogEntrySchema, _void(), { kind: "mutation" }), optionalMethod(
|
|
56252
|
+
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() }), {
|
|
55564
56253
|
kind: "mutation",
|
|
55565
56254
|
auth: "admin"
|
|
55566
56255
|
});
|
|
@@ -55664,164 +56353,6 @@ var LoginMethodContributionSchema = discriminatedUnion("kind", [
|
|
|
55664
56353
|
]);
|
|
55665
56354
|
method(_void(), array(LoginMethodContributionSchema).readonly());
|
|
55666
56355
|
/**
|
|
55667
|
-
* Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
|
|
55668
|
-
* Apple TV, Spotify, Roku, generic OTT receivers, …) on
|
|
55669
|
-
* `DeviceType.MediaPlayer`. Disjoint from the camera streaming
|
|
55670
|
-
* surface — those use `DeviceType.Camera` + the broker pipeline.
|
|
55671
|
-
*
|
|
55672
|
-
* The cap declares a wide method surface; not every backend supports
|
|
55673
|
-
* every method. Drivers signal sub-capability via the
|
|
55674
|
-
* `DeviceFeature.MediaPlayerXxx` flags (paired one-to-one with the
|
|
55675
|
-
* corresponding method) so the UI gates buttons without a slice
|
|
55676
|
-
* fetch — same pattern as `PanTiltZoom` for cameras.
|
|
55677
|
-
*/
|
|
55678
|
-
var MediaPlayerStateSchema = _enum([
|
|
55679
|
-
"off",
|
|
55680
|
-
"on",
|
|
55681
|
-
"idle",
|
|
55682
|
-
"playing",
|
|
55683
|
-
"paused",
|
|
55684
|
-
"buffering",
|
|
55685
|
-
"standby"
|
|
55686
|
-
]);
|
|
55687
|
-
var MediaPlayerRepeatSchema = _enum([
|
|
55688
|
-
"off",
|
|
55689
|
-
"all",
|
|
55690
|
-
"one"
|
|
55691
|
-
]);
|
|
55692
|
-
var MediaInfoSchema = object({
|
|
55693
|
-
/** Free-form media kind (`music`, `tvshow`, `movie`, `app`, `channel`,
|
|
55694
|
-
* `podcast`, …). */
|
|
55695
|
-
type: string(),
|
|
55696
|
-
/** Human-readable title. */
|
|
55697
|
-
title: string(),
|
|
55698
|
-
/** Optional artist / channel / station label. */
|
|
55699
|
-
artist: string().optional(),
|
|
55700
|
-
/** Optional album / season / show name. */
|
|
55701
|
-
album: string().optional(),
|
|
55702
|
-
/** Optional cover-art / thumbnail URL. */
|
|
55703
|
-
imageUrl: string().optional()
|
|
55704
|
-
});
|
|
55705
|
-
/**
|
|
55706
|
-
* Where `state`, `volumeLevel`, `isMuted` and `source` come from. `reported`:
|
|
55707
|
-
* the device said so. `assumed`: the driver has no feedback (infrared) and
|
|
55708
|
-
* tracks what it last SENT; it moves only after a send was accepted, and a
|
|
55709
|
-
* human at the real remote makes it wrong. A UI shows an assumed value as a
|
|
55710
|
-
* guess, never as a reading.
|
|
55711
|
-
*/
|
|
55712
|
-
var MediaPlayerStateSourceSchema = _enum(["reported", "assumed"]);
|
|
55713
|
-
/** The fields of a media-player status a driver can only guess. */
|
|
55714
|
-
var MediaPlayerAssumedFieldSchema = _enum([
|
|
55715
|
-
"state",
|
|
55716
|
-
"volumeLevel",
|
|
55717
|
-
"isMuted",
|
|
55718
|
-
"source"
|
|
55719
|
-
]);
|
|
55720
|
-
object({
|
|
55721
|
-
/** Playback lifecycle state. */
|
|
55722
|
-
state: MediaPlayerStateSchema,
|
|
55723
|
-
/**
|
|
55724
|
-
* Whether the state fields are readings or the driver's own bookkeeping.
|
|
55725
|
-
* A slice written before this field existed parses as `reported`.
|
|
55726
|
-
*/
|
|
55727
|
-
stateSource: MediaPlayerStateSourceSchema.default("reported"),
|
|
55728
|
-
/**
|
|
55729
|
-
* Which fields are the driver's own guess. `stateSource` is `assumed` when
|
|
55730
|
-
* this is not empty; a field not listed here is a reading even then (power
|
|
55731
|
-
* from a running-state source, mute guessed from the last send).
|
|
55732
|
-
*/
|
|
55733
|
-
assumedFields: array(MediaPlayerAssumedFieldSchema).default([]),
|
|
55734
|
-
/** Volume as 0..100 inclusive. Null when the device has no volume
|
|
55735
|
-
* surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
|
|
55736
|
-
volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
55737
|
-
/** Mute toggle distinct from volume=0. Null when no mute surface.
|
|
55738
|
-
* Pair with `DeviceFeature.MediaPlayerMute`. */
|
|
55739
|
-
isMuted: boolean().nullable(),
|
|
55740
|
-
/** Active source / input. Empty when no source surface. */
|
|
55741
|
-
source: string(),
|
|
55742
|
-
/** Selectable sources. Empty when no source surface.
|
|
55743
|
-
* Pair with `DeviceFeature.MediaPlayerSelectSource`. */
|
|
55744
|
-
availableSources: array(string()),
|
|
55745
|
-
/** Currently-playing media info. Null when nothing is playing. */
|
|
55746
|
-
currentMedia: MediaInfoSchema.nullable(),
|
|
55747
|
-
/** Current playback position in ms. Null when not seekable or
|
|
55748
|
-
* nothing is playing. Pair with `DeviceFeature.MediaPlayerSeek`. */
|
|
55749
|
-
position: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
55750
|
-
/** Total duration of the current media in ms. Null when unknown
|
|
55751
|
-
* (live stream) or nothing is playing. */
|
|
55752
|
-
duration: number().int().nonnegative().nullable().meta(readingMeta("duration-ms")),
|
|
55753
|
-
/** Shuffle toggle. Null when no shuffle surface.
|
|
55754
|
-
* Pair with `DeviceFeature.MediaPlayerShuffle`. */
|
|
55755
|
-
shuffle: boolean().nullable(),
|
|
55756
|
-
/** Repeat mode. Null when no repeat surface.
|
|
55757
|
-
* Pair with `DeviceFeature.MediaPlayerRepeat`. */
|
|
55758
|
-
repeat: MediaPlayerRepeatSchema.nullable(),
|
|
55759
|
-
/** Ms epoch when the slice was last updated. */
|
|
55760
|
-
lastChangedAt: number()
|
|
55761
|
-
});
|
|
55762
|
-
DeviceType.MediaPlayer, method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
55763
|
-
kind: "mutation",
|
|
55764
|
-
auth: "protected"
|
|
55765
|
-
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
55766
|
-
kind: "mutation",
|
|
55767
|
-
auth: "protected"
|
|
55768
|
-
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
55769
|
-
kind: "mutation",
|
|
55770
|
-
auth: "protected"
|
|
55771
|
-
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
55772
|
-
kind: "mutation",
|
|
55773
|
-
auth: "protected"
|
|
55774
|
-
}), method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
55775
|
-
kind: "mutation",
|
|
55776
|
-
auth: "protected"
|
|
55777
|
-
}), method(object({
|
|
55778
|
-
deviceId: number().int().nonnegative(),
|
|
55779
|
-
position: number().int().nonnegative()
|
|
55780
|
-
}), _void(), {
|
|
55781
|
-
kind: "mutation",
|
|
55782
|
-
auth: "protected"
|
|
55783
|
-
}), method(object({
|
|
55784
|
-
deviceId: number().int().nonnegative(),
|
|
55785
|
-
volumeLevel: number().min(0).max(100)
|
|
55786
|
-
}), _void(), {
|
|
55787
|
-
kind: "mutation",
|
|
55788
|
-
auth: "protected"
|
|
55789
|
-
}), method(object({
|
|
55790
|
-
deviceId: number().int().nonnegative(),
|
|
55791
|
-
muted: boolean()
|
|
55792
|
-
}), _void(), {
|
|
55793
|
-
kind: "mutation",
|
|
55794
|
-
auth: "protected"
|
|
55795
|
-
}), method(object({
|
|
55796
|
-
deviceId: number().int().nonnegative(),
|
|
55797
|
-
shuffle: boolean()
|
|
55798
|
-
}), _void(), {
|
|
55799
|
-
kind: "mutation",
|
|
55800
|
-
auth: "protected"
|
|
55801
|
-
}), method(object({
|
|
55802
|
-
deviceId: number().int().nonnegative(),
|
|
55803
|
-
repeat: MediaPlayerRepeatSchema
|
|
55804
|
-
}), _void(), {
|
|
55805
|
-
kind: "mutation",
|
|
55806
|
-
auth: "protected"
|
|
55807
|
-
}), method(object({
|
|
55808
|
-
deviceId: number().int().nonnegative(),
|
|
55809
|
-
source: string().min(1)
|
|
55810
|
-
}), _void(), {
|
|
55811
|
-
kind: "mutation",
|
|
55812
|
-
auth: "protected"
|
|
55813
|
-
}), method(object({
|
|
55814
|
-
deviceId: number().int().nonnegative(),
|
|
55815
|
-
/** Media identifier / URL. */
|
|
55816
|
-
mediaId: string().min(1),
|
|
55817
|
-
/** Media kind (`music`, `tvshow`, `movie`, `app`, …) — provider
|
|
55818
|
-
* passes it through to the upstream service. */
|
|
55819
|
-
mediaType: string().min(1)
|
|
55820
|
-
}), _void(), {
|
|
55821
|
-
kind: "mutation",
|
|
55822
|
-
auth: "protected"
|
|
55823
|
-
});
|
|
55824
|
-
/**
|
|
55825
56356
|
* mesh-network — collection cap for mesh-VPN providers.
|
|
55826
56357
|
*
|
|
55827
56358
|
* Different shape from `network-access` (public tunnels):
|
|
@@ -56382,6 +56913,27 @@ method(object({}), object({ entries: array(MigrationLedgerEntrySchema) }), { aut
|
|
|
56382
56913
|
}), {
|
|
56383
56914
|
kind: "mutation",
|
|
56384
56915
|
auth: "admin"
|
|
56916
|
+
}), method(object({ integrationId: string().min(1) }), record(string(), unknown()), { auth: "admin" }), method(object({ integrationId: string().min(1) }), object({
|
|
56917
|
+
rows: number().int().nonnegative(),
|
|
56918
|
+
secrets: number().int().nonnegative()
|
|
56919
|
+
}), {
|
|
56920
|
+
kind: "mutation",
|
|
56921
|
+
auth: "admin"
|
|
56922
|
+
}), method(object({
|
|
56923
|
+
integrationId: string().min(1),
|
|
56924
|
+
patch: record(string(), unknown())
|
|
56925
|
+
}), object({ written: boolean() }), {
|
|
56926
|
+
kind: "mutation",
|
|
56927
|
+
auth: "admin"
|
|
56928
|
+
}), method(object({
|
|
56929
|
+
integrationId: string().min(1),
|
|
56930
|
+
keys: array(string().min(1))
|
|
56931
|
+
}), object({
|
|
56932
|
+
present: boolean(),
|
|
56933
|
+
removed: record(string(), unknown())
|
|
56934
|
+
}), {
|
|
56935
|
+
kind: "mutation",
|
|
56936
|
+
auth: "admin"
|
|
56385
56937
|
});
|
|
56386
56938
|
method(object({
|
|
56387
56939
|
sourceUrl: string(),
|
|
@@ -64114,6 +64666,21 @@ var NcTemplateVarKindSchema = _enum([
|
|
|
64114
64666
|
"sound",
|
|
64115
64667
|
"system"
|
|
64116
64668
|
]);
|
|
64669
|
+
/**
|
|
64670
|
+
* The default title/body a rule of one SITUATION is delivered with when the
|
|
64671
|
+
* operator wrote no template — served so an editor can pre-fill it (and the
|
|
64672
|
+
* operator can rebuild it from placeholders) instead of starting from a blank
|
|
64673
|
+
* box that, once saved, silently replaces it. `id` names the situation inside
|
|
64674
|
+
* a rule `kind`: `appliance-cycle` is a `sensor` rule whose device is an
|
|
64675
|
+
* appliance finishing a cycle. Texts are raw templates (`{{var}}` intact), in
|
|
64676
|
+
* the hub's current language and with its overrides applied.
|
|
64677
|
+
*/
|
|
64678
|
+
var NcTemplateDefaultSchema = object({
|
|
64679
|
+
id: _enum(["appliance-cycle"]),
|
|
64680
|
+
kind: NcRuleKindSchema,
|
|
64681
|
+
title: string(),
|
|
64682
|
+
body: string()
|
|
64683
|
+
});
|
|
64117
64684
|
/** Which producer renders a template: an ordinary rule, a timelapse, a digest. */
|
|
64118
64685
|
var NcTemplateFamilySchema = _enum([
|
|
64119
64686
|
"rule",
|
|
@@ -64969,7 +65536,11 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
|
|
|
64969
65536
|
}), method(object({}), object({
|
|
64970
65537
|
catalog: array(NcConditionDescriptorSchema),
|
|
64971
65538
|
taxonomy: NcTaxonomySchema.optional()
|
|
64972
|
-
})), method(object({}), object({
|
|
65539
|
+
})), method(object({}), object({
|
|
65540
|
+
vars: array(NcTemplateVarDescriptorSchema),
|
|
65541
|
+
/** Per-situation default texts; absent on a hub that predates them. */
|
|
65542
|
+
defaults: array(NcTemplateDefaultSchema)
|
|
65543
|
+
})), method(NcTemplatePreviewInputSchema, NcTemplatePreviewSchema, {
|
|
64973
65544
|
kind: "mutation",
|
|
64974
65545
|
access: "view"
|
|
64975
65546
|
}), method(object({ filter: NcHistoryFilterSchema.default({ limit: 100 }) }), object({ entries: array(NcHistoryEntrySchema) }), { auth: "admin" }), method(object({}), NcRuleLastRunsSchema, { auth: "admin" }), method(object({ artifactId: string().min(1) }), object({ url: string().nullable() }), { auth: "admin" }), method(object({}), object({ snoozes: array(NcSnoozeSchema) }), { caller: "required" }), method(object({ snooze: NcSnoozeInputSchema }), object({ snooze: NcSnoozeSchema }), {
|
|
@@ -65717,52 +66288,27 @@ object({
|
|
|
65717
66288
|
empty: boolean().nullable(),
|
|
65718
66289
|
/** True while the pump is running. Null while unknown. */
|
|
65719
66290
|
pumpRunning: boolean().nullable(),
|
|
65720
|
-
/** True when the pump works on an on/off duty cycle rather than continuously. */
|
|
65721
|
-
pumpIntervalEnabled: boolean().nullable(),
|
|
65722
|
-
/** How long the pump runs per cycle, in seconds. */
|
|
65723
|
-
pumpOnTime: number().min(0).nullable().meta(readingMeta("duration")),
|
|
65724
|
-
/** How long the pump rests per cycle, in seconds. */
|
|
65725
|
-
pumpOffTime: number().min(0).nullable().meta(readingMeta("duration")),
|
|
65726
66291
|
/** Water drunk in the last drink, in litres. */
|
|
65727
66292
|
lastDrink: number().min(0).nullable().meta(readingMeta("volume")),
|
|
65728
66293
|
/** Water drunk in the current hour, in litres. */
|
|
65729
66294
|
currentHourDrink: number().min(0).nullable().meta(readingMeta("volume")),
|
|
65730
66295
|
/** Water drunk over the last 24 hours (rolling), in litres. */
|
|
65731
66296
|
last24hDrink: number().min(0).nullable().meta(readingMeta("volume")),
|
|
65732
|
-
/** Water drunk since local midnight, in litres. */
|
|
66297
|
+
/** Water drunk since local midnight, in litres. Null when the fountain has no such sensor. */
|
|
65733
66298
|
todayDrink: number().min(0).nullable().meta(readingMeta("volume")),
|
|
65734
66299
|
/** Decoded fault message. Null means healthy; a non-null string is an active fault. */
|
|
65735
66300
|
error: string().nullable(),
|
|
65736
66301
|
/** Ms epoch when the slice was last refreshed. */
|
|
65737
66302
|
lastFetchedAt: number()
|
|
65738
66303
|
});
|
|
65739
|
-
var DeviceIdSchema = number().int().nonnegative();
|
|
65740
|
-
var MinutesSchema = number().int().min(0);
|
|
66304
|
+
var DeviceIdSchema$1 = number().int().nonnegative();
|
|
65741
66305
|
DeviceType.PetFountain, method(object({
|
|
65742
|
-
deviceId: DeviceIdSchema,
|
|
65743
|
-
on: boolean()
|
|
65744
|
-
}), _void(), {
|
|
65745
|
-
kind: "mutation",
|
|
65746
|
-
auth: "protected"
|
|
65747
|
-
}), method(object({
|
|
65748
|
-
deviceId: DeviceIdSchema,
|
|
66306
|
+
deviceId: DeviceIdSchema$1,
|
|
65749
66307
|
on: boolean()
|
|
65750
66308
|
}), _void(), {
|
|
65751
66309
|
kind: "mutation",
|
|
65752
66310
|
auth: "protected"
|
|
65753
|
-
}), method(object({
|
|
65754
|
-
deviceId: DeviceIdSchema,
|
|
65755
|
-
minutes: MinutesSchema
|
|
65756
|
-
}), _void(), {
|
|
65757
|
-
kind: "mutation",
|
|
65758
|
-
auth: "protected"
|
|
65759
|
-
}), method(object({
|
|
65760
|
-
deviceId: DeviceIdSchema,
|
|
65761
|
-
minutes: MinutesSchema
|
|
65762
|
-
}), _void(), {
|
|
65763
|
-
kind: "mutation",
|
|
65764
|
-
auth: "protected"
|
|
65765
|
-
}), method(object({ deviceId: DeviceIdSchema }), _void(), {
|
|
66311
|
+
}), method(object({ deviceId: DeviceIdSchema$1 }), _void(), {
|
|
65766
66312
|
kind: "mutation",
|
|
65767
66313
|
auth: "protected"
|
|
65768
66314
|
});
|
|
@@ -67296,6 +67842,49 @@ object({
|
|
|
67296
67842
|
DeviceType.PetFeeder,
|
|
67297
67843
|
DeviceType.Heater
|
|
67298
67844
|
].filter((type) => type !== DeviceType.Sensor);
|
|
67845
|
+
var PowerSourceRefusalReasonSchema = _enum([
|
|
67846
|
+
"no-power-source",
|
|
67847
|
+
"switch-missing",
|
|
67848
|
+
"switch-offline",
|
|
67849
|
+
"switch-unknown",
|
|
67850
|
+
"restart-in-progress",
|
|
67851
|
+
"switch-call-failed"
|
|
67852
|
+
]);
|
|
67853
|
+
var PowerSourceActionResultSchema = discriminatedUnion("accepted", [object({ accepted: literal(true) }), object({
|
|
67854
|
+
accepted: literal(false),
|
|
67855
|
+
reason: PowerSourceRefusalReasonSchema,
|
|
67856
|
+
/** One line for the operator, naming the switch. */
|
|
67857
|
+
message: string()
|
|
67858
|
+
})]);
|
|
67859
|
+
var PowerSourceInfoSchema = object({
|
|
67860
|
+
/** False when the device has no power source linked; the rest is then empty. */
|
|
67861
|
+
linked: boolean(),
|
|
67862
|
+
switchDeviceId: number().nullable(),
|
|
67863
|
+
switchName: string().nullable(),
|
|
67864
|
+
/** The switch's on/off state, or null when it is unknown. Never false for unknown (D393). */
|
|
67865
|
+
switchOn: boolean().nullable(),
|
|
67866
|
+
switchOnline: boolean().nullable(),
|
|
67867
|
+
restartInProgress: boolean(),
|
|
67868
|
+
offSeconds: number().int()
|
|
67869
|
+
});
|
|
67870
|
+
object({
|
|
67871
|
+
/** Null while the switch has not reported: unknown is never `false` (D393). */
|
|
67872
|
+
switchOn: boolean().nullable(),
|
|
67873
|
+
offSeconds: number().int(),
|
|
67874
|
+
lastFetchedAt: number()
|
|
67875
|
+
});
|
|
67876
|
+
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;
|
|
67877
|
+
var deviceInput$1 = object({ deviceId: number().int().nonnegative() });
|
|
67878
|
+
systemMethod(deviceInput$1, PowerSourceInfoSchema, { auth: "admin" }), systemMethod(deviceInput$1, PowerSourceActionResultSchema, {
|
|
67879
|
+
kind: "mutation",
|
|
67880
|
+
auth: "admin"
|
|
67881
|
+
}), systemMethod(deviceInput$1, PowerSourceActionResultSchema, {
|
|
67882
|
+
kind: "mutation",
|
|
67883
|
+
auth: "admin"
|
|
67884
|
+
}), systemMethod(deviceInput$1, PowerSourceActionResultSchema, {
|
|
67885
|
+
kind: "mutation",
|
|
67886
|
+
auth: "admin"
|
|
67887
|
+
});
|
|
67299
67888
|
/**
|
|
67300
67889
|
* Presence cap. Models HA `person.*` and `device_tracker.*` entities
|
|
67301
67890
|
* on `DeviceType.Presence`. Read-only — no setters: presence is
|
|
@@ -67722,6 +68311,37 @@ DeviceType.Camera, method(object({ deviceId: number() }), PtzAutotrackStatusSche
|
|
|
67722
68311
|
deviceId: number(),
|
|
67723
68312
|
status: PtzAutotrackStatusSchema
|
|
67724
68313
|
});
|
|
68314
|
+
object({
|
|
68315
|
+
/** True when the pump works on the on/off cycle, false when it runs continuously. Null while unknown. */
|
|
68316
|
+
enabled: boolean().nullable(),
|
|
68317
|
+
/** How long the pump runs per cycle, in seconds. Null while unknown. */
|
|
68318
|
+
onTime: number().min(0).nullable().meta(readingMeta("duration")),
|
|
68319
|
+
/** How long the pump rests per cycle, in seconds. Null while unknown. */
|
|
68320
|
+
offTime: number().min(0).nullable().meta(readingMeta("duration")),
|
|
68321
|
+
/** Ms epoch when the slice was last refreshed. */
|
|
68322
|
+
lastFetchedAt: number()
|
|
68323
|
+
});
|
|
68324
|
+
var DeviceIdSchema = number().int().nonnegative();
|
|
68325
|
+
var MinutesSchema = number().int().min(0);
|
|
68326
|
+
DeviceType.PetFountain, method(object({
|
|
68327
|
+
deviceId: DeviceIdSchema,
|
|
68328
|
+
on: boolean()
|
|
68329
|
+
}), _void(), {
|
|
68330
|
+
kind: "mutation",
|
|
68331
|
+
auth: "protected"
|
|
68332
|
+
}), method(object({
|
|
68333
|
+
deviceId: DeviceIdSchema,
|
|
68334
|
+
minutes: MinutesSchema
|
|
68335
|
+
}), _void(), {
|
|
68336
|
+
kind: "mutation",
|
|
68337
|
+
auth: "protected"
|
|
68338
|
+
}), method(object({
|
|
68339
|
+
deviceId: DeviceIdSchema,
|
|
68340
|
+
minutes: MinutesSchema
|
|
68341
|
+
}), _void(), {
|
|
68342
|
+
kind: "mutation",
|
|
68343
|
+
auth: "protected"
|
|
68344
|
+
});
|
|
67725
68345
|
DeviceType.Camera, DeviceType.Sensor, DeviceType.Switch, DeviceType.Gateway, method(object({ deviceId: number().int().nonnegative() }), object({ success: literal(true) }), {
|
|
67726
68346
|
kind: "mutation",
|
|
67727
68347
|
auth: "admin"
|
|
@@ -70435,7 +71055,13 @@ var RemoteMediaCommandSchema = _enum([
|
|
|
70435
71055
|
"next",
|
|
70436
71056
|
"previous",
|
|
70437
71057
|
"setMute",
|
|
70438
|
-
"selectSource"
|
|
71058
|
+
"selectSource",
|
|
71059
|
+
"turnOn",
|
|
71060
|
+
"turnOff",
|
|
71061
|
+
"volumeUp",
|
|
71062
|
+
"volumeDown",
|
|
71063
|
+
"channelUp",
|
|
71064
|
+
"channelDown"
|
|
70439
71065
|
]);
|
|
70440
71066
|
/** A send result, or `not-routed`: the role is not bound, so the native cap answers. */
|
|
70441
71067
|
var RemoteMediaCommandResultSchema = discriminatedUnion("status", [...RemoteSendResultSchema.options, object({ status: literal("not-routed") })]);
|
|
@@ -70456,7 +71082,7 @@ method(deviceInput, RemoteConfigSchema, { auth: "admin" }), method(deviceInput.e
|
|
|
70456
71082
|
}), RemoteSendResultSchema, {
|
|
70457
71083
|
kind: "mutation",
|
|
70458
71084
|
auth: "protected"
|
|
70459
|
-
}), method(deviceInput.extend({
|
|
71085
|
+
}), method(deviceInput, MediaPlayerStatusSchema.nullable(), { auth: "protected" }), method(deviceInput.extend({
|
|
70460
71086
|
command: RemoteMediaCommandSchema,
|
|
70461
71087
|
muted: boolean().optional(),
|
|
70462
71088
|
source: string().optional(),
|
|
@@ -72297,7 +72923,11 @@ object({
|
|
|
72297
72923
|
});
|
|
72298
72924
|
Object.values(DeviceType), method(_void(), array(StreamSignalSchema).readonly());
|
|
72299
72925
|
object({
|
|
72300
|
-
|
|
72926
|
+
/**
|
|
72927
|
+
* `null` = the device has never reported its state (a measurement that
|
|
72928
|
+
* has not happened is unknown, never `false`).
|
|
72929
|
+
*/
|
|
72930
|
+
on: boolean().nullable().meta({ label: "On" }),
|
|
72301
72931
|
/** Ms epoch of the last state change. Useful for UI "X minutes ago". */
|
|
72302
72932
|
lastChangedAt: number()
|
|
72303
72933
|
});
|
|
@@ -72309,7 +72939,7 @@ DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceTyp
|
|
|
72309
72939
|
auth: "protected"
|
|
72310
72940
|
}), object({
|
|
72311
72941
|
deviceId: number(),
|
|
72312
|
-
on: boolean(),
|
|
72942
|
+
on: boolean().nullable(),
|
|
72313
72943
|
lastChangedAt: number()
|
|
72314
72944
|
});
|
|
72315
72945
|
/** C0 controls, DEL and the C1 range: none of them belongs in a name. */
|
|
@@ -75321,6 +75951,26 @@ function buildDescriptor(capName, kind) {
|
|
|
75321
75951
|
* contributes one entry per bound cap present in this map.
|
|
75322
75952
|
*/
|
|
75323
75953
|
var EVENT_KIND_BY_CAP = Object.freeze(Object.fromEntries(Object.entries(CAP_TO_KIND).map(([capName, kind]) => [capName, buildDescriptor(capName, kind)])));
|
|
75954
|
+
/**
|
|
75955
|
+
* What a remote key is CALLED and what it LOOKS like, for every role (D963),
|
|
75956
|
+
* shared by the admin UI and the viewer. A role is data on the wire; each
|
|
75957
|
+
* client resolves its name from `label` (English and Italian side by side) and
|
|
75958
|
+
* its glyph from `icon`, a lucide id from the shared icon catalogue — the
|
|
75959
|
+
* server sends an id, a client maps it to its own icon set.
|
|
75960
|
+
*
|
|
75961
|
+
* `legend` is text printed on the key INSTEAD of a glyph (a digit has no
|
|
75962
|
+
* pictogram): a client that can draw text shows it, one that cannot falls back
|
|
75963
|
+
* to `icon`.
|
|
75964
|
+
*/
|
|
75965
|
+
function digit(n) {
|
|
75966
|
+
return {
|
|
75967
|
+
label: t(n, n),
|
|
75968
|
+
icon: "hash",
|
|
75969
|
+
legend: n
|
|
75970
|
+
};
|
|
75971
|
+
}
|
|
75972
|
+
digit("0"), digit("1"), digit("2"), digit("3"), digit("4"), digit("5"), digit("6"), digit("7"), digit("8"), digit("9");
|
|
75973
|
+
DeviceFeature.MediaPlayerPower, DeviceFeature.MediaPlayerVolumeStep, DeviceFeature.MediaPlayerChannelStep, DeviceFeature.MediaPlayerMute, DeviceFeature.MediaPlayerStop, DeviceFeature.MediaPlayerNext, DeviceFeature.MediaPlayerPrevious;
|
|
75324
75974
|
var btn = (idSuffix, row, col, init) => ({
|
|
75325
75975
|
idSuffix,
|
|
75326
75976
|
row,
|
|
@@ -75447,6 +76097,42 @@ discriminatedUnion("kind", [object({
|
|
|
75447
76097
|
kind: literal("pronto"),
|
|
75448
76098
|
code: string()
|
|
75449
76099
|
})]);
|
|
76100
|
+
/**
|
|
76101
|
+
* Propose a running threshold from a device's own power history (planning#405).
|
|
76102
|
+
*
|
|
76103
|
+
* Pure and deterministic: the same series gives the same answer. The series is
|
|
76104
|
+
* a HELD-VALUE step series — a point's value stands until the next point — which
|
|
76105
|
+
* is how device-history stores a gauge, so "time at a level" is exact, not a
|
|
76106
|
+
* count of samples.
|
|
76107
|
+
*
|
|
76108
|
+
* 1. idle floor F = time-weighted median; noise ceiling N = highest value seen
|
|
76109
|
+
* near F. A device that is not near one level most of the time has no floor.
|
|
76110
|
+
* 2. coarse activity = above max(2N, N + 5 W); consecutive stretches are episodes.
|
|
76111
|
+
* 3. an episode shorter than `PULSE_MAX_MS` is a PULSE (an anti-crease tumble, a
|
|
76112
|
+
* display waking): it is neither a run nor a pause. Among the remaining
|
|
76113
|
+
* episodes, gaps over `PAUSE_MAX_MS` always separate runs; the rest split in
|
|
76114
|
+
* two by the largest log-scale jump (>= 3x): the short ones are pauses INSIDE
|
|
76115
|
+
* a run (a soak), the long ones separate runs. A stretch under `MIN_RUN_MS`
|
|
76116
|
+
* is no run.
|
|
76117
|
+
* 4. T = geometric mean of N and the plateau's 10th percentile, snapped to a
|
|
76118
|
+
* grid a person would type; stop dwell covers the longest pause and the
|
|
76119
|
+
* kind's floor; start dwell covers the longest pulse and standby blip above T.
|
|
76120
|
+
* 5. the numbers are proved by REPLAY through the real `dwell` evaluation: each
|
|
76121
|
+
* run must hold exactly one true interval and nothing may flap outside.
|
|
76122
|
+
*/
|
|
76123
|
+
var SECOND_MS = 1e3;
|
|
76124
|
+
var MINUTE_MS$1 = 60 * SECOND_MS;
|
|
76125
|
+
24 * (60 * MINUTE_MS$1);
|
|
76126
|
+
10 * MINUTE_MS$1;
|
|
76127
|
+
30 * SECOND_MS;
|
|
76128
|
+
60 * SECOND_MS;
|
|
76129
|
+
20 * MINUTE_MS$1;
|
|
76130
|
+
90 * SECOND_MS;
|
|
76131
|
+
30 * MINUTE_MS$1;
|
|
76132
|
+
10 * MINUTE_MS$1;
|
|
76133
|
+
DeviceType.Dryer, 3 * MINUTE_MS$1, DeviceType.Dishwasher, 3 * MINUTE_MS$1, DeviceType.Washer, 10 * MINUTE_MS$1;
|
|
76134
|
+
10 * SECOND_MS;
|
|
76135
|
+
10 * SECOND_MS;
|
|
75450
76136
|
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverStoppable, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
75451
76137
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
75452
76138
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
@@ -76428,6 +77114,27 @@ Object.freeze({
|
|
|
76428
77114
|
access: "view",
|
|
76429
77115
|
role: "viewer"
|
|
76430
77116
|
},
|
|
77117
|
+
"boardKeySource.getBoardKey": {
|
|
77118
|
+
capName: "board-key-source",
|
|
77119
|
+
capScope: "system",
|
|
77120
|
+
addonId: null,
|
|
77121
|
+
access: "view",
|
|
77122
|
+
role: "admin"
|
|
77123
|
+
},
|
|
77124
|
+
"boardKeySource.listBoards": {
|
|
77125
|
+
capName: "board-key-source",
|
|
77126
|
+
capScope: "system",
|
|
77127
|
+
addonId: null,
|
|
77128
|
+
access: "view",
|
|
77129
|
+
role: "admin"
|
|
77130
|
+
},
|
|
77131
|
+
"boardKeySource.listSources": {
|
|
77132
|
+
capName: "board-key-source",
|
|
77133
|
+
capScope: "system",
|
|
77134
|
+
addonId: null,
|
|
77135
|
+
access: "view",
|
|
77136
|
+
role: "admin"
|
|
77137
|
+
},
|
|
76431
77138
|
"brightness.getStatus": {
|
|
76432
77139
|
capName: "brightness",
|
|
76433
77140
|
capScope: "device",
|
|
@@ -76967,6 +77674,13 @@ Object.freeze({
|
|
|
76967
77674
|
access: "create",
|
|
76968
77675
|
role: "admin"
|
|
76969
77676
|
},
|
|
77677
|
+
"coreBlocks.proposeRunningThreshold": {
|
|
77678
|
+
capName: "core-blocks",
|
|
77679
|
+
capScope: "system",
|
|
77680
|
+
addonId: null,
|
|
77681
|
+
access: "view",
|
|
77682
|
+
role: "admin"
|
|
77683
|
+
},
|
|
76970
77684
|
"coreBlocks.reportAutomationRun": {
|
|
76971
77685
|
capName: "core-blocks",
|
|
76972
77686
|
capScope: "system",
|
|
@@ -77478,6 +78192,13 @@ Object.freeze({
|
|
|
77478
78192
|
access: "view",
|
|
77479
78193
|
role: "admin"
|
|
77480
78194
|
},
|
|
78195
|
+
"deviceExport.listInstances": {
|
|
78196
|
+
capName: "device-export",
|
|
78197
|
+
capScope: "system",
|
|
78198
|
+
addonId: null,
|
|
78199
|
+
access: "view",
|
|
78200
|
+
role: "admin"
|
|
78201
|
+
},
|
|
77481
78202
|
"deviceExport.listSupportedDeviceKinds": {
|
|
77482
78203
|
capName: "device-export",
|
|
77483
78204
|
capScope: "system",
|
|
@@ -79165,13 +79886,6 @@ Object.freeze({
|
|
|
79165
79886
|
access: "view",
|
|
79166
79887
|
role: "admin"
|
|
79167
79888
|
},
|
|
79168
|
-
"integrations.getSettings": {
|
|
79169
|
-
capName: "integrations",
|
|
79170
|
-
capScope: "system",
|
|
79171
|
-
addonId: null,
|
|
79172
|
-
access: "view",
|
|
79173
|
-
role: "admin"
|
|
79174
|
-
},
|
|
79175
79889
|
"integrations.list": {
|
|
79176
79890
|
capName: "integrations",
|
|
79177
79891
|
capScope: "system",
|
|
@@ -79179,13 +79893,6 @@ Object.freeze({
|
|
|
79179
79893
|
access: "view",
|
|
79180
79894
|
role: "admin"
|
|
79181
79895
|
},
|
|
79182
|
-
"integrations.setSettings": {
|
|
79183
|
-
capName: "integrations",
|
|
79184
|
-
capScope: "system",
|
|
79185
|
-
addonId: null,
|
|
79186
|
-
access: "create",
|
|
79187
|
-
role: "admin"
|
|
79188
|
-
},
|
|
79189
79896
|
"integrations.testConnection": {
|
|
79190
79897
|
capName: "integrations",
|
|
79191
79898
|
capScope: "system",
|
|
@@ -79914,6 +80621,20 @@ Object.freeze({
|
|
|
79914
80621
|
access: "view",
|
|
79915
80622
|
role: "admin"
|
|
79916
80623
|
},
|
|
80624
|
+
"mediaPlayer.channelDown": {
|
|
80625
|
+
capName: "media-player",
|
|
80626
|
+
capScope: "device",
|
|
80627
|
+
addonId: null,
|
|
80628
|
+
access: "create",
|
|
80629
|
+
role: "viewer"
|
|
80630
|
+
},
|
|
80631
|
+
"mediaPlayer.channelUp": {
|
|
80632
|
+
capName: "media-player",
|
|
80633
|
+
capScope: "device",
|
|
80634
|
+
addonId: null,
|
|
80635
|
+
access: "create",
|
|
80636
|
+
role: "viewer"
|
|
80637
|
+
},
|
|
79917
80638
|
"mediaPlayer.getStatus": {
|
|
79918
80639
|
capName: "media-player",
|
|
79919
80640
|
capScope: "device",
|
|
@@ -80005,6 +80726,34 @@ Object.freeze({
|
|
|
80005
80726
|
access: "create",
|
|
80006
80727
|
role: "viewer"
|
|
80007
80728
|
},
|
|
80729
|
+
"mediaPlayer.turnOff": {
|
|
80730
|
+
capName: "media-player",
|
|
80731
|
+
capScope: "device",
|
|
80732
|
+
addonId: null,
|
|
80733
|
+
access: "create",
|
|
80734
|
+
role: "viewer"
|
|
80735
|
+
},
|
|
80736
|
+
"mediaPlayer.turnOn": {
|
|
80737
|
+
capName: "media-player",
|
|
80738
|
+
capScope: "device",
|
|
80739
|
+
addonId: null,
|
|
80740
|
+
access: "create",
|
|
80741
|
+
role: "viewer"
|
|
80742
|
+
},
|
|
80743
|
+
"mediaPlayer.volumeDown": {
|
|
80744
|
+
capName: "media-player",
|
|
80745
|
+
capScope: "device",
|
|
80746
|
+
addonId: null,
|
|
80747
|
+
access: "create",
|
|
80748
|
+
role: "viewer"
|
|
80749
|
+
},
|
|
80750
|
+
"mediaPlayer.volumeUp": {
|
|
80751
|
+
capName: "media-player",
|
|
80752
|
+
capScope: "device",
|
|
80753
|
+
addonId: null,
|
|
80754
|
+
access: "create",
|
|
80755
|
+
role: "viewer"
|
|
80756
|
+
},
|
|
80008
80757
|
"meshNetwork.getStatus": {
|
|
80009
80758
|
capName: "mesh-network",
|
|
80010
80759
|
capScope: "system",
|
|
@@ -80138,6 +80887,13 @@ Object.freeze({
|
|
|
80138
80887
|
access: "view",
|
|
80139
80888
|
role: "admin"
|
|
80140
80889
|
},
|
|
80890
|
+
"migrations.clearLegacyIntegrationRows": {
|
|
80891
|
+
capName: "migrations",
|
|
80892
|
+
capScope: "system",
|
|
80893
|
+
addonId: null,
|
|
80894
|
+
access: "delete",
|
|
80895
|
+
role: "admin"
|
|
80896
|
+
},
|
|
80141
80897
|
"migrations.declareOwner": {
|
|
80142
80898
|
capName: "migrations",
|
|
80143
80899
|
capScope: "system",
|
|
@@ -80145,6 +80901,13 @@ Object.freeze({
|
|
|
80145
80901
|
access: "create",
|
|
80146
80902
|
role: "admin"
|
|
80147
80903
|
},
|
|
80904
|
+
"migrations.deleteLegacyIntegrationRowKeys": {
|
|
80905
|
+
capName: "migrations",
|
|
80906
|
+
capScope: "system",
|
|
80907
|
+
addonId: null,
|
|
80908
|
+
access: "delete",
|
|
80909
|
+
role: "admin"
|
|
80910
|
+
},
|
|
80148
80911
|
"migrations.dryRun": {
|
|
80149
80912
|
capName: "migrations",
|
|
80150
80913
|
capScope: "system",
|
|
@@ -80180,6 +80943,20 @@ Object.freeze({
|
|
|
80180
80943
|
access: "create",
|
|
80181
80944
|
role: "admin"
|
|
80182
80945
|
},
|
|
80946
|
+
"migrations.patchLegacyIntegrationRows": {
|
|
80947
|
+
capName: "migrations",
|
|
80948
|
+
capScope: "system",
|
|
80949
|
+
addonId: null,
|
|
80950
|
+
access: "create",
|
|
80951
|
+
role: "admin"
|
|
80952
|
+
},
|
|
80953
|
+
"migrations.readLegacyIntegrationRows": {
|
|
80954
|
+
capName: "migrations",
|
|
80955
|
+
capScope: "system",
|
|
80956
|
+
addonId: null,
|
|
80957
|
+
access: "view",
|
|
80958
|
+
role: "admin"
|
|
80959
|
+
},
|
|
80183
80960
|
"migrations.restorePreMigration": {
|
|
80184
80961
|
capName: "migrations",
|
|
80185
80962
|
capScope: "system",
|
|
@@ -81251,27 +82028,6 @@ Object.freeze({
|
|
|
81251
82028
|
access: "create",
|
|
81252
82029
|
role: "viewer"
|
|
81253
82030
|
},
|
|
81254
|
-
"petFountain.setPumpInterval": {
|
|
81255
|
-
capName: "pet-fountain",
|
|
81256
|
-
capScope: "device",
|
|
81257
|
-
addonId: null,
|
|
81258
|
-
access: "create",
|
|
81259
|
-
role: "viewer"
|
|
81260
|
-
},
|
|
81261
|
-
"petFountain.setPumpOffMinutes": {
|
|
81262
|
-
capName: "pet-fountain",
|
|
81263
|
-
capScope: "device",
|
|
81264
|
-
addonId: null,
|
|
81265
|
-
access: "create",
|
|
81266
|
-
role: "viewer"
|
|
81267
|
-
},
|
|
81268
|
-
"petFountain.setPumpOnMinutes": {
|
|
81269
|
-
capName: "pet-fountain",
|
|
81270
|
-
capScope: "device",
|
|
81271
|
-
addonId: null,
|
|
81272
|
-
access: "create",
|
|
81273
|
-
role: "viewer"
|
|
81274
|
-
},
|
|
81275
82031
|
"pipelineAnalytics.addRetrainFrameFromRecording": {
|
|
81276
82032
|
capName: "pipeline-analytics",
|
|
81277
82033
|
capScope: "device",
|
|
@@ -83050,6 +83806,34 @@ Object.freeze({
|
|
|
83050
83806
|
access: "create",
|
|
83051
83807
|
role: "admin"
|
|
83052
83808
|
},
|
|
83809
|
+
"pumpCycle.getStatus": {
|
|
83810
|
+
capName: "pump-cycle",
|
|
83811
|
+
capScope: "device",
|
|
83812
|
+
addonId: null,
|
|
83813
|
+
access: "view",
|
|
83814
|
+
role: "viewer"
|
|
83815
|
+
},
|
|
83816
|
+
"pumpCycle.setEnabled": {
|
|
83817
|
+
capName: "pump-cycle",
|
|
83818
|
+
capScope: "device",
|
|
83819
|
+
addonId: null,
|
|
83820
|
+
access: "create",
|
|
83821
|
+
role: "viewer"
|
|
83822
|
+
},
|
|
83823
|
+
"pumpCycle.setOffMinutes": {
|
|
83824
|
+
capName: "pump-cycle",
|
|
83825
|
+
capScope: "device",
|
|
83826
|
+
addonId: null,
|
|
83827
|
+
access: "create",
|
|
83828
|
+
role: "viewer"
|
|
83829
|
+
},
|
|
83830
|
+
"pumpCycle.setOnMinutes": {
|
|
83831
|
+
capName: "pump-cycle",
|
|
83832
|
+
capScope: "device",
|
|
83833
|
+
addonId: null,
|
|
83834
|
+
access: "create",
|
|
83835
|
+
role: "viewer"
|
|
83836
|
+
},
|
|
83053
83837
|
"reboot.reboot": {
|
|
83054
83838
|
capName: "reboot",
|
|
83055
83839
|
capScope: "device",
|
|
@@ -83554,6 +84338,13 @@ Object.freeze({
|
|
|
83554
84338
|
access: "create",
|
|
83555
84339
|
role: "admin"
|
|
83556
84340
|
},
|
|
84341
|
+
"remote.getAssumedStatus": {
|
|
84342
|
+
capName: "remote",
|
|
84343
|
+
capScope: "system",
|
|
84344
|
+
addonId: null,
|
|
84345
|
+
access: "view",
|
|
84346
|
+
role: "admin"
|
|
84347
|
+
},
|
|
83557
84348
|
"remote.getConfig": {
|
|
83558
84349
|
capName: "remote",
|
|
83559
84350
|
capScope: "system",
|
|
@@ -86369,6 +87160,11 @@ Object.freeze({
|
|
|
86369
87160
|
form: "single",
|
|
86370
87161
|
optional: false
|
|
86371
87162
|
}],
|
|
87163
|
+
"coreBlocks.proposeRunningThreshold": [{
|
|
87164
|
+
name: "deviceId",
|
|
87165
|
+
form: "single",
|
|
87166
|
+
optional: false
|
|
87167
|
+
}],
|
|
86372
87168
|
"cover.close": [{
|
|
86373
87169
|
name: "deviceId",
|
|
86374
87170
|
form: "single",
|
|
@@ -87220,6 +88016,16 @@ Object.freeze({
|
|
|
87220
88016
|
form: "single",
|
|
87221
88017
|
optional: true
|
|
87222
88018
|
}],
|
|
88019
|
+
"mediaPlayer.channelDown": [{
|
|
88020
|
+
name: "deviceId",
|
|
88021
|
+
form: "single",
|
|
88022
|
+
optional: false
|
|
88023
|
+
}],
|
|
88024
|
+
"mediaPlayer.channelUp": [{
|
|
88025
|
+
name: "deviceId",
|
|
88026
|
+
form: "single",
|
|
88027
|
+
optional: false
|
|
88028
|
+
}],
|
|
87223
88029
|
"mediaPlayer.getStatus": [{
|
|
87224
88030
|
name: "deviceId",
|
|
87225
88031
|
form: "single",
|
|
@@ -87285,6 +88091,26 @@ Object.freeze({
|
|
|
87285
88091
|
form: "single",
|
|
87286
88092
|
optional: false
|
|
87287
88093
|
}],
|
|
88094
|
+
"mediaPlayer.turnOff": [{
|
|
88095
|
+
name: "deviceId",
|
|
88096
|
+
form: "single",
|
|
88097
|
+
optional: false
|
|
88098
|
+
}],
|
|
88099
|
+
"mediaPlayer.turnOn": [{
|
|
88100
|
+
name: "deviceId",
|
|
88101
|
+
form: "single",
|
|
88102
|
+
optional: false
|
|
88103
|
+
}],
|
|
88104
|
+
"mediaPlayer.volumeDown": [{
|
|
88105
|
+
name: "deviceId",
|
|
88106
|
+
form: "single",
|
|
88107
|
+
optional: false
|
|
88108
|
+
}],
|
|
88109
|
+
"mediaPlayer.volumeUp": [{
|
|
88110
|
+
name: "deviceId",
|
|
88111
|
+
form: "single",
|
|
88112
|
+
optional: false
|
|
88113
|
+
}],
|
|
87288
88114
|
"meter.getStatus": [{
|
|
87289
88115
|
name: "deviceId",
|
|
87290
88116
|
form: "single",
|
|
@@ -87594,21 +88420,6 @@ Object.freeze({
|
|
|
87594
88420
|
form: "single",
|
|
87595
88421
|
optional: false
|
|
87596
88422
|
}],
|
|
87597
|
-
"petFountain.setPumpInterval": [{
|
|
87598
|
-
name: "deviceId",
|
|
87599
|
-
form: "single",
|
|
87600
|
-
optional: false
|
|
87601
|
-
}],
|
|
87602
|
-
"petFountain.setPumpOffMinutes": [{
|
|
87603
|
-
name: "deviceId",
|
|
87604
|
-
form: "single",
|
|
87605
|
-
optional: false
|
|
87606
|
-
}],
|
|
87607
|
-
"petFountain.setPumpOnMinutes": [{
|
|
87608
|
-
name: "deviceId",
|
|
87609
|
-
form: "single",
|
|
87610
|
-
optional: false
|
|
87611
|
-
}],
|
|
87612
88423
|
"pipelineAnalytics.addRetrainFrameFromRecording": [{
|
|
87613
88424
|
name: "deviceId",
|
|
87614
88425
|
form: "single",
|
|
@@ -88244,6 +89055,26 @@ Object.freeze({
|
|
|
88244
89055
|
form: "single",
|
|
88245
89056
|
optional: false
|
|
88246
89057
|
}],
|
|
89058
|
+
"pumpCycle.getStatus": [{
|
|
89059
|
+
name: "deviceId",
|
|
89060
|
+
form: "single",
|
|
89061
|
+
optional: false
|
|
89062
|
+
}],
|
|
89063
|
+
"pumpCycle.setEnabled": [{
|
|
89064
|
+
name: "deviceId",
|
|
89065
|
+
form: "single",
|
|
89066
|
+
optional: false
|
|
89067
|
+
}],
|
|
89068
|
+
"pumpCycle.setOffMinutes": [{
|
|
89069
|
+
name: "deviceId",
|
|
89070
|
+
form: "single",
|
|
89071
|
+
optional: false
|
|
89072
|
+
}],
|
|
89073
|
+
"pumpCycle.setOnMinutes": [{
|
|
89074
|
+
name: "deviceId",
|
|
89075
|
+
form: "single",
|
|
89076
|
+
optional: false
|
|
89077
|
+
}],
|
|
88247
89078
|
"reboot.reboot": [{
|
|
88248
89079
|
name: "deviceId",
|
|
88249
89080
|
form: "single",
|
|
@@ -88445,6 +89276,11 @@ Object.freeze({
|
|
|
88445
89276
|
form: "single",
|
|
88446
89277
|
optional: true
|
|
88447
89278
|
}],
|
|
89279
|
+
"remote.getAssumedStatus": [{
|
|
89280
|
+
name: "deviceId",
|
|
89281
|
+
form: "single",
|
|
89282
|
+
optional: false
|
|
89283
|
+
}],
|
|
88448
89284
|
"remote.getConfig": [{
|
|
88449
89285
|
name: "deviceId",
|
|
88450
89286
|
form: "single",
|
|
@@ -88992,99 +89828,6 @@ Object.freeze({
|
|
|
88992
89828
|
"network-access": "ingress",
|
|
88993
89829
|
"smtp-provider": "email"
|
|
88994
89830
|
});
|
|
88995
|
-
/**
|
|
88996
|
-
* A saved Customize assignment (D936). The schema is the type: callers use
|
|
88997
|
-
* `z.infer`, they do not restate the shape.
|
|
88998
|
-
*/
|
|
88999
|
-
var OverrideSourceSchema = object({
|
|
89000
|
-
addonId: string().min(1),
|
|
89001
|
-
stableId: string().min(1),
|
|
89002
|
-
cap: string().min(1),
|
|
89003
|
-
fieldPath: string().min(1)
|
|
89004
|
-
});
|
|
89005
|
-
var CustomizeTransformDraftSchema = discriminatedUnion("kind", [
|
|
89006
|
-
object({ kind: literal("identity") }),
|
|
89007
|
-
object({ kind: literal("invert") }),
|
|
89008
|
-
object({
|
|
89009
|
-
kind: literal("threshold"),
|
|
89010
|
-
comparison: _enum(["gte", "lte"]),
|
|
89011
|
-
threshold: number(),
|
|
89012
|
-
startDwellMs: number().nonnegative(),
|
|
89013
|
-
stopDwellMs: number().nonnegative()
|
|
89014
|
-
}),
|
|
89015
|
-
object({
|
|
89016
|
-
kind: literal("linear"),
|
|
89017
|
-
scale: number(),
|
|
89018
|
-
offset: number(),
|
|
89019
|
-
clampMin: number().optional(),
|
|
89020
|
-
clampMax: number().optional()
|
|
89021
|
-
}),
|
|
89022
|
-
object({
|
|
89023
|
-
kind: literal("enum-map"),
|
|
89024
|
-
mapping: record(string(), string().nullable())
|
|
89025
|
-
}),
|
|
89026
|
-
object({ kind: literal("number") })
|
|
89027
|
-
]);
|
|
89028
|
-
var ScalarOverrideDraftSchema = object({
|
|
89029
|
-
kind: literal("scalar"),
|
|
89030
|
-
cap: string().min(1),
|
|
89031
|
-
targetId: string().min(1),
|
|
89032
|
-
source: OverrideSourceSchema,
|
|
89033
|
-
transform: CustomizeTransformDraftSchema
|
|
89034
|
-
});
|
|
89035
|
-
var KeyedArrayOverrideDraftSchema = object({
|
|
89036
|
-
kind: literal("keyedArray"),
|
|
89037
|
-
cap: string().min(1),
|
|
89038
|
-
targetId: string().min(1),
|
|
89039
|
-
key: string().min(1),
|
|
89040
|
-
label: string().min(1),
|
|
89041
|
-
fieldPath: string().min(1),
|
|
89042
|
-
source: OverrideSourceSchema,
|
|
89043
|
-
transform: CustomizeTransformDraftSchema
|
|
89044
|
-
});
|
|
89045
|
-
var PulseOverrideDraftSchema = object({
|
|
89046
|
-
kind: literal("pulse"),
|
|
89047
|
-
cap: string().min(1),
|
|
89048
|
-
targetId: string().min(1),
|
|
89049
|
-
enabled: boolean(),
|
|
89050
|
-
debounceMs: number().nonnegative(),
|
|
89051
|
-
edges: array(_enum([
|
|
89052
|
-
"rising",
|
|
89053
|
-
"button",
|
|
89054
|
-
"native-event"
|
|
89055
|
-
])).min(1).optional(),
|
|
89056
|
-
sources: array(OverrideSourceSchema).min(1)
|
|
89057
|
-
});
|
|
89058
|
-
var SpanSourceDraftSchema = OverrideSourceSchema.extend({ mode: _enum([
|
|
89059
|
-
"in-addition",
|
|
89060
|
-
"confirm",
|
|
89061
|
-
"replace"
|
|
89062
|
-
]).optional() });
|
|
89063
|
-
discriminatedUnion("kind", [
|
|
89064
|
-
ScalarOverrideDraftSchema,
|
|
89065
|
-
KeyedArrayOverrideDraftSchema,
|
|
89066
|
-
PulseOverrideDraftSchema,
|
|
89067
|
-
object({
|
|
89068
|
-
kind: literal("span"),
|
|
89069
|
-
cap: string().min(1),
|
|
89070
|
-
targetId: string().min(1),
|
|
89071
|
-
timeoutMs: number().nonnegative(),
|
|
89072
|
-
cooldownMs: number().nonnegative().optional(),
|
|
89073
|
-
sources: array(SpanSourceDraftSchema)
|
|
89074
|
-
})
|
|
89075
|
-
]);
|
|
89076
|
-
object({
|
|
89077
|
-
targetId: string().min(1),
|
|
89078
|
-
configured: boolean(),
|
|
89079
|
-
source: _enum(["native", "assigned"]),
|
|
89080
|
-
state: _enum([
|
|
89081
|
-
"active",
|
|
89082
|
-
"stale",
|
|
89083
|
-
"released",
|
|
89084
|
-
"unavailable"
|
|
89085
|
-
]),
|
|
89086
|
-
reason: string().nullable()
|
|
89087
|
-
});
|
|
89088
89831
|
var G711_SCALE_CORRECTION_DB = {
|
|
89089
89832
|
PCMU: 20 * Math.log10(4),
|
|
89090
89833
|
PCMA: 20 * Math.log10(8)
|
|
@@ -89700,6 +90443,7 @@ var STATE_SUMMARY_VARS = [
|
|
|
89700
90443
|
[...RULE_TEXT],
|
|
89701
90444
|
([...RULE_TEXT], [...DEVICE_EVENTS]),
|
|
89702
90445
|
([...RULE_TEXT], [...COMBINED_ALARM]),
|
|
90446
|
+
([...RULE_TEXT], [...COMBINED_ALARM]),
|
|
89703
90447
|
([...RULE_TEXT], [...DEVICE_EVENTS]),
|
|
89704
90448
|
([...RULE_TEXT], [...BATTERY, ...CONSUMABLE]),
|
|
89705
90449
|
([...RULE_TEXT], [...CONSUMABLE]),
|
|
@@ -145067,6 +145811,7 @@ var SmtpNodemailerAddon = class extends BaseAddon {
|
|
|
145067
145811
|
});
|
|
145068
145812
|
this.instances = new RecordIntegrationInstances(records, smtpRelaysKind, {
|
|
145069
145813
|
description: {
|
|
145814
|
+
role: "services",
|
|
145070
145815
|
instanceMode: "multiple",
|
|
145071
145816
|
kind: "relays",
|
|
145072
145817
|
canTest: true
|
|
@@ -145262,7 +146007,8 @@ var SmtpNodemailerAddon = class extends BaseAddon {
|
|
|
145262
146007
|
const rows = (await records.kind(smtpRelaysKind).query({ limit: RELAY_LIST_MAX })).records;
|
|
145263
146008
|
return (rows.find((row) => row.data.host.length > 0) ?? rows[0])?.id;
|
|
145264
146009
|
}
|
|
145265
|
-
|
|
146010
|
+
/** The relay form. Its values live on the relay record, never in `addon-settings`. */
|
|
146011
|
+
async integrationFormSchema(_instance) {
|
|
145266
146012
|
return this.relayForm();
|
|
145267
146013
|
}
|
|
145268
146014
|
async getGlobalSettings() {
|