@matter/protocol 0.16.0-alpha.0-20250930-05e6cc3f8 → 0.16.0-alpha.0-20251003-dc6d5523d

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 (173) hide show
  1. package/dist/cjs/action/client/ReadScope.d.ts +4 -0
  2. package/dist/cjs/action/client/ReadScope.d.ts.map +1 -1
  3. package/dist/cjs/action/client/ReadScope.js +2 -1
  4. package/dist/cjs/action/client/ReadScope.js.map +1 -1
  5. package/dist/cjs/ble/Ble.d.ts +3 -3
  6. package/dist/cjs/ble/Ble.d.ts.map +1 -1
  7. package/dist/cjs/ble/Ble.js.map +1 -1
  8. package/dist/cjs/common/Scanner.d.ts +2 -2
  9. package/dist/cjs/common/Scanner.d.ts.map +1 -1
  10. package/dist/cjs/interaction/AttributeDataDecoder.d.ts +9 -7
  11. package/dist/cjs/interaction/AttributeDataDecoder.d.ts.map +1 -1
  12. package/dist/cjs/interaction/AttributeDataDecoder.js.map +1 -1
  13. package/dist/cjs/interaction/DecodedDataReport.d.ts +1 -0
  14. package/dist/cjs/interaction/DecodedDataReport.d.ts.map +1 -1
  15. package/dist/cjs/interaction/DecodedDataReport.js.map +1 -1
  16. package/dist/cjs/interaction/InteractionClient.d.ts +13 -3
  17. package/dist/cjs/interaction/InteractionClient.d.ts.map +1 -1
  18. package/dist/cjs/interaction/InteractionClient.js +94 -67
  19. package/dist/cjs/interaction/InteractionClient.js.map +1 -1
  20. package/dist/cjs/interaction/InteractionMessenger.d.ts +10 -90
  21. package/dist/cjs/interaction/InteractionMessenger.d.ts.map +1 -1
  22. package/dist/cjs/interaction/InteractionMessenger.js +98 -22
  23. package/dist/cjs/interaction/InteractionMessenger.js.map +2 -2
  24. package/dist/cjs/interaction/SubscriptionClient.d.ts +2 -2
  25. package/dist/cjs/interaction/SubscriptionClient.d.ts.map +1 -1
  26. package/dist/cjs/interaction/SubscriptionClient.js +1 -1
  27. package/dist/cjs/interaction/SubscriptionClient.js.map +1 -1
  28. package/dist/cjs/mdns/MdnsClient.d.ts +3 -3
  29. package/dist/cjs/mdns/MdnsClient.d.ts.map +1 -1
  30. package/dist/cjs/peer/ControllerCommissioner.d.ts +2 -2
  31. package/dist/cjs/peer/ControllerCommissioner.d.ts.map +1 -1
  32. package/dist/cjs/peer/ControllerCommissioner.js +7 -5
  33. package/dist/cjs/peer/ControllerCommissioner.js.map +1 -1
  34. package/dist/cjs/peer/OperationalPeer.d.ts +2 -2
  35. package/dist/cjs/peer/OperationalPeer.d.ts.map +1 -1
  36. package/dist/cjs/peer/PeerAddressStore.d.ts +3 -1
  37. package/dist/cjs/peer/PeerAddressStore.d.ts.map +1 -1
  38. package/dist/cjs/peer/PeerAddressStore.js.map +1 -1
  39. package/dist/cjs/peer/PeerSet.d.ts +3 -3
  40. package/dist/cjs/peer/PeerSet.d.ts.map +1 -1
  41. package/dist/cjs/peer/PeerSet.js +14 -9
  42. package/dist/cjs/peer/PeerSet.js.map +1 -1
  43. package/dist/cjs/protocol/ExchangeManager.d.ts +2 -2
  44. package/dist/cjs/protocol/ExchangeManager.d.ts.map +1 -1
  45. package/dist/cjs/protocol/ExchangeManager.js +21 -17
  46. package/dist/cjs/protocol/ExchangeManager.js.map +1 -1
  47. package/dist/cjs/protocol/MessageChannel.d.ts +1 -1
  48. package/dist/cjs/protocol/MessageChannel.d.ts.map +1 -1
  49. package/dist/cjs/protocol/MessageChannel.js +7 -5
  50. package/dist/cjs/protocol/MessageChannel.js.map +1 -1
  51. package/dist/cjs/protocol/MessageExchange.d.ts +0 -1
  52. package/dist/cjs/protocol/MessageExchange.d.ts.map +1 -1
  53. package/dist/cjs/protocol/MessageExchange.js +6 -17
  54. package/dist/cjs/protocol/MessageExchange.js.map +1 -1
  55. package/dist/cjs/session/NodeSession.d.ts.map +1 -1
  56. package/dist/cjs/session/NodeSession.js +3 -3
  57. package/dist/cjs/session/NodeSession.js.map +1 -1
  58. package/dist/cjs/session/SessionIntervals.d.ts +1 -5
  59. package/dist/cjs/session/SessionIntervals.d.ts.map +1 -1
  60. package/dist/cjs/session/SessionIntervals.js.map +1 -1
  61. package/dist/cjs/session/case/CaseMessages.d.ts +18 -9
  62. package/dist/cjs/session/case/CaseMessages.d.ts.map +1 -1
  63. package/dist/cjs/session/case/CaseMessages.js.map +1 -1
  64. package/dist/cjs/session/case/CaseMessenger.d.ts +4 -62
  65. package/dist/cjs/session/case/CaseMessenger.d.ts.map +1 -1
  66. package/dist/cjs/session/case/CaseMessenger.js.map +1 -1
  67. package/dist/cjs/session/case/CaseServer.js.map +1 -1
  68. package/dist/cjs/session/pase/PaseMessages.d.ts +20 -9
  69. package/dist/cjs/session/pase/PaseMessages.d.ts.map +1 -1
  70. package/dist/cjs/session/pase/PaseMessages.js +3 -3
  71. package/dist/cjs/session/pase/PaseMessages.js.map +1 -1
  72. package/dist/cjs/session/pase/PaseMessenger.d.ts +3 -51
  73. package/dist/cjs/session/pase/PaseMessenger.d.ts.map +1 -1
  74. package/dist/cjs/session/pase/PaseMessenger.js.map +1 -1
  75. package/dist/esm/action/client/ReadScope.d.ts +4 -0
  76. package/dist/esm/action/client/ReadScope.d.ts.map +1 -1
  77. package/dist/esm/action/client/ReadScope.js +2 -1
  78. package/dist/esm/action/client/ReadScope.js.map +1 -1
  79. package/dist/esm/ble/Ble.d.ts +3 -3
  80. package/dist/esm/ble/Ble.d.ts.map +1 -1
  81. package/dist/esm/ble/Ble.js.map +1 -1
  82. package/dist/esm/common/Scanner.d.ts +2 -2
  83. package/dist/esm/common/Scanner.d.ts.map +1 -1
  84. package/dist/esm/interaction/AttributeDataDecoder.d.ts +9 -7
  85. package/dist/esm/interaction/AttributeDataDecoder.d.ts.map +1 -1
  86. package/dist/esm/interaction/AttributeDataDecoder.js.map +1 -1
  87. package/dist/esm/interaction/DecodedDataReport.d.ts +1 -0
  88. package/dist/esm/interaction/DecodedDataReport.d.ts.map +1 -1
  89. package/dist/esm/interaction/DecodedDataReport.js.map +1 -1
  90. package/dist/esm/interaction/InteractionClient.d.ts +13 -3
  91. package/dist/esm/interaction/InteractionClient.d.ts.map +1 -1
  92. package/dist/esm/interaction/InteractionClient.js +96 -68
  93. package/dist/esm/interaction/InteractionClient.js.map +1 -1
  94. package/dist/esm/interaction/InteractionMessenger.d.ts +10 -90
  95. package/dist/esm/interaction/InteractionMessenger.d.ts.map +1 -1
  96. package/dist/esm/interaction/InteractionMessenger.js +98 -22
  97. package/dist/esm/interaction/InteractionMessenger.js.map +2 -2
  98. package/dist/esm/interaction/SubscriptionClient.d.ts +2 -2
  99. package/dist/esm/interaction/SubscriptionClient.d.ts.map +1 -1
  100. package/dist/esm/interaction/SubscriptionClient.js +1 -1
  101. package/dist/esm/interaction/SubscriptionClient.js.map +1 -1
  102. package/dist/esm/mdns/MdnsClient.d.ts +3 -3
  103. package/dist/esm/mdns/MdnsClient.d.ts.map +1 -1
  104. package/dist/esm/peer/ControllerCommissioner.d.ts +2 -2
  105. package/dist/esm/peer/ControllerCommissioner.d.ts.map +1 -1
  106. package/dist/esm/peer/ControllerCommissioner.js +9 -6
  107. package/dist/esm/peer/ControllerCommissioner.js.map +1 -1
  108. package/dist/esm/peer/OperationalPeer.d.ts +2 -2
  109. package/dist/esm/peer/OperationalPeer.d.ts.map +1 -1
  110. package/dist/esm/peer/PeerAddressStore.d.ts +3 -1
  111. package/dist/esm/peer/PeerAddressStore.d.ts.map +1 -1
  112. package/dist/esm/peer/PeerAddressStore.js.map +1 -1
  113. package/dist/esm/peer/PeerSet.d.ts +3 -3
  114. package/dist/esm/peer/PeerSet.d.ts.map +1 -1
  115. package/dist/esm/peer/PeerSet.js +15 -10
  116. package/dist/esm/peer/PeerSet.js.map +1 -1
  117. package/dist/esm/protocol/ExchangeManager.d.ts +2 -2
  118. package/dist/esm/protocol/ExchangeManager.d.ts.map +1 -1
  119. package/dist/esm/protocol/ExchangeManager.js +22 -18
  120. package/dist/esm/protocol/ExchangeManager.js.map +1 -1
  121. package/dist/esm/protocol/MessageChannel.d.ts +1 -1
  122. package/dist/esm/protocol/MessageChannel.d.ts.map +1 -1
  123. package/dist/esm/protocol/MessageChannel.js +7 -5
  124. package/dist/esm/protocol/MessageChannel.js.map +1 -1
  125. package/dist/esm/protocol/MessageExchange.d.ts +0 -1
  126. package/dist/esm/protocol/MessageExchange.d.ts.map +1 -1
  127. package/dist/esm/protocol/MessageExchange.js +6 -17
  128. package/dist/esm/protocol/MessageExchange.js.map +1 -1
  129. package/dist/esm/session/NodeSession.d.ts.map +1 -1
  130. package/dist/esm/session/NodeSession.js +4 -3
  131. package/dist/esm/session/NodeSession.js.map +1 -1
  132. package/dist/esm/session/SessionIntervals.d.ts +1 -5
  133. package/dist/esm/session/SessionIntervals.d.ts.map +1 -1
  134. package/dist/esm/session/SessionIntervals.js.map +1 -1
  135. package/dist/esm/session/case/CaseMessages.d.ts +18 -9
  136. package/dist/esm/session/case/CaseMessages.d.ts.map +1 -1
  137. package/dist/esm/session/case/CaseMessages.js.map +1 -1
  138. package/dist/esm/session/case/CaseMessenger.d.ts +4 -62
  139. package/dist/esm/session/case/CaseMessenger.d.ts.map +1 -1
  140. package/dist/esm/session/case/CaseMessenger.js +6 -1
  141. package/dist/esm/session/case/CaseMessenger.js.map +1 -1
  142. package/dist/esm/session/case/CaseServer.js.map +1 -1
  143. package/dist/esm/session/pase/PaseMessages.d.ts +20 -9
  144. package/dist/esm/session/pase/PaseMessages.d.ts.map +1 -1
  145. package/dist/esm/session/pase/PaseMessages.js +3 -3
  146. package/dist/esm/session/pase/PaseMessages.js.map +1 -1
  147. package/dist/esm/session/pase/PaseMessenger.d.ts +3 -51
  148. package/dist/esm/session/pase/PaseMessenger.d.ts.map +1 -1
  149. package/dist/esm/session/pase/PaseMessenger.js.map +1 -1
  150. package/package.json +6 -6
  151. package/src/action/client/ReadScope.ts +7 -0
  152. package/src/ble/Ble.ts +3 -3
  153. package/src/common/Scanner.ts +2 -2
  154. package/src/interaction/AttributeDataDecoder.ts +4 -1
  155. package/src/interaction/DecodedDataReport.ts +1 -0
  156. package/src/interaction/InteractionClient.ts +154 -79
  157. package/src/interaction/InteractionMessenger.ts +126 -22
  158. package/src/interaction/SubscriptionClient.ts +6 -5
  159. package/src/mdns/MdnsClient.ts +4 -4
  160. package/src/peer/ControllerCommissioner.ts +10 -7
  161. package/src/peer/OperationalPeer.ts +2 -2
  162. package/src/peer/PeerAddressStore.ts +3 -1
  163. package/src/peer/PeerSet.ts +23 -18
  164. package/src/protocol/ExchangeManager.ts +29 -21
  165. package/src/protocol/MessageChannel.ts +13 -11
  166. package/src/protocol/MessageExchange.ts +6 -18
  167. package/src/session/NodeSession.ts +4 -3
  168. package/src/session/SessionIntervals.ts +1 -1
  169. package/src/session/case/CaseMessages.ts +12 -2
  170. package/src/session/case/CaseMessenger.ts +12 -4
  171. package/src/session/case/CaseServer.ts +5 -5
  172. package/src/session/pase/PaseMessages.ts +27 -3
  173. package/src/session/pase/PaseMessenger.ts +8 -9
