@project-chip/matter.js 0.16.0-alpha.0-20251001-7eb06da95 → 0.16.0-alpha.0-20251004-92135c7df

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.
Files changed (49) hide show
  1. package/dist/cjs/CommissioningController.d.ts +2 -2
  2. package/dist/cjs/CommissioningController.d.ts.map +1 -1
  3. package/dist/cjs/CommissioningController.js +2 -2
  4. package/dist/cjs/CommissioningController.js.map +1 -1
  5. package/dist/cjs/MatterController.d.ts +6 -6
  6. package/dist/cjs/MatterController.d.ts.map +1 -1
  7. package/dist/cjs/MatterController.js +12 -12
  8. package/dist/cjs/MatterController.js.map +1 -1
  9. package/dist/cjs/PaseCommissioner.js +1 -1
  10. package/dist/cjs/PaseCommissioner.js.map +1 -1
  11. package/dist/cjs/device/CachedClientNodeStore.d.ts +5 -4
  12. package/dist/cjs/device/CachedClientNodeStore.d.ts.map +1 -1
  13. package/dist/cjs/device/CachedClientNodeStore.js +19 -2
  14. package/dist/cjs/device/CachedClientNodeStore.js.map +1 -1
  15. package/dist/cjs/device/Endpoint.d.ts +1 -1
  16. package/dist/cjs/device/Endpoint.d.ts.map +1 -1
  17. package/dist/cjs/device/Endpoint.js.map +1 -1
  18. package/dist/cjs/device/PairedNode.d.ts +52 -28
  19. package/dist/cjs/device/PairedNode.d.ts.map +1 -1
  20. package/dist/cjs/device/PairedNode.js +196 -77
  21. package/dist/cjs/device/PairedNode.js.map +1 -1
  22. package/dist/esm/CommissioningController.d.ts +2 -2
  23. package/dist/esm/CommissioningController.d.ts.map +1 -1
  24. package/dist/esm/CommissioningController.js +3 -3
  25. package/dist/esm/CommissioningController.js.map +1 -1
  26. package/dist/esm/MatterController.d.ts +6 -6
  27. package/dist/esm/MatterController.d.ts.map +1 -1
  28. package/dist/esm/MatterController.js +13 -13
  29. package/dist/esm/MatterController.js.map +1 -1
  30. package/dist/esm/PaseCommissioner.js +1 -1
  31. package/dist/esm/PaseCommissioner.js.map +1 -1
  32. package/dist/esm/device/CachedClientNodeStore.d.ts +5 -4
  33. package/dist/esm/device/CachedClientNodeStore.d.ts.map +1 -1
  34. package/dist/esm/device/CachedClientNodeStore.js +19 -2
  35. package/dist/esm/device/CachedClientNodeStore.js.map +1 -1
  36. package/dist/esm/device/Endpoint.d.ts +1 -1
  37. package/dist/esm/device/Endpoint.d.ts.map +1 -1
  38. package/dist/esm/device/Endpoint.js.map +1 -1
  39. package/dist/esm/device/PairedNode.d.ts +52 -28
  40. package/dist/esm/device/PairedNode.d.ts.map +1 -1
  41. package/dist/esm/device/PairedNode.js +197 -78
  42. package/dist/esm/device/PairedNode.js.map +1 -1
  43. package/package.json +8 -8
  44. package/src/CommissioningController.ts +3 -3
  45. package/src/MatterController.ts +18 -18
  46. package/src/PaseCommissioner.ts +1 -1
  47. package/src/device/CachedClientNodeStore.ts +24 -4
  48. package/src/device/Endpoint.ts +1 -1
  49. package/src/device/PairedNode.ts +292 -89
@@ -3,9 +3,10 @@
3
3
  * Copyright 2022-2025 Matter.js Authors
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
+ import { Descriptor } from "#clusters";
6
7
  import { AsyncObservable, BasicSet, Construction, Crypto, Diagnostic, MatterError, Observable } from "#general";
7
8
  import { Behavior, Commands } from "#node";
