@matter/protocol 0.12.0-alpha.0-20241229-9d9c99934 → 0.12.0-alpha.0-20241231-14ac774ba

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 (163) hide show
  1. package/dist/cjs/cluster/server/EventServer.d.ts.map +1 -1
  2. package/dist/cjs/cluster/server/EventServer.js +4 -1
  3. package/dist/cjs/cluster/server/EventServer.js.map +1 -1
  4. package/dist/cjs/codec/MessageCodec.d.ts +2 -1
  5. package/dist/cjs/codec/MessageCodec.d.ts.map +1 -1
  6. package/dist/cjs/codec/MessageCodec.js +39 -9
  7. package/dist/cjs/codec/MessageCodec.js.map +1 -1
  8. package/dist/cjs/events/NonvolatileEventStore.d.ts.map +1 -1
  9. package/dist/cjs/events/NonvolatileEventStore.js +2 -6
  10. package/dist/cjs/events/NonvolatileEventStore.js.map +1 -1
  11. package/dist/cjs/events/OccurrenceManager.d.ts.map +1 -1
  12. package/dist/cjs/events/OccurrenceManager.js +2 -2
  13. package/dist/cjs/events/OccurrenceManager.js.map +1 -1
  14. package/dist/cjs/interaction/InteractionClient.d.ts.map +1 -1
  15. package/dist/cjs/interaction/InteractionClient.js +0 -3
  16. package/dist/cjs/interaction/InteractionClient.js.map +1 -1
  17. package/dist/cjs/interaction/InteractionMessenger.d.ts +17 -6
  18. package/dist/cjs/interaction/InteractionMessenger.d.ts.map +1 -1
  19. package/dist/cjs/interaction/InteractionMessenger.js +88 -31
  20. package/dist/cjs/interaction/InteractionMessenger.js.map +1 -1
  21. package/dist/cjs/interaction/InteractionServer.d.ts.map +1 -1
  22. package/dist/cjs/interaction/InteractionServer.js +32 -7
  23. package/dist/cjs/interaction/InteractionServer.js.map +1 -1
  24. package/dist/cjs/interaction/ServerSubscription.d.ts.map +1 -1
  25. package/dist/cjs/interaction/ServerSubscription.js +28 -16
  26. package/dist/cjs/interaction/ServerSubscription.js.map +1 -1
  27. package/dist/cjs/mdns/MdnsScanner.d.ts +3 -3
  28. package/dist/cjs/mdns/MdnsScanner.d.ts.map +1 -1
  29. package/dist/cjs/mdns/MdnsScanner.js +18 -15
  30. package/dist/cjs/mdns/MdnsScanner.js.map +1 -1
  31. package/dist/cjs/mdns/MdnsServer.d.ts.map +1 -1
  32. package/dist/cjs/mdns/MdnsServer.js +8 -6
  33. package/dist/cjs/mdns/MdnsServer.js.map +1 -1
  34. package/dist/cjs/mdns/MdnsService.d.ts.map +1 -1
  35. package/dist/cjs/mdns/MdnsService.js +4 -1
  36. package/dist/cjs/mdns/MdnsService.js.map +1 -1
  37. package/dist/cjs/protocol/DeviceAdvertiser.d.ts.map +1 -1
  38. package/dist/cjs/protocol/DeviceAdvertiser.js +5 -5
  39. package/dist/cjs/protocol/DeviceAdvertiser.js.map +1 -1
  40. package/dist/cjs/protocol/ExchangeManager.d.ts +2 -2
  41. package/dist/cjs/protocol/ExchangeManager.d.ts.map +1 -1
  42. package/dist/cjs/protocol/ExchangeManager.js +8 -6
  43. package/dist/cjs/protocol/ExchangeManager.js.map +1 -1
  44. package/dist/cjs/protocol/MessageExchange.d.ts +12 -2
  45. package/dist/cjs/protocol/MessageExchange.d.ts.map +1 -1
  46. package/dist/cjs/protocol/MessageExchange.js +47 -36
  47. package/dist/cjs/protocol/MessageExchange.js.map +1 -1
  48. package/dist/cjs/securechannel/SecureChannelMessenger.d.ts +3 -6
  49. package/dist/cjs/securechannel/SecureChannelMessenger.d.ts.map +1 -1
  50. package/dist/cjs/securechannel/SecureChannelMessenger.js +21 -8
  51. package/dist/cjs/securechannel/SecureChannelMessenger.js.map +1 -1
  52. package/dist/cjs/session/SecureSession.d.ts +2 -1
  53. package/dist/cjs/session/SecureSession.d.ts.map +1 -1
  54. package/dist/cjs/session/SecureSession.js +16 -10
  55. package/dist/cjs/session/SecureSession.js.map +1 -1
  56. package/dist/cjs/session/SessionManager.d.ts +2 -1
  57. package/dist/cjs/session/SessionManager.d.ts.map +1 -1
  58. package/dist/cjs/session/SessionManager.js +10 -3
  59. package/dist/cjs/session/SessionManager.js.map +1 -1
  60. package/dist/cjs/session/case/CaseClient.js +4 -4
  61. package/dist/cjs/session/case/CaseClient.js.map +1 -1
  62. package/dist/cjs/session/case/CaseMessenger.js +3 -3
  63. package/dist/cjs/session/case/CaseMessenger.js.map +1 -1
  64. package/dist/cjs/session/case/CaseServer.js +2 -2
  65. package/dist/cjs/session/case/CaseServer.js.map +1 -1
  66. package/dist/cjs/session/pase/PaseClient.js +1 -1
  67. package/dist/cjs/session/pase/PaseClient.js.map +1 -1
  68. package/dist/cjs/session/pase/PaseMessenger.d.ts.map +1 -1
  69. package/dist/cjs/session/pase/PaseMessenger.js +3 -5
  70. package/dist/cjs/session/pase/PaseMessenger.js.map +1 -1
  71. package/dist/esm/cluster/server/EventServer.d.ts.map +1 -1
  72. package/dist/esm/cluster/server/EventServer.js +5 -1
  73. package/dist/esm/cluster/server/EventServer.js.map +1 -1
  74. package/dist/esm/codec/MessageCodec.d.ts +2 -1
  75. package/dist/esm/codec/MessageCodec.d.ts.map +1 -1
  76. package/dist/esm/codec/MessageCodec.js +40 -10
  77. package/dist/esm/codec/MessageCodec.js.map +1 -1
  78. package/dist/esm/events/NonvolatileEventStore.d.ts.map +1 -1
  79. package/dist/esm/events/NonvolatileEventStore.js +3 -7
  80. package/dist/esm/events/NonvolatileEventStore.js.map +1 -1
  81. package/dist/esm/events/OccurrenceManager.d.ts.map +1 -1
  82. package/dist/esm/events/OccurrenceManager.js +11 -3
  83. package/dist/esm/events/OccurrenceManager.js.map +1 -1
  84. package/dist/esm/interaction/InteractionClient.d.ts.map +1 -1
  85. package/dist/esm/interaction/InteractionClient.js +0 -3
  86. package/dist/esm/interaction/InteractionClient.js.map +1 -1
  87. package/dist/esm/interaction/InteractionMessenger.d.ts +17 -6
  88. package/dist/esm/interaction/InteractionMessenger.d.ts.map +1 -1
  89. package/dist/esm/interaction/InteractionMessenger.js +89 -32
  90. package/dist/esm/interaction/InteractionMessenger.js.map +1 -1
  91. package/dist/esm/interaction/InteractionServer.d.ts.map +1 -1
  92. package/dist/esm/interaction/InteractionServer.js +32 -7
  93. package/dist/esm/interaction/InteractionServer.js.map +1 -1
  94. package/dist/esm/interaction/ServerSubscription.d.ts.map +1 -1
  95. package/dist/esm/interaction/ServerSubscription.js +29 -17
  96. package/dist/esm/interaction/ServerSubscription.js.map +1 -1
  97. package/dist/esm/mdns/MdnsScanner.d.ts +3 -3
  98. package/dist/esm/mdns/MdnsScanner.d.ts.map +1 -1
  99. package/dist/esm/mdns/MdnsScanner.js +18 -15
  100. package/dist/esm/mdns/MdnsScanner.js.map +1 -1
  101. package/dist/esm/mdns/MdnsServer.d.ts.map +1 -1
  102. package/dist/esm/mdns/MdnsServer.js +9 -6
  103. package/dist/esm/mdns/MdnsServer.js.map +1 -1
  104. package/dist/esm/mdns/MdnsService.d.ts.map +1 -1
  105. package/dist/esm/mdns/MdnsService.js +5 -1
  106. package/dist/esm/mdns/MdnsService.js.map +1 -1
  107. package/dist/esm/protocol/DeviceAdvertiser.d.ts.map +1 -1
  108. package/dist/esm/protocol/DeviceAdvertiser.js +5 -5
  109. package/dist/esm/protocol/DeviceAdvertiser.js.map +1 -1
  110. package/dist/esm/protocol/ExchangeManager.d.ts +2 -2
  111. package/dist/esm/protocol/ExchangeManager.d.ts.map +1 -1
  112. package/dist/esm/protocol/ExchangeManager.js +9 -6
  113. package/dist/esm/protocol/ExchangeManager.js.map +1 -1
  114. package/dist/esm/protocol/MessageExchange.d.ts +12 -2
  115. package/dist/esm/protocol/MessageExchange.d.ts.map +1 -1
  116. package/dist/esm/protocol/MessageExchange.js +47 -36
  117. package/dist/esm/protocol/MessageExchange.js.map +1 -1
  118. package/dist/esm/securechannel/SecureChannelMessenger.d.ts +3 -6
  119. package/dist/esm/securechannel/SecureChannelMessenger.d.ts.map +1 -1
  120. package/dist/esm/securechannel/SecureChannelMessenger.js +22 -9
  121. package/dist/esm/securechannel/SecureChannelMessenger.js.map +1 -1
  122. package/dist/esm/session/SecureSession.d.ts +2 -1
  123. package/dist/esm/session/SecureSession.d.ts.map +1 -1
  124. package/dist/esm/session/SecureSession.js +16 -10
  125. package/dist/esm/session/SecureSession.js.map +1 -1
  126. package/dist/esm/session/SessionManager.d.ts +2 -1
  127. package/dist/esm/session/SessionManager.d.ts.map +1 -1
  128. package/dist/esm/session/SessionManager.js +11 -3
  129. package/dist/esm/session/SessionManager.js.map +1 -1
  130. package/dist/esm/session/case/CaseClient.js +5 -5
  131. package/dist/esm/session/case/CaseClient.js.map +1 -1
  132. package/dist/esm/session/case/CaseMessenger.js +3 -3
  133. package/dist/esm/session/case/CaseMessenger.js.map +1 -1
  134. package/dist/esm/session/case/CaseServer.js +2 -2
  135. package/dist/esm/session/case/CaseServer.js.map +1 -1
  136. package/dist/esm/session/pase/PaseClient.js +1 -1
  137. package/dist/esm/session/pase/PaseClient.js.map +1 -1
  138. package/dist/esm/session/pase/PaseMessenger.d.ts.map +1 -1
  139. package/dist/esm/session/pase/PaseMessenger.js +3 -5
  140. package/dist/esm/session/pase/PaseMessenger.js.map +1 -1
  141. package/package.json +6 -6
  142. package/src/cluster/server/EventServer.ts +4 -1
  143. package/src/codec/MessageCodec.ts +42 -10
  144. package/src/events/NonvolatileEventStore.ts +4 -5
  145. package/src/events/OccurrenceManager.ts +12 -2
  146. package/src/interaction/InteractionClient.ts +0 -3
  147. package/src/interaction/InteractionMessenger.ts +114 -32
  148. package/src/interaction/InteractionServer.ts +30 -5
  149. package/src/interaction/ServerSubscription.ts +23 -16
  150. package/src/mdns/MdnsScanner.ts +17 -14
  151. package/src/mdns/MdnsServer.ts +7 -4
  152. package/src/mdns/MdnsService.ts +5 -1
  153. package/src/protocol/DeviceAdvertiser.ts +5 -7
  154. package/src/protocol/ExchangeManager.ts +10 -7
  155. package/src/protocol/MessageExchange.ts +59 -38
  156. package/src/securechannel/SecureChannelMessenger.ts +31 -10
  157. package/src/session/SecureSession.ts +17 -10
  158. package/src/session/SessionManager.ts +11 -3
  159. package/src/session/case/CaseClient.ts +5 -5
  160. package/src/session/case/CaseMessenger.ts +3 -3
  161. package/src/session/case/CaseServer.ts +2 -2
  162. package/src/session/pase/PaseClient.ts +1 -1
  163. package/src/session/pase/PaseMessenger.ts +3 -5
