@camstack/addon-import-alexa 0.2.197 → 0.2.199

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/dist/addon.js CHANGED
@@ -5676,7 +5676,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
5676
5676
  return EventCategory;
5677
5677
  }({});
5678
5678
  //#endregion
5679
- //#region ../types/dist/device-type-D1fH5kaC.mjs
5679
+ //#region ../types/dist/device-type-DxCT0krO.mjs
5680
5680
  /**
5681
5681
  import { errMsg } from '@camstack/types'
5682
5682
  * Extract a human-readable message from an unknown error value.
@@ -5838,6 +5838,13 @@ var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
5838
5838
  * filters that want "all devices with on-motion behaviour".
5839
5839
  */
5840
5840
  DeviceFeature["MotionTrigger"] = "motion-trigger";
5841
+ /**
5842
+ * Device can be run for a chosen time and then stopped. Mirrors the
5843
+ * `timed-run` wrapper cap — the cap is bound by the core-blocks addon, not by
5844
+ * a driver, so the flag is derived from the device's bindings
5845
+ * (`featuresOfBoundCaps`). Gates the quick-run control in the viewer.
5846
+ */
5847
+ DeviceFeature["TimedRun"] = "timed-run";
5841
5848
  /** Light supports rgb-triplet color via `color` cap. */
5842
5849
  DeviceFeature["LightColorRgb"] = "light-color-rgb";
5843
5850
  /** Light supports HSV color via `color` cap. */
@@ -6061,7 +6068,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
6061
6068
  return DeviceRole;
6062
6069
  }({});
6063
6070
  //#endregion