8
- import { ClusterClientObj, DecodedAttributeReportValue, DecodedEventReportValue, InteractionClient, NodeDiscoveryType, NodeSession } from "#protocol";
9
+ import { AttributeClientValues, ClusterClientObj, DecodedAttributeReportValue, DecodedEventReportValue, InteractionClient, NodeDiscoveryType, NodeSession, StructuredReadAttributeData } from "#protocol";
9
10
  import { CaseAuthenticatedTag, ClusterId, ClusterType, EndpointNumber, NodeId } from "#types";
10
11
  import { ClusterServerObj } from "../cluster/server/ClusterServerTypes.js";
11
12
  import { CommissioningController } from "../CommissioningController.js";
@@ -120,6 +121,7 @@ export type CommissioningControllerNodeOptions = {
120
121
  };
121
122
  export declare class NodeNotConnectedError extends MatterError {
122
123
  }
124
+ type DescriptorData = AttributeClientValues<typeof Descriptor.Complete.attributes>;
123
125
  /**
124
126
  * Class to represents one node that is paired/commissioned with the matter.js Controller. Instances are returned by
125
127
  * the CommissioningController on commissioning or when connecting.
@@ -128,33 +130,7 @@ export declare class PairedNode {
128
130
  #private;
129
131
  [Diagnostic.value]: unknown;
130
132
  readonly nodeId: NodeId;
131
- readonly events: {
132
- /**
133
- * Emitted when the node is initialized from local data. These data usually are stale, but you can still already
134
- * use the node to interact with the device. If no local data are available this event will be emitted together
135
- * with the initializedFromRemote event.
136
- */
137
- initialized: AsyncObservable<[details: DeviceInformationData], void>;
138
- /**
139
- * Emitted when the node is fully initialized from remote and all attributes and events are subscribed.
140
- * This event can also be awaited if code needs to be blocked until the node is fully initialized.
141
- */
142
- initializedFromRemote: AsyncObservable<[details: DeviceInformationData], void>;
143
- /** Emitted when the state of the node changes. */
144
- stateChanged: Observable<[nodeState: NodeStates], void>;
145
- /**
146
- * Emitted when an attribute value changes. If the oldValue is undefined then no former value was known.
147
- */
148
- attributeChanged: Observable<[data: DecodedAttributeReportValue<any>, oldValue: any], void>;
149
- /** Emitted when an event is triggered. */
150
- eventTriggered: Observable<[DecodedEventReportValue<any>], void>;
151
- /** Emitted when the structure of the node changes (Endpoints got added or also removed). */
152
- structureChanged: Observable<[void], void>;
153
- /** Emitted when the node is decommissioned. */
154
- decommissioned: Observable<[void], void>;
155
- /** Emitted when a subscription alive trigger is received (max interval trigger or any data update) */
156
- connectionAlive: Observable<[void], void>;
157
- };
133
+ readonly events: PairedNode.Events;
158
134
  static create(nodeId: NodeId, commissioningController: CommissioningController, options: CommissioningControllerNodeOptions | undefined, knownNodeDetails: DeviceInformationData, interactionClient: InteractionClient, reconnectFunc: ReconnectionCallback, assignDisconnectedHandler: (handler: () => Promise<void>) => void, sessions: BasicSet<NodeSession>, crypto: Crypto, storedAttributeData?: DecodedAttributeReportValue<any>[]): Promise<PairedNode>;
159
135
  constructor(nodeId: NodeId, commissioningController: CommissioningController, options: CommissioningControllerNodeOptions | undefined, knownNodeDetails: DeviceInformationData, interactionClient: InteractionClient, reconnectFunc: ReconnectionCallback, assignDisconnectedHandler: (handler: () => Promise<void>) => void, sessions: BasicSet<NodeSession, NodeSession>, crypto: Crypto, storedAttributeData?: DecodedAttributeReportValue<any>[]);
160
136
  get construction(): Construction<PairedNode>;
@@ -217,6 +193,12 @@ export declare class PairedNode {
217
193
  }>;
218
194
  /** Read all attributes of the devices and return them. If a stored state exists this is used to minimize needed traffic. */
219
195
  readAllAttributes(): Promise<DecodedAttributeReportValue<any>[]>;
