@matter/protocol 0.12.4-alpha.0-20250223-1e0341a1a → 0.12.4-alpha.0-20250224-46934b522

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 (245) hide show
  1. package/dist/cjs/action/client/ClientInteraction.d.ts +38 -0
  2. package/dist/cjs/action/client/ClientInteraction.d.ts.map +1 -0
  3. package/dist/cjs/action/client/ClientInteraction.js +91 -0
  4. package/dist/cjs/action/client/ClientInteraction.js.map +6 -0
  5. package/dist/cjs/action/client/index.d.ts +7 -0
  6. package/dist/cjs/action/client/index.d.ts.map +1 -0
  7. package/dist/cjs/action/client/index.js +24 -0
  8. package/dist/cjs/action/client/index.js.map +6 -0
  9. package/dist/cjs/action/index.d.ts +1 -0
  10. package/dist/cjs/action/index.d.ts.map +1 -1
  11. package/dist/cjs/action/index.js +1 -0
  12. package/dist/cjs/action/index.js.map +1 -1
  13. package/dist/cjs/interaction/DecodedDataReport.d.ts +15 -0
  14. package/dist/cjs/interaction/DecodedDataReport.d.ts.map +1 -0
  15. package/dist/cjs/interaction/DecodedDataReport.js +42 -0
  16. package/dist/cjs/interaction/DecodedDataReport.js.map +6 -0
  17. package/dist/cjs/interaction/InteractionClient.d.ts +17 -23
  18. package/dist/cjs/interaction/InteractionClient.d.ts.map +1 -1
  19. package/dist/cjs/interaction/InteractionClient.js +100 -127
  20. package/dist/cjs/interaction/InteractionClient.js.map +1 -1
  21. package/dist/cjs/interaction/InteractionMessenger.d.ts +94 -1
  22. package/dist/cjs/interaction/InteractionMessenger.d.ts.map +1 -1
  23. package/dist/cjs/interaction/InteractionMessenger.js +56 -37
  24. package/dist/cjs/interaction/InteractionMessenger.js.map +1 -1
  25. package/dist/cjs/interaction/InteractionServer.d.ts +22 -3
  26. package/dist/cjs/interaction/InteractionServer.d.ts.map +1 -1
  27. package/dist/cjs/interaction/InteractionServer.js +129 -45
  28. package/dist/cjs/interaction/InteractionServer.js.map +1 -1
  29. package/dist/cjs/interaction/ServerSubscription.d.ts +8 -16
  30. package/dist/cjs/interaction/ServerSubscription.d.ts.map +1 -1
  31. package/dist/cjs/interaction/ServerSubscription.js +78 -55
  32. package/dist/cjs/interaction/ServerSubscription.js.map +1 -1
  33. package/dist/cjs/interaction/Subscription.d.ts +7 -1
  34. package/dist/cjs/interaction/Subscription.d.ts.map +1 -1
  35. package/dist/cjs/interaction/Subscription.js +25 -2
  36. package/dist/cjs/interaction/Subscription.js.map +1 -1
  37. package/dist/cjs/interaction/SubscriptionClient.d.ts +38 -0
  38. package/dist/cjs/interaction/SubscriptionClient.d.ts.map +1 -0
  39. package/dist/cjs/interaction/SubscriptionClient.js +98 -0
  40. package/dist/cjs/interaction/SubscriptionClient.js.map +6 -0
  41. package/dist/cjs/interaction/index.d.ts +1 -0
  42. package/dist/cjs/interaction/index.d.ts.map +1 -1
  43. package/dist/cjs/interaction/index.js +1 -0
  44. package/dist/cjs/interaction/index.js.map +1 -1
  45. package/dist/cjs/peer/ControllerCommissioner.d.ts +2 -2
  46. package/dist/cjs/peer/ControllerCommissioner.d.ts.map +1 -1
  47. package/dist/cjs/peer/ControllerCommissioner.js +7 -7
  48. package/dist/cjs/peer/ControllerCommissioner.js.map +1 -1
  49. package/dist/cjs/peer/InteractionQueue.d.ts +11 -0
  50. package/dist/cjs/peer/InteractionQueue.d.ts.map +1 -0
  51. package/dist/cjs/peer/InteractionQueue.js +42 -0
  52. package/dist/cjs/peer/InteractionQueue.js.map +6 -0
  53. package/dist/cjs/peer/PeerAddress.d.ts +5 -0
  54. package/dist/cjs/peer/PeerAddress.d.ts.map +1 -1
  55. package/dist/cjs/peer/PeerAddress.js +13 -1
  56. package/dist/cjs/peer/PeerAddress.js.map +1 -1
  57. package/dist/cjs/peer/PeerAddressStore.d.ts +1 -1
  58. package/dist/cjs/peer/PeerAddressStore.d.ts.map +1 -1
  59. package/dist/cjs/peer/PeerSet.d.ts +20 -7
  60. package/dist/cjs/peer/PeerSet.d.ts.map +1 -1
  61. package/dist/cjs/peer/PeerSet.js +74 -67
  62. package/dist/cjs/peer/PeerSet.js.map +1 -1
  63. package/dist/cjs/peer/PhysicalDeviceProperties.d.ts +26 -0
  64. package/dist/cjs/peer/PhysicalDeviceProperties.d.ts.map +1 -0
  65. package/dist/cjs/peer/PhysicalDeviceProperties.js +74 -0
  66. package/dist/cjs/peer/PhysicalDeviceProperties.js.map +6 -0
  67. package/dist/cjs/peer/index.d.ts +1 -0
  68. package/dist/cjs/peer/index.d.ts.map +1 -1
  69. package/dist/cjs/peer/index.js +1 -0
  70. package/dist/cjs/peer/index.js.map +1 -1
  71. package/dist/cjs/protocol/ExchangeManager.d.ts +1 -0
  72. package/dist/cjs/protocol/ExchangeManager.d.ts.map +1 -1
  73. package/dist/cjs/protocol/ExchangeManager.js +6 -3
  74. package/dist/cjs/protocol/ExchangeManager.js.map +1 -1
  75. package/dist/cjs/protocol/ExchangeProvider.d.ts +13 -1
  76. package/dist/cjs/protocol/ExchangeProvider.d.ts.map +1 -1
  77. package/dist/cjs/protocol/ExchangeProvider.js +2 -0
  78. package/dist/cjs/protocol/ExchangeProvider.js.map +1 -1
  79. package/dist/cjs/protocol/ProtocolHandler.d.ts +1 -1
  80. package/dist/cjs/protocol/ProtocolHandler.d.ts.map +1 -1
  81. package/dist/cjs/securechannel/SecureChannelMessenger.d.ts +1 -0
  82. package/dist/cjs/securechannel/SecureChannelMessenger.d.ts.map +1 -1
  83. package/dist/cjs/securechannel/SecureChannelMessenger.js +3 -0
  84. package/dist/cjs/securechannel/SecureChannelMessenger.js.map +1 -1
  85. package/dist/cjs/securechannel/SecureChannelProtocol.d.ts +1 -1
  86. package/dist/cjs/securechannel/SecureChannelProtocol.d.ts.map +1 -1
  87. package/dist/cjs/securechannel/SecureChannelProtocol.js +1 -3
  88. package/dist/cjs/securechannel/SecureChannelProtocol.js.map +1 -1
  89. package/dist/cjs/session/SecureSession.d.ts +1 -1
  90. package/dist/cjs/session/SecureSession.d.ts.map +1 -1
  91. package/dist/cjs/session/SecureSession.js +3 -2
  92. package/dist/cjs/session/SecureSession.js.map +1 -1
  93. package/dist/cjs/session/Session.d.ts +1 -0
  94. package/dist/cjs/session/Session.d.ts.map +1 -1
  95. package/dist/cjs/session/Session.js +1 -0
  96. package/dist/cjs/session/Session.js.map +1 -1
  97. package/dist/cjs/session/SessionManager.d.ts +2 -2
  98. package/dist/cjs/session/SessionManager.d.ts.map +1 -1
  99. package/dist/cjs/session/SessionManager.js +6 -9
  100. package/dist/cjs/session/SessionManager.js.map +1 -1
  101. package/dist/cjs/session/case/CaseServer.d.ts +1 -1
  102. package/dist/cjs/session/case/CaseServer.d.ts.map +1 -1
  103. package/dist/cjs/session/case/CaseServer.js +1 -3
  104. package/dist/cjs/session/case/CaseServer.js.map +1 -1
  105. package/dist/cjs/session/pase/PaseServer.d.ts +2 -3
  106. package/dist/cjs/session/pase/PaseServer.d.ts.map +1 -1
  107. package/dist/cjs/session/pase/PaseServer.js +12 -14
  108. package/dist/cjs/session/pase/PaseServer.js.map +1 -1
  109. package/dist/esm/action/client/ClientInteraction.d.ts +38 -0
  110. package/dist/esm/action/client/ClientInteraction.d.ts.map +1 -0
  111. package/dist/esm/action/client/ClientInteraction.js +71 -0
  112. package/dist/esm/action/client/ClientInteraction.js.map +6 -0
  113. package/dist/esm/action/client/index.d.ts +7 -0
  114. package/dist/esm/action/client/index.d.ts.map +1 -0
  115. package/dist/esm/action/client/index.js +7 -0
  116. package/dist/esm/action/client/index.js.map +6 -0
  117. package/dist/esm/action/index.d.ts +1 -0
  118. package/dist/esm/action/index.d.ts.map +1 -1
  119. package/dist/esm/action/index.js +1 -0
  120. package/dist/esm/action/index.js.map +1 -1
  121. package/dist/esm/interaction/DecodedDataReport.d.ts +15 -0
  122. package/dist/esm/interaction/DecodedDataReport.d.ts.map +1 -0
  123. package/dist/esm/interaction/DecodedDataReport.js +22 -0
  124. package/dist/esm/interaction/DecodedDataReport.js.map +6 -0
  125. package/dist/esm/interaction/InteractionClient.d.ts +17 -23
  126. package/dist/esm/interaction/InteractionClient.d.ts.map +1 -1
  127. package/dist/esm/interaction/InteractionClient.js +102 -134
  128. package/dist/esm/interaction/InteractionClient.js.map +1 -1
  129. package/dist/esm/interaction/InteractionMessenger.d.ts +94 -1
  130. package/dist/esm/interaction/InteractionMessenger.d.ts.map +1 -1
  131. package/dist/esm/interaction/InteractionMessenger.js +56 -37
  132. package/dist/esm/interaction/InteractionMessenger.js.map +1 -1
  133. package/dist/esm/interaction/InteractionServer.d.ts +22 -3
  134. package/dist/esm/interaction/InteractionServer.d.ts.map +1 -1
  135. package/dist/esm/interaction/InteractionServer.js +130 -46
  136. package/dist/esm/interaction/InteractionServer.js.map +1 -1
  137. package/dist/esm/interaction/ServerSubscription.d.ts +8 -16
  138. package/dist/esm/interaction/ServerSubscription.d.ts.map +1 -1
  139. package/dist/esm/interaction/ServerSubscription.js +78 -55
  140. package/dist/esm/interaction/ServerSubscription.js.map +1 -1
  141. package/dist/esm/interaction/Subscription.d.ts +7 -1
  142. package/dist/esm/interaction/Subscription.d.ts.map +1 -1
  143. package/dist/esm/interaction/Subscription.js +26 -3
  144. package/dist/esm/interaction/Subscription.js.map +1 -1
  145. package/dist/esm/interaction/SubscriptionClient.d.ts +38 -0
  146. package/dist/esm/interaction/SubscriptionClient.d.ts.map +1 -0
  147. package/dist/esm/interaction/SubscriptionClient.js +78 -0
  148. package/dist/esm/interaction/SubscriptionClient.js.map +6 -0
  149. package/dist/esm/interaction/index.d.ts +1 -0
  150. package/dist/esm/interaction/index.d.ts.map +1 -1
  151. package/dist/esm/interaction/index.js +1 -0
  152. package/dist/esm/interaction/index.js.map +1 -1
  153. package/dist/esm/peer/ControllerCommissioner.d.ts +2 -2
  154. package/dist/esm/peer/ControllerCommissioner.d.ts.map +1 -1
  155. package/dist/esm/peer/ControllerCommissioner.js +9 -9
  156. package/dist/esm/peer/ControllerCommissioner.js.map +1 -1
  157. package/dist/esm/peer/InteractionQueue.d.ts +11 -0
  158. package/dist/esm/peer/InteractionQueue.d.ts.map +1 -0
  159. package/dist/esm/peer/InteractionQueue.js +22 -0
  160. package/dist/esm/peer/InteractionQueue.js.map +6 -0
  161. package/dist/esm/peer/PeerAddress.d.ts +5 -0
  162. package/dist/esm/peer/PeerAddress.d.ts.map +1 -1
  163. package/dist/esm/peer/PeerAddress.js +13 -1
  164. package/dist/esm/peer/PeerAddress.js.map +1 -1
  165. package/dist/esm/peer/PeerAddressStore.d.ts +1 -1
  166. package/dist/esm/peer/PeerAddressStore.d.ts.map +1 -1
  167. package/dist/esm/peer/PeerSet.d.ts +20 -7
  168. package/dist/esm/peer/PeerSet.d.ts.map +1 -1
  169. package/dist/esm/peer/PeerSet.js +75 -68
  170. package/dist/esm/peer/PeerSet.js.map +1 -1
  171. package/dist/esm/peer/PhysicalDeviceProperties.d.ts +26 -0
  172. package/dist/esm/peer/PhysicalDeviceProperties.d.ts.map +1 -0
  173. package/dist/esm/peer/PhysicalDeviceProperties.js +54 -0
  174. package/dist/esm/peer/PhysicalDeviceProperties.js.map +6 -0
  175. package/dist/esm/peer/index.d.ts +1 -0
  176. package/dist/esm/peer/index.d.ts.map +1 -1
  177. package/dist/esm/peer/index.js +1 -0
  178. package/dist/esm/peer/index.js.map +1 -1
  179. package/dist/esm/protocol/ExchangeManager.d.ts +1 -0
  180. package/dist/esm/protocol/ExchangeManager.d.ts.map +1 -1
  181. package/dist/esm/protocol/ExchangeManager.js +6 -3
  182. package/dist/esm/protocol/ExchangeManager.js.map +1 -1
  183. package/dist/esm/protocol/ExchangeProvider.d.ts +13 -1
  184. package/dist/esm/protocol/ExchangeProvider.d.ts.map +1 -1
  185. package/dist/esm/protocol/ExchangeProvider.js +2 -0
  186. package/dist/esm/protocol/ExchangeProvider.js.map +1 -1
  187. package/dist/esm/protocol/ProtocolHandler.d.ts +1 -1
  188. package/dist/esm/protocol/ProtocolHandler.d.ts.map +1 -1
  189. package/dist/esm/securechannel/SecureChannelMessenger.d.ts +1 -0
  190. package/dist/esm/securechannel/SecureChannelMessenger.d.ts.map +1 -1
  191. package/dist/esm/securechannel/SecureChannelMessenger.js +3 -0
  192. package/dist/esm/securechannel/SecureChannelMessenger.js.map +1 -1
  193. package/dist/esm/securechannel/SecureChannelProtocol.d.ts +1 -1
  194. package/dist/esm/securechannel/SecureChannelProtocol.d.ts.map +1 -1
  195. package/dist/esm/securechannel/SecureChannelProtocol.js +1 -3
  196. package/dist/esm/securechannel/SecureChannelProtocol.js.map +1 -1
  197. package/dist/esm/session/SecureSession.d.ts +1 -1
  198. package/dist/esm/session/SecureSession.d.ts.map +1 -1
  199. package/dist/esm/session/SecureSession.js +3 -2
  200. package/dist/esm/session/SecureSession.js.map +1 -1
  201. package/dist/esm/session/Session.d.ts +1 -0
  202. package/dist/esm/session/Session.d.ts.map +1 -1
  203. package/dist/esm/session/Session.js +1 -0
  204. package/dist/esm/session/Session.js.map +1 -1
  205. package/dist/esm/session/SessionManager.d.ts +2 -2
  206. package/dist/esm/session/SessionManager.d.ts.map +1 -1
  207. package/dist/esm/session/SessionManager.js +7 -10
  208. package/dist/esm/session/SessionManager.js.map +1 -1
  209. package/dist/esm/session/case/CaseServer.d.ts +1 -1
  210. package/dist/esm/session/case/CaseServer.d.ts.map +1 -1
  211. package/dist/esm/session/case/CaseServer.js +1 -3
  212. package/dist/esm/session/case/CaseServer.js.map +1 -1
  213. package/dist/esm/session/pase/PaseServer.d.ts +2 -3
  214. package/dist/esm/session/pase/PaseServer.d.ts.map +1 -1
  215. package/dist/esm/session/pase/PaseServer.js +12 -14
  216. package/dist/esm/session/pase/PaseServer.js.map +1 -1
  217. package/package.json +6 -6
  218. package/src/action/client/ClientInteraction.ts +110 -0
  219. package/src/action/client/index.ts +7 -0
  220. package/src/action/index.ts +1 -0
  221. package/src/interaction/DecodedDataReport.ts +29 -0
  222. package/src/interaction/InteractionClient.ts +118 -165
  223. package/src/interaction/InteractionMessenger.ts +63 -43
  224. package/src/interaction/InteractionServer.ts +176 -50
  225. package/src/interaction/ServerSubscription.ts +87 -63
  226. package/src/interaction/Subscription.ts +34 -6
  227. package/src/interaction/SubscriptionClient.ts +107 -0
  228. package/src/interaction/index.ts +1 -0
  229. package/src/peer/ControllerCommissioner.ts +10 -10
  230. package/src/peer/InteractionQueue.ts +22 -0
  231. package/src/peer/PeerAddress.ts +14 -0
  232. package/src/peer/PeerAddressStore.ts +1 -1
  233. package/src/peer/PeerSet.ts +98 -83
  234. package/src/peer/PhysicalDeviceProperties.ts +80 -0
  235. package/src/peer/index.ts +1 -0
  236. package/src/protocol/ExchangeManager.ts +7 -3
  237. package/src/protocol/ExchangeProvider.ts +14 -1
  238. package/src/protocol/ProtocolHandler.ts +1 -1
  239. package/src/securechannel/SecureChannelMessenger.ts +4 -0
  240. package/src/securechannel/SecureChannelProtocol.ts +1 -3
  241. package/src/session/SecureSession.ts +3 -2
  242. package/src/session/Session.ts +1 -0
  243. package/src/session/SessionManager.ts +6 -9
  244. package/src/session/case/CaseServer.ts +2 -4
  245. package/src/session/pase/PaseServer.ts +13 -15
