@matter/protocol 0.13.0-alpha.0-20250311-3eb0af5f2 → 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.
- package/dist/cjs/certificate/CertificateAuthority.d.ts +1 -0
- package/dist/cjs/certificate/CertificateAuthority.d.ts.map +1 -1
- package/dist/cjs/certificate/CertificateAuthority.js.map +1 -1
- package/dist/cjs/cluster/client/ClusterClient.d.ts.map +1 -1
- package/dist/cjs/cluster/client/ClusterClient.js +3 -0
- package/dist/cjs/cluster/client/ClusterClient.js.map +1 -1
- package/dist/cjs/common/FailsafeContext.js +1 -1
- package/dist/cjs/common/FailsafeContext.js.map +1 -1
- package/dist/cjs/interaction/AttributeDataDecoder.d.ts +21 -4
- package/dist/cjs/interaction/AttributeDataDecoder.d.ts.map +1 -1
- package/dist/cjs/interaction/AttributeDataDecoder.js +40 -1
- package/dist/cjs/interaction/AttributeDataDecoder.js.map +1 -1
- package/dist/cjs/interaction/DecodedDataReport.d.ts +4 -2
- package/dist/cjs/interaction/DecodedDataReport.d.ts.map +1 -1
- package/dist/cjs/interaction/DecodedDataReport.js +6 -2
- package/dist/cjs/interaction/DecodedDataReport.js.map +1 -1
- package/dist/cjs/interaction/EventDataDecoder.d.ts +15 -3
- package/dist/cjs/interaction/EventDataDecoder.d.ts.map +1 -1
- package/dist/cjs/interaction/EventDataDecoder.js +39 -2
- package/dist/cjs/interaction/EventDataDecoder.js.map +1 -1
- package/dist/cjs/interaction/InteractionClient.d.ts +44 -4
- package/dist/cjs/interaction/InteractionClient.d.ts.map +1 -1
- package/dist/cjs/interaction/InteractionClient.js +65 -12
- package/dist/cjs/interaction/InteractionClient.js.map +1 -1
- package/dist/cjs/interaction/InteractionMessenger.d.ts.map +1 -1
- package/dist/cjs/interaction/InteractionMessenger.js +5 -2
- package/dist/cjs/interaction/InteractionMessenger.js.map +1 -1
- package/dist/cjs/interaction/InteractionServer.d.ts.map +1 -1
- package/dist/cjs/interaction/InteractionServer.js +2 -2
- package/dist/cjs/interaction/InteractionServer.js.map +1 -1
- package/dist/cjs/peer/ControllerCommissioner.d.ts +15 -3
- package/dist/cjs/peer/ControllerCommissioner.d.ts.map +1 -1
- package/dist/cjs/peer/ControllerCommissioner.js +17 -6
- package/dist/cjs/peer/ControllerCommissioner.js.map +1 -1
- package/dist/cjs/peer/ControllerCommissioningFlow.d.ts +47 -1
- package/dist/cjs/peer/ControllerCommissioningFlow.d.ts.map +1 -1
- package/dist/cjs/peer/ControllerCommissioningFlow.js +133 -133
- package/dist/cjs/peer/ControllerCommissioningFlow.js.map +1 -1
- package/dist/cjs/peer/PeerSet.d.ts +2 -2
- package/dist/cjs/peer/PeerSet.d.ts.map +1 -1
- package/dist/cjs/peer/PeerSet.js +6 -1
- package/dist/cjs/peer/PeerSet.js.map +1 -1
- package/dist/cjs/protocol/ChannelManager.d.ts.map +1 -1
- package/dist/cjs/protocol/ChannelManager.js +11 -4
- package/dist/cjs/protocol/ChannelManager.js.map +1 -1
- package/dist/cjs/protocol/ExchangeManager.d.ts.map +1 -1
- package/dist/cjs/protocol/ExchangeManager.js +0 -1
- package/dist/cjs/protocol/ExchangeManager.js.map +1 -1
- package/dist/esm/certificate/CertificateAuthority.d.ts +1 -0
- package/dist/esm/certificate/CertificateAuthority.d.ts.map +1 -1
- package/dist/esm/certificate/CertificateAuthority.js.map +1 -1
- package/dist/esm/cluster/client/ClusterClient.d.ts.map +1 -1
- package/dist/esm/cluster/client/ClusterClient.js +3 -0
- package/dist/esm/cluster/client/ClusterClient.js.map +1 -1
- package/dist/esm/common/FailsafeContext.js +1 -1
- package/dist/esm/common/FailsafeContext.js.map +1 -1
- package/dist/esm/interaction/AttributeDataDecoder.d.ts +21 -4
- package/dist/esm/interaction/AttributeDataDecoder.d.ts.map +1 -1
- package/dist/esm/interaction/AttributeDataDecoder.js +40 -1
- package/dist/esm/interaction/AttributeDataDecoder.js.map +1 -1
- package/dist/esm/interaction/DecodedDataReport.d.ts +4 -2
- package/dist/esm/interaction/DecodedDataReport.d.ts.map +1 -1
- package/dist/esm/interaction/DecodedDataReport.js +12 -4
- package/dist/esm/interaction/DecodedDataReport.js.map +1 -1
- package/dist/esm/interaction/EventDataDecoder.d.ts +15 -3
- package/dist/esm/interaction/EventDataDecoder.d.ts.map +1 -1
- package/dist/esm/interaction/EventDataDecoder.js +39 -2
- package/dist/esm/interaction/EventDataDecoder.js.map +1 -1
- package/dist/esm/interaction/InteractionClient.d.ts +44 -4
- package/dist/esm/interaction/InteractionClient.d.ts.map +1 -1
- package/dist/esm/interaction/InteractionClient.js +67 -12
- package/dist/esm/interaction/InteractionClient.js.map +1 -1
- package/dist/esm/interaction/InteractionMessenger.d.ts.map +1 -1
- package/dist/esm/interaction/InteractionMessenger.js +6 -2
- package/dist/esm/interaction/InteractionMessenger.js.map +1 -1
- package/dist/esm/interaction/InteractionServer.d.ts.map +1 -1
- package/dist/esm/interaction/InteractionServer.js +12 -3
- package/dist/esm/interaction/InteractionServer.js.map +1 -1
- package/dist/esm/peer/ControllerCommissioner.d.ts +15 -3
- package/dist/esm/peer/ControllerCommissioner.d.ts.map +1 -1
- package/dist/esm/peer/ControllerCommissioner.js +17 -6
- package/dist/esm/peer/ControllerCommissioner.js.map +1 -1
- package/dist/esm/peer/ControllerCommissioningFlow.d.ts +47 -1
- package/dist/esm/peer/ControllerCommissioningFlow.d.ts.map +1 -1
- package/dist/esm/peer/ControllerCommissioningFlow.js +133 -133
- package/dist/esm/peer/ControllerCommissioningFlow.js.map +1 -1
- package/dist/esm/peer/PeerSet.d.ts +2 -2
- package/dist/esm/peer/PeerSet.d.ts.map +1 -1
- package/dist/esm/peer/PeerSet.js +7 -2
- package/dist/esm/peer/PeerSet.js.map +1 -1
- package/dist/esm/protocol/ChannelManager.d.ts.map +1 -1
- package/dist/esm/protocol/ChannelManager.js +11 -4
- package/dist/esm/protocol/ChannelManager.js.map +1 -1
- package/dist/esm/protocol/ExchangeManager.d.ts.map +1 -1
- package/dist/esm/protocol/ExchangeManager.js +0 -1
- package/dist/esm/protocol/ExchangeManager.js.map +1 -1
- package/package.json +6 -6
- package/src/certificate/CertificateAuthority.ts +1 -0
- package/src/cluster/client/ClusterClient.ts +3 -0
- package/src/common/FailsafeContext.ts +1 -1
- package/src/interaction/AttributeDataDecoder.ts +66 -6
- package/src/interaction/DecodedDataReport.ts +24 -5
- package/src/interaction/EventDataDecoder.ts +57 -5
- package/src/interaction/InteractionClient.ts +105 -12
- package/src/interaction/InteractionMessenger.ts +6 -2
- package/src/interaction/InteractionServer.ts +14 -4
- package/src/peer/ControllerCommissioner.ts +30 -6
- package/src/peer/ControllerCommissioningFlow.ts +144 -140
- package/src/peer/PeerSet.ts +7 -2
- package/src/protocol/ChannelManager.ts +11 -4
- 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
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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 {
|
|
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
|
|
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
|
-
*
|
|
179
|
+
* Discover and establish a PASE channel with a device.
|
|
173
180
|
*/
|
|
174
|
-
async
|
|
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
|
-
`
|
|
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(`
|
|
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 {
|
|
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
|
|
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),
|