196
+ /**
197
+ * Traverse the structure data and collect all data for the given endpointId.
198
+ * Return true if data for the endpoint was found, otherwise false.
199
+ * If data was found it is added to the collectedData map.
200
+ */
201
+ collectDescriptorData(structure: StructuredReadAttributeData, endpointId: EndpointNumber, collectedData: Map<EndpointNumber, DescriptorData>): void;
220
202
  /** Returns all parts (endpoints) known for the Root Endpoint of this node. */
221
203
  get parts(): Map<number, Endpoint>;
222
204
  /** Returns the functional devices/endpoints (the "childs" of the Root Endpoint) known for this node. */
@@ -307,4 +289,46 @@ export declare class PairedNode {
307
289
  */
308
290
  commandsOf<T extends Behavior.Type>(type: T): Commands.OfBehavior<T>;
309
291
  }
292
+ export declare namespace PairedNode {
293
+ interface NodeStructureEvents {
294
+ /** Emitted when endpoints are added. */
295
+ nodeEndpointAdded: Observable<[EndpointNumber]>;
296
+ /** Emitted when endpoints are removed. */
297
+ nodeEndpointRemoved: Observable<[EndpointNumber]>;
298
+ /** Emitted when endpoints are updated (e.g. device type changed, structure changed). */
299
+ nodeEndpointChanged: Observable<[EndpointNumber]>;
300
+ }
301
+ interface Events extends NodeStructureEvents {
302
+ /**
303
+ * Emitted when the node is initialized from local data. These data usually are stale, but you can still already
304
+ * use the node to interact with the device. If no local data are available this event will be emitted together
305
+ * with the initializedFromRemote event.
306
+ */
307
+ initialized: AsyncObservable<[details: DeviceInformationData]>;
308
+ /**
309
+ * Emitted when the node is fully initialized from remote and all attributes and events are subscribed.
310
+ * This event can also be awaited if code needs to be blocked until the node is fully initialized.
311
+ */
312
+ initializedFromRemote: AsyncObservable<[details: DeviceInformationData]>;
313
+ /** Emitted when the state of the node changes. */
314
+ stateChanged: Observable<[nodeState: NodeStates]>;
315
+ /**
316
+ * Emitted when an attribute value changes. If the oldValue is undefined then no former value was known.
317
+ */
318
+ attributeChanged: Observable<[data: DecodedAttributeReportValue<any>, oldValue: any]>;
319
+ /** Emitted when an event is triggered. */
320
+ eventTriggered: Observable<[DecodedEventReportValue<any>]>;
321
+ /**
322
+ * Emitted when all node structure changes were applied (Endpoints got added or also removed).
323
+ * You can alternatively use the nodeEndpointAdded, nodeEndpointRemoved and nodeEndpointChanged events to react on specific changes.
324
+ * This event is emitted after all nodeEndpointAdded, nodeEndpointRemoved and nodeEndpointChanged events are emitted.
325
+ */
326
+ structureChanged: Observable<[void]>;
327
+ /** Emitted when the node is decommissioned. */
328
+ decommissioned: Observable<[void]>;
329
+ /** Emitted when a subscription alive trigger is received (max interval trigger or any data update) */
330
+ connectionAlive: Observable<[void]>;
331
+ }
332
+ }
333
+ export {};
310
334
  //# sourceMappingURL=PairedNode.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PairedNode.d.ts","sourceRoot":"","sources":["../../../src/device/PairedNode.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"PairedNode.d.ts","sourceRoot":"","sources":["../../../src/device/PairedNode.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,7 @@