@@ -18,11 +18,9 @@ import {
18
18
  StatusCode,
19
19
  StatusResponseError,
20
20
  TlvAny,
21
- TlvAttributeReport,
22
21
  TlvDataReport,
23
22
  TlvDataReportForSend,
24
23
  TlvDataVersionFilter,
25
- TlvEventReport,
26
24
  TlvInvokeRequest,
27
25
  TlvInvokeResponse,
28
26
  TlvReadRequest,
@@ -693,15 +691,10 @@ export class IncomingInteractionClientMessenger extends InteractionMessenger {
693
691
  return message;
694
692
  }
695
693
 
696
- // TODO: Adjust to use callbacks or events to push put received data to allow parallel processing
697
- async readDataReports(expectedSubscriptionIds?: number[]): Promise<DataReport> {
698
- let subscriptionId: number | undefined;
699
- const attributeValues: TypeFromSchema<typeof TlvAttributeReport>[] = [];
700
- const eventValues: TypeFromSchema<typeof TlvEventReport>[] = [];
694
+ async readAggregateDataReport(expectedSubscriptionIds?: number[]): Promise<DataReport> {
695
+ let result: DataReport | undefined;
701
696
 
702
- while (true) {
703
- const dataReportMessage = await this.waitFor("DataReport", MessageType.ReportData);
704
- const report = TlvDataReport.decode(dataReportMessage.payload);
697
+ for await (const report of this.readDataReports()) {
705
698
  if (expectedSubscriptionIds !== undefined) {
706
699
  if (report.subscriptionId === undefined || !expectedSubscriptionIds.includes(report.subscriptionId)) {
707
700
  await this.sendStatus(StatusCode.InvalidSubscription, {
@@ -718,56 +711,82 @@ export class IncomingInteractionClientMessenger extends InteractionMessenger {
718
711
  }
719
712
  }
720
713
 
721
- if (subscriptionId === undefined && report.subscriptionId !== undefined) {
722
- subscriptionId = report.subscriptionId;
723
- } else if (
724
- (subscriptionId !== undefined || report.subscriptionId !== undefined) &&
725
- report.subscriptionId !== subscriptionId
726
- ) {
714
+ if (result?.subscriptionId !== undefined && report.subscriptionId !== result.subscriptionId) {
727
715
  throw new UnexpectedDataError(`Invalid subscription ID ${report.subscriptionId} received`);
728
716
  }
729
717
 
730
- const logContext = {
731
- subId: report.subscriptionId,
732
- dataReportFlags: Diagnostic.asFlags({
733
- empty: !report.attributeReports?.length && !report.eventReports?.length,
734
- suppressResponse: report.suppressResponse,
735
- moreChunkedMessages: report.moreChunkedMessages,
736
- }),
737
- attr: report.attributeReports?.length,
738
- ev: report.eventReports?.length,
739
- };
740
-
741
- if (Array.isArray(report.attributeReports) && report.attributeReports.length > 0) {
742
- attributeValues.push(...report.attributeReports);
743
- }
744
- if (Array.isArray(report.eventReports) && report.eventReports.length > 0) {
745
- eventValues.push(...report.eventReports);
718
+ if (!result) {
719
+ result = report;
720
+ } else {
721
+ if (Array.isArray(report.attributeReports)) {
722
+ if (!result.attributeReports) {
723
+ result.attributeReports = report.attributeReports;
724
+ } else {
725
+ result.attributeReports.push(...report.attributeReports);
726
+ }
727
+ }
728
+ if (Array.isArray(report.eventReports)) {
729
+ if (!result.eventReports) {
730
+ result.eventReports = report.eventReports;
731
+ } else {
732
+ result.eventReports.push(...report.eventReports);
733
+ }
734
+ }
746
735
  }
736
+ }
737
+
738
+ if (result === undefined) {
739
+ // readDataReports should have thrown
740
+ throw new InternalError("No data reports loaded during read");
741
+ }
742
+
743
+ return result;
744
+ }
745
+
746
+ /**
747
+ * Read data reports as they come in on the wire.
748
+ *
749
+ * Data reports payloads are decoded but list attributes may be split across messages; these will require reassembly.
750
+ */
751
+ async *readDataReports() {
752
+ while (true) {
753
+ const dataReportMessage = await this.waitFor("DataReport", MessageType.ReportData);
754
+ const report = TlvDataReport.decode(dataReportMessage.payload);
755
+
756
+ yield report;
747
757
 
748
758
  if (report.moreChunkedMessages) {
749
759
  await this.sendStatus(StatusCode.Success, {
750
760
  multipleMessageInteraction: true,
751
- logContext,
761
+ logContext: this.#logContextOf(report),
752
762
  });
753
763
  } else if (!report.suppressResponse) {
754
- // We received the last message and need to send a final Success, but we do not need to wait for it and
764
+ // We received the last message and need to send a final success, but we do not need to wait for it and
755
765
  // also don't care if it fails
756
766
  this.sendStatus(StatusCode.Success, {
757
767
  multipleMessageInteraction: true,
758
- logContext,
759
- }).catch(error =>
760
- logger.info("Error while sending final Success after receiving all DataReport chunks", error),
761
- );
768
+ logContext: this.#logContextOf(report),
769
+ }).catch(error => logger.info("Error sending success after final data report chunk", error));
762
770
  }
763
771
 
764
772
  if (!report.moreChunkedMessages) {
765
- report.attributeReports = attributeValues;
766
- report.eventReports = eventValues;
767
- return report;
773
+ break;
768
774
  }
769
775
  }
770
776
  }
777
+
778
+ #logContextOf(report: DataReport) {
779
+ return {
780
+ subId: report.subscriptionId,
781
+ dataReportFlags: Diagnostic.asFlags({
782
+ empty: !report.attributeReports?.length && !report.eventReports?.length,
783
+ suppressResponse: report.suppressResponse,
784
+ moreChunkedMessages: report.moreChunkedMessages,
785
+ }),
786
+ attr: report.attributeReports?.length,
787
+ ev: report.eventReports?.length,
788
+ };
789
+ }
771
790
  }
772
791
 
773
792
  export class InteractionClientMessenger extends IncomingInteractionClientMessenger {
@@ -793,6 +812,7 @@ export class InteractionClientMessenger extends IncomingInteractionClientMesseng
793
812
  return await this.exchange.send(messageType, payload, options);
794
813
  } catch (error) {
795
814
  if (
815
+ this.exchangeProvider.supportsReconnect &&
796
816
  (error instanceof RetransmissionLimitReachedError || error instanceof ChannelNotConnectedError) &&
797
817
  !options?.multipleMessageInteraction
798
818
  ) {
@@ -815,7 +835,7 @@ export class InteractionClientMessenger extends IncomingInteractionClientMesseng
815
835
  async sendReadRequest(readRequest: ReadRequest) {
816
836
  await this.send(MessageType.ReadRequest, this.#encodeReadingRequest(TlvReadRequest, readRequest));
817
837
 
818
- return this.readDataReports();
838
+ return this.readAggregateDataReport();
819
839
  }
820
840
 
821
841
  #encodeReadingRequest<T extends TlvSchema<any>>(schema: T, request: TypeFromSchema<T>) {
@@ -876,7 +896,7 @@ export class InteractionClientMessenger extends IncomingInteractionClientMesseng
876
896
  const request = this.#encodeReadingRequest(TlvSubscribeRequest, subscribeRequest);
877
897
  await this.send(MessageType.SubscribeRequest, request);
878
898
 
879
- const report = await this.readDataReports();
899
+ const report = await this.readAggregateDataReport();
880
900
  const { subscriptionId } = report;
881
901
 
882
902
  if (subscriptionId === undefined) {
@@ -4,7 +4,7 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
- import { Crypto, Diagnostic, InternalError, Logger, MatterFlowError } from "#general";
7
+ import { Crypto, Diagnostic, InternalError, Logger, MatterFlowError, Observable, ServerAddressIp } from "#general";
8
8
  import { AttributeModel, ClusterModel, CommandModel, GLOBAL_IDS, MatterModel, Specification } from "#model";
9
9
  import { PeerAddress } from "#peer/PeerAddress.js";
10
10
  import { SessionManager } from "#session/SessionManager.js";
@@ -67,6 +67,19 @@ import { ServerSubscriptionConfig } from "./SubscriptionOptions.js";
67
67
 
68
68
  const logger = Logger.get("InteractionServer");
69
69
 
70
+ export interface PeerSubscription {
71
+ subscriptionId: number;
72
+ peerAddress: PeerAddress;
73
+ minIntervalFloorSeconds: number;
74
+ maxIntervalCeilingSeconds: number;
75
+ attributeRequests?: TypeFromSchema<typeof TlvAttributePath>[];
76
+ eventRequests?: TypeFromSchema<typeof TlvEventPath>[];
77
+ isFabricFiltered: boolean;
78
+ maxInterval: number;
79
+ sendInterval: number;
80
+ operationalAddress?: ServerAddressIp;
81
+ }
82
+
70
83
  export interface CommandPath {
71
84
  nodeId?: NodeId;
72
85
  endpointId: EndpointNumber;
@@ -227,11 +240,14 @@ export interface InteractionContext {
227
240
  * Translates interactions from the Matter protocol to matter.js APIs.
228
241
  */
229
242
  export class InteractionServer implements ProtocolHandler, InteractionRecipient {
243
+ readonly id = INTERACTION_PROTOCOL_ID;
230
244
  #context: InteractionContext;
231
245
  #nextSubscriptionId = Crypto.getRandomUInt32();
232
246
  #isClosing = false;
247
+ #clientHandler?: ProtocolHandler;
233
248
  readonly #subscriptionConfig: ServerSubscriptionConfig;
234
249
  readonly #maxPathsPerInvoke;
250
+ readonly #subscriptionEstablishmentStarted = Observable<[peerAddress: PeerAddress]>();
235
251
 
236
252
  constructor(context: InteractionContext) {
237
253
  this.#context = context;
@@ -244,10 +260,6 @@ export class InteractionServer implements ProtocolHandler, InteractionRecipient
244
260
  });
245
261
  }
246
262
 
247
- getId() {
248
- return INTERACTION_PROTOCOL_ID;
249
- }
250
-
251
263
  protected get isClosing() {
252
264
  return this.#isClosing;
253
265
  }
@@ -256,13 +268,32 @@ export class InteractionServer implements ProtocolHandler, InteractionRecipient
256
268
  return this.#maxPathsPerInvoke;
257
269
  }
258
270
 
259
- async onNewExchange(exchange: MessageExchange) {
271
+ get subscriptionEstablishmentStarted() {
272
+ return this.#subscriptionEstablishmentStarted;
273
+ }
274
+
275
+ async onNewExchange(exchange: MessageExchange, message: Message) {
260
276
  // Note - changes here must be copied to TransactionalInteractionServer as it does not call super() to avoid
261
277
  // the stack frame
262
278
  if (this.#isClosing) return; // We are closing, ignore anything newly incoming
279
+
280
+ // An incoming data report as the first message is not a valid server operation. We instead delegate to a
281
+ // client implementation if available
282
+ if (message.payloadHeader.messageType === MessageType.SubscribeRequest && this.#clientHandler) {
283
+ return this.#clientHandler.onNewExchange(exchange, message);
284
+ }
285
+
263
286
  await new InteractionServerMessenger(exchange).handleRequest(this);
264
287
  }
265
288
 
289
+ get clientHandler(): ProtocolHandler | undefined {
290
+ return this.#clientHandler;
291
+ }
292
+
293
+ set clientHandler(clientHandler: ProtocolHandler) {
294
+ this.#clientHandler = clientHandler;
295
+ }
296
+
266
297
  async #collectEventDataForRead(
267
298
  { eventRequests, eventFilters, isFabricFiltered }: ReadRequest,
268
299
  exchange: MessageExchange,
@@ -962,7 +993,11 @@ export class InteractionServer implements ProtocolHandler, InteractionRecipient
962
993
 
963
994
  async handleSubscribeRequest(
964
995
  exchange: MessageExchange,
965
- {
996
+ request: SubscribeRequest,
997
+ messenger: InteractionServerMessenger,
998
+ message: Message,
999
+ ): Promise<void> {
1000
+ const {
966
1001
  minIntervalFloorSeconds,
967
1002
  maxIntervalCeilingSeconds,
968
1003
  attributeRequests,
@@ -972,10 +1007,7 @@ export class InteractionServer implements ProtocolHandler, InteractionRecipient
972
1007
  keepSubscriptions,
973
1008
  isFabricFiltered,
974
1009
  interactionModelRevision,
975
- }: SubscribeRequest,
976
- messenger: InteractionServerMessenger,
977
- message: Message,
978
- ): Promise<void> {
1010
+ } = request;
979
1011
  logger.debug(
980
1012
  `Received subscribe request from ${exchange.channel.name} (keepSubscriptions=${keepSubscriptions}, isFabricFiltered=${isFabricFiltered})`,
981
1013
  );
@@ -1004,8 +1036,7 @@ export class InteractionServer implements ProtocolHandler, InteractionRecipient
1004
1036
 
1005
1037
  if (!keepSubscriptions) {
1006
1038
  const clearedCount = await this.#context.sessions.clearSubscriptionsForNode(
1007
- fabric.fabricIndex,
1008
- session.peerNodeId,
1039
+ fabric.addressOf(session.peerNodeId),
1009
1040
  true,
1010
1041
  );
1011
1042
  if (clearedCount > 0) {
@@ -1071,6 +1102,71 @@ export class InteractionServer implements ProtocolHandler, InteractionRecipient
1071
1102
  if (this.#nextSubscriptionId === 0xffffffff) this.#nextSubscriptionId = 0;
1072
1103
  const subscriptionId = this.#nextSubscriptionId++;
1073
1104
 
1105
+ this.#subscriptionEstablishmentStarted.emit(session.peerAddress);
1106
+ let subscription: ServerSubscription;
1107
+ try {
1108
+ subscription = await this.#establishSubscription(
1109
+ subscriptionId,
1110
+ request,
1111
+ messenger,
1112
+ session,
1113
+ exchange,
1114
+ message,
1115
+ );
1116
+ } catch (error: any) {
1117
+ logger.error(
1118
+ `Subscription ${subscriptionId} for Session ${session.id}: Error while sending initial data reports`,
1119
+ error,
1120
+ );
1121
+ if (error instanceof StatusResponseError) {
1122
+ logger.info(`Sending status response ${error.code} for interaction error: ${error.message}`);
1123
+ await messenger.sendStatus(error.code, {
1124
+ logContext: {
1125
+ for: "I/SubscriptionSeed-Status",
1126
+ },
1127
+ });
1128
+ }
1129
+ await messenger.close();
1130
+ return; // Make sure to not bubble up the exception
1131
+ }
1132
+
1133
+ const maxInterval = subscription.maxInterval;
1134
+ // Then send the subscription response
1135
+ await messenger.send(
1136
+ MessageType.SubscribeResponse,
1137
+ TlvSubscribeResponse.encode({
1138
+ subscriptionId,
1139
+ maxInterval,
1140
+ interactionModelRevision: Specification.INTERACTION_MODEL_REVISION,
1141
+ }),
1142
+ {
1143
+ logContext: {
1144
+ subId: subscriptionId,
1145
+ maxInterval,
1146
+ },
1147
+ },
1148
+ );
1149
+
1150
+ // When an error occurs while sending the response, the subscription is not yet active and will be cleaned up by GC
1151
+ subscription.activate();
1152
+ }
1153
+
1154
+ async #establishSubscription(
1155
+ id: number,
1156
+ {
1157
+ minIntervalFloorSeconds,
1158
+ maxIntervalCeilingSeconds,
1159
+ attributeRequests,
1160
+ dataVersionFilters,
1161
+ eventRequests,
1162
+ eventFilters,
1163
+ isFabricFiltered,
1164
+ }: SubscribeRequest,
1165
+ messenger: InteractionServerMessenger,
1166
+ session: SecureSession,
1167
+ exchange: MessageExchange,
1168
+ message: Message,
1169
+ ) {
1074
1170
  const context: ServerSubscriptionContext = {
1075
1171
  session,
1076
1172
  structure: this.#endpointStructure,
@@ -1088,7 +1184,7 @@ export class InteractionServer implements ProtocolHandler, InteractionRecipient
1088
1184
  };
1089
1185
 
1090
1186
  const subscription = new ServerSubscription({
1091
- id: subscriptionId,
1187
+ id,
1092
1188
  context,
1093
1189
  criteria: {
1094
1190
  attributeRequests,
@@ -1097,56 +1193,86 @@ export class InteractionServer implements ProtocolHandler, InteractionRecipient
1097
1193
  eventFilters,
1098
1194
  isFabricFiltered,
1099
1195
  },
1100
- minIntervalFloor: minIntervalFloorSeconds,
1101
- maxIntervalCeiling: maxIntervalCeilingSeconds,
1196
+ minIntervalFloorSeconds,
1197
+ maxIntervalCeilingSeconds,
1102
1198
  subscriptionOptions: this.#subscriptionConfig,
1103
1199
  });
1104
1200
 
1105
1201
  try {
1106
1202
  // Send initial data report to prime the subscription with initial data
1107
1203
  await subscription.sendInitialReport(messenger);
1108
- } catch (error: any) {
1109
- logger.error(
1110
- `Subscription ${subscriptionId} for Session ${session.id}: Error while sending initial data reports`,
1111
- error,
1112
- );
1204
+ } catch (error) {
1113
1205
  await subscription.close(); // Cleanup
1114
- if (error instanceof StatusResponseError) {
1115
- logger.info(`Sending status response ${error.code} for interaction error: ${error.message}`);
1116
- await messenger.sendStatus(error.code, {
1117
- logContext: {
1118
- for: "I/SubscriptionSeed-Status",
1119
- },
1120
- });
1121
- }
1122
- await messenger.close();
1123
- return; // Make sure to not bubble up the exception
1206
+ throw error;
1124
1207
  }
1125
1208
 
1126
- const maxInterval = subscription.maxInterval;
1127
1209
  logger.info(
1128
- `Successfully created subscription ${subscriptionId} for Session ${
1210
+ `Successfully created subscription ${id} for Session ${
1129
1211
  session.id
1130
- }. Updates: ${minIntervalFloorSeconds} - ${maxIntervalCeilingSeconds} => ${maxInterval} seconds (sendInterval = ${subscription.sendInterval} seconds)`,
1212
+ }. Updates: ${minIntervalFloorSeconds} - ${maxIntervalCeilingSeconds} => ${subscription.maxInterval} seconds (sendInterval = ${subscription.sendInterval} seconds)`,
1131
1213
  );
1132
- // Then send the subscription response
1133
- await messenger.send(
1134
- MessageType.SubscribeResponse,
1135
- TlvSubscribeResponse.encode({
1136
- subscriptionId,
1137
- maxInterval,
1138
- interactionModelRevision: Specification.INTERACTION_MODEL_REVISION,
1139
- }),
1140
- {
1141
- logContext: {
1142
- subId: subscriptionId,
1143
- maxInterval,
1144
- },
1145
- },
1214
+ return subscription;
1215
+ }
1216
+
1217
+ async establishFormerSubscription(
1218
+ {
1219
+ subscriptionId,
1220
+ attributeRequests,
1221
+ eventRequests,
1222
+ isFabricFiltered,
1223
+ minIntervalFloorSeconds,
1224
+ maxIntervalCeilingSeconds,
1225
+ maxInterval,
1226
+ sendInterval,
1227
+ }: PeerSubscription,
1228
+ session: SecureSession,
1229
+ ) {
1230
+ const exchange = this.#context.initiateExchange(session.peerAddress, INTERACTION_PROTOCOL_ID);
1231
+ const message = {} as Message;
1232
+ logger.debug(
1233
+ `Send DataReports to re-establish subscription ${subscriptionId} to `,
1234
+ Diagnostic.dict({ isFabricFiltered, maxInterval, sendInterval }),
1146
1235
  );
1236
+ const context: ServerSubscriptionContext = {
1237
+ session,
1238
+ structure: this.#endpointStructure,
1147
1239
 
1148
- // When an error occurs while sending the response, the subscription is not yet active and will be cleaned up by GC
1149
- subscription.activateSendingUpdates();
1240
+ readAttribute: (path, attribute, offline) =>
1241
+ this.readAttribute(path, attribute, exchange, isFabricFiltered, message, offline),
1242
+
1243
+ readEndpointAttributesForSubscription: attributes =>
1244
+ this.readEndpointAttributesForSubscription(attributes, exchange, isFabricFiltered, message),
1245
+
1246
+ readEvent: (path, event, eventFilters) =>
1247
+ this.readEvent(path, eventFilters, event, exchange, isFabricFiltered, message),
1248
+
1249
+ initiateExchange: (address: PeerAddress, protocolId) => this.#context.initiateExchange(address, protocolId),
1250
+ };
1251
+
1252
+ const subscription = new ServerSubscription({
1253
+ id: subscriptionId,
1254
+ context,
1255
+ minIntervalFloorSeconds,
1256
+ maxIntervalCeilingSeconds,
1257
+ criteria: {
1258
+ attributeRequests,
1259
+ eventRequests,
1260
+ isFabricFiltered,
1261
+ },
1262
+ subscriptionOptions: this.#subscriptionConfig,
1263
+ useAsMaxInterval: maxInterval,
1264
+ useAsSendInterval: sendInterval,
1265
+ });
1266
+
1267
+ try {
1268
+ // Send initial data report to prime the subscription with initial data
1269
+ await subscription.sendInitialReport(new InteractionServerMessenger(exchange));
1270
+ subscription.activate();
1271
+ } catch (error) {
1272
+ await subscription.close(); // Cleanup
1273
+ throw error;
1274
+ }
1275
+ return subscription;
1150
1276
  }
1151
1277
 
1152
1278
  async handleInvokeRequest(