6064
- //#region ../types/dist/sleep-CQP-REJp.mjs
6071
+ //#region ../types/dist/sleep-CGPZyzB5.mjs
6065
6072
  /**
6066
6073
  * The audio chunk plane's byte format, and the ONE expansion from a coded
6067
6074
  * window to float samples (D455).
@@ -7293,6 +7300,7 @@ var RecordIntegrationInstances = class {
7293
7300
  this.secretKeys = topLevelSecretKeys(kind.declaration.secretPaths);
7294
7301
  this.secretMaps = secretMapKeys(kind.declaration.secretPaths);
7295
7302
  this.nameField = kind.declaration.entry?.nameField ?? "name";
7303
+ if (hooks.kindOf === void 0 && hooks.description.kinds.length !== 1) throw new Error(`record kind '${kind.name}': describe().kinds has ${hooks.description.kinds.length} entries, so kindOf is required to say which one an instance is`);
7296
7304
  }
7297
7305
  /**
7298
7306
  * One instance read for the addon's own runtime; null when absent. When the keychain is
@@ -7334,10 +7342,21 @@ var RecordIntegrationInstances = class {
7334
7342
  }
7335
7343
  return out;
7336
7344
  }
7345
+ /** The kind an instance body is of: the provider's `kindOf`, else its one declared kind. */
7346
+ kindOfBody(body) {
7347
+ if (this.hooks.kindOf !== void 0) {
7348
+ const id = this.hooks.kindOf(body);
7349
+ if (!this.hooks.description.kinds.some((declared) => declared.id === id)) throw new Error(`record kind '${this.kind.name}': kindOf returned '${id}', which describe().kinds does not declare (D1013)`);
7350
+ return id;
7351
+ }
7352
+ const only = this.hooks.description.kinds[0];
7353
+ if (only === void 0) throw new Error(`record kind '${this.kind.name}' declares no kinds`);
7354
+ return only.id;
7355
+ }
7337
7356
  viewOf(record) {
7338
7357
  const shown = secretViewsOf(this.records.owner, this.kind.name, record.id, this.kind.declaration, record.data);
7339
7358
  const rawName = record.data[this.nameField];
7340
- const kind = this.hooks.kindOf?.(record.data);
7359
+ const kind = this.kindOfBody(record.data);
7341
7360
  const health = this.hooks.health?.(record.data, record.id);
7342
7361
  return {
7343
7362
  id: record.id,
@@ -7345,7 +7364,7 @@ var RecordIntegrationInstances = class {
7345
7364
  configured: this.hooks.configured(record.data),
7346
7365
  enabled: isInstanceEnabled(record.data),
7347
7366
  formValues: shown,
7348
- ...kind !== void 0 ? { kind } : {},
7367
+ kind,
7349
7368
  ...health?.status !== void 0 ? { status: health.status } : {},
7350
7369
  ...health?.lastCheckedAt !== void 0 ? { lastCheckedAt: health.lastCheckedAt } : {}
7351
7370
  };
@@ -8446,7 +8465,7 @@ function boundOf(list) {
8446
8465
  * Base class for CamStack addons. Eliminates settings boilerplate:
8447
8466
  *
8448
8467
  * - Typed `config` property with automatic resolution from store + defaults
8449
- * - `getGlobalSettings()` / `updateGlobalSettings()` auto-implemented
8468
+ * - the scoped settings API (`clusterSettings` / `nodeSettings` / `deviceDefaults`) auto-implemented
8450
8469
  * - `getAddonSettings()` / `updateAddonSettings()` auto-implemented
8451
8470
  * - `getDeviceSettings()` / `updateDeviceSettings()` auto-implemented
8452
8471
  * - `ctx` accessor for the AddonContext (no manual `this.ctxRef` storage)
@@ -8456,7 +8475,7 @@ function boundOf(list) {
8456
8475
  * Subclasses override:
8457
8476
  * - `onInitialize()` — addon-specific init logic, return ProviderRegistration[]
8458
8477
  * - `onShutdown()` — cleanup
8459
- * - `globalSettingsSchema()` / `deviceSettingsSchema()` — UI schemas
8478
+ * - `clusterSettingsSchema()` / `nodeSettingsSchema()` / `deviceSettingsSchema()` — UI schemas
8460
8479
  *
8461
8480
  * @example
8462
8481
  * ```ts
@@ -8470,7 +8489,7 @@ function boundOf(list) {
8470
8489
  * super({ maxRetries: 3, endpoint: 'http://localhost' })
8471
8490
  * }
8472
8491
  *
8473
- * protected globalSettingsSchema() {
8492
+ * protected clusterSettingsSchema() {
8474
8493
  * return this.schema({
8475
8494
  * sections: [{
8476
8495
  * id: 'main', title: 'Settings',
@@ -8513,7 +8532,7 @@ var BaseAddon = class {
8513
8532
  defaults;
8514
8533
  /**
8515
8534
  * Hidden-state paths that hold secrets (D787) — values the addon writes
8516
- * itself (`updateGlobalSettings`, `state()`) that no form field declares.
8535
+ * itself (`state()`, the scoped writers) that no form field declares.
8517
8536
  * Stored in the keychain exactly like a `password` field. Syntax:
8518
8537
  * `'snapshotSigningKey'`, `'identity.pincode'`, `'tokens[userId].accessToken'`
8519
8538
  * (`key[<id field>]` addresses array items by id, never by index).
@@ -8523,7 +8542,7 @@ var BaseAddon = class {
8523
8542
  /**
8524
8543
  * D986 M4: the scoped schemas this addon declared, resolved once per capability
8525
8544
  * (`loadScopedSchemas`) so the sync readers (`perNodeKeys`, the secret tree, the restart check)
8526
- * see them. Empty until loaded: the readers then fall back to {@link globalSettingsSchema}.
8545
+ * see them. Empty until loaded.
8527
8546
  */
8528
8547
  _scopedByCap = /* @__PURE__ */ new Map();
8529
8548
  _nodeKeysByCap = /* @__PURE__ */ new Map();
@@ -8709,7 +8728,7 @@ var BaseAddon = class {
8709
8728
  /** Addon-specific cleanup. Override if needed. */
8710
8729
  async onShutdown() {}
8711
8730
  /**
8712
- * Called after config is resolved during updateGlobalSettings/updateAddonSettings.
8731
+ * Called after config is resolved during a settings write.
8713
8732
  * Override to react to config changes (e.g. restart a sampler, reconnect a service).
8714
8733
  * Not called during initialize() — use onInitialize() for initial setup.
8715
8734
  */
@@ -8738,82 +8757,12 @@ var BaseAddon = class {
8738
8757
  scopedSettingsCapabilities() {
8739
8758
  return [];
8740
8759
  }
8741
- /** Override to provide global-level settings UI schema. */
8742
- globalSettingsSchema(_cap) {
8743
- return null;
8744
- }
8745
8760
  /** Override to provide device-level settings UI schema. */
8746
8761
  deviceSettingsSchema() {
8747
8762
  return null;
8748
8763
  }
8749
8764
  /**
8750
- * INTEGRATION-LEVEL SETTINGS — declare which of this addon's global sections
8751
- * ARE the configuration of its integration.
8752
- *
8753
- * Return the `ConfigSection.id`s, from {@link globalSettingsSchema}, that an
8754
- * operator should find on the addon's integration page (System →
8755
- * Integrations → <name>) rather than only in the cluster-wide list of every
8756
- * addon. Empty (the default) means the addon has no integration-level
8757
- * settings and no such surface is offered — this is opt-in, because whether
8758
- * an addon's configuration IS its integration's configuration depends on the
8759
- * nature of the integration.
8760
- *
8761
- * WHAT THIS IS NOT. It is not a scope. The selected sections keep living in
8762
- * the ONE global schema, in the ONE addon store, written by the ONE
8763
- * `updateGlobalSettings` path. There is deliberately no
8764
- * `updateIntegrationSettings`: a second write path is how a surface acquires
8765
- * a second store key, and this repo has shipped that twice (`btmPath@hub`,
8766
- * D266). Selecting sections cannot introduce a key that selecting cannot.
8767
- *
8768
- * WHY IT IS A LIST OF SECTION IDS AND NOT A MARKER ON THE SECTION.
8769
- * `ConfigFieldBase` used to carry `scope?: 'device' | 'global'` and it was
8770
- * removed with the reason recorded at
8771
- * `packages/types/src/interfaces/config-ui.ts:249` — *"a field's scope is
8772
- * determined by WHICH schema it lives in, not by a field-level marker."* A
8773
- * marker sprinkled across sections also has to borrow a field that already
8774
- * means something else; borrowing `section.tab` put the literal word
8775
- * "integration" into an operator-facing tab bar, because `tab` means "how to
8776
- * GROUP this visually" and cannot also mean "where this lives" (D269
8777
- * supersedes D268). One declaration, in one place, next to the schema whose
8778
- * ids it names.
8779
- */
8780
- integrationSettingSections() {
8781
- return [];
8782
- }
8783
- async getGlobalSettings(overlay, cap, nodeId) {
8784
- await this.loadScopedSchemas(cap);
8785
- const schema = this.globalSettingsSchema(cap) ?? this.scopedFormSchema(cap);
8786
- if (!schema) return { sections: [] };
8787
- const projected = await this.readRawProjectedStore(nodeId, cap);
8788
- return hydrateSchema(schema, overlay ? await this.overlayKeepingMarkers(projected, overlay) : projected, { inlineSecretsAsViews: true });
8789
- }
8790
- /**
8791
- * The integration-level view of this addon's settings: exactly the sections
8792
- * named by {@link integrationSettingSections}, hydrated from the SAME store
8793
- * `getGlobalSettings` reads, and narrowed to cluster-scoped fields.
8794
- *
8795
- * Returns `null` when the addon declared nothing — an addon that opts out has
8796
- * no integration settings surface at all, rather than an empty one that reads
8797
- * as a failed load.
8798
- *
8799
- * Three properties hold BY CONSTRUCTION, which is why they are here in core
8800
- * and not in whichever UI happens to render this:
8801
- *
8802
- * 1. **One key.** The payload is a SUBSET of the global schema, so a field
8803
- * shown here is the same field, with the same bare key, that the addon's
8804
- * own page shows. There is no integration-specific writer — callers save
8805
- * through `updateGlobalSettings` — so a second store key is unreachable,
8806
- * not merely discouraged.
8807
- * 2. **No node scope.** `perNode: true` fields are DROPPED. Their store key
8808
- * is `<key>@<nodeId>` and an integration is not a node; whichever node
8809
- * such a field silently picked would be a wrong answer for the operator
8810
- * who opened the page (D266).
8811
- * 3. **No silent typo.** A declared id that names no section throws. The
8812
- * alternative — skip it — turns a rename into a surface that quietly
8813
- * empties, which looks exactly like an addon with nothing to configure.
8814
- */
8815
- /**
8816
- * The form schema of ONE integration instance, or `null` to use {@link globalSettingsSchema}.
8765
+ * The form schema of ONE integration instance, or `null` to use the scoped form.
8817
8766
  *
8818
8767
  * Override it only when an `instanceMode: multiple` addon's instances differ: an NVR integration
8819
8768
  * needs its connection fields, the default integration the addon's own sections. It is the
@@ -8986,6 +8935,19 @@ var BaseAddon = class {
8986
8935
  const overlay = await this.scopedSettingsOverlay(scope, cap, nodeId);
8987
8936
  return hydrateSchema(schema, overlay ? await this.overlayKeepingMarkers(stored, overlay) : stored, { inlineSecretsAsViews: true });
8988
8937
  }
8938
+ /**
8939
+ * The scoped schemas this addon serves: one entry per loaded (capability, scope), capabilities
8940
+ * in declaration order, scopes in {@link SCOPED_SETTINGS_SCOPES} order. `[]` when it declares
8941
+ * nothing.
8942
+ */
8943
+ async listScopedSettings() {
8944
+ await this.loadScopedSchemas();
8945
+ return [...this._scopedByCap].flatMap(([cap, entries]) => entries.map((entry) => ({
8946
+ cap,
8947
+ scope: entry.scope,
8948
+ sectionIds: entry.schema.sections.map((section) => section.id)
8949
+ })));
8950
+ }
8989
8951
  /** The OPERATOR's write of declared keys of one scope (D787 secret semantics). */
8990
8952
  async updateScopedSettings(scope, cap, patch, nodeId) {
8991
8953
  this._operatorWriteDepth++;
@@ -9011,32 +8973,15 @@ var BaseAddon = class {
9011
8973
  */
9012
8974
  async getIntegrationFormSchema(instance, nodeId) {
9013
8975
  const scoped = await this.integrationFormSchema(instance);
9014
- if (scoped === null) return this.getGlobalSettings(void 0, void 0, nodeId);
8976
+ if (scoped === null) return this.scopedForm(nodeId);
9015
8977
  return hydrateSchema(scoped, {}, { inlineSecretsAsViews: true });
9016
8978
  }
9017
- async getIntegrationSettings(nodeId) {
9018
- const declared = this.integrationSettingSections();
9019
- if (declared.length === 0) return null;
9020
- const schema = this.globalSettingsSchema();
9021
- if (!schema) throw new Error(`${this.constructor.name}: integrationSettingSections() names [${declared.join(", ")}] but globalSettingsSchema() returns null.`);
9022
- const byId = new Map(schema.sections.map((section) => [section.id, section]));
9023
- const sections = [];
9024
- for (const id of declared) {
9025
- const section = byId.get(id);
9026
- if (!section) throw new Error(`${this.constructor.name}: integrationSettingSections() names unknown section "${id}". Known sections: [${[...byId.keys()].join(", ")}].`);
9027
- const fields = dropPerNodeFields(section.fields);
9028
- if (fields.length === 0) continue;
9029
- sections.push({
9030
- ...section,
9031
- fields
9032
- });
9033
- }
9034
- if (sections.length === 0) return null;
9035
- const projected = await this.readRawProjectedStore(nodeId);
9036
- return hydrateSchema({
9037
- ...schema,
9038
- sections
9039
- }, projected, { inlineSecretsAsViews: true });
8979
+ /** The form of the scoped schemas this addon declared, hydrated from its store. */
8980
+ async scopedForm(nodeId) {
8981
+ await this.loadScopedSchemas();
8982
+ const schema = this.scopedFormSchema();
8983
+ if (!schema) return { sections: [] };
8984
+ return hydrateSchema(schema, await this.readRawProjectedStore(nodeId), { inlineSecretsAsViews: true });
9040
8985
  }
9041
8986
  /**
9042
8987
  * The raw addon store PROJECTED onto the target node's bare per-node keys:
@@ -9045,8 +8990,8 @@ var BaseAddon = class {
9045
8990
  * bare fallback), all `@`-scoped and stray bare per-node keys are dropped.
9046
8991
  * A no-op passthrough when the schema declares no `perNode` field.
9047
8992
  *
9048
- * This is the sanctioned way for a `getGlobalSettings` OVERRIDE that needs
9049
- * the store for custom option logic (option narrowing, value snapping) to
8993
+ * This is the sanctioned way for an addon that needs
8994
+ * the store for custom logic (option narrowing, value snapping) to
9050
8995
  * read it per-node — never `ctx.settings.readAddonStore()` directly.
9051
8996
  * `nodeId` omitted ⇒ the local node (node-blind injection from ctx).
9052
8997
  *
@@ -9098,7 +9043,7 @@ var BaseAddon = class {
9098
9043
  }
9099
9044
  /**
9100
9045
  * Whether ONE global key of this addon's store was written, read three-way
9101
- * (D651). For a question a hydrated view cannot answer: `getGlobalSettings`
9046
+ * (D651). For a question a hydrated view cannot answer: a hydrated form
9102
9047
  * reports `value ?? default`, so "the operator chose the default" and "nobody
9103
9048
  * chose" look the same, and a failed read looks like both. A `perNode` key is
9104
9049
  * not supported here: its stored name is scoped.
@@ -9132,7 +9077,7 @@ var BaseAddon = class {
9132
9077
  }
9133
9078
  /**
9134
9079
  * The OPERATOR's write — a settings form or an admin API call, dispatched by
9135
- * the kernel. Identical to {@link updateGlobalSettings}, except that a secret
9080
+ * the kernel. The one in-process write of the addon blob that is not a scoped write; a secret
9136
9081
  * the operator sends is stored as sent even when it equals a pre-keychain
9137
9082
  * inline value: the operator re-entering a password is a change, an addon
9138
9083
  * writing back what it read is not (D787).
@@ -9145,9 +9090,6 @@ var BaseAddon = class {
9145
9090
  this._operatorWriteDepth--;
9146
9091
  }
9147
9092
  }
9148
- async updateGlobalSettings(patch, nodeId) {
9149
- await this.applyGlobalPatch(patch, nodeId);
9150
- }
9151
9093
  /**
9152
9094
  * REMOVE stored secrets — their keychain values and their markers (D787).
9153
9095
  *
@@ -9190,8 +9132,7 @@ var BaseAddon = class {
9190
9132
  await this.resolveConfig();
9191
9133
  await this.onConfigChanged();
9192
9134
  this.emitLifecycle(EventCategory.AddonUpdated, { level: "global" });
9193
- const legacy = this.globalSettingsSchema();
9194
- this.maybeAutoRestart(Object.keys(barePatch), [...legacy === null ? [] : [legacy], ...this.loadedScopedSchemas().map((entry) => entry.schema)]);
9135
+ this.maybeAutoRestart(Object.keys(barePatch), this.loadedScopedSchemas().map((entry) => entry.schema));
9195
9136
  return true;
9196
9137
  }
9197
9138
  /**
@@ -9229,10 +9170,7 @@ var BaseAddon = class {
9229
9170
  this._nodeKeysByCap = /* @__PURE__ */ new Map();
9230
9171
  this._addonSecretTree = null;
9231
9172
  }
9232
- /**
9233
- * The form the settings API serves while an addon declares no {@link globalSettingsSchema}: the
9234
- * sections of its scoped schemas, so dropping the old hook does not blank the form.
9235
- */
9173
+ /** The form of an addon's scoped schemas: their sections, each shown once. */
9236
9174
  scopedFormSchema(cap) {
9237
9175
  const byId = /* @__PURE__ */ new Map();
9238
9176
  for (const entry of this.loadedScopedSchemas(cap)) for (const section of entry.schema.sections) if (!byId.has(section.id)) byId.set(section.id, section);
@@ -9244,8 +9182,7 @@ var BaseAddon = class {
9244
9182
  return [...this._scopedByCap.values()].flat();
9245
9183
  }
9246
9184
  /**
9247
- * The field keys that are stored per node: every field of a {@link nodeSettingsSchema}, plus
9248
- * the fields the legacy {@link globalSettingsSchema} still marks `perNode: true`. Derived once
9185
+ * The field keys that are stored per node: every field of a {@link nodeSettingsSchema}. Derived once
9249
9186
  * per `cap` argument and memoized (schemas are static declarations). Empty set ⇒ every
9250
9187
  * per-node code path is bypassed and the settings API behaves like the node-agnostic one.
9251
9188
  * Call {@link loadScopedSchemas} first (the async seams do).
@@ -9254,8 +9191,7 @@ var BaseAddon = class {
9254
9191
  const cacheKey = cap ?? "";
9255
9192
  const cached = this._nodeKeysByCap.get(cacheKey);
9256
9193
  if (cached) return cached;
9257
- const legacy = this.globalSettingsSchema(cap);
9258
- const keys = new Set(legacy ? legacy.sections.flatMap((section) => collectPerNodeFieldKeys(section.fields)) : []);
9194
+ const keys = /* @__PURE__ */ new Set();
9259
9195
  for (const { scope, schema } of this.loadedScopedSchemas(cap)) {
9260
9196
  if (scope !== "node") continue;
9261
9197
  for (const section of schema.sections) for (const key of collectValueFieldKeys(section.fields)) keys.add(key);
@@ -9473,8 +9409,6 @@ var BaseAddon = class {
9473
9409
  }
9474
9410
  declaredKeySet() {
9475
9411
  const keys = new Set(Object.keys(this.defaults));
9476
- const schema = this.globalSettingsSchema();
9477
- if (schema) for (const section of schema.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
9478
9412
  for (const { schema: scoped } of this.loadedScopedSchemas()) for (const section of scoped.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
9479
9413
  for (const key of this._declaredStateKeys) keys.add(key);
9480
9414
  for (const path of this.secretStatePaths) {
@@ -9572,7 +9506,7 @@ var BaseAddon = class {
9572
9506
  /** The addon store's secret tree: schema fields by TYPE, plus `secretStatePaths`. */
9573
9507
  addonSecretTree() {
9574
9508
  if (this._addonSecretTree === null) {
9575
- const tree = new Map(secretTreeFromSchema(this.globalSettingsSchema()));
9509
+ const tree = /* @__PURE__ */ new Map();
9576
9510
  for (const { schema } of this.loadedScopedSchemas()) for (const [key, node] of secretTreeFromSchema(schema)) tree.set(key, node);
9577
9511
  this._addonSecretTree = mergeSecretStatePaths(tree, this.secretStatePaths);
9578
9512
  }
@@ -9784,47 +9718,6 @@ function classifyStoreError(err) {
9784
9718
  if (TRANSPORT_PHRASES.some((f) => message.includes(f))) return "transport";
9785
9719
  return "permanent";
9786
9720
  }
9787
- /**
9788
- * Collect the keys of every field marked `perNode: true`, recursing into
9789
- * layout containers (`group` fields and `sub-tabs` tabs) the same way
9790
- * `hydrateSchema` does. Valueless structural fields (separator/info/…)
9791
- * don't declare `perNode` and are excluded by the `in` narrowing.
9792
- */
9793
- /**
9794
- * The same fields with every `perNode: true` one removed, recursing into layout
9795
- * containers exactly as {@link collectPerNodeFieldKeys} does. A container left
9796
- * with no child is dropped rather than rendered empty.
9797
- *
9798
- * Used by `getIntegrationSettings`: an integration is not a node, so a field
9799
- * whose store key is `<key>@<nodeId>` has no node to belong to there.
9800
- */
9801
- function dropPerNodeFields(fields) {
9802
- const kept = [];
9803
- for (const field of fields) {
9804
- if (field.type === "group") {
9805
- const inner = dropPerNodeFields(field.fields);
9806
- if (inner.length > 0) kept.push({
9807
- ...field,
9808
- fields: inner
9809
- });
9810
- continue;
9811
- }
9812
- if (field.type === "sub-tabs") {
9813
- const tabs = field.tabs.map((tab) => ({
9814
- ...tab,
9815
- fields: dropPerNodeFields(tab.fields)
9816
- })).filter((tab) => tab.fields.length > 0);
9817
- if (tabs.length > 0) kept.push({
9818
- ...field,
9819
- tabs
9820
- });
9821
- continue;
9822
- }
9823
- if ("perNode" in field && field.perNode === true) continue;
9824
- kept.push(field);
9825
- }
9826
- return kept;
9827
- }
9828
9721
  /** The keys of the value-holding fields, recursing into layout containers. */
9829
9722
  function collectValueFieldKeys(fields) {
9830
9723
  const collected = [];
@@ -9857,21 +9750,6 @@ function collectFieldKeys(fields) {
9857
9750
  }
9858
9751
  return collected;
9859
9752
  }
9860
- function collectPerNodeFieldKeys(fields) {
9861
- const collected = [];
9862
- for (const field of fields) {
9863
- if (field.type === "group") {
9864
- collected.push(...collectPerNodeFieldKeys(field.fields));
9865
- continue;
9866
- }
9867
- if (field.type === "sub-tabs") {
9868
- for (const tab of field.tabs) collected.push(...collectPerNodeFieldKeys(tab.fields));
9869
- continue;
9870
- }
9871
- if ("perNode" in field && field.perNode === true) collected.push(field.key);
9872
- }
9873
- return collected;
9874
- }
9875
9753
  /**
9876
9754
  * Normalize an `ICamstackAddon.initialize()` return value into the
9877
9755
  * `AddonInitResult` envelope. Arrays are wrapped into `{ providers }`;
@@ -12632,13 +12510,25 @@ Object.freeze({
12632
12510
  "path": "temperature",
12633
12511
  "valueType": "number"
12634
12512
  }],
12635
- "timed-run": [{
12636
- "path": "activeUntil",
12637
- "valueType": "number"
12638
- }, {
12639
- "path": "requestedBy",
12640
- "valueType": "string"
12641
- }],
12513
+ "timed-run": [
12514
+ {
12515
+ "path": "activeUntil",
12516
+ "valueType": "number"
12517
+ },
12518
+ {
12519
+ "path": "requestedBy",
12520
+ "valueType": "string"
12521
+ },
12522
+ {
12523
+ "path": "quickRun.duration",
12524
+ "valueType": "number"
12525
+ },
12526
+ {
12527
+ "path": "quickRun.unit",
12528
+ "valueType": "enum",
12529
+ "enumValues": ["s", "min"]
12530
+ }
12531
+ ],
12642
12532
  "update": [
12643
12533
  {
12644
12534
  "path": "currentVersion",
@@ -12862,7 +12752,7 @@ object({
12862
12752
  params: record(string(), union([string(), number$1()])).optional()
12863
12753
  });
12864
12754
  //#endregion
12865
- //#region ../types/dist/composition-BmBHuDTQ.mjs
12755
+ //#region ../types/dist/composition-Bx0JvKUU.mjs
12866
12756
  /**
12867
12757
  * The ffmpeg build every CamStack node downloads, per platform and arch.
12868
12758
  *
@@ -14024,6 +13914,387 @@ var NodeColorIdSchema = _enum([
14024
13914
  "node-h"
14025
13915
  ]);
14026
13916
  //#endregion
13917
+ //#region ../types/dist/convert-BAzH15HU.mjs
13918
+ /**
13919
+ * Frozen unit → spec table. `•` in the doc marks each dimension's canonical
13920
+ * unit (`factor: 1, offset: 0`). °F derives from `°C = (°F − 32) × 5/9` ⇒
13921
+ * `factor = 5/9`, `offset = -160/9` (kept as exact expressions, not rounded
13922
+ * decimals). K derives from `°C = K − 273.15`.
13923
+ */
13924
+ var UNIT_TABLE = {
13925
+ "°C": {
13926
+ dimension: "temperature",
13927
+ factor: 1,
13928
+ offset: 0
13929
+ },
13930
+ "°F": {
13931
+ dimension: "temperature",
13932
+ factor: 5 / 9,
13933
+ offset: -160 / 9
13934
+ },
13935
+ K: {
13936
+ dimension: "temperature",
13937
+ factor: 1,
13938
+ offset: -273.15
13939
+ },
13940
+ hPa: {
13941
+ dimension: "pressure",
13942
+ factor: 1,
13943
+ offset: 0
13944
+ },
13945
+ kPa: {
13946
+ dimension: "pressure",
13947
+ factor: 10,
13948
+ offset: 0
13949
+ },
13950
+ Pa: {
13951
+ dimension: "pressure",
13952
+ factor: .01,
13953
+ offset: 0
13954
+ },
13955
+ mbar: {
13956
+ dimension: "pressure",
13957
+ factor: 1,
13958
+ offset: 0
13959
+ },
13960
+ bar: {
13961
+ dimension: "pressure",
13962
+ factor: 1e3,
13963
+ offset: 0
13964
+ },
13965
+ inHg: {
13966
+ dimension: "pressure",
13967
+ factor: 33.8639,
13968
+ offset: 0
13969
+ },
13970
+ mmHg: {
13971
+ dimension: "pressure",
13972
+ factor: 1.33322,
13973
+ offset: 0
13974
+ },
13975
+ psi: {
13976
+ dimension: "pressure",
13977
+ factor: 68.9476,
13978
+ offset: 0
13979
+ },
13980
+ "m/s": {
13981
+ dimension: "speed",
13982
+ factor: 1,
13983
+ offset: 0
13984
+ },
13985
+ "km/h": {
13986
+ dimension: "speed",
13987
+ factor: 1 / 3.6,
13988
+ offset: 0
13989
+ },
13990
+ mph: {
13991
+ dimension: "speed",
13992
+ factor: .44704,
13993
+ offset: 0
13994
+ },
13995
+ kn: {
13996
+ dimension: "speed",
13997
+ factor: .514444,
13998
+ offset: 0
13999
+ },
14000
+ "mm/h": {
14001
+ dimension: "precipitation-rate",
14002
+ factor: 1,
14003
+ offset: 0
14004
+ },
14005
+ "in/h": {
14006
+ dimension: "precipitation-rate",
14007
+ factor: 25.4,
14008
+ offset: 0
14009
+ },
14010
+ m: {
14011
+ dimension: "length",
14012
+ factor: 1,
14013
+ offset: 0
14014
+ },
14015
+ mm: {
14016
+ dimension: "length",
14017
+ factor: .001,
14018
+ offset: 0
14019
+ },
14020
+ cm: {
14021
+ dimension: "length",
14022
+ factor: .01,
14023
+ offset: 0
14024
+ },
14025
+ km: {
14026
+ dimension: "length",
14027
+ factor: 1e3,
14028
+ offset: 0
14029
+ },
14030
+ in: {
14031
+ dimension: "length",
14032
+ factor: .0254,
14033
+ offset: 0
14034
+ },
14035
+ ft: {
14036
+ dimension: "length",
14037
+ factor: .3048,
14038
+ offset: 0
14039
+ },
14040
+ mi: {
14041
+ dimension: "length",
14042
+ factor: 1609.344,
14043
+ offset: 0
14044
+ },
14045
+ "m²": {
14046
+ dimension: "area",
14047
+ factor: 1,
14048
+ offset: 0
14049
+ },
14050
+ lx: {
14051
+ dimension: "illuminance",
14052
+ factor: 1,
14053
+ offset: 0
14054
+ },
14055
+ "W/m²": {
14056
+ dimension: "irradiance",
14057
+ factor: 1,
14058
+ offset: 0
14059
+ },
14060
+ W: {
14061
+ dimension: "power",
14062
+ factor: 1,
14063
+ offset: 0
14064
+ },
14065
+ kW: {
14066
+ dimension: "power",
14067
+ factor: 1e3,
14068
+ offset: 0
14069
+ },
14070
+ VA: {
14071
+ dimension: "apparent-power",
14072
+ factor: 1,
14073
+ offset: 0
14074
+ },
14075
+ Wh: {
14076
+ dimension: "energy",
14077
+ factor: 1,
14078
+ offset: 0
14079
+ },
14080
+ kWh: {
14081
+ dimension: "energy",
14082
+ factor: 1e3,
14083
+ offset: 0
14084
+ },
14085
+ MWh: {
14086
+ dimension: "energy",
14087
+ factor: 1e6,
14088
+ offset: 0
14089
+ },
14090
+ V: {
14091
+ dimension: "voltage",
14092
+ factor: 1,
14093
+ offset: 0
14094
+ },
14095
+ mV: {
14096
+ dimension: "voltage",
14097
+ factor: .001,
14098
+ offset: 0
14099
+ },
14100
+ A: {
14101
+ dimension: "current",
14102
+ factor: 1,
14103
+ offset: 0
14104
+ },
14105
+ mA: {
14106
+ dimension: "current",
14107
+ factor: .001,
14108
+ offset: 0
14109
+ },
14110
+ ppm: {
14111
+ dimension: "concentration-volume",
14112
+ factor: 1,
14113
+ offset: 0
14114
+ },
14115
+ ppb: {
14116
+ dimension: "concentration-volume",
14117
+ factor: .001,
14118
+ offset: 0
14119
+ },
14120
+ "µg/m³": {
14121
+ dimension: "concentration-mass",
14122
+ factor: 1,
14123
+ offset: 0
14124
+ },
14125
+ "mg/m³": {
14126
+ dimension: "concentration-mass",
14127
+ factor: 1e3,
14128
+ offset: 0
14129
+ },
14130
+ kg: {
14131
+ dimension: "mass",
14132
+ factor: 1,
14133
+ offset: 0
14134
+ },
14135
+ g: {
14136
+ dimension: "mass",
14137
+ factor: .001,
14138
+ offset: 0
14139
+ },
14140
+ lb: {
14141
+ dimension: "mass",
14142
+ factor: .453592,
14143
+ offset: 0
14144
+ },
14145
+ oz: {
14146
+ dimension: "mass",
14147
+ factor: .0283495,
14148
+ offset: 0
14149
+ },
14150
+ "%": {
14151
+ dimension: "percentage",
14152
+ factor: 1,
14153
+ offset: 0
14154
+ },
14155
+ L: {
14156
+ dimension: "volume",
14157
+ factor: 1,
14158
+ offset: 0
14159
+ },
14160
+ mL: {
14161
+ dimension: "volume",
14162
+ factor: .001,
14163
+ offset: 0
14164
+ },
14165
+ gal: {
14166
+ dimension: "volume",
14167
+ factor: 3.785411784,
14168
+ offset: 0
14169
+ },
14170
+ s: {
14171
+ dimension: "duration",
14172
+ factor: 1,
14173
+ offset: 0
14174
+ },
14175
+ ms: {
14176
+ dimension: "duration",
14177
+ factor: .001,
14178
+ offset: 0
14179
+ },
14180
+ min: {
14181
+ dimension: "duration",
14182
+ factor: 60,
14183
+ offset: 0
14184
+ },
14185
+ h: {
14186
+ dimension: "duration",
14187
+ factor: 3600,
14188
+ offset: 0
14189
+ },
14190
+ d: {
14191
+ dimension: "duration",
14192
+ factor: 86400,
14193
+ offset: 0
14194
+ },
14195
+ B: {
14196
+ dimension: "information",
14197
+ factor: 1,
14198
+ offset: 0
14199
+ },
14200
+ KiB: {
14201
+ dimension: "information",
14202
+ factor: 1024,
14203
+ offset: 0
14204
+ },
14205
+ MiB: {
14206
+ dimension: "information",
14207
+ factor: 1024 ** 2,
14208
+ offset: 0
14209
+ },
14210
+ GiB: {
14211
+ dimension: "information",
14212
+ factor: 1024 ** 3,
14213
+ offset: 0
14214
+ },
14215
+ TiB: {
14216
+ dimension: "information",
14217
+ factor: 1024 ** 4,
14218
+ offset: 0
14219
+ },
14220
+ kB: {
14221
+ dimension: "information",
14222
+ factor: 1e3,
14223
+ offset: 0
14224
+ },
14225
+ MB: {
14226
+ dimension: "information",
14227
+ factor: 1e6,
14228
+ offset: 0
14229
+ },
14230
+ GB: {
14231
+ dimension: "information",
14232
+ factor: 1e9,
14233
+ offset: 0
14234
+ },
14235
+ "°": {
14236
+ dimension: "angle",
14237
+ factor: 1,
14238
+ offset: 0
14239
+ },
14240
+ dBFS: {
14241
+ dimension: "sound-level",
14242
+ factor: 1,
14243
+ offset: 0
14244
+ },
14245
+ dBm: {
14246
+ dimension: "signal-level",
14247
+ factor: 1,
14248
+ offset: 0
14249
+ },
14250
+ mired: {
14251
+ dimension: "colour-temperature",
14252
+ factor: 1,
14253
+ offset: 0
14254
+ },
14255
+ kbps: {
14256
+ dimension: "bitrate",
14257
+ factor: 1,
14258
+ offset: 0
14259
+ },
14260
+ Mbps: {
14261
+ dimension: "bitrate",
14262
+ factor: 1e3,
14263
+ offset: 0
14264
+ },
14265
+ fps: {
14266
+ dimension: "rate",
14267
+ factor: 1,
14268
+ offset: 0
14269
+ }
14270
+ };
14271
+ /** Resolve a unit's `UnitSpec`, or `undefined` when the spelling is unknown. */
14272
+ function specFor(unit) {
14273
+ return Object.hasOwn(UNIT_TABLE, unit) ? UNIT_TABLE[unit] : void 0;
14274
+ }
14275
+ /** Value in `spec`'s unit → its dimension's canonical unit. */
14276
+ function toCanonical(value, spec) {
14277
+ return value * spec.factor + spec.offset;
14278
+ }
14279
+ /** Canonical-unit value → the unit described by `spec`. */
14280
+ function fromCanonical(canonical, spec) {
14281
+ return (canonical - spec.offset) / spec.factor;
14282
+ }
14283
+ /**
14284
+ * Graceful variant for the render path: returns the converted value, or
14285
+ * `undefined` when the conversion is refused (unknown unit / cross-dimension).
14286
+ * Never throws — a render caller falls back to the raw value + source unit.
14287
+ */
14288
+ function tryConvertUnit(value, from, to) {
14289
+ if (from === to) return value;
14290
+ const fromSpec = specFor(from);
14291
+ if (!fromSpec) return void 0;
14292
+ const toSpec = specFor(to);
14293
+ if (!toSpec) return void 0;
14294
+ if (fromSpec.dimension !== toSpec.dimension) return void 0;
14295
+ return fromCanonical(toCanonical(value, fromSpec), toSpec);
14296
+ }
14297
+ //#endregion
14027
14298
  //#region ../types/dist/index.mjs
14028
14299
  var SMTP_INBOUND_DEFAULT_PORT = 2525;
14029
14300
  var SMTP_INBOUND_DEFAULT_DOMAIN = "camstack.local";
@@ -15091,43 +15362,44 @@ function notificationTargetParts(records, spec) {
15091
15362
  const defined = spec.kinds[notificationTargetKindName(descriptor.kind)];
15092
15363
  if (defined === void 0) return [];
15093
15364
  const canTest = spec.probe !== void 0 && spec.canProbe(descriptor.kind);
15094
- const instances = new RecordIntegrationInstances(records, defined, {
15095
- description: {
15096
- role: "services",
15097
- instanceMode: "multiple",
15098
- kind: "target",
15099
- canTest
15100
- },
15101
- formSchema: () => formOf(descriptor),
15102
- initialBody: (name) => ({ name }),
15103
- mintId: () => spec.mintId(),
15104
- configured: () => true,
15105
- validateWrite: ({ body }) => spec.validate?.({
15106
- kind: descriptor.kind,
15107
- config: body
15108
- }),
15109
- test: async ({ id, values }) => {
15110
- if (spec.probe === void 0 || !canTest) return {
15111
- success: false,
15112
- status: "unsupported",
15113
- error: "This kind cannot be tested"
15114
- };
15115
- return probeToTestResult(await spec.probe({
15116
- kind: descriptor.kind,
15117
- config: values,
15118
- targetId: id ?? void 0
15119
- }));
15120
- }
15121
- });
15365
+ const kind = {
15366
+ id: descriptor.kind,
15367
+ label: descriptor.label,
15368
+ functionCap: NOTIFICATION_OUTPUT_CAP,
15369
+ role: "services",
15370
+ canTest
15371
+ };
15122
15372
  return [{
15123
- kind: {
15124
- id: descriptor.kind,
15125
- label: descriptor.label,
15126
- functionCap: NOTIFICATION_OUTPUT_CAP,
15127
- role: "services",
15128
- canTest
15129
- },
15130
- provider: instances.toProvider()
15373
+ kind,
15374
+ provider: new RecordIntegrationInstances(records, defined, {
15375
+ description: {
15376
+ role: "services",
15377
+ instanceMode: "multiple",
15378
+ kind: "target",
15379
+ canTest,
15380
+ kinds: [kind]
15381
+ },
15382
+ formSchema: () => formOf(descriptor),
15383
+ initialBody: (name) => ({ name }),
15384
+ mintId: () => spec.mintId(),
15385
+ configured: () => true,
15386
+ validateWrite: ({ body }) => spec.validate?.({
15387
+ kind: descriptor.kind,
15388
+ config: body
15389
+ }),
15390
+ test: async ({ id, values }) => {
15391
+ if (spec.probe === void 0 || !canTest) return {
15392
+ success: false,
15393
+ status: "unsupported",
15394
+ error: "This kind cannot be tested"
15395
+ };
15396
+ return probeToTestResult(await spec.probe({
15397
+ kind: descriptor.kind,
15398
+ config: values,
15399
+ targetId: id ?? void 0
15400
+ }));
15401
+ }
15402
+ }).toProvider()
15131
15403
  }];
15132
15404
  });
15133
15405
  }
@@ -43488,6 +43760,7 @@ var ExpressionBindingSourceSchema = union([
43488
43760
  object({
43489
43761
  kind: literal("global"),
43490
43762
  sourceStableId: string(),
43763
+ addonId: string().optional(),
43491
43764
  cap: string(),
43492
43765
  fieldPath: string()
43493
43766
  })
@@ -43614,385 +43887,6 @@ function extractSourceInfoFromMetadata(metadata) {
43614
43887
  return parsed.data;
43615
43888
  }
43616
43889
  /**
43617
- * Frozen unit → spec table. `•` in the doc marks each dimension's canonical
43618
- * unit (`factor: 1, offset: 0`). °F derives from `°C = (°F − 32) × 5/9` ⇒
43619
- * `factor = 5/9`, `offset = -160/9` (kept as exact expressions, not rounded
43620
- * decimals). K derives from `°C = K − 273.15`.
43621
- */
43622
- var UNIT_TABLE = {
43623
- "°C": {
43624
- dimension: "temperature",
43625
- factor: 1,
43626
- offset: 0
43627
- },
43628
- "°F": {
43629
- dimension: "temperature",
43630
- factor: 5 / 9,
43631
- offset: -160 / 9
43632
- },
43633
- K: {
43634
- dimension: "temperature",
43635
- factor: 1,
43636
- offset: -273.15
43637
- },
43638
- hPa: {
43639
- dimension: "pressure",
43640
- factor: 1,
43641
- offset: 0
43642
- },
43643
- kPa: {
43644
- dimension: "pressure",
43645
- factor: 10,
43646
- offset: 0
43647
- },
43648
- Pa: {
43649
- dimension: "pressure",
43650
- factor: .01,
43651
- offset: 0
43652
- },
43653
- mbar: {
43654
- dimension: "pressure",
43655
- factor: 1,
43656
- offset: 0
43657
- },
43658
- bar: {
43659
- dimension: "pressure",
43660
- factor: 1e3,
43661
- offset: 0
43662
- },
43663
- inHg: {
43664
- dimension: "pressure",
43665
- factor: 33.8639,
43666
- offset: 0
43667
- },
43668
- mmHg: {
43669
- dimension: "pressure",
43670
- factor: 1.33322,
43671
- offset: 0
43672
- },
43673
- psi: {
43674
- dimension: "pressure",
43675
- factor: 68.9476,
43676
- offset: 0
43677
- },
43678
- "m/s": {
43679
- dimension: "speed",
43680
- factor: 1,
43681
- offset: 0
43682
- },
43683
- "km/h": {
43684
- dimension: "speed",
43685
- factor: 1 / 3.6,
43686
- offset: 0
43687
- },
43688
- mph: {
43689
- dimension: "speed",
43690
- factor: .44704,
43691
- offset: 0
43692
- },
43693
- kn: {
43694
- dimension: "speed",
43695
- factor: .514444,
43696
- offset: 0
43697
- },
43698
- "mm/h": {
43699
- dimension: "precipitation-rate",
43700
- factor: 1,
43701
- offset: 0
43702
- },
43703
- "in/h": {
43704
- dimension: "precipitation-rate",
43705
- factor: 25.4,
43706
- offset: 0
43707
- },
43708
- m: {
43709
- dimension: "length",
43710
- factor: 1,
43711
- offset: 0
43712
- },
43713
- mm: {
43714
- dimension: "length",
43715
- factor: .001,
43716
- offset: 0
43717
- },
43718
- cm: {
43719
- dimension: "length",
43720
- factor: .01,
43721
- offset: 0
43722
- },
43723
- km: {
43724
- dimension: "length",
43725
- factor: 1e3,
43726
- offset: 0
43727
- },
43728
- in: {
43729
- dimension: "length",
43730
- factor: .0254,
43731
- offset: 0
43732
- },
43733
- ft: {
43734
- dimension: "length",
43735
- factor: .3048,
43736
- offset: 0
43737
- },
43738
- mi: {
43739
- dimension: "length",
43740
- factor: 1609.344,
43741
- offset: 0
43742
- },
43743
- "m²": {
43744
- dimension: "area",
43745
- factor: 1,
43746
- offset: 0
43747
- },
43748
- lx: {
43749
- dimension: "illuminance",
43750
- factor: 1,
43751
- offset: 0
43752
- },
43753
- "W/m²": {
43754
- dimension: "irradiance",
43755
- factor: 1,
43756
- offset: 0
43757
- },
43758
- W: {
43759
- dimension: "power",
43760
- factor: 1,
43761
- offset: 0
43762
- },
43763
- kW: {
43764
- dimension: "power",
43765
- factor: 1e3,
43766
- offset: 0
43767
- },
43768
- VA: {
43769
- dimension: "apparent-power",
43770
- factor: 1,
43771
- offset: 0
43772
- },
43773
- Wh: {
43774
- dimension: "energy",
43775
- factor: 1,
43776
- offset: 0
43777
- },
43778
- kWh: {
43779
- dimension: "energy",
43780
- factor: 1e3,
43781
- offset: 0
43782
- },
43783
- MWh: {
43784
- dimension: "energy",
43785
- factor: 1e6,
43786
- offset: 0
43787
- },
43788
- V: {
43789
- dimension: "voltage",
43790
- factor: 1,
43791
- offset: 0
43792
- },
43793
- mV: {
43794
- dimension: "voltage",
43795
- factor: .001,
43796
- offset: 0
43797
- },
43798
- A: {
43799
- dimension: "current",
43800
- factor: 1,
43801
- offset: 0
43802
- },
43803
- mA: {
43804
- dimension: "current",
43805
- factor: .001,
43806
- offset: 0
43807
- },
43808
- ppm: {
43809
- dimension: "concentration-volume",
43810
- factor: 1,
43811
- offset: 0
43812
- },
43813
- ppb: {
43814
- dimension: "concentration-volume",
43815
- factor: .001,
43816
- offset: 0
43817
- },
43818
- "µg/m³": {
43819
- dimension: "concentration-mass",
43820
- factor: 1,
43821
- offset: 0
43822
- },
43823
- "mg/m³": {
43824
- dimension: "concentration-mass",
43825
- factor: 1e3,
43826
- offset: 0
43827
- },
43828
- kg: {
43829
- dimension: "mass",
43830
- factor: 1,
43831
- offset: 0
43832
- },
43833
- g: {
43834
- dimension: "mass",
43835
- factor: .001,
43836
- offset: 0
43837
- },
43838
- lb: {
43839
- dimension: "mass",
43840
- factor: .453592,
43841
- offset: 0
43842
- },
43843
- oz: {
43844
- dimension: "mass",
43845
- factor: .0283495,
43846
- offset: 0
43847
- },
43848
- "%": {
43849
- dimension: "percentage",
43850
- factor: 1,
43851
- offset: 0
43852
- },
43853
- L: {
43854
- dimension: "volume",
43855
- factor: 1,
43856
- offset: 0
43857
- },
43858
- mL: {
43859
- dimension: "volume",
43860
- factor: .001,
43861
- offset: 0
43862
- },
43863
- gal: {
43864
- dimension: "volume",
43865
- factor: 3.785411784,
43866
- offset: 0
43867
- },
43868
- s: {
43869
- dimension: "duration",
43870
- factor: 1,
43871
- offset: 0
43872
- },
43873
- ms: {
43874
- dimension: "duration",
43875
- factor: .001,
43876
- offset: 0
43877
- },
43878
- min: {
43879
- dimension: "duration",
43880
- factor: 60,
43881
- offset: 0
43882
- },
43883
- h: {
43884
- dimension: "duration",
43885
- factor: 3600,
43886
- offset: 0
43887
- },
43888
- d: {
43889
- dimension: "duration",
43890
- factor: 86400,
43891
- offset: 0
43892
- },
43893
- B: {
43894
- dimension: "information",
43895
- factor: 1,
43896
- offset: 0
43897
- },
43898
- KiB: {
43899
- dimension: "information",
43900
- factor: 1024,
43901
- offset: 0
43902
- },
43903
- MiB: {
43904
- dimension: "information",
43905
- factor: 1024 ** 2,
43906
- offset: 0
43907
- },
43908
- GiB: {
43909
- dimension: "information",
43910
- factor: 1024 ** 3,
43911
- offset: 0
43912
- },
43913
- TiB: {
43914
- dimension: "information",
43915
- factor: 1024 ** 4,
43916
- offset: 0
43917
- },
43918
- kB: {
43919
- dimension: "information",
43920
- factor: 1e3,
43921
- offset: 0
43922
- },
43923
- MB: {
43924
- dimension: "information",
43925
- factor: 1e6,
43926
- offset: 0
43927
- },
43928
- GB: {
43929
- dimension: "information",
43930
- factor: 1e9,
43931
- offset: 0
43932
- },
43933
- "°": {
43934
- dimension: "angle",
43935
- factor: 1,
43936
- offset: 0
43937
- },
43938
- dBFS: {
43939
- dimension: "sound-level",
43940
- factor: 1,
43941
- offset: 0
43942
- },
43943
- dBm: {
43944
- dimension: "signal-level",
43945
- factor: 1,
43946
- offset: 0
43947
- },
43948
- mired: {
43949
- dimension: "colour-temperature",
43950
- factor: 1,
43951
- offset: 0
43952
- },
43953
- kbps: {
43954
- dimension: "bitrate",
43955
- factor: 1,
43956
- offset: 0
43957
- },
43958
- Mbps: {
43959
- dimension: "bitrate",
43960
- factor: 1e3,
43961
- offset: 0
43962
- },
43963
- fps: {
43964
- dimension: "rate",
43965
- factor: 1,
43966
- offset: 0
43967
- }
43968
- };
43969
- /** Resolve a unit's `UnitSpec`, or `undefined` when the spelling is unknown. */
43970
- function specFor(unit) {
43971
- return Object.hasOwn(UNIT_TABLE, unit) ? UNIT_TABLE[unit] : void 0;
43972
- }
43973
- /** Value in `spec`'s unit → its dimension's canonical unit. */
43974
- function toCanonical(value, spec) {
43975
- return value * spec.factor + spec.offset;
43976
- }
43977
- /** Canonical-unit value → the unit described by `spec`. */
43978
- function fromCanonical(canonical, spec) {
43979
- return (canonical - spec.offset) / spec.factor;
43980
- }
43981
- /**
43982
- * Graceful variant for the render path: returns the converted value, or
43983
- * `undefined` when the conversion is refused (unknown unit / cross-dimension).
43984
- * Never throws — a render caller falls back to the raw value + source unit.
43985
- */
43986
- function tryConvertUnit(value, from, to) {
43987
- if (from === to) return value;
43988
- const fromSpec = specFor(from);
43989
- if (!fromSpec) return void 0;
43990
- const toSpec = specFor(to);
43991
- if (!toSpec) return void 0;
43992
- if (fromSpec.dimension !== toSpec.dimension) return void 0;
43993
- return fromCanonical(toCanonical(value, fromSpec), toSpec);
43994
- }
43995
- /**
43996
43890
  * A number a person types for a physical quantity — the input side of the
43997
43891
  * value + unit contract (D972).
43998
43892
  *
@@ -45690,26 +45584,23 @@ var SettingsSchemaWithValuesSchema = object({
45690
45584
  var SettingsPatchSchema = record(string(), unknown());
45691
45585
  /** The scope of a scoped-settings call (D986): where the capability declared the values. */
45692
45586
  var ScopedSettingsScopeSchema = _enum(SCOPED_SETTINGS_SCOPES);
45587
+ /** One scoped schema an addon serves: its capability, scope and section ids. */
45588
+ var ScopedSettingsDeclarationSchema = object({
45589
+ cap: string(),
45590
+ scope: ScopedSettingsScopeSchema,
45591
+ sectionIds: array(string())
45592
+ });
45693
45593
  /** Standard success response for update operations. */
45694
45594
  var SettingsUpdateResultSchema = object({ success: literal(true) });
45695
45595
  method(object({
45696
- addonId: string(),
45697
- nodeId: string().optional(),
45698
- overlay: record(string(), unknown()).optional(),
45699
- cap: string().optional()
45700
- }), SettingsSchemaWithValuesSchema.nullable()), method(object({
45701
- addonId: string(),
45702
- nodeId: string().optional(),
45703
- patch: SettingsPatchSchema
45704
- }), SettingsUpdateResultSchema, {
45705
- kind: "mutation",
45706
- auth: "admin"
45707
- }), method(object({
45708
45596
  addonId: string(),
45709
45597
  cap: string(),
45710
45598
  scope: ScopedSettingsScopeSchema,
45711
45599
  nodeId: string().optional()
45712
45600
  }), SettingsSchemaWithValuesSchema.nullable()), method(object({
45601
+ addonId: string(),
45602
+ nodeId: string().optional()
45603
+ }), array(ScopedSettingsDeclarationSchema).nullable()), method(object({
45713
45604
  addonId: string(),
45714
45605
  cap: string(),
45715
45606
  scope: ScopedSettingsScopeSchema,
@@ -45724,9 +45615,6 @@ method(object({
45724
45615
  scope: ScopedSettingsScopeSchema,
45725
45616
  nodeId: string().optional()
45726
45617
  }), record(string(), unknown()).nullable()), method(object({
45727
- addonId: string(),
45728
- nodeId: string().optional()
45729
- }), SettingsSchemaWithValuesSchema.nullable()), method(object({
45730
45618
  addonId: string(),
45731
45619
  nodeId: string().optional(),
45732
45620
  instance: IntegrationInstanceRefSchema
@@ -47289,7 +47177,7 @@ var BackupDestinationInfoSchema = object({
47289
47177
  id: string(),
47290
47178
  /**
47291
47179
  * Manifest id of the owning addon. The Settings button on the
47292
- * destination card opens that addon's `globalSettingsSchema` panel.
47180
+ * destination card opens that addon's scoped settings panel.
47293
47181
  */
47294
47182
  addonId: string(),
47295
47183
  /** Operator-toggled enable flag from the destination-policy table. */
@@ -50748,7 +50636,7 @@ var mediaPlayerCapability = {
50748
50636
  isMuted: { kind: "boolean" }
50749
50637
  } }
50750
50638
  };
50751
- 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;
50639
+ 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, DeviceType.MediaPlayer;
50752
50640
  var NUMBER = {
50753
50641
  kind: "fixed",
50754
50642
  result: ["number"]
@@ -52766,6 +52654,7 @@ method(InstancePinSchema, DeviceExportStatusSchema), method(_void(), array(Expor
52766
52654
  kind: "mutation",
52767
52655
  auth: "admin"
52768
52656
  });
52657
+ var DeviceAliasSchema = string().max(64).regex(/^[a-z][a-z0-9_]*$/);
52769
52658
  /**
52770
52659
  * A SITE ZONE — a named part of the property that several locations belong to.
52771
52660
  *
@@ -53252,6 +53141,12 @@ var DeviceInfoSchema = object({
53252
53141
  addonId: string(),
53253
53142
  type: _enum(DeviceType),
53254
53143
  name: string(),
53144
+ /** The name a value template uses for this device (`{{ stufa_veranda.temperature }}`),
53145
+ * entity_id style. Generated once from the name at first registration, unique
53146
+ * cluster-wide, editable through `setAlias`, NOT changed by a rename (D1021).
53147
+ * Optional: a record produced by an older build omits it until the boot
53148
+ * backfill has run. */
53149
+ alias: DeviceAliasSchema.optional(),
53255
53150
  /** Operator-organisational location label. `null` when unset. */
53256
53151
  location: string().nullable(),
53257
53152
  /** Soft-disabled flag. */
@@ -53571,6 +53466,25 @@ method(object({
53571
53466
  }), _void(), {
53572
53467
  kind: "mutation",
53573
53468
  auth: "admin"
53469
+ }), method(object({
53470
+ deviceId: number$1(),
53471
+ alias: string()
53472
+ }), discriminatedUnion("ok", [object({
53473
+ ok: literal(true),
53474
+ alias: DeviceAliasSchema
53475
+ }), object({
53476
+ ok: literal(false),
53477
+ reason: _enum([
53478
+ "empty",
53479
+ "too-long",
53480
+ "grammar",
53481
+ "reserved",
53482
+ "taken"
53483
+ ]),
53484
+ takenBy: number$1().optional()
53485
+ })]), {
53486
+ kind: "mutation",
53487
+ auth: "admin"
53574
53488
  }), method(object({
53575
53489
  deviceId: number$1(),
53576
53490
  location: string().nullable()
@@ -57376,10 +57290,11 @@ var IntegrationInstanceDescriptionSchema = object({
57376
57290
  /** True when `testInstance` really probes the remote. */
57377
57291
  canTest: boolean(),
57378
57292
  /**
57379
- * The kinds an instance can be of. Absent or one entry: nothing to choose; more than one: the
57380
- * Add flow asks which first and sends it as `createInstance.kind`.
57293
+ * The kinds an instance can be of (D1013 rule 1), each naming its `functionCap`. One entry:
57294
+ * nothing to choose; more than one: the Add flow asks which first and sends it as
57295
+ * `createInstance.kind`. Never empty: every instance belongs to exactly one kind.
57381
57296
  */
57382
- kinds: array(IntegrationInstanceKindSchema).optional(),
57297
+ kinds: array(IntegrationInstanceKindSchema).min(1),
57383
57298
  /**
57384
57299
  * The provider accepts `createInstance.id` for an id that once existed (an adopted id, because
57385
57300
  * devices carry it). The UI then offers "restore under its old id" on Add. Absent means no.
@@ -57415,8 +57330,8 @@ var IntegrationInstanceSchema = object({
57415
57330
  * the body is the one store, `setInstanceEnabled` the one writer (epic #440, step 3).
57416
57331
  */
57417
57332
  enabled: boolean().optional(),
57418
- /** The kind of this instance (one of `describe().kinds[].id`), when the provider has kinds. */
57419
- kind: string().optional(),
57333
+ /** The kind of this instance: exactly one of `describe().kinds[].id` (D1013 rule 2). */
57334
+ kind: string().min(1),
57420
57335
  /** Values for the instance form; a secret is a `SecretFieldView`, never text. */
57421
57336
  formValues: record(string(), unknown()),
57422
57337
  status: IntegrationInstanceStatusSchema.optional(),
@@ -67671,6 +67586,11 @@ var NcScheduleSchema = object({
67671
67586
  * as a STATE SUMMARY at the end of each window of `schedule`.
67672
67587
  */
67673
67588
  var NcSystemEventDeliveryModeSchema = _enum(["instant", "summary"]);
67589
+ _enum([
67590
+ "activation",
67591
+ "deactivation",
67592
+ "schedule"
67593
+ ]);
67674
67594
  /**
67675
67595
  * How a summary window is SENT (D698): `rule` (absent) = ONE notification with
67676
67596
  * every section; `section` = one notification per section with something to
@@ -67687,11 +67607,26 @@ var NcStateSummarySplitSchema = _enum(["rule", "section"]);
67687
67607
  * midnight crossing); the send time is each window's END. Refused on the
67688
67608
  * `alarm` family, which stays instant (`systemEventDeliveryProblems`).
67689
67609
  */
67690
- var NcSystemEventDeliverySchema = discriminatedUnion("mode", [object({ mode: literal("instant") }), object({
67691
- mode: literal("summary"),
67692
- schedule: NcScheduleSchema,
67693
- split: NcStateSummarySplitSchema.optional()
67694
- })]);
67610
+ var NcSystemEventDeliverySchema = discriminatedUnion("mode", [
67611
+ object({ mode: literal("instant") }),
67612
+ object({
67613
+ mode: literal("summary"),
67614
+ schedule: NcScheduleSchema,
67615
+ split: NcStateSummarySplitSchema.optional()
67616
+ }),
67617
+ object({
67618
+ mode: literal("modes"),
67619
+ /** The bad edge, and every kind that has a single edge. */
67620
+ onActivation: boolean(),
67621
+ /** The recovery edges of the classes of the selected kinds (derived, never stored per kind). */
67622
+ onDeactivation: boolean(),
67623
+ /** A state summary at the end of each window; absent = no summary. */
67624
+ onSchedule: object({
67625
+ schedule: NcScheduleSchema,
67626
+ grouping: NcStateSummarySplitSchema
67627
+ }).optional()
67628
+ })
67629
+ ]);
67695
67630
  /** Fuzzy plate matcher — OCR noise makes exact match useless (spec row 12/13). */
67696
67631
  var NcPlateMatcherSchema = object({
67697
67632
  /**
@@ -75765,6 +75700,16 @@ var RemoteLayoutComponentSchema = object({
75765
75700
  method(object({}), array(RemoteLayoutComponentSchema), { auth: "protected" });
75766
75701
  var RUNNING_POWER_START_DELAY_MS = 3e4;
75767
75702
  var RUNNING_POWER_STOP_DELAY_MS = 18e4;
75703
+ /**
75704
+ * Per-type overrides of the dwell defaults. A media player draws power the
75705
+ * moment it is on and stops drawing the moment it is off, so it needs neither
75706
+ * a long dwell nor a second sample. A type absent here keeps the global values.
75707
+ */
75708
+ var RUNNING_POWER_DEFAULTS_BY_TYPE = { [DeviceType.MediaPlayer]: {
75709
+ startDwellMs: 1e4,
75710
+ stopDwellMs: 1e4,
75711
+ minSamples: 1
75712
+ } };
75768
75713
  var RunningStateSourceSchema = _enum(["native", "assigned"]);
75769
75714
  var RunningStateStatusSchema = object({
75770
75715
  /** null = unknown — never coerced to false (D393). */
@@ -75782,93 +75727,27 @@ var RunningFlipEventDataSchema = object({
75782
75727
  phase: string().nullable()
75783
75728
  });
75784
75729
  var RUNNING_DEVICE_TYPES = Object.values(DeviceType);
75785
- /** Generic Customize target for the running boolean (D936). */
75786
- var runningCustomize = {
75787
- version: 1,
75788
- overridable: [{
75789
- path: "running",
75790
- by: "running"
75791
- }],
75792
- targets: [{
75793
- id: "running",
75794
- cardinality: "scalar",
75795
- vocabulary: {
75796
- cap: "running-state",
75797
- stateField: "running"
75798
- },
75799
- deviceTypes: [
75800
- DeviceType.Washer,
75801
- DeviceType.Dryer,
75802
- DeviceType.Dishwasher,
75803
- DeviceType.Vacuum,
75804
- DeviceType.LawnMower,
75805
- DeviceType.MediaPlayer,
75806
- DeviceType.WaterHeater,
75807
- DeviceType.Humidifier,
75808
- DeviceType.Fan,
75809
- DeviceType.Climate,
75810
- DeviceType.Thermostat,
75811
- DeviceType.PetFeeder,
75812
- DeviceType.Heater
75813
- ],
75814
- apply: "add",
75815
- path: "running",
75816
- sources: [
75817
- {
75818
- kind: "switch",
75819
- transforms: [{ kind: "identity" }]
75820
- },
75821
- {
75822
- kind: "binary",
75823
- transforms: [{ kind: "identity" }, { kind: "invert" }]
75824
- },
75825
- {
75826
- kind: "numeric",
75827
- requiredUnit: "W",
75828
- transforms: [{
75829
- kind: "threshold",
75830
- comparisons: ["gte", "lte"],
75831
- comparison: "gte",
75832
- defaultThreshold: 5,
75833
- defaultStartDwellMs: RUNNING_POWER_START_DELAY_MS,
75834
- defaultStopDwellMs: RUNNING_POWER_STOP_DELAY_MS,
75835
- thresholdUnit: "W"
75836
- }]
75837
- }
75838
- ],
75839
- companions: [
75840
- {
75841
- kind: "constant",
75842
- path: "source",
75843
- value: "assigned",
75844
- when: "always"
75845
- },
75846
- {
75847
- kind: "constant",
75848
- path: "phase",
75849
- value: null,
75850
- when: "add"
75851
- },
75852
- {
75853
- kind: "clock",
75854
- path: "lastFetchedAt",
75855
- when: "always"
75856
- },
75857
- {
75858
- kind: "transition-time",
75859
- path: "since",
75860
- sourceChangedAt: "prefer"
75861
- }
75862
- ],
75863
- sourceReport: {
75864
- status: "assignment-status",
75865
- runtimeField: "source",
75866
- nativeValue: "native",
75867
- assignedValue: "assigned"
75868
- },
75869
- advancedExpression: true
75870
- }]
75871
- };
75730
+ /**
75731
+ * Customize offers "running" only where a job makes sense (D933): a cycle,
75732
+ * a clean, a mow, playback, or another appliance that is on and working.
75733
+ * The cap itself stays on every type — a camera can still report running
75734
+ * natively; it just has no card.
75735
+ */
75736
+ var RUNNING_CUSTOMIZE_DEVICE_TYPES = [
75737
+ DeviceType.Washer,
75738
+ DeviceType.Dryer,
75739
+ DeviceType.Dishwasher,
75740
+ DeviceType.Vacuum,
75741
+ DeviceType.LawnMower,
75742
+ DeviceType.MediaPlayer,
75743
+ DeviceType.WaterHeater,
75744
+ DeviceType.Humidifier,
75745
+ DeviceType.Fan,
75746
+ DeviceType.Climate,
75747
+ DeviceType.Thermostat,
75748
+ DeviceType.PetFeeder,
75749
+ DeviceType.Heater
75750
+ ];
75872
75751
  var runningStateCapability = {
75873
75752
  name: "running-state",
75874
75753
  scope: "device",
@@ -75888,7 +75767,79 @@ var runningStateCapability = {
75888
75767
  durability: "restored",
75889
75768
  history: { fields: { running: { kind: "boolean" } } },
75890
75769
  volatileStateFields: ["lastFetchedAt"],
75891
- customize: runningCustomize
75770
+ customize: {
75771
+ version: 1,
75772
+ overridable: [{
75773
+ path: "running",
75774
+ by: "running"
75775
+ }],
75776
+ targets: [{
75777
+ id: "running",
75778
+ cardinality: "scalar",
75779
+ vocabulary: {
75780
+ cap: "running-state",
75781
+ stateField: "running"
75782
+ },
75783
+ deviceTypes: RUNNING_CUSTOMIZE_DEVICE_TYPES,
75784
+ apply: "add",
75785
+ path: "running",
75786
+ sources: [
75787
+ {
75788
+ kind: "switch",
75789
+ transforms: [{ kind: "identity" }]
75790
+ },
75791
+ {
75792
+ kind: "binary",
75793
+ transforms: [{ kind: "identity" }, { kind: "invert" }]
75794
+ },
75795
+ {
75796
+ kind: "numeric",
75797
+ requiredUnit: "W",
75798
+ transforms: [{
75799
+ kind: "threshold",
75800
+ comparisons: ["gte", "lte"],
75801
+ comparison: "gte",
75802
+ defaultThreshold: 5,
75803
+ defaultStartDwellMs: RUNNING_POWER_START_DELAY_MS,
75804
+ defaultStopDwellMs: RUNNING_POWER_STOP_DELAY_MS,
75805
+ deviceTypeDefaults: RUNNING_POWER_DEFAULTS_BY_TYPE,
75806
+ thresholdUnit: "W"
75807
+ }]
75808
+ }
75809
+ ],
75810
+ companions: [
75811
+ {
75812
+ kind: "constant",
75813
+ path: "source",
75814
+ value: "assigned",
75815
+ when: "always"
75816
+ },
75817
+ {
75818
+ kind: "constant",
75819
+ path: "phase",
75820
+ value: null,
75821
+ when: "add"
75822
+ },
75823
+ {
75824
+ kind: "clock",
75825
+ path: "lastFetchedAt",
75826
+ when: "always"
75827
+ },
75828
+ {
75829
+ kind: "transition-time",
75830
+ path: "since",
75831
+ sourceChangedAt: "prefer"
75832
+ }
75833
+ ],
75834
+ sourceReport: {
75835
+ status: "assignment-status",
75836
+ runtimeField: "source",
75837
+ nativeValue: "native",
75838
+ assignedValue: "assigned"
75839
+ },
75840
+ advancedExpression: true
75841
+ }]
75842
+ }
75892
75843
  };
75893
75844
  /**
75894
75845
  * scene-monitor — device-scoped reference-region state cap. An operator marks
@@ -78822,6 +78773,11 @@ var TimedRunUnitSchema = _enum(["s", "min"]);
78822
78773
  _enum(["switch", "pet-fountain"]);
78823
78774
  DeviceType.Heater, DeviceType.PetFountain;
78824
78775
  var TIMED_RUN_DEVICE_TYPES = [DeviceType.Heater, DeviceType.PetFountain];
78776
+ /** One duration, as the one-tap run asks for it. */
78777
+ var TimedRunQuickRunSchema = object({
78778
+ duration: number$1().positive().finite(),
78779
+ unit: TimedRunUnitSchema
78780
+ });
78825
78781
  /** Why a request was not carried out. Typed, so a caller can say it in words. */
78826
78782
  var TimedRunRefusalSchema = _enum([
78827
78783
  "unsupported-device",
@@ -78842,8 +78798,23 @@ var TimedRunStatusSchema = object({
78842
78798
  activeUntil: number$1().nullable(),
78843
78799
  /** Who asked for the run (a user id or an addon id); `null` when nothing is armed or it did not say. */
78844
78800
  requestedBy: string().nullable(),
78801
+ /**
78802
+ * The one-tap run of THIS device: what a "Run" button on any surface starts. `null` when
78803
+ * the device is gone or its type has no timed run; a status from before this field
78804
+ * existed has none, and a surface falls back to the type's default.
78805
+ */
78806
+ quickRun: TimedRunQuickRunSchema.nullable(),
78845
78807
  lastFetchedAt: number$1()
78846
78808
  });
78809
+ /** What the settings form needs: the unit, the bounds and the type's default. */
78810
+ var TimedRunOptionsSchema = object({
78811
+ unit: TimedRunUnitSchema,
78812
+ /** The longest run, in `unit`. */
78813
+ maxDuration: number$1(),
78814
+ defaultDuration: number$1()
78815
+ });
78816
+ /** The one-tap run duration, in the type's unit; `null` returns to the type's default. */
78817
+ var TimedRunSettingsPatchSchema = object({ quickRunDuration: number$1().positive().finite().nullable().optional() });
78847
78818
  var timedRunCapability = {
78848
78819
  name: "timed-run",
78849
78820
  scope: "device",
@@ -78851,7 +78822,24 @@ var timedRunCapability = {
78851
78822
  kind: "wrapper",
78852
78823
  defaultActive: true,
78853
78824
  deviceTypes: TIMED_RUN_DEVICE_TYPES,
78825
+ deviceConfig: { ui: {
78826
+ kind: "derived-form",
78827
+ builderId: "timed-run",
78828
+ tab: "general"
78829
+ } },
78854
78830
  methods: {
78831
+ getOptions: method(object({ deviceId: number$1().int() }), TimedRunOptionsSchema),
78832
+ /**
78833
+ * The one-tap run of this device. A duration above the type's maximum is
78834
+ * REFUSED (thrown), like `runFor`: the maximum is a safety bound.
78835
+ */
78836
+ setSettings: method(object({
78837
+ deviceId: number$1().int(),
78838
+ settings: TimedRunSettingsPatchSchema
78839
+ }), _void(), {
78840
+ kind: "mutation",
78841
+ auth: "admin"
78842
+ }),
78855
78843
  /**
78856
78844
  * Turn the device on and arm its off. A run already armed is replaced (the
78857
78845
  * new deadline wins). Refuses with a typed reason above the type's maximum.
@@ -83552,13 +83540,6 @@ Object.freeze({
83552
83540
  access: "view",
83553
83541
  role: "admin"
83554
83542
  },
83555
- "addonSettings.getGlobalSettings": {
83556
- capName: "addon-settings",
83557
- capScope: "system",
83558
- addonId: null,
83559
- access: "view",
83560
- role: "admin"
83561
- },
83562
83543
  "addonSettings.getIntegrationFormSchema": {
83563
83544
  capName: "addon-settings",
83564
83545
  capScope: "system",
@@ -83566,14 +83547,14 @@ Object.freeze({
83566
83547
  access: "view",
83567
83548
  role: "admin"
83568
83549
  },
83569
- "addonSettings.getIntegrationSettings": {
83550
+ "addonSettings.getScopedSettings": {
83570
83551
  capName: "addon-settings",
83571
83552
  capScope: "system",
83572
83553
  addonId: null,
83573
83554
  access: "view",
83574
83555
  role: "admin"
83575
83556
  },
83576
- "addonSettings.getScopedSettings": {
83557
+ "addonSettings.listScopedSettings": {
83577
83558
  capName: "addon-settings",
83578
83559
  capScope: "system",
83579
83560
  addonId: null,
@@ -83601,13 +83582,6 @@ Object.freeze({
83601
83582
  access: "create",
83602
83583
  role: "admin"
83603
83584
  },
83604
- "addonSettings.updateGlobalSettings": {
83605
- capName: "addon-settings",
83606
- capScope: "system",
83607
- addonId: null,
83608
- access: "create",
83609
- role: "admin"
83610
- },
83611
83585
  "addonSettings.updateScopedSettings": {
83612
83586
  capName: "addon-settings",
83613
83587
  capScope: "system",
@@ -85778,6 +85752,13 @@ Object.freeze({
85778
85752
  access: "create",
85779
85753
  role: "admin"
85780
85754
  },
85755
+ "deviceManager.setAlias": {
85756
+ capName: "device-manager",
85757
+ capScope: "system",
85758
+ addonId: null,
85759
+ access: "create",
85760
+ role: "admin"
85761
+ },
85781
85762
  "deviceManager.setChildLayout": {
85782
85763
  capName: "device-manager",
85783
85764
  capScope: "system",
@@ -92561,6 +92542,13 @@ Object.freeze({
92561
92542
  access: "create",
92562
92543
  role: "viewer"
92563
92544
  },
92545
+ "timedRun.getOptions": {
92546
+ capName: "timed-run",
92547
+ capScope: "device",
92548
+ addonId: null,
92549
+ access: "view",
92550
+ role: "viewer"
92551
+ },
92564
92552
  "timedRun.getStatus": {
92565
92553
  capName: "timed-run",
92566
92554
  capScope: "device",
@@ -92575,6 +92563,13 @@ Object.freeze({
92575
92563
  access: "create",
92576
92564
  role: "viewer"
92577
92565
  },
92566
+ "timedRun.setSettings": {
92567
+ capName: "timed-run",
92568
+ capScope: "device",
92569
+ addonId: null,
92570
+ access: "create",
92571
+ role: "admin"
92572
+ },
92578
92573
  "toast.onToast": {
92579
92574
  capName: "toast",
92580
92575
  capScope: "system",
@@ -93505,6 +93500,7 @@ function offered(received) {
93505
93500
  };
93506
93501
  }
93507
93502
  offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
93503
+ string().min(1).max(4096);
93508
93504
  Object.freeze({
93509
93505
  "accessories.getStatus": [{
93510
93506
  name: "deviceId",
@@ -94188,6 +94184,11 @@ Object.freeze({
94188
94184
  form: "single",
94189
94185
  optional: false
94190
94186
  }],
94187
+ "deviceManager.setAlias": [{
94188
+ name: "deviceId",
94189
+ form: "single",
94190
+ optional: false
94191
+ }],
94191
94192
  "deviceManager.setChildLayout": [{
94192
94193
  name: "deviceId",
94193
94194
  form: "single",
@@ -96252,6 +96253,11 @@ Object.freeze({
96252
96253
  form: "single",
96253
96254
  optional: false
96254
96255
  }],
96256
+ "timedRun.getOptions": [{
96257
+ name: "deviceId",
96258
+ form: "single",
96259
+ optional: false
96260
+ }],
96255
96261
  "timedRun.getStatus": [{
96256
96262
  name: "deviceId",
96257
96263
  form: "single",
@@ -96262,6 +96268,11 @@ Object.freeze({
96262
96268
  form: "single",
96263
96269
  optional: false
96264
96270
  }],
96271
+ "timedRun.setSettings": [{
96272
+ name: "deviceId",
96273
+ form: "single",
96274
+ optional: false
96275
+ }],
96265
96276
  "update.getStatus": [{
96266
96277
  name: "deviceId",
96267
96278
  form: "single",
@@ -97331,6 +97342,7 @@ var STATE_SUMMARY_VARS = [
97331
97342
  [...RULE_TEXT],
97332
97343
  [...RULE_TEXT],
97333
97344
  [...RULE_TEXT],
97345
+ [...RULE_TEXT],
97334
97346
  [...RULE_BODY_ONLY],
97335
97347
  [...RULE_TEXT],
97336
97348
  ([...RULE_TEXT], [...DEVICE_EVENTS]),
@@ -145194,7 +145206,14 @@ function buildAlexaAccountInstances(records, deps) {
145194
145206
  instanceMode: "multiple",
145195
145207
  kind: "account",
145196
145208
  canTest: true,
145197
- allowRestoreId: true
145209
+ allowRestoreId: true,
145210
+ kinds: [{
145211
+ id: ALEXA_ACCOUNT_KIND,
145212
+ label: "Alexa account",
145213
+ functionCap: "broker",
145214
+ role: "services",
145215
+ brokerKind: ALEXA_BROKER_KIND
145216
+ }]
145198
145217
  },
145199
145218
  formSchema: () => buildConnectionFormSchema(),
145200
145219
  initialBody: (name) => ({
@@ -145248,7 +145267,13 @@ function buildAlexaInstances(records) {
145248
145267
  role: "devices",
145249
145268
  instanceMode: "multiple",
145250
145269
  kind: "connection",
145251
- canTest: false
145270
+ canTest: false,
145271
+ kinds: [{
145272
+ id: "integration",
145273
+ label: "Alexa",
145274
+ functionCap: "device-provider",
145275
+ role: "devices"
145276
+ }]
145252
145277
  },
145253
145278
  labels: { brokerId: { label: "Alexa account" } }
145254
145279
  });