3
3
  * Copyright 2022-2025 Matter.js Authors
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
- import { AdministratorCommissioning, BasicInformation, DescriptorCluster, OperationalCredentials } from "#clusters";
6
+ import { AdministratorCommissioning, BasicInformation, Descriptor, OperationalCredentials } from "#clusters";
7
7
  import {
8
8
  AsyncObservable,
9
9
  Construction,
@@ -74,6 +74,15 @@ var NodeStateInformation = /* @__PURE__ */ ((NodeStateInformation2) => {
74
74
  })(NodeStateInformation || {});
75
75
  class NodeNotConnectedError extends MatterError {
76
76
  }
77
+ function areNumberListsSame(list1, list2) {
78
+ const set1 = new Set(list1);
79
+ const set2 = new Set(list2);
80
+ if (set1.size !== set2.size) return false;
81
+ for (const entry of set1.values()) {
82
+ if (!set2.has(entry)) return false;
83
+ }
84
+ return true;
85
+ }
77
86
  class PairedNode {
78
87
  constructor(nodeId, commissioningController, options = {}, knownNodeDetails, interactionClient, reconnectFunc, assignDisconnectedHandler, sessions, crypto, storedAttributeData) {
79
88
  this.nodeId = nodeId;
@@ -167,31 +176,23 @@ class PairedNode {
167
176
  #reconnectFunc;
168
177
  #currentSubscriptionIntervalS;
169
178
  #crypto;
179
+ /**
180
+ * Endpoint structure change information that are checked when updating structure
181
+ * - null means that the endpoint itself changed, so will be regenerated completely any case
182
+ * - array of ClusterIds means that only these clusters changed and will be updated
183
+ */
184
+ #registeredEndpointStructureChanges = /* @__PURE__ */ new Map();
170
185
  events = {
171
- /**
172
- * Emitted when the node is initialized from local data. These data usually are stale, but you can still already
173
- * use the node to interact with the device. If no local data are available this event will be emitted together
174
- * with the initializedFromRemote event.
175
- */
176
186
  initialized: AsyncObservable(),
177
- /**
178
- * Emitted when the node is fully initialized from remote and all attributes and events are subscribed.
179
- * This event can also be awaited if code needs to be blocked until the node is fully initialized.
180
- */
181
187
  initializedFromRemote: AsyncObservable(),
182
- /** Emitted when the state of the node changes. */
183
188
  stateChanged: Observable(),
184
- /**
185
- * Emitted when an attribute value changes. If the oldValue is undefined then no former value was known.
186
- */
187
189
  attributeChanged: Observable(),
188
- /** Emitted when an event is triggered. */
189
190
  eventTriggered: Observable(),
190
- /** Emitted when the structure of the node changes (Endpoints got added or also removed). */
191
191
  structureChanged: Observable(),
192
- /** Emitted when the node is decommissioned. */
192
+ nodeEndpointAdded: Observable(),
193
+ nodeEndpointRemoved: Observable(),
194
+ nodeEndpointChanged: Observable(),
193
195
  decommissioned: Observable(),
194
- /** Emitted when a subscription alive trigger is received (max interval trigger or any data update) */
195
196
  connectionAlive: Observable()
196
197
  };
197
198
  static async create(nodeId, commissioningController, options = {}, knownNodeDetails, interactionClient, reconnectFunc, assignDisconnectedHandler, sessions, crypto, storedAttributeData) {
@@ -406,7 +407,7 @@ class PairedNode {
406
407
  })) {
407
408
  return;
408
409
  }
409
- await this.#initializeEndpointStructure(storedAttributeData);
410
+ await this.#initializeEndpointStructure(storedAttributeData, false);
410
411
  await this.events.initialized.emit(this.#nodeDetails.toStorageData());
411
412
  this.#localInitializationDone = true;
412
413
  }
@@ -444,7 +445,7 @@ class PairedNode {
444
445
  if (attributeReports === void 0) {
445
446
  throw new InternalError("No attribute reports received when subscribing to all values!");
446
447
  }
447
- await this.#initializeEndpointStructure(attributeReports, anyInitializationDone);
448
+ await this.#initializeEndpointStructure(attributeReports);
448
449
  this.#remoteInitializationInProgress = false;
449
450
  if (!deviceDetailsUpdated) {
450
451
  const rootEndpoint = this.getRootEndpoint();
@@ -455,7 +456,7 @@ class PairedNode {
455
456
  this.#currentSubscriptionIntervalS = maxInterval;
456
457
  } else {
457
458
  const allClusterAttributes = await this.readAllAttributes();
458
- await this.#initializeEndpointStructure(allClusterAttributes, anyInitializationDone);
459
+ await this.#initializeEndpointStructure(allClusterAttributes);
459
460
  this.#remoteInitializationInProgress = false;
460
461
  }
461
462
  if (!this.#remoteInitializationDone) {
@@ -594,18 +595,21 @@ class PairedNode {
594
595
  this.#reconnectDelayTimer = void 0;
595
596
  this.#setConnectionState(0 /* Connected */);
596
597
  }
598
+ if (this.#remoteInitializationDone && this.#registeredEndpointStructureChanges.size > 0 && !this.#updateEndpointStructureTimer.isRunning) {
599
+ logger.info(`Node ${this.nodeId}: Endpoint structure needs to be updated ...`);
600
+ this.#updateEndpointStructureTimer.stop().start();
601
+ }
597
602
  this.events.connectionAlive.emit();
598
603
  }
599
604
  };
600
605
  this.#currentSubscriptionHandler = subscriptionHandler;
601
606
  const maxKnownEventNumber = this.#interactionClient.maxKnownEventNumber;
602
- const attributeData = await this.#interactionClient.getAllAttributes({
607
+ await this.#interactionClient.getAllAttributes({
603
608
  dataVersionFilters: this.#interactionClient.getCachedClusterDataVersions(),
604
- executeQueued: !!threadConnected
609
+ executeQueued: !!threadConnected,
605
610
  // We queue subscriptions for thread devices
611
+ attributeChangeListener: subscriptionHandler.attributeListener
606
612
  });
607
- await this.#interactionClient.processAttributeUpdates(attributeData, subscriptionHandler.attributeListener);
608
- attributeData.length = 0;
609
613
  const initialSubscriptionData = await this.#interactionClient.subscribeAllAttributesAndEvents({
610
614
  isUrgent: true,
611
615
  minIntervalFloorSeconds,
@@ -631,30 +635,31 @@ class PairedNode {
631
635
  enrichCachedAttributeData: true
632
636
  });
633
637
  }
