@matter/protocol 0.16.0-alpha.0-20251016-b56cf5683 → 0.16.0-alpha.0-20251020-3f6e46245

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 (201) hide show
  1. package/dist/cjs/action/client/ClientInteraction.d.ts +10 -5
  2. package/dist/cjs/action/client/ClientInteraction.d.ts.map +1 -1
  3. package/dist/cjs/action/client/ClientInteraction.js +129 -18
  4. package/dist/cjs/action/client/ClientInteraction.js.map +1 -1
  5. package/dist/cjs/action/request/Invoke.d.ts +36 -8
  6. package/dist/cjs/action/request/Invoke.d.ts.map +1 -1
  7. package/dist/cjs/action/request/Invoke.js +80 -15
  8. package/dist/cjs/action/request/Invoke.js.map +1 -1
  9. package/dist/cjs/action/request/Read.d.ts +1 -1
  10. package/dist/cjs/action/request/Read.d.ts.map +1 -1
  11. package/dist/cjs/action/request/Read.js +10 -2
  12. package/dist/cjs/action/request/Read.js.map +1 -1
  13. package/dist/cjs/action/request/Specifier.d.ts +16 -1
  14. package/dist/cjs/action/request/Specifier.d.ts.map +1 -1
  15. package/dist/cjs/action/request/Specifier.js +56 -1
  16. package/dist/cjs/action/request/Specifier.js.map +1 -1
  17. package/dist/cjs/action/request/Write.d.ts +5 -3
  18. package/dist/cjs/action/request/Write.d.ts.map +1 -1
  19. package/dist/cjs/action/request/Write.js +52 -12
  20. package/dist/cjs/action/request/Write.js.map +1 -1
  21. package/dist/cjs/action/response/InvokeResult.d.ts +6 -0
  22. package/dist/cjs/action/response/InvokeResult.d.ts.map +1 -1
  23. package/dist/cjs/common/FailsafeContext.d.ts +2 -1
  24. package/dist/cjs/common/FailsafeContext.d.ts.map +1 -1
  25. package/dist/cjs/common/FailsafeContext.js +10 -5
  26. package/dist/cjs/common/FailsafeContext.js.map +1 -1
  27. package/dist/cjs/common/FailsafeTimer.d.ts +2 -0
  28. package/dist/cjs/common/FailsafeTimer.d.ts.map +1 -1
  29. package/dist/cjs/common/FailsafeTimer.js +9 -0
  30. package/dist/cjs/common/FailsafeTimer.js.map +1 -1
  31. package/dist/cjs/interaction/InteractionClient.d.ts.map +1 -1
  32. package/dist/cjs/interaction/InteractionClient.js +91 -74
  33. package/dist/cjs/interaction/InteractionClient.js.map +1 -1
  34. package/dist/cjs/interaction/InteractionMessenger.d.ts +3 -2
  35. package/dist/cjs/interaction/InteractionMessenger.d.ts.map +1 -1
  36. package/dist/cjs/interaction/InteractionMessenger.js +10 -3
  37. package/dist/cjs/interaction/InteractionMessenger.js.map +1 -1
  38. package/dist/cjs/protocol/ExchangeManager.d.ts.map +1 -1
  39. package/dist/cjs/protocol/ExchangeManager.js +20 -17
  40. package/dist/cjs/protocol/ExchangeManager.js.map +1 -1
  41. package/dist/cjs/protocol/MessageChannel.d.ts.map +1 -1
  42. package/dist/cjs/protocol/MessageChannel.js +1 -1
  43. package/dist/cjs/protocol/MessageChannel.js.map +1 -1
  44. package/dist/cjs/protocol/MessageExchange.d.ts +1 -0
  45. package/dist/cjs/protocol/MessageExchange.d.ts.map +1 -1
  46. package/dist/cjs/protocol/MessageExchange.js +14 -4
  47. package/dist/cjs/protocol/MessageExchange.js.map +1 -1
  48. package/dist/cjs/protocol/ProtocolHandler.d.ts +7 -2
  49. package/dist/cjs/protocol/ProtocolHandler.d.ts.map +1 -1
  50. package/dist/cjs/securechannel/SecureChannelProtocol.d.ts +1 -1
  51. package/dist/cjs/securechannel/SecureChannelProtocol.d.ts.map +1 -1
  52. package/dist/cjs/securechannel/SecureChannelProtocol.js +5 -4
  53. package/dist/cjs/securechannel/SecureChannelProtocol.js.map +1 -1
  54. package/dist/cjs/session/GroupSession.d.ts +8 -1
  55. package/dist/cjs/session/GroupSession.d.ts.map +1 -1
  56. package/dist/cjs/session/GroupSession.js +10 -0
  57. package/dist/cjs/session/GroupSession.js.map +1 -1
  58. package/dist/cjs/session/NodeSession.d.ts +4 -1
  59. package/dist/cjs/session/NodeSession.d.ts.map +1 -1
  60. package/dist/cjs/session/NodeSession.js +17 -0
  61. package/dist/cjs/session/NodeSession.js.map +2 -2
  62. package/dist/cjs/session/SecureSession.d.ts +2 -0
  63. package/dist/cjs/session/SecureSession.d.ts.map +1 -1
  64. package/dist/cjs/session/SecureSession.js.map +1 -1
  65. package/dist/cjs/session/Session.d.ts +1 -0
  66. package/dist/cjs/session/Session.d.ts.map +1 -1
  67. package/dist/cjs/session/Session.js +4 -0
  68. package/dist/cjs/session/Session.js.map +1 -1
  69. package/dist/cjs/session/case/CaseClient.d.ts.map +1 -1
  70. package/dist/cjs/session/case/CaseClient.js +3 -15
  71. package/dist/cjs/session/case/CaseClient.js.map +1 -1
  72. package/dist/cjs/session/case/CaseMessenger.d.ts.map +1 -1
  73. package/dist/cjs/session/case/CaseMessenger.js.map +1 -1
  74. package/dist/cjs/session/case/CaseServer.d.ts.map +1 -1
  75. package/dist/cjs/session/case/CaseServer.js +10 -18
  76. package/dist/cjs/session/case/CaseServer.js.map +1 -1
  77. package/dist/cjs/session/index.d.ts +1 -0
  78. package/dist/cjs/session/index.d.ts.map +1 -1
  79. package/dist/cjs/session/index.js +1 -0
  80. package/dist/cjs/session/index.js.map +1 -1
  81. package/dist/cjs/session/pase/PaseClient.js +1 -1
  82. package/dist/cjs/session/pase/PaseClient.js.map +1 -1
  83. package/dist/cjs/session/pase/PaseMessenger.d.ts.map +1 -1
  84. package/dist/cjs/session/pase/PaseMessenger.js.map +1 -1
  85. package/dist/cjs/session/pase/PaseServer.d.ts.map +1 -1
  86. package/dist/cjs/session/pase/PaseServer.js +2 -2
  87. package/dist/cjs/session/pase/PaseServer.js.map +1 -1
  88. package/dist/esm/action/client/ClientInteraction.d.ts +10 -5
  89. package/dist/esm/action/client/ClientInteraction.d.ts.map +1 -1
  90. package/dist/esm/action/client/ClientInteraction.js +140 -20
  91. package/dist/esm/action/client/ClientInteraction.js.map +1 -1
  92. package/dist/esm/action/request/Invoke.d.ts +36 -8
  93. package/dist/esm/action/request/Invoke.d.ts.map +1 -1
  94. package/dist/esm/action/request/Invoke.js +81 -16
  95. package/dist/esm/action/request/Invoke.js.map +1 -1
  96. package/dist/esm/action/request/Read.d.ts +1 -1
  97. package/dist/esm/action/request/Read.d.ts.map +1 -1
  98. package/dist/esm/action/request/Read.js +12 -4
  99. package/dist/esm/action/request/Read.js.map +1 -1
  100. package/dist/esm/action/request/Specifier.d.ts +16 -1
  101. package/dist/esm/action/request/Specifier.d.ts.map +1 -1
  102. package/dist/esm/action/request/Specifier.js +56 -1
  103. package/dist/esm/action/request/Specifier.js.map +1 -1
  104. package/dist/esm/action/request/Write.d.ts +5 -3
  105. package/dist/esm/action/request/Write.d.ts.map +1 -1
  106. package/dist/esm/action/request/Write.js +53 -13
  107. package/dist/esm/action/request/Write.js.map +1 -1
  108. package/dist/esm/action/response/InvokeResult.d.ts +6 -0
  109. package/dist/esm/action/response/InvokeResult.d.ts.map +1 -1
  110. package/dist/esm/common/FailsafeContext.d.ts +2 -1
  111. package/dist/esm/common/FailsafeContext.d.ts.map +1 -1
  112. package/dist/esm/common/FailsafeContext.js +10 -5
  113. package/dist/esm/common/FailsafeContext.js.map +1 -1
  114. package/dist/esm/common/FailsafeTimer.d.ts +2 -0
  115. package/dist/esm/common/FailsafeTimer.d.ts.map +1 -1
  116. package/dist/esm/common/FailsafeTimer.js +9 -0
  117. package/dist/esm/common/FailsafeTimer.js.map +1 -1
  118. package/dist/esm/interaction/InteractionClient.d.ts.map +1 -1
  119. package/dist/esm/interaction/InteractionClient.js +92 -74
  120. package/dist/esm/interaction/InteractionClient.js.map +1 -1
  121. package/dist/esm/interaction/InteractionMessenger.d.ts +3 -2
  122. package/dist/esm/interaction/InteractionMessenger.d.ts.map +1 -1
  123. package/dist/esm/interaction/InteractionMessenger.js +10 -3
  124. package/dist/esm/interaction/InteractionMessenger.js.map +1 -1
  125. package/dist/esm/protocol/ExchangeManager.d.ts.map +1 -1
  126. package/dist/esm/protocol/ExchangeManager.js +20 -17
  127. package/dist/esm/protocol/ExchangeManager.js.map +1 -1
  128. package/dist/esm/protocol/MessageChannel.d.ts.map +1 -1
  129. package/dist/esm/protocol/MessageChannel.js +2 -2
  130. package/dist/esm/protocol/MessageChannel.js.map +1 -1
  131. package/dist/esm/protocol/MessageExchange.d.ts +1 -0
  132. package/dist/esm/protocol/MessageExchange.d.ts.map +1 -1
  133. package/dist/esm/protocol/MessageExchange.js +14 -4
  134. package/dist/esm/protocol/MessageExchange.js.map +1 -1
  135. package/dist/esm/protocol/ProtocolHandler.d.ts +7 -2
  136. package/dist/esm/protocol/ProtocolHandler.d.ts.map +1 -1
  137. package/dist/esm/securechannel/SecureChannelProtocol.d.ts +1 -1
  138. package/dist/esm/securechannel/SecureChannelProtocol.d.ts.map +1 -1
  139. package/dist/esm/securechannel/SecureChannelProtocol.js +5 -4
  140. package/dist/esm/securechannel/SecureChannelProtocol.js.map +1 -1
  141. package/dist/esm/session/GroupSession.d.ts +8 -1
  142. package/dist/esm/session/GroupSession.d.ts.map +1 -1
  143. package/dist/esm/session/GroupSession.js +11 -1
  144. package/dist/esm/session/GroupSession.js.map +1 -1
  145. package/dist/esm/session/NodeSession.d.ts +4 -1
  146. package/dist/esm/session/NodeSession.d.ts.map +1 -1
  147. package/dist/esm/session/NodeSession.js +17 -0
  148. package/dist/esm/session/NodeSession.js.map +2 -2
  149. package/dist/esm/session/SecureSession.d.ts +2 -0
  150. package/dist/esm/session/SecureSession.d.ts.map +1 -1
  151. package/dist/esm/session/SecureSession.js.map +1 -1
  152. package/dist/esm/session/Session.d.ts +1 -0
  153. package/dist/esm/session/Session.d.ts.map +1 -1
  154. package/dist/esm/session/Session.js +4 -0
  155. package/dist/esm/session/Session.js.map +1 -1
  156. package/dist/esm/session/case/CaseClient.d.ts.map +1 -1
  157. package/dist/esm/session/case/CaseClient.js +5 -17
  158. package/dist/esm/session/case/CaseClient.js.map +1 -1
  159. package/dist/esm/session/case/CaseMessenger.d.ts.map +1 -1
  160. package/dist/esm/session/case/CaseMessenger.js.map +1 -1
  161. package/dist/esm/session/case/CaseServer.d.ts.map +1 -1
  162. package/dist/esm/session/case/CaseServer.js +12 -20
  163. package/dist/esm/session/case/CaseServer.js.map +1 -1
  164. package/dist/esm/session/index.d.ts +1 -0
  165. package/dist/esm/session/index.d.ts.map +1 -1
  166. package/dist/esm/session/index.js +1 -0
  167. package/dist/esm/session/index.js.map +1 -1
  168. package/dist/esm/session/pase/PaseClient.js +1 -1
  169. package/dist/esm/session/pase/PaseClient.js.map +1 -1
  170. package/dist/esm/session/pase/PaseMessenger.d.ts.map +1 -1
  171. package/dist/esm/session/pase/PaseMessenger.js.map +1 -1
  172. package/dist/esm/session/pase/PaseServer.d.ts.map +1 -1
  173. package/dist/esm/session/pase/PaseServer.js +3 -2
  174. package/dist/esm/session/pase/PaseServer.js.map +1 -1
  175. package/package.json +6 -6
  176. package/src/action/client/ClientInteraction.ts +181 -27
  177. package/src/action/request/Invoke.ts +149 -27
  178. package/src/action/request/Read.ts +27 -7
  179. package/src/action/request/Specifier.ts +80 -1
  180. package/src/action/request/Write.ts +71 -19
  181. package/src/action/response/InvokeResult.ts +8 -0
  182. package/src/common/FailsafeContext.ts +15 -6
  183. package/src/common/FailsafeTimer.ts +11 -0
  184. package/src/interaction/InteractionClient.ts +135 -96
  185. package/src/interaction/InteractionMessenger.ts +15 -3
  186. package/src/protocol/ExchangeManager.ts +27 -29
  187. package/src/protocol/MessageChannel.ts +2 -2
  188. package/src/protocol/MessageExchange.ts +18 -4
  189. package/src/protocol/ProtocolHandler.ts +7 -2
  190. package/src/securechannel/SecureChannelProtocol.ts +8 -5
  191. package/src/session/GroupSession.ts +12 -1
  192. package/src/session/NodeSession.ts +26 -0
  193. package/src/session/SecureSession.ts +2 -0
  194. package/src/session/Session.ts +5 -0
  195. package/src/session/case/CaseClient.ts +3 -23
  196. package/src/session/case/CaseMessenger.ts +2 -0
  197. package/src/session/case/CaseServer.ts +15 -20
  198. package/src/session/index.ts +1 -0
  199. package/src/session/pase/PaseClient.ts +1 -1
  200. package/src/session/pase/PaseMessenger.ts +2 -0
  201. package/src/session/pase/PaseServer.ts +3 -2
