@camstack/addon-provider-homeassistant 1.2.661 → 1.2.662
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 +6 -0
- package/dist/addon.js +2 -2
- package/dist/addon.mjs +2 -2
- package/dist/ha-export/ha-export.addon.js +1 -1
- package/dist/ha-export/ha-export.addon.mjs +1 -1
- package/dist/{legacy-integration-rows-B_X4VL29.js → legacy-integration-rows-BaTsNMtt.js} +141 -22
- package/dist/{legacy-integration-rows-Bhvthv-s.mjs → legacy-integration-rows-uGrBo3a3.mjs} +141 -22
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.2.662] - 2026-11-05
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- The integration form is declared as the instance form; brokers and their stored values are unchanged.
|
|
15
|
+
|
|
10
16
|
## [1.2.661] - 2026-11-05
|
|
11
17
|
|
|
12
18
|
### Changed
|
package/dist/addon.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_legacy_integration_rows = require("./legacy-integration-rows-
|
|
2
|
+
const require_legacy_integration_rows = require("./legacy-integration-rows-BaTsNMtt.js");
|
|
3
3
|
let node_crypto = require("node:crypto");
|
|
4
4
|
let node_zlib = require("node:zlib");
|
|
5
5
|
//#region src/ha-admin-link.ts
|
|
@@ -9326,7 +9326,7 @@ var HaProviderAddon = class HaProviderAddon extends require_legacy_integration_r
|
|
|
9326
9326
|
eb.subscribe({ category: "integration.disabled" }, handler);
|
|
9327
9327
|
eb.subscribe({ category: "integration.deleted" }, handler);
|
|
9328
9328
|
}
|
|
9329
|
-
|
|
9329
|
+
async integrationFormSchema(_instance) {
|
|
9330
9330
|
return this.schema({ sections: [{
|
|
9331
9331
|
id: "ha-broker",
|
|
9332
9332
|
title: "Home Assistant broker",
|
package/dist/addon.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as fanControlCapability, $t as createEvent, A as bestLocationMatch, At as runningFromMediaPlayer, B as climateControlCapability, Bt as tryConvertDelta, C as accessoriesCapability, Ct as pressureSensorCapability, D as ambientLightSensorCapability, Dt as resolveAttachmentUrls, E as alarmPanelCapability, Et as quantityFromUnit, F as buildAddonRouteProvider, Ft as smokeCapability, G as contactCapability, Gt as vibrationCapability, H as connectivityCapability, Ht as updateCapability, I as buildDeviceAdminUrl, J as deviceAdminLinkCapability, K as controlCapability, Kt as waterHeaterCapability, L as buttonCapability, Lt as switchCapability, M as boardKeySourceCapability, Mt as runningStateCapability, N as brightnessCapability, Nt as scriptRunnerCapability, O as automationControlCapability, P as brokerCapability, Q as eventEmitterCapability, Qt as baseUnitOf, R as calendarSourceCapability, Rt as tamperCapability, S as TargetSchema, Sn as EventCategory, St as presenceCapability, T as airQualitySensorCapability, Tt as publicEndpointReaderFrom, Ut as vacuumControlCapability, V as colorCapability, Vt as tryConvertUnit, Wt as valveCapability, Xt as RecordSecretUnavailableError, Y as deviceAdoptionCapability, Z as enumSensorCapability, _n as url, at as humiditySensorCapability, b as RUNNING_STATE_COLD, bn as DeviceType, bt as preconditionOf, cn as boolean, ct as integrationInstancesCapability, dn as number, dt as mediaPlayerCapability, en as defineAddonMigrations, et as flattenNestedRecords, fn as object, ft as motionCapability, gn as unknown, gt as numericSensorCapability, hn as union, ht as notifierCapability, it as humidifierCapability, j as binaryCapability, jt as runningFromVacuumState, k as batteryCapability, kt as runningFromLawnMower, ln as discriminatedUnion, lt as lawnMowerControlCapability, m as EnumSensorDateTimeFormatSchema, mn as string, mt as notificationOutputCapability, n as HA_BROKER_KIND, nn as defineRecordKinds, nt as floodCapability, o as BaseDevice, on as _enum, ot as imageCapability, pn as record, pt as normalizeUnit, q as coverCapability, qt as weatherCapability, rn as entryBody, rt as gasCapability, s as BaseDeviceProvider, sn as array, st as ingestReading, t as haInstanceSettingsPortsOver, tn as defineEntryKind, tt as flattenPortsOver, un as literal, ut as lockControlCapability, vn as DeviceFeature, vt as parseAdminBase, w as addonRoutesCapability, x as RecordIntegrationInstances, xn as errMsg, xt as prepareNotification, yn as DeviceRole, yt as powerMeterCapability, z as carbonMonoxideCapability, zt as temperatureSensorCapability } from "./legacy-integration-rows-
|
|
1
|
+
import { $ as fanControlCapability, $t as createEvent, A as bestLocationMatch, At as runningFromMediaPlayer, B as climateControlCapability, Bt as tryConvertDelta, C as accessoriesCapability, Ct as pressureSensorCapability, D as ambientLightSensorCapability, Dt as resolveAttachmentUrls, E as alarmPanelCapability, Et as quantityFromUnit, F as buildAddonRouteProvider, Ft as smokeCapability, G as contactCapability, Gt as vibrationCapability, H as connectivityCapability, Ht as updateCapability, I as buildDeviceAdminUrl, J as deviceAdminLinkCapability, K as controlCapability, Kt as waterHeaterCapability, L as buttonCapability, Lt as switchCapability, M as boardKeySourceCapability, Mt as runningStateCapability, N as brightnessCapability, Nt as scriptRunnerCapability, O as automationControlCapability, P as brokerCapability, Q as eventEmitterCapability, Qt as baseUnitOf, R as calendarSourceCapability, Rt as tamperCapability, S as TargetSchema, Sn as EventCategory, St as presenceCapability, T as airQualitySensorCapability, Tt as publicEndpointReaderFrom, Ut as vacuumControlCapability, V as colorCapability, Vt as tryConvertUnit, Wt as valveCapability, Xt as RecordSecretUnavailableError, Y as deviceAdoptionCapability, Z as enumSensorCapability, _n as url, at as humiditySensorCapability, b as RUNNING_STATE_COLD, bn as DeviceType, bt as preconditionOf, cn as boolean, ct as integrationInstancesCapability, dn as number, dt as mediaPlayerCapability, en as defineAddonMigrations, et as flattenNestedRecords, fn as object, ft as motionCapability, gn as unknown, gt as numericSensorCapability, hn as union, ht as notifierCapability, it as humidifierCapability, j as binaryCapability, jt as runningFromVacuumState, k as batteryCapability, kt as runningFromLawnMower, ln as discriminatedUnion, lt as lawnMowerControlCapability, m as EnumSensorDateTimeFormatSchema, mn as string, mt as notificationOutputCapability, n as HA_BROKER_KIND, nn as defineRecordKinds, nt as floodCapability, o as BaseDevice, on as _enum, ot as imageCapability, pn as record, pt as normalizeUnit, q as coverCapability, qt as weatherCapability, rn as entryBody, rt as gasCapability, s as BaseDeviceProvider, sn as array, st as ingestReading, t as haInstanceSettingsPortsOver, tn as defineEntryKind, tt as flattenPortsOver, un as literal, ut as lockControlCapability, vn as DeviceFeature, vt as parseAdminBase, w as addonRoutesCapability, x as RecordIntegrationInstances, xn as errMsg, xt as prepareNotification, yn as DeviceRole, yt as powerMeterCapability, z as carbonMonoxideCapability, zt as temperatureSensorCapability } from "./legacy-integration-rows-uGrBo3a3.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { brotliCompressSync, deflateSync, gzipSync } from "node:zlib";
|
|
4
4
|
//#region src/ha-admin-link.ts
|
|
@@ -9305,7 +9305,7 @@ var HaProviderAddon = class HaProviderAddon extends BaseDeviceProvider {
|
|
|
9305
9305
|
eb.subscribe({ category: "integration.disabled" }, handler);
|
|
9306
9306
|
eb.subscribe({ category: "integration.deleted" }, handler);
|
|
9307
9307
|
}
|
|
9308
|
-
|
|
9308
|
+
async integrationFormSchema(_instance) {
|
|
9309
9309
|
return this.schema({ sections: [{
|
|
9310
9310
|
id: "ha-broker",
|
|
9311
9311
|
title: "Home Assistant broker",
|
|
@@ -24,7 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
enumerable: true
|
|
25
25
|
}) : target, mod));
|
|
26
26
|
//#endregion
|
|
27
|
-
const require_legacy_integration_rows = require("../legacy-integration-rows-
|
|
27
|
+
const require_legacy_integration_rows = require("../legacy-integration-rows-BaTsNMtt.js");
|
|
28
28
|
let node_crypto = require("node:crypto");
|
|
29
29
|
require("node:zlib");
|
|
30
30
|
let node_fs = require("node:fs");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { F as buildAddonRouteProvider, It as statusInstanceOf, Jt as BaseAddon, Ot as runExportedAction, Pt as sendToExportedTarget, Qt as baseUnitOf, Sn as EventCategory, U as consumableFormOf, W as consumableKindOf, X as deviceExportCapability, Zt as RevConflictError, _ as HvacModeSchema, _t as oauthIntegrationCapability, a as AlarmArmModeSchema, an as nodePin, bt as preconditionOf, c as CAMERA_SWITCH_ORDER, cn as boolean, ct as integrationInstancesCapability, d as ConsumableFormSchema, dn as number, en as defineAddonMigrations, f as ConsumableItemSchema, fn as object, g as FanDirectionSchema, gn as unknown, h as ExportInstanceUnknownError, i as listHomeAssistantBrokers, in as isReadingQuantity, l as COCO_TO_MACRO, mn as string, on as _enum, p as ConsumableStatusSchema, sn as array, t as haInstanceSettingsPortsOver, tn as defineEntryKind, u as CameraSwitchIdSchema, v as MediaPlayerRepeatSchema, w as addonRoutesCapability, wt as pruneDeletedEntities, x as RecordIntegrationInstances, y as PLUG_APPLIANCE_TYPES } from "../legacy-integration-rows-
|
|
1
|
+
import { F as buildAddonRouteProvider, It as statusInstanceOf, Jt as BaseAddon, Ot as runExportedAction, Pt as sendToExportedTarget, Qt as baseUnitOf, Sn as EventCategory, U as consumableFormOf, W as consumableKindOf, X as deviceExportCapability, Zt as RevConflictError, _ as HvacModeSchema, _t as oauthIntegrationCapability, a as AlarmArmModeSchema, an as nodePin, bt as preconditionOf, c as CAMERA_SWITCH_ORDER, cn as boolean, ct as integrationInstancesCapability, d as ConsumableFormSchema, dn as number, en as defineAddonMigrations, f as ConsumableItemSchema, fn as object, g as FanDirectionSchema, gn as unknown, h as ExportInstanceUnknownError, i as listHomeAssistantBrokers, in as isReadingQuantity, l as COCO_TO_MACRO, mn as string, on as _enum, p as ConsumableStatusSchema, sn as array, t as haInstanceSettingsPortsOver, tn as defineEntryKind, u as CameraSwitchIdSchema, v as MediaPlayerRepeatSchema, w as addonRoutesCapability, wt as pruneDeletedEntities, x as RecordIntegrationInstances, y as PLUG_APPLIANCE_TYPES } from "../legacy-integration-rows-uGrBo3a3.mjs";
|
|
2
2
|
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
3
3
|
import "node:zlib";
|
|
4
4
|
import "node:fs";
|
|
@@ -5981,7 +5981,7 @@ var ZodIssueCode = {
|
|
|
5981
5981
|
var ZodFirstPartyTypeKind;
|
|
5982
5982
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5983
5983
|
//#endregion
|
|
5984
|
-
//#region ../types/dist/sleep-
|
|
5984
|
+
//#region ../types/dist/sleep-DEZaLbiu.mjs
|
|
5985
5985
|
/**
|
|
5986
5986
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5987
5987
|
* window to float samples (D455).
|
|
@@ -6800,6 +6800,12 @@ function projectStore(store, perNodeKeys, nodeId) {
|
|
|
6800
6800
|
}
|
|
6801
6801
|
return out;
|
|
6802
6802
|
}
|
|
6803
|
+
/** Every scope, in the order the guard and the docs list them. */
|
|
6804
|
+
var SCOPED_SETTINGS_SCOPES = [
|
|
6805
|
+
"cluster",
|
|
6806
|
+
"device-defaults",
|
|
6807
|
+
"node"
|
|
6808
|
+
];
|
|
6803
6809
|
/**
|
|
6804
6810
|
* The address of one keychain value: `secret://<owner>/<scope>/<fieldPath>`.
|
|
6805
6811
|
*
|
|
@@ -7691,6 +7697,13 @@ var BaseAddon = class {
|
|
|
7691
7697
|
*/
|
|
7692
7698
|
secretStatePaths = [];
|
|
7693
7699
|
_addonSecretTree = null;
|
|
7700
|
+
/**
|
|
7701
|
+
* D986 M4: the scoped schemas this addon declared, resolved once per capability
|
|
7702
|
+
* (`loadScopedSchemas`) so the sync readers (`perNodeKeys`, the secret tree, the restart check)
|
|
7703
|
+
* see them. Empty until loaded: the readers then fall back to {@link globalSettingsSchema}.
|
|
7704
|
+
*/
|
|
7705
|
+
_scopedByCap = /* @__PURE__ */ new Map();
|
|
7706
|
+
_nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
7694
7707
|
_deviceSecretTree = null;
|
|
7695
7708
|
/** Refs the last resolved config pointed at — a change to one re-resolves. */
|
|
7696
7709
|
_configSecretRefs = /* @__PURE__ */ new Set();
|
|
@@ -7760,6 +7773,7 @@ var BaseAddon = class {
|
|
|
7760
7773
|
this._ctx = context;
|
|
7761
7774
|
this._secretSettings = null;
|
|
7762
7775
|
}
|
|
7776
|
+
await this.loadScopedSchemas();
|
|
7763
7777
|
await this.resolveConfig();
|
|
7764
7778
|
if (secrets !== void 0) this._secretChangeUnsub = secrets.onChange((ref) => {
|
|
7765
7779
|
if (this._configSecretRefs.has(ref)) this.scheduleSecretReresolve(SECRET_CHANGE_DEBOUNCE_MS);
|
|
@@ -7769,6 +7783,7 @@ var BaseAddon = class {
|
|
|
7769
7783
|
const normalized = normalizeAddonInitResult(result);
|
|
7770
7784
|
const providers = normalized && "providers" in normalized && normalized.providers ? normalized.providers : [];
|
|
7771
7785
|
this._registeredCapNames = providers.map((p) => p.capability.name);
|
|
7786
|
+
await this.loadScopedSchemas();
|
|
7772
7787
|
return normalized;
|
|
7773
7788
|
}
|
|
7774
7789
|
/**
|
|
@@ -7857,6 +7872,16 @@ var BaseAddon = class {
|
|
|
7857
7872
|
schema(schema) {
|
|
7858
7873
|
return schema;
|
|
7859
7874
|
}
|
|
7875
|
+
/**
|
|
7876
|
+
* The capability names whose scoped settings schemas ({@link clusterSettingsSchema},
|
|
7877
|
+
* {@link nodeSettingsSchema}, {@link deviceDefaultsSchema}) this addon declares. Read BEFORE
|
|
7878
|
+
* `onInitialize`, when the providers it registers are not known yet: the first config resolve
|
|
7879
|
+
* must already know which keys are per node. Names the providers register at init are added
|
|
7880
|
+
* once known.
|
|
7881
|
+
*/
|
|
7882
|
+
scopedSettingsCapabilities() {
|
|
7883
|
+
return [];
|
|
7884
|
+
}
|
|
7860
7885
|
/** Override to provide global-level settings UI schema. */
|
|
7861
7886
|
globalSettingsSchema(_cap) {
|
|
7862
7887
|
return null;
|
|
@@ -7900,7 +7925,8 @@ var BaseAddon = class {
|
|
|
7900
7925
|
return [];
|
|
7901
7926
|
}
|
|
7902
7927
|
async getGlobalSettings(overlay, cap, nodeId) {
|
|
7903
|
-
|
|
7928
|
+
await this.loadScopedSchemas(cap);
|
|
7929
|
+
const schema = this.globalSettingsSchema(cap) ?? this.scopedFormSchema(cap);
|
|
7904
7930
|
if (!schema) return { sections: [] };
|
|
7905
7931
|
const projected = await this.readRawProjectedStore(nodeId, cap);
|
|
7906
7932
|
return hydrateSchema(schema, overlay ? await this.overlayKeepingMarkers(projected, overlay) : projected, { inlineSecretsAsViews: true });
|
|
@@ -8031,6 +8057,7 @@ var BaseAddon = class {
|
|
|
8031
8057
|
* resolved blob back is a no-op for every unchanged secret.
|
|
8032
8058
|
*/
|
|
8033
8059
|
async resolveGlobalStore(nodeId, cap) {
|
|
8060
|
+
await this.loadScopedSchemas(cap);
|
|
8034
8061
|
const raw = await this._ctx?.settings?.readAddonStore() ?? {};
|
|
8035
8062
|
return this.projectForNode(raw, nodeId, cap);
|
|
8036
8063
|
}
|
|
@@ -8041,6 +8068,7 @@ var BaseAddon = class {
|
|
|
8041
8068
|
}
|
|
8042
8069
|
/** The PROJECTED store with its markers — the form hydrate only (D798). */
|
|
8043
8070
|
async readRawProjectedStore(nodeId, cap) {
|
|
8071
|
+
await this.loadScopedSchemas(cap);
|
|
8044
8072
|
const raw = await this.rawSettings()?.readAddonStore() ?? {};
|
|
8045
8073
|
return this.projectForNode(raw, nodeId, cap);
|
|
8046
8074
|
}
|
|
@@ -8143,6 +8171,7 @@ var BaseAddon = class {
|
|
|
8143
8171
|
await this.applyGlobalPatch(patch, nodeId);
|
|
8144
8172
|
}
|
|
8145
8173
|
async applyGlobalPatch(barePatch, nodeId) {
|
|
8174
|
+
await this.loadScopedSchemas();
|
|
8146
8175
|
const keys = this.perNodeKeys();
|
|
8147
8176
|
const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
|
|
8148
8177
|
const target = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
|
|
@@ -8156,7 +8185,8 @@ var BaseAddon = class {
|
|
|
8156
8185
|
await this.resolveConfig();
|
|
8157
8186
|
await this.onConfigChanged();
|
|
8158
8187
|
this.emitLifecycle(EventCategory.AddonUpdated, { level: "global" });
|
|
8159
|
-
|
|
8188
|
+
const legacy = this.globalSettingsSchema();
|
|
8189
|
+
this.maybeAutoRestart(Object.keys(barePatch), [...legacy === null ? [] : [legacy], ...this.loadedScopedSchemas().map((entry) => entry.schema)]);
|
|
8160
8190
|
}
|
|
8161
8191
|
/**
|
|
8162
8192
|
* The set of field keys the global settings schema declares `perNode: true`
|
|
@@ -8164,14 +8194,67 @@ var BaseAddon = class {
|
|
|
8164
8194
|
* declarations). Empty set ⇒ every per-node code path is bypassed and the
|
|
8165
8195
|
* settings API behaves exactly like the legacy node-agnostic one.
|
|
8166
8196
|
*/
|
|
8167
|
-
|
|
8197
|
+
/**
|
|
8198
|
+
* Load the scoped schemas of every capability this addon declares (the override and the
|
|
8199
|
+
* providers registered at init, plus `extraCap`) that is not loaded yet, then drop what was
|
|
8200
|
+
* derived from the previous set. Idempotent.
|
|
8201
|
+
*/
|
|
8202
|
+
async loadScopedSchemas(extraCap) {
|
|
8203
|
+
const caps = new Set([
|
|
8204
|
+
...this.scopedSettingsCapabilities(),
|
|
8205
|
+
...this._registeredCapNames,
|
|
8206
|
+
...extraCap === void 0 ? [] : [extraCap]
|
|
8207
|
+
]);
|
|
8208
|
+
let added = false;
|
|
8209
|
+
for (const cap of caps) {
|
|
8210
|
+
if (this._scopedByCap.has(cap)) continue;
|
|
8211
|
+
const loaded = [];
|
|
8212
|
+
for (const scope of SCOPED_SETTINGS_SCOPES) {
|
|
8213
|
+
const schema = await this.getScopedSettingsSchema(scope, cap);
|
|
8214
|
+
if (schema !== null) loaded.push({
|
|
8215
|
+
scope,
|
|
8216
|
+
schema
|
|
8217
|
+
});
|
|
8218
|
+
}
|
|
8219
|
+
this._scopedByCap.set(cap, loaded);
|
|
8220
|
+
added = true;
|
|
8221
|
+
}
|
|
8222
|
+
if (!added) return;
|
|
8223
|
+
this._nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
8224
|
+
this._addonSecretTree = null;
|
|
8225
|
+
}
|
|
8226
|
+
/**
|
|
8227
|
+
* The form the settings API serves while an addon declares no {@link globalSettingsSchema}: the
|
|
8228
|
+
* sections of its scoped schemas, so dropping the old hook does not blank the form.
|
|
8229
|
+
*/
|
|
8230
|
+
scopedFormSchema(cap) {
|
|
8231
|
+
const byId = /* @__PURE__ */ new Map();
|
|
8232
|
+
for (const entry of this.loadedScopedSchemas(cap)) for (const section of entry.schema.sections) if (!byId.has(section.id)) byId.set(section.id, section);
|
|
8233
|
+
return byId.size === 0 ? null : { sections: [...byId.values()] };
|
|
8234
|
+
}
|
|
8235
|
+
/** Every loaded scoped schema of `cap`, or of every loaded capability. */
|
|
8236
|
+
loadedScopedSchemas(cap) {
|
|
8237
|
+
if (cap !== void 0) return this._scopedByCap.get(cap) ?? [];
|
|
8238
|
+
return [...this._scopedByCap.values()].flat();
|
|
8239
|
+
}
|
|
8240
|
+
/**
|
|
8241
|
+
* The field keys that are stored per node: every field of a {@link nodeSettingsSchema}, plus
|
|
8242
|
+
* the fields the legacy {@link globalSettingsSchema} still marks `perNode: true`. Derived once
|
|
8243
|
+
* per `cap` argument and memoized (schemas are static declarations). Empty set ⇒ every
|
|
8244
|
+
* per-node code path is bypassed and the settings API behaves like the node-agnostic one.
|
|
8245
|
+
* Call {@link loadScopedSchemas} first (the async seams do).
|
|
8246
|
+
*/
|
|
8168
8247
|
perNodeKeys(cap) {
|
|
8169
8248
|
const cacheKey = cap ?? "";
|
|
8170
|
-
const cached = this.
|
|
8249
|
+
const cached = this._nodeKeysByCap.get(cacheKey);
|
|
8171
8250
|
if (cached) return cached;
|
|
8172
|
-
const
|
|
8173
|
-
const keys = new Set(
|
|
8174
|
-
this.
|
|
8251
|
+
const legacy = this.globalSettingsSchema(cap);
|
|
8252
|
+
const keys = new Set(legacy ? legacy.sections.flatMap((section) => collectPerNodeFieldKeys(section.fields)) : []);
|
|
8253
|
+
for (const { scope, schema } of this.loadedScopedSchemas(cap)) {
|
|
8254
|
+
if (scope !== "node") continue;
|
|
8255
|
+
for (const section of schema.sections) for (const key of collectValueFieldKeys(section.fields)) keys.add(key);
|
|
8256
|
+
}
|
|
8257
|
+
this._nodeKeysByCap.set(cacheKey, keys);
|
|
8175
8258
|
return keys;
|
|
8176
8259
|
}
|
|
8177
8260
|
/**
|
|
@@ -8181,10 +8264,9 @@ var BaseAddon = class {
|
|
|
8181
8264
|
* time to return its response before the addon is torn down and
|
|
8182
8265
|
* re-initialised by `AddonRegistryService.restartAddon`.
|
|
8183
8266
|
*/
|
|
8184
|
-
maybeAutoRestart(patchKeys,
|
|
8185
|
-
if (!schema) return;
|
|
8267
|
+
maybeAutoRestart(patchKeys, schemas) {
|
|
8186
8268
|
const restartKeys = /* @__PURE__ */ new Set();
|
|
8187
|
-
for (const section of schema.sections) for (const field of section.fields) {
|
|
8269
|
+
for (const schema of schemas) for (const section of schema.sections) for (const field of section.fields) {
|
|
8188
8270
|
if (field.type === "separator" || field.type === "info") continue;
|
|
8189
8271
|
if (field.requiresRestart) restartKeys.add(field.key);
|
|
8190
8272
|
}
|
|
@@ -8380,12 +8462,14 @@ var BaseAddon = class {
|
|
|
8380
8462
|
* here, in the addon's own process, so the hub keeps no copy of it.
|
|
8381
8463
|
*/
|
|
8382
8464
|
async getDeclaredSettingsKeys() {
|
|
8465
|
+
await this.loadScopedSchemas();
|
|
8383
8466
|
return { keys: [...this.declaredKeySet()].toSorted() };
|
|
8384
8467
|
}
|
|
8385
8468
|
declaredKeySet() {
|
|
8386
8469
|
const keys = new Set(Object.keys(this.defaults));
|
|
8387
8470
|
const schema = this.globalSettingsSchema();
|
|
8388
8471
|
if (schema) for (const section of schema.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
8472
|
+
for (const { schema: scoped } of this.loadedScopedSchemas()) for (const section of scoped.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
8389
8473
|
for (const key of this._declaredStateKeys) keys.add(key);
|
|
8390
8474
|
for (const path of this.secretStatePaths) {
|
|
8391
8475
|
const root = path.split(/[.[]/, 1)[0];
|
|
@@ -8481,7 +8565,11 @@ var BaseAddon = class {
|
|
|
8481
8565
|
}
|
|
8482
8566
|
/** The addon store's secret tree: schema fields by TYPE, plus `secretStatePaths`. */
|
|
8483
8567
|
addonSecretTree() {
|
|
8484
|
-
this._addonSecretTree
|
|
8568
|
+
if (this._addonSecretTree === null) {
|
|
8569
|
+
const tree = new Map(secretTreeFromSchema(this.globalSettingsSchema()));
|
|
8570
|
+
for (const { schema } of this.loadedScopedSchemas()) for (const [key, node] of secretTreeFromSchema(schema)) tree.set(key, node);
|
|
8571
|
+
this._addonSecretTree = mergeSecretStatePaths(tree, this.secretStatePaths);
|
|
8572
|
+
}
|
|
8485
8573
|
return this._addonSecretTree;
|
|
8486
8574
|
}
|
|
8487
8575
|
deviceSecretTree() {
|
|
@@ -8731,6 +8819,23 @@ function dropPerNodeFields(fields) {
|
|
|
8731
8819
|
}
|
|
8732
8820
|
return kept;
|
|
8733
8821
|
}
|
|
8822
|
+
/** The keys of the value-holding fields, recursing into layout containers. */
|
|
8823
|
+
function collectValueFieldKeys(fields) {
|
|
8824
|
+
const collected = [];
|
|
8825
|
+
for (const field of fields) {
|
|
8826
|
+
if (field.type === "group") {
|
|
8827
|
+
collected.push(...collectValueFieldKeys(field.fields));
|
|
8828
|
+
continue;
|
|
8829
|
+
}
|
|
8830
|
+
if (field.type === "sub-tabs") {
|
|
8831
|
+
for (const tab of field.tabs) collected.push(...collectValueFieldKeys(tab.fields));
|
|
8832
|
+
continue;
|
|
8833
|
+
}
|
|
8834
|
+
if (field.type === "separator" || field.type === "info") continue;
|
|
8835
|
+
if ("key" in field && typeof field.key === "string") collected.push(field.key);
|
|
8836
|
+
}
|
|
8837
|
+
return collected;
|
|
8838
|
+
}
|
|
8734
8839
|
function collectFieldKeys(fields) {
|
|
8735
8840
|
const collected = [];
|
|
8736
8841
|
for (const field of fields) {
|
|
@@ -13585,12 +13690,22 @@ var RecordIntegrationInstances = class {
|
|
|
13585
13690
|
...this.hooks.description,
|
|
13586
13691
|
mintsId: true
|
|
13587
13692
|
};
|
|
13588
|
-
|
|
13693
|
+
/**
|
|
13694
|
+
* The record an id names. A caller holding the `integrations` registry id (the Integrations
|
|
13695
|
+
* page) reaches a kind whose body links to it through `integrationId`; an id that is itself a
|
|
13696
|
+
* record id answers as it is. An id that names nothing comes back unchanged.
|
|
13697
|
+
*/
|
|
13698
|
+
async resolveRecordId(id) {
|
|
13699
|
+
if (await this.handle.get(id) !== null) return id;
|
|
13700
|
+
const linked = (await this.handle.query({ limit: INSTANCES_LIST_MAX })).records.find((record) => record.data["integrationId"] === id);
|
|
13701
|
+
return linked === void 0 ? id : linked.id;
|
|
13702
|
+
}
|
|
13703
|
+
instanceFormSchema = async ({ id }) => this.hooks.formSchema(id === null ? null : await this.resolveRecordId(id));
|
|
13589
13704
|
listInstances = async () => {
|
|
13590
13705
|
return (await this.handle.query({ limit: INSTANCES_LIST_MAX })).records.map((record) => this.viewOf(record));
|
|
13591
13706
|
};
|
|
13592
13707
|
getInstance = async ({ id }) => {
|
|
13593
|
-
const record = await this.handle.get(id);
|
|
13708
|
+
const record = await this.handle.get(await this.resolveRecordId(id));
|
|
13594
13709
|
return record === null ? null : this.viewOf(record);
|
|
13595
13710
|
};
|
|
13596
13711
|
/** Insert ONE row (`ifRev: 0`: an existing id is refused, never overwritten). */
|
|
@@ -13607,7 +13722,8 @@ var RecordIntegrationInstances = class {
|
|
|
13607
13722
|
await this.hooks.onChanged?.(minted, "created");
|
|
13608
13723
|
return { id: minted };
|
|
13609
13724
|
};
|
|
13610
|
-
updateInstance = async ({ id, name, values }) => {
|
|
13725
|
+
updateInstance = async ({ id: askedId, name, values }) => {
|
|
13726
|
+
const id = await this.resolveRecordId(askedId);
|
|
13611
13727
|
const patch = {};
|
|
13612
13728
|
for (const [key, value] of Object.entries(values)) {
|
|
13613
13729
|
if (value === void 0) continue;
|
|
@@ -13642,7 +13758,10 @@ var RecordIntegrationInstances = class {
|
|
|
13642
13758
|
status: "unsupported",
|
|
13643
13759
|
error: "This integration declares no connection test"
|
|
13644
13760
|
};
|
|
13645
|
-
return this.hooks.test(input
|
|
13761
|
+
return this.hooks.test(input.id === null ? input : {
|
|
13762
|
+
...input,
|
|
13763
|
+
id: await this.resolveRecordId(input.id)
|
|
13764
|
+
});
|
|
13646
13765
|
};
|
|
13647
13766
|
/** The flat provider object to hand to `registerProvider`. */
|
|
13648
13767
|
toProvider() {
|
|
@@ -52300,12 +52419,12 @@ var deviceExportCapability = {
|
|
|
52300
52419
|
kind: "poll"
|
|
52301
52420
|
},
|
|
52302
52421
|
/**
|
|
52303
|
-
* Each export provider
|
|
52304
|
-
* (enabled toggle, preferred stream, addon-specific knobs
|
|
52305
|
-
*
|
|
52306
|
-
* `
|
|
52307
|
-
* `
|
|
52308
|
-
*
|
|
52422
|
+
* Each export provider MAY contribute its own per-device "Export" panel
|
|
52423
|
+
* (enabled toggle, preferred stream, addon-specific knobs). The three
|
|
52424
|
+
* framework methods are mounted on the cap router when present.
|
|
52425
|
+
* `IDeviceExportProvider` leaves them optional: an exporter with no
|
|
52426
|
+
* per-device panel omits them (a `deviceConfig` cap's UI is derived and
|
|
52427
|
+
* never hand-written — D14 — and an exporter is not one of those).
|
|
52309
52428
|
*/
|
|
52310
52429
|
exposesDeviceSettings: true,
|
|
52311
52430
|
methods: {
|
|
@@ -5980,7 +5980,7 @@ var ZodIssueCode = {
|
|
|
5980
5980
|
var ZodFirstPartyTypeKind;
|
|
5981
5981
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5982
5982
|
//#endregion
|
|
5983
|
-
//#region ../types/dist/sleep-
|
|
5983
|
+
//#region ../types/dist/sleep-DEZaLbiu.mjs
|
|
5984
5984
|
/**
|
|
5985
5985
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5986
5986
|
* window to float samples (D455).
|
|
@@ -6799,6 +6799,12 @@ function projectStore(store, perNodeKeys, nodeId) {
|
|
|
6799
6799
|
}
|
|
6800
6800
|
return out;
|
|
6801
6801
|
}
|
|
6802
|
+
/** Every scope, in the order the guard and the docs list them. */
|
|
6803
|
+
var SCOPED_SETTINGS_SCOPES = [
|
|
6804
|
+
"cluster",
|
|
6805
|
+
"device-defaults",
|
|
6806
|
+
"node"
|
|
6807
|
+
];
|
|
6802
6808
|
/**
|
|
6803
6809
|
* The address of one keychain value: `secret://<owner>/<scope>/<fieldPath>`.
|
|
6804
6810
|
*
|
|
@@ -7690,6 +7696,13 @@ var BaseAddon = class {
|
|
|
7690
7696
|
*/
|
|
7691
7697
|
secretStatePaths = [];
|
|
7692
7698
|
_addonSecretTree = null;
|
|
7699
|
+
/**
|
|
7700
|
+
* D986 M4: the scoped schemas this addon declared, resolved once per capability
|
|
7701
|
+
* (`loadScopedSchemas`) so the sync readers (`perNodeKeys`, the secret tree, the restart check)
|
|
7702
|
+
* see them. Empty until loaded: the readers then fall back to {@link globalSettingsSchema}.
|
|
7703
|
+
*/
|
|
7704
|
+
_scopedByCap = /* @__PURE__ */ new Map();
|
|
7705
|
+
_nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
7693
7706
|
_deviceSecretTree = null;
|
|
7694
7707
|
/** Refs the last resolved config pointed at — a change to one re-resolves. */
|
|
7695
7708
|
_configSecretRefs = /* @__PURE__ */ new Set();
|
|
@@ -7759,6 +7772,7 @@ var BaseAddon = class {
|
|
|
7759
7772
|
this._ctx = context;
|
|
7760
7773
|
this._secretSettings = null;
|
|
7761
7774
|
}
|
|
7775
|
+
await this.loadScopedSchemas();
|
|
7762
7776
|
await this.resolveConfig();
|
|
7763
7777
|
if (secrets !== void 0) this._secretChangeUnsub = secrets.onChange((ref) => {
|
|
7764
7778
|
if (this._configSecretRefs.has(ref)) this.scheduleSecretReresolve(SECRET_CHANGE_DEBOUNCE_MS);
|
|
@@ -7768,6 +7782,7 @@ var BaseAddon = class {
|
|
|
7768
7782
|
const normalized = normalizeAddonInitResult(result);
|
|
7769
7783
|
const providers = normalized && "providers" in normalized && normalized.providers ? normalized.providers : [];
|
|
7770
7784
|
this._registeredCapNames = providers.map((p) => p.capability.name);
|
|
7785
|
+
await this.loadScopedSchemas();
|
|
7771
7786
|
return normalized;
|
|
7772
7787
|
}
|
|
7773
7788
|
/**
|
|
@@ -7856,6 +7871,16 @@ var BaseAddon = class {
|
|
|
7856
7871
|
schema(schema) {
|
|
7857
7872
|
return schema;
|
|
7858
7873
|
}
|
|
7874
|
+
/**
|
|
7875
|
+
* The capability names whose scoped settings schemas ({@link clusterSettingsSchema},
|
|
7876
|
+
* {@link nodeSettingsSchema}, {@link deviceDefaultsSchema}) this addon declares. Read BEFORE
|
|
7877
|
+
* `onInitialize`, when the providers it registers are not known yet: the first config resolve
|
|
7878
|
+
* must already know which keys are per node. Names the providers register at init are added
|
|
7879
|
+
* once known.
|
|
7880
|
+
*/
|
|
7881
|
+
scopedSettingsCapabilities() {
|
|
7882
|
+
return [];
|
|
7883
|
+
}
|
|
7859
7884
|
/** Override to provide global-level settings UI schema. */
|
|
7860
7885
|
globalSettingsSchema(_cap) {
|
|
7861
7886
|
return null;
|
|
@@ -7899,7 +7924,8 @@ var BaseAddon = class {
|
|
|
7899
7924
|
return [];
|
|
7900
7925
|
}
|
|
7901
7926
|
async getGlobalSettings(overlay, cap, nodeId) {
|
|
7902
|
-
|
|
7927
|
+
await this.loadScopedSchemas(cap);
|
|
7928
|
+
const schema = this.globalSettingsSchema(cap) ?? this.scopedFormSchema(cap);
|
|
7903
7929
|
if (!schema) return { sections: [] };
|
|
7904
7930
|
const projected = await this.readRawProjectedStore(nodeId, cap);
|
|
7905
7931
|
return hydrateSchema(schema, overlay ? await this.overlayKeepingMarkers(projected, overlay) : projected, { inlineSecretsAsViews: true });
|
|
@@ -8030,6 +8056,7 @@ var BaseAddon = class {
|
|
|
8030
8056
|
* resolved blob back is a no-op for every unchanged secret.
|
|
8031
8057
|
*/
|
|
8032
8058
|
async resolveGlobalStore(nodeId, cap) {
|
|
8059
|
+
await this.loadScopedSchemas(cap);
|
|
8033
8060
|
const raw = await this._ctx?.settings?.readAddonStore() ?? {};
|
|
8034
8061
|
return this.projectForNode(raw, nodeId, cap);
|
|
8035
8062
|
}
|
|
@@ -8040,6 +8067,7 @@ var BaseAddon = class {
|
|
|
8040
8067
|
}
|
|
8041
8068
|
/** The PROJECTED store with its markers — the form hydrate only (D798). */
|
|
8042
8069
|
async readRawProjectedStore(nodeId, cap) {
|
|
8070
|
+
await this.loadScopedSchemas(cap);
|
|
8043
8071
|
const raw = await this.rawSettings()?.readAddonStore() ?? {};
|
|
8044
8072
|
return this.projectForNode(raw, nodeId, cap);
|
|
8045
8073
|
}
|
|
@@ -8142,6 +8170,7 @@ var BaseAddon = class {
|
|
|
8142
8170
|
await this.applyGlobalPatch(patch, nodeId);
|
|
8143
8171
|
}
|
|
8144
8172
|
async applyGlobalPatch(barePatch, nodeId) {
|
|
8173
|
+
await this.loadScopedSchemas();
|
|
8145
8174
|
const keys = this.perNodeKeys();
|
|
8146
8175
|
const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
|
|
8147
8176
|
const target = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
|
|
@@ -8155,7 +8184,8 @@ var BaseAddon = class {
|
|
|
8155
8184
|
await this.resolveConfig();
|
|
8156
8185
|
await this.onConfigChanged();
|
|
8157
8186
|
this.emitLifecycle(EventCategory.AddonUpdated, { level: "global" });
|
|
8158
|
-
|
|
8187
|
+
const legacy = this.globalSettingsSchema();
|
|
8188
|
+
this.maybeAutoRestart(Object.keys(barePatch), [...legacy === null ? [] : [legacy], ...this.loadedScopedSchemas().map((entry) => entry.schema)]);
|
|
8159
8189
|
}
|
|
8160
8190
|
/**
|
|
8161
8191
|
* The set of field keys the global settings schema declares `perNode: true`
|
|
@@ -8163,14 +8193,67 @@ var BaseAddon = class {
|
|
|
8163
8193
|
* declarations). Empty set ⇒ every per-node code path is bypassed and the
|
|
8164
8194
|
* settings API behaves exactly like the legacy node-agnostic one.
|
|
8165
8195
|
*/
|
|
8166
|
-
|
|
8196
|
+
/**
|
|
8197
|
+
* Load the scoped schemas of every capability this addon declares (the override and the
|
|
8198
|
+
* providers registered at init, plus `extraCap`) that is not loaded yet, then drop what was
|
|
8199
|
+
* derived from the previous set. Idempotent.
|
|
8200
|
+
*/
|
|
8201
|
+
async loadScopedSchemas(extraCap) {
|
|
8202
|
+
const caps = new Set([
|
|
8203
|
+
...this.scopedSettingsCapabilities(),
|
|
8204
|
+
...this._registeredCapNames,
|
|
8205
|
+
...extraCap === void 0 ? [] : [extraCap]
|
|
8206
|
+
]);
|
|
8207
|
+
let added = false;
|
|
8208
|
+
for (const cap of caps) {
|
|
8209
|
+
if (this._scopedByCap.has(cap)) continue;
|
|
8210
|
+
const loaded = [];
|
|
8211
|
+
for (const scope of SCOPED_SETTINGS_SCOPES) {
|
|
8212
|
+
const schema = await this.getScopedSettingsSchema(scope, cap);
|
|
8213
|
+
if (schema !== null) loaded.push({
|
|
8214
|
+
scope,
|
|
8215
|
+
schema
|
|
8216
|
+
});
|
|
8217
|
+
}
|
|
8218
|
+
this._scopedByCap.set(cap, loaded);
|
|
8219
|
+
added = true;
|
|
8220
|
+
}
|
|
8221
|
+
if (!added) return;
|
|
8222
|
+
this._nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
8223
|
+
this._addonSecretTree = null;
|
|
8224
|
+
}
|
|
8225
|
+
/**
|
|
8226
|
+
* The form the settings API serves while an addon declares no {@link globalSettingsSchema}: the
|
|
8227
|
+
* sections of its scoped schemas, so dropping the old hook does not blank the form.
|
|
8228
|
+
*/
|
|
8229
|
+
scopedFormSchema(cap) {
|
|
8230
|
+
const byId = /* @__PURE__ */ new Map();
|
|
8231
|
+
for (const entry of this.loadedScopedSchemas(cap)) for (const section of entry.schema.sections) if (!byId.has(section.id)) byId.set(section.id, section);
|
|
8232
|
+
return byId.size === 0 ? null : { sections: [...byId.values()] };
|
|
8233
|
+
}
|
|
8234
|
+
/** Every loaded scoped schema of `cap`, or of every loaded capability. */
|
|
8235
|
+
loadedScopedSchemas(cap) {
|
|
8236
|
+
if (cap !== void 0) return this._scopedByCap.get(cap) ?? [];
|
|
8237
|
+
return [...this._scopedByCap.values()].flat();
|
|
8238
|
+
}
|
|
8239
|
+
/**
|
|
8240
|
+
* The field keys that are stored per node: every field of a {@link nodeSettingsSchema}, plus
|
|
8241
|
+
* the fields the legacy {@link globalSettingsSchema} still marks `perNode: true`. Derived once
|
|
8242
|
+
* per `cap` argument and memoized (schemas are static declarations). Empty set ⇒ every
|
|
8243
|
+
* per-node code path is bypassed and the settings API behaves like the node-agnostic one.
|
|
8244
|
+
* Call {@link loadScopedSchemas} first (the async seams do).
|
|
8245
|
+
*/
|
|
8167
8246
|
perNodeKeys(cap) {
|
|
8168
8247
|
const cacheKey = cap ?? "";
|
|
8169
|
-
const cached = this.
|
|
8248
|
+
const cached = this._nodeKeysByCap.get(cacheKey);
|
|
8170
8249
|
if (cached) return cached;
|
|
8171
|
-
const
|
|
8172
|
-
const keys = new Set(
|
|
8173
|
-
this.
|
|
8250
|
+
const legacy = this.globalSettingsSchema(cap);
|
|
8251
|
+
const keys = new Set(legacy ? legacy.sections.flatMap((section) => collectPerNodeFieldKeys(section.fields)) : []);
|
|
8252
|
+
for (const { scope, schema } of this.loadedScopedSchemas(cap)) {
|
|
8253
|
+
if (scope !== "node") continue;
|
|
8254
|
+
for (const section of schema.sections) for (const key of collectValueFieldKeys(section.fields)) keys.add(key);
|
|
8255
|
+
}
|
|
8256
|
+
this._nodeKeysByCap.set(cacheKey, keys);
|
|
8174
8257
|
return keys;
|
|
8175
8258
|
}
|
|
8176
8259
|
/**
|
|
@@ -8180,10 +8263,9 @@ var BaseAddon = class {
|
|
|
8180
8263
|
* time to return its response before the addon is torn down and
|
|
8181
8264
|
* re-initialised by `AddonRegistryService.restartAddon`.
|
|
8182
8265
|
*/
|
|
8183
|
-
maybeAutoRestart(patchKeys,
|
|
8184
|
-
if (!schema) return;
|
|
8266
|
+
maybeAutoRestart(patchKeys, schemas) {
|
|
8185
8267
|
const restartKeys = /* @__PURE__ */ new Set();
|
|
8186
|
-
for (const section of schema.sections) for (const field of section.fields) {
|
|
8268
|
+
for (const schema of schemas) for (const section of schema.sections) for (const field of section.fields) {
|
|
8187
8269
|
if (field.type === "separator" || field.type === "info") continue;
|
|
8188
8270
|
if (field.requiresRestart) restartKeys.add(field.key);
|
|
8189
8271
|
}
|
|
@@ -8379,12 +8461,14 @@ var BaseAddon = class {
|
|
|
8379
8461
|
* here, in the addon's own process, so the hub keeps no copy of it.
|
|
8380
8462
|
*/
|
|
8381
8463
|
async getDeclaredSettingsKeys() {
|
|
8464
|
+
await this.loadScopedSchemas();
|
|
8382
8465
|
return { keys: [...this.declaredKeySet()].toSorted() };
|
|
8383
8466
|
}
|
|
8384
8467
|
declaredKeySet() {
|
|
8385
8468
|
const keys = new Set(Object.keys(this.defaults));
|
|
8386
8469
|
const schema = this.globalSettingsSchema();
|
|
8387
8470
|
if (schema) for (const section of schema.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
8471
|
+
for (const { schema: scoped } of this.loadedScopedSchemas()) for (const section of scoped.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
8388
8472
|
for (const key of this._declaredStateKeys) keys.add(key);
|
|
8389
8473
|
for (const path of this.secretStatePaths) {
|
|
8390
8474
|
const root = path.split(/[.[]/, 1)[0];
|
|
@@ -8480,7 +8564,11 @@ var BaseAddon = class {
|
|
|
8480
8564
|
}
|
|
8481
8565
|
/** The addon store's secret tree: schema fields by TYPE, plus `secretStatePaths`. */
|
|
8482
8566
|
addonSecretTree() {
|
|
8483
|
-
this._addonSecretTree
|
|
8567
|
+
if (this._addonSecretTree === null) {
|
|
8568
|
+
const tree = new Map(secretTreeFromSchema(this.globalSettingsSchema()));
|
|
8569
|
+
for (const { schema } of this.loadedScopedSchemas()) for (const [key, node] of secretTreeFromSchema(schema)) tree.set(key, node);
|
|
8570
|
+
this._addonSecretTree = mergeSecretStatePaths(tree, this.secretStatePaths);
|
|
8571
|
+
}
|
|
8484
8572
|
return this._addonSecretTree;
|
|
8485
8573
|
}
|
|
8486
8574
|
deviceSecretTree() {
|
|
@@ -8730,6 +8818,23 @@ function dropPerNodeFields(fields) {
|
|
|
8730
8818
|
}
|
|
8731
8819
|
return kept;
|
|
8732
8820
|
}
|
|
8821
|
+
/** The keys of the value-holding fields, recursing into layout containers. */
|
|
8822
|
+
function collectValueFieldKeys(fields) {
|
|
8823
|
+
const collected = [];
|
|
8824
|
+
for (const field of fields) {
|
|
8825
|
+
if (field.type === "group") {
|
|
8826
|
+
collected.push(...collectValueFieldKeys(field.fields));
|
|
8827
|
+
continue;
|
|
8828
|
+
}
|
|
8829
|
+
if (field.type === "sub-tabs") {
|
|
8830
|
+
for (const tab of field.tabs) collected.push(...collectValueFieldKeys(tab.fields));
|
|
8831
|
+
continue;
|
|
8832
|
+
}
|
|
8833
|
+
if (field.type === "separator" || field.type === "info") continue;
|
|
8834
|
+
if ("key" in field && typeof field.key === "string") collected.push(field.key);
|
|
8835
|
+
}
|
|
8836
|
+
return collected;
|
|
8837
|
+
}
|
|
8733
8838
|
function collectFieldKeys(fields) {
|
|
8734
8839
|
const collected = [];
|
|
8735
8840
|
for (const field of fields) {
|
|
@@ -13584,12 +13689,22 @@ var RecordIntegrationInstances = class {
|
|
|
13584
13689
|
...this.hooks.description,
|
|
13585
13690
|
mintsId: true
|
|
13586
13691
|
};
|
|
13587
|
-
|
|
13692
|
+
/**
|
|
13693
|
+
* The record an id names. A caller holding the `integrations` registry id (the Integrations
|
|
13694
|
+
* page) reaches a kind whose body links to it through `integrationId`; an id that is itself a
|
|
13695
|
+
* record id answers as it is. An id that names nothing comes back unchanged.
|
|
13696
|
+
*/
|
|
13697
|
+
async resolveRecordId(id) {
|
|
13698
|
+
if (await this.handle.get(id) !== null) return id;
|
|
13699
|
+
const linked = (await this.handle.query({ limit: INSTANCES_LIST_MAX })).records.find((record) => record.data["integrationId"] === id);
|
|
13700
|
+
return linked === void 0 ? id : linked.id;
|
|
13701
|
+
}
|
|
13702
|
+
instanceFormSchema = async ({ id }) => this.hooks.formSchema(id === null ? null : await this.resolveRecordId(id));
|
|
13588
13703
|
listInstances = async () => {
|
|
13589
13704
|
return (await this.handle.query({ limit: INSTANCES_LIST_MAX })).records.map((record) => this.viewOf(record));
|
|
13590
13705
|
};
|
|
13591
13706
|
getInstance = async ({ id }) => {
|
|
13592
|
-
const record = await this.handle.get(id);
|
|
13707
|
+
const record = await this.handle.get(await this.resolveRecordId(id));
|
|
13593
13708
|
return record === null ? null : this.viewOf(record);
|
|
13594
13709
|
};
|
|
13595
13710
|
/** Insert ONE row (`ifRev: 0`: an existing id is refused, never overwritten). */
|
|
@@ -13606,7 +13721,8 @@ var RecordIntegrationInstances = class {
|
|
|
13606
13721
|
await this.hooks.onChanged?.(minted, "created");
|
|
13607
13722
|
return { id: minted };
|
|
13608
13723
|
};
|
|
13609
|
-
updateInstance = async ({ id, name, values }) => {
|
|
13724
|
+
updateInstance = async ({ id: askedId, name, values }) => {
|
|
13725
|
+
const id = await this.resolveRecordId(askedId);
|
|
13610
13726
|
const patch = {};
|
|
13611
13727
|
for (const [key, value] of Object.entries(values)) {
|
|
13612
13728
|
if (value === void 0) continue;
|
|
@@ -13641,7 +13757,10 @@ var RecordIntegrationInstances = class {
|
|
|
13641
13757
|
status: "unsupported",
|
|
13642
13758
|
error: "This integration declares no connection test"
|
|
13643
13759
|
};
|
|
13644
|
-
return this.hooks.test(input
|
|
13760
|
+
return this.hooks.test(input.id === null ? input : {
|
|
13761
|
+
...input,
|
|
13762
|
+
id: await this.resolveRecordId(input.id)
|
|
13763
|
+
});
|
|
13645
13764
|
};
|
|
13646
13765
|
/** The flat provider object to hand to `registerProvider`. */
|
|
13647
13766
|
toProvider() {
|
|
@@ -52299,12 +52418,12 @@ var deviceExportCapability = {
|
|
|
52299
52418
|
kind: "poll"
|
|
52300
52419
|
},
|
|
52301
52420
|
/**
|
|
52302
|
-
* Each export provider
|
|
52303
|
-
* (enabled toggle, preferred stream, addon-specific knobs
|
|
52304
|
-
*
|
|
52305
|
-
* `
|
|
52306
|
-
* `
|
|
52307
|
-
*
|
|
52421
|
+
* Each export provider MAY contribute its own per-device "Export" panel
|
|
52422
|
+
* (enabled toggle, preferred stream, addon-specific knobs). The three
|
|
52423
|
+
* framework methods are mounted on the cap router when present.
|
|
52424
|
+
* `IDeviceExportProvider` leaves them optional: an exporter with no
|
|
52425
|
+
* per-device panel omits them (a `deviceConfig` cap's UI is derived and
|
|
52426
|
+
* never hand-written — D14 — and an exporter is not one of those).
|
|
52308
52427
|
*/
|
|
52309
52428
|
exposesDeviceSettings: true,
|
|
52310
52429
|
methods: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-provider-homeassistant",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.662",
|
|
4
4
|
"description": "Home Assistant device provider addon for CamStack",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|
|
@@ -137,7 +137,7 @@
|
|
|
137
137
|
"description": "Device provider (import) + native entity export (push). Two addons, one bundle, separate processes."
|
|
138
138
|
},
|
|
139
139
|
"requires": {
|
|
140
|
-
"server": ">=1.2.
|
|
140
|
+
"server": ">=1.2.777"
|
|
141
141
|
}
|
|
142
142
|
},
|
|
143
143
|
"files": [
|