634
- #checkAttributesForNeededStructureUpdate(_endpointId, clusterId, attributeId) {
635
- let structureUpdateNeeded = false;
636
- if (clusterId === DescriptorCluster.id) {
638
+ #checkAttributesForNeededStructureUpdate(endpointId, clusterId, attributeId) {
639
+ if (clusterId === Descriptor.Complete.id) {
637
640
  switch (attributeId) {
638
- case DescriptorCluster.attributes.partsList.id:
639
- case DescriptorCluster.attributes.serverList.id:
640
- case DescriptorCluster.attributes.deviceTypeList.id:
641
- structureUpdateNeeded = true;
642
- break;
643
- }
644
- }
645
- if (!structureUpdateNeeded) {
646
- switch (attributeId) {
647
- case FeatureMap.id:
648
- case AttributeList.id:
649
- case AcceptedCommandList.id:
650
- case ClusterRevision.id:
651
- structureUpdateNeeded = true;
652
- break;
641
+ case Descriptor.Complete.attributes.partsList.id:
642
+ case Descriptor.Complete.attributes.serverList.id:
643
+ case Descriptor.Complete.attributes.clientList.id:
644
+ case Descriptor.Complete.attributes.deviceTypeList.id:
645
+ this.#registeredEndpointStructureChanges.set(endpointId, null);
646
+ return;
653
647
  }
654
648
  }
655
- if (structureUpdateNeeded) {
656
- logger.info(`Node ${this.nodeId}: Endpoint structure needs to be updated ...`);
657
- this.#updateEndpointStructureTimer.stop().start();
649
+ switch (attributeId) {
650
+ case FeatureMap.id:
651
+ case AttributeList.id:
652
+ case AcceptedCommandList.id:
653
+ case ClusterRevision.id:
654
+ let knownForUpdate = this.#registeredEndpointStructureChanges.get(endpointId);
655
+ if (knownForUpdate !== null) {
656
+ knownForUpdate = knownForUpdate ?? [];
657
+ if (!knownForUpdate.includes(clusterId)) {
658
+ knownForUpdate.push(clusterId);
659
+ this.#registeredEndpointStructureChanges.set(endpointId, knownForUpdate);
660
+ }
661
+ }
662
+ break;
658
663
  }
659
664
  }
660
665
  #checkEventsForNeededStructureUpdate(_endpointId, clusterId, eventId) {
@@ -682,55 +687,165 @@ class PairedNode {
682
687
  this.#reconnectDelayTimer.start();
683
688
  }
684
689
  async #updateEndpointStructure() {
685
- const allClusterAttributes = await this.readAllAttributes();
690
+ const allClusterAttributes = this.#interactionClient.getAllCachedClusterData();
686
691
  await this.#initializeEndpointStructure(allClusterAttributes, true);
687
- this.#options.stateInformationCallback?.(this.nodeId, 4 /* StructureChanged */);
688
- this.events.structureChanged.emit();
692
+ }
693
+ /**
694
+ * Traverse the structure data and collect all data for the given endpointId.
695
+ * Return true if data for the endpoint was found, otherwise false.
696
+ * If data was found it is added to the collectedData map.
697
+ */
698
+ collectDescriptorData(structure, endpointId, collectedData) {
699
+ if (collectedData.has(endpointId)) {
700
+ return;
701
+ }
702
+ const endpointData = structure[endpointId];
703
+ const descriptorData = endpointData?.[Descriptor.Complete.id];
704
+ if (endpointData === void 0 || descriptorData === void 0) {
705
+ logger.info(`Descriptor data for endpoint ${endpointId} not found in structure! Ignoring endpoint ...`);
706
+ return;
707
+ }
708
+ collectedData.set(endpointId, descriptorData);
709
+ if (descriptorData.partsList.length) {
710
+ for (const partEndpointId of descriptorData.partsList) {
711
+ this.collectDescriptorData(structure, partEndpointId, collectedData);
712
+ }
713
+ }
714
+ }
715
+ #hasEndpointChanged(device, descriptorData) {
716
+ return !(areNumberListsSame(
717
+ device.getDeviceTypes().map(({ code }) => code),
718
+ descriptorData.deviceTypeList.map(({ deviceType }) => deviceType)
719
+ ) && // Check if the cluster clients are the same - they map to the serverList attribute
720
+ areNumberListsSame(
721
+ device.getAllClusterClients().map(({ id }) => id),
722
+ descriptorData.serverList
723
+ ) && // Check if the cluster servers are the same - they map to the clientList attribute
724
+ areNumberListsSame(
725
+ device.getAllClusterServers().map(({ id }) => id),
726
+ descriptorData.clientList
727
+ ));
689
728
  }
