@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
@@ -7,6 +7,7 @@
7
7
  import { DiscoveryData, ScannerSet } from "#common/Scanner.js";
8
8
  import {
9
9
  anyPromise,
10
+ AsyncObservable,
10
11
  BasicSet,
11
12
  ChannelType,
12
13
  Construction,
@@ -22,13 +23,12 @@ import {
22
23
  NetInterfaceSet,
23
24
  NoResponseTimeoutError,
24
25
  ObservableSet,
25
- PromiseQueue,
26
26
  ServerAddressIp,
27
27
  serverAddressToString,
28
28
  Time,
29
29
  Timer,
30
30
  } from "#general";
31
- import { InteractionClient } from "#interaction/InteractionClient.js";
31
+ import { SubscriptionClient } from "#interaction/SubscriptionClient.js";
32
32
  import { MdnsScanner } from "#mdns/MdnsScanner.js";
33
33
  import { PeerAddress, PeerAddressMap } from "#peer/PeerAddress.js";
34
34
  import { CaseClient, SecureSession, Session } from "#session/index.js";
@@ -39,6 +39,7 @@ import { ChannelNotConnectedError, ExchangeManager, MessageChannel } from "../pr
39
39
  import { ReconnectableExchangeProvider } from "../protocol/ExchangeProvider.js";
40
40
  import { RetransmissionLimitReachedError } from "../protocol/MessageExchange.js";
41
41
  import { ControllerDiscovery, DiscoveryError, PairRetransmissionLimitReachedError } from "./ControllerDiscovery.js";
42
+ import { InteractionQueue } from "./InteractionQueue.js";
42
43
  import { OperationalPeer } from "./OperationalPeer.js";
43
44
  import { PeerAddressStore, PeerDataStore } from "./PeerAddressStore.js";
44
45
 
@@ -47,9 +48,6 @@ const logger = Logger.get("PeerSet");
47
48
  const RECONNECTION_POLLING_INTERVAL_MS = 600_000; // 10 minutes
48
49
  const RETRANSMISSION_DISCOVERY_TIMEOUT_S = 5;
49
50
 
50
- const CONCURRENT_QUEUED_INTERACTIONS = 4;
51
- const INTERACTION_QUEUE_DELAY_MS = 100;
52
-
53
51
  /**
54
52
  * Types of discovery that may be performed when connecting operationally.
55
53
  */
@@ -93,6 +91,7 @@ export interface PeerSetContext {
93
91
  sessions: SessionManager;
94
92
  channels: ChannelManager;
95
93
  exchanges: ExchangeManager;
94
+ subscriptionClient: SubscriptionClient;
96
95
  scanners: ScannerSet;
97
96
  netInterfaces: NetInterfaceSet;
98
97
  store: PeerAddressStore;
@@ -105,6 +104,7 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
105
104
  readonly #sessions: SessionManager;
106
105
  readonly #channels: ChannelManager;
107
106
  readonly #exchanges: ExchangeManager;
107
+ readonly #subscriptionClient: SubscriptionClient;
108
108
  readonly #scanners: ScannerSet;
109
109
  readonly #netInterfaces: NetInterfaceSet;
110
110
  readonly #caseClient: CaseClient;
@@ -117,16 +117,17 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
117
117
  }>();
118
118
  readonly #construction: Construction<PeerSet>;
119
119
  readonly #store: PeerAddressStore;
120
- readonly #interactionQueue = new PromiseQueue(CONCURRENT_QUEUED_INTERACTIONS, INTERACTION_QUEUE_DELAY_MS);
120
+ readonly #interactionQueue = new InteractionQueue();
121
121
  readonly #nodeCachedData = new PeerAddressMap<PeerDataStore>(); // Temporarily until we store it in new API
122
- readonly #clients = new PeerAddressMap<InteractionClient>();
122
+ readonly #disconnected = AsyncObservable<[address: PeerAddress]>();
123
123
 
124
124
  constructor(context: PeerSetContext) {
125
- const { sessions, channels, exchanges, scanners, netInterfaces, store } = context;
125
+ const { sessions, channels, exchanges, subscriptionClient, scanners, netInterfaces, store } = context;
126
126
 
127
127
  this.#sessions = sessions;
128
128
  this.#channels = channels;
129
129
  this.#exchanges = exchanges;
130
+ this.#subscriptionClient = subscriptionClient;
130
131
  this.#scanners = scanners;
131
132
  this.#netInterfaces = netInterfaces;
132
133
  this.#store = store;
@@ -143,6 +144,7 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
143
144
 
144
145
  this.#sessions.resubmissionStarted.on(this.#handleResubmissionStarted.bind(this));
145
146
 
147
+ /** A channel was added by ourselves */
146
148
  this.#channels.added.on((address, msgChannel) => {
147
149
  if (isIpNetworkChannel(msgChannel.channel)) {
148
150
  // Update the channel address if it has one
@@ -165,6 +167,10 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
165
167
  return this.#peers.deleted;
166
168
  }
167
169
 
170
+ get disconnected() {
171
+ return this.#disconnected;
172
+ }
173
+
168
174
  has(item: PeerAddress | OperationalPeer) {
169
175
  if ("address" in item) {
170
176
  return this.#peers.has(item);
@@ -201,6 +207,7 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
201
207
  sessions: env.get(SessionManager),
202
208
  channels: env.get(ChannelManager),
203
209
  exchanges: env.get(ExchangeManager),
210
+ subscriptionClient: env.get(SubscriptionClient),
204
211
  scanners: env.get(ScannerSet),
205
212
  netInterfaces: env.get(NetInterfaceSet),
206
213
  store: env.get(PeerAddressStore),
@@ -213,20 +220,26 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
213
220
  return this.#peers;
214
221
  }
215
222
 
216
- /**
217
- * Connect to a node on a fabric.
218
- */
219
- async connect(
220
- address: PeerAddress,
221
- discoveryOptions: DiscoveryOptions,
222
- allowUnknownPeer = false,
223
- ): Promise<InteractionClient> {
224
- await this.#ensureConnection(address, discoveryOptions, allowUnknownPeer);
223
+ get subscriptionClient() {
224
+ return this.#subscriptionClient;
225
+ }
225
226
 
226
- return this.initializeInteractionClient(address, discoveryOptions);
227
+ get interactionQueue() {
228
+ return this.#interactionQueue;
227
229
  }
228
230
 
229
- async #ensureConnection(address: PeerAddress, discoveryOptions: DiscoveryOptions, allowUnknownPeer = false) {
231
+ /**
232
+ * Ensure there is a channel to the designated peer.
233
+ */
234
+ async ensureConnection(
235
+ address: PeerAddress,
236
+ options: {
237
+ discoveryOptions: DiscoveryOptions;
238
+ allowUnknownPeer?: boolean;
239
+ operationalAddress?: ServerAddressIp;
240
+ },
241
+ ) {
242
+ const { discoveryOptions, allowUnknownPeer, operationalAddress } = options;
230
243
  if (!this.#peersByAddress.has(address) && !allowUnknownPeer) {
231
244
  throw new UnknownNodeError(`Cannot connect to unknown device ${PeerAddress(address)}`);
232
245
  }
@@ -242,7 +255,7 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
242
255
  const { promise, resolver, rejecter } = createPromise<MessageChannel>();
243
256
  this.#runningPeerReconnections.set(address, { promise, rejecter });
244
257
 
245
- this.#resume(address, discoveryOptions)
258
+ this.#resume(address, discoveryOptions, operationalAddress)
246
259
  .then(channel => {
247
260
  this.#runningPeerReconnections.delete(address);
248
261
  resolver(channel);
@@ -256,62 +269,46 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
256
269
  }
257
270
  }
258
271
 
259
- async initializeInteractionClient(address: PeerAddress, discoveryOptions?: DiscoveryOptions) {
260
- const existingClient = this.#clients.get(address);
261
- if (existingClient !== undefined) {
262
- return existingClient;
263
- }
264
-
265
- const nodeStore = this.get(address)?.dataStore;
266
- await nodeStore?.construction; // Lazy initialize the data if not already done
267
-
272
+ /**
273
+ * Obtain an exchange provider for the designated peer.
274
+ */
275
+ async exchangeProviderFor(address: PeerAddress, discoveryOptions?: DiscoveryOptions) {
268
276
  let initiallyConnected = this.#channels.hasChannel(address);
269
- const client = new InteractionClient(
270
- new ReconnectableExchangeProvider(this.#exchanges, this.#channels, address, async () => {
271
- if (!initiallyConnected && !this.#channels.hasChannel(address)) {
272
- // We got an uninitialized node, so do the first connection as usual
273
- await this.#ensureConnection(address, { discoveryType: NodeDiscoveryType.None });
274
- initiallyConnected = true; // We only do this connection once, rest is handled in following code
275
- if (this.#channels.hasChannel(address)) {
276
- return;
277
- }
277
+ return new ReconnectableExchangeProvider(this.#exchanges, this.#channels, address, async () => {
278
+ if (!initiallyConnected && !this.#channels.hasChannel(address)) {
279
+ // We got an uninitialized node, so do the first connection as usual
280
+ await this.ensureConnection(address, {
281
+ discoveryOptions: { discoveryType: NodeDiscoveryType.None },
282
+ });
283
+ initiallyConnected = true; // We only do this connection once, rest is handled in following code
284
+ if (this.#channels.hasChannel(address)) {
285
+ return;
278
286
  }
287
+ }
279
288
 
280
- if (!this.#channels.hasChannel(address)) {
281
- throw new RetransmissionLimitReachedError(
282
- `Device ${PeerAddress(address)} is currently not reachable.`,
283
- );
284
- }
285
- await this.#channels.removeAllNodeChannels(address);
289
+ if (!this.#channels.hasChannel(address)) {
290
+ throw new RetransmissionLimitReachedError(`Device ${PeerAddress(address)} is currently not reachable.`);
291
+ }
292
+ await this.#channels.removeAllNodeChannels(address);
286
293
 
287
- // Enrich discoveryData with data from the node store when not provided
288
- const { discoveryData } = discoveryOptions ?? {
289
- discoveryData: this.#peersByAddress.get(address)?.discoveryData,
290
- };
291
- // Try to use first result for one last try before we need to reconnect
292
- const operationalAddress = this.#knownOperationalAddressFor(address, true);
293
- if (operationalAddress === undefined) {
294
- logger.info(
295
- `Re-discovering device failed (no address found), remove all sessions for ${PeerAddress(address)}`,
296
- );
297
- // We remove all sessions, this also informs the PairedNode class
298
- await this.#sessions.removeAllSessionsForNode(address);
299
- throw new RetransmissionLimitReachedError(
300
- `No operational address found for ${PeerAddress(address)}`,
301
- );
302
- }
303
- if (
304
- (await this.#reconnectKnownAddress(address, operationalAddress, discoveryData, 2_000)) === undefined
305
- ) {
306
- throw new RetransmissionLimitReachedError(`${PeerAddress(address)} is not reachable.`);
307
- }
308
- }),
309
- address,
310
- this.#interactionQueue,
311
- nodeStore,
312
- );
313
- this.#clients.set(address, client);
314
- return client;
294
+ // Enrich discoveryData with data from the node store when not provided
295
+ const { discoveryData } = discoveryOptions ?? {
296
+ discoveryData: this.#peersByAddress.get(address)?.discoveryData,
297
+ };
298
+ // Try to use first result for one last try before we need to reconnect
299
+ const operationalAddress = this.#knownOperationalAddressFor(address, true);
300
+ if (operationalAddress === undefined) {
301
+ logger.info(
302
+ `Re-discovering device failed (no address found), remove all sessions for ${PeerAddress(address)}`,
303
+ );
304
+ // We remove all sessions, this also informs the PairedNode class
305
+ await this.#sessions.removeAllSessionsForNode(address);
306
+ throw new RetransmissionLimitReachedError(`No operational address found for ${PeerAddress(address)}`);
307
+ }
308
+ if ((await this.#reconnectKnownAddress(address, operationalAddress, discoveryData, 2_000)) === undefined) {
309
+ throw new RetransmissionLimitReachedError(`${PeerAddress(address)} is not reachable.`);
310
+ }
311
+ });
315
312
  }
316
313
 
317
314
  /**
@@ -327,10 +324,15 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
327
324
  /**
328
325
  * Terminate any active peer connection.
329
326
  */
330
- async disconnect(address: PeerAddress, sendSessionClose = true) {
331
- this.#clients.get(address)?.removeAllSubscriptions();
327
+ async disconnect(peer: PeerAddress | OperationalPeer, sendSessionClose = true) {
328
+ const address = this.get(peer)?.address;
329
+ if (address === undefined) {
330
+ return;
331
+ }
332
+
332
333
  await this.#sessions.removeAllSessionsForNode(address, sendSessionClose);
333
334
  await this.#channels.removeAllNodeChannels(address);
335
+ await this.#disconnected.emit(address);
334
336
  }
335
337
 
336
338
  /**
@@ -348,7 +350,6 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
348
350
  await this.#store.deletePeer(address);
349
351
  await this.disconnect(address, false);
350
352
  await this.#sessions.deleteResumptionRecord(address);
351
- this.#clients.delete(address);
352
353
  }
353
354
 
354
355
  async close() {
@@ -358,8 +359,11 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
358
359
  // This ends discovery without triggering promises
359
360
  mdnsScanner?.cancelOperationalDeviceDiscovery(this.#sessions.fabricFor(address), address.nodeId, false);
360
361
  }
361
- this.#clients.forEach(client => client.close());
362
- this.#clients.clear();
362
+
363
+ for (const { address } of this.#peers) {
364
+ await this.disconnect(address, false);
365
+ }
366
+
363
367
  this.#interactionQueue.close();
364
368
  this.#runningPeerReconnections.forEach(({ rejecter }) =>
365
369
  rejecter(new ChannelNotConnectedError("PeerSet closed")),
@@ -373,18 +377,22 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
373
377
  * device is discovered again using its operational instance details.
374
378
  * It returns the operational MessageChannel on success.
375
379
  */
376
- async #resume(address: PeerAddress, discoveryOptions?: DiscoveryOptions) {
380
+ async #resume(address: PeerAddress, discoveryOptions?: DiscoveryOptions, tryOperationalAddress?: ServerAddressIp) {
377
381
  const { discoveryType } = discoveryOptions ?? {};
382
+
378
383
  const operationalAddress =
379
- discoveryType === NodeDiscoveryType.None
384
+ tryOperationalAddress ??
385
+ (discoveryType === NodeDiscoveryType.None
380
386
  ? this.#getLastOperationalAddress(address)
381
- : this.#knownOperationalAddressFor(address);
387
+ : this.#knownOperationalAddressFor(address));
388
+
382
389
  try {
383
390
  return await this.#connectOrDiscoverNode(address, operationalAddress, discoveryOptions);
384
391
  } catch (error) {
385
392
  if (
386
393
  (error instanceof DiscoveryError || error instanceof NoResponseTimeoutError) &&
387
- this.#peersByAddress.has(address)
394
+ this.#peersByAddress.has(address) &&
395
+ tryOperationalAddress === undefined
388
396
  ) {
389
397
  logger.info(`Resume failed, remove all sessions for ${PeerAddress(address)}`);
390
398
  // We remove all sessions, this also informs the PairedNode class
@@ -439,7 +447,13 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
439
447
  operationalAddress !== undefined &&
440
448
  (runningDiscoveryType === NodeDiscoveryType.None || requestedDiscoveryType === NodeDiscoveryType.None)
441
449
  ) {
442
- const directReconnection = await this.#reconnectKnownAddress(address, operationalAddress, discoveryData);
450
+ const directReconnection = await this.#reconnectKnownAddress(
451
+ address,
452
+ operationalAddress,
453
+ discoveryData,
454
+ // When we use a timeout for discovery also use this for reconnecting to the node
455
+ timeoutSeconds ? timeoutSeconds * 1000 : undefined,
456
+ );
443
457
  if (directReconnection !== undefined) {
444
458
  return directReconnection;
445
459
  }
@@ -574,6 +588,7 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
574
588
  address = PeerAddress(address);
575
589
 
576
590
  const { ip, port } = operationalAddress;
591
+ const startTime = Time.nowMs();
577
592
  try {
578
593
  logger.debug(
579
594
  `Resuming connection to ${PeerAddress(address)} at ${ip}:${port}${
@@ -592,7 +607,7 @@ export class PeerSet implements ImmutableSet<OperationalPeer>, ObservableSet<Ope
592
607
  error,
593
608
  );
594
609
  // We remove all sessions, this also informs the PairedNode class
595
- await this.#sessions.removeAllSessionsForNode(address);
610
+ await this.#sessions.removeAllSessionsForNode(address, false, startTime);
596
611
  return undefined;
597
612
  } else {
598
613
  throw error;
@@ -0,0 +1,80 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { Logger } from "#general";
8
+
9
+ const logger = Logger.get("PhysicalDeviceProperties");
10
+
11
+ const DEFAULT_SUBSCRIPTION_FLOOR_DEFAULT_S = 1;
12
+ const DEFAULT_SUBSCRIPTION_FLOOR_ICD_S = 0;
13
+ const DEFAULT_SUBSCRIPTION_CEILING_WIFI_S = 60;
14
+ const DEFAULT_SUBSCRIPTION_CEILING_THREAD_S = 60;
15
+ const DEFAULT_SUBSCRIPTION_CEILING_THREAD_SLEEPY_S = 180;
16
+ const DEFAULT_SUBSCRIPTION_CEILING_BATTERY_POWERED_S = 600;
17
+
18
+ export interface PhysicalDeviceProperties {
19
+ threadConnected: boolean;
20
+ wifiConnected: boolean;
21
+ ethernetConnected: boolean;
22
+ rootEndpointServerList: number[];
23
+ isBatteryPowered: boolean;
24
+ isIntermittentlyConnected: boolean;
25
+ isThreadSleepyEndDevice: boolean;
26
+ }
27
+
28
+ export namespace PhysicalDeviceProperties {
29
+ export function determineSubscriptionParameters(options?: {
30
+ properties?: PhysicalDeviceProperties;
31
+ description?: string;
32
+ subscribeMinIntervalFloorSeconds?: number;
33
+ subscribeMaxIntervalCeilingSeconds?: number;
34
+ }) {
35
+ const { properties } = options ?? {};
36
+
37
+ let {
38
+ description,
39
+ subscribeMinIntervalFloorSeconds: minIntervalFloorSeconds,
40
+ subscribeMaxIntervalCeilingSeconds: maxIntervalCeilingSeconds,
41
+ } = options ?? {};
42
+
43
+ if (description === undefined) {
44
+ description = "Node";
45
+ }
46
+
47
+ const { isBatteryPowered, isIntermittentlyConnected, threadConnected, isThreadSleepyEndDevice } =
48
+ properties ?? {};
49
+
50
+ if (isIntermittentlyConnected) {
51
+ if (minIntervalFloorSeconds !== undefined && minIntervalFloorSeconds !== DEFAULT_SUBSCRIPTION_FLOOR_ICD_S) {
52
+ logger.info(
53
+ `${description}: Overwriting minIntervalFloorSeconds for intermittently connected device to ${DEFAULT_SUBSCRIPTION_FLOOR_ICD_S}`,
54
+ );
55
+ minIntervalFloorSeconds = DEFAULT_SUBSCRIPTION_FLOOR_ICD_S;
56
+ }
57
+ }
58
+
59
+ const defaultCeiling = isBatteryPowered
60
+ ? DEFAULT_SUBSCRIPTION_CEILING_BATTERY_POWERED_S
61
+ : isThreadSleepyEndDevice
62
+ ? DEFAULT_SUBSCRIPTION_CEILING_THREAD_SLEEPY_S
63
+ : threadConnected
64
+ ? DEFAULT_SUBSCRIPTION_CEILING_THREAD_S
65
+ : DEFAULT_SUBSCRIPTION_CEILING_WIFI_S;
66
+ if (maxIntervalCeilingSeconds === undefined) {
67
+ maxIntervalCeilingSeconds = defaultCeiling;
68
+ }
69
+ if (maxIntervalCeilingSeconds < defaultCeiling) {
70
+ logger.debug(
71
+ `${description}: maxIntervalCeilingSeconds ideally is ${defaultCeiling}s instead of ${maxIntervalCeilingSeconds}s due to device type`,
72
+ );
73
+ }
74
+
75
+ return {
76
+ minIntervalFloorSeconds: minIntervalFloorSeconds ?? DEFAULT_SUBSCRIPTION_FLOOR_DEFAULT_S,
77
+ maxIntervalCeilingSeconds,
78
+ };
79
+ }
80
+ }
package/src/peer/index.ts CHANGED
@@ -11,3 +11,4 @@ export * from "./OperationalPeer.js";
11
11
  export * from "./PeerAddress.js";
12
12
  export * from "./PeerAddressStore.js";
13
13
  export * from "./PeerSet.js";
14
+ export * from "./PhysicalDeviceProperties.js";
@@ -150,6 +150,10 @@ export class ExchangeManager {
150
150
  return instance;
151
151
  }
152
152
 
153
+ get channels() {
154
+ return this.#channelManager;
155
+ }
156
+
153
157
  hasProtocolHandler(protocolId: number) {
154
158
  return this.#protocols.has(protocolId);
155
159
  }
@@ -159,10 +163,10 @@ export class ExchangeManager {
159
163
  }
160
164
 
161
165
  addProtocolHandler(protocol: ProtocolHandler) {
162
- if (this.hasProtocolHandler(protocol.getId())) {
163
- throw new ImplementationError(`Handler for protocol ${protocol.getId()} already registered.`);
166
+ if (this.hasProtocolHandler(protocol.id)) {
167
+ throw new ImplementationError(`Handler for protocol ${protocol.id} already registered.`);
164
168
  }
165
- this.#protocols.set(protocol.getId(), protocol);
169
+ this.#protocols.set(protocol.id, protocol);
166
170
  }
167
171
 
168
172
  initiateExchange(address: PeerAddress, protocolId: number) {
@@ -12,8 +12,13 @@ import { MessageExchange } from "../protocol/MessageExchange.js";
12
12
  import { ProtocolHandler } from "../protocol/ProtocolHandler.js";
13
13
  import { Session } from "../session/Session.js";
14
14
 
15
+ /**
16
+ * Interface for obtaining an exchange with a specific peer.
17
+ */
15
18
  export abstract class ExchangeProvider {
16
- protected constructor(protected readonly exchangeManager: ExchangeManager) {}
19
+ abstract readonly supportsReconnect: boolean;
20
+
21
+ constructor(protected readonly exchangeManager: ExchangeManager) {}
17
22
 
18
23
  hasProtocolHandler(protocolId: number) {
19
24
  return this.exchangeManager.hasProtocolHandler(protocolId);
@@ -33,8 +38,12 @@ export abstract class ExchangeProvider {
33
38
  abstract channelType: ChannelType;
34
39
  }
35
40
 
41
+ /**
42
+ * Manages an exchange over an established channel.
43
+ */
36
44
  export class DedicatedChannelExchangeProvider extends ExchangeProvider {
37
45
  #channel: MessageChannel;
46
+ readonly supportsReconnect = false;
38
47
 
39
48
  constructor(exchangeManager: ExchangeManager, channel: MessageChannel) {
40
49
  super(exchangeManager);
@@ -58,7 +67,11 @@ export class DedicatedChannelExchangeProvider extends ExchangeProvider {
58
67
  }
59
68
  }
60
69
 
70
+ /**
71
+ * Manages peer exchange that will reestablish automatically in the case of communication failure.
72
+ */
61
73
  export class ReconnectableExchangeProvider extends ExchangeProvider {
74
+ readonly supportsReconnect = true;
62
75
  readonly #address: PeerAddress;
63
76
  readonly #reconnectChannelFunc: () => Promise<void>;
64
77
  readonly #channelUpdated = Observable<[void]>();
@@ -8,7 +8,7 @@ import { Message } from "../codec/MessageCodec.js";
8
8
  import { MessageExchange } from "./MessageExchange.js";
9
9
 
10
10
  export interface ProtocolHandler {
11
- getId(): number;
11
+ readonly id: number;
12
12
  onNewExchange(exchange: MessageExchange, message: Message): Promise<void>;
13
13
  close(): Promise<void>;
14
14
  }
@@ -43,6 +43,10 @@ export class SecureChannelMessenger {
43
43
  this.#defaultExpectedProcessingTimeMs = defaultExpectedProcessingTimeMs;
44
44
  }
45
45
 
46
+ get channel() {
47
+ return this.exchange.channel;
48
+ }
49
+
46
50
  async nextMessage(
47
51
  expectedMessageType: number,
48
52
  expectedProcessingTimeMs = this.#defaultExpectedProcessingTimeMs,
@@ -28,9 +28,7 @@ import { TlvSecureChannelStatusMessage } from "./SecureChannelStatusMessageSchem
28
28
  const logger = Logger.get("SecureChannelProtocol");
29
29
 
30
30
  export class StatusReportOnlySecureChannelProtocol implements ProtocolHandler {
31
- getId(): number {
32
- return SECURE_CHANNEL_PROTOCOL_ID;
33
- }
31
+ readonly id = SECURE_CHANNEL_PROTOCOL_ID;
34
32
 
35
33
  async onNewExchange(exchange: MessageExchange, message: Message) {
36
34
  const messageType = message.payloadHeader.messageType;
@@ -292,11 +292,12 @@ export class SecureSession extends Session {
292
292
  return this.#fabric;
293
293
  }
294
294
 
295
- async clearSubscriptions(flushSubscriptions = false) {
295
+ async clearSubscriptions(flushSubscriptions = false, cancelledByPeer = false) {
296
296
  const subscriptions = [...this.#subscriptions]; // get all values because subscriptions will remove themselves when cancelled
297
297
  for (const subscription of subscriptions) {
298
- await subscription.close(flushSubscriptions);
298
+ await subscription.close(flushSubscriptions, cancelledByPeer);
299
299
  }
300
+ return subscriptions.length;
300
301
  }
301
302
 
302
303
  /** Ends a session. Outstanding subscription data will be flushed before the session is destroyed. */
@@ -62,6 +62,7 @@ export abstract class Session {
62
62
  abstract get closingAfterExchangeFinished(): boolean;
63
63
  #manager?: SessionManager;
64
64
  timestamp = Time.nowMs();
65
+ readonly createdAt = Time.nowMs();
65
66
  activeTimestamp = 0;
66
67
  protected readonly idleIntervalMs: number;
67
68
  protected readonly activeIntervalMs: number;
@@ -378,14 +378,16 @@ export class SessionManager {
378
378
  });
379
379
  }
380
380
 
381
- async removeAllSessionsForNode(address: PeerAddress, sendClose = false) {
381
+ async removeAllSessionsForNode(address: PeerAddress, sendClose = false, closeBeforeCreatedTimestamp?: number) {
382
382
  await this.#construction;
383
383
 
384
384
  for (const session of this.#sessions) {
385
385
  if (!session.isSecure) continue;
386
+ if (closeBeforeCreatedTimestamp !== undefined && session.createdAt >= closeBeforeCreatedTimestamp) continue;
386
387
  const secureSession = session;
387
388
  if (secureSession.peerIs(address)) {
388
389
  await secureSession.destroy(sendClose, false);
390
+ this.#sessions.delete(session);
389
391
  }
390
392
  }
391
393
  }
@@ -563,17 +565,12 @@ export class SessionManager {
563
565
  }
564
566
 
565
567
  /** Clears all subscriptions for a given node and returns how many were cleared. */
566
- async clearSubscriptionsForNode(fabricIndex: FabricIndex, nodeId: NodeId, flushSubscriptions?: boolean) {
568
+ async clearSubscriptionsForNode(peerAddress: PeerAddress, flushSubscriptions?: boolean) {
567
569
  let clearedCount = 0;
568
570
  for (const session of this.#sessions) {
569
- if (session.fabric?.fabricIndex !== fabricIndex) {
570
- continue;
571
+ if (PeerAddress.is(session.peerAddress, peerAddress)) {
572
+ clearedCount += await session.clearSubscriptions(flushSubscriptions, true);
571
573
  }
572
- if (session.peerNodeId !== nodeId) {
573
- continue;
574
- }
575
- await session.clearSubscriptions(flushSubscriptions);
576
- clearedCount++;
577
574
  }
578
575
  return clearedCount;
579
576
  }
@@ -30,6 +30,8 @@ import { CaseServerMessenger } from "./CaseMessenger.js";
30
30
  const logger = Logger.get("CaseServer");
31
31
 
32
32
  export class CaseServer implements ProtocolHandler {
33
+ readonly id = SECURE_CHANNEL_PROTOCOL_ID;
34
+
33
35
  #sessions: SessionManager;
34
36
  #fabrics: FabricManager;
35
37
 
@@ -58,10 +60,6 @@ export class CaseServer implements ProtocolHandler {
58
60
  }
59
61
  }
60
62
 
61
- getId(): number {
62
- return SECURE_CHANNEL_PROTOCOL_ID;
63
- }
64
-
65
63
  private async handleSigma1(messenger: CaseServerMessenger) {
66
64
  logger.info(`Received pairing request from ${messenger.getChannelName()}`);
67
65
  // Generate pairing info