@project-chip/matter.js 0.15.4 → 0.15.6

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 (42) hide show
  1. package/dist/cjs/CommissioningController.d.ts +2 -1
  2. package/dist/cjs/CommissioningController.d.ts.map +1 -1
  3. package/dist/cjs/CommissioningController.js +26 -35
  4. package/dist/cjs/CommissioningController.js.map +1 -1
  5. package/dist/cjs/MatterController.d.ts.map +1 -1
  6. package/dist/cjs/MatterController.js +2 -2
  7. package/dist/cjs/MatterController.js.map +1 -1
  8. package/dist/cjs/device/CachedClientNodeStore.d.ts +4 -3
  9. package/dist/cjs/device/CachedClientNodeStore.d.ts.map +1 -1
  10. package/dist/cjs/device/CachedClientNodeStore.js +19 -2
  11. package/dist/cjs/device/CachedClientNodeStore.js.map +1 -1
  12. package/dist/cjs/device/Endpoint.d.ts +1 -1
  13. package/dist/cjs/device/Endpoint.d.ts.map +1 -1
  14. package/dist/cjs/device/Endpoint.js.map +1 -1
  15. package/dist/cjs/device/PairedNode.d.ts +56 -28
  16. package/dist/cjs/device/PairedNode.d.ts.map +1 -1
  17. package/dist/cjs/device/PairedNode.js +217 -78
  18. package/dist/cjs/device/PairedNode.js.map +1 -1
  19. package/dist/esm/CommissioningController.d.ts +2 -1
  20. package/dist/esm/CommissioningController.d.ts.map +1 -1
  21. package/dist/esm/CommissioningController.js +26 -35
  22. package/dist/esm/CommissioningController.js.map +1 -1
  23. package/dist/esm/MatterController.d.ts.map +1 -1
  24. package/dist/esm/MatterController.js +2 -3
  25. package/dist/esm/MatterController.js.map +1 -1
  26. package/dist/esm/device/CachedClientNodeStore.d.ts +4 -3
  27. package/dist/esm/device/CachedClientNodeStore.d.ts.map +1 -1
  28. package/dist/esm/device/CachedClientNodeStore.js +19 -2
  29. package/dist/esm/device/CachedClientNodeStore.js.map +1 -1
  30. package/dist/esm/device/Endpoint.d.ts +1 -1
  31. package/dist/esm/device/Endpoint.d.ts.map +1 -1
  32. package/dist/esm/device/Endpoint.js.map +1 -1
  33. package/dist/esm/device/PairedNode.d.ts +56 -28
  34. package/dist/esm/device/PairedNode.d.ts.map +1 -1
  35. package/dist/esm/device/PairedNode.js +218 -79
  36. package/dist/esm/device/PairedNode.js.map +1 -1
  37. package/package.json +8 -8
  38. package/src/CommissioningController.ts +31 -38
  39. package/src/MatterController.ts +3 -4
  40. package/src/device/CachedClientNodeStore.ts +24 -4
  41. package/src/device/Endpoint.ts +1 -1
  42. package/src/device/PairedNode.ts +322 -94
@@ -3,8 +3,9 @@
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
- import { ClusterClientObj, DecodedAttributeReportValue, DecodedEventReportValue, InteractionClient, NodeDiscoveryType, NodeSession } from "#protocol";
8
+ import { AttributeClientValues, ClusterClientObj, DecodedAttributeReportValue, DecodedEventReportValue, InteractionClient, NodeDiscoveryType, NodeSession, StructuredReadAttributeData } from "#protocol";
8
9
  import { ClusterType, EndpointNumber, NodeId } from "#types";
9
10
  import { ClusterServerObj } from "../cluster/server/ClusterServerTypes.js";
10
11
  import { CommissioningController } from "../CommissioningController.js";
@@ -110,6 +111,7 @@ export type CommissioningControllerNodeOptions = {
110
111
  };
111
112
  export declare class NodeNotConnectedError extends MatterError {
112
113
  }