690
729
  /** Reads all data from the device and create a device object structure out of it. */
691
- async #initializeEndpointStructure(allClusterAttributes, updateStructure = false) {
730
+ async #initializeEndpointStructure(allClusterAttributes, updateStructure = this.#localInitializationDone || this.#remoteInitializationDone) {
731
+ if (this.#updateEndpointStructureTimer.isRunning) {
732
+ this.#updateEndpointStructureTimer.stop();
733
+ }
734
+ const eventsToEmit = /* @__PURE__ */ new Map();
735
+ const structureUpdateDetails = this.#registeredEndpointStructureChanges;
736
+ this.#registeredEndpointStructureChanges = /* @__PURE__ */ new Map();
692
737
  const allData = structureReadAttributeDataToClusterObject(allClusterAttributes);
738
+ const descriptors = /* @__PURE__ */ new Map();
739
+ this.collectDescriptorData(allData, EndpointNumber(0), descriptors);
693
740
  if (updateStructure) {
694
741
  const endpointsToRemove = new Set(this.#endpoints.keys());
695
- for (const [endpointId] of Object.entries(allData)) {
696
- const endpointIdNumber = EndpointNumber(parseInt(endpointId));
697
- if (this.#endpoints.has(endpointIdNumber)) {
698
- logger.debug(`Node ${this.nodeId}: Retaining device`, endpointId);
699
- endpointsToRemove.delete(endpointIdNumber);
742
+ for (const endpointId of descriptors.keys()) {
743
+ const device = this.#endpoints.get(endpointId);
744
+ if (device !== void 0) {
745
+ const hasChanged = structureUpdateDetails.has(endpointId);
746
+ if (!hasChanged || !this.#hasEndpointChanged(device, descriptors.get(endpointId))) {
747
+ logger.debug(
748
+ `Node ${this.nodeId}: Retaining endpoint`,
749
+ endpointId,
750
+ hasChanged ? "(with only structure changes)" : "(unchanged)"
751
+ );
752
+ endpointsToRemove.delete(endpointId);
753
+ if (hasChanged) {
754
+ eventsToEmit.set(endpointId, "nodeEndpointChanged");
755
+ }
756
+ } else {
757
+ logger.debug(`Node ${this.nodeId}: Recreating endpoint`, endpointId);
758
+ eventsToEmit.set(endpointId, "nodeEndpointChanged");
759
+ }
700
760
  }
701
761
  }
702
- for (const endpointId of endpointsToRemove.values()) {
703
- logger.debug(`Node ${this.nodeId}: Removing device`, endpointId);
704
- this.#endpoints.get(endpointId)?.removeFromStructure();
705
- this.#endpoints.delete(endpointId);
762
+ for (const endpoint of endpointsToRemove.values()) {
763
+ const endpointId = EndpointNumber(endpoint);
764
+ const device = this.#endpoints.get(endpointId);
765
+ if (device !== void 0) {
766
+ if (eventsToEmit.get(endpointId) !== "nodeEndpointChanged") {
767
+ logger.debug(`Node ${this.nodeId}: Removing endpoint`, endpointId);
768
+ eventsToEmit.set(endpointId, "nodeEndpointRemoved");
769
+ }
770
+ device.removeFromStructure();
771
+ this.#endpoints.delete(endpointId);
772
+ }
706
773
  }
707
774
  } else {
708
775
  this.#endpoints.clear();
709
776
  }
710
- const partLists = /* @__PURE__ */ new Map();
711
- for (const [endpointId, clusters] of Object.entries(allData)) {
712
- const endpointIdNumber = EndpointNumber(parseInt(endpointId));
713
- const descriptorData = clusters[DescriptorCluster.id];
714
- partLists.set(endpointIdNumber, descriptorData.partsList);
715
- if (this.#endpoints.has(endpointIdNumber)) {
777
+ for (const endpointId of descriptors.keys()) {
778
+ const clusters = allData[endpointId];
779
+ if (this.#endpoints.has(endpointId)) {
716
780
  continue;
717
781
  }
718
- logger.debug(`Node ${this.nodeId}: Creating device`, endpointId, Diagnostic.json(clusters));
719
- this.#endpoints.set(
720
- endpointIdNumber,
721
- this.#createDevice(endpointIdNumber, clusters, this.#interactionClient)
782
+ const isRecreation = eventsToEmit.get(endpointId) === "nodeEndpointChanged";
783
+ logger.debug(
784
+ `Node ${this.nodeId}: ${isRecreation ? "Recreating" : "Creating"} endpoint`,
785
+ endpointId,
786
+ Diagnostic.json(clusters)
722
787
  );
788
+ this.#endpoints.set(endpointId, this.#createDevice(endpointId, clusters, this.#interactionClient));
789
+ if (!isRecreation) {
790
+ eventsToEmit.set(endpointId, "nodeEndpointAdded");
791
+ }
792
+ }
793
+ for (const [endpointId, { partsList }] of descriptors.entries()) {
794
+ const endpoint = this.#endpoints.get(endpointId);
795
+ if (endpoint === void 0) {
796
+ continue;
797
+ }
798
+ endpoint.getChildEndpoints().forEach((child) => {
799
+ if (child.number !== void 0 && !partsList.includes(child.number)) {
800
+ endpoint.removeChildEndpoint(child);
801
+ if (!eventsToEmit.has(endpointId)) {
802
+ eventsToEmit.set(endpointId, "nodeEndpointChanged");
803
+ }
804
+ }
805
+ });
806
+ }
807
+ this.#structureEndpoints(descriptors);
808
+ if (updateStructure && eventsToEmit.size) {
809
+ for (const [endpointId, eventName] of eventsToEmit.entries()) {
810
+ if (eventName !== "nodeEndpointAdded") {
811
+ const clusterServers = descriptors.get(endpointId)?.serverList;
812
+ await this.#interactionClient.cleanupAttributeData(
813
+ endpointId,
814
+ eventName === "nodeEndpointRemoved" ? void 0 : clusterServers
815
+ );
816
+ }
817
+ }
818
+ const emitChangeEvents = () => {
819
+ for (const [endpointId, eventName] of eventsToEmit.entries()) {
820
+ logger.debug(`Node ${this.nodeId}: Emitting event ${eventName} for endpoint ${endpointId}`);
821
+ this.events[eventName].emit(endpointId);
822
+ }
823
+ this.#options.stateInformationCallback?.(this.nodeId, 4 /* StructureChanged */);
824
+ this.events.structureChanged.emit();
825
+ };
826
+ if (this.#connectionState === 0 /* Connected */) {
827
+ emitChangeEvents();
828
+ } else {
829
+ this.events.stateChanged.once((State) => {
830
+ if (State === 0 /* Connected */) {
831
+ emitChangeEvents();
832
+ }
833
+ });
834
+ }
723
835
  }
724
- this.#structureEndpoints(partLists);
725
836
  }
726
- /** Bring the endpoints in a structure based on their partsList attribute. */
727
- #structureEndpoints(partLists) {
728
- logger.debug(
729
- `Node ${this.nodeId}: Endpoints from PartsLists`,
730
- Diagnostic.json(Array.from(partLists.entries()))
837
+ /**
838
+ * Bring the endpoints in a structure based on their partsList attribute. This method only adds endpoints into the
839
+ * right place as children, Cleanup is not happening here
840
+ */
841
+ #structureEndpoints(descriptors) {
842
+ const partLists = Array.from(descriptors.entries()).map(
843
+ ([ep, { partsList }]) => [ep, partsList]
844
+ // else Typescript gets confused
731
845
  );
846
+ logger.debug(`Node ${this.nodeId}: Endpoints from PartsLists`, Diagnostic.json(partLists));
732
847
  const endpointUsages = {};
733
- Array.from(partLists.entries()).forEach(
848
+ partLists.forEach(
734
849
  ([parent, partsList]) => partsList.forEach((endPoint) => {
735
850
  if (endPoint === parent) {
736
851
  logger.warn(`Node ${this.nodeId}: Endpoint ${endPoint} is referencing itself!`);
@@ -754,14 +869,18 @@ class PairedNode {
754
869
  const childEndpointId = EndpointNumber(parseInt(childId));
755
870
  const childEndpoint = this.#endpoints.get(childEndpointId);
756
871
  const parentEndpoint = this.#endpoints.get(usages[0]);
872
+ const existingChildEndpoint = parentEndpoint?.getChildEndpoint(childEndpointId);
757
873
  if (childEndpoint === void 0 || parentEndpoint === void 0) {
758
874
  logger.warn(
759
875
  `Node ${this.nodeId}: Endpoint ${usages[0]} not found in the data received from the device!`
760
876
  );
761
- } else if (parentEndpoint.getChildEndpoint(childEndpointId) === void 0) {
877
+ } else if (existingChildEndpoint !== childEndpoint) {
762
878
  logger.debug(
763
879
  `Node ${this.nodeId}: Endpoint structure: Child: ${childEndpointId} -> Parent: ${parentEndpoint.number}`
764
880
  );
881
+ if (existingChildEndpoint !== void 0) {
882
+ parentEndpoint.removeChildEndpoint(existingChildEndpoint);
883
+ }
765
884
  parentEndpoint.addChildEndpoint(childEndpoint);
766
885
  }
767
886
  delete endpointUsages[EndpointNumber(parseInt(childId))];
@@ -783,7 +902,7 @@ class PairedNode {
783
902
  }
784
903
  /** Create a device object from the data read from the device. */
785
904
  #createDevice(endpointId, data, interactionClient) {
786
- const descriptorData = data[DescriptorCluster.id];
905
+ const descriptorData = data[Descriptor.Complete.id];
787
906
  const deviceTypes = descriptorData.deviceTypeList.flatMap(({ deviceType, revision }) => {
788
907
  const deviceTypeDefinition = getDeviceTypeDefinitionFromModelByCode(deviceType);
789
908
  if (deviceTypeDefinition === void 0) {