@@ -4,7 +4,7 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
- import { Logger, MatterFlowError, NoResponseTimeoutError, UnexpectedDataError } from "#general";
7
+ import { Diagnostic, Logger, MatterFlowError, NoResponseTimeoutError, UnexpectedDataError } from "#general";
8
8
  import { Specification } from "#model";
9
9
  import {
10
10
  Status,
@@ -85,22 +85,64 @@ class InteractionMessenger {
85
85
  return this.send(
86
86
  MessageType.StatusResponse,
87
87
  TlvStatusResponse.encode({ status, interactionModelRevision: Specification.INTERACTION_MODEL_REVISION }),
88
- options,
88
+ {
89
+ ...options,
90
+ logContext: {
91
+ for: options?.logContext?.for ? `I/Status-${options?.logContext?.for}` : undefined,
92
+ status: `${StatusCode[status] ?? "unknown"}(${Diagnostic.hex(status)})`,
93
+ ...options?.logContext,
94
+ },
95
+ },
89
96
  );
90
97
  }
91
98
 
92
- async waitForSuccess(expectedProcessingTimeMs?: number) {
99
+ async waitForSuccess(
100
+ expectedMessageInfo: string,
101
+ options?: { expectedProcessingTimeMs?: number; timeoutMs?: number },
102
+ ) {
93
103
  // If the status is not Success, this would throw an Error.
94
- await this.nextMessage(MessageType.StatusResponse, expectedProcessingTimeMs);
104
+ await this.nextMessage(MessageType.StatusResponse, options, `Success-${expectedMessageInfo}`);
105
+ }
106
+
107
+ async nextMessage(
108
+ expectedMessageType: number,
109
+ options?: {
110
+ expectedProcessingTimeMs?: number;
111
+ timeoutMs?: number;
112
+ },
113
+ expectedMessageInfo?: string,
114
+ ) {
115
+ return this.#nextMessage(expectedMessageType, options, expectedMessageInfo);
116
+ }
117
+
118
+ async anyNextMessage(
119
+ expectedMessageInfo: string,
120
+ options?: {
121
+ expectedProcessingTimeMs?: number;
122
+ timeoutMs?: number;
123
+ },
124
+ ) {
125
+ return this.#nextMessage(undefined, options, expectedMessageInfo);
95
126
  }
96
127
 
97
- async nextMessage(expectedMessageType?: number, expectedProcessingTimeMs?: number) {
98
- const message = await this.exchange.nextMessage(expectedProcessingTimeMs);
128
+ async #nextMessage(
129
+ expectedMessageType?: number,
130
+ options?: {
131
+ expectedProcessingTimeMs?: number;
132
+ timeoutMs?: number;
133
+ },
134
+ expectedMessageInfo?: string,
135
+ ) {
136
+ const { expectedProcessingTimeMs, timeoutMs } = options ?? {};
137
+ const message = await this.exchange.nextMessage({ expectedProcessingTimeMs, timeoutMs });
99
138
  const messageType = message.payloadHeader.messageType;
100
- this.throwIfErrorStatusMessage(message);
139
+ if (expectedMessageType !== undefined && expectedMessageInfo === undefined) {
140
+ expectedMessageInfo = MessageType[expectedMessageType];
141
+ }
142
+ this.throwIfErrorStatusMessage(message, expectedMessageInfo);
101
143
  if (expectedMessageType !== undefined && messageType !== expectedMessageType) {
102
144
  throw new UnexpectedDataError(
103
- `Received unexpected message type: ${messageType}, expected: ${expectedMessageType}`,
145
+ `Received unexpected message for ${expectedMessageInfo} type: ${messageType}, expected: ${expectedMessageType}`,
104
146
  );
105
147
  }
106
148
  return message;
@@ -110,7 +152,7 @@ class InteractionMessenger {
110
152
  await this.exchange.close();
111
153
  }
112
154
 
113
- protected throwIfErrorStatusMessage(message: Message) {
155
+ protected throwIfErrorStatusMessage(message: Message, logHint?: string) {
114
156
  const {
115
157
  payloadHeader: { messageType },
116
158
  payload,
@@ -118,7 +160,8 @@ class InteractionMessenger {
118
160
 
119
161
  if (messageType !== MessageType.StatusResponse) return;
120
162
  const { status } = TlvStatusResponse.decode(payload);
121
- if (status !== StatusCode.Success) throw new StatusResponseError(`Received error status: ${status}`, status);
163
+ if (status !== StatusCode.Success)
164
+ throw new StatusResponseError(`Received error status: ${status}${logHint ? ` (${logHint})` : ""}`, status);
122
165
  }
123
166
 
124
167
  getExchangeChannelName() {
@@ -193,7 +236,9 @@ export class InteractionServerMessenger extends InteractionMessenger {
193
236
  case MessageType.TimedRequest: {
194
237
  const timedRequest = TlvTimedRequest.decode(message.payload);
195
238
  recipient.handleTimedRequest(this.exchange, timedRequest, message);
196
- await this.sendStatus(StatusCode.Success);
239
+ await this.sendStatus(StatusCode.Success, {
240
+ logContext: { for: "TimedRequest" },
241
+ });
197
242
  continueExchange = true;
198
243
  break;
199
244
  }
@@ -226,7 +271,7 @@ export class InteractionServerMessenger extends InteractionMessenger {
226
271
  * Handle DataReportPayload with the content of a DataReport to send, split them into multiple DataReport
227
272
  * messages and send them out based on the size.
228
273
  */
229
- async sendDataReport(dataReportPayload: DataReportPayload, forFabricFilteredRead: boolean) {
274
+ async sendDataReport(dataReportPayload: DataReportPayload, forFabricFilteredRead: boolean, waitForAck = true) {
230
275
  const {
231
276
  subscriptionId,
232
277
  attributeReportsPayload,
@@ -255,7 +300,7 @@ export class InteractionServerMessenger extends InteractionMessenger {
255
300
  let firstAttributeAddedToReportMessage = false;
256
301
  let firstEventAddedToReportMessage = false;
257
302
  const sendAndResetReport = async () => {
258
- await this.sendDataReportMessage(dataReport);
303
+ await this.sendDataReportMessage(dataReport, waitForAck);
259
304
  dataReport.attributeReports = undefined;
260
305
  dataReport.eventReports = undefined;
261
306
  messageSize = emptyDataReportBytes.length;
@@ -321,10 +366,10 @@ export class InteractionServerMessenger extends InteractionMessenger {
321
366
  }
322
367
  }
323
368
 
324
- await this.sendDataReportMessage(dataReport);
369
+ await this.sendDataReportMessage(dataReport, waitForAck);
325
370
  }
326
371
 
327
- async sendDataReportMessage(dataReport: TypeFromSchema<typeof TlvDataReportForSend>) {
372
+ async sendDataReportMessage(dataReport: TypeFromSchema<typeof TlvDataReportForSend>, waitForAck = true) {
328
373
  const dataReportToSend = {
329
374
  ...dataReport,
330
375
  suppressResponse: dataReport.moreChunkedMessages ? false : dataReport.suppressResponse, // always false when moreChunkedMessages is true
@@ -337,17 +382,24 @@ export class InteractionServerMessenger extends InteractionMessenger {
337
382
  )}`,
338
383
  );
339
384
  }
340
- logger.debug(
341
- `Sending DataReport chunk with ${dataReportToSend.attributeReports?.length ?? 0} attributes and ${
342
- dataReportToSend.eventReports?.length ?? 0
343
- } events: ${encodedMessage.length} bytes (moreChunkedMessages: ${dataReportToSend.moreChunkedMessages ?? false}, suppressResponse: ${dataReportToSend.suppressResponse})`,
344
- );
385
+
386
+ const logContext = {
387
+ subId: dataReportToSend.subscriptionId,
388
+ interactionFlags: Diagnostic.asFlags({
389
+ suppressResponse: dataReportToSend.suppressResponse ?? false,
390
+ moreChunkedMessages: dataReportToSend.moreChunkedMessages ?? false,
391
+ }),
392
+ attr: dataReportToSend.attributeReports?.length,
393
+ ev: dataReportToSend.eventReports?.length,
394
+ };
345
395
 
346
396
  if (dataReportToSend.suppressResponse) {
347
397
  // We do not expect a response other than a Standalone Ack, so if we receive anything else, we throw an error
348
398
  try {
349
399
  await this.exchange.send(MessageType.ReportData, encodedMessage, {
350
400
  expectAckOnly: true,
401
+ disableMrpLogic: !waitForAck,
402
+ logContext,
351
403
  });
352
404
  } catch (e) {
353
405
  UnexpectedMessageError.accept(e);
@@ -356,15 +408,19 @@ export class InteractionServerMessenger extends InteractionMessenger {
356
408
  this.throwIfErrorStatusMessage(receivedMessage);
357
409
  }
358
410
  } else {
359
- await this.exchange.send(MessageType.ReportData, encodedMessage);
360
- await this.waitForSuccess();
411
+ await this.exchange.send(MessageType.ReportData, encodedMessage, {
412
+ disableMrpLogic: !waitForAck,
413
+ logContext,
414
+ });
415
+ // We wait for a Success Message - when we don't request an Ack only wait 500ms
416
+ await this.waitForSuccess("DataReport", { timeoutMs: waitForAck ? undefined : 500 });
361
417
  }
362
418
  }
363
419
  }
364
420
 
365
421
  export class IncomingInteractionClientMessenger extends InteractionMessenger {
366
- async waitFor(messageType: number, timeoutMs?: number) {
367
- const message = await this.nextMessage(timeoutMs);
422
+ async waitFor(expectedMessageInfo: string, messageType: number, timeoutMs?: number) {
423
+ const message = await this.anyNextMessage(expectedMessageInfo, { timeoutMs });
368
424
  const {
369
425
  payloadHeader: { messageType: receivedMessageType },
370
426
  } = message;
@@ -389,11 +445,16 @@ export class IncomingInteractionClientMessenger extends InteractionMessenger {
389
445
  const eventValues: TypeFromSchema<typeof TlvEventReport>[] = [];
390
446
 
391
447
  while (true) {
392
- const dataReportMessage = await this.waitFor(MessageType.ReportData);
448
+ const dataReportMessage = await this.waitFor("DataReport", MessageType.ReportData);
393
449
  const report = TlvDataReport.decode(dataReportMessage.payload);
394
450
  if (expectedSubscriptionIds !== undefined) {
395
451
  if (report.subscriptionId === undefined || !expectedSubscriptionIds.includes(report.subscriptionId)) {
396
- await this.sendStatus(StatusCode.InvalidSubscription, { multipleMessageInteraction: true });
452
+ await this.sendStatus(StatusCode.InvalidSubscription, {
453
+ multipleMessageInteraction: true,
454
+ logContext: {
455
+ subId: report.subscriptionId,
456
+ },
457
+ });
397
458
  throw new UnexpectedDataError(
398
459
  report.subscriptionId === undefined
399
460
  ? "Invalid Data report without Subscription ID"
@@ -411,9 +472,15 @@ export class IncomingInteractionClientMessenger extends InteractionMessenger {
411
472
  throw new UnexpectedDataError(`Invalid subscription ID ${report.subscriptionId} received`);
412
473
  }
413
474
 
414
- logger.debug(
415
- `Received DataReport chunk with ${report.attributeReports?.length ?? 0} attributes and ${report.eventReports?.length ?? 0} events, suppressResponse: ${report.suppressResponse}, moreChunkedMessages: ${report.moreChunkedMessages}${report.subscriptionId !== undefined ? `, subscriptionId: ${report.subscriptionId}` : ""}`,
416
- );
475
+ const logContext = {
476
+ subId: report.subscriptionId,
477
+ dataReportFlags: Diagnostic.asFlags({
478
+ suppressResponse: report.suppressResponse,
479
+ moreChunkedMessages: report.moreChunkedMessages,
480
+ }),
481
+ attr: report.attributeReports?.length,
482
+ ev: report.eventReports?.length,
483
+ };
417
484
 
418
485
  if (Array.isArray(report.attributeReports) && report.attributeReports.length > 0) {
419
486
  attributeValues.push(...report.attributeReports);
@@ -423,11 +490,17 @@ export class IncomingInteractionClientMessenger extends InteractionMessenger {
423
490
  }
424
491
 
425
492
  if (report.moreChunkedMessages) {
426
- await this.sendStatus(StatusCode.Success, { multipleMessageInteraction: true });
493
+ await this.sendStatus(StatusCode.Success, {
494
+ multipleMessageInteraction: true,
495
+ logContext,
496
+ });
427
497
  } else if (!report.suppressResponse) {
428
498
  // We received the last message and need to send a final Success, but we do not need to wait for it and
429
499
  // also don't care if it fails
430
- this.sendStatus(StatusCode.Success, { multipleMessageInteraction: true }).catch(error =>
500
+ this.sendStatus(StatusCode.Success, {
501
+ multipleMessageInteraction: true,
502
+ logContext,
503
+ }).catch(error =>
431
504
  logger.info("Error while sending final Success after receiving all DataReport chunks", error),
432
505
  );
433
506
  }
@@ -619,6 +692,11 @@ export class InteractionClientMessenger extends IncomingInteractionClientMesseng
619
692
  await this.send(requestMessageType, requestSchema.encode(request), {
620
693
  expectAckOnly: true,
621
694
  expectedProcessingTimeMs,
695
+ logContext: {
696
+ invokeFlags: Diagnostic.asFlags({
697
+ suppressResponse: true,
698
+ }),
699
+ },
622
700
  });
623
701
  }
624
702
 
@@ -634,7 +712,11 @@ export class InteractionClientMessenger extends IncomingInteractionClientMesseng
634
712
  expectAckOnly: false,
635
713
  expectedProcessingTimeMs,
636
714
  });
637
- const responseMessage = await this.nextMessage(responseMessageType, expectedProcessingTimeMs);
715
+ const responseMessage = await this.nextMessage(
716
+ responseMessageType,
717
+ { expectedProcessingTimeMs },
718
+ MessageType[responseMessageType] ?? `Response-${Diagnostic.hex(responseMessageType)}`,
719
+ );
638
720
  return responseSchema.decode(responseMessage.payload);
639
721
  }
640
722
  }
@@ -955,10 +955,16 @@ export class InteractionServer implements ProtocolHandler, InteractionRecipient
955
955
  );
956
956
 
957
957
  if (!keepSubscriptions) {
958
- logger.debug(
959
- `Clear subscriptions for Subscriber node ${session.peerNodeId} because keepSubscriptions=false`,
958
+ const clearedCount = await this.#context.sessions.clearSubscriptionsForNode(
959
+ fabric.fabricIndex,
960
+ session.peerNodeId,
961
+ true,
960
962
  );
961
- await this.#context.sessions.clearSubscriptionsForNode(fabric.fabricIndex, session.peerNodeId, true);
963
+ if (clearedCount > 0) {
964
+ logger.debug(
965
+ `Cleared ${clearedCount} subscriptions for Subscriber node ${session.peerNodeId} because keepSubscriptions=false`,
966
+ );
967
+ }
962
968
  }
963
969
 
964
970
  if (
@@ -1072,7 +1078,11 @@ export class InteractionServer implements ProtocolHandler, InteractionRecipient
1072
1078
  await subscription.close(); // Cleanup
1073
1079
  if (error instanceof StatusResponseError) {
1074
1080
  logger.info(`Sending status response ${error.code} for interaction error: ${error.message}`);
1075
- await messenger.sendStatus(error.code);
1081
+ await messenger.sendStatus(error.code, {
1082
+ logContext: {
1083
+ for: "I/SubscriptionSeed-Status",
1084
+ },
1085
+ });
1076
1086
  }
1077
1087
  await messenger.close();
1078
1088
  return; // Make sure to not bubble up the exception
@@ -1092,6 +1102,12 @@ export class InteractionServer implements ProtocolHandler, InteractionRecipient
1092
1102
  maxInterval,
1093
1103
  interactionModelRevision: Specification.INTERACTION_MODEL_REVISION,
1094
1104
  }),
1105
+ {
1106
+ logContext: {
1107
+ subId: subscriptionId,
1108
+ maxInterval,
1109
+ },
1110
+ },
1095
1111
  );
1096
1112
 
1097
1113
  // When an error occurs while sending the response, the subscription is not yet active and will be cleaned up by GC
@@ -1230,12 +1246,21 @@ export class InteractionServer implements ProtocolHandler, InteractionRecipient
1230
1246
  `Send ${lastMessageProcessed ? "final " : ""}invoke response for ${invokeResponseMessage.invokeResponses} commands`,
1231
1247
  );
1232
1248
  }
1249
+ const moreChunkedMessages = lastMessageProcessed ? undefined : true;
1233
1250
  await messenger.send(
1234
1251
  MessageType.InvokeResponse,
1235
1252
  TlvInvokeResponseForSend.encode({
1236
1253
  ...invokeResponseMessage,
1237
- moreChunkedMessages: lastMessageProcessed ? undefined : true,
1254
+ moreChunkedMessages,
1238
1255
  }),
1256
+ {
1257
+ logContext: {
1258
+ invokeMsgFlags: Diagnostic.asFlags({
1259
+ suppressResponse,
1260
+ moreChunkedMessages,
1261
+ }),
1262
+ },
1263
+ },
1239
1264
  );
1240
1265
  invokeResponseMessage.invokeResponses = [];
1241
1266
  messageSize = emptyInvokeResponseBytes.length;
@@ -8,13 +8,13 @@ import { NumberedOccurrence } from "#events/Occurrence.js";
8
8
  import {
9
9
  InternalError,
10
10
  Logger,
11
+ MatterAggregateError,
11
12
  MatterError,
12
13
  MaybePromise,
13
14
  NetworkError,
14
15
  NoResponseTimeoutError,
15
16
  Time,
16
17
  Timer,
17
- createPromise,
18
18
  isObject,
19
19
  } from "#general";
20
20
  import { Specification } from "#model";
@@ -151,7 +151,9 @@ export class ServerSubscription extends Subscription {
151
151
 
152
152
  #lastUpdateTimeMs = 0;
153
153
  #updateTimer: Timer;
154
- readonly #sendDelayTimer = Time.getTimer("Subscription delay", 50, () => this.#triggerSendUpdate());
154
+ readonly #sendDelayTimer: Timer = Time.getTimer(`Subscription ${this.id} delay`, 50, () =>
155
+ this.#triggerSendUpdate(),
156
+ );
155
157
  readonly #outstandingAttributeUpdates = new Map<string, AttributePathWithValueVersion<any>>();
156
158
  readonly #outstandingEventUpdates = new Set<EventPathWithEventData<any>>();
157
159
  readonly #attributeListeners = new Map<
@@ -206,7 +208,9 @@ export class ServerSubscription extends Subscription {
206
208
  this.#maxIntervalMs = maxInterval;
207
209
  this.#sendIntervalMs = sendInterval;
208
210
 
209
- this.#updateTimer = Time.getTimer("Subscription update", this.#sendIntervalMs, () => this.#prepareDataUpdate()); // will be started later
211
+ this.#updateTimer = Time.getTimer(`Subscription ${this.id} update`, this.#sendIntervalMs, () =>
212
+ this.#prepareDataUpdate(),
213
+ ); // will be started later
210
214
  }
211
215
 
212
216
  private determineSendingIntervals(
@@ -536,7 +540,7 @@ export class ServerSubscription extends Subscription {
536
540
  }
537
541
 
538
542
  this.#sendDelayTimer.start();
539
- this.#updateTimer = Time.getTimer("Subscription update", this.#sendIntervalMs, () =>
543
+ this.#updateTimer = Time.getTimer(`Subscription update ${this.id}`, this.#sendIntervalMs, () =>
540
544
  this.#prepareDataUpdate(),
541
545
  ).start();
542
546
  }
@@ -547,19 +551,16 @@ export class ServerSubscription extends Subscription {
547
551
  this.sendNextUpdateImmediately = true;
548
552
  return;
549
553
  }
550
- const { promise, resolver } = createPromise<void>();
551
- this.#sendUpdate()
552
- .then(resolver)
554
+ this.currentUpdatePromise = this.#sendUpdate()
553
555
  .catch(error => logger.warn("Sending subscription update failed:", error))
554
556
  .finally(() => (this.currentUpdatePromise = undefined));
555
- this.currentUpdatePromise = promise;
556
557
  }
557
558
 
558
559
  /**
559
560
  * Determine all attributes that have changed since the last update and send them tout to the subscriber.
560
561
  * Important: This method MUST NOT be called directly. Use triggerSendUpdate() instead!
561
562
  */
562
- async #sendUpdate() {
563
+ async #sendUpdate(onlyWithData = false) {
563
564
  // Get all outstanding updates, make sure the order is correct per endpoint and cluster
564
565
  const attributeUpdatesToSend = new Array<AttributePathWithValueVersion<any>>();
565
566
  const attributeUpdates: Record<string, AttributePathWithValueVersion<any>[]> = {};
@@ -580,6 +581,11 @@ export class ServerSubscription extends Subscription {
580
581
 
581
582
  const eventUpdatesToSend = Array.from(this.#outstandingEventUpdates.values());
582
583
  this.#outstandingEventUpdates.clear();
584
+
585
+ if (onlyWithData && attributeUpdatesToSend.length === 0 && eventUpdatesToSend.length === 0) {
586
+ return;
587
+ }
588
+
583
589
  this.#lastUpdateTimeMs = Time.nowMs();
584
590
 
585
591
  try {
@@ -629,7 +635,7 @@ export class ServerSubscription extends Subscription {
629
635
  if (this.sendNextUpdateImmediately) {
630
636
  logger.debug("Sending delayed update immediately after last one was sent.");
631
637
  this.sendNextUpdateImmediately = false;
632
- await this.#sendUpdate();
638
+ await this.#sendUpdate(true); // Send but only if non-empty
633
639
  }
634
640
  }
635
641
 
@@ -836,7 +842,7 @@ export class ServerSubscription extends Subscription {
836
842
  this.#sendDelayTimer.stop();
837
843
  if (this.#outstandingAttributeUpdates.size > 0 || this.#outstandingEventUpdates.size > 0) {
838
844
  logger.debug(
839
- `Flushing subscription ${this.id} with ${this.#outstandingAttributeUpdates.size} attributes and ${this.#outstandingEventUpdates.size} events`,
845
+ `Flushing subscription ${this.id} with ${this.#outstandingAttributeUpdates.size} attributes and ${this.#outstandingEventUpdates.size} events${this.isClosed ? " (for closing)" : ""}`,
840
846
  );
841
847
  this.#triggerSendUpdate();
842
848
  if (this.currentUpdatePromise) {
@@ -853,7 +859,9 @@ export class ServerSubscription extends Subscription {
853
859
  if (this.attributeUpdatePromises.size) {
854
860
  const resolvers = [...this.attributeUpdatePromises.values()];
855
861
  this.attributeUpdatePromises.clear();
856
- await Promise.all(resolvers);
862
+ await MatterAggregateError.allSettled(resolvers, "Error receiving all outstanding attribute values").catch(
863
+ error => logger.error(error),
864
+ );
857
865
  }
858
866
  this.#updateTimer.stop();
859
867
  this.#sendDelayTimer.stop();
@@ -870,9 +878,6 @@ export class ServerSubscription extends Subscription {
870
878
  attributes: AttributePathWithValueVersion<any>[],
871
879
  events: EventPathWithEventData<any>[],
872
880
  ) {
873
- logger.debug(
874
- `Sending subscription update message for ID ${this.id} with ${attributes.length} attributes and ${events.length} events`,
875
- );
876
881
  const exchange = this.#context.initiateExchange(this.peerAddress, INTERACTION_PROTOCOL_ID);
877
882
  if (exchange === undefined) return;
878
883
  if (attributes.length) {
@@ -896,11 +901,12 @@ export class ServerSubscription extends Subscription {
896
901
  interactionModelRevision: Specification.INTERACTION_MODEL_REVISION,
897
902
  },
898
903
  this.criteria.isFabricFiltered,
904
+ !this.isClosed, // Do not wait for ack when closed
899
905
  );
900
906
  } else {
901
907
  await messenger.sendDataReport(
902
908
  {
903
- suppressResponse: false, // Non empty data reports always need to send response
909
+ suppressResponse: false, // Non-empty data reports always need to send response
904
910
  subscriptionId: this.id,
905
911
  interactionModelRevision: Specification.INTERACTION_MODEL_REVISION,
906
912
  attributeReportsPayload: attributes.map(({ path, schema, value, version, attribute }) => ({
@@ -928,6 +934,7 @@ export class ServerSubscription extends Subscription {
928
934
  }),
929
935
  },
930
936
  this.criteria.isFabricFiltered,
937
+ !this.isClosed, // Do not wait for ack when closed
931
938
  );
932
939
  }
933
940
  } catch (error) {
@@ -1206,27 +1206,30 @@ export class MdnsScanner implements Scanner {
1206
1206
  }
1207
1207
 
1208
1208
  static discoveryDataDiagnostics(data: DiscoveryData) {
1209
- return Diagnostic.dict({
1210
- SII: data.SII,
1211
- SAI: data.SAI,
1212
- SAT: data.SAT,
1213
- T: data.T,
1214
- DT: data.DT,
1215
- PH: data.PH,
1216
- ICD: data.ICD,
1217
- VP: data.VP,
1218
- DN: data.DN,
1219
- RI: data.RI,
1220
- PI: data.PI,
1221
- });
1209
+ return Diagnostic.dict(
1210
+ {
1211
+ SII: data.SII,
1212
+ SAI: data.SAI,
1213
+ SAT: data.SAT,
1214
+ T: data.T,
1215
+ DT: data.DT,
1216
+ PH: data.PH,
1217
+ ICD: data.ICD,
1218
+ VP: data.VP,
1219
+ DN: data.DN,
1220
+ RI: data.RI,
1221
+ PI: data.PI,
1222
+ },
1223
+ true,
1224
+ );
1222
1225
  }
1223
1226
 
1224
1227
  static deviceAddressDiagnostics(addresses: Map<string, MatterServerRecordWithExpire>) {
1225
1228
  return Array.from(addresses.values()).map(address =>
1226
1229
  Diagnostic.dict({
1230
+ type: address.type,
1227
1231
  ip: address.ip,
1228
1232
  port: address.port,
1229
- type: address.type,
1230
1233
  }),
1231
1234
  );
1232
1235
  }
@@ -15,6 +15,7 @@ import {
15
15
  DnsRecordType,
16
16
  isDeepEqual,
17
17
  Logger,
18
+ MatterAggregateError,
18
19
  MAX_MDNS_MESSAGE_SIZE,
19
20
  Network,
20
21
  Time,
@@ -252,7 +253,7 @@ export class MdnsServer {
252
253
  }
253
254
 
254
255
  async announce(announcedNetPort?: number) {
255
- await Promise.all(
256
+ await MatterAggregateError.allSettled(
256
257
  (await this.#getMulticastInterfacesForAnnounce()).map(async ({ name: netInterface }) => {
257
258
  const records = await this.#records.get(netInterface);
258
259
  for (const [portType, portTypeRecords] of records) {
@@ -263,11 +264,12 @@ export class MdnsServer {
263
264
  await Time.sleep("MDNS delay", 20 + Math.floor(Math.random() * 100)); // as per DNS-SD spec wait 20-120ms before sending more packets
264
265
  }
265
266
  }),
266
- );
267
+ "Error happened when announcing MDNS messages",
268
+ ).catch(error => logger.error(error));
267
269
  }
268
270
 
269
271
  async expireAnnouncements(announcedNetPort?: number, type?: AnnouncementType) {
270
- await Promise.all(
272
+ await MatterAggregateError.allSettled(
271
273
  this.#records.keys().map(async netInterface => {
272
274
  const records = await this.#records.get(netInterface);
273
275
  for (const [portType, portTypeRecords] of records) {
@@ -300,7 +302,8 @@ export class MdnsServer {
300
302
  await Time.sleep("MDNS delay", 20 + Math.floor(Math.random() * 100)); // as per DNS-SD spec wait 20-120ms before sending more packets
301
303
  }
302
304
  }),
303
- );
305
+ "Error happened when expiring MDNS announcements",
306
+ ).catch(error => logger.error(error));
304
307
  await this.#records.clear();
305
308
  this.#recordLastSentAsMulticastAnswer.clear();
306
309
  this.#recordLastSentAsUnicastAnswer.clear();
@@ -10,6 +10,7 @@ import {
10
10
  Environment,
11
11
  Environmental,
12
12
  Logger,
13
+ MatterAggregateError,
13
14
  MaybePromise,
14
15
  Network,
15
16
  VariableService,
@@ -92,7 +93,10 @@ export class MdnsService {
92
93
  logger.error("Error disposing of MDNS scanner", e),
93
94
  );
94
95
 
95
- await Promise.all([broadcasterDisposal, scannerDisposal]);
96
+ await MatterAggregateError.allSettled(
97
+ [broadcasterDisposal, scannerDisposal],
98
+ "Error disposing MDNS services",
99
+ ).catch(error => logger.error(error));
96
100
 
97
101
  this.#broadcaster = this.#scanner = undefined;
98
102
  });
@@ -208,13 +208,11 @@ export class DeviceAdvertiser {
208
208
 
209
209
  async clearBroadcasters() {
210
210
  const broadcasters = [...this.#broadcasters];
211
- const closed = Promise.allSettled(broadcasters.map(b => b.close()));
211
+ const closed = MatterAggregateError.allSettled(
212
+ broadcasters.map(b => b.close()),
213
+ "Error closing broadcasters",
214
+ ).catch(error => logger.error(error));
212
215
  this.#broadcasters.clear();
213
- const errors = (await closed)
214
- .map(status => (status.status === "rejected" ? status.reason : undefined))
215
- .filter(reason => reason !== undefined);
216
- if (errors.length) {
217
- throw new MatterAggregateError(errors, "Error closing broadcasters");
218
- }
216
+ await closed;
219
217
  }
220
218
  }
@@ -11,6 +11,7 @@ import {
11
11
  Environmental,
12
12
  ImplementationError,
13
13
  Logger,
14
+ MatterAggregateError,
14
15
  MatterError,
15
16
  MatterFlowError,
16
17
  NotImplementedError,
@@ -27,7 +28,7 @@ import { SecureSession } from "../session/SecureSession.js";
27
28
  import { Session } from "../session/Session.js";
28
29
  import { SessionManager, UNICAST_UNSECURE_SESSION_ID } from "../session/SessionManager.js";
29
30
  import { ChannelManager } from "./ChannelManager.js";
30
- import { MessageExchange, MessageExchangeContext } from "./MessageExchange.js";
31
+ import { ExchangeLogContext, MessageExchange, MessageExchangeContext } from "./MessageExchange.js";
31
32
  import { DuplicateMessageError } from "./MessageReceptionState.js";
32
33
  import { ProtocolHandler } from "./ProtocolHandler.js";
33
34
 
@@ -70,8 +71,8 @@ export class MessageChannel implements Channel<Message> {
70
71
  return this.channel.maxPayloadSize;
71
72
  }
72
73
 
73
- send(message: Message): Promise<void> {
74
- logger.debug("Message »", MessageCodec.messageDiagnostics(message));
74
+ send(message: Message, logContext?: ExchangeLogContext): Promise<void> {
75
+ logger.debug("Message »", MessageCodec.messageDiagnostics(message, logContext));
75
76
  const packet = this.session.encode(message);
76
77
  const bytes = MessageCodec.encodePacket(packet);
77
78
  if (bytes.length > this.maxPayloadSize) {
@@ -181,10 +182,12 @@ export class ExchangeManager {
181
182
  for (const listeners of this.#listeners.keys()) {
182
183
  this.#deleteListener(listeners);
183
184
  }
184
- await Promise.allSettled(this.#closers);
185
- for (const exchange of this.#exchanges.values()) {
186
- await exchange.destroy();
187
- }
185
+ await MatterAggregateError.allSettled(this.#closers, "Error closing exchanges").catch(error =>
186
+ logger.error(error),
187
+ );
188
+ await MatterAggregateError.allSettled(this.#exchanges.values(), "Error closing exchanges").catch(error =>
189
+ logger.error(error),
190
+ );
188
191
  this.#exchanges.clear();
189
192
  }
190
193