@project-chip/matter.js 0.13.0-alpha.0-20250311-3eb0af5f2 → 0.13.0-alpha.0-20250322-f085fa576
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/CommissioningController.d.ts +31 -9
- package/dist/cjs/CommissioningController.d.ts.map +1 -1
- package/dist/cjs/CommissioningController.js +34 -16
- package/dist/cjs/CommissioningController.js.map +1 -1
- package/dist/cjs/MatterController.d.ts +13 -3
- package/dist/cjs/MatterController.d.ts.map +1 -1
- package/dist/cjs/MatterController.js +41 -21
- package/dist/cjs/MatterController.js.map +2 -2
- package/dist/cjs/PaseCommissioner.js +1 -1
- package/dist/cjs/PaseCommissioner.js.map +1 -1
- package/dist/cjs/device/PairedNode.d.ts.map +1 -1
- package/dist/cjs/device/PairedNode.js +14 -8
- package/dist/cjs/device/PairedNode.js.map +1 -1
- package/dist/esm/CommissioningController.d.ts +31 -9
- package/dist/esm/CommissioningController.d.ts.map +1 -1
- package/dist/esm/CommissioningController.js +35 -16
- package/dist/esm/CommissioningController.js.map +1 -1
- package/dist/esm/MatterController.d.ts +13 -3
- package/dist/esm/MatterController.d.ts.map +1 -1
- package/dist/esm/MatterController.js +43 -23
- package/dist/esm/MatterController.js.map +1 -1
- package/dist/esm/PaseCommissioner.js +1 -1
- package/dist/esm/PaseCommissioner.js.map +1 -1
- package/dist/esm/device/PairedNode.d.ts.map +1 -1
- package/dist/esm/device/PairedNode.js +14 -8
- package/dist/esm/device/PairedNode.js.map +1 -1
- package/package.json +8 -8
- package/src/CommissioningController.ts +67 -17
- package/src/MatterController.ts +56 -24
- package/src/PaseCommissioner.ts +1 -1
- package/src/device/PairedNode.ts +20 -14
package/src/MatterController.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
CRYPTO_SYMMETRIC_KEY_LENGTH,
|
|
23
23
|
ImplementationError,
|
|
24
24
|
Logger,
|
|
25
|
+
MatterError,
|
|
25
26
|
NetInterfaceSet,
|
|
26
27
|
ServerAddressIp,
|
|
27
28
|
serverAddressToString,
|
|
@@ -59,7 +60,6 @@ import {
|
|
|
59
60
|
SecureChannelProtocol,
|
|
60
61
|
SessionManager,
|
|
61
62
|
SubscriptionClient,
|
|
62
|
-
UnknownNodeError,
|
|
63
63
|
} from "#protocol";
|
|
64
64
|
import {
|
|
65
65
|
CaseAuthenticatedTag,
|
|
@@ -72,7 +72,9 @@ import {
|
|
|
72
72
|
TypeFromPartialBitSchema,
|
|
73
73
|
VendorId,
|
|
74
74
|
} from "#types";
|
|
75
|
+
import { ClassExtends } from "@matter/general";
|
|
75
76
|
import { ControllerStoreInterface } from "@matter/node";
|
|
77
|
+
import { ControllerCommissioningFlow, MessageChannel } from "@matter/protocol";
|
|
76
78
|
|
|
77
79
|
export type CommissionedNodeDetails = {
|
|
78
80
|
operationalServerAddress?: ServerAddressIp;
|
|
@@ -104,6 +106,9 @@ export class MatterController {
|
|
|
104
106
|
adminFabricIndex?: FabricIndex;
|
|
105
107
|
caseAuthenticatedTags?: CaseAuthenticatedTag[];
|
|
106
108
|
adminFabricLabel: string;
|
|
109
|
+
rootNodeId?: NodeId;
|
|
110
|
+
rootCertificateAuthority?: CertificateAuthority;
|
|
111
|
+
rootFabric?: Fabric;
|
|
107
112
|
}): Promise<MatterController> {
|
|
108
113
|
const {
|
|
109
114
|
controllerStore,
|
|
@@ -115,17 +120,20 @@ export class MatterController {
|
|
|
115
120
|
adminFabricIndex = FabricIndex(DEFAULT_FABRIC_INDEX),
|
|
116
121
|
caseAuthenticatedTags,
|
|
117
122
|
adminFabricLabel,
|
|
123
|
+
rootNodeId,
|
|
124
|
+
rootCertificateAuthority,
|
|
125
|
+
rootFabric,
|
|
118
126
|
} = options;
|
|
119
127
|
|
|
120
|
-
const ca = await CertificateAuthority.create(controllerStore.caStorage);
|
|
128
|
+
const ca = rootCertificateAuthority ?? (await CertificateAuthority.create(controllerStore.caStorage));
|
|
121
129
|
const fabricStorage = controllerStore.fabricStorage;
|
|
122
130
|
|
|
123
131
|
let controller: MatterController | undefined = undefined;
|
|
124
132
|
// Check if we have a fabric stored in the storage, if yes initialize this one, else build a new one
|
|
125
|
-
if (await fabricStorage.has("fabric")) {
|
|
126
|
-
const fabric = new Fabric(await fabricStorage.get<Fabric.Config>("fabric"));
|
|
133
|
+
if (rootFabric !== undefined || (await fabricStorage.has("fabric"))) {
|
|
134
|
+
const fabric = rootFabric ?? new Fabric(await fabricStorage.get<Fabric.Config>("fabric"));
|
|
127
135
|
if (Bytes.areEqual(fabric.rootCert, ca.rootCert)) {
|
|
128
|
-
logger.info("
|
|
136
|
+
logger.info("Used existing fabric");
|
|
129
137
|
controller = new MatterController({
|
|
130
138
|
controllerStore,
|
|
131
139
|
scanners,
|
|
@@ -136,9 +144,12 @@ export class MatterController {
|
|
|
136
144
|
sessionClosedCallback,
|
|
137
145
|
});
|
|
138
146
|
} else {
|
|
147
|
+
if (rootFabric !== undefined) {
|
|
148
|
+
throw new MatterError("Fabric CA certificate is not in sync with CA.");
|
|
149
|
+
}
|
|
139
150
|
logger.info("Fabric CA certificate changed ...");
|
|
140
151
|
if (await controllerStore.nodesStorage.has("commissionedNodes")) {
|
|
141
|
-
throw new
|
|
152
|
+
throw new MatterError(
|
|
142
153
|
"Fabric certificate changed, but commissioned nodes are still present. Please clear the storage.",
|
|
143
154
|
);
|
|
144
155
|
}
|
|
@@ -146,16 +157,16 @@ export class MatterController {
|
|
|
146
157
|
}
|
|
147
158
|
if (controller === undefined) {
|
|
148
159
|
logger.info("Creating new fabric");
|
|
149
|
-
const
|
|
160
|
+
const controllerNodeId = rootNodeId ?? NodeId.randomOperationalNodeId();
|
|
150
161
|
const ipkValue = Crypto.getRandomData(CRYPTO_SYMMETRIC_KEY_LENGTH);
|
|
151
162
|
const fabricBuilder = new FabricBuilder()
|
|
152
163
|
.setRootCert(ca.rootCert)
|
|
153
|
-
.setRootNodeId(
|
|
164
|
+
.setRootNodeId(controllerNodeId)
|
|
154
165
|
.setIdentityProtectionKey(ipkValue)
|
|
155
166
|
.setRootVendorId(adminVendorId ?? DEFAULT_ADMIN_VENDOR_ID)
|
|
156
167
|
.setLabel(adminFabricLabel);
|
|
157
168
|
fabricBuilder.setOperationalCert(
|
|
158
|
-
ca.generateNoc(fabricBuilder.publicKey, adminFabricId,
|
|
169
|
+
ca.generateNoc(fabricBuilder.publicKey, adminFabricId, controllerNodeId, caseAuthenticatedTags),
|
|
159
170
|
);
|
|
160
171
|
const fabric = await fabricBuilder.build(adminFabricIndex);
|
|
161
172
|
|
|
@@ -174,7 +185,8 @@ export class MatterController {
|
|
|
174
185
|
}
|
|
175
186
|
|
|
176
187
|
public static async createAsPaseCommissioner(options: {
|
|
177
|
-
certificateAuthorityConfig
|
|
188
|
+
certificateAuthorityConfig?: CertificateAuthority.Configuration;
|
|
189
|
+
rootCertificateAuthority?: CertificateAuthority;
|
|
178
190
|
fabricConfig: Fabric.Config;
|
|
179
191
|
scanners: ScannerSet;
|
|
180
192
|
netInterfaces: NetInterfaceSet;
|
|
@@ -183,6 +195,7 @@ export class MatterController {
|
|
|
183
195
|
}): Promise<MatterController> {
|
|
184
196
|
const {
|
|
185
197
|
certificateAuthorityConfig,
|
|
198
|
+
rootCertificateAuthority,
|
|
186
199
|
fabricConfig,
|
|
187
200
|
adminFabricLabel,
|
|
188
201
|
scanners,
|
|
@@ -200,7 +213,12 @@ export class MatterController {
|
|
|
200
213
|
logger.info("BLE is not enabled. Using only IP network for commissioning.");
|
|
201
214
|
}
|
|
202
215
|
|
|
203
|
-
|
|
216
|
+
if (rootCertificateAuthority === undefined && certificateAuthorityConfig === undefined) {
|
|
217
|
+
throw new ImplementationError("Either rootCertificateAuthority or certificateAuthorityConfig must be set.");
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const certificateManager =
|
|
221
|
+
rootCertificateAuthority ?? (await CertificateAuthority.create(certificateAuthorityConfig!));
|
|
204
222
|
|
|
205
223
|
// Stored data are temporary anyway and no node will be connected, so just use an in-memory storage
|
|
206
224
|
const storageManager = new StorageManager(new StorageBackendMemory());
|
|
@@ -388,7 +406,10 @@ export class MatterController {
|
|
|
388
406
|
*/
|
|
389
407
|
async commission(
|
|
390
408
|
options: NodeCommissioningOptions,
|
|
391
|
-
|
|
409
|
+
customizations?: {
|
|
410
|
+
completeCommissioningCallback?: (peerNodeId: NodeId, discoveryData?: DiscoveryData) => Promise<boolean>;
|
|
411
|
+
commissioningFlowImpl?: ClassExtends<ControllerCommissioningFlow>;
|
|
412
|
+
},
|
|
392
413
|
): Promise<NodeId> {
|
|
393
414
|
const commissioningOptions: DiscoveryAndCommissioningOptions = {
|
|
394
415
|
...options.commissioning,
|
|
@@ -397,6 +418,8 @@ export class MatterController {
|
|
|
397
418
|
passcode: options.passcode,
|
|
398
419
|
};
|
|
399
420
|
|
|
421
|
+
const { completeCommissioningCallback, commissioningFlowImpl } = customizations ?? {};
|
|
422
|
+
|
|
400
423
|
if (completeCommissioningCallback) {
|
|
401
424
|
commissioningOptions.finalizeCommissioning = async (peerAddress, discoveryData) => {
|
|
402
425
|
const result = await completeCommissioningCallback(peerAddress.nodeId, discoveryData);
|
|
@@ -405,6 +428,7 @@ export class MatterController {
|
|
|
405
428
|
}
|
|
406
429
|
};
|
|
407
430
|
}
|
|
431
|
+
commissioningOptions.commissioningFlowImpl = commissioningFlowImpl;
|
|
408
432
|
|
|
409
433
|
const address = await this.commissioner.commissionWithDiscovery(commissioningOptions);
|
|
410
434
|
|
|
@@ -417,6 +441,17 @@ export class MatterController {
|
|
|
417
441
|
return this.peers.disconnect(this.fabric.addressOf(nodeId));
|
|
418
442
|
}
|
|
419
443
|
|
|
444
|
+
async connectPaseChannel(options: NodeCommissioningOptions) {
|
|
445
|
+
const { paseSecureChannel } = await this.commissioner.discoverAndEstablishPase({
|
|
446
|
+
...options.commissioning,
|
|
447
|
+
fabric: this.fabric,
|
|
448
|
+
discovery: options.discovery,
|
|
449
|
+
passcode: options.passcode,
|
|
450
|
+
});
|
|
451
|
+
logger.warn("PASE channel established", paseSecureChannel.session.name, paseSecureChannel.session.isSecure);
|
|
452
|
+
return paseSecureChannel;
|
|
453
|
+
}
|
|
454
|
+
|
|
420
455
|
async removeNode(nodeId: NodeId) {
|
|
421
456
|
return this.peers.delete(this.fabric.addressOf(nodeId));
|
|
422
457
|
}
|
|
@@ -504,8 +539,11 @@ export class MatterController {
|
|
|
504
539
|
return this.clients.connect(this.fabric.addressOf(peerNodeId), { discoveryOptions, allowUnknownPeer });
|
|
505
540
|
}
|
|
506
541
|
|
|
507
|
-
createInteractionClient(
|
|
508
|
-
|
|
542
|
+
createInteractionClient(peerNodeIdOrChannel: NodeId | MessageChannel, discoveryOptions: DiscoveryOptions) {
|
|
543
|
+
if (peerNodeIdOrChannel instanceof MessageChannel) {
|
|
544
|
+
return this.clients.getInteractionClientForChannel(peerNodeIdOrChannel);
|
|
545
|
+
}
|
|
546
|
+
return this.clients.getInteractionClient(this.fabric.addressOf(peerNodeIdOrChannel), discoveryOptions);
|
|
509
547
|
}
|
|
510
548
|
|
|
511
549
|
async getNextAvailableSessionId() {
|
|
@@ -548,11 +586,8 @@ export class MatterController {
|
|
|
548
586
|
filterClusterId?: ClusterId,
|
|
549
587
|
): Promise<{ endpointId: EndpointNumber; clusterId: ClusterId; dataVersion: number }[]> {
|
|
550
588
|
const peer = this.peers.get(this.fabric.addressOf(nodeId));
|
|
551
|
-
if (peer === undefined) {
|
|
552
|
-
|
|
553
|
-
}
|
|
554
|
-
if (peer.dataStore === undefined) {
|
|
555
|
-
return []; // We have no store, also also no data
|
|
589
|
+
if (peer === undefined || peer.dataStore === undefined) {
|
|
590
|
+
return []; // We have no store, also no data
|
|
556
591
|
}
|
|
557
592
|
await peer.dataStore.construction;
|
|
558
593
|
return peer.dataStore.getClusterDataVersions(filterEndpointId, filterClusterId);
|
|
@@ -564,11 +599,8 @@ export class MatterController {
|
|
|
564
599
|
clusterId: ClusterId,
|
|
565
600
|
): Promise<DecodedAttributeReportValue<any>[]> {
|
|
566
601
|
const peer = this.peers.get(this.fabric.addressOf(nodeId));
|
|
567
|
-
if (peer === undefined) {
|
|
568
|
-
|
|
569
|
-
}
|
|
570
|
-
if (peer.dataStore === undefined) {
|
|
571
|
-
return []; // We have no store, also also no data
|
|
602
|
+
if (peer === undefined || peer.dataStore === undefined) {
|
|
603
|
+
return []; // We have no store, also no data
|
|
572
604
|
}
|
|
573
605
|
await peer.dataStore.construction;
|
|
574
606
|
return peer.dataStore.retrieveAttributes(endpointId, clusterId);
|
package/src/PaseCommissioner.ts
CHANGED
|
@@ -119,7 +119,7 @@ export class PaseCommissioner {
|
|
|
119
119
|
) {
|
|
120
120
|
const controller = this.assertControllerIsStarted();
|
|
121
121
|
|
|
122
|
-
return await controller.commission(nodeOptions, completeCommissioningCallback);
|
|
122
|
+
return await controller.commission(nodeOptions, { completeCommissioningCallback });
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
/** Disconnects all connected nodes and Closes the network connections and other resources of the controller. */
|
package/src/device/PairedNode.ts
CHANGED
|
@@ -346,20 +346,26 @@ export class PairedNode {
|
|
|
346
346
|
this.#reconnectFunc = reconnectFunc;
|
|
347
347
|
|
|
348
348
|
this.#interactionClient = interactionClient;
|
|
349
|
-
this.#interactionClient.
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
349
|
+
if (this.#interactionClient.isReconnectable) {
|
|
350
|
+
this.#interactionClient.channelUpdated.on(() => {
|
|
351
|
+
// When we had planned a reconnect because of a disconnect we can stop the timer now
|
|
352
|
+
if (
|
|
353
|
+
this.#reconnectDelayTimer?.isRunning &&
|
|
354
|
+
!this.#clientReconnectInProgress &&
|
|
355
|
+
!this.#reconnectionInProgress &&
|
|
356
|
+
this.#connectionState === NodeStates.Reconnecting
|
|
357
|
+
) {
|
|
358
|
+
logger.info(`Node ${this.nodeId}: Got a reconnect, so reconnection not needed anymore ...`);
|
|
359
|
+
this.#reconnectDelayTimer?.stop();
|
|
360
|
+
this.#reconnectDelayTimer = undefined;
|
|
361
|
+
this.#setConnectionState(NodeStates.Connected);
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
} else {
|
|
365
|
+
logger.warn(
|
|
366
|
+
`Node ${this.nodeId}: InteractionClient is not reconnectable, no automatic reconnection will happen in case of errors.`,
|
|
367
|
+
);
|
|
368
|
+
}
|
|
363
369
|
this.#nodeDetails = new DeviceInformation(nodeId, knownNodeDetails);
|
|
364
370
|
logger.info(`Node ${this.nodeId}: Created paired node with device data`, this.#nodeDetails.meta);
|
|
365
371
|
|