@@ -383,26 +383,6 @@ export class InteractionClient {
383
383
  );
384
384
  }
385
385
 
386
- logger.debug(
387
- `Sending read request: attributes: ${attributeRequests
388
- ?.map(path => resolveAttributeName(path))
389
- .join(", ")} and events: ${eventRequests?.map(path => resolveEventName(path)).join(", ")}`,
390
- );
391
- if (dataVersionFilters !== undefined && dataVersionFilters?.length > 0) {
392
- logger.debug(
393
- `Using DataVersionFilters: ${dataVersionFilters
394
- .map(({ endpointId, clusterId, dataVersion }) => `${endpointId}/${clusterId}=${dataVersion}`)
395
- .join(", ")}`,
396
- );
397
- }
398
- if (eventFilters !== undefined && eventFilters?.length > 0) {
399
- logger.debug(
400
- `Using event filters: ${eventFilters
401
- .map(({ nodeId, eventMin }) => `${nodeId}=${eventMin}`)
402
- .join(", ")}`,
403
- );
404
- }
405
-
406
386
  const result = await this.withMessenger(async messenger => {
407
387
  return await this.processReadRequest(
408
388
  messenger,
@@ -560,12 +540,32 @@ export class InteractionClient {
560
540
  oldValue?: any,
561
541
  ) => void,
562
542
  ): Promise<DecodedDataReport> {
563
- const { attributeRequests, eventRequests } = request;
564
- logger.debug(
565
- `Sending read request to ${messenger.getExchangeChannelName()} for attributes ${attributeRequests
566
- ?.map(path => resolveAttributeName(path))
567
- .join(", ")} and events ${eventRequests?.map(path => resolveEventName(path)).join(", ")}`,
568
- );
543
+ const { attributeRequests, eventRequests, dataVersionFilters, eventFilters, isFabricFiltered } = request;
544
+ logger.debug(() => [
545
+ "Read »",
546
+ messenger.exchange.via,
547
+ Diagnostic.dict({
548
+ attributes: attributeRequests?.length
549
+ ? attributeRequests?.map(path => resolveAttributeName(path)).join(", ")
550
+ : undefined,
551
+ events: eventRequests?.length
552
+ ? eventRequests?.map(path => resolveEventName(path)).join(", ")
553
+ : undefined,
554
+ dataVersionFilters: dataVersionFilters?.length
555
+ ? dataVersionFilters
556
+ .map(
557
+ ({ path: { endpointId, clusterId }, dataVersion }) =>
558
+ `${endpointId}/${clusterId}=${dataVersion}`,
559
+ )
560
+ .join(", ")
561
+ : undefined,
562
+ eventFilters: eventFilters?.length
563
+ ? eventFilters.map(({ nodeId, eventMin }) => `${nodeId}=${eventMin}`).join(", ")
564
+ : undefined,
565
+ fabricFiltered: isFabricFiltered,
566
+ }),
567
+ ]);
568
+
569
569
  // Send read request and combine all (potentially chunked) responses
570
570
  await messenger.sendReadRequest(request);
571
571
  const scope = ReadScope(request);
@@ -576,24 +576,32 @@ export class InteractionClient {
576
576
  // Normalize and decode the response
577
577
  const { attributeReports, attributeStatus, eventReports, eventStatus } = response;
578
578
 
579
- const logData = Array<string>();
580
- if (attributeReports.length > 0) {
581
- logData.push(
582
- `attributes ${attributeReports.map(({ path, value }) => `${resolveAttributeName(path)}=${serialize(value)}`).join(", ")}`,
583
- );
584
- }
585
- if (eventReports.length > 0) {
586
- logData.push(`events ${eventReports.map(({ path }) => resolveEventName(path)).join(", ")}`);
579
+ if (attributeReports.length || eventReports.length || attributeStatus?.length || eventStatus?.length) {
580
+ logger.debug(() => [
581
+ "Read «",
582
+ messenger.exchange.via,
583
+ Diagnostic.dict({
584
+ attributes: attributeReports.length
585
+ ? attributeReports
586
+ .map(({ path, value }) => `${resolveAttributeName(path)}=${serialize(value)}`)
587
+ .join(", ")
588
+ : undefined,
589
+ events: eventReports.length
590
+ ? eventReports.map(({ path }) => resolveEventName(path)).join(", ")
591
+ : undefined,
592
+ attributeStatus: attributeStatus?.length
593
+ ? attributeStatus.map(({ path }) => resolveAttributeName(path)).join(", ")
594
+ : undefined,
595
+ eventStatus: eventStatus?.length
596
+ ? eventStatus.map(({ path }) => resolveEventName(path)).join(", ")
597
+ : undefined,
598
+ fabricFiltered: isFabricFiltered,
599
+ }),
600
+ ]);
601
+ } else {
602
+ logger.debug("Read «", messenger.exchange.via, "empty response");
587
603
  }
588
- if (attributeStatus !== undefined && attributeStatus.length > 0) {
589
- logData.push(`attributeErrors ${attributeStatus.map(({ path }) => resolveAttributeName(path)).join(", ")}`);
590
- }
591
- if (eventStatus !== undefined && eventStatus.length > 0) {
592
- logData.push(`eventErrors ${eventStatus.map(({ path }) => resolveEventName(path)).join(", ")}`);
593
- }
594
- logger.debug(
595
- logData.length ? `Received read response with ${logData.join(", ")}` : "Received empty read response",
596
- );
604
+
597
605
  return response;
598
606
  }
599
607
 
@@ -674,16 +682,7 @@ export class InteractionClient {
674
682
  throw new ImplementationError("Not all attribute write paths are valid for group address writes.");
675
683
  }
676
684
  }
677
- logger.debug(
678
- `Sending write request: ${attributes
679
- .map(
680
- ({ endpointId, clusterId, attribute: { id }, value, dataVersion }) =>
681
- `${resolveAttributeName({ endpointId, clusterId, attributeId: id })} = ${Diagnostic.json(
682
- value,
683
- )} (version=${dataVersion})`,
684
- )
685
- .join(", ")}`,
686
- );
685
+
687
686
  // TODO Add multi message write handling with streamed encoding
688
687
  const writeRequests = attributes.flatMap(
689
688
  ({ endpointId, clusterId, attribute: { id, schema }, value, dataVersion }) => {
@@ -727,6 +726,21 @@ export class InteractionClient {
727
726
  await messenger.sendTimedRequest(timedRequestTimeout);
728
727
  }
729
728
 
729
+ logger.debug(() => [
730
+ "Write »",
731
+ messenger.exchange.via,
732
+ Diagnostic.dict({
733
+ attributes: attributes
734
+ .map(
735
+ ({ endpointId, clusterId, attribute: { id }, value, dataVersion }) =>
736
+ `${resolveAttributeName({ endpointId, clusterId, attributeId: id })} = ${Diagnostic.json(
737
+ value,
738
+ )} (version=${dataVersion})`,
739
+ )
740
+ .join(", "),
741
+ }),
742
+ ]);
743
+
730
744
  return await messenger.sendWriteCommand({
731
745
  suppressResponse,
732
746
  timedRequest,
@@ -798,14 +812,6 @@ export class InteractionClient {
798
812
  }
799
813
  }
800
814
 
801
- logger.debug(
802
- `Sending subscribe request for attribute: ${resolveAttributeName({
803
- endpointId,
804
- clusterId,
805
- attributeId,
806
- })}${knownDataVersion !== undefined ? ` (knownDataVersion=${knownDataVersion})` : ""} with minInterval=${minIntervalFloorSeconds}s/maxInterval=${maxIntervalCeilingSeconds}s`,
807
- );
808
-
809
815
  const request: SubscribeRequest = {
810
816
  interactionModelRevision: Specification.INTERACTION_MODEL_REVISION,
811
817
  attributeRequests: [{ endpointId, clusterId, attributeId }],
@@ -829,6 +835,18 @@ export class InteractionClient {
829
835
  report: DecodedDataReport;
830
836
  maximumPeerResponseTime: Duration;
831
837
  }>(async messenger => {
838
+ logger.debug(() => [
839
+ "Subscribe »",
840
+ messenger.exchange.via,
841
+ Diagnostic.dict({
842
+ attributes: resolveAttributeName({ endpointId, clusterId, attributeId }),
843
+ dataVersionFilter: knownDataVersion,
844
+ fabricFiltered: isFabricFiltered,
845
+ minInterval: Duration.format(Seconds(minIntervalFloorSeconds)),
846
+ maxInterval: Duration.format(Seconds(maxIntervalCeilingSeconds)),
847
+ }),
848
+ ]);
849
+
832
850
  await messenger.sendSubscribeRequest(request);
833
851
  const { subscribeResponse, report } = await messenger.readAggregateSubscribeResponse();
834
852
  return {
@@ -913,10 +931,6 @@ export class InteractionClient {
913
931
  } = options;
914
932
  const { id: eventId } = event;
915
933
 
916
- logger.debug(
917
- `Sending subscribe request for event: ${resolveEventName({ endpointId, clusterId, eventId })} with minInterval=${minIntervalFloor}/maxInterval=${maxIntervalCeiling}`,
918
- );
919
-
920
934
  const {
921
935
  report,
922
936
  subscribeResponse: { subscriptionId, maxInterval },
@@ -926,6 +940,17 @@ export class InteractionClient {
926
940
  report: DecodedDataReport;
927
941
  maximumPeerResponseTime: Duration;
928
942
  }>(async messenger => {
943
+ logger.debug(() => [
944
+ "Subscribe »",
945
+ messenger.exchange.via,
946
+ Diagnostic.dict({
947
+ events: resolveEventName({ endpointId, clusterId, eventId }),
948
+ fabricFiltered: isFabricFiltered,
949
+ minInterval: Duration.format(minIntervalFloor),
950
+ maxInterval: Duration.format(maxIntervalCeiling),
951
+ }),
952
+ ]);
953
+
929
954
  await messenger.sendSubscribeRequest({
930
955
  interactionModelRevision: Specification.INTERACTION_MODEL_REVISION,
931
956
  eventRequests: [{ endpointId, clusterId, eventId, isUrgent }],
@@ -1060,28 +1085,6 @@ export class InteractionClient {
1060
1085
  }
1061
1086
  }
1062
1087
 
1063
- logger.debug(
1064
- `Sending subscribe request: attributes: ${attributeRequests
1065
- .map(path => resolveAttributeName(path))
1066
- .join(
1067
- ", ",
1068
- )} and events: ${eventRequests.map(path => resolveEventName(path)).join(", ")}, keepSubscriptions=${keepSubscriptions} with minInterval=${minIntervalFloorSeconds}s/maxInterval=${maxIntervalCeilingSeconds}s`,
1069
- );
1070
- if (dataVersionFilters !== undefined && dataVersionFilters?.length > 0) {
1071
- logger.debug(
1072
- `Using DataVersionFilters: ${dataVersionFilters
1073
- .map(({ endpointId, clusterId, dataVersion }) => `${endpointId}/${clusterId}=${dataVersion}`)
1074
- .join(", ")}`,
1075
- );
1076
- }
1077
- if (eventFilters !== undefined && eventFilters?.length > 0) {
1078
- logger.debug(
1079
- `Using event filters: ${eventFilters
1080
- .map(({ nodeId, eventMin }) => `${nodeId}=${eventMin}`)
1081
- .join(", ")}`,
1082
- );
1083
- }
1084
-
1085
1088
  const request: SubscribeRequest = {
1086
1089
  interactionModelRevision: Specification.INTERACTION_MODEL_REVISION,
1087
1090
  attributeRequests,
@@ -1108,10 +1111,48 @@ export class InteractionClient {
1108
1111
  report: DecodedDataReport;
1109
1112
  maximumPeerResponseTime: Duration;
1110
1113
  }>(async messenger => {
1114
+ logger.debug(() => [
1115
+ "Subscribe »",
1116
+ messenger.exchange.via,
1117
+ Diagnostic.dict({
1118
+ attributes: attributeRequests.length
1119
+ ? attributeRequests.map(path => resolveAttributeName(path)).join(", ")
1120
+ : undefined,
1121
+ events: eventRequests.length
1122
+ ? eventRequests.map(path => resolveEventName(path)).join(", ")
1123
+ : undefined,
1124
+ dataVersionFilter: dataVersionFilters?.length
1125
+ ? dataVersionFilters
1126
+ .map(
1127
+ ({ endpointId, clusterId, dataVersion }) =>
1128
+ `${endpointId}/${clusterId}=${dataVersion}`,
1129
+ )
1130
+ .join(", ")
1131
+ : undefined,
1132
+ eventFilters: eventFilters?.length
1133
+ ? eventFilters.map(({ nodeId, eventMin }) => `${nodeId}=${eventMin}`).join(", ")
1134
+ : undefined,
1135
+ fabricFiltered: isFabricFiltered,
1136
+ keepSubscriptions,
1137
+ minInterval: Duration.format(Seconds(minIntervalFloorSeconds)),
1138
+ maxInterval: Duration.format(Seconds(maxIntervalCeilingSeconds)),
1139
+ }),
1140
+ ]);
1141
+
1111
1142
  await messenger.sendSubscribeRequest(request);
1112
1143
  const { subscribeResponse, report } = await messenger.readAggregateSubscribeResponse(attributeReports =>
1113
1144
  this.processAttributeUpdates(scope, attributeReports, attributeListener),
1114
1145
  );
1146
+
1147
+ logger.info(
1148
+ "Subscription successful «",
1149
+ messenger.exchange.via,
1150
+ Diagnostic.dict({
1151
+ subId: subscribeResponse.subscriptionId,
1152
+ maxInterval: Duration.format(Seconds(subscribeResponse.maxInterval)),
1153
+ }),
1154
+ );
1155
+
1115
1156
  return {
1116
1157
  subscribeResponse,
1117
1158
  report,
@@ -1119,8 +1160,6 @@ export class InteractionClient {
1119
1160
  };
1120
1161
  }, executeQueued);
1121
1162
 
1122
- logger.info(`Subscription successfully initialized with ID ${subscriptionId} and maxInterval ${maxInterval}s.`);
1123
-
1124
1163
  const subscriptionListener = async (dataReport: {
1125
1164
  attributeReports?: DecodedAttributeReportValue<any>[];
1126
1165
  eventReports?: DecodedEventReportValue<any>[];
@@ -1139,9 +1178,7 @@ export class InteractionClient {
1139
1178
  if (eventReports !== undefined) {
1140
1179
  let maxEventNumber = this.#nodeStore?.maxEventNumber ?? eventReports[0].events[0].eventNumber;
1141
1180
  eventReports.forEach(data => {
1142
- logger.debug(
1143
- `Received event update: ${resolveEventName(data.path)}: ${Diagnostic.json(data.events)}`,
1144
- );
1181
+ logger.debug(`Event update « ${resolveEventName(data.path)}: ${Diagnostic.json(data.events)}`);
1145
1182
  const { events } = data;
1146
1183
 
1147
1184
  maxEventNumber =
@@ -1202,7 +1239,9 @@ export class InteractionClient {
1202
1239
  version,
1203
1240
  } = data;
1204
1241
 
1205
- if (value === undefined) throw new MatterFlowError("Received empty subscription result value.");
1242
+ if (value === undefined) {
1243
+ throw new MatterFlowError("Received empty subscription result value.");
1244
+ }
1206
1245
  const { value: oldValue, version: oldVersion } =
1207
1246
  this.#nodeStore?.retrieveAttribute(endpointId, clusterId, attributeId) ?? {};
1208
1247
  const changed = oldValue !== undefined ? !isDeepEqual(oldValue, value) : undefined;
@@ -1210,7 +1249,7 @@ export class InteractionClient {
1210
1249
  await this.#nodeStore?.persistAttributes([data], scope);
1211
1250
  }
1212
1251
  logger.debug(
1213
- `Received attribute update${changed ? "(value changed)" : ""}: ${resolveAttributeName({
1252
+ `Attribute update «${changed ? " (value changed)" : ""}: ${resolveAttributeName({
1214
1253
  endpointId,
1215
1254
  clusterId,
1216
1255
  attributeId,
@@ -1355,7 +1394,7 @@ export class InteractionClient {
1355
1394
  }
1356
1395
  const response = responseSchema.decodeTlv(commandFields);
1357
1396
  logger.debug(
1358
- `Received invoke response: ${resolveCommandName({
1397
+ `Invoke « ${resolveCommandName({
1359
1398
  endpointId,
1360
1399
  clusterId,
1361
1400
  commandId: requestId,
@@ -1429,7 +1468,7 @@ export class InteractionClient {
1429
1468
  }, executeQueued);
1430
1469
 
1431
1470
  logger.debug(
1432
- `Invoke successful: ${resolveCommandName({
1471
+ `Invoke successful « ${resolveCommandName({
1433
1472
  endpointId,
1434
1473
  clusterId,
1435
1474
  commandId: requestId,
@@ -105,7 +105,19 @@ const DATA_REPORT_MAX_QUEUED_ATTRIBUTE_MESSAGES = 20;
105
105
  const DATA_REPORT_MIN_AVAILABLE_BYTES_BEFORE_SENDING = 40;
106
106
 
107
107
  class InteractionMessenger {
108
- constructor(protected exchange: MessageExchange) {}
108
+ #exchange: MessageExchange;
109
+
110
+ constructor(exchange: MessageExchange) {
111
+ this.#exchange = exchange;
112
+ }
113
+
114
+ get exchange() {
115
+ return this.#exchange;
116
+ }
117
+
118
+ protected set exchange(value: MessageExchange) {
119
+ this.#exchange = value;
120
+ }
109
121
 
110
122
  send(messageType: number, payload: Bytes, options?: ExchangeSendOptions) {
111
123
  return this.exchange.send(messageType, payload, options);
@@ -197,8 +209,8 @@ class InteractionMessenger {
197
209
  );
198
210
  }
199
211
 
200
- getExchangeChannelName() {
201
- return this.exchange.channel.name;
212
+ get session() {
213
+ return this.exchange.session;
202
214
  }
203
215
  }
204
216
 
@@ -225,6 +225,12 @@ export class ExchangeManager {
225
225
  message.payloadHeader.protocolId,
226
226
  message.payloadHeader.messageType,
227
227
  );
228
+ const messageDiagnostics = Diagnostic.dict({
229
+ message: messageId,
230
+ protocol: message.payloadHeader.protocolId,
231
+ exId: message.payloadHeader.exchangeId,
232
+ via: channel.name,
233
+ });
228
234
  if (exchange !== undefined) {
229
235
  if (
230
236
  exchange.requiresSecureSession !== session.isSecure ||
@@ -232,14 +238,15 @@ export class ExchangeManager {
232
238
  (exchange.isClosing && !isStandaloneAck)
233
239
  ) {
234
240
  logger.debug(
235
- `Ignoring message ${messageId} for protocol ${message.payloadHeader.protocolId} and exchange id ${message.payloadHeader.exchangeId} on channel ${channel.name} because ${
236
- exchange.isClosing
237
- ? "exchange is closing"
238
- : exchange.session.id !== packet.header.sessionId
239
- ? `session ID mismatch ${exchange.session.id} vs ${packet.header.sessionId}`
240
- : `session security requirements (${exchange.requiresSecureSession}) not fulfilled`
241
- }`,
241
+ "Ignore « message because",
242
+ exchange.isClosing
243
+ ? "exchange is closing"
244
+ : exchange.session.id !== packet.header.sessionId
245
+ ? `session ID mismatch ${exchange.session.id} vs ${packet.header.sessionId}`
246
+ : `session security requirements (${exchange.requiresSecureSession}) not fulfilled`,
247
+ messageDiagnostics,
242
248
  );
249
+
243
250
  await exchange.send(SecureMessageType.StandaloneAck, new Uint8Array(0), {
244
251
  includeAcknowledgeMessageId: message.packetHeader.messageId,
245
252
  protocolId: SECURE_CHANNEL_PROTOCOL_ID,
@@ -259,15 +266,15 @@ export class ExchangeManager {
259
266
 
260
267
  const protocolHandler = this.#protocols.get(message.payloadHeader.protocolId);
261
268
 
269
+ const handlerSecurityMismatch =
270
+ protocolHandler?.requiresSecureSession !== undefined &&
271
+ protocolHandler.requiresSecureSession !== session.isSecure;
262
272
  // Having a "Secure Session" means it is encrypted in our internal working
263
273
  // TODO When adding Group sessions, we need to check how to adjust that handling
264
- if (
265
- protocolHandler !== undefined &&
266
- protocolHandler.requiresSecureSession !== session.isSecure &&
267
- !isStandaloneAck
268
- ) {
274
+ if (handlerSecurityMismatch) {
269
275
  logger.debug(
270
- `Ignoring message ${messageId} for protocol ${message.payloadHeader.protocolId} and exchange id ${message.payloadHeader.exchangeId} on channel ${channel.name} because not matching the security requirements.`,
276
+ `Ignore « message because not matching the security requirements (${protocolHandler.requiresSecureSession} vs. ${session.isSecure})`,
277
+ messageDiagnostics,
271
278
  );
272
279
  }
273
280
 
@@ -275,12 +282,10 @@ export class ExchangeManager {
275
282
  protocolHandler !== undefined &&
276
283
  message.payloadHeader.isInitiatorMessage &&
277
284
  !isDuplicate &&
278
- protocolHandler.requiresSecureSession === session.isSecure
285
+ !handlerSecurityMismatch
279
286
  ) {
280
287
  if (isStandaloneAck && !message.payloadHeader.requiresAck) {
281
- logger.debug(
282
- `Ignoring unsolicited standalone ack message ${messageId} for protocol ${message.payloadHeader.protocolId} and exchange id ${message.payloadHeader.exchangeId} on channel ${channel.name}`,
283
- );
288
+ logger.debug("Ignore « unsolicited standalone ack message", messageDiagnostics);
284
289
  return;
285
290
  }
286
291
 
@@ -302,9 +307,7 @@ export class ExchangeManager {
302
307
  protocolId: SECURE_CHANNEL_PROTOCOL_ID,
303
308
  });
304
309
  await exchange.close();
305
- logger.debug(
306
- `Ignoring unsolicited message ${messageId} for protocol ${message.payloadHeader.protocolId} on channel ${channel.name}`,
307
- );
310
+ logger.debug("Ignore « unsolicited message", messageDiagnostics);
308
311
  } else {
309
312
  if (protocolHandler === undefined) {
310
313
  throw new MatterFlowError(`Unsupported protocol ${message.payloadHeader.protocolId}`);
@@ -312,17 +315,12 @@ export class ExchangeManager {
312
315
  if (isDuplicate) {
313
316
  if (message.packetHeader.destGroupId === undefined) {
314
317
  // Duplicate Non-Group messages are still interesting to log to know them
315
- logger.info(
316
- `Ignoring duplicate message ${messageId} (requires no ack) for protocol ${message.payloadHeader.protocolId} on channel ${channel.name}`,
317
- );
318
+ logger.debug("Ignore « duplicate message", messageDiagnostics);
318
319
  }
319
320
  return;
320
- } else if (!isStandaloneAck) {
321
- logger.info(
322
- `Discarding unexpected message ${messageId} for protocol ${
323
- message.payloadHeader.protocolId
324
- }, exchangeIndex ${exchangeIndex} and sessionId ${session.id} on channel ${channel.name}: ${Diagnostic.json(message)}`,
325
- );
321
+ }
322
+ if (!isStandaloneAck) {
323
+ logger.info("Discard « unexpected message", messageDiagnostics, Diagnostic.json(message));
326
324
  }
327
325
  }
328
326
  }
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import { Message, MessageCodec } from "#codec/MessageCodec.js";
8
- import { Bytes, Channel, Duration, Logger, MatterError, MatterFlowError, Millis, Seconds } from "#general";
8
+ import { Bytes, Channel, Diagnostic, Duration, Logger, MatterError, MatterFlowError, Millis, Seconds } from "#general";
9
9
  import type { ExchangeLogContext } from "#protocol/MessageExchange.js";
10
10
  import { Session, SessionParameters } from "#session/Session.js";
11
11
 
@@ -101,7 +101,7 @@ export class MessageChannel implements Channel<Message> {
101
101
  }
102
102
 
103
103
  get name() {
104
- return `${this.channel.name} on session ${this.session.name}`;
104
+ return Diagnostic.via(`${this.session.name}@${this.channel.name}`);
105
105
  }
106
106
 
107
107
  async close() {
@@ -30,6 +30,7 @@ import {
30
30
  MRP,
31
31
  } from "#protocol/MessageChannel.js";
32
32
  import { GroupSession } from "#session/GroupSession.js";
33
+ import { SecureSession } from "#session/SecureSession.js";
33
34
  import { SessionParameters } from "#session/Session.js";
34
35
  import {
35
36
  GroupId,
@@ -199,8 +200,8 @@ export class MessageExchange {
199
200
  logger.debug(
200
201
  "New exchange",
201
202
  isInitiator ? "»" : "«",
203
+ channel.name,
202
204
  Diagnostic.dict({
203
- channel: channel.name,
204
205
  protocol: this.#protocolId,
205
206
  exId: this.#exchangeId,
206
207
  sess: session.name,
@@ -582,12 +583,16 @@ export class MessageExchange {
582
583
  StatusCode.InvalidAction,
583
584
  );
584
585
  }
586
+
585
587
  logger.debug(
586
- `Starting timed interaction with Transaction ID ${this.#exchangeId} for ${Duration.format(timeout)} from ${this.channel.name}`,
588
+ "Starting timed interaction «",
589
+ this.channel.name,
590
+ Diagnostic.dict({ exId: this.#exchangeId, timeout: Duration.format(timeout) }),
587
591
  );
588
592
  this.#timedInteractionTimer = Time.getTimer("Timed interaction", timeout, () => {
589
593
  logger.debug(
590
- `Timed interaction with Transaction ID ${this.#exchangeId} from ${this.channel.name} timed out`,
594
+ "Timed interaction timeout!",
595
+ Diagnostic.dict({ exId: this.#exchangeId, via: this.channel.name }),
591
596
  );
592
597
  }).start();
593
598
  }
@@ -595,7 +600,8 @@ export class MessageExchange {
595
600
  clearTimedInteraction() {
596
601
  if (this.#timedInteractionTimer !== undefined) {
597
602
  logger.debug(
598
- `Clearing timed interaction with Transaction ID ${this.#exchangeId} from ${this.channel.name}`,
603
+ "Clearing timed interaction",
604
+ Diagnostic.dict({ exId: this.#exchangeId, via: this.channel.name }),
599
605
  );
600
606
  this.#timedInteractionTimer.stop();
601
607
  this.#timedInteractionTimer = undefined;
@@ -679,4 +685,12 @@ export class MessageExchange {
679
685
  this.#messagesQueue.close();
680
686
  await this.#closed.emit();
681
687
  }
688
+
689
+ get via() {
690
+ if (this.session == undefined || !this.session.isSecure) {
691
+ return this.channel.name; // already formatted as "via"
692
+ }
693
+
694
+ return Diagnostic.via(`${(this.session as SecureSession).peerAddress.toString()}#${this.session.id}`);
695
+ }
682
696
  }
@@ -11,8 +11,13 @@ export interface ProtocolHandler {
11
11
  /** Protocol ID that this handler implements */
12
12
  readonly id: number;
13
13
 
14
- /** Indicates whether this protocol requires a secure session to be established before it can be used */
15
- readonly requiresSecureSession: boolean;
14
+ /**
15
+ * Indicates whether this protocol requires a secure session to be established before it can be used.
16
+ * When `true`, the protocol can only be used over secure sessions.
17
+ * When `false`, the protocol can only be used without a secure session.
18
+ * When `undefined`, the protocol can be used with or without a secure session and it is up to the handler to check
19
+ */
20
+ readonly requiresSecureSession: boolean | undefined;
16
21
 
17
22
  /** Called on a new exchange that uses this protocol. The message is the first message of the exchange. */
18
23
  onNewExchange(exchange: MessageExchange, message: Message): Promise<void>;
@@ -7,6 +7,7 @@
7
7
  import { FabricManager } from "#fabric/FabricManager.js";
8
8
  import { AsyncObservable, Environment, Environmental, Logger, MatterFlowError } from "#general";
9
9
  import { ExchangeManager } from "#protocol/ExchangeManager.js";
10
+ import { NodeSession } from "#session/NodeSession.js";
10
11
  import { SessionManager } from "#session/SessionManager.js";
11
12
  import {
12
13
  GeneralStatusCode,
@@ -19,7 +20,6 @@ import {
19
20
  import { Message } from "../codec/MessageCodec.js";
20
21
  import { MessageExchange } from "../protocol/MessageExchange.js";
21
22
  import { ProtocolHandler } from "../protocol/ProtocolHandler.js";
22
- import { SecureSession } from "../session/SecureSession.js";
23
23
  import { CaseServer } from "../session/case/CaseServer.js";
24
24
  import { MaximumPasePairingErrorsReachedError, PaseServer } from "../session/pase/PaseServer.js";
25
25
  import { ChannelStatusResponseError, SecureChannelMessenger } from "./SecureChannelMessenger.js";
@@ -29,7 +29,9 @@ const logger = Logger.get("SecureChannelProtocol");
29
29
 
30
30
  export class StatusReportOnlySecureChannelProtocol implements ProtocolHandler {
31
31
  readonly id = SECURE_CHANNEL_PROTOCOL_ID;
32
- readonly requiresSecureSession = false;
32
+
33
+ // We need to check the message details to decide if ok or not, so we take them all
34
+ readonly requiresSecureSession = undefined;
33
35
 
34
36
  async onNewExchange(exchange: MessageExchange, message: Message) {
35
37
  const messageType = message.payloadHeader.messageType;
@@ -70,6 +72,8 @@ export class StatusReportOnlySecureChannelProtocol implements ProtocolHandler {
70
72
  protocolStatus,
71
73
  );
72
74
  }
75
+
76
+ // CloseSession is the only case where a StatusReport comes as initial message
73
77
  if (protocolStatus !== SecureChannelStatusCode.CloseSession) {
74
78
  throw new ChannelStatusResponseError(
75
79
  `Received general success status, but protocol status is not CloseSession`,
@@ -79,10 +83,9 @@ export class StatusReportOnlySecureChannelProtocol implements ProtocolHandler {
79
83
  }
80
84
 
81
85
  const { session } = exchange;
82
- SecureSession.assert(session);
86
+ NodeSession.assert(session);
83
87
  logger.debug(`Peer requested to close session ${session.name}. Remove session now.`);
84
- // TODO: and do more - see Core Specs 5.5
85
- await session.destroy(false, false);
88
+ await session.closeByPeer();
86
89
  }
87
90
 
88
91
  async close() {
@@ -16,8 +16,9 @@ import {
16
16
  MatterFlowError,
17
17
  UnexpectedDataError,
18
18
  } from "#general";
19
+ import { PeerAddress } from "#peer/PeerAddress.js";
19
20
  import type { SessionManager } from "#session/SessionManager.js";
20
- import { GroupId, NodeId } from "#types";
21
+ import { FabricIndex, GroupId, NodeId } from "#types";
21
22
  import { SecureSession } from "./SecureSession.js";
22
23
  import { Session } from "./Session.js";
23
24
 
@@ -92,6 +93,16 @@ export class GroupSession extends SecureSession {
92
93
  return this.#fabric;
93
94
  }
94
95
 
96
+ /**
97
+ * The peer group's address.
98
+ */
99
+ get peerAddress() {
100
+ return PeerAddress({
101
+ fabricIndex: this.#fabric?.fabricIndex ?? FabricIndex.NO_FABRIC,
102
+ nodeId: this.#peerNodeId,
103
+ });
104
+ }
105
+
95
106
  subjectFor(message?: Message): Subject {
96
107
  if (message === undefined || message.packetHeader.destGroupId === undefined) {
97
108
  throw new ImplementationError("GroupSession requires a message with destGroupId");