@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
@@ -175,41 +175,58 @@ export class ServerSubscription extends Subscription {
175
175
  }
176
176
  >();
177
177
  #sendUpdatesActivated = false;
178
- readonly #maxIntervalMs: number;
179
178
  readonly #sendIntervalMs: number;
180
- private readonly minIntervalFloorMs: number;
181
- private readonly maxIntervalCeilingMs: number;
182
- private readonly peerAddress: PeerAddress;
179
+ readonly #minIntervalFloorMs: number;
180
+ readonly #maxIntervalCeilingMs: number;
181
+ readonly #peerAddress: PeerAddress;
183
182
 
184
- private sendNextUpdateImmediately = false;
185
- private sendUpdateErrorCounter = 0;
186
- private attributeUpdatePromises = new Set<PromiseLike<void>>();
187
- private currentUpdatePromise?: Promise<void>;
183
+ #sendNextUpdateImmediately = false;
184
+ #sendUpdateErrorCounter = 0;
185
+ readonly #attributeUpdatePromises = new Set<PromiseLike<void>>();
186
+ #currentUpdatePromise?: Promise<void>;
188
187
 
189
188
  constructor(options: {
190
189
  id: number;
191
190
  context: ServerSubscriptionContext;
192
191
  criteria: SubscriptionCriteria;
193
- minIntervalFloor: number;
194
- maxIntervalCeiling: number;
192
+ minIntervalFloorSeconds: number;
193
+ maxIntervalCeilingSeconds: number;
195
194
  subscriptionOptions: ServerSubscriptionConfig;
195
+ useAsMaxInterval?: number;
196
+ useAsSendInterval?: number;
196
197
  }) {
197
- const { id, context, criteria, minIntervalFloor, maxIntervalCeiling, subscriptionOptions } = options;
198
+ const {
199
+ id,
200
+ context,
201
+ criteria,
202
+ minIntervalFloorSeconds,
203
+ maxIntervalCeilingSeconds,
204
+ subscriptionOptions,
205
+ useAsMaxInterval,
206
+ useAsSendInterval,
207
+ } = options;
198
208
 
199
209
  super(context.session, id, criteria);
200
210
  this.#context = context;
201
211
  this.#structure = context.structure;
202
212
 
203
- this.peerAddress = this.session.peerAddress;
204
- this.minIntervalFloorMs = minIntervalFloor * 1000;
205
- this.maxIntervalCeilingMs = maxIntervalCeiling * 1000;
206
-
207
- const { maxInterval, sendInterval } = this.determineSendingIntervals(
208
- subscriptionOptions.minIntervalSeconds * 1000,
209
- subscriptionOptions.maxIntervalSeconds * 1000,
210
- subscriptionOptions.randomizationWindowSeconds * 1000,
211
- );
212
- this.#maxIntervalMs = maxInterval;
213
+ this.#peerAddress = this.session.peerAddress;
214
+ this.#minIntervalFloorMs = minIntervalFloorSeconds * 1000;
215
+ this.#maxIntervalCeilingMs = maxIntervalCeilingSeconds * 1000;
216
+
217
+ let maxInterval: number;
218
+ let sendInterval: number;
219
+ if (useAsMaxInterval !== undefined && useAsSendInterval !== undefined) {
220
+ maxInterval = useAsMaxInterval * 1000;
221
+ sendInterval = useAsSendInterval * 1000;
222
+ } else {
223
+ ({ maxInterval, sendInterval } = this.#determineSendingIntervals(
224
+ subscriptionOptions.minIntervalSeconds * 1000,
225
+ subscriptionOptions.maxIntervalSeconds * 1000,
226
+ subscriptionOptions.randomizationWindowSeconds * 1000,
227
+ ));
228
+ }
229
+ this.maxIntervalMs = maxInterval;
213
230
  this.#sendIntervalMs = sendInterval;
214
231
 
215
232
  this.#updateTimer = Time.getTimer(`Subscription ${this.id} update`, this.#sendIntervalMs, () =>
@@ -217,7 +234,7 @@ export class ServerSubscription extends Subscription {
217
234
  ); // will be started later
218
235
  }
219
236
 
