@project-chip/matter.js 0.12.3 → 0.12.4-alpha.0-20250210-ad8edf096

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.
@@ -257,6 +257,9 @@ export class PairedNode {
257
257
  #construction: Construction<PairedNode>;
258
258
  #clientReconnectInProgress = false;
259
259
  #currentSubscriptionHandler?: SubscriptionHandlerCallbacks;
260
+ readonly #commissioningController: CommissioningController;
261
+ #options: CommissioningControllerNodeOptions;
262
+ readonly #reconnectFunc: (discoveryType?: NodeDiscoveryType, noForcedConnection?: boolean) => Promise<void>;
260
263
 
261
264
  readonly events = {
262
265
  /**
@@ -318,14 +321,11 @@ export class PairedNode {
318
321
 
319
322
  constructor(
320
323
  readonly nodeId: NodeId,
321
- private readonly commissioningController: CommissioningController,
322
- private options: CommissioningControllerNodeOptions = {},
324
+ commissioningController: CommissioningController,
325
+ options: CommissioningControllerNodeOptions = {},
323
326
  knownNodeDetails: DeviceInformationData,
324
327
  interactionClient: InteractionClient,
325
- private readonly reconnectFunc: (
326
- discoveryType?: NodeDiscoveryType,
327
- noForcedConnection?: boolean,
328
- ) => Promise<void>,
328
+ reconnectFunc: (discoveryType?: NodeDiscoveryType, noForcedConnection?: boolean) => Promise<void>,
329
329
  assignDisconnectedHandler: (handler: () => Promise<void>) => void,
330
330
  sessions: BasicSet<SecureSession, SecureSession>,
331
331
  storedAttributeData?: DecodedAttributeReportValue<any>[],
@@ -341,9 +341,13 @@ export class PairedNode {
341
341
  }
342
342
  });
343
343
 
344
+ this.#commissioningController = commissioningController;
345
+ this.#options = options;
346
+ this.#reconnectFunc = reconnectFunc;
347
+
344
348
  this.#interactionClient = interactionClient;
345
349
  this.#interactionClient.channelUpdated.on(() => {
346
- // When we planned an reconnect because of a disconnect we can stop the timer now
350
+ // When we had planned a reconnect because of a disconnect we can stop the timer now
347
351
  if (
348
352
  this.#reconnectDelayTimer?.isRunning &&
349
353
  !this.#clientReconnectInProgress &&
@@ -376,7 +380,7 @@ export class PairedNode {
376
380
  await this.#initializeFromStoredData(storedAttributeData);
377
381
  }
378
382
 
379
- if (this.options.autoConnect !== false) {
383
+ if (this.#options.autoConnect !== false) {
380
384
  // This kicks of the remote initialization and automatic reconnection handling if it can not be connected
381
385
  this.#initialize().catch(error => {
382
386
  logger.info(`Node ${nodeId}: Error during remote initialization`, error);
@@ -397,26 +401,32 @@ export class PairedNode {
397
401
  return this.#connectionState === NodeStates.Connected;
398
402
  }
399
403
 
404
+ /** Returns the Node connection state. */
400
405
  get state() {
401
406
  return this.#connectionState;
402
407
  }
403
408
 
409
+ /** Returns the BasicInformation cluster metadata collected from the device. */
404
410
  get basicInformation() {
405
411
  return this.#nodeDetails.basicInformation;
406
412
  }
407
413
 
414
+ /** Returns the general capability metadata collected from the device. */
408
415
  get deviceInformation() {
409
416
  return this.#nodeDetails.meta;
410
417
  }
411
418
 
419
+ /** Is the Node fully initialized with formerly stored subscription data? False when the node was never connected so far. */
412
420
  get localInitializationDone() {
413
421
  return this.#localInitializationDone;
414
422
  }
415
423
 
424
+ /** Is the Node fully initialized with remote subscription or read data? */
416
425
  get remoteInitializationDone() {
417
426
  return this.#remoteInitializationDone;
418
427
  }
419
428
 
429
+ /** Is the Node initialized - locally or remotely? */
420
430
  get initialized() {
421
431
  return this.#remoteInitializationDone || this.#localInitializationDone;
422
432
  }
@@ -438,7 +448,7 @@ export class PairedNode {
438
448
  )
439
449
  return;
440
450
  this.#connectionState = state;
441
- this.options.stateInformationCallback?.(this.nodeId, state as unknown as NodeStateInformation);
451
+ this.#options.stateInformationCallback?.(this.nodeId, state as unknown as NodeStateInformation);
442
452
  this.events.stateChanged.emit(state);
443
453
  if (state === NodeStates.Disconnected) {
444
454
  this.#reconnectDelayTimer?.stop();
@@ -454,7 +464,7 @@ export class PairedNode {
454
464
 
455
465
  this.#clientReconnectInProgress = true;
456
466
  try {
457
- await this.reconnectFunc(discoveryType);
467
+ await this.#reconnectFunc(discoveryType);
458
468
  } finally {
459
469
  this.#clientReconnectInProgress = false;
460
470
  }
@@ -462,19 +472,20 @@ export class PairedNode {
462
472
 
463
473
  /**
464
474
  * Schedule a connection to the device. This method is non-blocking and will return immediately.
465
- * The connection happens in the background. Please monitor the state of the node to see if the
475
+ * The connection happens in the background. Please monitor the state events of the node to see if the
466
476
  * connection was successful.
477
+ * The provided connection options will be set and used internally if the node reconnects successfully.
467
478
  */
468
479
  connect(connectOptions?: CommissioningControllerNodeOptions) {
469
480
  if (connectOptions !== undefined) {
470
- this.options = connectOptions;
481
+ this.#options = connectOptions;
471
482
  }
472
483
  this.triggerReconnect();
473
484
  }
474
485
 
475
486
  /**
476
487
  * Trigger a reconnection to the device. This method is non-blocking and will return immediately.
477
- * The reconnection happens in the background. Please monitor the state of the node to see if the
488
+ * The reconnection happens in the background. Please monitor the state events of the node to see if the
478
489
  * reconnection was successful.
479
490
  */
480
491
  triggerReconnect() {
@@ -496,7 +507,7 @@ export class PairedNode {
496
507
  */
497
508
  async reconnect(connectOptions?: CommissioningControllerNodeOptions) {
498
509
  if (connectOptions !== undefined) {
499
- this.options = connectOptions;
510
+ this.#options = connectOptions;
500
511
  }
501
512
  if (this.#reconnectionInProgress || this.#remoteInitializationInProgress) {
502
513
  logger.debug(
@@ -583,7 +594,7 @@ export class PairedNode {
583
594
  }
584
595
 
585
596
  async #initializeFromStoredData(storedAttributeData: DecodedAttributeReportValue<any>[]) {
586
- const { autoSubscribe } = this.options;
597
+ const { autoSubscribe } = this.#options;
587
598
  if (this.#remoteInitializationDone || this.#localInitializationDone || autoSubscribe === false) return;
588
599
 
589
600
  // Minimum sanity check that we have at least data for the Root endpoint and one other endpoint to initialize
@@ -621,7 +632,7 @@ export class PairedNode {
621
632
  try {
622
633
  // Enforce a new Connection
623
634
  await this.#ensureConnection(true);
624
- const { autoSubscribe, attributeChangedCallback, eventTriggeredCallback } = this.options;
635
+ const { autoSubscribe, attributeChangedCallback, eventTriggeredCallback } = this.#options;
625
636
 
626
637
  let deviceDetailsUpdated = false;
627
638
  // We need to query some Device metadata because we do not have them (or update them anyway)
@@ -664,7 +675,7 @@ export class PairedNode {
664
675
  }
665
676
  if (!this.#remoteInitializationDone) {
666
677
  try {
667
- await this.commissioningController.validateAndUpdateFabricLabel(this.nodeId);
678
+ await this.#commissioningController.validateAndUpdateFabricLabel(this.nodeId);
668
679
  } catch (error) {
669
680
  logger.info(`Node ${this.nodeId}: Error updating fabric label`, error);
670
681
  }
@@ -683,8 +694,9 @@ export class PairedNode {
683
694
  }
684
695
 
685
696
  /**
686
- * Request the current InteractionClient for custom special case interactions with the device. Usually the
687
- * ClusterClients of the Devices of the node should be used instead.
697
+ * Request the current InteractionClient for custom special interactions with the device. Usually the
698
+ * ClusterClients of the Devices of the node should be used instead. An own InteractionClient is only needed
699
+ * when you want to read or write multiple attributes or events in a single request or send batch invokes.
688
700
  */
689
701
  getInteractionClient() {
690
702
  return this.#ensureConnection();
@@ -714,7 +726,7 @@ export class PairedNode {
714
726
  let { ignoreInitialTriggers = false } = options;
715
727
 
716
728
  const { minIntervalFloorSeconds, maxIntervalCeilingSeconds } =
717
- this.#nodeDetails.determineSubscriptionParameters(this.options);
729
+ this.#nodeDetails.determineSubscriptionParameters(this.#options);
718
730
  const { threadConnected } = this.#nodeDetails.meta ?? {};
719
731
 
720
732
  this.#invalidateSubscriptionHandler();
@@ -835,6 +847,7 @@ export class PairedNode {
835
847
  return initialSubscriptionData;
836
848
  }
837
849
 
850
+ /** Read all attributes of the devices and return them. If a stored state exists this is used to minimize needed traffic. */
838
851
  async readAllAttributes() {
839
852
  return this.#interactionClient.getAllAttributes({
840
853
  dataVersionFilters: this.#interactionClient.getCachedClusterDataVersions(),
@@ -909,7 +922,7 @@ export class PairedNode {
909
922
  async #updateEndpointStructure() {
910
923
  const allClusterAttributes = await this.readAllAttributes();
911
924
  await this.#initializeEndpointStructure(allClusterAttributes, true);
912
- this.options.stateInformationCallback?.(this.nodeId, NodeStateInformation.StructureChanged);
925
+ this.#options.stateInformationCallback?.(this.nodeId, NodeStateInformation.StructureChanged);
913
926
  this.events.structureChanged.emit();
914
927
  }
915
928
 
@@ -1029,14 +1042,7 @@ export class PairedNode {
1029
1042
  }
1030
1043
  }
1031
1044
 
1032
- /**
1033
- * Create a device object from the data read from the device.
1034
- *
1035
- * @param endpointId Endpoint ID
1036
- * @param data Data of all clusters read from the device
1037
- * @param interactionClient InteractionClient to use for the device
1038
- * @private
1039
- */
1045
+ /** Create a device object from the data read from the device. */
1040
1046
  #createDevice(
1041
1047
  endpointId: EndpointNumber,
1042
1048
  data: { [key: ClusterId]: { [key: string]: any } },
@@ -1136,7 +1142,7 @@ export class PairedNode {
1136
1142
  }
1137
1143
  }
1138
1144
 
1139
- /** Returns the functional devices/endpoints (those below the Root Endpoint) known for this node. */
1145
+ /** Returns the functional devices/endpoints (the "childs" of the Root Endpoint) known for this node. */
1140
1146
  getDevices(): EndpointInterface[] {
1141
1147
  return this.#endpoints.get(EndpointNumber(0))?.getChildEndpoints() ?? [];
1142
1148
  }
@@ -1146,13 +1152,14 @@ export class PairedNode {
1146
1152
  return this.#endpoints.get(EndpointNumber(endpointId));
1147
1153
  }
1148
1154
 
1155
+ /** Returns the Root Endpoint of the device. */
1149
1156
  getRootEndpoint() {
1150
1157
  return this.getDeviceById(0);
1151
1158
  }
1152
1159
 
1153
1160
  /** De-Commission (unpair) the device from this controller by removing the fabric from the device. */
1154
1161
  async decommission() {
1155
- if (!this.commissioningController.isNodeCommissioned(this.nodeId)) {
1162
+ if (!this.#commissioningController.isNodeCommissioned(this.nodeId)) {
1156
1163
  throw new ImplementationError(`This Node ${this.nodeId} is not commissioned.`);
1157
1164
  }
1158
1165
  if (
@@ -1180,10 +1187,14 @@ export class PairedNode {
1180
1187
  );
1181
1188
  }
1182
1189
  this.#setConnectionState(NodeStates.Disconnected);
1183
- await this.commissioningController.removeNode(this.nodeId, false);
1190
+ await this.#commissioningController.removeNode(this.nodeId, false);
1184
1191
  }
1185
1192
 
1186
- /** Opens a Basic Commissioning Window (uses the original Passcode printed on the device) with the device. */
1193
+ /**
1194
+ * Opens a Basic Commissioning Window (uses the original Passcode printed on the device) with the device.
1195
+ * This is an optional method, so it might not be supported by all devices and could be rejected with an error in
1196
+ * this case! Better use openEnhancedCommissioningWindow() instead.
1197
+ */
1187
1198
  async openBasicCommissioningWindow(commissioningTimeout = 900 /* 15 minutes */) {
1188
1199
  const adminCommissioningCluster = this.getRootClusterClient(AdministratorCommissioning.Cluster.with("Basic"));
1189
1200
  if (adminCommissioningCluster === undefined) {
@@ -1277,18 +1288,20 @@ export class PairedNode {
1277
1288
  };
1278
1289
  }
1279
1290
 
1291
+ /** Closes the current session, ends the subscription and disconnects the device. */
1280
1292
  async disconnect() {
1281
1293
  this.close();
1282
- await this.commissioningController.disconnectNode(this.nodeId);
1294
+ await this.#commissioningController.disconnectNode(this.nodeId);
1283
1295
  }
1284
1296
 
1297
+ /** Closes the current subscription and ends all timers for reconnects or such used by this PairedNode instance. */
1285
1298
  close(sendDecommissionedStatus = false) {
1286
1299
  this.#newChannelReconnectDelayTimer.stop();
1287
1300
  this.#reconnectDelayTimer?.stop();
1288
1301
  this.#reconnectDelayTimer = undefined;
1289
1302
  this.#updateEndpointStructureTimer.stop();
1290
1303
  if (sendDecommissionedStatus) {
1291
- this.options.stateInformationCallback?.(this.nodeId, NodeStateInformation.Decommissioned);
1304
+ this.#options.stateInformationCallback?.(this.nodeId, NodeStateInformation.Decommissioned);
1292
1305
  this.events.decommissioned.emit();
1293
1306
  }
1294
1307
  this.#setConnectionState(NodeStates.Disconnected);