114
+ type DescriptorData = AttributeClientValues<typeof Descriptor.Complete.attributes>;
113
115
  /**
114
116
  * Class to represents one node that is paired/commissioned with the matter.js Controller. Instances are returned by
115
117
  * the CommissioningController on commissioning or when connecting.
@@ -117,33 +119,7 @@ export declare class NodeNotConnectedError extends MatterError {
117
119
  export declare class PairedNode {
118
120
  #private;
119
121
  readonly nodeId: NodeId;
120
- readonly events: {
121
- /**
122
- * Emitted when the node is initialized from local data. These data usually are stale, but you can still already
123
- * use the node to interact with the device. If no local data are available this event will be emitted together
124
- * with the initializedFromRemote event.
125
- */
126
- initialized: AsyncObservable<[details: DeviceInformationData], void>;
127
- /**
128
- * Emitted when the node is fully initialized from remote and all attributes and events are subscribed.
129
- * This event can also be awaited if code needs to be blocked until the node is fully initialized.
130
- */
131
- initializedFromRemote: AsyncObservable<[details: DeviceInformationData], void>;
132
- /** Emitted when the state of the node changes. */
133
- stateChanged: Observable<[nodeState: NodeStates], void>;
134
- /**
135
- * Emitted when an attribute value changes. If the oldValue is undefined then no former value was known.
136
- */
137
- attributeChanged: Observable<[data: DecodedAttributeReportValue<any>, oldValue: any], void>;
138
- /** Emitted when an event is triggered. */
139
- eventTriggered: Observable<[DecodedEventReportValue<any>], void>;
140
- /** Emitted when the structure of the node changes (Endpoints got added or also removed). */
141
- structureChanged: Observable<[void], void>;
142
- /** Emitted when the node is decommissioned. */
143
- decommissioned: Observable<[void], void>;
144
- /** Emitted when a subscription alive trigger is received (max interval trigger or any data update) */
145
- connectionAlive: Observable<[void], void>;
146
- };
122
+ readonly events: PairedNode.Events;
147
123
  static create(nodeId: NodeId, commissioningController: CommissioningController, options: CommissioningControllerNodeOptions | undefined, knownNodeDetails: DeviceInformationData, interactionClient: InteractionClient, reconnectFunc: (discoveryType?: NodeDiscoveryType, noForcedConnection?: boolean) => Promise<void>, assignDisconnectedHandler: (handler: () => Promise<void>) => void, sessions: BasicSet<NodeSession>, crypto: Crypto, storedAttributeData?: DecodedAttributeReportValue<any>[]): Promise<PairedNode>;
148
124
  constructor(nodeId: NodeId, commissioningController: CommissioningController, options: CommissioningControllerNodeOptions | undefined, knownNodeDetails: DeviceInformationData, interactionClient: InteractionClient, reconnectFunc: (discoveryType?: NodeDiscoveryType, noForcedConnection?: boolean) => Promise<void>, assignDisconnectedHandler: (handler: () => Promise<void>) => void, sessions: BasicSet<NodeSession, NodeSession>, crypto: Crypto, storedAttributeData?: DecodedAttributeReportValue<any>[]);
149
125
  get construction(): Construction<PairedNode>;
@@ -206,6 +182,12 @@ export declare class PairedNode {
206
182
  }>;
207
183
  /** Read all attributes of the devices and return them. If a stored state exists this is used to minimize needed traffic. */
208
184
  readAllAttributes(): Promise<DecodedAttributeReportValue<any>[]>;
185
+ /**
186
+ * Traverse the structure data and collect all data for the given endpointId.
187
+ * Return true if data for the endpoint was found, otherwise false.
188
+ * If data was found it is added to the collectedData map.
189
+ */
190
+ collectDescriptorData(structure: StructuredReadAttributeData, endpointId: EndpointNumber, collectedData: Map<EndpointNumber, DescriptorData>): void;
209
191
  /** Returns the functional devices/endpoints (the "childs" of the Root Endpoint) known for this node. */
210
192
  getDevices(): Endpoint[];
211
193
  /** Returns the device/endpoint with the given endpoint ID. */
@@ -257,4 +239,50 @@ export declare class PairedNode {
257
239
  getClusterClientForDevice<const T extends ClusterType>(endpointId: EndpointNumber, cluster: T): ClusterClientObj<T> | undefined;
258
240
  get [Diagnostic.value](): unknown;
259
241
  }
