@project-chip/matter.js 0.11.9-alpha.0-20241207-b604cfa44 → 0.11.9-alpha.0-20241208-09ad57f78

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.
@@ -206,7 +206,7 @@ export class PairedNode {
206
206
  readonly #updateEndpointStructureTimer = Time.getTimer(
207
207
  "Endpoint structure update",
208
208
  STRUCTURE_UPDATE_TIMEOUT_MS,
209
- async () => await this.updateEndpointStructure(),
209
+ async () => await this.#updateEndpointStructure(),
210
210
  );
211
211
  #connectionState: NodeStates = NodeStates.Disconnected;
212
212
  #reconnectionInProgress = false;
@@ -294,7 +294,7 @@ export class PairedNode {
294
294
  }`,
295
295
  );
296
296
  if (this.#connectionState === NodeStates.Connected) {
297
- this.scheduleReconnect();
297
+ this.#scheduleReconnect();
298
298
  }
299
299
  });
300
300
 
@@ -310,7 +310,7 @@ export class PairedNode {
310
310
  logger.info(`Node ${this.nodeId}: Got a reconnect, so reconnection not needed anymore ...`);
311
311
  this.#reconnectDelayTimer?.stop();
312
312
  this.#reconnectDelayTimer = undefined;
313
- this.setConnectionState(NodeStates.Connected);
313
+ this.#setConnectionState(NodeStates.Connected);
314
314
  }
315
315
  });
316
316
  this.#nodeDetails = new DeviceInformation(nodeId, knownNodeDetails);
@@ -319,15 +319,15 @@ export class PairedNode {
319
319
  this.#construction = Construction(this, async () => {
320
320
  // We try to initialize from stored data already
321
321
  if (storedAttributeData !== undefined) {
322
- await this.initializeFromStoredData(storedAttributeData);
322
+ await this.#initializeFromStoredData(storedAttributeData);
323
323
  }
324
324
 
325
325
  // This kicks of the remote initialization and automatic reconnection handling if it can not be connected
326
- this.initialize().catch(error => {
326
+ this.#initialize().catch(error => {
327
327
  logger.info(`Node ${nodeId}: Error during remote initialization`, error);
328
328
  if (this.state !== NodeStates.Disconnected) {
329
- this.setConnectionState(NodeStates.WaitingForDeviceDiscovery);
330
- this.scheduleReconnect();
329
+ this.#setConnectionState(NodeStates.WaitingForDeviceDiscovery);
330
+ this.#scheduleReconnect();
331
331
  }
332
332
  });
333
333
  });
@@ -365,7 +365,7 @@ export class PairedNode {
365
365
  return this.#remoteInitializationDone || this.#localInitializationDone;
366
366
  }
367
367
 
368
- private setConnectionState(state: NodeStates) {
368
+ #setConnectionState(state: NodeStates) {
369
369
  if (
370
370
  this.#connectionState === state ||
371
371
  (this.#connectionState === NodeStates.WaitingForDeviceDiscovery && state === NodeStates.Reconnecting)
@@ -381,7 +381,7 @@ export class PairedNode {
381
381
  }
382
382
 
383
383
  /** Make sure to not request a new Interaction client multiple times in parallel. */
384
- async handleReconnect(discoveryType?: NodeDiscoveryType): Promise<void> {
384
+ async #handleReconnect(discoveryType?: NodeDiscoveryType): Promise<void> {
385
385
  if (this.#clientReconnectInProgress) {
386
386
  throw new NodeNotConnectedError("Reconnection already in progress. Node not reachable currently.");
387
387
  }
@@ -395,47 +395,74 @@ export class PairedNode {
395
395
  }
396
396
 
397
397
  /**
398
- * Force a reconnection to the device. This method is mainly used internally to reconnect after the active session
398
+ * Trigger a reconnection to the device. This method is non-blocking and will return immediately.
399
+ * The reconnection happens in the background. Please monitor the state of the node to see if the
400
+ * reconnection was successful.
401
+ */
402
+ triggerReconnect() {
403
+ if (this.#reconnectionInProgress || this.#remoteInitializationInProgress) {
404
+ logger.info(
405
+ `Ignoring reconnect request because ${this.#remoteInitializationInProgress ? "init" : "reconnect"} already underway.`,
406
+ );
407
+ return;
408
+ }
409
+ this.#scheduleReconnect(0);
410
+ }
411
+
412
+ /**
413
+ * Force a reconnection to the device.
414
+ * This method is mainly used internally to reconnect after the active session
399
415
  * was closed or the device went offline and was detected as being online again.
416
+ * Please note that this method does not return until the device is reconnected.
417
+ * Please use triggerReconnect method for a non-blocking reconnection triggering.
400
418
  */
