@camstack/addon-matter-broker 0.2.157 → 0.2.159
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/addon.js +245 -139
- package/dist/addon.mjs +246 -140
- package/dist/index.mjs +1 -1
- package/i18n/en.json +0 -3
- package/i18n/it.json +0 -3
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.159] - 2026-11-05
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- The integration form is declared as the instance form (the connection form); fabrics and their stored values are unchanged.
|
|
15
|
+
|
|
16
|
+
## [0.2.158] - 2026-11-05
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- The Matter manifest declares multiple instances, as the integration already allowed.
|
|
21
|
+
|
|
10
22
|
## [0.2.157] - 2026-11-05
|
|
11
23
|
|
|
12
24
|
### Changed
|
package/dist/addon.js
CHANGED
|
@@ -5995,7 +5995,7 @@ var ZodIssueCode = {
|
|
|
5995
5995
|
var ZodFirstPartyTypeKind;
|
|
5996
5996
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5997
5997
|
//#endregion
|
|
5998
|
-
//#region ../types/dist/sleep-
|
|
5998
|
+
//#region ../types/dist/sleep-DEZaLbiu.mjs
|
|
5999
5999
|
/**
|
|
6000
6000
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
6001
6001
|
* window to float samples (D455).
|
|
@@ -6804,6 +6804,12 @@ function projectStore(store, perNodeKeys, nodeId) {
|
|
|
6804
6804
|
}
|
|
6805
6805
|
return out;
|
|
6806
6806
|
}
|
|
6807
|
+
/** Every scope, in the order the guard and the docs list them. */
|
|
6808
|
+
var SCOPED_SETTINGS_SCOPES = [
|
|
6809
|
+
"cluster",
|
|
6810
|
+
"device-defaults",
|
|
6811
|
+
"node"
|
|
6812
|
+
];
|
|
6807
6813
|
/**
|
|
6808
6814
|
* The address of one keychain value: `secret://<owner>/<scope>/<fieldPath>`.
|
|
6809
6815
|
*
|
|
@@ -7695,6 +7701,13 @@ var BaseAddon = class {
|
|
|
7695
7701
|
*/
|
|
7696
7702
|
secretStatePaths = [];
|
|
7697
7703
|
_addonSecretTree = null;
|
|
7704
|
+
/**
|
|
7705
|
+
* D986 M4: the scoped schemas this addon declared, resolved once per capability
|
|
7706
|
+
* (`loadScopedSchemas`) so the sync readers (`perNodeKeys`, the secret tree, the restart check)
|
|
7707
|
+
* see them. Empty until loaded: the readers then fall back to {@link globalSettingsSchema}.
|
|
7708
|
+
*/
|
|
7709
|
+
_scopedByCap = /* @__PURE__ */ new Map();
|
|
7710
|
+
_nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
7698
7711
|
_deviceSecretTree = null;
|
|
7699
7712
|
/** Refs the last resolved config pointed at — a change to one re-resolves. */
|
|
7700
7713
|
_configSecretRefs = /* @__PURE__ */ new Set();
|
|
@@ -7764,6 +7777,7 @@ var BaseAddon = class {
|
|
|
7764
7777
|
this._ctx = context;
|
|
7765
7778
|
this._secretSettings = null;
|
|
7766
7779
|
}
|
|
7780
|
+
await this.loadScopedSchemas();
|
|
7767
7781
|
await this.resolveConfig();
|
|
7768
7782
|
if (secrets !== void 0) this._secretChangeUnsub = secrets.onChange((ref) => {
|
|
7769
7783
|
if (this._configSecretRefs.has(ref)) this.scheduleSecretReresolve(SECRET_CHANGE_DEBOUNCE_MS);
|
|
@@ -7773,6 +7787,7 @@ var BaseAddon = class {
|
|
|
7773
7787
|
const normalized = normalizeAddonInitResult(result);
|
|
7774
7788
|
const providers = normalized && "providers" in normalized && normalized.providers ? normalized.providers : [];
|
|
7775
7789
|
this._registeredCapNames = providers.map((p) => p.capability.name);
|
|
7790
|
+
await this.loadScopedSchemas();
|
|
7776
7791
|
return normalized;
|
|
7777
7792
|
}
|
|
7778
7793
|
/**
|
|
@@ -7861,6 +7876,16 @@ var BaseAddon = class {
|
|
|
7861
7876
|
schema(schema) {
|
|
7862
7877
|
return schema;
|
|
7863
7878
|
}
|
|
7879
|
+
/**
|
|
7880
|
+
* The capability names whose scoped settings schemas ({@link clusterSettingsSchema},
|
|
7881
|
+
* {@link nodeSettingsSchema}, {@link deviceDefaultsSchema}) this addon declares. Read BEFORE
|
|
7882
|
+
* `onInitialize`, when the providers it registers are not known yet: the first config resolve
|
|
7883
|
+
* must already know which keys are per node. Names the providers register at init are added
|
|
7884
|
+
* once known.
|
|
7885
|
+
*/
|
|
7886
|
+
scopedSettingsCapabilities() {
|
|
7887
|
+
return [];
|
|
7888
|
+
}
|
|
7864
7889
|
/** Override to provide global-level settings UI schema. */
|
|
7865
7890
|
globalSettingsSchema(_cap) {
|
|
7866
7891
|
return null;
|
|
@@ -7904,7 +7929,8 @@ var BaseAddon = class {
|
|
|
7904
7929
|
return [];
|
|
7905
7930
|
}
|
|
7906
7931
|
async getGlobalSettings(overlay, cap, nodeId) {
|
|
7907
|
-
|
|
7932
|
+
await this.loadScopedSchemas(cap);
|
|
7933
|
+
const schema = this.globalSettingsSchema(cap) ?? this.scopedFormSchema(cap);
|
|
7908
7934
|
if (!schema) return { sections: [] };
|
|
7909
7935
|
const projected = await this.readRawProjectedStore(nodeId, cap);
|
|
7910
7936
|
return hydrateSchema(schema, overlay ? await this.overlayKeepingMarkers(projected, overlay) : projected, { inlineSecretsAsViews: true });
|
|
@@ -8035,6 +8061,7 @@ var BaseAddon = class {
|
|
|
8035
8061
|
* resolved blob back is a no-op for every unchanged secret.
|
|
8036
8062
|
*/
|
|
8037
8063
|
async resolveGlobalStore(nodeId, cap) {
|
|
8064
|
+
await this.loadScopedSchemas(cap);
|
|
8038
8065
|
const raw = await this._ctx?.settings?.readAddonStore() ?? {};
|
|
8039
8066
|
return this.projectForNode(raw, nodeId, cap);
|
|
8040
8067
|
}
|
|
@@ -8045,6 +8072,7 @@ var BaseAddon = class {
|
|
|
8045
8072
|
}
|
|
8046
8073
|
/** The PROJECTED store with its markers — the form hydrate only (D798). */
|
|
8047
8074
|
async readRawProjectedStore(nodeId, cap) {
|
|
8075
|
+
await this.loadScopedSchemas(cap);
|
|
8048
8076
|
const raw = await this.rawSettings()?.readAddonStore() ?? {};
|
|
8049
8077
|
return this.projectForNode(raw, nodeId, cap);
|
|
8050
8078
|
}
|
|
@@ -8147,6 +8175,7 @@ var BaseAddon = class {
|
|
|
8147
8175
|
await this.applyGlobalPatch(patch, nodeId);
|
|
8148
8176
|
}
|
|
8149
8177
|
async applyGlobalPatch(barePatch, nodeId) {
|
|
8178
|
+
await this.loadScopedSchemas();
|
|
8150
8179
|
const keys = this.perNodeKeys();
|
|
8151
8180
|
const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
|
|
8152
8181
|
const target = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
|
|
@@ -8160,7 +8189,8 @@ var BaseAddon = class {
|
|
|
8160
8189
|
await this.resolveConfig();
|
|
8161
8190
|
await this.onConfigChanged();
|
|
8162
8191
|
this.emitLifecycle(EventCategory.AddonUpdated, { level: "global" });
|
|
8163
|
-
|
|
8192
|
+
const legacy = this.globalSettingsSchema();
|
|
8193
|
+
this.maybeAutoRestart(Object.keys(barePatch), [...legacy === null ? [] : [legacy], ...this.loadedScopedSchemas().map((entry) => entry.schema)]);
|
|
8164
8194
|
}
|
|
8165
8195
|
/**
|
|
8166
8196
|
* The set of field keys the global settings schema declares `perNode: true`
|
|
@@ -8168,14 +8198,67 @@ var BaseAddon = class {
|
|
|
8168
8198
|
* declarations). Empty set ⇒ every per-node code path is bypassed and the
|
|
8169
8199
|
* settings API behaves exactly like the legacy node-agnostic one.
|
|
8170
8200
|
*/
|
|
8171
|
-
|
|
8201
|
+
/**
|
|
8202
|
+
* Load the scoped schemas of every capability this addon declares (the override and the
|
|
8203
|
+
* providers registered at init, plus `extraCap`) that is not loaded yet, then drop what was
|
|
8204
|
+
* derived from the previous set. Idempotent.
|
|
8205
|
+
*/
|
|
8206
|
+
async loadScopedSchemas(extraCap) {
|
|
8207
|
+
const caps = new Set([
|
|
8208
|
+
...this.scopedSettingsCapabilities(),
|
|
8209
|
+
...this._registeredCapNames,
|
|
8210
|
+
...extraCap === void 0 ? [] : [extraCap]
|
|
8211
|
+
]);
|
|
8212
|
+
let added = false;
|
|
8213
|
+
for (const cap of caps) {
|
|
8214
|
+
if (this._scopedByCap.has(cap)) continue;
|
|
8215
|
+
const loaded = [];
|
|
8216
|
+
for (const scope of SCOPED_SETTINGS_SCOPES) {
|
|
8217
|
+
const schema = await this.getScopedSettingsSchema(scope, cap);
|
|
8218
|
+
if (schema !== null) loaded.push({
|
|
8219
|
+
scope,
|
|
8220
|
+
schema
|
|
8221
|
+
});
|
|
8222
|
+
}
|
|
8223
|
+
this._scopedByCap.set(cap, loaded);
|
|
8224
|
+
added = true;
|
|
8225
|
+
}
|
|
8226
|
+
if (!added) return;
|
|
8227
|
+
this._nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
8228
|
+
this._addonSecretTree = null;
|
|
8229
|
+
}
|
|
8230
|
+
/**
|
|
8231
|
+
* The form the settings API serves while an addon declares no {@link globalSettingsSchema}: the
|
|
8232
|
+
* sections of its scoped schemas, so dropping the old hook does not blank the form.
|
|
8233
|
+
*/
|
|
8234
|
+
scopedFormSchema(cap) {
|
|
8235
|
+
const byId = /* @__PURE__ */ new Map();
|
|
8236
|
+
for (const entry of this.loadedScopedSchemas(cap)) for (const section of entry.schema.sections) if (!byId.has(section.id)) byId.set(section.id, section);
|
|
8237
|
+
return byId.size === 0 ? null : { sections: [...byId.values()] };
|
|
8238
|
+
}
|
|
8239
|
+
/** Every loaded scoped schema of `cap`, or of every loaded capability. */
|
|
8240
|
+
loadedScopedSchemas(cap) {
|
|
8241
|
+
if (cap !== void 0) return this._scopedByCap.get(cap) ?? [];
|
|
8242
|
+
return [...this._scopedByCap.values()].flat();
|
|
8243
|
+
}
|
|
8244
|
+
/**
|
|
8245
|
+
* The field keys that are stored per node: every field of a {@link nodeSettingsSchema}, plus
|
|
8246
|
+
* the fields the legacy {@link globalSettingsSchema} still marks `perNode: true`. Derived once
|
|
8247
|
+
* per `cap` argument and memoized (schemas are static declarations). Empty set ⇒ every
|
|
8248
|
+
* per-node code path is bypassed and the settings API behaves like the node-agnostic one.
|
|
8249
|
+
* Call {@link loadScopedSchemas} first (the async seams do).
|
|
8250
|
+
*/
|
|
8172
8251
|
perNodeKeys(cap) {
|
|
8173
8252
|
const cacheKey = cap ?? "";
|
|
8174
|
-
const cached = this.
|
|
8253
|
+
const cached = this._nodeKeysByCap.get(cacheKey);
|
|
8175
8254
|
if (cached) return cached;
|
|
8176
|
-
const
|
|
8177
|
-
const keys = new Set(
|
|
8178
|
-
this.
|
|
8255
|
+
const legacy = this.globalSettingsSchema(cap);
|
|
8256
|
+
const keys = new Set(legacy ? legacy.sections.flatMap((section) => collectPerNodeFieldKeys(section.fields)) : []);
|
|
8257
|
+
for (const { scope, schema } of this.loadedScopedSchemas(cap)) {
|
|
8258
|
+
if (scope !== "node") continue;
|
|
8259
|
+
for (const section of schema.sections) for (const key of collectValueFieldKeys(section.fields)) keys.add(key);
|
|
8260
|
+
}
|
|
8261
|
+
this._nodeKeysByCap.set(cacheKey, keys);
|
|
8179
8262
|
return keys;
|
|
8180
8263
|
}
|
|
8181
8264
|
/**
|
|
@@ -8185,10 +8268,9 @@ var BaseAddon = class {
|
|
|
8185
8268
|
* time to return its response before the addon is torn down and
|
|
8186
8269
|
* re-initialised by `AddonRegistryService.restartAddon`.
|
|
8187
8270
|
*/
|
|
8188
|
-
maybeAutoRestart(patchKeys,
|
|
8189
|
-
if (!schema) return;
|
|
8271
|
+
maybeAutoRestart(patchKeys, schemas) {
|
|
8190
8272
|
const restartKeys = /* @__PURE__ */ new Set();
|
|
8191
|
-
for (const section of schema.sections) for (const field of section.fields) {
|
|
8273
|
+
for (const schema of schemas) for (const section of schema.sections) for (const field of section.fields) {
|
|
8192
8274
|
if (field.type === "separator" || field.type === "info") continue;
|
|
8193
8275
|
if (field.requiresRestart) restartKeys.add(field.key);
|
|
8194
8276
|
}
|
|
@@ -8384,12 +8466,14 @@ var BaseAddon = class {
|
|
|
8384
8466
|
* here, in the addon's own process, so the hub keeps no copy of it.
|
|
8385
8467
|
*/
|
|
8386
8468
|
async getDeclaredSettingsKeys() {
|
|
8469
|
+
await this.loadScopedSchemas();
|
|
8387
8470
|
return { keys: [...this.declaredKeySet()].toSorted() };
|
|
8388
8471
|
}
|
|
8389
8472
|
declaredKeySet() {
|
|
8390
8473
|
const keys = new Set(Object.keys(this.defaults));
|
|
8391
8474
|
const schema = this.globalSettingsSchema();
|
|
8392
8475
|
if (schema) for (const section of schema.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
8476
|
+
for (const { schema: scoped } of this.loadedScopedSchemas()) for (const section of scoped.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
8393
8477
|
for (const key of this._declaredStateKeys) keys.add(key);
|
|
8394
8478
|
for (const path of this.secretStatePaths) {
|
|
8395
8479
|
const root = path.split(/[.[]/, 1)[0];
|
|
@@ -8485,7 +8569,11 @@ var BaseAddon = class {
|
|
|
8485
8569
|
}
|
|
8486
8570
|
/** The addon store's secret tree: schema fields by TYPE, plus `secretStatePaths`. */
|
|
8487
8571
|
addonSecretTree() {
|
|
8488
|
-
this._addonSecretTree
|
|
8572
|
+
if (this._addonSecretTree === null) {
|
|
8573
|
+
const tree = new Map(secretTreeFromSchema(this.globalSettingsSchema()));
|
|
8574
|
+
for (const { schema } of this.loadedScopedSchemas()) for (const [key, node] of secretTreeFromSchema(schema)) tree.set(key, node);
|
|
8575
|
+
this._addonSecretTree = mergeSecretStatePaths(tree, this.secretStatePaths);
|
|
8576
|
+
}
|
|
8489
8577
|
return this._addonSecretTree;
|
|
8490
8578
|
}
|
|
8491
8579
|
deviceSecretTree() {
|
|
@@ -8735,6 +8823,23 @@ function dropPerNodeFields(fields) {
|
|
|
8735
8823
|
}
|
|
8736
8824
|
return kept;
|
|
8737
8825
|
}
|
|
8826
|
+
/** The keys of the value-holding fields, recursing into layout containers. */
|
|
8827
|
+
function collectValueFieldKeys(fields) {
|
|
8828
|
+
const collected = [];
|
|
8829
|
+
for (const field of fields) {
|
|
8830
|
+
if (field.type === "group") {
|
|
8831
|
+
collected.push(...collectValueFieldKeys(field.fields));
|
|
8832
|
+
continue;
|
|
8833
|
+
}
|
|
8834
|
+
if (field.type === "sub-tabs") {
|
|
8835
|
+
for (const tab of field.tabs) collected.push(...collectValueFieldKeys(tab.fields));
|
|
8836
|
+
continue;
|
|
8837
|
+
}
|
|
8838
|
+
if (field.type === "separator" || field.type === "info") continue;
|
|
8839
|
+
if ("key" in field && typeof field.key === "string") collected.push(field.key);
|
|
8840
|
+
}
|
|
8841
|
+
return collected;
|
|
8842
|
+
}
|
|
8738
8843
|
function collectFieldKeys(fields) {
|
|
8739
8844
|
const collected = [];
|
|
8740
8845
|
for (const field of fields) {
|
|
@@ -13403,12 +13508,22 @@ var RecordIntegrationInstances = class {
|
|
|
13403
13508
|
...this.hooks.description,
|
|
13404
13509
|
mintsId: true
|
|
13405
13510
|
};
|
|
13406
|
-
|
|
13511
|
+
/**
|
|
13512
|
+
* The record an id names. A caller holding the `integrations` registry id (the Integrations
|
|
13513
|
+
* page) reaches a kind whose body links to it through `integrationId`; an id that is itself a
|
|
13514
|
+
* record id answers as it is. An id that names nothing comes back unchanged.
|
|
13515
|
+
*/
|
|
13516
|
+
async resolveRecordId(id) {
|
|
13517
|
+
if (await this.handle.get(id) !== null) return id;
|
|
13518
|
+
const linked = (await this.handle.query({ limit: INSTANCES_LIST_MAX })).records.find((record) => record.data["integrationId"] === id);
|
|
13519
|
+
return linked === void 0 ? id : linked.id;
|
|
13520
|
+
}
|
|
13521
|
+
instanceFormSchema = async ({ id }) => this.hooks.formSchema(id === null ? null : await this.resolveRecordId(id));
|
|
13407
13522
|
listInstances = async () => {
|
|
13408
13523
|
return (await this.handle.query({ limit: INSTANCES_LIST_MAX })).records.map((record) => this.viewOf(record));
|
|
13409
13524
|
};
|
|
13410
13525
|
getInstance = async ({ id }) => {
|
|
13411
|
-
const record = await this.handle.get(id);
|
|
13526
|
+
const record = await this.handle.get(await this.resolveRecordId(id));
|
|
13412
13527
|
return record === null ? null : this.viewOf(record);
|
|
13413
13528
|
};
|
|
13414
13529
|
/** Insert ONE row (`ifRev: 0`: an existing id is refused, never overwritten). */
|
|
@@ -13425,7 +13540,8 @@ var RecordIntegrationInstances = class {
|
|
|
13425
13540
|
await this.hooks.onChanged?.(minted, "created");
|
|
13426
13541
|
return { id: minted };
|
|
13427
13542
|
};
|
|
13428
|
-
updateInstance = async ({ id, name, values }) => {
|
|
13543
|
+
updateInstance = async ({ id: askedId, name, values }) => {
|
|
13544
|
+
const id = await this.resolveRecordId(askedId);
|
|
13429
13545
|
const patch = {};
|
|
13430
13546
|
for (const [key, value] of Object.entries(values)) {
|
|
13431
13547
|
if (value === void 0) continue;
|
|
@@ -13460,7 +13576,10 @@ var RecordIntegrationInstances = class {
|
|
|
13460
13576
|
status: "unsupported",
|
|
13461
13577
|
error: "This integration declares no connection test"
|
|
13462
13578
|
};
|
|
13463
|
-
return this.hooks.test(input
|
|
13579
|
+
return this.hooks.test(input.id === null ? input : {
|
|
13580
|
+
...input,
|
|
13581
|
+
id: await this.resolveRecordId(input.id)
|
|
13582
|
+
});
|
|
13464
13583
|
};
|
|
13465
13584
|
/** The flat provider object to hand to `registerProvider`. */
|
|
13466
13585
|
toProvider() {
|
|
@@ -97060,6 +97179,111 @@ object({
|
|
|
97060
97179
|
commands: record(string$2(), unknown())
|
|
97061
97180
|
});
|
|
97062
97181
|
//#endregion
|
|
97182
|
+
//#region src/config.ts
|
|
97183
|
+
/**
|
|
97184
|
+
* Matter broker connection settings for ONE fabric/controller (one broker entry).
|
|
97185
|
+
* v1 is intentionally minimal: optional Thread / Wi-Fi credentials supplied up
|
|
97186
|
+
* front for wireless commissioning. The pairing code itself is NOT stored here —
|
|
97187
|
+
* it is passed transiently to the commissioning action.
|
|
97188
|
+
*
|
|
97189
|
+
* The fabric credentials (root cert / NOC / node registry) live in the addon
|
|
97190
|
+
* `dataDir` via the controller's storage, NEVER in this typed KV.
|
|
97191
|
+
*/
|
|
97192
|
+
var matterConnectionSchema = object({
|
|
97193
|
+
/** Optional Thread operational dataset (hex) for Thread device onboarding. */
|
|
97194
|
+
threadDataset: string$2().default("").describe("Thread operational dataset (hex) — required only for Thread devices").meta({ secret: true }),
|
|
97195
|
+
/** Optional Wi-Fi SSID for Wi-Fi device onboarding over BLE. */
|
|
97196
|
+
wifiSsid: string$2().default("").describe("Wi-Fi SSID — required only for BLE Wi-Fi onboarding"),
|
|
97197
|
+
/** Optional Wi-Fi password for Wi-Fi device onboarding over BLE. */
|
|
97198
|
+
wifiPassword: string$2().default("").describe("Wi-Fi password (optional)").meta({ secret: true }),
|
|
97199
|
+
/** PULL-reconcile interval for the commissioned-node inventory. */
|
|
97200
|
+
reconcileIntervalMs: preprocess((v) => v === "" || v === null ? void 0 : v, number().int().min(1e4).max(864e5).default(3e5)).describe("Node-inventory reconcile interval (ms)")
|
|
97201
|
+
});
|
|
97202
|
+
/** A single registered Matter fabric ("broker"). */
|
|
97203
|
+
var MatterBrokerEntrySchema = object({
|
|
97204
|
+
id: string$2().min(1),
|
|
97205
|
+
name: string$2().min(1),
|
|
97206
|
+
connection: matterConnectionSchema,
|
|
97207
|
+
integrationId: string$2().optional(),
|
|
97208
|
+
/** Absent means on; the instances page switches a fabric off without deleting it. */
|
|
97209
|
+
enabled: boolean().optional()
|
|
97210
|
+
});
|
|
97211
|
+
/** Top-level addon config — the fabrics are records, so nothing is left here. */
|
|
97212
|
+
var matterAddonConfigSchema = object({});
|
|
97213
|
+
/** Narrow an `unknown` to a shallow plain-object record (cast-free boundary). */
|
|
97214
|
+
function toRecord(value) {
|
|
97215
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) return Object.fromEntries(Object.entries(value));
|
|
97216
|
+
return {};
|
|
97217
|
+
}
|
|
97218
|
+
/** Coerce a loose settings blob through the connection schema, applying defaults. */
|
|
97219
|
+
function settingsToMatterConnection(settings) {
|
|
97220
|
+
return matterConnectionSchema.parse(toRecord(settings));
|
|
97221
|
+
}
|
|
97222
|
+
/**
|
|
97223
|
+
* Settings readers see an empty string for both secrets. The stored value
|
|
97224
|
+
* stays in the keychain (records `secret` meta). An empty round-trip must
|
|
97225
|
+
* not wipe it — see {@link mergeMatterSecrets}.
|
|
97226
|
+
*/
|
|
97227
|
+
function publicMatterConnection(connection) {
|
|
97228
|
+
return {
|
|
97229
|
+
...connection,
|
|
97230
|
+
wifiPassword: "",
|
|
97231
|
+
threadDataset: ""
|
|
97232
|
+
};
|
|
97233
|
+
}
|
|
97234
|
+
/** Keep a stored secret when the form sent back the redacted empty string. */
|
|
97235
|
+
function mergeMatterSecrets(existing, incoming) {
|
|
97236
|
+
return {
|
|
97237
|
+
...incoming,
|
|
97238
|
+
wifiPassword: incoming.wifiPassword.length > 0 ? incoming.wifiPassword : existing.wifiPassword,
|
|
97239
|
+
threadDataset: incoming.threadDataset.length > 0 ? incoming.threadDataset : existing.threadDataset
|
|
97240
|
+
};
|
|
97241
|
+
}
|
|
97242
|
+
/** Hand-written connection form for the broker/integration creation UI. */
|
|
97243
|
+
function buildConnectionFormSchema() {
|
|
97244
|
+
return { sections: [{
|
|
97245
|
+
id: "matter-fabric",
|
|
97246
|
+
title: "Matter fabric",
|
|
97247
|
+
titleKey: "matter-broker:connection.s_matter-fabric.title",
|
|
97248
|
+
description: "CamStack owns a Matter fabric on this hub. Add wireless onboarding credentials only if you will commission Thread or BLE-Wi-Fi devices. On-network (Ethernet/Wi-Fi) Matter devices need none of these.",
|
|
97249
|
+
descriptionKey: "matter-broker:connection.s_matter-fabric.description",
|
|
97250
|
+
columns: 1,
|
|
97251
|
+
fields: [
|
|
97252
|
+
{
|
|
97253
|
+
type: "password",
|
|
97254
|
+
key: "threadDataset",
|
|
97255
|
+
secret: true,
|
|
97256
|
+
showToggle: true,
|
|
97257
|
+
label: "Thread operational dataset (hex, optional)",
|
|
97258
|
+
labelKey: "matter-broker:connection.s_matter-fabric.threadDataset.label",
|
|
97259
|
+
placeholder: "0e08000000000001…"
|
|
97260
|
+
},
|
|
97261
|
+
{
|
|
97262
|
+
type: "text",
|
|
97263
|
+
key: "wifiSsid",
|
|
97264
|
+
label: "Wi-Fi SSID (optional)",
|
|
97265
|
+
labelKey: "matter-broker:connection.s_matter-fabric.wifiSsid.label"
|
|
97266
|
+
},
|
|
97267
|
+
{
|
|
97268
|
+
type: "password",
|
|
97269
|
+
key: "wifiPassword",
|
|
97270
|
+
label: "Wi-Fi password (optional)",
|
|
97271
|
+
labelKey: "matter-broker:connection.s_matter-fabric.wifiPassword.label",
|
|
97272
|
+
showToggle: true
|
|
97273
|
+
},
|
|
97274
|
+
{
|
|
97275
|
+
type: "number",
|
|
97276
|
+
key: "reconcileIntervalMs",
|
|
97277
|
+
label: "Node reconcile interval (ms)",
|
|
97278
|
+
labelKey: "matter-broker:connection.s_matter-fabric.reconcileIntervalMs.label",
|
|
97279
|
+
min: 1e4,
|
|
97280
|
+
max: 864e5,
|
|
97281
|
+
default: 3e5
|
|
97282
|
+
}
|
|
97283
|
+
]
|
|
97284
|
+
}] };
|
|
97285
|
+
}
|
|
97286
|
+
//#endregion
|
|
97063
97287
|
//#region ../../node_modules/@matter/general/dist/esm/net/tcp/TcpConnection.js
|
|
97064
97288
|
/**
|
|
97065
97289
|
* @license
|
|
@@ -207386,8 +207610,8 @@ function sameConnection(a, b) {
|
|
|
207386
207610
|
* Manages N live Matter fabric connections ("brokers"), each backed by a
|
|
207387
207611
|
* {@link MatterIntegrationManager}. Allocates stable ids (`matter_001`, …), CRUD,
|
|
207388
207612
|
* an integration FK index, lifecycle helpers. Mirrors the Ecowitt / Dreame
|
|
207389
|
-
* registry.
|
|
207390
|
-
* fabric,
|
|
207613
|
+
* registry. The integration is `instanceMode: multiple`: the common case is one
|
|
207614
|
+
* fabric, and an operator may add more.
|
|
207391
207615
|
*/
|
|
207392
207616
|
var MatterBrokerRegistry = class {
|
|
207393
207617
|
#logger;
|
|
@@ -207575,111 +207799,6 @@ var MatterBrokerRegistry = class {
|
|
|
207575
207799
|
}
|
|
207576
207800
|
};
|
|
207577
207801
|
//#endregion
|
|
207578
|
-
//#region src/config.ts
|
|
207579
|
-
/**
|
|
207580
|
-
* Matter broker connection settings for ONE fabric/controller (one broker entry).
|
|
207581
|
-
* v1 is intentionally minimal: optional Thread / Wi-Fi credentials supplied up
|
|
207582
|
-
* front for wireless commissioning. The pairing code itself is NOT stored here —
|
|
207583
|
-
* it is passed transiently to the commissioning action.
|
|
207584
|
-
*
|
|
207585
|
-
* The fabric credentials (root cert / NOC / node registry) live in the addon
|
|
207586
|
-
* `dataDir` via the controller's storage, NEVER in this typed KV.
|
|
207587
|
-
*/
|
|
207588
|
-
var matterConnectionSchema = object({
|
|
207589
|
-
/** Optional Thread operational dataset (hex) for Thread device onboarding. */
|
|
207590
|
-
threadDataset: string$2().default("").describe("Thread operational dataset (hex) — required only for Thread devices").meta({ secret: true }),
|
|
207591
|
-
/** Optional Wi-Fi SSID for Wi-Fi device onboarding over BLE. */
|
|
207592
|
-
wifiSsid: string$2().default("").describe("Wi-Fi SSID — required only for BLE Wi-Fi onboarding"),
|
|
207593
|
-
/** Optional Wi-Fi password for Wi-Fi device onboarding over BLE. */
|
|
207594
|
-
wifiPassword: string$2().default("").describe("Wi-Fi password (optional)").meta({ secret: true }),
|
|
207595
|
-
/** PULL-reconcile interval for the commissioned-node inventory. */
|
|
207596
|
-
reconcileIntervalMs: preprocess((v) => v === "" || v === null ? void 0 : v, number().int().min(1e4).max(864e5).default(3e5)).describe("Node-inventory reconcile interval (ms)")
|
|
207597
|
-
});
|
|
207598
|
-
/** A single registered Matter fabric ("broker"). */
|
|
207599
|
-
var MatterBrokerEntrySchema = object({
|
|
207600
|
-
id: string$2().min(1),
|
|
207601
|
-
name: string$2().min(1),
|
|
207602
|
-
connection: matterConnectionSchema,
|
|
207603
|
-
integrationId: string$2().optional(),
|
|
207604
|
-
/** Absent means on; the instances page switches a fabric off without deleting it. */
|
|
207605
|
-
enabled: boolean().optional()
|
|
207606
|
-
});
|
|
207607
|
-
/** Top-level addon config — the fabrics are records, so nothing is left here. */
|
|
207608
|
-
var matterAddonConfigSchema = object({});
|
|
207609
|
-
/** Narrow an `unknown` to a shallow plain-object record (cast-free boundary). */
|
|
207610
|
-
function toRecord(value) {
|
|
207611
|
-
if (typeof value === "object" && value !== null && !Array.isArray(value)) return Object.fromEntries(Object.entries(value));
|
|
207612
|
-
return {};
|
|
207613
|
-
}
|
|
207614
|
-
/** Coerce a loose settings blob through the connection schema, applying defaults. */
|
|
207615
|
-
function settingsToMatterConnection(settings) {
|
|
207616
|
-
return matterConnectionSchema.parse(toRecord(settings));
|
|
207617
|
-
}
|
|
207618
|
-
/**
|
|
207619
|
-
* Settings readers see an empty string for both secrets. The stored value
|
|
207620
|
-
* stays in the keychain (records `secret` meta). An empty round-trip must
|
|
207621
|
-
* not wipe it — see {@link mergeMatterSecrets}.
|
|
207622
|
-
*/
|
|
207623
|
-
function publicMatterConnection(connection) {
|
|
207624
|
-
return {
|
|
207625
|
-
...connection,
|
|
207626
|
-
wifiPassword: "",
|
|
207627
|
-
threadDataset: ""
|
|
207628
|
-
};
|
|
207629
|
-
}
|
|
207630
|
-
/** Keep a stored secret when the form sent back the redacted empty string. */
|
|
207631
|
-
function mergeMatterSecrets(existing, incoming) {
|
|
207632
|
-
return {
|
|
207633
|
-
...incoming,
|
|
207634
|
-
wifiPassword: incoming.wifiPassword.length > 0 ? incoming.wifiPassword : existing.wifiPassword,
|
|
207635
|
-
threadDataset: incoming.threadDataset.length > 0 ? incoming.threadDataset : existing.threadDataset
|
|
207636
|
-
};
|
|
207637
|
-
}
|
|
207638
|
-
/** Hand-written connection form for the broker/integration creation UI. */
|
|
207639
|
-
function buildConnectionFormSchema() {
|
|
207640
|
-
return { sections: [{
|
|
207641
|
-
id: "matter-fabric",
|
|
207642
|
-
title: "Matter fabric",
|
|
207643
|
-
titleKey: "matter-broker:connection.s_matter-fabric.title",
|
|
207644
|
-
description: "CamStack owns a Matter fabric on this hub. Add wireless onboarding credentials only if you will commission Thread or BLE-Wi-Fi devices. On-network (Ethernet/Wi-Fi) Matter devices need none of these.",
|
|
207645
|
-
descriptionKey: "matter-broker:connection.s_matter-fabric.description",
|
|
207646
|
-
columns: 1,
|
|
207647
|
-
fields: [
|
|
207648
|
-
{
|
|
207649
|
-
type: "password",
|
|
207650
|
-
key: "threadDataset",
|
|
207651
|
-
secret: true,
|
|
207652
|
-
showToggle: true,
|
|
207653
|
-
label: "Thread operational dataset (hex, optional)",
|
|
207654
|
-
labelKey: "matter-broker:connection.s_matter-fabric.threadDataset.label",
|
|
207655
|
-
placeholder: "0e08000000000001…"
|
|
207656
|
-
},
|
|
207657
|
-
{
|
|
207658
|
-
type: "text",
|
|
207659
|
-
key: "wifiSsid",
|
|
207660
|
-
label: "Wi-Fi SSID (optional)",
|
|
207661
|
-
labelKey: "matter-broker:connection.s_matter-fabric.wifiSsid.label"
|
|
207662
|
-
},
|
|
207663
|
-
{
|
|
207664
|
-
type: "password",
|
|
207665
|
-
key: "wifiPassword",
|
|
207666
|
-
label: "Wi-Fi password (optional)",
|
|
207667
|
-
labelKey: "matter-broker:connection.s_matter-fabric.wifiPassword.label",
|
|
207668
|
-
showToggle: true
|
|
207669
|
-
},
|
|
207670
|
-
{
|
|
207671
|
-
type: "number",
|
|
207672
|
-
key: "reconcileIntervalMs",
|
|
207673
|
-
label: "Node reconcile interval (ms)",
|
|
207674
|
-
labelKey: "matter-broker:connection.s_matter-fabric.reconcileIntervalMs.label",
|
|
207675
|
-
min: 1e4,
|
|
207676
|
-
max: 864e5,
|
|
207677
|
-
default: 3e5
|
|
207678
|
-
}
|
|
207679
|
-
]
|
|
207680
|
-
}] };
|
|
207681
|
-
}
|
|
207682
|
-
//#endregion
|
|
207683
207802
|
//#region src/matter-commissioning.ts
|
|
207684
207803
|
/** A clear validation error for a malformed pairing code. */
|
|
207685
207804
|
var InvalidPairingCodeError = class extends Error {
|
|
@@ -209241,22 +209360,9 @@ var MatterBrokerAddon = class extends BaseDeviceProvider {
|
|
|
209241
209360
|
}
|
|
209242
209361
|
});
|
|
209243
209362
|
}
|
|
209244
|
-
|
|
209245
|
-
|
|
209246
|
-
|
|
209247
|
-
title: "Matter fabric",
|
|
209248
|
-
titleKey: "matter-broker:globalSettings.s_matter-fabric.title",
|
|
209249
|
-
description: "Matter fabrics are managed in the External systems → Brokers tab.",
|
|
209250
|
-
descriptionKey: "matter-broker:globalSettings.s_matter-fabric.description",
|
|
209251
|
-
columns: 1,
|
|
209252
|
-
fields: [{
|
|
209253
|
-
type: "info",
|
|
209254
|
-
key: "fabricHelp",
|
|
209255
|
-
label: "Fabrics are managed separately",
|
|
209256
|
-
labelKey: "matter-broker:globalSettings.s_matter-fabric.fabricHelp.label",
|
|
209257
|
-
content: "This integration links to a Matter fabric broker. Add, edit, or remove fabrics from External systems → Brokers, then commission devices into the fabric. The integration only stores a reference to its broker."
|
|
209258
|
-
}]
|
|
209259
|
-
}] });
|
|
209363
|
+
/** The connection form a record carries; the values live on the record, never in `addon-settings`. */
|
|
209364
|
+
async integrationFormSchema(_instance) {
|
|
209365
|
+
return buildConnectionFormSchema();
|
|
209260
209366
|
}
|
|
209261
209367
|
async supportsManualCreation() {
|
|
209262
209368
|
return false;
|
package/dist/addon.mjs
CHANGED
|
@@ -5993,7 +5993,7 @@ var ZodIssueCode = {
|
|
|
5993
5993
|
var ZodFirstPartyTypeKind;
|
|
5994
5994
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5995
5995
|
//#endregion
|
|
5996
|
-
//#region ../types/dist/sleep-
|
|
5996
|
+
//#region ../types/dist/sleep-DEZaLbiu.mjs
|
|
5997
5997
|
/**
|
|
5998
5998
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5999
5999
|
* window to float samples (D455).
|
|
@@ -6802,6 +6802,12 @@ function projectStore(store, perNodeKeys, nodeId) {
|
|
|
6802
6802
|
}
|
|
6803
6803
|
return out;
|
|
6804
6804
|
}
|
|
6805
|
+
/** Every scope, in the order the guard and the docs list them. */
|
|
6806
|
+
var SCOPED_SETTINGS_SCOPES = [
|
|
6807
|
+
"cluster",
|
|
6808
|
+
"device-defaults",
|
|
6809
|
+
"node"
|
|
6810
|
+
];
|
|
6805
6811
|
/**
|
|
6806
6812
|
* The address of one keychain value: `secret://<owner>/<scope>/<fieldPath>`.
|
|
6807
6813
|
*
|
|
@@ -7693,6 +7699,13 @@ var BaseAddon = class {
|
|
|
7693
7699
|
*/
|
|
7694
7700
|
secretStatePaths = [];
|
|
7695
7701
|
_addonSecretTree = null;
|
|
7702
|
+
/**
|
|
7703
|
+
* D986 M4: the scoped schemas this addon declared, resolved once per capability
|
|
7704
|
+
* (`loadScopedSchemas`) so the sync readers (`perNodeKeys`, the secret tree, the restart check)
|
|
7705
|
+
* see them. Empty until loaded: the readers then fall back to {@link globalSettingsSchema}.
|
|
7706
|
+
*/
|
|
7707
|
+
_scopedByCap = /* @__PURE__ */ new Map();
|
|
7708
|
+
_nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
7696
7709
|
_deviceSecretTree = null;
|
|
7697
7710
|
/** Refs the last resolved config pointed at — a change to one re-resolves. */
|
|
7698
7711
|
_configSecretRefs = /* @__PURE__ */ new Set();
|
|
@@ -7762,6 +7775,7 @@ var BaseAddon = class {
|
|
|
7762
7775
|
this._ctx = context;
|
|
7763
7776
|
this._secretSettings = null;
|
|
7764
7777
|
}
|
|
7778
|
+
await this.loadScopedSchemas();
|
|
7765
7779
|
await this.resolveConfig();
|
|
7766
7780
|
if (secrets !== void 0) this._secretChangeUnsub = secrets.onChange((ref) => {
|
|
7767
7781
|
if (this._configSecretRefs.has(ref)) this.scheduleSecretReresolve(SECRET_CHANGE_DEBOUNCE_MS);
|
|
@@ -7771,6 +7785,7 @@ var BaseAddon = class {
|
|
|
7771
7785
|
const normalized = normalizeAddonInitResult(result);
|
|
7772
7786
|
const providers = normalized && "providers" in normalized && normalized.providers ? normalized.providers : [];
|
|
7773
7787
|
this._registeredCapNames = providers.map((p) => p.capability.name);
|
|
7788
|
+
await this.loadScopedSchemas();
|
|
7774
7789
|
return normalized;
|
|
7775
7790
|
}
|
|
7776
7791
|
/**
|
|
@@ -7859,6 +7874,16 @@ var BaseAddon = class {
|
|
|
7859
7874
|
schema(schema) {
|
|
7860
7875
|
return schema;
|
|
7861
7876
|
}
|
|
7877
|
+
/**
|
|
7878
|
+
* The capability names whose scoped settings schemas ({@link clusterSettingsSchema},
|
|
7879
|
+
* {@link nodeSettingsSchema}, {@link deviceDefaultsSchema}) this addon declares. Read BEFORE
|
|
7880
|
+
* `onInitialize`, when the providers it registers are not known yet: the first config resolve
|
|
7881
|
+
* must already know which keys are per node. Names the providers register at init are added
|
|
7882
|
+
* once known.
|
|
7883
|
+
*/
|
|
7884
|
+
scopedSettingsCapabilities() {
|
|
7885
|
+
return [];
|
|
7886
|
+
}
|
|
7862
7887
|
/** Override to provide global-level settings UI schema. */
|
|
7863
7888
|
globalSettingsSchema(_cap) {
|
|
7864
7889
|
return null;
|
|
@@ -7902,7 +7927,8 @@ var BaseAddon = class {
|
|
|
7902
7927
|
return [];
|
|
7903
7928
|
}
|
|
7904
7929
|
async getGlobalSettings(overlay, cap, nodeId) {
|
|
7905
|
-
|
|
7930
|
+
await this.loadScopedSchemas(cap);
|
|
7931
|
+
const schema = this.globalSettingsSchema(cap) ?? this.scopedFormSchema(cap);
|
|
7906
7932
|
if (!schema) return { sections: [] };
|
|
7907
7933
|
const projected = await this.readRawProjectedStore(nodeId, cap);
|
|
7908
7934
|
return hydrateSchema(schema, overlay ? await this.overlayKeepingMarkers(projected, overlay) : projected, { inlineSecretsAsViews: true });
|
|
@@ -8033,6 +8059,7 @@ var BaseAddon = class {
|
|
|
8033
8059
|
* resolved blob back is a no-op for every unchanged secret.
|
|
8034
8060
|
*/
|
|
8035
8061
|
async resolveGlobalStore(nodeId, cap) {
|
|
8062
|
+
await this.loadScopedSchemas(cap);
|
|
8036
8063
|
const raw = await this._ctx?.settings?.readAddonStore() ?? {};
|
|
8037
8064
|
return this.projectForNode(raw, nodeId, cap);
|
|
8038
8065
|
}
|
|
@@ -8043,6 +8070,7 @@ var BaseAddon = class {
|
|
|
8043
8070
|
}
|
|
8044
8071
|
/** The PROJECTED store with its markers — the form hydrate only (D798). */
|
|
8045
8072
|
async readRawProjectedStore(nodeId, cap) {
|
|
8073
|
+
await this.loadScopedSchemas(cap);
|
|
8046
8074
|
const raw = await this.rawSettings()?.readAddonStore() ?? {};
|
|
8047
8075
|
return this.projectForNode(raw, nodeId, cap);
|
|
8048
8076
|
}
|
|
@@ -8145,6 +8173,7 @@ var BaseAddon = class {
|
|
|
8145
8173
|
await this.applyGlobalPatch(patch, nodeId);
|
|
8146
8174
|
}
|
|
8147
8175
|
async applyGlobalPatch(barePatch, nodeId) {
|
|
8176
|
+
await this.loadScopedSchemas();
|
|
8148
8177
|
const keys = this.perNodeKeys();
|
|
8149
8178
|
const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
|
|
8150
8179
|
const target = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
|
|
@@ -8158,7 +8187,8 @@ var BaseAddon = class {
|
|
|
8158
8187
|
await this.resolveConfig();
|
|
8159
8188
|
await this.onConfigChanged();
|
|
8160
8189
|
this.emitLifecycle(EventCategory.AddonUpdated, { level: "global" });
|
|
8161
|
-
|
|
8190
|
+
const legacy = this.globalSettingsSchema();
|
|
8191
|
+
this.maybeAutoRestart(Object.keys(barePatch), [...legacy === null ? [] : [legacy], ...this.loadedScopedSchemas().map((entry) => entry.schema)]);
|
|
8162
8192
|
}
|
|
8163
8193
|
/**
|
|
8164
8194
|
* The set of field keys the global settings schema declares `perNode: true`
|
|
@@ -8166,14 +8196,67 @@ var BaseAddon = class {
|
|
|
8166
8196
|
* declarations). Empty set ⇒ every per-node code path is bypassed and the
|
|
8167
8197
|
* settings API behaves exactly like the legacy node-agnostic one.
|
|
8168
8198
|
*/
|
|
8169
|
-
|
|
8199
|
+
/**
|
|
8200
|
+
* Load the scoped schemas of every capability this addon declares (the override and the
|
|
8201
|
+
* providers registered at init, plus `extraCap`) that is not loaded yet, then drop what was
|
|
8202
|
+
* derived from the previous set. Idempotent.
|
|
8203
|
+
*/
|
|
8204
|
+
async loadScopedSchemas(extraCap) {
|
|
8205
|
+
const caps = new Set([
|
|
8206
|
+
...this.scopedSettingsCapabilities(),
|
|
8207
|
+
...this._registeredCapNames,
|
|
8208
|
+
...extraCap === void 0 ? [] : [extraCap]
|
|
8209
|
+
]);
|
|
8210
|
+
let added = false;
|
|
8211
|
+
for (const cap of caps) {
|
|
8212
|
+
if (this._scopedByCap.has(cap)) continue;
|
|
8213
|
+
const loaded = [];
|
|
8214
|
+
for (const scope of SCOPED_SETTINGS_SCOPES) {
|
|
8215
|
+
const schema = await this.getScopedSettingsSchema(scope, cap);
|
|
8216
|
+
if (schema !== null) loaded.push({
|
|
8217
|
+
scope,
|
|
8218
|
+
schema
|
|
8219
|
+
});
|
|
8220
|
+
}
|
|
8221
|
+
this._scopedByCap.set(cap, loaded);
|
|
8222
|
+
added = true;
|
|
8223
|
+
}
|
|
8224
|
+
if (!added) return;
|
|
8225
|
+
this._nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
8226
|
+
this._addonSecretTree = null;
|
|
8227
|
+
}
|
|
8228
|
+
/**
|
|
8229
|
+
* The form the settings API serves while an addon declares no {@link globalSettingsSchema}: the
|
|
8230
|
+
* sections of its scoped schemas, so dropping the old hook does not blank the form.
|
|
8231
|
+
*/
|
|
8232
|
+
scopedFormSchema(cap) {
|
|
8233
|
+
const byId = /* @__PURE__ */ new Map();
|
|
8234
|
+
for (const entry of this.loadedScopedSchemas(cap)) for (const section of entry.schema.sections) if (!byId.has(section.id)) byId.set(section.id, section);
|
|
8235
|
+
return byId.size === 0 ? null : { sections: [...byId.values()] };
|
|
8236
|
+
}
|
|
8237
|
+
/** Every loaded scoped schema of `cap`, or of every loaded capability. */
|
|
8238
|
+
loadedScopedSchemas(cap) {
|
|
8239
|
+
if (cap !== void 0) return this._scopedByCap.get(cap) ?? [];
|
|
8240
|
+
return [...this._scopedByCap.values()].flat();
|
|
8241
|
+
}
|
|
8242
|
+
/**
|
|
8243
|
+
* The field keys that are stored per node: every field of a {@link nodeSettingsSchema}, plus
|
|
8244
|
+
* the fields the legacy {@link globalSettingsSchema} still marks `perNode: true`. Derived once
|
|
8245
|
+
* per `cap` argument and memoized (schemas are static declarations). Empty set ⇒ every
|
|
8246
|
+
* per-node code path is bypassed and the settings API behaves like the node-agnostic one.
|
|
8247
|
+
* Call {@link loadScopedSchemas} first (the async seams do).
|
|
8248
|
+
*/
|
|
8170
8249
|
perNodeKeys(cap) {
|
|
8171
8250
|
const cacheKey = cap ?? "";
|
|
8172
|
-
const cached = this.
|
|
8251
|
+
const cached = this._nodeKeysByCap.get(cacheKey);
|
|
8173
8252
|
if (cached) return cached;
|
|
8174
|
-
const
|
|
8175
|
-
const keys = new Set(
|
|
8176
|
-
this.
|
|
8253
|
+
const legacy = this.globalSettingsSchema(cap);
|
|
8254
|
+
const keys = new Set(legacy ? legacy.sections.flatMap((section) => collectPerNodeFieldKeys(section.fields)) : []);
|
|
8255
|
+
for (const { scope, schema } of this.loadedScopedSchemas(cap)) {
|
|
8256
|
+
if (scope !== "node") continue;
|
|
8257
|
+
for (const section of schema.sections) for (const key of collectValueFieldKeys(section.fields)) keys.add(key);
|
|
8258
|
+
}
|
|
8259
|
+
this._nodeKeysByCap.set(cacheKey, keys);
|
|
8177
8260
|
return keys;
|
|
8178
8261
|
}
|
|
8179
8262
|
/**
|
|
@@ -8183,10 +8266,9 @@ var BaseAddon = class {
|
|
|
8183
8266
|
* time to return its response before the addon is torn down and
|
|
8184
8267
|
* re-initialised by `AddonRegistryService.restartAddon`.
|
|
8185
8268
|
*/
|
|
8186
|
-
maybeAutoRestart(patchKeys,
|
|
8187
|
-
if (!schema) return;
|
|
8269
|
+
maybeAutoRestart(patchKeys, schemas) {
|
|
8188
8270
|
const restartKeys = /* @__PURE__ */ new Set();
|
|
8189
|
-
for (const section of schema.sections) for (const field of section.fields) {
|
|
8271
|
+
for (const schema of schemas) for (const section of schema.sections) for (const field of section.fields) {
|
|
8190
8272
|
if (field.type === "separator" || field.type === "info") continue;
|
|
8191
8273
|
if (field.requiresRestart) restartKeys.add(field.key);
|
|
8192
8274
|
}
|
|
@@ -8382,12 +8464,14 @@ var BaseAddon = class {
|
|
|
8382
8464
|
* here, in the addon's own process, so the hub keeps no copy of it.
|
|
8383
8465
|
*/
|
|
8384
8466
|
async getDeclaredSettingsKeys() {
|
|
8467
|
+
await this.loadScopedSchemas();
|
|
8385
8468
|
return { keys: [...this.declaredKeySet()].toSorted() };
|
|
8386
8469
|
}
|
|
8387
8470
|
declaredKeySet() {
|
|
8388
8471
|
const keys = new Set(Object.keys(this.defaults));
|
|
8389
8472
|
const schema = this.globalSettingsSchema();
|
|
8390
8473
|
if (schema) for (const section of schema.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
8474
|
+
for (const { schema: scoped } of this.loadedScopedSchemas()) for (const section of scoped.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
8391
8475
|
for (const key of this._declaredStateKeys) keys.add(key);
|
|
8392
8476
|
for (const path of this.secretStatePaths) {
|
|
8393
8477
|
const root = path.split(/[.[]/, 1)[0];
|
|
@@ -8483,7 +8567,11 @@ var BaseAddon = class {
|
|
|
8483
8567
|
}
|
|
8484
8568
|
/** The addon store's secret tree: schema fields by TYPE, plus `secretStatePaths`. */
|
|
8485
8569
|
addonSecretTree() {
|
|
8486
|
-
this._addonSecretTree
|
|
8570
|
+
if (this._addonSecretTree === null) {
|
|
8571
|
+
const tree = new Map(secretTreeFromSchema(this.globalSettingsSchema()));
|
|
8572
|
+
for (const { schema } of this.loadedScopedSchemas()) for (const [key, node] of secretTreeFromSchema(schema)) tree.set(key, node);
|
|
8573
|
+
this._addonSecretTree = mergeSecretStatePaths(tree, this.secretStatePaths);
|
|
8574
|
+
}
|
|
8487
8575
|
return this._addonSecretTree;
|
|
8488
8576
|
}
|
|
8489
8577
|
deviceSecretTree() {
|
|
@@ -8733,6 +8821,23 @@ function dropPerNodeFields(fields) {
|
|
|
8733
8821
|
}
|
|
8734
8822
|
return kept;
|
|
8735
8823
|
}
|
|
8824
|
+
/** The keys of the value-holding fields, recursing into layout containers. */
|
|
8825
|
+
function collectValueFieldKeys(fields) {
|
|
8826
|
+
const collected = [];
|
|
8827
|
+
for (const field of fields) {
|
|
8828
|
+
if (field.type === "group") {
|
|
8829
|
+
collected.push(...collectValueFieldKeys(field.fields));
|
|
8830
|
+
continue;
|
|
8831
|
+
}
|
|
8832
|
+
if (field.type === "sub-tabs") {
|
|
8833
|
+
for (const tab of field.tabs) collected.push(...collectValueFieldKeys(tab.fields));
|
|
8834
|
+
continue;
|
|
8835
|
+
}
|
|
8836
|
+
if (field.type === "separator" || field.type === "info") continue;
|
|
8837
|
+
if ("key" in field && typeof field.key === "string") collected.push(field.key);
|
|
8838
|
+
}
|
|
8839
|
+
return collected;
|
|
8840
|
+
}
|
|
8736
8841
|
function collectFieldKeys(fields) {
|
|
8737
8842
|
const collected = [];
|
|
8738
8843
|
for (const field of fields) {
|
|
@@ -13401,12 +13506,22 @@ var RecordIntegrationInstances = class {
|
|
|
13401
13506
|
...this.hooks.description,
|
|
13402
13507
|
mintsId: true
|
|
13403
13508
|
};
|
|
13404
|
-
|
|
13509
|
+
/**
|
|
13510
|
+
* The record an id names. A caller holding the `integrations` registry id (the Integrations
|
|
13511
|
+
* page) reaches a kind whose body links to it through `integrationId`; an id that is itself a
|
|
13512
|
+
* record id answers as it is. An id that names nothing comes back unchanged.
|
|
13513
|
+
*/
|
|
13514
|
+
async resolveRecordId(id) {
|
|
13515
|
+
if (await this.handle.get(id) !== null) return id;
|
|
13516
|
+
const linked = (await this.handle.query({ limit: INSTANCES_LIST_MAX })).records.find((record) => record.data["integrationId"] === id);
|
|
13517
|
+
return linked === void 0 ? id : linked.id;
|
|
13518
|
+
}
|
|
13519
|
+
instanceFormSchema = async ({ id }) => this.hooks.formSchema(id === null ? null : await this.resolveRecordId(id));
|
|
13405
13520
|
listInstances = async () => {
|
|
13406
13521
|
return (await this.handle.query({ limit: INSTANCES_LIST_MAX })).records.map((record) => this.viewOf(record));
|
|
13407
13522
|
};
|
|
13408
13523
|
getInstance = async ({ id }) => {
|
|
13409
|
-
const record = await this.handle.get(id);
|
|
13524
|
+
const record = await this.handle.get(await this.resolveRecordId(id));
|
|
13410
13525
|
return record === null ? null : this.viewOf(record);
|
|
13411
13526
|
};
|
|
13412
13527
|
/** Insert ONE row (`ifRev: 0`: an existing id is refused, never overwritten). */
|
|
@@ -13423,7 +13538,8 @@ var RecordIntegrationInstances = class {
|
|
|
13423
13538
|
await this.hooks.onChanged?.(minted, "created");
|
|
13424
13539
|
return { id: minted };
|
|
13425
13540
|
};
|
|
13426
|
-
updateInstance = async ({ id, name, values }) => {
|
|
13541
|
+
updateInstance = async ({ id: askedId, name, values }) => {
|
|
13542
|
+
const id = await this.resolveRecordId(askedId);
|
|
13427
13543
|
const patch = {};
|
|
13428
13544
|
for (const [key, value] of Object.entries(values)) {
|
|
13429
13545
|
if (value === void 0) continue;
|
|
@@ -13458,7 +13574,10 @@ var RecordIntegrationInstances = class {
|
|
|
13458
13574
|
status: "unsupported",
|
|
13459
13575
|
error: "This integration declares no connection test"
|
|
13460
13576
|
};
|
|
13461
|
-
return this.hooks.test(input
|
|
13577
|
+
return this.hooks.test(input.id === null ? input : {
|
|
13578
|
+
...input,
|
|
13579
|
+
id: await this.resolveRecordId(input.id)
|
|
13580
|
+
});
|
|
13462
13581
|
};
|
|
13463
13582
|
/** The flat provider object to hand to `registerProvider`. */
|
|
13464
13583
|
toProvider() {
|
|
@@ -97058,6 +97177,111 @@ object({
|
|
|
97058
97177
|
commands: record(string$2(), unknown())
|
|
97059
97178
|
});
|
|
97060
97179
|
//#endregion
|
|
97180
|
+
//#region src/config.ts
|
|
97181
|
+
/**
|
|
97182
|
+
* Matter broker connection settings for ONE fabric/controller (one broker entry).
|
|
97183
|
+
* v1 is intentionally minimal: optional Thread / Wi-Fi credentials supplied up
|
|
97184
|
+
* front for wireless commissioning. The pairing code itself is NOT stored here —
|
|
97185
|
+
* it is passed transiently to the commissioning action.
|
|
97186
|
+
*
|
|
97187
|
+
* The fabric credentials (root cert / NOC / node registry) live in the addon
|
|
97188
|
+
* `dataDir` via the controller's storage, NEVER in this typed KV.
|
|
97189
|
+
*/
|
|
97190
|
+
var matterConnectionSchema = object({
|
|
97191
|
+
/** Optional Thread operational dataset (hex) for Thread device onboarding. */
|
|
97192
|
+
threadDataset: string$2().default("").describe("Thread operational dataset (hex) — required only for Thread devices").meta({ secret: true }),
|
|
97193
|
+
/** Optional Wi-Fi SSID for Wi-Fi device onboarding over BLE. */
|
|
97194
|
+
wifiSsid: string$2().default("").describe("Wi-Fi SSID — required only for BLE Wi-Fi onboarding"),
|
|
97195
|
+
/** Optional Wi-Fi password for Wi-Fi device onboarding over BLE. */
|
|
97196
|
+
wifiPassword: string$2().default("").describe("Wi-Fi password (optional)").meta({ secret: true }),
|
|
97197
|
+
/** PULL-reconcile interval for the commissioned-node inventory. */
|
|
97198
|
+
reconcileIntervalMs: preprocess((v) => v === "" || v === null ? void 0 : v, number().int().min(1e4).max(864e5).default(3e5)).describe("Node-inventory reconcile interval (ms)")
|
|
97199
|
+
});
|
|
97200
|
+
/** A single registered Matter fabric ("broker"). */
|
|
97201
|
+
var MatterBrokerEntrySchema = object({
|
|
97202
|
+
id: string$2().min(1),
|
|
97203
|
+
name: string$2().min(1),
|
|
97204
|
+
connection: matterConnectionSchema,
|
|
97205
|
+
integrationId: string$2().optional(),
|
|
97206
|
+
/** Absent means on; the instances page switches a fabric off without deleting it. */
|
|
97207
|
+
enabled: boolean().optional()
|
|
97208
|
+
});
|
|
97209
|
+
/** Top-level addon config — the fabrics are records, so nothing is left here. */
|
|
97210
|
+
var matterAddonConfigSchema = object({});
|
|
97211
|
+
/** Narrow an `unknown` to a shallow plain-object record (cast-free boundary). */
|
|
97212
|
+
function toRecord(value) {
|
|
97213
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) return Object.fromEntries(Object.entries(value));
|
|
97214
|
+
return {};
|
|
97215
|
+
}
|
|
97216
|
+
/** Coerce a loose settings blob through the connection schema, applying defaults. */
|
|
97217
|
+
function settingsToMatterConnection(settings) {
|
|
97218
|
+
return matterConnectionSchema.parse(toRecord(settings));
|
|
97219
|
+
}
|
|
97220
|
+
/**
|
|
97221
|
+
* Settings readers see an empty string for both secrets. The stored value
|
|
97222
|
+
* stays in the keychain (records `secret` meta). An empty round-trip must
|
|
97223
|
+
* not wipe it — see {@link mergeMatterSecrets}.
|
|
97224
|
+
*/
|
|
97225
|
+
function publicMatterConnection(connection) {
|
|
97226
|
+
return {
|
|
97227
|
+
...connection,
|
|
97228
|
+
wifiPassword: "",
|
|
97229
|
+
threadDataset: ""
|
|
97230
|
+
};
|
|
97231
|
+
}
|
|
97232
|
+
/** Keep a stored secret when the form sent back the redacted empty string. */
|
|
97233
|
+
function mergeMatterSecrets(existing, incoming) {
|
|
97234
|
+
return {
|
|
97235
|
+
...incoming,
|
|
97236
|
+
wifiPassword: incoming.wifiPassword.length > 0 ? incoming.wifiPassword : existing.wifiPassword,
|
|
97237
|
+
threadDataset: incoming.threadDataset.length > 0 ? incoming.threadDataset : existing.threadDataset
|
|
97238
|
+
};
|
|
97239
|
+
}
|
|
97240
|
+
/** Hand-written connection form for the broker/integration creation UI. */
|
|
97241
|
+
function buildConnectionFormSchema() {
|
|
97242
|
+
return { sections: [{
|
|
97243
|
+
id: "matter-fabric",
|
|
97244
|
+
title: "Matter fabric",
|
|
97245
|
+
titleKey: "matter-broker:connection.s_matter-fabric.title",
|
|
97246
|
+
description: "CamStack owns a Matter fabric on this hub. Add wireless onboarding credentials only if you will commission Thread or BLE-Wi-Fi devices. On-network (Ethernet/Wi-Fi) Matter devices need none of these.",
|
|
97247
|
+
descriptionKey: "matter-broker:connection.s_matter-fabric.description",
|
|
97248
|
+
columns: 1,
|
|
97249
|
+
fields: [
|
|
97250
|
+
{
|
|
97251
|
+
type: "password",
|
|
97252
|
+
key: "threadDataset",
|
|
97253
|
+
secret: true,
|
|
97254
|
+
showToggle: true,
|
|
97255
|
+
label: "Thread operational dataset (hex, optional)",
|
|
97256
|
+
labelKey: "matter-broker:connection.s_matter-fabric.threadDataset.label",
|
|
97257
|
+
placeholder: "0e08000000000001…"
|
|
97258
|
+
},
|
|
97259
|
+
{
|
|
97260
|
+
type: "text",
|
|
97261
|
+
key: "wifiSsid",
|
|
97262
|
+
label: "Wi-Fi SSID (optional)",
|
|
97263
|
+
labelKey: "matter-broker:connection.s_matter-fabric.wifiSsid.label"
|
|
97264
|
+
},
|
|
97265
|
+
{
|
|
97266
|
+
type: "password",
|
|
97267
|
+
key: "wifiPassword",
|
|
97268
|
+
label: "Wi-Fi password (optional)",
|
|
97269
|
+
labelKey: "matter-broker:connection.s_matter-fabric.wifiPassword.label",
|
|
97270
|
+
showToggle: true
|
|
97271
|
+
},
|
|
97272
|
+
{
|
|
97273
|
+
type: "number",
|
|
97274
|
+
key: "reconcileIntervalMs",
|
|
97275
|
+
label: "Node reconcile interval (ms)",
|
|
97276
|
+
labelKey: "matter-broker:connection.s_matter-fabric.reconcileIntervalMs.label",
|
|
97277
|
+
min: 1e4,
|
|
97278
|
+
max: 864e5,
|
|
97279
|
+
default: 3e5
|
|
97280
|
+
}
|
|
97281
|
+
]
|
|
97282
|
+
}] };
|
|
97283
|
+
}
|
|
97284
|
+
//#endregion
|
|
97061
97285
|
//#region ../../node_modules/@matter/general/dist/esm/net/tcp/TcpConnection.js
|
|
97062
97286
|
/**
|
|
97063
97287
|
* @license
|
|
@@ -207384,8 +207608,8 @@ function sameConnection(a, b) {
|
|
|
207384
207608
|
* Manages N live Matter fabric connections ("brokers"), each backed by a
|
|
207385
207609
|
* {@link MatterIntegrationManager}. Allocates stable ids (`matter_001`, …), CRUD,
|
|
207386
207610
|
* an integration FK index, lifecycle helpers. Mirrors the Ecowitt / Dreame
|
|
207387
|
-
* registry.
|
|
207388
|
-
* fabric,
|
|
207611
|
+
* registry. The integration is `instanceMode: multiple`: the common case is one
|
|
207612
|
+
* fabric, and an operator may add more.
|
|
207389
207613
|
*/
|
|
207390
207614
|
var MatterBrokerRegistry = class {
|
|
207391
207615
|
#logger;
|
|
@@ -207573,111 +207797,6 @@ var MatterBrokerRegistry = class {
|
|
|
207573
207797
|
}
|
|
207574
207798
|
};
|
|
207575
207799
|
//#endregion
|
|
207576
|
-
//#region src/config.ts
|
|
207577
|
-
/**
|
|
207578
|
-
* Matter broker connection settings for ONE fabric/controller (one broker entry).
|
|
207579
|
-
* v1 is intentionally minimal: optional Thread / Wi-Fi credentials supplied up
|
|
207580
|
-
* front for wireless commissioning. The pairing code itself is NOT stored here —
|
|
207581
|
-
* it is passed transiently to the commissioning action.
|
|
207582
|
-
*
|
|
207583
|
-
* The fabric credentials (root cert / NOC / node registry) live in the addon
|
|
207584
|
-
* `dataDir` via the controller's storage, NEVER in this typed KV.
|
|
207585
|
-
*/
|
|
207586
|
-
var matterConnectionSchema = object({
|
|
207587
|
-
/** Optional Thread operational dataset (hex) for Thread device onboarding. */
|
|
207588
|
-
threadDataset: string$2().default("").describe("Thread operational dataset (hex) — required only for Thread devices").meta({ secret: true }),
|
|
207589
|
-
/** Optional Wi-Fi SSID for Wi-Fi device onboarding over BLE. */
|
|
207590
|
-
wifiSsid: string$2().default("").describe("Wi-Fi SSID — required only for BLE Wi-Fi onboarding"),
|
|
207591
|
-
/** Optional Wi-Fi password for Wi-Fi device onboarding over BLE. */
|
|
207592
|
-
wifiPassword: string$2().default("").describe("Wi-Fi password (optional)").meta({ secret: true }),
|
|
207593
|
-
/** PULL-reconcile interval for the commissioned-node inventory. */
|
|
207594
|
-
reconcileIntervalMs: preprocess((v) => v === "" || v === null ? void 0 : v, number().int().min(1e4).max(864e5).default(3e5)).describe("Node-inventory reconcile interval (ms)")
|
|
207595
|
-
});
|
|
207596
|
-
/** A single registered Matter fabric ("broker"). */
|
|
207597
|
-
var MatterBrokerEntrySchema = object({
|
|
207598
|
-
id: string$2().min(1),
|
|
207599
|
-
name: string$2().min(1),
|
|
207600
|
-
connection: matterConnectionSchema,
|
|
207601
|
-
integrationId: string$2().optional(),
|
|
207602
|
-
/** Absent means on; the instances page switches a fabric off without deleting it. */
|
|
207603
|
-
enabled: boolean().optional()
|
|
207604
|
-
});
|
|
207605
|
-
/** Top-level addon config — the fabrics are records, so nothing is left here. */
|
|
207606
|
-
var matterAddonConfigSchema = object({});
|
|
207607
|
-
/** Narrow an `unknown` to a shallow plain-object record (cast-free boundary). */
|
|
207608
|
-
function toRecord(value) {
|
|
207609
|
-
if (typeof value === "object" && value !== null && !Array.isArray(value)) return Object.fromEntries(Object.entries(value));
|
|
207610
|
-
return {};
|
|
207611
|
-
}
|
|
207612
|
-
/** Coerce a loose settings blob through the connection schema, applying defaults. */
|
|
207613
|
-
function settingsToMatterConnection(settings) {
|
|
207614
|
-
return matterConnectionSchema.parse(toRecord(settings));
|
|
207615
|
-
}
|
|
207616
|
-
/**
|
|
207617
|
-
* Settings readers see an empty string for both secrets. The stored value
|
|
207618
|
-
* stays in the keychain (records `secret` meta). An empty round-trip must
|
|
207619
|
-
* not wipe it — see {@link mergeMatterSecrets}.
|
|
207620
|
-
*/
|
|
207621
|
-
function publicMatterConnection(connection) {
|
|
207622
|
-
return {
|
|
207623
|
-
...connection,
|
|
207624
|
-
wifiPassword: "",
|
|
207625
|
-
threadDataset: ""
|
|
207626
|
-
};
|
|
207627
|
-
}
|
|
207628
|
-
/** Keep a stored secret when the form sent back the redacted empty string. */
|
|
207629
|
-
function mergeMatterSecrets(existing, incoming) {
|
|
207630
|
-
return {
|
|
207631
|
-
...incoming,
|
|
207632
|
-
wifiPassword: incoming.wifiPassword.length > 0 ? incoming.wifiPassword : existing.wifiPassword,
|
|
207633
|
-
threadDataset: incoming.threadDataset.length > 0 ? incoming.threadDataset : existing.threadDataset
|
|
207634
|
-
};
|
|
207635
|
-
}
|
|
207636
|
-
/** Hand-written connection form for the broker/integration creation UI. */
|
|
207637
|
-
function buildConnectionFormSchema() {
|
|
207638
|
-
return { sections: [{
|
|
207639
|
-
id: "matter-fabric",
|
|
207640
|
-
title: "Matter fabric",
|
|
207641
|
-
titleKey: "matter-broker:connection.s_matter-fabric.title",
|
|
207642
|
-
description: "CamStack owns a Matter fabric on this hub. Add wireless onboarding credentials only if you will commission Thread or BLE-Wi-Fi devices. On-network (Ethernet/Wi-Fi) Matter devices need none of these.",
|
|
207643
|
-
descriptionKey: "matter-broker:connection.s_matter-fabric.description",
|
|
207644
|
-
columns: 1,
|
|
207645
|
-
fields: [
|
|
207646
|
-
{
|
|
207647
|
-
type: "password",
|
|
207648
|
-
key: "threadDataset",
|
|
207649
|
-
secret: true,
|
|
207650
|
-
showToggle: true,
|
|
207651
|
-
label: "Thread operational dataset (hex, optional)",
|
|
207652
|
-
labelKey: "matter-broker:connection.s_matter-fabric.threadDataset.label",
|
|
207653
|
-
placeholder: "0e08000000000001…"
|
|
207654
|
-
},
|
|
207655
|
-
{
|
|
207656
|
-
type: "text",
|
|
207657
|
-
key: "wifiSsid",
|
|
207658
|
-
label: "Wi-Fi SSID (optional)",
|
|
207659
|
-
labelKey: "matter-broker:connection.s_matter-fabric.wifiSsid.label"
|
|
207660
|
-
},
|
|
207661
|
-
{
|
|
207662
|
-
type: "password",
|
|
207663
|
-
key: "wifiPassword",
|
|
207664
|
-
label: "Wi-Fi password (optional)",
|
|
207665
|
-
labelKey: "matter-broker:connection.s_matter-fabric.wifiPassword.label",
|
|
207666
|
-
showToggle: true
|
|
207667
|
-
},
|
|
207668
|
-
{
|
|
207669
|
-
type: "number",
|
|
207670
|
-
key: "reconcileIntervalMs",
|
|
207671
|
-
label: "Node reconcile interval (ms)",
|
|
207672
|
-
labelKey: "matter-broker:connection.s_matter-fabric.reconcileIntervalMs.label",
|
|
207673
|
-
min: 1e4,
|
|
207674
|
-
max: 864e5,
|
|
207675
|
-
default: 3e5
|
|
207676
|
-
}
|
|
207677
|
-
]
|
|
207678
|
-
}] };
|
|
207679
|
-
}
|
|
207680
|
-
//#endregion
|
|
207681
207800
|
//#region src/matter-commissioning.ts
|
|
207682
207801
|
/** A clear validation error for a malformed pairing code. */
|
|
207683
207802
|
var InvalidPairingCodeError = class extends Error {
|
|
@@ -209239,22 +209358,9 @@ var MatterBrokerAddon = class extends BaseDeviceProvider {
|
|
|
209239
209358
|
}
|
|
209240
209359
|
});
|
|
209241
209360
|
}
|
|
209242
|
-
|
|
209243
|
-
|
|
209244
|
-
|
|
209245
|
-
title: "Matter fabric",
|
|
209246
|
-
titleKey: "matter-broker:globalSettings.s_matter-fabric.title",
|
|
209247
|
-
description: "Matter fabrics are managed in the External systems → Brokers tab.",
|
|
209248
|
-
descriptionKey: "matter-broker:globalSettings.s_matter-fabric.description",
|
|
209249
|
-
columns: 1,
|
|
209250
|
-
fields: [{
|
|
209251
|
-
type: "info",
|
|
209252
|
-
key: "fabricHelp",
|
|
209253
|
-
label: "Fabrics are managed separately",
|
|
209254
|
-
labelKey: "matter-broker:globalSettings.s_matter-fabric.fabricHelp.label",
|
|
209255
|
-
content: "This integration links to a Matter fabric broker. Add, edit, or remove fabrics from External systems → Brokers, then commission devices into the fabric. The integration only stores a reference to its broker."
|
|
209256
|
-
}]
|
|
209257
|
-
}] });
|
|
209361
|
+
/** The connection form a record carries; the values live on the record, never in `addon-settings`. */
|
|
209362
|
+
async integrationFormSchema(_instance) {
|
|
209363
|
+
return buildConnectionFormSchema();
|
|
209258
209364
|
}
|
|
209259
209365
|
async supportsManualCreation() {
|
|
209260
209366
|
return false;
|
|
@@ -209303,4 +209409,4 @@ function endpointsEqual(a, b) {
|
|
|
209303
209409
|
return true;
|
|
209304
209410
|
}
|
|
209305
209411
|
//#endregion
|
|
209306
|
-
export { MatterBrokerAddon, parsePairingCode as a,
|
|
209412
|
+
export { MatterBrokerAddon, parsePairingCode as a, mapMatterEndpointToCamStack as c, createMatterController as d, buildConnectionFormSchema as f, settingsToMatterConnection as h, isValidPairingCode as i, MatterControllerNotWiredError as l, matterConnectionSchema as m, endpointNativeId as n, MatterClusterId as o, matterAddonConfigSchema as p, InvalidPairingCodeError as r, endpointLabel as s, buildMatterNodeCandidate as t, StubMatterController as u };
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { MatterBrokerAddon, a as parsePairingCode, c as
|
|
1
|
+
import { MatterBrokerAddon, a as parsePairingCode, c as mapMatterEndpointToCamStack, d as createMatterController, f as buildConnectionFormSchema, h as settingsToMatterConnection, i as isValidPairingCode, l as MatterControllerNotWiredError, m as matterConnectionSchema, n as endpointNativeId, o as MatterClusterId, p as matterAddonConfigSchema, r as InvalidPairingCodeError, s as endpointLabel, t as buildMatterNodeCandidate, u as StubMatterController } from "./addon.mjs";
|
|
2
2
|
export { InvalidPairingCodeError, MatterBrokerAddon, MatterClusterId, MatterControllerNotWiredError, StubMatterController, buildConnectionFormSchema, buildMatterNodeCandidate, createMatterController, endpointLabel, endpointNativeId, isValidPairingCode, mapMatterEndpointToCamStack, matterAddonConfigSchema, matterConnectionSchema, parsePairingCode, settingsToMatterConnection };
|
package/i18n/en.json
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
{
|
|
2
|
-
"globalSettings.s_matter-fabric.title": "Matter fabric",
|
|
3
|
-
"globalSettings.s_matter-fabric.description": "Matter fabrics are managed in the External systems → Brokers tab.",
|
|
4
|
-
"globalSettings.s_matter-fabric.fabricHelp.label": "Fabrics are managed separately",
|
|
5
2
|
"connection.s_matter-fabric.title": "Matter fabric",
|
|
6
3
|
"connection.s_matter-fabric.description": "CamStack owns a Matter fabric on this hub. Add wireless onboarding credentials only if you will commission Thread or BLE-Wi-Fi devices. On-network (Ethernet/Wi-Fi) Matter devices need none of these.",
|
|
7
4
|
"connection.s_matter-fabric.threadDataset.label": "Thread operational dataset (hex, optional)",
|
package/i18n/it.json
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
{
|
|
2
|
-
"globalSettings.s_matter-fabric.title": "Fabric Matter",
|
|
3
|
-
"globalSettings.s_matter-fabric.description": "I fabric Matter si gestiscono nella scheda Sistemi esterni → Broker.",
|
|
4
|
-
"globalSettings.s_matter-fabric.fabricHelp.label": "I fabric si gestiscono separatamente",
|
|
5
2
|
"connection.s_matter-fabric.title": "Fabric Matter",
|
|
6
3
|
"connection.s_matter-fabric.description": "CamStack gestisce un fabric Matter su questo hub. Aggiungi le credenziali di onboarding wireless solo se metterai in servizio dispositivi Thread o BLE-Wi-Fi. I dispositivi Matter sulla rete (Ethernet/Wi-Fi) non ne hanno bisogno.",
|
|
7
4
|
"connection.s_matter-fabric.threadDataset.label": "Dataset operativo Thread (esadecimale, facoltativo)",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-matter-broker",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.159",
|
|
4
4
|
"description": "Matter broker addon for CamStack — owns a Matter fabric (commissioning + the long-lived controller) via the matter.js controller and brokers commissioned Matter nodes into CamStack",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"entry": "./dist/addon.js",
|
|
44
44
|
"icon": "assets/icon.svg",
|
|
45
45
|
"color": "#3b6ea5",
|
|
46
|
-
"instanceMode": "
|
|
46
|
+
"instanceMode": "multiple",
|
|
47
47
|
"brokerKind": "matter",
|
|
48
48
|
"execution": {
|
|
49
49
|
"placement": "hub-only"
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
}
|
|
66
66
|
],
|
|
67
67
|
"requires": {
|
|
68
|
-
"server": ">=1.2.
|
|
68
|
+
"server": ">=1.2.777"
|
|
69
69
|
}
|
|
70
70
|
},
|
|
71
71
|
"files": [
|