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