242
+ export declare namespace PairedNode {
243
+ interface NodeStructureEvents {
244
+ /** Emitted when endpoints are added. */
245
+ nodeEndpointAdded: Observable<[EndpointNumber]>;
246
+ /** Emitted when endpoints are removed. */
247
+ nodeEndpointRemoved: Observable<[EndpointNumber]>;
248
+ /** Emitted when endpoints are updated (e.g. device type changed, structure changed). */
249
+ nodeEndpointChanged: Observable<[EndpointNumber]>;
250
+ }
251
+ interface Events extends NodeStructureEvents {
252
+ /**
253
+ * Emitted when the node is initialized from local data. These data usually are stale, but you can still already
254
+ * use the node to interact with the device. If no local data are available this event will be emitted together
255
+ * with the initializedFromRemote event.
256
+ */
257
+ initialized: AsyncObservable<[details: DeviceInformationData]>;
258
+ /**
259
+ * Emitted when the node is fully initialized from remote and all attributes and events are subscribed.
260
+ * This event can also be awaited if code needs to be blocked until the node is fully initialized.
261
+ */
262
+ initializedFromRemote: AsyncObservable<[details: DeviceInformationData]>;
263
+ /**
264
+ * Emitted when the device information changes.
265
+ */
266
+ deviceInformationChanged: AsyncObservable<[details: DeviceInformationData]>;
267
+ /** Emitted when the state of the node changes. */
268
+ stateChanged: Observable<[nodeState: NodeStates]>;
269
+ /**
270
+ * Emitted when an attribute value changes. If the oldValue is undefined then no former value was known.
271
+ */
272
+ attributeChanged: Observable<[data: DecodedAttributeReportValue<any>, oldValue: any]>;
273
+ /** Emitted when an event is triggered. */
274
+ eventTriggered: Observable<[DecodedEventReportValue<any>]>;
275
+ /**
276
+ * Emitted when all node structure changes were applied (Endpoints got added or also removed).
277
+ * You can alternatively use the nodeEndpointAdded, nodeEndpointRemoved and nodeEndpointChanged events to react on specific changes.
278
+ * This event is emitted after all nodeEndpointAdded, nodeEndpointRemoved and nodeEndpointChanged events are emitted.
279
+ */
280
+ structureChanged: Observable<[void]>;
281
+ /** Emitted when the node is decommissioned. */
282
+ decommissioned: Observable<[void]>;
283
+ /** Emitted when a subscription alive trigger is received (max interval trigger or any data update) */
284
+ connectionAlive: Observable<[void]>;
285
+ }
286
+ }
287
+ export {};
260
288
  //# 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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"}
@@ -66,6 +66,15 @@ var NodeStateInformation = /* @__PURE__ */ ((NodeStateInformation2) => {
66
66
  })(NodeStateInformation || {});
67
67
  class NodeNotConnectedError extends import_general.MatterError {
68
68
  }