@@ -5,10 +5,11 @@
5
5
  */
6
6
 
7
7
  import { Duration, Environment, Environmental, Logger, MaybePromise, Millis, Time, Timer } from "#general";
8
+ import { DecodedDataReport } from "#interaction/DecodedDataReport.js";
8
9
  import { MessageExchange } from "#protocol/MessageExchange.js";
9
10
  import { ProtocolHandler } from "#protocol/ProtocolHandler.js";
10
11
  import { INTERACTION_PROTOCOL_ID } from "#types";
11
- import { DataReport, IncomingInteractionClientMessenger } from "./InteractionMessenger.js";
12
+ import { IncomingInteractionClientMessenger } from "./InteractionMessenger.js";
12
13
 
13
14
  const logger = Logger.get("SubscriptionClient");
14
15
 
@@ -16,7 +17,7 @@ export interface RegisteredSubscription {
16
17
  id: number;
17
18
  maximumPeerResponseTime: Duration;
18
19
  maxInterval: Duration;
19
- onData: (dataReport: DataReport) => MaybePromise<void>;
20
+ onData: (dataReport: DecodedDataReport) => MaybePromise<void>;
20
21
  onTimeout?: () => void;
21
22
  }
22
23
 
@@ -28,7 +29,7 @@ export interface RegisteredSubscription {
28
29
  export class SubscriptionClient implements ProtocolHandler {
29
30
  readonly id = INTERACTION_PROTOCOL_ID;
30
31
  readonly requiresSecureSession = true;
31
- readonly #listeners = new Map<number, (dataReport: DataReport) => MaybePromise<void>>();
32
+ readonly #listeners = new Map<number, (dataReport: DecodedDataReport) => MaybePromise<void>>();
32
33
  readonly #timeouts = new Map<number, Timer>();
33
34
 
34
35
  constructor() {}
@@ -80,10 +81,10 @@ export class SubscriptionClient implements ProtocolHandler {
80
81
  async onNewExchange(exchange: MessageExchange) {
81
82
  const messenger = new IncomingInteractionClientMessenger(exchange);
82
83
 
83
- let dataReport: DataReport;
84
+ let dataReport: DecodedDataReport;
84
85
  try {
85
86
  // TODO Adjust this to getting packages as callback when received to handle error cases and checks outside
86
- dataReport = await messenger.readAggregateDataReport([...this.#listeners.keys()]);
87
+ dataReport = await messenger.readAggregateDataReport(undefined, [...this.#listeners.keys()]);
87
88
  } finally {
88
89
  messenger.close().catch(error => logger.info("Error closing client messenger", error));
89
90
  }
@@ -26,7 +26,7 @@ import {
26
26
  ObserverGroup,
27
27
  Seconds,
28
28
  ServerAddress,
29
- ServerAddressIp,
29
+ ServerAddressUdp,
30
30
  SrvRecordValue,
31
31
  Time,
32
32
  Timer,
@@ -60,7 +60,7 @@ const logger = Logger.get("MdnsClient");
60
60
 
61
61
  const MDNS_EXPIRY_GRACE_PERIOD_FACTOR = 1.05;
62
62
 
63
- type MatterServerRecordWithExpire = ServerAddressIp & Lifespan;
63
+ type MatterServerRecordWithExpire = ServerAddressUdp & Lifespan;
64
64
 
65
65
  /** Type for commissionable Device records including Lifespan details. */
66
66
  type CommissionableDeviceRecordWithExpire = Omit<CommissionableDevice, "addresses"> &
@@ -354,7 +354,7 @@ export class MdnsClient implements Scanner {
354
354
  ip,
355
355
  port,
356
356
  type: "udp",
357
- })) as ServerAddressIp[],
357
+ })) as ServerAddressUdp[],
358
358
  };
359
359
  }
360
360
 
@@ -570,7 +570,7 @@ export class MdnsClient implements Scanner {
570
570
  ip,
571
571
  port,
572
572
  type: "udp",
573
- })) as ServerAddressIp[],
573
+ })) as ServerAddressUdp[],
574
574
  discoveredAt: undefined,