401
419
  async reconnect(connectOptions?: CommissioningControllerNodeOptions) {
402
420
  if (connectOptions !== undefined) {
403
421
  this.options = connectOptions;
404
422
  }
405
- if (this.#reconnectionInProgress) {
406
- logger.debug("Reconnection already in progress ...");
423
+ if (this.#reconnectionInProgress || this.#remoteInitializationInProgress) {
424
+ logger.debug(
425
+ `Ignoring reconnect request because ${this.#remoteInitializationInProgress ? "init" : "reconnect"} already underway.`,
426
+ );
407
427
  return;
408
428
  }
429
+ if (this.#reconnectDelayTimer?.isRunning) {
430
+ this.#reconnectDelayTimer.stop();
431
+ }
409
432
 
410
433
  this.#reconnectionInProgress = true;
411
434
  if (this.#connectionState !== NodeStates.WaitingForDeviceDiscovery) {
412
- this.setConnectionState(NodeStates.Reconnecting);
435
+ this.#setConnectionState(NodeStates.Reconnecting);
413
436
 
414
437
  try {
415
438
  // First try a reconnect to known addresses to see if the device is reachable
416
- await this.handleReconnect(NodeDiscoveryType.None);
439
+ await this.#handleReconnect(NodeDiscoveryType.None);
417
440
  this.#reconnectionInProgress = false;
418
- await this.initialize();
441
+ await this.#initialize();
419
442
  return;
420
443
  } catch (error) {
421
- MatterError.accept(error);
422
- logger.info(
423
- `Node ${this.nodeId}: Simple re-establishing session did not worked. Reconnect ... `,
424
- error,
425
- );
444
+ if (error instanceof MatterError) {
445
+ logger.info(
446
+ `Node ${this.nodeId}: Simple re-establishing session did not worked. Reconnect ... `,
447
+ error,
448
+ );
449
+ } else {
450
+ this.#reconnectionInProgress = false;
451
+ throw error;
452
+ }
426
453
  }
427
454
  }
428
455
 
429
- this.setConnectionState(NodeStates.WaitingForDeviceDiscovery);
456
+ this.#setConnectionState(NodeStates.WaitingForDeviceDiscovery);
430
457
 
431
458
  try {
432
- await this.initialize();
459
+ await this.#initialize();
433
460
  } catch (error) {
434
461
  MatterError.accept(error);
435
462
 
436
463
  if (error instanceof UnknownNodeError) {
437
464
  logger.info(`Node ${this.nodeId}: Node is unknown by controller, we can not connect.`);
438
- this.setConnectionState(NodeStates.Disconnected);
465
+ this.#setConnectionState(NodeStates.Disconnected);
439
466
  } else if (this.#connectionState === NodeStates.Disconnected) {
440
467
  logger.info("No reconnection desired because requested status is Disconnected.");
441
468
  } else {
@@ -447,18 +474,19 @@ export class PairedNode {
447
474
  logger.info(`Node ${this.nodeId}: Error waiting for device rediscovery, retrying`, error);
448
475
  }
449
476
  this.#reconnectErrorCount++;
450
- this.scheduleReconnect();
477
+ this.#scheduleReconnect();
451
478
  }
479
+ } finally {
480
+ this.#reconnectionInProgress = false;
452
481
  }
453
- this.#reconnectionInProgress = false;
454
482
  }
455
483
 
456
484
  /** Ensure that the node is connected by creating a new InteractionClient if needed. */
457
- private async ensureConnection(forceConnect = false): Promise<InteractionClient> {
485
+ async #ensureConnection(forceConnect = false): Promise<InteractionClient> {
458
486
  if (this.#connectionState === NodeStates.Disconnected) {
459
487
  // Disconnected and having an InteractionClient means we initialized with an Offline one, so we do
460
488
  // connection now on usage
461
- this.setConnectionState(NodeStates.Reconnecting);
489
+ this.#setConnectionState(NodeStates.Reconnecting);
462
490
  return this.#interactionClient;
463
491
  }
464
492
  if (this.#connectionState === NodeStates.Connected && !forceConnect) {
@@ -466,17 +494,17 @@ export class PairedNode {
466
494
  }
467
495
 
468
496
  if (forceConnect) {
469
- this.setConnectionState(NodeStates.WaitingForDeviceDiscovery);
497
+ this.#setConnectionState(NodeStates.WaitingForDeviceDiscovery);
470
498
  }
471
499
 
472
- await this.handleReconnect(NodeDiscoveryType.FullDiscovery);
500
+ await this.#handleReconnect(NodeDiscoveryType.FullDiscovery);
473
501
  if (!forceConnect) {
474
- this.setConnectionState(NodeStates.Connected);
502
+ this.#setConnectionState(NodeStates.Connected);
475
503
  }
476
504
  return this.#interactionClient;
477
505
  }
478
506
 
479
- private async initializeFromStoredData(storedAttributeData: DecodedAttributeReportValue<any>[]) {
507
+ async #initializeFromStoredData(storedAttributeData: DecodedAttributeReportValue<any>[]) {
480
508
  const { autoSubscribe } = this.options;
481
509
  if (this.#remoteInitializationDone || this.#localInitializationDone || autoSubscribe === false) return;
482
510
 
@@ -496,7 +524,7 @@ export class PairedNode {
496
524
  return;
497
525
  }
498
526
 
499
- await this.initializeEndpointStructure(storedAttributeData);
527
+ await this.#initializeEndpointStructure(storedAttributeData);
500
528
 
501
529
  // Inform interested parties that the node is initialized
502
530
  await this.events.initialized.emit(this.#nodeDetails.toStorageData());
@@ -506,7 +534,7 @@ export class PairedNode {
506
534
  /**
507
535
  * Initialize the node after the InteractionClient was created and to subscribe attributes and events if requested.
508
536
  */
509
- private async initialize() {
537
+ async #initialize() {
510
538
  if (this.#remoteInitializationInProgress) {
511
539
  logger.info(`Node ${this.nodeId}: Remote initialization already in progress ...`);
512
540
  return;
@@ -514,7 +542,7 @@ export class PairedNode {
514
542
  this.#remoteInitializationInProgress = true;
515
543
  try {
516
544
  // Enforce a new Connection
517
- await this.ensureConnection(true);
545
+ await this.#ensureConnection(true);
518
546
  const { autoSubscribe, attributeChangedCallback, eventTriggeredCallback } = this.options;
519
547
 
520
548
  let deviceDetailsUpdated = false;
@@ -541,7 +569,10 @@ export class PairedNode {
541
569
  if (initialSubscriptionData.attributeReports === undefined) {
542
570
  throw new InternalError("No attribute reports received when subscribing to all values!");
543
571
  }
544
- await this.initializeEndpointStructure(initialSubscriptionData.attributeReports, anyInitializationDone);
572
+ await this.#initializeEndpointStructure(
573
+ initialSubscriptionData.attributeReports,
574
+ anyInitializationDone,
575
+ );
545
576
 
546
577
  if (!deviceDetailsUpdated) {
547
578
  const rootEndpoint = this.getRootEndpoint();
@@ -551,10 +582,10 @@ export class PairedNode {
551
582
  }
552
583
  } else {
553
584
  const allClusterAttributes = await this.readAllAttributes();
554
- await this.initializeEndpointStructure(allClusterAttributes, anyInitializationDone);
585
+ await this.#initializeEndpointStructure(allClusterAttributes, anyInitializationDone);
555
586
  }
556
587
  this.#reconnectErrorCount = 0;
557
- this.setConnectionState(NodeStates.Connected);
588
+ this.#setConnectionState(NodeStates.Connected);
558
589
  await this.events.initializedFromRemote.emit(this.#nodeDetails.toStorageData());
559
590
  if (!this.#localInitializationDone) {
560
591
  await this.events.initialized.emit(this.#nodeDetails.toStorageData());
@@ -571,7 +602,7 @@ export class PairedNode {
571
602
  * ClusterClients of the Devices of the node should be used instead.
572
603
  */
573
604
  getInteractionClient() {
574
- return this.ensureConnection();
605
+ return this.#ensureConnection();
575
606
  }
576
607
 
577
608
  /** Method to log the structure of this node with all endpoint and clusters. */
@@ -676,17 +707,17 @@ export class PairedNode {
676
707
  },
677
708
  updateTimeoutHandler: async () => {
678
709
  logger.info(`Node ${this.nodeId}: Subscription timed out ... trying to re-establish ...`);
679
- this.setConnectionState(NodeStates.Reconnecting);
710
+ this.#setConnectionState(NodeStates.Reconnecting);
680
711
  this.#reconnectionInProgress = true;
681
712
  try {
682
713
  await this.subscribeAllAttributesAndEvents({ ...options, ignoreInitialTriggers: false });
683
- this.setConnectionState(NodeStates.Connected);
714
+ this.#setConnectionState(NodeStates.Connected);
684
715
  } catch (error) {
685
716
  logger.info(
686
717
  `Node ${this.nodeId}: Error resubscribing to all attributes and events. Try to reconnect ...`,
687
718
  error,
688
719
  );
689
- this.scheduleReconnect();
720
+ this.#scheduleReconnect();
690
721
  } finally {
691
722
  this.#reconnectionInProgress = false;
692
723
  }
@@ -696,7 +727,7 @@ export class PairedNode {
696
727
  logger.info(`Node ${this.nodeId}: Got subscription update, so reconnection not needed anymore ...`);
697
728
  this.#reconnectDelayTimer.stop();
698
729
  this.#reconnectDelayTimer = undefined;
699
- this.setConnectionState(NodeStates.Connected);
730
+ this.#setConnectionState(NodeStates.Connected);
700
731
  }
701
732
  },
702
733
  };
@@ -768,19 +799,19 @@ export class PairedNode {
768
799
  #checkEventsForNeededStructureUpdate(_endpointId: EndpointNumber, clusterId: ClusterId, eventId: EventId) {
769
800
  // When we subscribe all data here then we can also catch this case and handle it
770
801
  if (clusterId === BasicInformation.Cluster.id && eventId === BasicInformation.Cluster.events.shutDown.id) {
771
- this.handleNodeShutdown();
802
+ this.#handleNodeShutdown();
772
803
  }
773
804
  }
774
805
 
775
806
  /** Handles a node shutDown event (if supported by the node and received). */
776
- private handleNodeShutdown() {
807
+ #handleNodeShutdown() {
777
808
  logger.info(`Node ${this.nodeId}: Node shutdown detected, trying to reconnect ...`);
778
- this.scheduleReconnect(RECONNECT_DELAY_AFTER_SHUTDOWN_MS);
809
+ this.#scheduleReconnect(RECONNECT_DELAY_AFTER_SHUTDOWN_MS);
779
810
  }
780
811
 
781
- private scheduleReconnect(delay?: number) {
812
+ #scheduleReconnect(delay?: number) {
782
813
  if (this.state !== NodeStates.WaitingForDeviceDiscovery) {
783
- this.setConnectionState(NodeStates.Reconnecting);
814
+ this.#setConnectionState(NodeStates.Reconnecting);
784
815
  }
785
816
 
786
817
  if (!this.#reconnectDelayTimer?.isRunning) {
@@ -796,15 +827,15 @@ export class PairedNode {
796
827
  this.#reconnectDelayTimer.start();
797
828
  }
798
829
 
799
- async updateEndpointStructure() {
830
+ async #updateEndpointStructure() {
800
831
  const allClusterAttributes = await this.readAllAttributes();
801
- await this.initializeEndpointStructure(allClusterAttributes, true);
832
+ await this.#initializeEndpointStructure(allClusterAttributes, true);
802
833
  this.options.stateInformationCallback?.(this.nodeId, NodeStateInformation.StructureChanged);
803
834
  this.events.structureChanged.emit();
804
835
  }
805
836
 
806
837
  /** Reads all data from the device and create a device object structure out of it. */
807
- private async initializeEndpointStructure(
838
+ async #initializeEndpointStructure(
808
839
  allClusterAttributes: DecodedAttributeReportValue<any>[],
809
840
  updateStructure = false,
810
841
  ) {
@@ -847,15 +878,15 @@ export class PairedNode {
847
878
  logger.debug("Creating device", endpointId, Logger.toJSON(clusters));
848
879
  this.#endpoints.set(
849
880
  endpointIdNumber,
850
- this.createDevice(endpointIdNumber, clusters, this.#interactionClient),
881
+ this.#createDevice(endpointIdNumber, clusters, this.#interactionClient),
851
882
  );
852
883
  }
853
884
 
854
- this.structureEndpoints(partLists);
885
+ this.#structureEndpoints(partLists);
855
886
  }
856
887
 
857
888
  /** Bring the endpoints in a structure based on their partsList attribute. */
858
- private structureEndpoints(partLists: Map<EndpointNumber, EndpointNumber[]>) {
889
+ #structureEndpoints(partLists: Map<EndpointNumber, EndpointNumber[]>) {
859
890
  logger.debug(`Node ${this.nodeId}: Endpoints from PartsLists`, Logger.toJSON(Array.from(partLists.entries())));
860
891
 
861
892
  const endpointUsages: { [key: EndpointNumber]: EndpointNumber[] } = {};
@@ -927,7 +958,7 @@ export class PairedNode {
927
958
  * @param interactionClient InteractionClient to use for the device
928
959
  * @private
929
960
  */
930
- private createDevice(
961
+ #createDevice(
931
962
  endpointId: EndpointNumber,
932
963
  data: { [key: ClusterId]: { [key: string]: any } },
933
964
  interactionClient: InteractionClient,
@@ -1069,7 +1100,7 @@ export class PairedNode {
1069
1100
  `Removing node ${this.nodeId} failed with status ${result.statusCode} "${result.debugText}".`,
1070
1101
  );
1071
1102
  }
1072
- this.setConnectionState(NodeStates.Disconnected);
1103
+ this.#setConnectionState(NodeStates.Disconnected);
1073
1104
  await this.commissioningController.removeNode(this.nodeId, false);
1074
1105
  }
1075
1106
 
@@ -1180,7 +1211,7 @@ export class PairedNode {
1180
1211
  this.options.stateInformationCallback?.(this.nodeId, NodeStateInformation.Decommissioned);
1181
1212
  this.events.decommissioned.emit();
1182
1213
  }
1183
- this.setConnectionState(NodeStates.Disconnected);
1214
+ this.#setConnectionState(NodeStates.Disconnected);
1184
1215
  }
1185
1216
 
1186
1217
  /**