69
+ function areNumberListsSame(list1, list2) {
70
+ const set1 = new Set(list1);
71
+ const set2 = new Set(list2);
72
+ if (set1.size !== set2.size) return false;
73
+ for (const entry of set1.values()) {
74
+ if (!set2.has(entry)) return false;
75
+ }
76
+ return true;
77
+ }
69
78
  class PairedNode {
70
79
  constructor(nodeId, commissioningController, options = {}, knownNodeDetails, interactionClient, reconnectFunc, assignDisconnectedHandler, sessions, crypto, storedAttributeData) {
71
80
  this.nodeId = nodeId;
@@ -159,31 +168,25 @@ class PairedNode {
159
168
  #reconnectFunc;
160
169
  #currentSubscriptionIntervalS;
161
170
  #crypto;
171
+ #deviceInformationUpdateNeeded = false;
172
+ /**
173
+ * Endpoint structure change information that are checked when updating structure
174
+ * - null means that the endpoint itself changed, so will be regenerated completely any case
175
+ * - array of ClusterIds means that only these clusters changed and will be updated
176
+ */
177
+ #registeredEndpointStructureChanges = /* @__PURE__ */ new Map();
162
178
  events = {
163
- /**
164
- * Emitted when the node is initialized from local data. These data usually are stale, but you can still already
165
- * use the node to interact with the device. If no local data are available this event will be emitted together
166
- * with the initializedFromRemote event.
167
- */
168
179
  initialized: (0, import_general.AsyncObservable)(),
169
- /**
170
- * Emitted when the node is fully initialized from remote and all attributes and events are subscribed.
171
- * This event can also be awaited if code needs to be blocked until the node is fully initialized.
172
- */
173
180
  initializedFromRemote: (0, import_general.AsyncObservable)(),
174
- /** Emitted when the state of the node changes. */
181
+ deviceInformationChanged: (0, import_general.AsyncObservable)(),
175
182
  stateChanged: (0, import_general.Observable)(),
176
- /**
177
- * Emitted when an attribute value changes. If the oldValue is undefined then no former value was known.
178
- */
179
183
  attributeChanged: (0, import_general.Observable)(),
180
- /** Emitted when an event is triggered. */
181
184
  eventTriggered: (0, import_general.Observable)(),
182
- /** Emitted when the structure of the node changes (Endpoints got added or also removed). */
183
185
  structureChanged: (0, import_general.Observable)(),
184
- /** Emitted when the node is decommissioned. */
186
+ nodeEndpointAdded: (0, import_general.Observable)(),
187
+ nodeEndpointRemoved: (0, import_general.Observable)(),
188
+ nodeEndpointChanged: (0, import_general.Observable)(),
185
189
  decommissioned: (0, import_general.Observable)(),
186
- /** Emitted when a subscription alive trigger is received (max interval trigger or any data update) */
187
190
  connectionAlive: (0, import_general.Observable)()
188
191
  };
189
192
  static async create(nodeId, commissioningController, options = {}, knownNodeDetails, interactionClient, reconnectFunc, assignDisconnectedHandler, sessions, crypto, storedAttributeData) {
@@ -398,7 +401,7 @@ class PairedNode {
398
401
  })) {
399
402
  return;
400
403
  }
401
- await this.#initializeEndpointStructure(storedAttributeData);
404
+ await this.#initializeEndpointStructure(storedAttributeData, false);
402
405
  await this.events.initialized.emit(this.#nodeDetails.toStorageData());
403
406
  this.#localInitializationDone = true;
404
407
  }
@@ -436,7 +439,7 @@ class PairedNode {
436
439
  if (attributeReports === void 0) {
437
440
  throw new import_general.InternalError("No attribute reports received when subscribing to all values!");
438
441
  }
439
- await this.#initializeEndpointStructure(attributeReports, anyInitializationDone);
442
+ await this.#initializeEndpointStructure(attributeReports);
440
443
  this.#remoteInitializationInProgress = false;
441
444
  if (!deviceDetailsUpdated) {
442
445
  const rootEndpoint = this.getRootEndpoint();
@@ -447,7 +450,7 @@ class PairedNode {
447
450
  this.#currentSubscriptionIntervalS = maxInterval;
448
451
  } else {
449
452
  const allClusterAttributes = await this.readAllAttributes();
450
- await this.#initializeEndpointStructure(allClusterAttributes, anyInitializationDone);
453
+ await this.#initializeEndpointStructure(allClusterAttributes);
451
454
  this.#remoteInitializationInProgress = false;
452
455
  }
453
456
  if (!this.#remoteInitializationDone) {
@@ -529,7 +532,7 @@ class PairedNode {
529
532
  );
530
533
  (0, import_TypeHelpers.asClusterClientInternal)(cluster)._triggerAttributeUpdate(attributeId, value);
531
534
  attributeChangedCallback?.(data, oldValue);
532
- this.#checkAttributesForNeededStructureUpdate(endpointId, clusterId, attributeId);
535
+ this.#checkAttributesForNeededUpdates(endpointId, clusterId, attributeId);
533
536
  },
534
537
  eventListener: (data) => {
535
538
  if (ignoreInitialTriggers) return;
@@ -586,18 +589,32 @@ class PairedNode {
586
589
  this.#reconnectDelayTimer = void 0;
587
590
  this.#setConnectionState(0 /* Connected */);
588
591
  }
592
+ if (this.#remoteInitializationDone && this.#registeredEndpointStructureChanges.size > 0 && !this.#updateEndpointStructureTimer.isRunning) {
593
+ logger.info(`Node ${this.nodeId}: Endpoint structure needs to be updated ...`);
594
+ this.#updateEndpointStructureTimer.stop().start();
595
+ } else if (this.#deviceInformationUpdateNeeded) {
596
+ const rootEndpoint = this.getRootEndpoint();
597
+ if (rootEndpoint !== void 0) {
598
+ this.#nodeDetails.enhanceDeviceDetailsFromCache(rootEndpoint).then(() => this.events.deviceInformationChanged.emit(this.#nodeDetails.toStorageData())).catch(
599
+ (error) => logger.warn(
600
+ `Node ${this.nodeId}: Error updating device information from root endpoint`,
601
+ error
602
+ )
603
+ );
604
+ }
605
+ }
606
+ this.#deviceInformationUpdateNeeded = false;
589
607
  this.events.connectionAlive.emit();
590
608
  }
591
609
  };
592
610
  this.#currentSubscriptionHandler = subscriptionHandler;
593
611
  const maxKnownEventNumber = this.#interactionClient.maxKnownEventNumber;
594
- const attributeData = await this.#interactionClient.getAllAttributes({
612
+ await this.#interactionClient.getAllAttributes({
595
613
  dataVersionFilters: this.#interactionClient.getCachedClusterDataVersions(),
596
- executeQueued: !!threadConnected
614
+ executeQueued: !!threadConnected,
597
615
  // We queue subscriptions for thread devices
616
+ attributeChangeListener: subscriptionHandler.attributeListener
598
617
  });