220
- private determineSendingIntervals(
237
+ #determineSendingIntervals(
221
238
  subscriptionMinIntervalMs: number,
222
239
  subscriptionMaxIntervalMs: number,
223
240
  subscriptionRandomizationWindowMs: number,
@@ -230,7 +247,7 @@ export class ServerSubscription extends Subscription {
230
247
  const maxInterval = Math.min(
231
248
  Math.max(
232
249
  subscriptionMinIntervalMs,
233
- Math.max(this.minIntervalFloorMs, Math.min(subscriptionMaxIntervalMs, this.maxIntervalCeilingMs)),
250
+ Math.max(this.#minIntervalFloorMs, Math.min(subscriptionMaxIntervalMs, this.#maxIntervalCeilingMs)),
234
251
  ) + Math.floor(subscriptionRandomizationWindowMs * Math.random()),
235
252
  MAX_INTERVAL_PUBLISHER_LIMIT_S * 1000,
236
253
  );
@@ -239,7 +256,7 @@ export class ServerSubscription extends Subscription {
239
256
  // But if we have no chance of at least one full resubmission process we do like chip-tool.
240
257
  // One full resubmission process takes 33-45 seconds. So 60s means we reach at least first 2 retries of a
241
258
  // second subscription report after first failed.
242
- sendInterval = Math.max(this.minIntervalFloorMs, Math.floor(maxInterval * 0.8));
259
+ sendInterval = Math.max(this.#minIntervalFloorMs, Math.floor(maxInterval * 0.8));
243
260
  }
244
261
  if (sendInterval < subscriptionMinIntervalMs) {
245
262
  // But not faster than once every 2s
@@ -251,7 +268,7 @@ export class ServerSubscription extends Subscription {
251
268
  return { maxInterval, sendInterval };
252
269
  }
253
270
 
254
- private registerNewAttributes() {
271
+ #registerNewAttributes() {
255
272
  const newAttributes = new Array<AttributeWithPath>();
256
273
  const attributeErrors = new Array<TypeFromSchema<typeof TlvAttributeStatus>>();
257
274
  const formerAttributes = new Set<string>(this.#attributeListeners.keys());
@@ -426,7 +443,7 @@ export class ServerSubscription extends Subscription {
426
443
  * controller. The data of newly added events are not sent automatically.
427
444
  */
428
445
  async updateSubscription() {
429
- const { newAttributes } = this.registerNewAttributes();
446
+ const { newAttributes } = this.#registerNewAttributes();
430
447
 
431
448
  for (const { path, attribute } of newAttributes) {
432
449
  const { version, value } = this.#context.readAttribute(path, attribute);
@@ -485,15 +502,21 @@ export class ServerSubscription extends Subscription {
485
502
  this.#prepareDataUpdate();
486
503
  }
487
504
 
488
- get maxInterval(): number {
489
- return Math.ceil(this.#maxIntervalMs / 1000);
490
- }
491
-
492
505
  get sendInterval(): number {
493
506
  return Math.ceil(this.#sendIntervalMs / 1000);
494
507
  }
495
508
 
496
- activateSendingUpdates() {
509
+ get minIntervalFloorSeconds(): number {
510
+ return Math.ceil(this.#minIntervalFloorMs / 1000);
511
+ }
512
+
513
+ get maxIntervalCeilingSeconds(): number {
514
+ return Math.ceil(this.#maxIntervalCeilingMs / 1000);
515
+ }
516
+
517
+ override activate() {
518
+ super.activate();
519
+
497
520
  // We do not need these data anymore, so we can free some memory
498
521
  if (this.criteria.eventFilters !== undefined) this.criteria.eventFilters.length = 0;
499
522
  if (this.criteria.dataVersionFilters !== undefined) this.criteria.dataVersionFilters.length = 0;
@@ -533,11 +556,11 @@ export class ServerSubscription extends Subscription {
533
556
  this.#updateTimer.stop();
534
557
  const now = Time.nowMs();
535
558
  const timeSinceLastUpdateMs = now - this.#lastUpdateTimeMs;
536
- if (timeSinceLastUpdateMs < this.minIntervalFloorMs) {
559
+ if (timeSinceLastUpdateMs < this.#minIntervalFloorMs) {
537
560
  // Respect minimum delay time between updates
538
561
  this.#updateTimer = Time.getTimer(
539
562
  "Subscription update",
540
- this.minIntervalFloorMs - timeSinceLastUpdateMs,
563
+ this.#minIntervalFloorMs - timeSinceLastUpdateMs,
541
564
  () => this.#prepareDataUpdate(),
542
565
  ).start();
543
566
  return;
@@ -550,14 +573,14 @@ export class ServerSubscription extends Subscription {
550
573
  }
551
574
 
552
575
  #triggerSendUpdate() {
553
- if (this.currentUpdatePromise !== undefined) {
576
+ if (this.#currentUpdatePromise !== undefined) {
554
577
  logger.debug("Sending update already in progress, delaying update ...");
555
- this.sendNextUpdateImmediately = true;
578
+ this.#sendNextUpdateImmediately = true;
556
579
  return;
557
580
  }
558
- this.currentUpdatePromise = this.#sendUpdate()
581
+ this.#currentUpdatePromise = this.#sendUpdate()
559
582
  .catch(error => logger.warn("Sending subscription update failed:", error))
560
- .finally(() => (this.currentUpdatePromise = undefined));
583
+ .finally(() => (this.#currentUpdatePromise = undefined));
561
584
  }
562
585
 
563
586
  /**
@@ -593,20 +616,20 @@ export class ServerSubscription extends Subscription {
593
616
  this.#lastUpdateTimeMs = Time.nowMs();
594
617
 
595
618
  try {
596
- await this.sendUpdateMessage(attributeUpdatesToSend, eventUpdatesToSend);
597
- this.sendUpdateErrorCounter = 0;
619
+ await this.#sendUpdateMessage(attributeUpdatesToSend, eventUpdatesToSend);
620
+ this.#sendUpdateErrorCounter = 0;
598
621
  } catch (error) {
599
622
  if (this.isClosed) {
600
623
  // No need to care about resubmissions when the server is closing
601
624
  return;
602
625
  }
603
626
 
604
- this.sendUpdateErrorCounter++;
627
+ this.#sendUpdateErrorCounter++;
605
628
  logger.info(
606
- `Error sending subscription update message (error count=${this.sendUpdateErrorCounter}):`,
629
+ `Error sending subscription update message (error count=${this.#sendUpdateErrorCounter}):`,
607
630
  (error instanceof MatterError && error.message) || error,
608
631
  );
609
- if (this.sendUpdateErrorCounter <= 2) {
632
+ if (this.#sendUpdateErrorCounter <= 2) {
610
633
  // fill the data back in the queue to resend with next try
611
634
  const newAttributeUpdatesToSend = Array.from(this.#outstandingAttributeUpdates.values());
612
635
  this.#outstandingAttributeUpdates.clear();
@@ -622,13 +645,14 @@ export class ServerSubscription extends Subscription {
622
645
  logger.info(
623
646
  `Sending update failed 3 times in a row, canceling subscription ${this.id} and let controller subscribe again.`,
624
647
  );
625
- this.sendNextUpdateImmediately = false;
648
+ this.#sendNextUpdateImmediately = false;
626
649
  if (
627
650
  error instanceof NoResponseTimeoutError ||
628
651
  error instanceof NetworkError ||
629
652
  error instanceof NoChannelError
630
653
  ) {
631
654
  // Let's consider this subscription as dead and wait for a reconnect
655
+ this.isCanceledByPeer = true; // We handle this case like if the controller canceled the subscription
632
656
  await this.destroy();
633
657
  return;
634
658
  } else {
@@ -637,9 +661,9 @@ export class ServerSubscription extends Subscription {
637
661
  }
638
662
  }
639
663
 
640
- if (this.sendNextUpdateImmediately) {
664
+ if (this.#sendNextUpdateImmediately) {
641
665
  logger.debug("Sending delayed update immediately after last one was sent.");
642
- this.sendNextUpdateImmediately = false;
666
+ this.#sendNextUpdateImmediately = false;
643
667
  await this.#sendUpdate(true); // Send but only if non-empty
644
668
  }
645
669
  }
@@ -799,7 +823,7 @@ export class ServerSubscription extends Subscription {
799
823
  async sendInitialReport(messenger: InteractionServerMessenger) {
800
824
  this.#updateTimer.stop();
801
825
 
802
- const { newAttributes, attributeErrors } = this.registerNewAttributes();
826
+ const { newAttributes, attributeErrors } = this.#registerNewAttributes();
803
827
  const { newEvents, eventErrors } = this.#registerNewEvents();
804
828
  const { eventReportsPayload, eventsFiltered } = await this.#collectInitialEventReportPayloads(newEvents);
805
829
 
@@ -822,8 +846,8 @@ export class ServerSubscription extends Subscription {
822
846
  if (MaybePromise.is(changeResult)) {
823
847
  const resolver = Promise.resolve(changeResult)
824
848
  .catch(error => logger.error(`Error handling attribute change:`, error))
825
- .finally(() => this.attributeUpdatePromises.delete(resolver));
826
- this.attributeUpdatePromises.add(resolver);
849
+ .finally(() => this.#attributeUpdatePromises.delete(resolver));
850
+ this.#attributeUpdatePromises.add(resolver);
827
851
  }
828
852
  }
829
853
 
@@ -877,15 +901,15 @@ export class ServerSubscription extends Subscription {
877
901
  }
878
902
  }
879
903
 
880
- async flush() {
904
+ async #flush() {
881
905
  this.#sendDelayTimer.stop();
882
906
  if (this.#outstandingAttributeUpdates.size > 0 || this.#outstandingEventUpdates.size > 0) {
883
907
  logger.debug(
884
908
  `Flushing subscription ${this.id} with ${this.#outstandingAttributeUpdates.size} attributes and ${this.#outstandingEventUpdates.size} events${this.isClosed ? " (for closing)" : ""}`,
885
909
  );
886
910
  this.#triggerSendUpdate();
887
- if (this.currentUpdatePromise) {
888
- await this.currentUpdatePromise;
911
+ if (this.#currentUpdatePromise) {
912
+ await this.#currentUpdatePromise;
889
913
  }
890
914
  }
891
915
  }
@@ -894,9 +918,9 @@ export class ServerSubscription extends Subscription {
894
918
  this.#sendUpdatesActivated = false;
895
919
  this.unregisterAttributeListeners(Array.from(this.#attributeListeners.keys()));
896
920
  this.unregisterEventListeners(Array.from(this.#eventListeners.keys()));
897
- if (this.attributeUpdatePromises.size) {
898
- const resolvers = [...this.attributeUpdatePromises.values()];
899
- this.attributeUpdatePromises.clear();
921
+ if (this.#attributeUpdatePromises.size) {
922
+ const resolvers = [...this.#attributeUpdatePromises.values()];
923
+ this.#attributeUpdatePromises.clear();
900
924
  await MatterAggregateError.allSettled(resolvers, "Error receiving all outstanding attribute values").catch(
901
925
  error => logger.error(error),
902
926
  );
@@ -909,16 +933,19 @@ export class ServerSubscription extends Subscription {
909
933
  /**
910
934
  * Closes the subscription and flushes all outstanding data updates if requested.
911
935
  */
912
- override async close(graceful = false) {
936
+ override async close(graceful = false, cancelledByPeer = false) {
913
937
  if (this.isClosed) {
914
938
  return;
915
939
  }
940
+ if (cancelledByPeer) {
941
+ this.isCanceledByPeer = true;
942
+ }
916
943
  await this.destroy();
917
944
  if (graceful) {
918
- await this.flush();
945
+ await this.#flush();
919
946
  }
920
- if (this.currentUpdatePromise) {
921
- await this.currentUpdatePromise;
947
+ if (this.#currentUpdatePromise) {
948
+ await this.#currentUpdatePromise;
922
949
  }
923
950
  }
924
951
 
@@ -954,11 +981,8 @@ export class ServerSubscription extends Subscription {
954
981
  }
955
982
  }
956
983
 
957
- private async sendUpdateMessage(
958
- attributes: AttributePathWithValueVersion<any>[],
959
- events: EventPathWithEventData<any>[],
960
- ) {
961
- const exchange = this.#context.initiateExchange(this.peerAddress, INTERACTION_PROTOCOL_ID);
984
+ async #sendUpdateMessage(attributes: AttributePathWithValueVersion<any>[], events: EventPathWithEventData<any>[]) {
985
+ const exchange = this.#context.initiateExchange(this.#peerAddress, INTERACTION_PROTOCOL_ID);
962
986
  if (exchange === undefined) return;
963
987
  if (attributes.length) {
964
988
  logger.debug(
@@ -4,7 +4,7 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
- import { Logger } from "#general";
7
+ import { InternalError, Logger, Observable } from "#general";
8
8
  import { type SecureSession } from "#session/SecureSession.js";
9
9
  import { TlvAttributePath, TlvDataVersionFilter, TlvEventFilter, TlvEventPath, TypeFromSchema } from "#types";
10
10
 
@@ -29,15 +29,13 @@ export abstract class Subscription {
29
29
  #isClosed?: boolean;
30
30
  #isCanceledByPeer?: boolean;
31
31
  #criteria: SubscriptionCriteria;
32
+ #cancelled = Observable<[subscription: Subscription]>();
33
+ #maxIntervalMs?: number;
32
34
 
33
35
  constructor(session: SecureSession, id: SubscriptionId, criteria: SubscriptionCriteria) {
34
36
  this.#session = session;
35
37
  this.#id = id;
36
38
  this.#criteria = criteria;
37
-
38
- // TODO Do not add to session but to node/peer
39
- this.#session.subscriptions.add(this);
40
- logger.debug(`Added subscription ${this.#id} to ${this.#session.name}`);
41
39
  }
42
40
 
43
41
  get id() {
@@ -60,6 +58,28 @@ export abstract class Subscription {
60
58
  return this.#session;
61
59
  }
62
60
 
61
+ get cancelled() {
62
+ return this.#cancelled;
63
+ }
64
+
65
+ get maxIntervalMs(): number {
66
+ if (this.#maxIntervalMs === undefined) {
67
+ throw new InternalError("Subscription MaxIntervalMs accessed before it was set");
68
+ }
69
+ return this.#maxIntervalMs;
70
+ }
71
+
72
+ set maxIntervalMs(value: number) {
73
+ if (this.#maxIntervalMs !== undefined) {
74
+ throw new InternalError("Subscription MaxIntervalMs set twice. This should never happen.");
75
+ }
76
+ this.#maxIntervalMs = value;
77
+ }
78
+
79
+ get maxInterval(): number {
80
+ return Math.ceil(this.maxIntervalMs / 1000);
81
+ }
82
+
63
83
  /**
64
84
  * Update session state. This probably is meaningless except in a server context.
65
85
  */
@@ -77,7 +97,7 @@ export abstract class Subscription {
77
97
  }
78
98
 
79
99
  /** Close the subscription with the option to gracefully flush outstanding data. */
80
- abstract close(graceful: boolean): Promise<void>;
100
+ abstract close(graceful: boolean, cancelledByPeer?: boolean): Promise<void>;
81
101
 
82
102
  /**
83
103
  * Destroy the subscription. Unsubscribe from all attributes and events and stop all timers.
@@ -86,5 +106,13 @@ export abstract class Subscription {
86
106
  this.#isClosed = true;
87
107
  this.#session.subscriptions.delete(this);
88
108
  logger.debug(`Removed subscription ${this.id} from ${this.#session.name}`);
109
+
110
+ this.#cancelled.emit(this);
111
+ }
112
+
113
+ protected activate() {
114
+ // TODO Do not add to session but to node/peer
115
+ this.#session.subscriptions.add(this);
116
+ logger.debug(`Added subscription ${this.#id} to ${this.#session.name}`);
89
117
  }
90
118
  }
@@ -0,0 +1,107 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { MessageExchange } from "#protocol/MessageExchange.js";
8
+ import { ProtocolHandler } from "#protocol/ProtocolHandler.js";
9
+ import { Environment, Environmental, Logger, MaybePromise, Time, Timer } from "@matter/general";
10
+ import { INTERACTION_PROTOCOL_ID } from "@matter/types";
11
+ import { DataReport, IncomingInteractionClientMessenger } from "./InteractionMessenger.js";
12
+
13
+ const logger = Logger.get("SubscriptionClient");
14
+
15
+ export interface RegisteredSubscription {
16
+ id: number;
17
+ maximumPeerResponseTime: number;
18
+ maxIntervalS: number;
19
+ onData: (dataReport: DataReport) => MaybePromise<void>;
20
+ onTimeout?: () => void;
21
+ }
22
+
23
+ /**
24
+ * A simple protocol handler that handles exchanges starting with data reports.
25
+ *
26
+ * Incoming data reports must match to a subscription registered with {@link add} or the exchange is invalid.
27
+ */
28
+ export class SubscriptionClient implements ProtocolHandler {
29
+ readonly #listeners = new Map<number, (dataReport: DataReport) => MaybePromise<void>>();
30
+ readonly #timeouts = new Map<number, Timer>();
31
+
32
+ constructor() {}
33
+
34
+ static [Environmental.create](env: Environment) {
35
+ const client = new SubscriptionClient();
36
+ env.set(SubscriptionClient, client);
37
+ return client;
38
+ }
39
+
40
+ readonly id = INTERACTION_PROTOCOL_ID;
41
+
42
+ /**
43
+ * Register a subscription.
44
+ */
45
+ add(subscription: RegisteredSubscription) {
46
+ const { id, onData, onTimeout } = subscription;
47
+
48
+ this.#listeners.set(id, onData);
49
+ if (onTimeout) {
50
+ let timer = this.#timeouts.get(id);
51
+ if (timer !== undefined) {
52
+ timer.stop();
53
+ this.#timeouts.delete(id);
54
+ }
55
+
56
+ const maxIntervalMs = subscription.maxIntervalS * 1000 + subscription.maximumPeerResponseTime;
57
+
58
+ timer = Time.getTimer("Subscription timeout", maxIntervalMs, () => {
59
+ logger.info(`Subscription ${id} timed out after ${maxIntervalMs}ms`);
60
+ this.delete(id);
61
+ onTimeout();
62
+ }).start();
63
+
64
+ this.#timeouts.set(id, timer);
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Unregister a subscription.
70
+ */
71
+ delete(id: number) {
72
+ this.#listeners.delete(id);
73
+ const timer = this.#timeouts.get(id);
74
+ if (timer !== undefined) {
75
+ timer.stop();
76
+ this.#timeouts.delete(id);
77
+ }
78
+ }
79
+
80
+ async onNewExchange(exchange: MessageExchange) {
81
+ const messenger = new IncomingInteractionClientMessenger(exchange);
82
+
83
+ let dataReport: DataReport;
84
+ try {
85
+ // 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
+ } finally {
88
+ messenger.close().catch(error => logger.info("Error closing client messenger", error));
89
+ }
90
+ const subscriptionId = dataReport.subscriptionId as number; // this is checked in the messenger already because we hand over allowed list
91
+
92
+ const listener = this.#listeners.get(subscriptionId);
93
+ const timer = this.#timeouts.get(subscriptionId);
94
+
95
+ if (timer !== undefined) {
96
+ timer.stop().start(); // Restart timer because we received data
97
+ }
98
+
99
+ await listener?.(dataReport);
100
+ }
101
+
102
+ async close() {
103
+ this.#listeners.clear();
104
+ this.#timeouts.forEach(timer => timer.stop());
105
+ this.#timeouts.clear();
106
+ }
107
+ }
@@ -14,3 +14,4 @@ export * from "./InteractionMessenger.js";
14
14
  export * from "./InteractionServer.js";
15
15
  export * from "./ServerSubscription.js";
16
16
  export * from "./Subscription.js";
17
+ export * from "./SubscriptionClient.js";
@@ -27,11 +27,11 @@ import { ChannelStatusResponseError } from "#securechannel/index.js";
27
27
  import { PaseClient } from "#session/index.js";
28
28
  import { SessionManager } from "#session/SessionManager.js";
29
29
  import { DiscoveryCapabilitiesBitmap, NodeId, SECURE_CHANNEL_PROTOCOL_ID, TypeFromPartialBitSchema } from "#types";
30
- import { InteractionClient } from "../interaction/InteractionClient.js";
30
+ import { InteractionClient, InteractionClientProvider } from "../interaction/InteractionClient.js";
31
31
  import { ExchangeManager, MessageChannel } from "../protocol/ExchangeManager.js";
32
32
  import { DedicatedChannelExchangeProvider } from "../protocol/ExchangeProvider.js";
33
33
  import { PeerAddress } from "./PeerAddress.js";
34
- import { NodeDiscoveryType, PeerSet } from "./PeerSet.js";
34
+ import { NodeDiscoveryType } from "./PeerSet.js";
35
35
 
36
36
  const logger = Logger.get("PeerCommissioner");
37
37
 
@@ -108,7 +108,7 @@ export interface DiscoveryAndCommissioningOptions extends CommissioningOptions {
108
108
  * Interfaces {@link ControllerCommissioner} with other components.
109
109
  */
110
110
  export interface ControllerCommissionerContext {
111
- peers: PeerSet;
111
+ clients: InteractionClientProvider;
112
112
  scanners: ScannerSet;
113
113
  netInterfaces: NetInterfaceSet;
114
114
  sessions: SessionManager;
@@ -130,7 +130,7 @@ export class ControllerCommissioner {
130
130
 
131
131
  static [Environmental.create](env: Environment) {
132
132
  const instance = new ControllerCommissioner({
133
- peers: env.get(PeerSet),
133
+ clients: env.get(InteractionClientProvider),
134
134
  scanners: env.get(ScannerSet),
135
135
  netInterfaces: env.get(NetInterfaceSet),
136
136
  sessions: env.get(SessionManager),
@@ -385,6 +385,7 @@ export class ControllerCommissioner {
385
385
  // Use the created secure session to do the commissioning
386
386
  new InteractionClient(
387
387
  new DedicatedChannelExchangeProvider(this.#context.exchanges, paseSecureMessageChannel),
388
+ this.#context.clients.peers.subscriptionClient,
388
389
  address,
389
390
  ),
390
391
  this.#context.ca,
@@ -406,15 +407,14 @@ export class ControllerCommissioner {
406
407
  }
407
408
 
408
409
  // Look for the device broadcast over MDNS and do CASE pairing
409
- return await this.#context.peers.connect(
410
- address,
411
- {
410
+ return await this.#context.clients.connect(address, {
411
+ discoveryOptions: {
412
412
  discoveryType: NodeDiscoveryType.TimedDiscovery,
413
413
  timeoutSeconds: 120,
414
414
  discoveryData,
415
415
  },
416
- true,
417
- ); // Wait maximum 120s to find the operational device for commissioning process
416
+ allowUnknownPeer: true,
417
+ }); // Wait maximum 120s to find the operational device for commissioning process
418
418
  },
419
419
  );
420
420
 
@@ -422,7 +422,7 @@ export class ControllerCommissioner {
422
422
  await commissioningManager.executeCommissioning();
423
423
  } catch (error) {
424
424
  // We might have added data for an operational address that we need to cleanup
425
- await this.#context.peers.delete(address);
425
+ await this.#context.clients.peers.delete(address);
426
426
  throw error;
427
427
  } finally {
428
428
  if (!paseSecureMessageChannel.closed) {
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { Environment, Environmental, PromiseQueue } from "@matter/general";
8
+
9
+ const CONCURRENT_QUEUED_INTERACTIONS = 4;
10
+ const INTERACTION_QUEUE_DELAY_MS = 100;
11
+
12
+ export class InteractionQueue extends PromiseQueue {
13
+ constructor() {
14
+ super(CONCURRENT_QUEUED_INTERACTIONS, INTERACTION_QUEUE_DELAY_MS);
15
+ }
16
+
17
+ static [Environmental.create](env: Environment) {
18
+ const instance = new InteractionQueue();
19
+ env.set(InteractionQueue, instance);
20
+ return instance;
21
+ }
22
+ }
@@ -86,3 +86,17 @@ export class PeerAddressMap<T> extends Map<PeerAddress, T> {
86
86
  return super.get(PeerAddress(key));
87
87
  }
88
88
  }
89
+
90
+ export class PeerAddressSet extends Set<PeerAddress> {
91
+ override add(value: PeerAddress) {
92
+ return super.add(PeerAddress(value));
93
+ }
94
+
95
+ override has(value: PeerAddress) {
96
+ return super.has(PeerAddress(value));
97
+ }
98
+
99
+ override delete(value: PeerAddress) {
100
+ return super.delete(PeerAddress(value));
101
+ }
102
+ }
@@ -18,7 +18,7 @@ export abstract class PeerAddressStore {
18
18
  abstract loadPeers(): MaybePromise<Iterable<OperationalPeer>>;
19
19
  abstract updatePeer(peer: OperationalPeer): MaybePromise<void>;
20
20
  abstract deletePeer(address: PeerAddress): MaybePromise<void>;
21
- abstract createNodeStore(address: PeerAddress): Promise<PeerDataStore>;
21
+ abstract createNodeStore(address: PeerAddress): MaybePromise<PeerDataStore | undefined>;
22
22
  }
23
23
 
24
24
  export abstract class PeerDataStore {