575
575
  ttl: undefined,
576
576
  };
@@ -13,15 +13,16 @@ import {
13
13
  Channel,
14
14
  ChannelType,
15
15
  ClassExtends,
16
+ ConnectionlessTransportSet,
16
17
  Diagnostic,
17
18
  Duration,
18
19
  Environment,
19
20
  Environmental,
21
+ ImplementationError,
20
22
  isIPv6,
21
23
  Logger,
22
24
  Millis,
23
25
  Minutes,
24
- NetInterfaceSet,
25
26
  NoResponseTimeoutError,
26
27
  Seconds,
27
28
  ServerAddress,
@@ -129,7 +130,7 @@ export interface ControllerCommissionerContext {
129
130
  peers: PeerSet;
130
131
  clients: InteractionClientProvider;
131
132
  scanners: ScannerSet;
132
- netInterfaces: NetInterfaceSet;
133
+ transports: ConnectionlessTransportSet;
133
134
  sessions: SessionManager;
134
135
  exchanges: ExchangeManager;
135
136
  ca: CertificateAuthority;
@@ -152,7 +153,7 @@ export class ControllerCommissioner {
152
153
  peers: env.get(PeerSet),
153
154
  clients: env.get(InteractionClientProvider),
154
155
  scanners: env.get(ScannerSet),
155
- netInterfaces: env.get(NetInterfaceSet),
156
+ transports: env.get(ConnectionlessTransportSet),
156
157
  sessions: env.get(SessionManager),
157
158
  exchanges: env.get(ExchangeManager),
158
159
  ca: env.get(CertificateAuthority),
@@ -213,7 +214,7 @@ export class ControllerCommissioner {
213
214
 
214
215
  if (
215
216
  this.#context.scanners.hasScannerFor(ChannelType.UDP) &&
216
- this.#context.netInterfaces.hasInterfaceFor(ChannelType.UDP, "::") !== undefined
217
+ this.#context.transports.hasInterfaceFor(ChannelType.UDP, "::") !== undefined
217
218
  ) {
218
219
  discoveryCapabilities.onIpNetwork = true; // We always discover on network as defined by specs
219
220
  }
@@ -316,7 +317,7 @@ export class ControllerCommissioner {
316
317
  const { ip } = address;
317
318
 
318
319
  const isIpv6Address = isIPv6(ip);
319
- const paseInterface = this.#context.netInterfaces.interfaceFor(
320
+ const paseInterface = this.#context.transports.interfaceFor(
320
321
  ChannelType.UDP,
321
322
  isIpv6Address ? "::" : "0.0.0.0",
322
323
  );
@@ -327,8 +328,8 @@ export class ControllerCommissioner {
327
328
  );
328
329
  }
329
330
  paseChannel = await paseInterface.openChannel(address);
330
- } else {
331
- const ble = this.#context.netInterfaces.interfaceFor(ChannelType.BLE);
331
+ } else if (address.type === "ble") {
332
+ const ble = this.#context.transports.interfaceFor(ChannelType.BLE);
332
333
  if (!ble) {
333
334
  throw new PairRetransmissionLimitReachedError(
334
335
  `BLE interface not initialized. Cannot use ${address.peripheralAddress} for commissioning.`,
@@ -336,6 +337,8 @@ export class ControllerCommissioner {
336
337
  }
337
338
  // TODO Have a Timeout mechanism here for connections
338
339
  paseChannel = await ble.openChannel(address);
340
+ } else {
341
+ throw new ImplementationError(`Unsupported PASE address type ${address.type}`);
339
342
  }
340
343
 
341
344
  // Do PASE paring
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import { DiscoveryData } from "#common/Scanner.js";
8
- import { ServerAddressIp } from "#general";
8
+ import { ServerAddressUdp } from "#general";
9
9
  import type { PeerDataStore } from "#peer/PeerAddressStore.js";
10
10
  import { SessionParameters } from "#session/Session.js";
11
11
  import { PeerAddress } from "./PeerAddress.js";
@@ -24,7 +24,7 @@ export interface OperationalPeer {
24
24
  /**
25
25
  * A physical address the peer may be accessed at, if known.
26
26
  */
27
- operationalAddress?: ServerAddressIp;
27
+ operationalAddress?: ServerAddressUdp;
28
28
 
29
29
  /**
30
30
  * The peer's session parameters reported during discovery.
@@ -4,6 +4,7 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
+ import { ReadScope } from "#action/client/ReadScope.js";
7
8
  import { Construction, MaybePromise } from "#general";
8
9
  import { DecodedAttributeReportValue } from "#interaction/AttributeDataDecoder.js";
9
10
  import { AttributeId, ClusterId, EndpointNumber, EventNumber } from "#types";
@@ -27,7 +28,7 @@ export abstract class PeerDataStore {
27
28
  abstract maxEventNumber: EventNumber;
28
29
  abstract updateLastEventNumber(eventNumber: EventNumber): MaybePromise<void>;
29
30
 
30
- abstract persistAttributes(attributes: DecodedAttributeReportValue<any>[]): MaybePromise<void>;
31
+ abstract persistAttributes(attributes: DecodedAttributeReportValue<any>[], scope: ReadScope): MaybePromise<void>;
31
32
 
32
33
  // TODO: Find a maybe better way to achieve this without functions
33
34
  abstract retrieveAttribute(
@@ -41,4 +42,5 @@ export abstract class PeerDataStore {
41
42
  filterEndpointId?: EndpointNumber,
42
43
  filterClusterId?: ClusterId,
43
44
  ): { endpointId: EndpointNumber; clusterId: ClusterId; dataVersion: number }[];
45
+ abstract cleanupAttributeData(endpointId: EndpointNumber, clusterIds?: ClusterId[]): MaybePromise<void>;
44
46
  }
@@ -10,6 +10,7 @@ import {
10
10
  AsyncObservable,
11
11
  BasicSet,
12
12
  ChannelType,
13
+ ConnectionlessTransportSet,
13
14
  Construction,
14
15
  createPromise,
15
16
  Duration,
@@ -22,12 +23,11 @@ import {
22
23
  Logger,
23
24
  MatterError,
24
25
  Minutes,
25
- NetInterfaceSet,
26
26
  NoResponseTimeoutError,
27
27
  ObservableSet,
28
28
  Seconds,
29
29
  ServerAddress,
30
- ServerAddressIp,
30
+ ServerAddressUdp,
31
31
  STANDARD_MATTER_PORT,
32
32
  Time,
33
33
  Timer,
@@ -109,7 +109,7 @@ export interface PeerSetContext {
109
109
  exchanges: ExchangeManager;
110
110
  subscriptionClient: SubscriptionClient;
111
111
  scanners: ScannerSet;
112
- netInterfaces: NetInterfaceSet;
112
+ transports: ConnectionlessTransportSet;
113
113
  store: PeerAddressStore;
114
114
  }
115
115
 
@@ -122,7 +122,7 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
122
122
  readonly #exchanges: ExchangeManager;
123
123
  readonly #subscriptionClient: SubscriptionClient;
124
124
  readonly #scanners: ScannerSet;
125
- readonly #netInterfaces: NetInterfaceSet;
125
+ readonly #transports: ConnectionlessTransportSet;
126
126
  readonly #caseClient: CaseClient;
127
127
  readonly #peers = new BasicSet<OperationalPeer>();
128
128
  readonly #peersByAddress = new PeerAddressMap<OperationalPeer>();
@@ -138,14 +138,22 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
138
138
  readonly #disconnected = AsyncObservable<[address: PeerAddress]>();
139
139
 
140
140
  constructor(context: PeerSetContext) {
141
- const { sessions, channels, exchanges, subscriptionClient, scanners, netInterfaces, store } = context;
141
+ const {
142
+ sessions,
143
+ channels,
144
+ exchanges,
145
+ subscriptionClient,
146
+ scanners,
147
+ transports: netInterfaces,
148
+ store,
149
+ } = context;
142
150
 
143
151
  this.#sessions = sessions;
144
152
  this.#channels = channels;
145
153
  this.#exchanges = exchanges;
146
154
  this.#subscriptionClient = subscriptionClient;
147
155
  this.#scanners = scanners;
148
- this.#netInterfaces = netInterfaces;
156
+ this.#transports = netInterfaces;
149
157
  this.#store = store;
150
158
  this.#caseClient = new CaseClient(this.#sessions);
151
159
 
@@ -231,7 +239,7 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
231
239
  exchanges: env.get(ExchangeManager),
232
240
  subscriptionClient: env.get(SubscriptionClient),
233
241
  scanners: env.get(ScannerSet),
234
- netInterfaces: env.get(NetInterfaceSet),
242
+ transports: env.get(ConnectionlessTransportSet),
235
243
  store: env.get(PeerAddressStore),
236
244
  });
237
245
  env.set(PeerSet, instance);
@@ -257,7 +265,7 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
257
265
  address: PeerAddress,
258
266
  options: PeerConnectionOptions & {
259
267
  allowUnknownPeer?: boolean;
260
- operationalAddress?: ServerAddressIp;
268
+ operationalAddress?: ServerAddressUdp;
261
269
  },
262
270
  ) {
263
271
  address = PeerAddress(address);
@@ -422,7 +430,7 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
422
430
  * device is discovered again using its operational instance details.
423
431
  * It returns the operational MessageChannel on success.
424
432
  */
425
- async #resume(address: PeerAddress, options: PeerConnectionOptions, tryOperationalAddress?: ServerAddressIp) {
433
+ async #resume(address: PeerAddress, options: PeerConnectionOptions, tryOperationalAddress?: ServerAddressUdp) {
426
434
  const { discoveryOptions: { discoveryType } = {} } = options;
427
435
 
428
436
  const operationalAddress =
@@ -449,7 +457,7 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
449
457
 
450
458
  async #connectOrDiscoverNode(
451
459
  address: PeerAddress,
452
- operationalAddress?: ServerAddressIp,
460
+ operationalAddress?: ServerAddressUdp,
453
461
  options?: PeerConnectionOptions,
454
462
  ) {
455
463
  address = PeerAddress(address);
@@ -630,7 +638,7 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
630
638
 
631
639
  async #reconnectKnownAddress(
632
640
  address: PeerAddress,
633
- operationalAddress: ServerAddressIp,
641
+ operationalAddress: ServerAddressUdp,
634
642
  discoveryData?: DiscoveryData,
635
643
  options?: CaseClient.PairOptions,
636
644
  ): Promise<MessageChannel | undefined> {
@@ -670,7 +678,7 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
670
678
  GroupId.assertGroupId(groupId);
671
679
  const multicastAddress = this.#sessions.fabricFor(address).groups.multicastAddressFor(groupId);
672
680
 
673
- const operationalInterface = this.#netInterfaces.interfaceFor(ChannelType.UDP, multicastAddress);
681
+ const operationalInterface = this.#transports.interfaceFor(ChannelType.UDP, multicastAddress);
674
682
  if (operationalInterface === undefined) {
675
683
  throw new PairRetransmissionLimitReachedError(`IPv6 interface not initialized`);
676
684
  }
@@ -689,7 +697,7 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
689
697
  /** Pair with an operational device (already commissioned) and establish a CASE session. */
690
698
  async #pair(
691
699
  address: PeerAddress,
692
- operationalServerAddress: ServerAddressIp,
700
+ operationalServerAddress: ServerAddressUdp,
693
701
  discoveryData?: DiscoveryData,
694
702
  options?: CaseClient.PairOptions,
695
703
  ) {
@@ -697,10 +705,7 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
697
705
  const { ip, port } = operationalServerAddress;
698
706
  // Do CASE pairing
699
707
  const isIpv6Address = isIPv6(ip);
700
- const operationalInterface = this.#netInterfaces.interfaceFor(
701
- ChannelType.UDP,
702
- isIpv6Address ? "::" : "0.0.0.0",
703
- );
708
+ const operationalInterface = this.#transports.interfaceFor(ChannelType.UDP, isIpv6Address ? "::" : "0.0.0.0");
704
709
 
705
710
  if (operationalInterface === undefined) {
706
711
  throw new PairRetransmissionLimitReachedError(
@@ -812,7 +817,7 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
812
817
 
813
818
  async #addOrUpdatePeer(
814
819
  address: PeerAddress,
815
- operationalServerAddress: ServerAddressIp,
820
+ operationalServerAddress: ServerAddressUdp,
816
821
  discoveryData?: DiscoveryData,
817
822
  ) {
818
823
  let peer = this.#peersByAddress.get(address);
@@ -8,6 +8,8 @@ import { DecodedMessage, MessageCodec, SessionType } from "#codec/MessageCodec.j
8
8
  import {
9
9
  Bytes,
10
10
  Channel,
11
+ ConnectionlessTransport,
12
+ ConnectionlessTransportSet,
11
13
  Crypto,
12
14
  Diagnostic,
13
15
  Environment,
@@ -18,8 +20,6 @@ import {
18
20
  MatterError,
19
21
  MatterFlowError,
20
22
  ObserverGroup,
21
- TransportInterface,
22
- TransportInterfaceSet,
23
23
  UdpInterface,
24
24
  UnexpectedDataError,
25
25
  } from "#general";
@@ -49,35 +49,35 @@ const MAXIMUM_CONCURRENT_EXCHANGES_PER_SESSION = 5;
49
49
  */
50
50
  export interface ExchangeManagerContext {
51
51
  crypto: Crypto;
52
- transportInterfaces: TransportInterfaceSet;
52
+ netInterface: ConnectionlessTransportSet;
53
53
  sessionManager: SessionManager;
54
54
  channelManager: ChannelManager;
55
55
  }
56
56
 
57
57
  export class ExchangeManager {
58
- readonly #transportInterfaces: TransportInterfaceSet;
58
+ readonly #transports: ConnectionlessTransportSet;
59
59
  readonly #sessionManager: SessionManager;
60
60
  readonly #channelManager: ChannelManager;
61
61
  readonly #exchangeCounter: ExchangeCounter;
62
62
  readonly #exchanges = new Map<number, MessageExchange>();
63
63
  readonly #protocols = new Map<number, ProtocolHandler>();
64
- readonly #listeners = new Map<TransportInterface, TransportInterface.Listener>();
64
+ readonly #listeners = new Map<ConnectionlessTransport, ConnectionlessTransport.Listener>();
65
65
  readonly #closers = new Set<Promise<void>>();
66
66
  readonly #observers = new ObserverGroup(this);
67
67
  #closing = false;
68
68
 
69
69
  constructor(context: ExchangeManagerContext) {
70
- this.#transportInterfaces = context.transportInterfaces;
70
+ this.#transports = context.netInterface;
71
71
  this.#sessionManager = context.sessionManager;
72
72
  this.#channelManager = context.channelManager;
73
73
  this.#exchangeCounter = new ExchangeCounter(context.crypto);
74
74
 
75
- for (const transportInterface of this.#transportInterfaces) {
76
- this.#addListener(transportInterface);
75
+ for (const netInterface of this.#transports) {
76
+ this.#addListener(netInterface);
77
77
  }
78
78
 
79
- this.#observers.on(this.#transportInterfaces.added, this.#addListener);
80
- this.#observers.on(this.#transportInterfaces.deleted, this.#deleteListener);
79
+ this.#observers.on(this.#transports.added, this.#addListener);
80
+ this.#observers.on(this.#transports.deleted, this.#deleteListener);
81
81
 
82
82
  this.#observers.on(this.#sessionManager.sessions.deleted, session => {
83
83
  if (!session.closingAfterExchangeFinished) {
@@ -90,7 +90,7 @@ export class ExchangeManager {
90
90
  static [Environmental.create](env: Environment) {
91
91
  const instance = new ExchangeManager({
92
92
  crypto: env.get(Crypto),
93
- transportInterfaces: env.get(TransportInterfaceSet),
93
+ netInterface: env.get(ConnectionlessTransportSet),
94
94
  sessionManager: env.get(SessionManager),
95
95
  channelManager: env.get(ChannelManager),
96
96
  });
@@ -261,7 +261,11 @@ export class ExchangeManager {
261
261
 
262
262
  // Having a "Secure Session" means it is encrypted in our internal working
263
263
  // TODO When adding Group sessions, we need to check how to adjust that handling
264
- if (protocolHandler !== undefined && protocolHandler.requiresSecureSession !== session.isSecure) {
264
+ if (
265
+ protocolHandler !== undefined &&
266
+ protocolHandler.requiresSecureSession !== session.isSecure &&
267
+ !isStandaloneAck
268
+ ) {
265
269
  logger.debug(
266
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.`,
267
271
  );
@@ -313,7 +317,7 @@ export class ExchangeManager {
313
317
  );
314
318
  }
315
319
  return;
316
- } else {
320
+ } else if (!isStandaloneAck) {
317
321
  logger.info(
318
322
  `Discarding unexpected message ${messageId} for protocol ${
319
323
  message.payloadHeader.protocolId
@@ -416,7 +420,11 @@ export class ExchangeManager {
416
420
  channel: MessageChannel,
417
421
  expectedProcessingTime = DEFAULT_EXPECTED_PROCESSING_TIME,
418
422
  ) {
419
- return channel.calculateMaximumPeerResponseTime(this.#sessionManager.sessionParameters, expectedProcessingTime);
423
+ return channel.calculateMaximumPeerResponseTime(
424
+ channel.session.parameters,
425
+ this.#sessionManager.sessionParameters,
426
+ expectedProcessingTime,
427
+ );
420
428
  }
421
429
 
422
430
  #messageExchangeContextFor(channel: MessageChannel): MessageExchangeContext {
@@ -427,11 +435,11 @@ export class ExchangeManager {
427
435
  };
428
436
  }
429
437
 
430
- #addListener(transportInterface: TransportInterface) {
431
- const udpInterface = transportInterface instanceof UdpInterface;
438
+ #addListener(netInterface: ConnectionlessTransport) {
439
+ const udpInterface = netInterface instanceof UdpInterface;
432
440
  this.#listeners.set(
433
- transportInterface,
434
- transportInterface.onData((socket, data) => {
441
+ netInterface,
442
+ netInterface.onData((socket, data) => {
435
443
  if (udpInterface && data.byteLength > socket.maxPayloadSize) {
436
444
  logger.warn(
437
445
  `Ignoring UDP message on channel ${socket.name} with size ${data.byteLength} from ${socket.name}, which is larger than the maximum allowed size of ${socket.maxPayloadSize}.`,
@@ -456,12 +464,12 @@ export class ExchangeManager {
456
464
  );
457
465
  }
458
466
 
459
- #deleteListener(transportInterface: TransportInterface) {
460
- const listener = this.#listeners.get(transportInterface);
467
+ #deleteListener(netInterface: ConnectionlessTransport) {
468
+ const listener = this.#listeners.get(netInterface);
461
469
  if (listener === undefined) {
462
470
  return;
463
471
  }
464
- this.#listeners.delete(transportInterface);
472
+ this.#listeners.delete(netInterface);
465
473
 
466
474
  const closer = listener
467
475
  .close()
@@ -114,7 +114,8 @@ export class MessageChannel implements Channel<Message> {
114
114
  }
115
115
 
116
116
  calculateMaximumPeerResponseTime(
117
- sessionParameters: SessionParameters,
117
+ peerSessionParameters: SessionParameters,
118
+ localSessionParameters: SessionParameters,
118
119
  expectedProcessingTime = DEFAULT_EXPECTED_PROCESSING_TIME,
119
120
  ): Duration {
120
121
  switch (this.channel.type) {
@@ -127,8 +128,11 @@ export class MessageChannel implements Channel<Message> {
127
128
  if (!this.usesMrp) {
128
129
  throw new MatterFlowError("No response expected for this message exchange because UDP and no MRP.");
129
130
  }
131
+ // Calculate the maximum time till the peer got our last retry and worst case for the way back
130
132
  return Millis(
131
- this.#calculateMrpMaximumPeerResponseTime(sessionParameters, expectedProcessingTime) +
133
+ this.#calculateMrpMaximumPeerResponseTime(peerSessionParameters) +
134
+ this.#calculateMrpMaximumPeerResponseTime(localSessionParameters) +
135
+ expectedProcessingTime +
132
136
  PEER_RESPONSE_TIME_BUFFER,
133
137
  );
134
138
 
@@ -153,8 +157,10 @@ export class MessageChannel implements Channel<Message> {
153
157
  */
154
158
  getMrpResubmissionBackOffTime(retransmissionCount: number, sessionParameters?: SessionParameters) {
155
159
  const { activeInterval, idleInterval } = sessionParameters ?? this.session.parameters;
156
- const baseInterval =
157
- sessionParameters !== undefined || this.session.isPeerActive() ? activeInterval : idleInterval;
160
+ // For the first message of a new exchange ... SHALL be set according to the idle state of the peer node.
161
+ // For all subsequent messages of the exchange, ... SHOULD be set according to the active state of the peer node
162
+ const peerActive = retransmissionCount > 0 && (sessionParameters !== undefined || this.session.isPeerActive());
163
+ const baseInterval = peerActive ? activeInterval : idleInterval;
158
164
  return Millis.floor(
159
165
  Millis(
160
166
  baseInterval *
@@ -165,19 +171,15 @@ export class MessageChannel implements Channel<Message> {
165
171
  );
166
172
  }
167
173
 
168
- #calculateMrpMaximumPeerResponseTime(
169
- sessionParameters: SessionParameters,
170
- expectedProcessingTime = DEFAULT_EXPECTED_PROCESSING_TIME,
171
- ) {
172
- // We use the expected processing time and deduct the time we already waited since last resubmission
173
- let finalWaitTime = expectedProcessingTime;
174
+ /** Calculates the maximum time the peer might take to respond when using MRP for one direction. */
175
+ #calculateMrpMaximumPeerResponseTime(sessionParameters: SessionParameters) {
176
+ let finalWaitTime = 0;
174
177
 
175
178
  // and then add the time the other side needs for a full resubmission cycle under the assumption we are active
176
179
  for (let i = 0; i < MRP.MAX_TRANSMISSIONS; i++) {
177
180
  finalWaitTime = Millis(finalWaitTime + this.getMrpResubmissionBackOffTime(i, sessionParameters));
178
181
  }
179
182
 
180
- // TODO: Also add any network latency buffer, for now lets consider it's included in the processing time already
181
183
  return finalWaitTime;
182
184
  }
183
185
  }
@@ -31,7 +31,6 @@ import {
31
31
  } from "#protocol/MessageChannel.js";
32
32
  import { GroupSession } from "#session/GroupSession.js";
33
33
  import { SessionParameters } from "#session/Session.js";
34
- import { SessionIntervals } from "#session/SessionIntervals.js";
35
34
  import {
36
35
  GroupId,
37
36
  NodeId,
@@ -145,9 +144,6 @@ export class MessageExchange {
145
144
  );
146
145
  }
147
146
 
148
- readonly #activeInterval: Duration;
149
- readonly #idleInterval: Duration;
150
- readonly #activeThreshold: Duration;
151
147
  readonly #messagesQueue = new DataReadQueue<Message>();
152
148
  #receivedMessageToAck: Message | undefined;
153
149
  #receivedMessageAckTimer = Time.getTimer("Ack receipt timeout", MRP.STANDALONE_ACK_TIMEOUT, () => {
@@ -196,10 +192,7 @@ export class MessageExchange {
196
192
  this.#exchangeId = exchangeId;
197
193
  this.#protocolId = protocolId;
198
194
 
199
- const { activeInterval, idleInterval, activeThreshold } = SessionIntervals(session.parameters);
200
- this.#activeInterval = activeInterval;
201
- this.#idleInterval = idleInterval;
202
- this.#activeThreshold = activeThreshold;
195
+ const { activeThreshold, activeInterval, idleInterval } = this.session.parameters;
203
196
 
204
197
  this.#used = !isInitiator; // If we are the initiator then exchange was not used yet, so track it
205
198
 
@@ -211,9 +204,9 @@ export class MessageExchange {
211
204
  exId: this.#exchangeId,
212
205
  sess: session.name,
213
206
  peerSess: this.#peerSessionId,
214
- SAT: this.#activeThreshold,
215
- SAI: this.#activeInterval,
216
- SII: this.#idleInterval,
207
+ SAT: Duration.format(activeThreshold),
208
+ SAI: Duration.format(activeInterval),
209
+ SII: Duration.format(idleInterval),
217
210
  maxTrans: MRP.MAX_TRANSMISSIONS,
218
211
  exchangeFlags: Diagnostic.asFlags({
219
212
  MRP: this.channel.usesMrp,
@@ -489,6 +482,7 @@ export class MessageExchange {
489
482
  timeout = Instant; // If we have messages in the queue, we can return them immediately
490
483
  } else {
491
484
  timeout = this.channel.calculateMaximumPeerResponseTime(
485
+ this.session.parameters,
492
486
  this.context.localSessionParameters,
493
487
  options?.expectedProcessingTime,
494
488
  );
@@ -496,13 +490,6 @@ export class MessageExchange {
496
490
  return this.#messagesQueue.read(timeout);
497
491
  }
498
492
 
499
- calculateMaximumPeerResponseTimeMs(expectedProcessingTimeMs = DEFAULT_EXPECTED_PROCESSING_TIME) {
500
- return this.channel.calculateMaximumPeerResponseTime(
501
- this.context.localSessionParameters,
502
- expectedProcessingTimeMs,
503
- );
504
- }
505
-
506
493
  #retransmitMessage(message: Message, expectedProcessingTime?: Duration) {
507
494
  this.#retransmissionCounter++;
508
495
  if (this.#retransmissionCounter >= MRP.MAX_TRANSMISSIONS) {
@@ -512,6 +499,7 @@ export class MessageExchange {
512
499
  // We already have waited after the last message was sent, so deduct this time from the final wait time
513
500
  const finalWaitTime = Millis(
514
501
  this.channel.calculateMaximumPeerResponseTime(
502
+ this.session.parameters,
515
503
  this.context.localSessionParameters,
516
504
  expectedProcessingTime,
517
505
  ) - (this.#retransmissionTimer?.interval ?? Instant),
@@ -13,6 +13,7 @@ import {
13
13
  CRYPTO_SYMMETRIC_KEY_LENGTH,
14
14
  Crypto,
15
15
  Diagnostic,
16
+ Duration,
16
17
  Logger,
17
18
  MatterError,
18
19
  MatterFlowError,
@@ -176,9 +177,9 @@ export class NodeSession extends SecureSession {
176
177
  get parameterDiagnostics() {
177
178
  return Diagnostic.dict(
178
179
  {
179
- SII: this.idleInterval,
180
- SAI: this.activeInterval,
181
- SAT: this.activeThreshold,
180
+ SII: Duration.format(this.idleInterval),
181
+ SAI: Duration.format(this.activeInterval),
182
+ SAT: Duration.format(this.activeThreshold),
182
183
  DMRev: this.dataModelRevision,
183
184
  IMRev: this.interactionModelRevision,
184
185
  spec: Diagnostic.hex(this.specificationVersion),
@@ -49,7 +49,7 @@ export function SessionIntervals(intervals?: Partial<SessionIntervals>): Session
49
49
  }
50
50
 
51
51
  export namespace SessionIntervals {
52
- export const defaults = {
52
+ export const defaults: SessionIntervals = {
53
53
  idleInterval: Millis(500),
54
54
  activeInterval: Millis(300),
55
55
  activeThreshold: Seconds(4),