599
- await this.#interactionClient.processAttributeUpdates(attributeData, subscriptionHandler.attributeListener);
600
- attributeData.length = 0;
601
618
  const initialSubscriptionData = await this.#interactionClient.subscribeAllAttributesAndEvents({
602
619
  isUrgent: true,
603
620
  minIntervalFloorSeconds,
@@ -623,30 +640,33 @@ class PairedNode {
623
640
  enrichCachedAttributeData: true
624
641
  });
625
642
  }
626
- #checkAttributesForNeededStructureUpdate(_endpointId, clusterId, attributeId) {
627
- let structureUpdateNeeded = false;
628
- if (clusterId === import_clusters.DescriptorCluster.id) {
643
+ #checkAttributesForNeededUpdates(endpointId, clusterId, attributeId) {
644
+ if (clusterId === import_clusters.Descriptor.Complete.id) {
629
645
  switch (attributeId) {
630
- case import_clusters.DescriptorCluster.attributes.partsList.id:
631
- case import_clusters.DescriptorCluster.attributes.serverList.id:
632
- case import_clusters.DescriptorCluster.attributes.deviceTypeList.id:
633
- structureUpdateNeeded = true;
634
- break;
635
- }
636
- }
637
- if (!structureUpdateNeeded) {
638
- switch (attributeId) {
639
- case import_model.FeatureMap.id:
640
- case import_model.AttributeList.id:
641
- case import_model.AcceptedCommandList.id:
642
- case import_model.ClusterRevision.id:
643
- structureUpdateNeeded = true;
644
- break;
646
+ case import_clusters.Descriptor.Complete.attributes.partsList.id:
647
+ case import_clusters.Descriptor.Complete.attributes.serverList.id:
648
+ case import_clusters.Descriptor.Complete.attributes.clientList.id:
649
+ case import_clusters.Descriptor.Complete.attributes.deviceTypeList.id:
650
+ this.#registeredEndpointStructureChanges.set(endpointId, null);
651
+ return;
645
652
  }
653
+ } else if (clusterId === import_clusters.BasicInformation.Cluster.id) {
654
+ this.#deviceInformationUpdateNeeded = true;
646
655
  }
647
- if (structureUpdateNeeded) {
648
- logger.info(`Node ${this.nodeId}: Endpoint structure needs to be updated ...`);
649
- this.#updateEndpointStructureTimer.stop().start();
656
+ switch (attributeId) {
657
+ case import_model.FeatureMap.id:
658
+ case import_model.AttributeList.id:
659
+ case import_model.AcceptedCommandList.id:
660
+ case import_model.ClusterRevision.id:
661
+ let knownForUpdate = this.#registeredEndpointStructureChanges.get(endpointId);
662
+ if (knownForUpdate !== null) {
663
+ knownForUpdate = knownForUpdate ?? [];
664
+ if (!knownForUpdate.includes(clusterId)) {
665
+ knownForUpdate.push(clusterId);
666
+ this.#registeredEndpointStructureChanges.set(endpointId, knownForUpdate);
667
+ }
668
+ }
669
+ break;
650
670
  }
651
671
  }
652
672
  #checkEventsForNeededStructureUpdate(_endpointId, clusterId, eventId) {
@@ -674,55 +694,170 @@ class PairedNode {
674
694
  this.#reconnectDelayTimer.start();
675
695
  }
676
696
  async #updateEndpointStructure() {
677
- const allClusterAttributes = await this.readAllAttributes();
697
+ const allClusterAttributes = this.#interactionClient.getAllCachedClusterData();
678
698
  await this.#initializeEndpointStructure(allClusterAttributes, true);
679
- this.#options.stateInformationCallback?.(this.nodeId, 4 /* StructureChanged */);
680
- this.events.structureChanged.emit();
699
+ const rootEndpoint = this.getRootEndpoint();
700
+ if (rootEndpoint !== void 0) {
701
+ await this.#nodeDetails.enhanceDeviceDetailsFromCache(rootEndpoint);
702
+ await this.events.deviceInformationChanged.emit(this.#nodeDetails.toStorageData());
703
+ }
704
+ }
705
+ /**
706
+ * Traverse the structure data and collect all data for the given endpointId.
707
+ * Return true if data for the endpoint was found, otherwise false.
708
+ * If data was found it is added to the collectedData map.
709
+ */
710
+ collectDescriptorData(structure, endpointId, collectedData) {
711
+ if (collectedData.has(endpointId)) {
712
+ return;
713
+ }
714
+ const endpointData = structure[endpointId];
715
+ const descriptorData = endpointData?.[import_clusters.Descriptor.Complete.id];
716
+ if (endpointData === void 0 || descriptorData === void 0) {
717
+ logger.info(`Descriptor data for endpoint ${endpointId} not found in structure! Ignoring endpoint ...`);
718
+ return;
719
+ }
720
+ collectedData.set(endpointId, descriptorData);
721
+ if (descriptorData.partsList.length) {
722
+ for (const partEndpointId of descriptorData.partsList) {
723
+ this.collectDescriptorData(structure, partEndpointId, collectedData);
724
+ }
725
+ }
726
+ }
727
+ #hasEndpointChanged(device, descriptorData) {
728
+ return !(areNumberListsSame(
729
+ device.getDeviceTypes().map(({ code }) => code),
730
+ descriptorData.deviceTypeList.map(({ deviceType }) => deviceType)
731
+ ) && // Check if the cluster clients are the same - they map to the serverList attribute
732
+ areNumberListsSame(
733
+ device.getAllClusterClients().map(({ id }) => id),
734
+ descriptorData.serverList
735
+ ) && // Check if the cluster servers are the same - they map to the clientList attribute
736
+ areNumberListsSame(
737
+ device.getAllClusterServers().map(({ id }) => id),
738
+ descriptorData.clientList
739
+ ));
681
740
  }
