@matter/protocol 0.13.0-alpha.0-20250318-c1aa38b08 → 0.13.0-alpha.0-20250322-f085fa576

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 (111) hide show
  1. package/dist/cjs/certificate/CertificateAuthority.d.ts +1 -0
  2. package/dist/cjs/certificate/CertificateAuthority.d.ts.map +1 -1
  3. package/dist/cjs/certificate/CertificateAuthority.js.map +1 -1
  4. package/dist/cjs/cluster/client/ClusterClient.d.ts.map +1 -1
  5. package/dist/cjs/cluster/client/ClusterClient.js +3 -0
  6. package/dist/cjs/cluster/client/ClusterClient.js.map +1 -1
  7. package/dist/cjs/common/FailsafeContext.js +1 -1
  8. package/dist/cjs/common/FailsafeContext.js.map +1 -1
  9. package/dist/cjs/interaction/AttributeDataDecoder.d.ts +21 -4
  10. package/dist/cjs/interaction/AttributeDataDecoder.d.ts.map +1 -1
  11. package/dist/cjs/interaction/AttributeDataDecoder.js +40 -1
  12. package/dist/cjs/interaction/AttributeDataDecoder.js.map +1 -1
  13. package/dist/cjs/interaction/DecodedDataReport.d.ts +4 -2
  14. package/dist/cjs/interaction/DecodedDataReport.d.ts.map +1 -1
  15. package/dist/cjs/interaction/DecodedDataReport.js +6 -2
  16. package/dist/cjs/interaction/DecodedDataReport.js.map +1 -1
  17. package/dist/cjs/interaction/EventDataDecoder.d.ts +15 -3
  18. package/dist/cjs/interaction/EventDataDecoder.d.ts.map +1 -1
  19. package/dist/cjs/interaction/EventDataDecoder.js +39 -2
  20. package/dist/cjs/interaction/EventDataDecoder.js.map +1 -1
  21. package/dist/cjs/interaction/InteractionClient.d.ts +44 -4
  22. package/dist/cjs/interaction/InteractionClient.d.ts.map +1 -1
  23. package/dist/cjs/interaction/InteractionClient.js +65 -12
  24. package/dist/cjs/interaction/InteractionClient.js.map +1 -1
  25. package/dist/cjs/interaction/InteractionMessenger.d.ts.map +1 -1
  26. package/dist/cjs/interaction/InteractionMessenger.js +5 -2
  27. package/dist/cjs/interaction/InteractionMessenger.js.map +1 -1
  28. package/dist/cjs/interaction/InteractionServer.d.ts.map +1 -1
  29. package/dist/cjs/interaction/InteractionServer.js +2 -2
  30. package/dist/cjs/interaction/InteractionServer.js.map +1 -1
  31. package/dist/cjs/peer/ControllerCommissioner.d.ts +15 -3
  32. package/dist/cjs/peer/ControllerCommissioner.d.ts.map +1 -1
  33. package/dist/cjs/peer/ControllerCommissioner.js +17 -6
  34. package/dist/cjs/peer/ControllerCommissioner.js.map +1 -1
  35. package/dist/cjs/peer/ControllerCommissioningFlow.d.ts +47 -1
  36. package/dist/cjs/peer/ControllerCommissioningFlow.d.ts.map +1 -1
  37. package/dist/cjs/peer/ControllerCommissioningFlow.js +133 -133
  38. package/dist/cjs/peer/ControllerCommissioningFlow.js.map +1 -1
  39. package/dist/cjs/peer/PeerSet.d.ts +2 -2
  40. package/dist/cjs/peer/PeerSet.d.ts.map +1 -1
  41. package/dist/cjs/peer/PeerSet.js +6 -1
  42. package/dist/cjs/peer/PeerSet.js.map +1 -1
  43. package/dist/cjs/protocol/ChannelManager.d.ts.map +1 -1
  44. package/dist/cjs/protocol/ChannelManager.js +11 -4
  45. package/dist/cjs/protocol/ChannelManager.js.map +1 -1
  46. package/dist/cjs/protocol/ExchangeManager.d.ts.map +1 -1
  47. package/dist/cjs/protocol/ExchangeManager.js +0 -1
  48. package/dist/cjs/protocol/ExchangeManager.js.map +1 -1
  49. package/dist/esm/certificate/CertificateAuthority.d.ts +1 -0
  50. package/dist/esm/certificate/CertificateAuthority.d.ts.map +1 -1
  51. package/dist/esm/certificate/CertificateAuthority.js.map +1 -1
  52. package/dist/esm/cluster/client/ClusterClient.d.ts.map +1 -1
  53. package/dist/esm/cluster/client/ClusterClient.js +3 -0
  54. package/dist/esm/cluster/client/ClusterClient.js.map +1 -1
  55. package/dist/esm/common/FailsafeContext.js +1 -1
  56. package/dist/esm/common/FailsafeContext.js.map +1 -1
  57. package/dist/esm/interaction/AttributeDataDecoder.d.ts +21 -4
  58. package/dist/esm/interaction/AttributeDataDecoder.d.ts.map +1 -1
  59. package/dist/esm/interaction/AttributeDataDecoder.js +40 -1
  60. package/dist/esm/interaction/AttributeDataDecoder.js.map +1 -1
  61. package/dist/esm/interaction/DecodedDataReport.d.ts +4 -2
  62. package/dist/esm/interaction/DecodedDataReport.d.ts.map +1 -1
  63. package/dist/esm/interaction/DecodedDataReport.js +12 -4
  64. package/dist/esm/interaction/DecodedDataReport.js.map +1 -1
  65. package/dist/esm/interaction/EventDataDecoder.d.ts +15 -3
  66. package/dist/esm/interaction/EventDataDecoder.d.ts.map +1 -1
  67. package/dist/esm/interaction/EventDataDecoder.js +39 -2
  68. package/dist/esm/interaction/EventDataDecoder.js.map +1 -1
  69. package/dist/esm/interaction/InteractionClient.d.ts +44 -4
  70. package/dist/esm/interaction/InteractionClient.d.ts.map +1 -1
  71. package/dist/esm/interaction/InteractionClient.js +67 -12
  72. package/dist/esm/interaction/InteractionClient.js.map +1 -1
  73. package/dist/esm/interaction/InteractionMessenger.d.ts.map +1 -1
  74. package/dist/esm/interaction/InteractionMessenger.js +6 -2
  75. package/dist/esm/interaction/InteractionMessenger.js.map +1 -1
  76. package/dist/esm/interaction/InteractionServer.d.ts.map +1 -1
  77. package/dist/esm/interaction/InteractionServer.js +12 -3
  78. package/dist/esm/interaction/InteractionServer.js.map +1 -1
  79. package/dist/esm/peer/ControllerCommissioner.d.ts +15 -3
  80. package/dist/esm/peer/ControllerCommissioner.d.ts.map +1 -1
  81. package/dist/esm/peer/ControllerCommissioner.js +17 -6
  82. package/dist/esm/peer/ControllerCommissioner.js.map +1 -1
  83. package/dist/esm/peer/ControllerCommissioningFlow.d.ts +47 -1
  84. package/dist/esm/peer/ControllerCommissioningFlow.d.ts.map +1 -1
  85. package/dist/esm/peer/ControllerCommissioningFlow.js +133 -133
  86. package/dist/esm/peer/ControllerCommissioningFlow.js.map +1 -1
  87. package/dist/esm/peer/PeerSet.d.ts +2 -2
  88. package/dist/esm/peer/PeerSet.d.ts.map +1 -1
  89. package/dist/esm/peer/PeerSet.js +7 -2
  90. package/dist/esm/peer/PeerSet.js.map +1 -1
  91. package/dist/esm/protocol/ChannelManager.d.ts.map +1 -1
  92. package/dist/esm/protocol/ChannelManager.js +11 -4
  93. package/dist/esm/protocol/ChannelManager.js.map +1 -1
  94. package/dist/esm/protocol/ExchangeManager.d.ts.map +1 -1
  95. package/dist/esm/protocol/ExchangeManager.js +0 -1
  96. package/dist/esm/protocol/ExchangeManager.js.map +1 -1
  97. package/package.json +6 -6
  98. package/src/certificate/CertificateAuthority.ts +1 -0
  99. package/src/cluster/client/ClusterClient.ts +3 -0
  100. package/src/common/FailsafeContext.ts +1 -1
  101. package/src/interaction/AttributeDataDecoder.ts +66 -6
  102. package/src/interaction/DecodedDataReport.ts +24 -5
  103. package/src/interaction/EventDataDecoder.ts +57 -5
  104. package/src/interaction/InteractionClient.ts +105 -12
  105. package/src/interaction/InteractionMessenger.ts +6 -2
  106. package/src/interaction/InteractionServer.ts +14 -4
  107. package/src/peer/ControllerCommissioner.ts +30 -6
  108. package/src/peer/ControllerCommissioningFlow.ts +144 -140
  109. package/src/peer/PeerSet.ts +7 -2
  110. package/src/protocol/ChannelManager.ts +11 -4
  111. package/src/protocol/ExchangeManager.ts +0 -1