682
741
  /** Reads all data from the device and create a device object structure out of it. */
683
- async #initializeEndpointStructure(allClusterAttributes, updateStructure = false) {
742
+ async #initializeEndpointStructure(allClusterAttributes, updateStructure = this.#localInitializationDone || this.#remoteInitializationDone) {
743
+ if (this.#updateEndpointStructureTimer.isRunning) {
744
+ this.#updateEndpointStructureTimer.stop();
745
+ }
746
+ const eventsToEmit = /* @__PURE__ */ new Map();
747
+ const structureUpdateDetails = this.#registeredEndpointStructureChanges;
748
+ this.#registeredEndpointStructureChanges = /* @__PURE__ */ new Map();
684
749
  const allData = (0, import_protocol.structureReadAttributeDataToClusterObject)(allClusterAttributes);
750
+ const descriptors = /* @__PURE__ */ new Map();
751
+ this.collectDescriptorData(allData, (0, import_types.EndpointNumber)(0), descriptors);
685
752
  if (updateStructure) {
686
753
  const endpointsToRemove = new Set(this.#endpoints.keys());
687
- for (const [endpointId] of Object.entries(allData)) {
688
- const endpointIdNumber = (0, import_types.EndpointNumber)(parseInt(endpointId));
689
- if (this.#endpoints.has(endpointIdNumber)) {
690
- logger.debug(`Node ${this.nodeId}: Retaining device`, endpointId);
691
- endpointsToRemove.delete(endpointIdNumber);
754
+ for (const endpointId of descriptors.keys()) {
755
+ const device = this.#endpoints.get(endpointId);
756
+ if (device !== void 0) {
757
+ const hasChanged = structureUpdateDetails.has(endpointId);
758
+ if (!hasChanged || !this.#hasEndpointChanged(device, descriptors.get(endpointId))) {
759
+ logger.debug(
760
+ `Node ${this.nodeId}: Retaining endpoint`,
761
+ endpointId,
762
+ hasChanged ? "(with only structure changes)" : "(unchanged)"
763
+ );
764
+ endpointsToRemove.delete(endpointId);
765
+ if (hasChanged) {
766
+ eventsToEmit.set(endpointId, "nodeEndpointChanged");
767
+ }
768
+ } else {
769
+ logger.debug(`Node ${this.nodeId}: Recreating endpoint`, endpointId);
770
+ eventsToEmit.set(endpointId, "nodeEndpointChanged");
771
+ }
692
772
  }
693
773
  }
694
- for (const endpointId of endpointsToRemove.values()) {
695
- logger.debug(`Node ${this.nodeId}: Removing device`, endpointId);
696
- this.#endpoints.get(endpointId)?.removeFromStructure();
697
- this.#endpoints.delete(endpointId);
774
+ for (const endpoint of endpointsToRemove.values()) {
775
+ const endpointId = (0, import_types.EndpointNumber)(endpoint);
776
+ const device = this.#endpoints.get(endpointId);
777
+ if (device !== void 0) {
778
+ if (eventsToEmit.get(endpointId) !== "nodeEndpointChanged") {
779
+ logger.debug(`Node ${this.nodeId}: Removing endpoint`, endpointId);
780
+ eventsToEmit.set(endpointId, "nodeEndpointRemoved");
781
+ }
782
+ device.removeFromStructure();
783
+ this.#endpoints.delete(endpointId);
784
+ }
698
785
  }
699
786
  } else {
700
787
  this.#endpoints.clear();
701
788
  }
702
- const partLists = /* @__PURE__ */ new Map();
703
- for (const [endpointId, clusters] of Object.entries(allData)) {
704
- const endpointIdNumber = (0, import_types.EndpointNumber)(parseInt(endpointId));
705
- const descriptorData = clusters[import_clusters.DescriptorCluster.id];
706
- partLists.set(endpointIdNumber, descriptorData.partsList);
707
- if (this.#endpoints.has(endpointIdNumber)) {
789
+ for (const endpointId of descriptors.keys()) {
790
+ const clusters = allData[endpointId];
791
+ if (this.#endpoints.has(endpointId)) {
708
792
  continue;
709
793
  }
710
- logger.debug(`Node ${this.nodeId}: Creating device`, endpointId, import_general.Diagnostic.json(clusters));
711
- this.#endpoints.set(
712
- endpointIdNumber,
713
- this.#createDevice(endpointIdNumber, clusters, this.#interactionClient)
794
+ const isRecreation = eventsToEmit.get(endpointId) === "nodeEndpointChanged";
795
+ logger.debug(
796
+ `Node ${this.nodeId}: ${isRecreation ? "Recreating" : "Creating"} endpoint`,
797
+ endpointId,
798
+ import_general.Diagnostic.json(clusters)
714
799
  );
800
+ this.#endpoints.set(endpointId, this.#createDevice(endpointId, clusters, this.#interactionClient));
801
+ if (!isRecreation) {
802
+ eventsToEmit.set(endpointId, "nodeEndpointAdded");
803
+ }
804
+ }
805
+ for (const [endpointId, { partsList }] of descriptors.entries()) {
806
+ const endpoint = this.#endpoints.get(endpointId);
807
+ if (endpoint === void 0) {
808
+ continue;
809
+ }
810
+ endpoint.getChildEndpoints().forEach((child) => {
811
+ if (child.number !== void 0 && !partsList.includes(child.number)) {
812
+ endpoint.removeChildEndpoint(child);
813
+ if (!eventsToEmit.has(endpointId)) {
814
+ eventsToEmit.set(endpointId, "nodeEndpointChanged");
815
+ }
816
+ }
817
+ });
818
+ }
819
+ this.#structureEndpoints(descriptors);
820
+ if (updateStructure && eventsToEmit.size) {
821
+ for (const [endpointId, eventName] of eventsToEmit.entries()) {
822
+ if (eventName !== "nodeEndpointAdded") {
823
+ const clusterServers = descriptors.get(endpointId)?.serverList;
824
+ await this.#interactionClient.cleanupAttributeData(
825
+ endpointId,
826
+ eventName === "nodeEndpointRemoved" ? void 0 : clusterServers
827
+ );
828
+ }
829
+ }
830
+ const emitChangeEvents = () => {
831
+ for (const [endpointId, eventName] of eventsToEmit.entries()) {
832
+ logger.debug(`Node ${this.nodeId}: Emitting event ${eventName} for endpoint ${endpointId}`);
833
+ this.events[eventName].emit(endpointId);
834
+ }
835
+ this.#options.stateInformationCallback?.(this.nodeId, 4 /* StructureChanged */);
836
+ this.events.structureChanged.emit();
837
+ };
838
+ if (this.#connectionState === 0 /* Connected */) {
839
+ emitChangeEvents();
840
+ } else {
841
+ this.events.stateChanged.once((State) => {
842
+ if (State === 0 /* Connected */) {
843
+ emitChangeEvents();
844
+ }
845
+ });
846
+ }
715
847
  }
716
- this.#structureEndpoints(partLists);
717
848
  }
718
- /** Bring the endpoints in a structure based on their partsList attribute. */
719
- #structureEndpoints(partLists) {
720
- logger.debug(
721
- `Node ${this.nodeId}: Endpoints from PartsLists`,
722
- import_general.Diagnostic.json(Array.from(partLists.entries()))
849
+ /**
850
+ * Bring the endpoints in a structure based on their partsList attribute. This method only adds endpoints into the
851
+ * right place as children, Cleanup is not happening here
852
+ */
853
+ #structureEndpoints(descriptors) {
854
+ const partLists = Array.from(descriptors.entries()).map(
855
+ ([ep, { partsList }]) => [ep, partsList]
856
+ // else Typescript gets confused
723
857
  );
858
+ logger.debug(`Node ${this.nodeId}: Endpoints from PartsLists`, import_general.Diagnostic.json(partLists));
724
859
  const endpointUsages = {};
725
- Array.from(partLists.entries()).forEach(
860
+ partLists.forEach(
726
861
  ([parent, partsList]) => partsList.forEach((endPoint) => {
727
862
  if (endPoint === parent) {
728
863
  logger.warn(`Node ${this.nodeId}: Endpoint ${endPoint} is referencing itself!`);
@@ -746,14 +881,18 @@ class PairedNode {
746
881
  const childEndpointId = (0, import_types.EndpointNumber)(parseInt(childId));
747
882
  const childEndpoint = this.#endpoints.get(childEndpointId);
748
883
  const parentEndpoint = this.#endpoints.get(usages[0]);
884
+ const existingChildEndpoint = parentEndpoint?.getChildEndpoint(childEndpointId);
749
885
  if (childEndpoint === void 0 || parentEndpoint === void 0) {
750
886
  logger.warn(
751
887
  `Node ${this.nodeId}: Endpoint ${usages[0]} not found in the data received from the device!`
752
888
  );
753
- } else if (parentEndpoint.getChildEndpoint(childEndpointId) === void 0) {
889
+ } else if (existingChildEndpoint !== childEndpoint) {
754
890
  logger.debug(
755
891
  `Node ${this.nodeId}: Endpoint structure: Child: ${childEndpointId} -> Parent: ${parentEndpoint.number}`
756
892
  );
893
+ if (existingChildEndpoint !== void 0) {
894
+ parentEndpoint.removeChildEndpoint(existingChildEndpoint);
895
+ }
757
896
  parentEndpoint.addChildEndpoint(childEndpoint);
758
897
  }
759
898
  delete endpointUsages[(0, import_types.EndpointNumber)(parseInt(childId))];
@@ -775,7 +914,7 @@ class PairedNode {
775
914
  }
776
915
  /** Create a device object from the data read from the device. */
777
916
  #createDevice(endpointId, data, interactionClient) {
778
- const descriptorData = data[import_clusters.DescriptorCluster.id];
917
+ const descriptorData = data[import_clusters.Descriptor.Complete.id];
779
918
  const deviceTypes = descriptorData.deviceTypeList.flatMap(({ deviceType, revision }) => {
780
919
  const deviceTypeDefinition = (0, import_DeviceTypes.getDeviceTypeDefinitionFromModelByCode)(deviceType);
781
920
  if (deviceTypeDefinition === void 0) {