@@ -14,9 +14,11 @@ import {
14
14
  getClusterById,
15
15
  getClusterEventById,
16
16
  NodeId,
17
+ Status,
17
18
  TlvAny,
18
19
  TlvEventData,
19
20
  TlvEventReport,
21
+ TlvEventStatus,
20
22
  TlvStream,
21
23
  TypeFromSchema,
22
24
  } from "#types";
@@ -33,7 +35,7 @@ export type DecodedEventData<T> = {
33
35
  data?: T;
34
36
  };
35
37
 
36
- export type DecodedEventReportValue<T> = {
38
+ export type DecodedEventReportEntry = {
37
39
  path: {
38
40
  nodeId?: NodeId;
39
41
  endpointId: EndpointNumber;
@@ -41,16 +43,32 @@ export type DecodedEventReportValue<T> = {
41
43
  eventId: EventId;
42
44
  eventName: string;
43
45
  };
46
+ };
47
+
48
+ /** Represents a fully qualified and decoded attribute value from a received DataReport */
49
+ export type DecodedEventReportValue<T> = DecodedEventReportEntry & {
44
50
  events: DecodedEventData<T>[];
45
51
  };
46
52
 
47
- export function normalizeAndDecodeReadEventReport(
48
- data: TypeFromSchema<typeof TlvEventReport>[],
49
- ): DecodedEventReportValue<any>[] {
53
+ /** Represents a fully qualified and decoded attribute status from a received DataReport */
54
+ export type DecodedEventReportStatus = DecodedEventReportEntry & {
55
+ status?: Status;
56
+ clusterStatus?: number;
57
+ };
58
+
59
+ // TODO: Convert into a Generator function once we migrate Reading Data for controller to also be streaming
60
+ export function normalizeAndDecodeReadEventReport(data: TypeFromSchema<typeof TlvEventReport>[]): {
61
+ eventData: DecodedEventReportValue<any>[];
62
+ eventStatus: DecodedEventReportStatus[];
63
+ } {
50
64
  // TODO Decide how to handle the attribute report status field, right now we ignore it
51
65
  const dataValues = data.flatMap(({ eventData }) => (eventData !== undefined ? eventData : []));
66
+ const dataStatus = data.flatMap(({ eventStatus }) => (eventStatus !== undefined ? eventStatus : []));
52
67
 
53
- return normalizeAndDecodeEventData(dataValues);
68
+ return {
69
+ eventData: normalizeAndDecodeEventData(dataValues),
70
+ eventStatus: normalizeEventStatus(dataStatus),
71
+ };
54
72
  }
55
73
 
56
74
  export function normalizeEventData(
@@ -121,6 +139,40 @@ export function normalizeAndDecodeEventData(
121
139
  return result;
122
140
  }
123
141
 
142
+ export function normalizeEventStatus(data: TypeFromSchema<typeof TlvEventStatus>[]): DecodedEventReportStatus[] {
143
+ const result = new Array<DecodedEventReportStatus>();
144
+ data.forEach(entry => {
145
+ const {
146
+ path: { nodeId, endpointId, clusterId, eventId },
147
+ status,
148
+ } = entry;
149
+
150
+ if (endpointId === undefined || clusterId === undefined || eventId === undefined) {
151
+ throw new UnexpectedDataError(`Invalid event path ${endpointId}/${clusterId}/${eventId}`);
152
+ }
153
+ try {
154
+ const cluster = getClusterById(clusterId);
155
+ const eventDetail = getClusterEventById(cluster, eventId);
156
+ if (eventDetail === undefined) {
157
+ result.push({
158
+ path: { nodeId, endpointId, clusterId, eventId, eventName: `Unknown (${Diagnostic.hex(eventId)})` },
159
+ status: status.status,
160
+ clusterStatus: status.clusterStatus,
161
+ });
162
+ return;
163
+ }
164
+ result.push({
165
+ path: { nodeId, endpointId, clusterId, eventId, eventName: eventDetail.name },
166
+ status: status.status,
167
+ clusterStatus: status.clusterStatus,
168
+ });
169
+ } catch (error: any) {
170
+ logger.error(`Error decoding event ${endpointId}/${clusterId}/${eventId}: ${error.message}`);
171
+ }
172
+ });
173
+ return result;
174
+ }
175
+
124
176
  export function decodeUnknownEventValue(data: TlvStream): any {
125
177
  const schema = TlvAny;
126
178
 
@@ -31,7 +31,9 @@ import {
31
31
  Event,
32
32
  EventId,
33
33
  EventNumber,
34
+ FabricIndex,
34
35
  NodeId,
36
+ ObjectSchema,
35
37
  RequestType,
36
38
  ResponseType,
37
39
  StatusCode,
@@ -46,10 +48,11 @@ import {
46
48
  resolveCommandName,
47
49
  resolveEventName,
48
50
  } from "#types";
51
+ import { MessageChannel } from "../protocol/ExchangeManager.js";
49
52
  import { ExchangeProvider, ReconnectableExchangeProvider } from "../protocol/ExchangeProvider.js";
50
- import { DecodedAttributeReportValue } from "./AttributeDataDecoder.js";
53
+ import { DecodedAttributeReportStatus, DecodedAttributeReportValue } from "./AttributeDataDecoder.js";
51
54
  import { DecodedDataReport } from "./DecodedDataReport.js";
52
- import { DecodedEventData, DecodedEventReportValue } from "./EventDataDecoder.js";
55
+ import { DecodedEventData, DecodedEventReportStatus, DecodedEventReportValue } from "./EventDataDecoder.js";
53
56
  import { DataReport, InteractionClientMessenger, ReadRequest } from "./InteractionMessenger.js";
54
57
  import { RegisteredSubscription, SubscriptionClient } from "./SubscriptionClient.js";
55
58
 
@@ -103,6 +106,17 @@ export class InteractionClientProvider {
103
106
  return this.getInteractionClient(address, discoveryOptions);
104
107
  }
105
108
 
109
+ async getInteractionClientForChannel(channel: MessageChannel): Promise<InteractionClient> {
110
+ const exchangeProvider = await this.#peers.exchangeProviderFor(channel);
111
+
112
+ return new InteractionClient(
113
+ exchangeProvider,
114
+ this.#peers.subscriptionClient,
115
+ undefined,
116
+ this.#peers.interactionQueue,
117
+ );
118
+ }
119
+
106
120
  async getInteractionClient(address: PeerAddress, discoveryOptions: DiscoveryOptions) {
107
121
  let client = this.#clients.get(address);
108
122
  if (client !== undefined) {
@@ -141,11 +155,12 @@ export class InteractionClient {
141
155
  readonly #ownSubscriptionIds = new Set<number>();
142
156
  readonly #subscriptionClient: SubscriptionClient;
143
157
  readonly #queue?: PromiseQueue;
158
+ readonly #address?: PeerAddress;
144
159
 
145
160
  constructor(
146
161
  exchangeProvider: ExchangeProvider,
147
162
  subscriptionClient: SubscriptionClient,
148
- readonly address: PeerAddress,
163
+ address?: PeerAddress,
149
164
  queue?: PromiseQueue,
150
165
  nodeStore?: PeerDataStore,
151
166
  ) {
@@ -153,6 +168,18 @@ export class InteractionClient {
153
168
  this.#nodeStore = nodeStore;
154
169
  this.#subscriptionClient = subscriptionClient;
155
170
  this.#queue = queue;
171
+ this.#address = address;
172
+ }
173
+
174
+ get address() {
175
+ if (this.#address === undefined) {
176
+ throw new ImplementationError("This InteractionClient is not bound to a specific peer.");
177
+ }
178
+ return this.#address;
179
+ }
180
+
181
+ get isReconnectable() {
182
+ return this.#exchangeProvider instanceof ReconnectableExchangeProvider;
156
183
  }
157
184
 
158
185
  get channelUpdated() {
@@ -233,6 +260,22 @@ export class InteractionClient {
233
260
  return (await this.getMultipleAttributesAndEvents(options)).attributeReports;
234
261
  }
235
262
 
263
+ async getMultipleAttributesAndStatus(
264
+ options: {
265
+ attributes?: { endpointId?: EndpointNumber; clusterId?: ClusterId; attributeId?: AttributeId }[];
266
+ dataVersionFilters?: { endpointId: EndpointNumber; clusterId: ClusterId; dataVersion: number }[];
267
+ enrichCachedAttributeData?: boolean;
268
+ isFabricFiltered?: boolean;
269
+ executeQueued?: boolean;
270
+ } = {},
271
+ ): Promise<{
272
+ attributeData: DecodedAttributeReportValue<any>[];
273
+ attributeStatus?: DecodedAttributeReportStatus[];
274
+ }> {
275
+ const { attributeReports, attributeStatus } = await this.getMultipleAttributesAndEvents(options);
276
+ return { attributeData: attributeReports, attributeStatus };
277
+ }
278
+
236
279
  async getMultipleEvents(
237
280
  options: {
238
281
  events?: { endpointId?: EndpointNumber; clusterId?: ClusterId; eventId?: EventId }[];
@@ -244,6 +287,18 @@ export class InteractionClient {
244
287
  return (await this.getMultipleAttributesAndEvents(options)).eventReports;
245
288
  }
246
289
 
290
+ async getMultipleEventsAndStatus(
291
+ options: {
292
+ events?: { endpointId?: EndpointNumber; clusterId?: ClusterId; eventId?: EventId }[];
293
+ eventFilters?: TypeFromSchema<typeof TlvEventFilter>[];
294
+ isFabricFiltered?: boolean;
295
+ executeQueued?: boolean;
296
+ } = {},
297
+ ): Promise<{ eventData: DecodedEventReportValue<any>[]; eventStatus?: DecodedEventReportStatus[] }> {
298
+ const { eventReports, eventStatus } = await this.getMultipleAttributesAndEvents(options);
299
+ return { eventData: eventReports, eventStatus };
300
+ }
301
+
247
302
  async getMultipleAttributesAndEvents(
248
303
  options: {
249
304
  attributes?: { endpointId?: EndpointNumber; clusterId?: ClusterId; attributeId?: AttributeId }[];
@@ -405,12 +460,23 @@ export class InteractionClient {
405
460
 
406
461
  // Normalize and decode the response
407
462
  const normalizedResult = DecodedDataReport(response);
463
+ const { attributeReports, attributeStatus, eventReports, eventStatus } = normalizedResult;
464
+
465
+ const logData = Array<string>();
466
+ if (attributeReports.length > 0) {
467
+ logData.push(`attributes ${attributeReports.map(({ path }) => resolveAttributeName(path)).join(", ")}`);
468
+ }
469
+ if (eventReports.length > 0) {
470
+ logData.push(`events ${eventReports.map(({ path }) => resolveEventName(path)).join(", ")}`);
471
+ }
472
+ if (attributeStatus !== undefined && attributeStatus.length > 0) {
473
+ logData.push(`attributeErrors ${attributeStatus.map(({ path }) => resolveAttributeName(path)).join(", ")}`);
474
+ }
475
+ if (eventStatus !== undefined && eventStatus.length > 0) {
476
+ logData.push(`eventErrors ${eventStatus.map(({ path }) => resolveEventName(path)).join(", ")}`);
477
+ }
408
478
  logger.debug(
409
- `Received read response with attributes ${normalizedResult.attributeReports
410
- .map(({ path, value }) => `${resolveAttributeName(path)} = ${Logger.toJSON(value)}`)
411
- .join(", ")} and events ${normalizedResult.eventReports
412
- .map(({ path, events }) => `${resolveEventName(path)} = ${Logger.toJSON(events)}`)
413
- .join(", ")}`,
479
+ logData.length ? `Received read response with ${logData.join(", ")}` : "Received empty read response",
414
480
  );
415
481
  return normalizedResult;
416
482
  }
@@ -893,11 +959,11 @@ export class InteractionClient {
893
959
  eventReports?: DecodedEventReportValue<any>[];
894
960
  subscriptionId?: number;
895
961
  }) => {
896
- updateReceived?.();
897
962
  if (
898
963
  (!Array.isArray(dataReport.attributeReports) || !dataReport.attributeReports.length) &&
899
964
  (!Array.isArray(dataReport.eventReports) || !dataReport.eventReports.length)
900
965
  ) {
966
+ updateReceived?.();
901
967
  return;
902
968
  }
903
969
  const { attributeReports, eventReports } = dataReport;
@@ -948,6 +1014,7 @@ export class InteractionClient {
948
1014
  attributeListener?.(data, changed, oldValue);
949
1015
  }
950
1016
  }
1017
+ updateReceived?.();
951
1018
  };
952
1019
 
953
1020
  const seedReport = DecodedDataReport(report);
@@ -977,23 +1044,47 @@ export class InteractionClient {
977
1044
  clusterId: ClusterId;
978
1045
  request: RequestType<C>;
979
1046
  command: C;
1047
+
1048
+ /** Send as timed request. If no timedRequestTimeoutMs is provided the default of 10s will be used. */
980
1049
  asTimedRequest?: boolean;
1050
+
1051
+ /** Use this timeout and send the request as Timed Request. If this is specified the above parameter is implied. */
981
1052
  timedRequestTimeoutMs?: number;
1053
+
1054
+ /** Use an extended Message Response Timeout as defined for FailSafe cases which is 30s. */
982
1055
  useExtendedFailSafeMessageResponseTimeout?: boolean;
1056
+
1057
+ /** Execute this request queued - mainly used to execute invokes sequentially for thread devices. */
983
1058
  executeQueued?: boolean;
1059
+
1060
+ /** Skip request data validation. Use this only when you know that your data is correct and validation would return an error. */
1061
+ skipValidation?: boolean;
984
1062
  }): Promise<ResponseType<C>> {
985
1063
  const { executeQueued } = options;
986
1064
 
987
1065
  const {
988
1066
  endpointId,
989
1067
  clusterId,
990
- request,
991
1068
  command: { requestId, requestSchema, responseId, responseSchema, optional, timed },
992
1069
  asTimedRequest,
993
1070
  timedRequestTimeoutMs = DEFAULT_TIMED_REQUEST_TIMEOUT_MS,
994
1071
  useExtendedFailSafeMessageResponseTimeout = false,
1072
+ skipValidation,
995
1073
  } = options;
996
- const timedRequest = timed || asTimedRequest === true || options.timedRequestTimeoutMs !== undefined;
1074
+ let { request } = options;
1075
+ const timedRequest =
1076
+ (timed && !skipValidation) || asTimedRequest === true || options.timedRequestTimeoutMs !== undefined;
1077
+
1078
+ if (requestSchema instanceof ObjectSchema) {
1079
+ if (request === undefined) {
1080
+ // If developer did not provide a request object, create an empty one if it needs to be an object
1081
+ // This can happen when all object properties are optional
1082
+ request = {} as RequestType<C>;
1083
+ }
1084
+ if (requestSchema.isFabricScoped && request.fabricIndex === undefined) {
1085
+ request.fabricIndex = FabricIndex.NO_FABRIC;
1086
+ }
1087
+ }
997
1088
 
998
1089
  logger.debug(
999
1090
  `Invoking command: ${resolveCommandName({
@@ -1003,7 +1094,9 @@ export class InteractionClient {
1003
1094
  })} with ${Logger.toJSON(request)}`,
1004
1095
  );
1005
1096
 
1006
- requestSchema.validate(request);
1097
+ if (!skipValidation) {
1098
+ requestSchema.validate(request);
1099
+ }
1007
1100
 
1008
1101
  const commandFields = requestSchema.encodeTlv(request);
1009
1102
 
@@ -14,6 +14,7 @@ import {
14
14
  } from "#general";
15
15
  import { Specification } from "#model";
16
16
  import {
17
+ ReceivedStatusResponseError,
17
18
  Status,
18
19
  StatusCode,
19
20
  StatusResponseError,
@@ -184,7 +185,10 @@ class InteractionMessenger {
184
185
  if (messageType !== MessageType.StatusResponse) return;
185
186
  const { status } = TlvStatusResponse.decode(payload);
186
187
  if (status !== StatusCode.Success)
187
- throw new StatusResponseError(`Received error status: ${status}${logHint ? ` (${logHint})` : ""}`, status);
188
+ throw new ReceivedStatusResponseError(
189
+ `Received error status: ${status}${logHint ? ` (${logHint})` : ""}`,
190
+ status,
191
+ );
188
192
  }
189
193
 
190
194
  getExchangeChannelName() {
@@ -680,7 +684,7 @@ export class IncomingInteractionClientMessenger extends InteractionMessenger {
680
684
  if (receivedMessageType !== messageType) {
681
685
  if (receivedMessageType === MessageType.StatusResponse) {
682
686
  const statusCode = TlvStatusResponse.decode(message.payload).status;
683
- throw new StatusResponseError(`Received status response ${statusCode}`, statusCode);
687
+ throw new ReceivedStatusResponseError(`Received status response ${statusCode}`, statusCode);
684
688
  }
685
689
  throw new MatterFlowError(
686
690
  `Received unexpected message type ${receivedMessageType.toString(16)}. Expected ${messageType.toString(
@@ -4,7 +4,16 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
- import { Crypto, Diagnostic, InternalError, Logger, MatterFlowError, Observable, ServerAddressIp } from "#general";
7
+ import {
8
+ Crypto,
9
+ Diagnostic,
10
+ InternalError,
11
+ Logger,
12
+ MatterError,
13
+ MatterFlowError,
14
+ Observable,
15
+ ServerAddressIp,
16
+ } from "#general";
8
17
  import { AttributeModel, ClusterModel, CommandModel, GLOBAL_IDS, MatterModel, Specification } from "#model";
9
18
  import { PeerAddress } from "#peer/PeerAddress.js";
10
19
  import { SessionManager } from "#session/SessionManager.js";
@@ -19,6 +28,7 @@ import {
19
28
  EventNumber,
20
29
  INTERACTION_PROTOCOL_ID,
21
30
  NodeId,
31
+ ReceivedStatusResponseError,
22
32
  StatusCode,
23
33
  StatusResponseError,
24
34
  TlvAny,
@@ -1115,12 +1125,12 @@ export class InteractionServer implements ProtocolHandler, InteractionRecipient
1115
1125
  exchange,
1116
1126
  message,
1117
1127
  );
1118
- } catch (error: any) {
1128
+ } catch (error) {
1119
1129
  logger.error(
1120
1130
  `Subscription ${subscriptionId} for Session ${session.id}: Error while sending initial data reports`,
1121
- error,
1131
+ error instanceof MatterError ? error.message : error,
1122
1132
  );
1123
- if (error instanceof StatusResponseError) {
1133
+ if (error instanceof StatusResponseError && !(error instanceof ReceivedStatusResponseError)) {
1124
1134
  logger.info(`Sending status response ${error.code} for interaction error: ${error.message}`);
1125
1135
  await messenger.sendStatus(error.code, {
1126
1136
  logContext: {
@@ -11,6 +11,7 @@ import { Fabric } from "#fabric/Fabric.js";
11
11
  import {
12
12
  Channel,
13
13
  ChannelType,
14
+ ClassExtends,
14
15
  Environment,
15
16
  Environmental,
16
17
  isIPv6,
@@ -55,6 +56,12 @@ export interface CommissioningOptions extends Partial<ControllerCommissioningFlo
55
56
  * not throw, the commissioner considers commissioning complete.
56
57
  */
57
58
  finalizeCommissioning?: (peerAddress: PeerAddress, discoveryData?: DiscoveryData) => Promise<void>;
59
+
60
+ /**
61
+ * Commissioning Flow Implementation as class that extends the official implementation to use for commissioning.
62
+ * Defaults to the matter.js default implementation {@link ControllerCommissioningFlow}.
63
+ */
64
+ commissioningFlowImpl?: ClassExtends<ControllerCommissioningFlow>;
58
65
  }
59
66
 
60
67
  /**
@@ -169,9 +176,11 @@ export class ControllerCommissioner {
169
176
  }
170
177
 
171
178
  /**
172
- * Commission a node with discovery.
179
+ * Discover and establish a PASE channel with a device.
173
180
  */
174
- async commissionWithDiscovery(options: DiscoveryAndCommissioningOptions): Promise<PeerAddress> {
181
+ async discoverAndEstablishPase(
182
+ options: DiscoveryAndCommissioningOptions,
183
+ ): Promise<{ paseSecureChannel: MessageChannel; discoveryData?: DiscoveryData }> {
175
184
  const {
176
185
  discovery: { timeoutSeconds = 30 },
177
186
  passcode,
@@ -211,7 +220,7 @@ export class ControllerCommissioner {
211
220
  const scannersToUse = this.#context.scanners.select(discoveryCapabilities);
212
221
 
213
222
  logger.info(
214
- `Commissioning device with identifier ${Logger.toJSON(identifierData)} and ${
223
+ `Connecting to device with identifier ${Logger.toJSON(identifierData)} and ${
215
224
  scannersToUse.length
216
225
  } scanners and knownAddress ${Logger.toJSON(knownAddress)}`,
217
226
  );
@@ -251,6 +260,17 @@ export class ControllerCommissioner {
251
260
  paseSecureChannel = result;
252
261
  }
253
262
 
263
+ return { paseSecureChannel, discoveryData };
264
+ }
265
+
266
+ /**
267
+ * Commission a node with discovery.
268
+ */
269
+ async commissionWithDiscovery(options: DiscoveryAndCommissioningOptions): Promise<PeerAddress> {
270
+ // Establish PASE channel
271
+ const { paseSecureChannel, discoveryData } = await this.discoverAndEstablishPase(options);
272
+
273
+ // Commission the node
254
274
  return await this.#commissionConnectedNode(paseSecureChannel, options, discoveryData);
255
275
  }
256
276
 
@@ -266,7 +286,7 @@ export class ControllerCommissioner {
266
286
  ): Promise<MessageChannel> {
267
287
  let paseChannel: Channel<Uint8Array>;
268
288
  if (device !== undefined) {
269
- logger.info(`Commissioning device`, MdnsScanner.discoveryDataDiagnostics(device));
289
+ logger.info(`Establish PASE to device`, MdnsScanner.discoveryDataDiagnostics(device));
270
290
  }
271
291
  if (address.type === "udp") {
272
292
  const { ip } = address;
@@ -347,7 +367,11 @@ export class ControllerCommissioner {
347
367
  ...options,
348
368
  };
349
369
 
350
- const { fabric, finalizeCommissioning: performCaseCommissioning } = commissioningOptions;
370
+ const {
371
+ fabric,
372
+ finalizeCommissioning: performCaseCommissioning,
373
+ commissioningFlowImpl = ControllerCommissioningFlow,
374
+ } = commissioningOptions;
351
375
 
352
376
  // TODO: Create the fabric only when needed before commissioning (to do when refactoring MatterController away)
353
377
  // TODO also move certificateManager and other parts into that class to get rid of them here
@@ -381,7 +405,7 @@ export class ControllerCommissioner {
381
405
  logger.info(
382
406
  `Start commissioning of node ${address.nodeId} into fabric ${fabric.fabricId} (index ${address.fabricIndex})`,
383
407
  );
384
- const commissioningManager = new ControllerCommissioningFlow(
408
+ const commissioningManager = new commissioningFlowImpl(
385
409
  // Use the created secure session to do the commissioning
386
410
  new InteractionClient(
387
411
  new DedicatedChannelExchangeProvider(this.#context.exchanges, paseSecureMessageChannel),