@project-chip/matter.js 0.11.0-alpha.0-20240926-407400ecb → 0.11.0-alpha.0-20241002-e7b377c34
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/CommissioningController.d.ts +17 -35
- package/dist/cjs/CommissioningController.d.ts.map +1 -1
- package/dist/cjs/CommissioningController.js +37 -5
- package/dist/cjs/CommissioningController.js.map +1 -1
- package/dist/cjs/CommissioningServer.d.ts.map +1 -1
- package/dist/cjs/CommissioningServer.js +28 -13
- package/dist/cjs/CommissioningServer.js.map +1 -1
- package/dist/cjs/MatterController.d.ts +32 -70
- package/dist/cjs/MatterController.d.ts.map +1 -1
- package/dist/cjs/MatterController.js +138 -579
- package/dist/cjs/MatterController.js.map +2 -2
- package/dist/cjs/PaseCommissioner.d.ts.map +1 -1
- package/dist/cjs/PaseCommissioner.js +9 -10
- package/dist/cjs/PaseCommissioner.js.map +2 -2
- package/dist/cjs/cluster/server/AccessControlServer.d.ts.map +1 -1
- package/dist/cjs/cluster/server/AccessControlServer.js +4 -2
- package/dist/cjs/cluster/server/AccessControlServer.js.map +1 -1
- package/dist/cjs/cluster/server/AdministratorCommissioningServer.d.ts.map +1 -1
- package/dist/cjs/cluster/server/AdministratorCommissioningServer.js +1 -1
- package/dist/cjs/cluster/server/AdministratorCommissioningServer.js.map +1 -1
- package/dist/cjs/cluster/server/ClusterServer.d.ts.map +1 -1
- package/dist/cjs/cluster/server/ClusterServer.js +3 -0
- package/dist/cjs/cluster/server/ClusterServer.js.map +1 -1
- package/dist/cjs/cluster/server/ClusterServerTypes.d.ts +5 -5
- package/dist/cjs/cluster/server/ClusterServerTypes.d.ts.map +1 -1
- package/dist/cjs/cluster/server/OperationalCredentialsServer.d.ts.map +1 -1
- package/dist/cjs/cluster/server/OperationalCredentialsServer.js +9 -10
- package/dist/cjs/cluster/server/OperationalCredentialsServer.js.map +1 -1
- package/dist/cjs/compat/interaction.d.ts +3 -2
- package/dist/cjs/compat/interaction.d.ts.map +1 -1
- package/dist/cjs/compat/interaction.js +8 -6
- package/dist/cjs/compat/interaction.js.map +1 -1
- package/dist/cjs/compat/protocol.d.ts +1 -1
- package/dist/cjs/compat/protocol.d.ts.map +1 -1
- package/dist/cjs/compat/protocol.js +1 -2
- package/dist/cjs/compat/protocol.js.map +1 -1
- package/dist/cjs/compat/securechannel.d.ts +2 -1
- package/dist/cjs/compat/securechannel.d.ts.map +1 -1
- package/dist/cjs/compat/securechannel.js +4 -3
- package/dist/cjs/compat/securechannel.js.map +1 -1
- package/dist/cjs/compat/util.d.ts +1 -1
- package/dist/cjs/compat/util.d.ts.map +1 -1
- package/dist/cjs/compat/util.js +1 -1
- package/dist/cjs/device/LegacyInteractionServer.d.ts +2 -2
- package/dist/cjs/device/LegacyInteractionServer.d.ts.map +1 -1
- package/dist/cjs/device/LegacyInteractionServer.js +3 -4
- package/dist/cjs/device/LegacyInteractionServer.js.map +1 -1
- package/dist/cjs/device/PairedNode.d.ts +1 -2
- package/dist/cjs/device/PairedNode.d.ts.map +1 -1
- package/dist/cjs/device/PairedNode.js +2 -3
- package/dist/cjs/device/PairedNode.js.map +1 -1
- package/dist/esm/CommissioningController.d.ts +17 -35
- package/dist/esm/CommissioningController.d.ts.map +1 -1
- package/dist/esm/CommissioningController.js +41 -5
- package/dist/esm/CommissioningController.js.map +1 -1
- package/dist/esm/CommissioningServer.d.ts.map +1 -1
- package/dist/esm/CommissioningServer.js +28 -13
- package/dist/esm/CommissioningServer.js.map +1 -1
- package/dist/esm/MatterController.d.ts +32 -70
- package/dist/esm/MatterController.d.ts.map +1 -1
- package/dist/esm/MatterController.js +144 -601
- package/dist/esm/MatterController.js.map +2 -2
- package/dist/esm/PaseCommissioner.d.ts.map +1 -1
- package/dist/esm/PaseCommissioner.js +12 -11
- package/dist/esm/PaseCommissioner.js.map +1 -1
- package/dist/esm/cluster/server/AccessControlServer.d.ts.map +1 -1
- package/dist/esm/cluster/server/AccessControlServer.js +4 -2
- package/dist/esm/cluster/server/AccessControlServer.js.map +1 -1
- package/dist/esm/cluster/server/AdministratorCommissioningServer.d.ts.map +1 -1
- package/dist/esm/cluster/server/AdministratorCommissioningServer.js +1 -1
- package/dist/esm/cluster/server/AdministratorCommissioningServer.js.map +1 -1
- package/dist/esm/cluster/server/ClusterServer.d.ts.map +1 -1
- package/dist/esm/cluster/server/ClusterServer.js +3 -0
- package/dist/esm/cluster/server/ClusterServer.js.map +1 -1
- package/dist/esm/cluster/server/ClusterServerTypes.d.ts +5 -5
- package/dist/esm/cluster/server/ClusterServerTypes.d.ts.map +1 -1
- package/dist/esm/cluster/server/OperationalCredentialsServer.d.ts.map +1 -1
- package/dist/esm/cluster/server/OperationalCredentialsServer.js +9 -11
- package/dist/esm/cluster/server/OperationalCredentialsServer.js.map +1 -1
- package/dist/esm/compat/interaction.d.ts +3 -2
- package/dist/esm/compat/interaction.d.ts.map +1 -1
- package/dist/esm/compat/interaction.js +14 -15
- package/dist/esm/compat/interaction.js.map +1 -1
- package/dist/esm/compat/protocol.d.ts +1 -1
- package/dist/esm/compat/protocol.d.ts.map +1 -1
- package/dist/esm/compat/protocol.js +2 -4
- package/dist/esm/compat/protocol.js.map +1 -1
- package/dist/esm/compat/securechannel.d.ts +2 -1
- package/dist/esm/compat/securechannel.d.ts.map +1 -1
- package/dist/esm/compat/securechannel.js +1 -3
- package/dist/esm/compat/securechannel.js.map +1 -1
- package/dist/esm/compat/util.d.ts +1 -1
- package/dist/esm/compat/util.d.ts.map +1 -1
- package/dist/esm/compat/util.js +2 -2
- package/dist/esm/compat/util.js.map +1 -1
- package/dist/esm/device/LegacyInteractionServer.d.ts +2 -2
- package/dist/esm/device/LegacyInteractionServer.d.ts.map +1 -1
- package/dist/esm/device/LegacyInteractionServer.js +3 -4
- package/dist/esm/device/LegacyInteractionServer.js.map +1 -1
- package/dist/esm/device/PairedNode.d.ts +1 -2
- package/dist/esm/device/PairedNode.d.ts.map +1 -1
- package/dist/esm/device/PairedNode.js +1 -1
- package/dist/esm/device/PairedNode.js.map +1 -1
- package/package.json +8 -8
- package/src/CommissioningController.ts +56 -47
- package/src/CommissioningServer.ts +27 -12
- package/src/MatterController.ts +177 -816
- package/src/PaseCommissioner.ts +11 -11
- package/src/cluster/server/AccessControlServer.ts +2 -0
- package/src/cluster/server/AdministratorCommissioningServer.ts +1 -1
- package/src/cluster/server/ClusterServer.ts +4 -0
- package/src/cluster/server/ClusterServerTypes.ts +5 -5
- package/src/cluster/server/OperationalCredentialsServer.ts +13 -13
- package/src/compat/interaction.ts +14 -13
- package/src/compat/protocol.ts +2 -3
- package/src/compat/securechannel.ts +2 -3
- package/src/compat/util.ts +1 -1
- package/src/device/LegacyInteractionServer.ts +4 -4
- package/src/device/PairedNode.ts +1 -1
|
@@ -6,47 +6,30 @@
|
|
|
6
6
|
import { GeneralCommissioning } from "#clusters";
|
|
7
7
|
import {
|
|
8
8
|
CRYPTO_SYMMETRIC_KEY_LENGTH,
|
|
9
|
+
ChannelType,
|
|
9
10
|
Construction,
|
|
10
11
|
Crypto,
|
|
11
12
|
ImplementationError,
|
|
12
13
|
Logger,
|
|
13
|
-
|
|
14
|
-
NoResponseTimeoutError,
|
|
14
|
+
NetInterfaceSet,
|
|
15
15
|
StorageBackendMemory,
|
|
16
16
|
StorageManager,
|
|
17
|
-
Time,
|
|
18
|
-
anyPromise,
|
|
19
|
-
createPromise,
|
|
20
|
-
isIPv6,
|
|
21
17
|
serverAddressToString
|
|
22
18
|
} from "#general";
|
|
23
|
-
import { Specification } from "#model";
|
|
24
19
|
import {
|
|
25
|
-
Ble,
|
|
26
|
-
CaseClient,
|
|
27
20
|
ChannelManager,
|
|
28
21
|
ClusterClient,
|
|
29
22
|
CommissioningError,
|
|
30
|
-
CommissioningSuccessfullyFinished,
|
|
31
23
|
ControllerCommissioner,
|
|
32
|
-
ControllerDiscovery,
|
|
33
|
-
DiscoveryError,
|
|
34
24
|
ExchangeManager,
|
|
35
|
-
ExchangeProvider,
|
|
36
25
|
Fabric,
|
|
37
26
|
FabricBuilder,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
PairRetransmissionLimitReachedError,
|
|
43
|
-
PaseClient,
|
|
27
|
+
FabricManager,
|
|
28
|
+
NodeDiscoveryType,
|
|
29
|
+
PeerSet,
|
|
30
|
+
PeerStore,
|
|
44
31
|
RetransmissionLimitReachedError,
|
|
45
32
|
RootCertificateManager,
|
|
46
|
-
SECURE_CHANNEL_PROTOCOL_ID,
|
|
47
|
-
SESSION_ACTIVE_INTERVAL_MS,
|
|
48
|
-
SESSION_ACTIVE_THRESHOLD_MS,
|
|
49
|
-
SESSION_IDLE_INTERVAL_MS,
|
|
50
33
|
SessionManager,
|
|
51
34
|
StatusReportOnlySecureChannelProtocol
|
|
52
35
|
} from "#protocol";
|
|
@@ -67,21 +50,12 @@ const TlvCommissioningSuccessFailureResponse = TlvObject({
|
|
|
67
50
|
/** Should help developers in troubleshooting errors. The value MAY go into logs or crash reports, not User UIs. */
|
|
68
51
|
debugText: TlvField(1, TlvString.bound({ maxLength: 128 }))
|
|
69
52
|
});
|
|
53
|
+
const DEFAULT_ADMIN_VENDOR_ID = VendorId(65521);
|
|
70
54
|
const DEFAULT_FABRIC_INDEX = FabricIndex(1);
|
|
71
55
|
const DEFAULT_FABRIC_ID = FabricId(1);
|
|
72
|
-
const DEFAULT_ADMIN_VENDOR_ID = VendorId(65521);
|
|
73
|
-
const RECONNECTION_POLLING_INTERVAL_MS = 6e5;
|
|
74
|
-
const RETRANSMISSION_DISCOVERY_TIMEOUT_MS = 5e3;
|
|
75
56
|
const CONTROLLER_CONNECTIONS_PER_FABRIC_AND_NODE = 3;
|
|
76
57
|
const CONTROLLER_MAX_PATHS_PER_INVOKE = 10;
|
|
77
58
|
const logger = Logger.get("MatterController");
|
|
78
|
-
var NodeDiscoveryType = /* @__PURE__ */ ((NodeDiscoveryType2) => {
|
|
79
|
-
NodeDiscoveryType2[NodeDiscoveryType2["None"] = 0] = "None";
|
|
80
|
-
NodeDiscoveryType2[NodeDiscoveryType2["RetransmissionDiscovery"] = 1] = "RetransmissionDiscovery";
|
|
81
|
-
NodeDiscoveryType2[NodeDiscoveryType2["TimedDiscovery"] = 2] = "TimedDiscovery";
|
|
82
|
-
NodeDiscoveryType2[NodeDiscoveryType2["FullDiscovery"] = 3] = "FullDiscovery";
|
|
83
|
-
return NodeDiscoveryType2;
|
|
84
|
-
})(NodeDiscoveryType || {});
|
|
85
59
|
class MatterController {
|
|
86
60
|
static async create(options) {
|
|
87
61
|
const {
|
|
@@ -89,11 +63,10 @@ class MatterController {
|
|
|
89
63
|
rootCertificateStorage,
|
|
90
64
|
fabricStorage,
|
|
91
65
|
nodesStorage,
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
netInterfaceIpv6,
|
|
66
|
+
scanners,
|
|
67
|
+
netInterfaces,
|
|
95
68
|
sessionClosedCallback,
|
|
96
|
-
adminVendorId
|
|
69
|
+
adminVendorId,
|
|
97
70
|
adminFabricId = FabricId(DEFAULT_FABRIC_ID),
|
|
98
71
|
adminFabricIndex = FabricIndex(DEFAULT_FABRIC_INDEX),
|
|
99
72
|
caseAuthenticatedTags
|
|
@@ -106,18 +79,16 @@ class MatterController {
|
|
|
106
79
|
sessionStorage,
|
|
107
80
|
fabricStorage,
|
|
108
81
|
nodesStorage,
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
netInterfaceIpv6,
|
|
82
|
+
scanners,
|
|
83
|
+
netInterfaces,
|
|
112
84
|
certificateManager,
|
|
113
85
|
fabric,
|
|
114
|
-
adminVendorId: fabric.rootVendorId,
|
|
115
86
|
sessionClosedCallback
|
|
116
87
|
});
|
|
117
88
|
} else {
|
|
118
89
|
const rootNodeId = NodeId.randomOperationalNodeId();
|
|
119
90
|
const ipkValue = Crypto.getRandomData(CRYPTO_SYMMETRIC_KEY_LENGTH);
|
|
120
|
-
const fabricBuilder = new FabricBuilder().setRootCert(certificateManager.rootCert).setRootNodeId(rootNodeId).setIdentityProtectionKey(ipkValue).setRootVendorId(adminVendorId);
|
|
91
|
+
const fabricBuilder = new FabricBuilder().setRootCert(certificateManager.rootCert).setRootNodeId(rootNodeId).setIdentityProtectionKey(ipkValue).setRootVendorId(adminVendorId ?? DEFAULT_ADMIN_VENDOR_ID);
|
|
121
92
|
fabricBuilder.setOperationalCert(
|
|
122
93
|
certificateManager.generateNoc(
|
|
123
94
|
fabricBuilder.publicKey,
|
|
@@ -131,12 +102,10 @@ class MatterController {
|
|
|
131
102
|
sessionStorage,
|
|
132
103
|
fabricStorage,
|
|
133
104
|
nodesStorage,
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
netInterfaceIpv6,
|
|
105
|
+
scanners,
|
|
106
|
+
netInterfaces,
|
|
137
107
|
certificateManager,
|
|
138
108
|
fabric,
|
|
139
|
-
adminVendorId,
|
|
140
109
|
sessionClosedCallback
|
|
141
110
|
});
|
|
142
111
|
}
|
|
@@ -144,19 +113,9 @@ class MatterController {
|
|
|
144
113
|
return controller;
|
|
145
114
|
}
|
|
146
115
|
static async createAsPaseCommissioner(options) {
|
|
147
|
-
const {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
mdnsScanner,
|
|
151
|
-
netInterfaceIpv4,
|
|
152
|
-
netInterfaceIpv6,
|
|
153
|
-
sessionClosedCallback
|
|
154
|
-
} = options;
|
|
155
|
-
try {
|
|
156
|
-
Ble.get();
|
|
157
|
-
} catch (error) {
|
|
158
|
-
NoProviderError.accept(error);
|
|
159
|
-
if (!mdnsScanner || !netInterfaceIpv6) {
|
|
116
|
+
const { rootCertificateData, fabricData, scanners, netInterfaces, sessionClosedCallback } = options;
|
|
117
|
+
if (!netInterfaces.hasInterfaceFor(ChannelType.BLE)) {
|
|
118
|
+
if (!scanners.hasScannerFor(ChannelType.UDP) || !netInterfaces.hasInterfaceFor(ChannelType.UDP, "::")) {
|
|
160
119
|
throw new ImplementationError(
|
|
161
120
|
"Ble must be initialized to create a Sub Commissioner without an IP network!"
|
|
162
121
|
);
|
|
@@ -172,37 +131,29 @@ class MatterController {
|
|
|
172
131
|
const controller = new MatterController({
|
|
173
132
|
sessionStorage,
|
|
174
133
|
nodesStorage,
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
netInterfaceIpv6,
|
|
134
|
+
scanners,
|
|
135
|
+
netInterfaces,
|
|
178
136
|
certificateManager,
|
|
179
137
|
fabric,
|
|
180
|
-
adminVendorId: fabric.rootVendorId,
|
|
181
138
|
sessionClosedCallback
|
|
182
139
|
});
|
|
183
140
|
await controller.construction;
|
|
184
141
|
return controller;
|
|
185
142
|
}
|
|
186
143
|
sessionManager;
|
|
144
|
+
netInterfaces = new NetInterfaceSet();
|
|
187
145
|
channelManager = new ChannelManager(CONTROLLER_CONNECTIONS_PER_FABRIC_AND_NODE);
|
|
188
146
|
exchangeManager;
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
netInterfaceBle;
|
|
192
|
-
bleScanner;
|
|
193
|
-
commissionedNodes = /* @__PURE__ */ new Map();
|
|
147
|
+
peers;
|
|
148
|
+
commissioner;
|
|
194
149
|
#construction;
|
|
195
150
|
sessionStorage;
|
|
196
151
|
fabricStorage;
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
netInterfaceIpv4;
|
|
200
|
-
netInterfaceIpv6;
|
|
152
|
+
nodesStore;
|
|
153
|
+
scanners;
|
|
201
154
|
certificateManager;
|
|
202
155
|
fabric;
|
|
203
|
-
adminVendorId;
|
|
204
156
|
sessionClosedCallback;
|
|
205
|
-
#runningNodeDiscoveries = /* @__PURE__ */ new Map();
|
|
206
157
|
get construction() {
|
|
207
158
|
return this.#construction;
|
|
208
159
|
}
|
|
@@ -211,48 +162,57 @@ class MatterController {
|
|
|
211
162
|
sessionStorage,
|
|
212
163
|
fabricStorage,
|
|
213
164
|
nodesStorage,
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
netInterfaceIpv6,
|
|
165
|
+
scanners,
|
|
166
|
+
netInterfaces,
|
|
217
167
|
certificateManager,
|
|
218
168
|
fabric,
|
|
219
|
-
sessionClosedCallback
|
|
220
|
-
adminVendorId
|
|
169
|
+
sessionClosedCallback
|
|
221
170
|
} = options;
|
|
222
171
|
this.sessionStorage = sessionStorage;
|
|
223
172
|
this.fabricStorage = fabricStorage;
|
|
224
|
-
this.
|
|
225
|
-
this.
|
|
226
|
-
this.netInterfaceIpv4 = netInterfaceIpv4;
|
|
227
|
-
this.netInterfaceIpv6 = netInterfaceIpv6;
|
|
173
|
+
this.scanners = scanners;
|
|
174
|
+
this.netInterfaces = netInterfaces;
|
|
228
175
|
this.certificateManager = certificateManager;
|
|
229
176
|
this.fabric = fabric;
|
|
230
177
|
this.sessionClosedCallback = sessionClosedCallback;
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
this.sessionManager
|
|
234
|
-
|
|
235
|
-
|
|
178
|
+
const fabricManager = new FabricManager();
|
|
179
|
+
fabricManager.addFabric(fabric);
|
|
180
|
+
this.sessionManager = new SessionManager({
|
|
181
|
+
fabrics: fabricManager,
|
|
182
|
+
storage: sessionStorage,
|
|
183
|
+
parameters: {
|
|
184
|
+
maxPathsPerInvoke: CONTROLLER_MAX_PATHS_PER_INVOKE
|
|
236
185
|
}
|
|
186
|
+
});
|
|
187
|
+
this.sessionManager.sessions.deleted.on(async (session) => {
|
|
237
188
|
this.sessionClosedCallback?.(session.peerNodeId);
|
|
238
189
|
});
|
|
239
|
-
this.exchangeManager = new ExchangeManager(
|
|
190
|
+
this.exchangeManager = new ExchangeManager({
|
|
191
|
+
sessionManager: this.sessionManager,
|
|
192
|
+
channelManager: this.channelManager,
|
|
193
|
+
transportInterfaces: this.netInterfaces
|
|
194
|
+
});
|
|
240
195
|
this.exchangeManager.addProtocolHandler(new StatusReportOnlySecureChannelProtocol());
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
this.
|
|
246
|
-
|
|
196
|
+
this.nodesStore = new CommissionedNodeStore(nodesStorage, fabric);
|
|
197
|
+
this.nodesStore.peers = this.peers = new PeerSet({
|
|
198
|
+
sessions: this.sessionManager,
|
|
199
|
+
channels: this.channelManager,
|
|
200
|
+
exchanges: this.exchangeManager,
|
|
201
|
+
scanners: this.scanners,
|
|
202
|
+
netInterfaces: this.netInterfaces,
|
|
203
|
+
store: this.nodesStore
|
|
204
|
+
});
|
|
205
|
+
this.commissioner = new ControllerCommissioner({
|
|
206
|
+
peers: this.peers,
|
|
207
|
+
scanners: this.scanners,
|
|
208
|
+
netInterfaces: this.netInterfaces,
|
|
209
|
+
exchanges: this.exchangeManager,
|
|
210
|
+
sessions: this.sessionManager,
|
|
211
|
+
certificates: this.certificateManager
|
|
212
|
+
});
|
|
247
213
|
this.#construction = Construction(this, async () => {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
this.commissionedNodes.clear();
|
|
251
|
-
for (const [nodeId, details] of commissionedNodes) {
|
|
252
|
-
this.commissionedNodes.set(nodeId, details);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
await this.sessionManager.initFromStorage([this.fabric]);
|
|
214
|
+
await this.peers.construction.ready;
|
|
215
|
+
await this.sessionManager.construction.ready;
|
|
256
216
|
});
|
|
257
217
|
}
|
|
258
218
|
get nodeId() {
|
|
@@ -267,44 +227,10 @@ class MatterController {
|
|
|
267
227
|
getFabrics() {
|
|
268
228
|
return [this.fabric];
|
|
269
229
|
}
|
|
270
|
-
/** Our own client/controller session parameters. */
|
|
271
|
-
get sessionParameters() {
|
|
272
|
-
return {
|
|
273
|
-
idleIntervalMs: SESSION_IDLE_INTERVAL_MS,
|
|
274
|
-
activeIntervalMs: SESSION_ACTIVE_INTERVAL_MS,
|
|
275
|
-
activeThresholdMs: SESSION_ACTIVE_THRESHOLD_MS,
|
|
276
|
-
dataModelRevision: Specification.DATA_MODEL_REVISION,
|
|
277
|
-
interactionModelRevision: Specification.INTERACTION_MODEL_REVISION,
|
|
278
|
-
specificationVersion: Specification.SPECIFICATION_VERSION,
|
|
279
|
-
maxPathsPerInvoke: CONTROLLER_MAX_PATHS_PER_INVOKE
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
addTransportInterface(netInterface) {
|
|
283
|
-
this.exchangeManager.addTransportInterface(netInterface);
|
|
284
|
-
}
|
|
285
230
|
collectScanners(discoveryCapabilities = { onIpNetwork: true }) {
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
}
|
|
290
|
-
if (discoveryCapabilities.ble) {
|
|
291
|
-
if (this.bleScanner === void 0) {
|
|
292
|
-
let ble;
|
|
293
|
-
try {
|
|
294
|
-
ble = Ble.get();
|
|
295
|
-
this.netInterfaceBle = ble.getBleCentralInterface();
|
|
296
|
-
this.addTransportInterface(this.netInterfaceBle);
|
|
297
|
-
this.bleScanner = ble.getBleScanner();
|
|
298
|
-
} catch (error) {
|
|
299
|
-
NoProviderError.accept(error);
|
|
300
|
-
logger.warn("BLE is not supported on this platform. The device to commission might not be found!");
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
if (this.bleScanner !== void 0) {
|
|
304
|
-
scannersToUse.push(this.bleScanner);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
return scannersToUse;
|
|
231
|
+
return this.scanners.filter(
|
|
232
|
+
(scanner) => scanner.type === ChannelType.UDP || discoveryCapabilities.ble && scanner.type === ChannelType.BLE
|
|
233
|
+
);
|
|
308
234
|
}
|
|
309
235
|
/**
|
|
310
236
|
* Commission a device by its identifier and the Passcode. If a known address is provided this is tried first
|
|
@@ -318,186 +244,36 @@ class MatterController {
|
|
|
318
244
|
* Return true when the commissioning process is completed successfully, false on error.
|
|
319
245
|
*/
|
|
320
246
|
async commission(options, completeCommissioningCallback) {
|
|
321
|
-
const {
|
|
322
|
-
commissioning
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
discovery: { discoveryCapabilities = {}, knownAddress }
|
|
333
|
-
} = options;
|
|
334
|
-
let identifierData = "identifierData" in options.discovery ? options.discovery.identifierData : {};
|
|
335
|
-
if (this.mdnsScanner !== void 0 && this.netInterfaceIpv6 !== void 0) {
|
|
336
|
-
discoveryCapabilities.onIpNetwork = true;
|
|
337
|
-
}
|
|
338
|
-
if (commissionableDevice !== void 0) {
|
|
339
|
-
let { addresses } = commissionableDevice;
|
|
340
|
-
if (discoveryCapabilities.ble === true) {
|
|
341
|
-
discoveryCapabilities = { onIpNetwork: true, ble: addresses.some((address) => address.type === "ble") };
|
|
342
|
-
} else if (discoveryCapabilities.onIpNetwork === true) {
|
|
343
|
-
addresses = addresses.filter((address) => address.type !== "ble");
|
|
344
|
-
}
|
|
345
|
-
addresses.sort((a) => a.type === "udp" ? -1 : 1);
|
|
346
|
-
knownAddress = addresses[0];
|
|
347
|
-
if ("instanceId" in commissionableDevice && commissionableDevice.instanceId !== void 0) {
|
|
348
|
-
identifierData = { instanceId: commissionableDevice.instanceId };
|
|
349
|
-
} else {
|
|
350
|
-
identifierData = { longDiscriminator: commissionableDevice.D };
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
const scannersToUse = this.collectScanners(discoveryCapabilities);
|
|
354
|
-
logger.info(
|
|
355
|
-
`Commissioning device with identifier ${Logger.toJSON(identifierData)} and ${scannersToUse.length} scanners and knownAddress ${Logger.toJSON(knownAddress)}`
|
|
356
|
-
);
|
|
357
|
-
let paseSecureChannel;
|
|
358
|
-
let discoveryData;
|
|
359
|
-
if (knownAddress !== void 0) {
|
|
360
|
-
try {
|
|
361
|
-
paseSecureChannel = await this.initializePaseSecureChannel(knownAddress, passcode);
|
|
362
|
-
} catch (error) {
|
|
363
|
-
NoResponseTimeoutError.accept(error);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
if (paseSecureChannel === void 0) {
|
|
367
|
-
const discoveredDevices = await ControllerDiscovery.discoverDeviceAddressesByIdentifier(
|
|
368
|
-
scannersToUse,
|
|
369
|
-
identifierData,
|
|
370
|
-
timeoutSeconds
|
|
371
|
-
);
|
|
372
|
-
const { result } = await ControllerDiscovery.iterateServerAddresses(
|
|
373
|
-
discoveredDevices,
|
|
374
|
-
NoResponseTimeoutError,
|
|
375
|
-
async () => scannersToUse.flatMap((scanner) => scanner.getDiscoveredCommissionableDevices(identifierData)),
|
|
376
|
-
async (address, device) => {
|
|
377
|
-
const channel = await this.initializePaseSecureChannel(address, passcode, device);
|
|
378
|
-
discoveryData = device;
|
|
379
|
-
return channel;
|
|
247
|
+
const commissioningOptions = {
|
|
248
|
+
...options.commissioning,
|
|
249
|
+
fabric: this.fabric,
|
|
250
|
+
discovery: options.discovery,
|
|
251
|
+
passcode: options.passcode
|
|
252
|
+
};
|
|
253
|
+
if (completeCommissioningCallback) {
|
|
254
|
+
commissioningOptions.performCaseCommissioning = async (peerAddress, discoveryData) => {
|
|
255
|
+
const result = await completeCommissioningCallback(peerAddress.nodeId, discoveryData);
|
|
256
|
+
if (!result) {
|
|
257
|
+
throw new RetransmissionLimitReachedError("Device could not be discovered");
|
|
380
258
|
}
|
|
381
|
-
|
|
382
|
-
paseSecureChannel = result;
|
|
259
|
+
};
|
|
383
260
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
discoveryData,
|
|
388
|
-
completeCommissioningCallback
|
|
389
|
-
);
|
|
261
|
+
const address = await this.commissioner.commission(commissioningOptions);
|
|
262
|
+
await this.fabricStorage?.set("fabric", this.fabric.toStorageObject());
|
|
263
|
+
return address.nodeId;
|
|
390
264
|
}
|
|
391
265
|
async disconnect(nodeId) {
|
|
392
|
-
|
|
393
|
-
await this.channelManager.removeAllNodeChannels(this.fabric, nodeId);
|
|
266
|
+
return this.peers.disconnect(this.fabric.addressOf(nodeId));
|
|
394
267
|
}
|
|
395
268
|
async removeNode(nodeId) {
|
|
396
|
-
|
|
397
|
-
await this.sessionManager.removeAllSessionsForNode(nodeId);
|
|
398
|
-
await this.sessionManager.removeResumptionRecord(nodeId);
|
|
399
|
-
await this.channelManager.removeAllNodeChannels(this.fabric, nodeId);
|
|
400
|
-
this.commissionedNodes.delete(nodeId);
|
|
401
|
-
await this.storeCommissionedNodes();
|
|
402
|
-
}
|
|
403
|
-
/**
|
|
404
|
-
* Method to start commission process with a PASE pairing.
|
|
405
|
-
* If this not successful and throws an RetransmissionLimitReachedError the address is invalid or the passcode
|
|
406
|
-
* is wrong.
|
|
407
|
-
*/
|
|
408
|
-
async initializePaseSecureChannel(address, passcode, device) {
|
|
409
|
-
let paseChannel;
|
|
410
|
-
if (device !== void 0) {
|
|
411
|
-
logger.info(`Commissioning device`, MdnsScanner.discoveryDataDiagnostics(device));
|
|
412
|
-
}
|
|
413
|
-
if (address.type === "udp") {
|
|
414
|
-
const { ip } = address;
|
|
415
|
-
const isIpv6Address = isIPv6(ip);
|
|
416
|
-
const paseInterface = isIpv6Address ? this.netInterfaceIpv6 : this.netInterfaceIpv4;
|
|
417
|
-
if (paseInterface === void 0) {
|
|
418
|
-
throw new PairRetransmissionLimitReachedError(
|
|
419
|
-
`IPv${isIpv6Address ? "6" : "4"} interface not initialized. Cannot use ${ip} for commissioning.`
|
|
420
|
-
);
|
|
421
|
-
}
|
|
422
|
-
paseChannel = await paseInterface.openChannel(address);
|
|
423
|
-
} else {
|
|
424
|
-
if (this.netInterfaceBle === void 0) {
|
|
425
|
-
throw new PairRetransmissionLimitReachedError(
|
|
426
|
-
`BLE interface not initialized. Cannot use ${address.peripheralAddress} for commissioning.`
|
|
427
|
-
);
|
|
428
|
-
}
|
|
429
|
-
paseChannel = await this.netInterfaceBle.openChannel(address);
|
|
430
|
-
}
|
|
431
|
-
const unsecureSession = this.sessionManager.createUnsecureSession({
|
|
432
|
-
// Use the session parameters from MDNS announcements when available and rest is assumed to be fallbacks
|
|
433
|
-
sessionParameters: {
|
|
434
|
-
idleIntervalMs: device?.SII,
|
|
435
|
-
activeIntervalMs: device?.SAI,
|
|
436
|
-
activeThresholdMs: device?.SAT
|
|
437
|
-
},
|
|
438
|
-
isInitiator: true
|
|
439
|
-
});
|
|
440
|
-
const paseUnsecureMessageChannel = new MessageChannel(paseChannel, unsecureSession);
|
|
441
|
-
const paseExchange = this.exchangeManager.initiateExchangeWithChannel(
|
|
442
|
-
paseUnsecureMessageChannel,
|
|
443
|
-
SECURE_CHANNEL_PROTOCOL_ID
|
|
444
|
-
);
|
|
445
|
-
let paseSecureSession;
|
|
446
|
-
try {
|
|
447
|
-
paseSecureSession = await this.paseClient.pair(this, paseExchange, passcode);
|
|
448
|
-
} catch (e) {
|
|
449
|
-
await paseExchange.close();
|
|
450
|
-
throw e;
|
|
451
|
-
}
|
|
452
|
-
await unsecureSession.destroy();
|
|
453
|
-
return new MessageChannel(paseChannel, paseSecureSession);
|
|
454
|
-
}
|
|
455
|
-
/**
|
|
456
|
-
* Method to commission a device with a PASE secure channel. It returns the NodeId of the commissioned device on
|
|
457
|
-
* success.
|
|
458
|
-
*/
|
|
459
|
-
async commissionDevice(paseSecureMessageChannel, commissioningOptions, discoveryData, completeCommissioningCallback) {
|
|
460
|
-
const peerNodeId = commissioningOptions.nodeId ?? NodeId.randomOperationalNodeId();
|
|
461
|
-
const commissioningManager = new ControllerCommissioner(
|
|
462
|
-
// Use the created secure session to do the commissioning
|
|
463
|
-
new InteractionClient(new ExchangeProvider(this.exchangeManager, paseSecureMessageChannel), peerNodeId),
|
|
464
|
-
this.certificateManager,
|
|
465
|
-
this.fabric,
|
|
466
|
-
commissioningOptions,
|
|
467
|
-
peerNodeId,
|
|
468
|
-
this.adminVendorId,
|
|
469
|
-
async () => {
|
|
470
|
-
await paseSecureMessageChannel.close();
|
|
471
|
-
if (completeCommissioningCallback !== void 0) {
|
|
472
|
-
if (!await completeCommissioningCallback(peerNodeId, discoveryData)) {
|
|
473
|
-
throw new RetransmissionLimitReachedError("Device could not be discovered");
|
|
474
|
-
}
|
|
475
|
-
throw new CommissioningSuccessfullyFinished();
|
|
476
|
-
}
|
|
477
|
-
return await this.connect(peerNodeId, {
|
|
478
|
-
discoveryType: 2 /* TimedDiscovery */,
|
|
479
|
-
timeoutSeconds: 120,
|
|
480
|
-
discoveryData
|
|
481
|
-
});
|
|
482
|
-
}
|
|
483
|
-
);
|
|
484
|
-
try {
|
|
485
|
-
await commissioningManager.executeCommissioning();
|
|
486
|
-
} catch (error) {
|
|
487
|
-
if (this.commissionedNodes.has(peerNodeId)) {
|
|
488
|
-
this.commissionedNodes.delete(peerNodeId);
|
|
489
|
-
}
|
|
490
|
-
throw error;
|
|
491
|
-
}
|
|
492
|
-
await this.fabricStorage?.set("fabric", this.fabric.toStorageObject());
|
|
493
|
-
return peerNodeId;
|
|
269
|
+
return this.peers.delete(this.fabric.addressOf(nodeId));
|
|
494
270
|
}
|
|
495
271
|
/**
|
|
496
272
|
* Method to complete the commissioning process to a node which was initialized with a PASE secure channel.
|
|
497
273
|
*/
|
|
498
274
|
async completeCommissioning(peerNodeId, discoveryData) {
|
|
499
275
|
const interactionClient = await this.connect(peerNodeId, {
|
|
500
|
-
discoveryType:
|
|
276
|
+
discoveryType: NodeDiscoveryType.TimedDiscovery,
|
|
501
277
|
timeoutSeconds: 120,
|
|
502
278
|
discoveryData
|
|
503
279
|
});
|
|
@@ -510,315 +286,44 @@ class MatterController {
|
|
|
510
286
|
useExtendedFailSafeMessageResponseTimeout: true
|
|
511
287
|
});
|
|
512
288
|
if (errorCode !== GeneralCommissioning.CommissioningError.Ok) {
|
|
513
|
-
|
|
514
|
-
this.commissionedNodes.delete(peerNodeId);
|
|
515
|
-
}
|
|
289
|
+
await this.peers.delete(this.fabric.addressOf(peerNodeId));
|
|
516
290
|
throw new CommissioningError(`Commission error on commissioningComplete: ${errorCode}, ${debugText}`);
|
|
517
291
|
}
|
|
518
292
|
await this.fabricStorage?.set("fabric", this.fabric.toStorageObject());
|
|
519
293
|
}
|
|
520
|
-
handleResubmissionStarted(peerNodeId) {
|
|
521
|
-
if (this.#runningNodeDiscoveries.has(peerNodeId)) {
|
|
522
|
-
return;
|
|
523
|
-
}
|
|
524
|
-
this.#runningNodeDiscoveries.set(peerNodeId, { type: 1 /* RetransmissionDiscovery */ });
|
|
525
|
-
this.mdnsScanner?.findOperationalDevice(this.fabric, peerNodeId, RETRANSMISSION_DISCOVERY_TIMEOUT_MS, true).catch((error) => {
|
|
526
|
-
logger.error(`Failed to discover device ${peerNodeId} after resubmission started.`, error);
|
|
527
|
-
}).finally(() => {
|
|
528
|
-
if (this.#runningNodeDiscoveries.get(peerNodeId)?.type === 1 /* RetransmissionDiscovery */) {
|
|
529
|
-
this.#runningNodeDiscoveries.delete(peerNodeId);
|
|
530
|
-
}
|
|
531
|
-
});
|
|
532
|
-
}
|
|
533
|
-
async reconnectKnownAddress(peerNodeId, operationalAddress, discoveryData, expectedProcessingTimeMs) {
|
|
534
|
-
const { ip, port } = operationalAddress;
|
|
535
|
-
try {
|
|
536
|
-
logger.debug(
|
|
537
|
-
`Resume device connection to configured server at ${ip}:${port}${expectedProcessingTimeMs !== void 0 ? ` with expected processing time of ${expectedProcessingTimeMs}ms` : ""} ...`
|
|
538
|
-
);
|
|
539
|
-
const channel = await this.pair(peerNodeId, operationalAddress, discoveryData, expectedProcessingTimeMs);
|
|
540
|
-
await this.setOperationalDeviceData(peerNodeId, operationalAddress);
|
|
541
|
-
return channel;
|
|
542
|
-
} catch (error) {
|
|
543
|
-
if (error instanceof NoResponseTimeoutError) {
|
|
544
|
-
logger.debug(
|
|
545
|
-
`Failed to resume connection to node ${peerNodeId} connection with ${ip}:${port}, discover the device ...`,
|
|
546
|
-
error
|
|
547
|
-
);
|
|
548
|
-
await this.sessionManager.removeAllSessionsForNode(peerNodeId);
|
|
549
|
-
return void 0;
|
|
550
|
-
} else {
|
|
551
|
-
throw error;
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
async connectOrDiscoverNode(peerNodeId, operationalAddress, discoveryOptions = {}) {
|
|
556
|
-
const {
|
|
557
|
-
discoveryType: requestedDiscoveryType = 3 /* FullDiscovery */,
|
|
558
|
-
timeoutSeconds,
|
|
559
|
-
discoveryData = this.commissionedNodes.get(peerNodeId)?.discoveryData
|
|
560
|
-
} = discoveryOptions;
|
|
561
|
-
if (timeoutSeconds !== void 0 && requestedDiscoveryType !== 2 /* TimedDiscovery */) {
|
|
562
|
-
throw new ImplementationError("Cannot set timeout without timed discovery.");
|
|
563
|
-
}
|
|
564
|
-
if (requestedDiscoveryType === 1 /* RetransmissionDiscovery */) {
|
|
565
|
-
throw new ImplementationError("Cannot set retransmission discovery type.");
|
|
566
|
-
}
|
|
567
|
-
if (this.mdnsScanner === void 0) {
|
|
568
|
-
throw new ImplementationError("Cannot discover device without mDNS scanner.");
|
|
569
|
-
}
|
|
570
|
-
const mdnsScanner = this.mdnsScanner;
|
|
571
|
-
const existingDiscoveryDetails = this.#runningNodeDiscoveries.get(peerNodeId) ?? {
|
|
572
|
-
type: 0 /* None */
|
|
573
|
-
};
|
|
574
|
-
if (existingDiscoveryDetails.type !== 0 /* None */ && existingDiscoveryDetails.type < requestedDiscoveryType) {
|
|
575
|
-
mdnsScanner.cancelOperationalDeviceDiscovery(this.fabric, peerNodeId);
|
|
576
|
-
this.#runningNodeDiscoveries.delete(peerNodeId);
|
|
577
|
-
existingDiscoveryDetails.type = 0 /* None */;
|
|
578
|
-
}
|
|
579
|
-
const { type: runningDiscoveryType, promises } = existingDiscoveryDetails;
|
|
580
|
-
if (operationalAddress !== void 0 && (runningDiscoveryType === 0 /* None */ || requestedDiscoveryType === 0 /* None */)) {
|
|
581
|
-
const directReconnection = await this.reconnectKnownAddress(peerNodeId, operationalAddress, discoveryData);
|
|
582
|
-
if (directReconnection !== void 0) {
|
|
583
|
-
return directReconnection;
|
|
584
|
-
}
|
|
585
|
-
if (requestedDiscoveryType === 0 /* None */) {
|
|
586
|
-
throw new DiscoveryError(`Node ${peerNodeId} is not reachable right now.`);
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
if (promises !== void 0) {
|
|
590
|
-
if (runningDiscoveryType > requestedDiscoveryType) {
|
|
591
|
-
throw new DiscoveryError(
|
|
592
|
-
`Node ${peerNodeId} is not reachable right now and discovery already running.`
|
|
593
|
-
);
|
|
594
|
-
} else {
|
|
595
|
-
return await anyPromise(promises);
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
const discoveryPromises = new Array();
|
|
599
|
-
let reconnectionPollingTimer;
|
|
600
|
-
if (operationalAddress !== void 0) {
|
|
601
|
-
if (requestedDiscoveryType === 3 /* FullDiscovery */) {
|
|
602
|
-
const { promise, resolver, rejecter } = createPromise();
|
|
603
|
-
reconnectionPollingTimer = Time.getPeriodicTimer(
|
|
604
|
-
"Controller reconnect",
|
|
605
|
-
RECONNECTION_POLLING_INTERVAL_MS,
|
|
606
|
-
async () => {
|
|
607
|
-
try {
|
|
608
|
-
logger.debug(`Polling for device at ${serverAddressToString(operationalAddress)} ...`);
|
|
609
|
-
const result = await this.reconnectKnownAddress(
|
|
610
|
-
peerNodeId,
|
|
611
|
-
operationalAddress,
|
|
612
|
-
discoveryData
|
|
613
|
-
);
|
|
614
|
-
if (result !== void 0 && reconnectionPollingTimer?.isRunning) {
|
|
615
|
-
reconnectionPollingTimer?.stop();
|
|
616
|
-
mdnsScanner.cancelOperationalDeviceDiscovery(this.fabric, peerNodeId);
|
|
617
|
-
this.#runningNodeDiscoveries.delete(peerNodeId);
|
|
618
|
-
resolver(result);
|
|
619
|
-
}
|
|
620
|
-
} catch (error) {
|
|
621
|
-
if (reconnectionPollingTimer?.isRunning) {
|
|
622
|
-
reconnectionPollingTimer?.stop();
|
|
623
|
-
mdnsScanner.cancelOperationalDeviceDiscovery(this.fabric, peerNodeId);
|
|
624
|
-
this.#runningNodeDiscoveries.delete(peerNodeId);
|
|
625
|
-
rejecter(error);
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
).start();
|
|
630
|
-
discoveryPromises.push(() => promise);
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
discoveryPromises.push(async () => {
|
|
634
|
-
const scanResult = await ControllerDiscovery.discoverOperationalDevice(
|
|
635
|
-
this.fabric,
|
|
636
|
-
peerNodeId,
|
|
637
|
-
mdnsScanner,
|
|
638
|
-
timeoutSeconds,
|
|
639
|
-
timeoutSeconds === void 0
|
|
640
|
-
);
|
|
641
|
-
const { timer } = this.#runningNodeDiscoveries.get(peerNodeId) ?? {};
|
|
642
|
-
timer?.stop();
|
|
643
|
-
this.#runningNodeDiscoveries.delete(peerNodeId);
|
|
644
|
-
const { result } = await ControllerDiscovery.iterateServerAddresses(
|
|
645
|
-
[scanResult],
|
|
646
|
-
NoResponseTimeoutError,
|
|
647
|
-
async () => {
|
|
648
|
-
const device = mdnsScanner.getDiscoveredOperationalDevice(this.fabric, peerNodeId);
|
|
649
|
-
return device !== void 0 ? [device] : [];
|
|
650
|
-
},
|
|
651
|
-
async (address, device) => {
|
|
652
|
-
const result2 = await this.pair(peerNodeId, address, device);
|
|
653
|
-
await this.setOperationalDeviceData(peerNodeId, address, {
|
|
654
|
-
...discoveryData,
|
|
655
|
-
...device
|
|
656
|
-
});
|
|
657
|
-
return result2;
|
|
658
|
-
}
|
|
659
|
-
);
|
|
660
|
-
return result;
|
|
661
|
-
});
|
|
662
|
-
this.#runningNodeDiscoveries.set(peerNodeId, {
|
|
663
|
-
type: requestedDiscoveryType,
|
|
664
|
-
promises: discoveryPromises,
|
|
665
|
-
timer: reconnectionPollingTimer
|
|
666
|
-
});
|
|
667
|
-
return await anyPromise(discoveryPromises).finally(() => this.#runningNodeDiscoveries.delete(peerNodeId));
|
|
668
|
-
}
|
|
669
|
-
/**
|
|
670
|
-
* Resume a device connection and establish a CASE session that was previously paired with the controller. This
|
|
671
|
-
* method will try to connect to the device using the previously used server address (if set). If that fails, the
|
|
672
|
-
* device is discovered again using its operational instance details.
|
|
673
|
-
* It returns the operational MessageChannel on success.
|
|
674
|
-
*/
|
|
675
|
-
async resume(peerNodeId, discoveryOptions) {
|
|
676
|
-
const operationalAddress = this.getLastOperationalAddress(peerNodeId);
|
|
677
|
-
try {
|
|
678
|
-
return await this.connectOrDiscoverNode(peerNodeId, operationalAddress, discoveryOptions);
|
|
679
|
-
} catch (error) {
|
|
680
|
-
if ((error instanceof DiscoveryError || error instanceof NoResponseTimeoutError) && this.commissionedNodes.has(peerNodeId)) {
|
|
681
|
-
logger.info(`Resume failed, remove all sessions for node ${peerNodeId}`);
|
|
682
|
-
await this.sessionManager.removeAllSessionsForNode(peerNodeId);
|
|
683
|
-
}
|
|
684
|
-
throw error;
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
/** Pair with an operational device (already commissioned) and establish a CASE session. */
|
|
688
|
-
async pair(peerNodeId, operationalServerAddress, discoveryData, expectedProcessingTimeMs) {
|
|
689
|
-
const { ip, port } = operationalServerAddress;
|
|
690
|
-
const isIpv6Address = isIPv6(ip);
|
|
691
|
-
const operationalInterface = isIpv6Address ? this.netInterfaceIpv6 : this.netInterfaceIpv4;
|
|
692
|
-
if (operationalInterface === void 0) {
|
|
693
|
-
throw new PairRetransmissionLimitReachedError(
|
|
694
|
-
`IPv${isIpv6Address ? "6" : "4"} interface not initialized for port ${port}. Cannot use ${ip} for pairing.`
|
|
695
|
-
);
|
|
696
|
-
}
|
|
697
|
-
const operationalChannel = await operationalInterface.openChannel(operationalServerAddress);
|
|
698
|
-
const { sessionParameters } = this.findResumptionRecordByNodeId(peerNodeId) ?? {};
|
|
699
|
-
const unsecureSession = this.sessionManager.createUnsecureSession({
|
|
700
|
-
// Use the session parameters from MDNS announcements when available and rest is assumed to be fallbacks
|
|
701
|
-
sessionParameters: {
|
|
702
|
-
idleIntervalMs: discoveryData?.SII ?? sessionParameters?.idleIntervalMs,
|
|
703
|
-
activeIntervalMs: discoveryData?.SAI ?? sessionParameters?.activeIntervalMs,
|
|
704
|
-
activeThresholdMs: discoveryData?.SAT ?? sessionParameters?.activeThresholdMs
|
|
705
|
-
},
|
|
706
|
-
isInitiator: true
|
|
707
|
-
});
|
|
708
|
-
const operationalUnsecureMessageExchange = new MessageChannel(operationalChannel, unsecureSession);
|
|
709
|
-
let operationalSecureSession;
|
|
710
|
-
try {
|
|
711
|
-
const exchange = this.exchangeManager.initiateExchangeWithChannel(
|
|
712
|
-
operationalUnsecureMessageExchange,
|
|
713
|
-
SECURE_CHANNEL_PROTOCOL_ID
|
|
714
|
-
);
|
|
715
|
-
try {
|
|
716
|
-
operationalSecureSession = await this.caseClient.pair(
|
|
717
|
-
this,
|
|
718
|
-
exchange,
|
|
719
|
-
this.fabric,
|
|
720
|
-
peerNodeId,
|
|
721
|
-
expectedProcessingTimeMs
|
|
722
|
-
);
|
|
723
|
-
} catch (e) {
|
|
724
|
-
await exchange.close();
|
|
725
|
-
throw e;
|
|
726
|
-
}
|
|
727
|
-
} catch (e) {
|
|
728
|
-
NoResponseTimeoutError.accept(e);
|
|
729
|
-
throw new PairRetransmissionLimitReachedError(e.message);
|
|
730
|
-
}
|
|
731
|
-
await unsecureSession.destroy();
|
|
732
|
-
const channel = new MessageChannel(operationalChannel, operationalSecureSession);
|
|
733
|
-
await this.channelManager.setChannel(this.fabric, peerNodeId, channel);
|
|
734
|
-
return channel;
|
|
735
|
-
}
|
|
736
294
|
isCommissioned() {
|
|
737
|
-
return this.
|
|
295
|
+
return this.peers.size > 0;
|
|
738
296
|
}
|
|
739
297
|
getCommissionedNodes() {
|
|
740
|
-
return
|
|
298
|
+
return this.peers.map((peer) => peer.address.nodeId);
|
|
741
299
|
}
|
|
742
300
|
getCommissionedNodesDetails() {
|
|
743
|
-
return
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
301
|
+
return this.peers.map((peer) => {
|
|
302
|
+
const { address, operationalAddress, discoveryData, basicInformationData } = peer;
|
|
303
|
+
return {
|
|
304
|
+
nodeId: address.nodeId,
|
|
305
|
+
operationalAddress: operationalAddress ? serverAddressToString(operationalAddress) : void 0,
|
|
747
306
|
advertisedName: discoveryData?.DN,
|
|
748
307
|
discoveryData,
|
|
749
308
|
basicInformationData
|
|
750
|
-
})
|
|
751
|
-
);
|
|
752
|
-
}
|
|
753
|
-
async setOperationalDeviceData(nodeId, operationalServerAddress, discoveryData) {
|
|
754
|
-
const nodeDetails = this.commissionedNodes.get(nodeId) ?? {};
|
|
755
|
-
nodeDetails.operationalServerAddress = operationalServerAddress;
|
|
756
|
-
if (discoveryData !== void 0) {
|
|
757
|
-
nodeDetails.discoveryData = {
|
|
758
|
-
...nodeDetails.discoveryData,
|
|
759
|
-
...discoveryData
|
|
760
309
|
};
|
|
761
|
-
}
|
|
762
|
-
this.commissionedNodes.set(nodeId, nodeDetails);
|
|
763
|
-
await this.storeCommissionedNodes();
|
|
310
|
+
});
|
|
764
311
|
}
|
|
765
312
|
async enhanceCommissionedNodeDetails(nodeId, data) {
|
|
766
|
-
const nodeDetails = this.
|
|
313
|
+
const nodeDetails = this.peers.get(this.fabric.addressOf(nodeId));
|
|
767
314
|
if (nodeDetails === void 0) {
|
|
768
315
|
throw new Error(`Node ${nodeId} is not commissioned.`);
|
|
769
316
|
}
|
|
770
317
|
const { basicInformationData } = data;
|
|
771
318
|
nodeDetails.basicInformationData = basicInformationData;
|
|
772
|
-
this.
|
|
773
|
-
await this.storeCommissionedNodes();
|
|
774
|
-
}
|
|
775
|
-
getLastOperationalAddress(nodeId) {
|
|
776
|
-
return this.commissionedNodes.get(nodeId)?.operationalServerAddress;
|
|
777
|
-
}
|
|
778
|
-
async storeCommissionedNodes() {
|
|
779
|
-
await this.nodesStorage.set("commissionedNodes", Array.from(this.commissionedNodes.entries()));
|
|
319
|
+
await this.nodesStore.save();
|
|
780
320
|
}
|
|
781
321
|
/**
|
|
782
322
|
* Connect to the device by opening a channel and creating a new CASE session if necessary.
|
|
783
323
|
* Returns a InteractionClient on success.
|
|
784
324
|
*/
|
|
785
325
|
async connect(peerNodeId, discoveryOptions) {
|
|
786
|
-
|
|
787
|
-
let channel;
|
|
788
|
-
try {
|
|
789
|
-
channel = this.channelManager.getChannel(this.fabric, peerNodeId);
|
|
790
|
-
} catch (error) {
|
|
791
|
-
NoChannelError.accept(error);
|
|
792
|
-
channel = await this.resume(peerNodeId, discoveryOptions);
|
|
793
|
-
}
|
|
794
|
-
return new InteractionClient(
|
|
795
|
-
new ExchangeProvider(this.exchangeManager, channel, async () => {
|
|
796
|
-
if (!this.channelManager.hasChannel(this.fabric, peerNodeId)) {
|
|
797
|
-
throw new RetransmissionLimitReachedError(`Device ${peerNodeId} is currently not reachable.`);
|
|
798
|
-
}
|
|
799
|
-
await this.channelManager.removeAllNodeChannels(this.fabric, peerNodeId);
|
|
800
|
-
const discoveredAddresses = this.mdnsScanner?.getDiscoveredOperationalDevice(this.fabric, peerNodeId);
|
|
801
|
-
const lastKnownAddress = this.getLastOperationalAddress(peerNodeId);
|
|
802
|
-
if (lastKnownAddress !== void 0 && discoveredAddresses !== void 0 && discoveredAddresses.addresses.some(
|
|
803
|
-
({ ip, port }) => ip === lastKnownAddress.ip && port === lastKnownAddress.port
|
|
804
|
-
)) {
|
|
805
|
-
discoveredAddresses.addresses.length = 0;
|
|
806
|
-
}
|
|
807
|
-
const operationalAddress = discoveredAddresses?.addresses[0];
|
|
808
|
-
if (operationalAddress === void 0) {
|
|
809
|
-
logger.info(
|
|
810
|
-
`Re-Discovering device failed (no address found), remove all sessions for node ${peerNodeId}`
|
|
811
|
-
);
|
|
812
|
-
await this.sessionManager.removeAllSessionsForNode(peerNodeId);
|
|
813
|
-
throw new RetransmissionLimitReachedError(`No operational address found for node ${peerNodeId}`);
|
|
814
|
-
}
|
|
815
|
-
if (await this.reconnectKnownAddress(peerNodeId, operationalAddress, discoveryData, 2e3) === void 0) {
|
|
816
|
-
throw new RetransmissionLimitReachedError(`Device ${peerNodeId} is not reachable.`);
|
|
817
|
-
}
|
|
818
|
-
return this.channelManager.getChannel(this.fabric, peerNodeId);
|
|
819
|
-
}),
|
|
820
|
-
peerNodeId
|
|
821
|
-
);
|
|
326
|
+
return this.peers.connect(this.fabric.addressOf(peerNodeId), discoveryOptions);
|
|
822
327
|
}
|
|
823
328
|
async getNextAvailableSessionId() {
|
|
824
329
|
return this.sessionManager.getNextAvailableSessionId();
|
|
@@ -827,7 +332,7 @@ class MatterController {
|
|
|
827
332
|
return this.sessionManager.findResumptionRecordById(resumptionId);
|
|
828
333
|
}
|
|
829
334
|
findResumptionRecordByNodeId(nodeId) {
|
|
830
|
-
return this.sessionManager.
|
|
335
|
+
return this.sessionManager.findResumptionRecordByAddress(this.fabric.addressOf(nodeId));
|
|
831
336
|
}
|
|
832
337
|
async saveResumptionRecord(resumptionRecord) {
|
|
833
338
|
return this.sessionManager.saveResumptionRecord(resumptionRecord);
|
|
@@ -835,23 +340,61 @@ class MatterController {
|
|
|
835
340
|
announce() {
|
|
836
341
|
}
|
|
837
342
|
async close() {
|
|
838
|
-
|
|
839
|
-
timer?.stop();
|
|
840
|
-
this.mdnsScanner?.cancelOperationalDeviceDiscovery(this.fabric, nodeId, false);
|
|
841
|
-
}
|
|
343
|
+
await this.peers.close();
|
|
842
344
|
await this.exchangeManager.close();
|
|
843
345
|
await this.sessionManager.close();
|
|
844
346
|
await this.channelManager.close();
|
|
845
|
-
await this.
|
|
846
|
-
await this.netInterfaceIpv4?.close();
|
|
847
|
-
await this.netInterfaceIpv6?.close();
|
|
347
|
+
await this.netInterfaces.close();
|
|
848
348
|
}
|
|
849
349
|
getActiveSessionInformation() {
|
|
850
350
|
return this.sessionManager.getActiveSessionInformation();
|
|
851
351
|
}
|
|
852
352
|
}
|
|
353
|
+
class CommissionedNodeStore extends PeerStore {
|
|
354
|
+
constructor(nodesStorage, fabric) {
|
|
355
|
+
super();
|
|
356
|
+
this.nodesStorage = nodesStorage;
|
|
357
|
+
this.fabric = fabric;
|
|
358
|
+
}
|
|
359
|
+
async loadPeers() {
|
|
360
|
+
if (!await this.nodesStorage.has("commissionedNodes")) {
|
|
361
|
+
return [];
|
|
362
|
+
}
|
|
363
|
+
const commissionedNodes = await this.nodesStorage.get("commissionedNodes");
|
|
364
|
+
return commissionedNodes.map(
|
|
365
|
+
([nodeId, { operationalServerAddress, discoveryData, basicInformationData }]) => ({
|
|
366
|
+
address: this.fabric.addressOf(nodeId),
|
|
367
|
+
operationalAddress: operationalServerAddress,
|
|
368
|
+
discoveryData,
|
|
369
|
+
basicInformationData
|
|
370
|
+
})
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
async updatePeer() {
|
|
374
|
+
return this.save();
|
|
375
|
+
}
|
|
376
|
+
async deletePeer() {
|
|
377
|
+
return this.save();
|
|
378
|
+
}
|
|
379
|
+
async save() {
|
|
380
|
+
await this.nodesStorage.set(
|
|
381
|
+
"commissionedNodes",
|
|
382
|
+
this.peers.map((peer) => {
|
|
383
|
+
const {
|
|
384
|
+
address,
|
|
385
|
+
operationalAddress: operationalServerAddress,
|
|
386
|
+
basicInformationData,
|
|
387
|
+
discoveryData
|
|
388
|
+
} = peer;
|
|
389
|
+
return [
|
|
390
|
+
address.nodeId,
|
|
391
|
+
{ operationalServerAddress, basicInformationData, discoveryData }
|
|
392
|
+
];
|
|
393
|
+
})
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
853
397
|
export {
|
|
854
|
-
MatterController
|
|
855
|
-
NodeDiscoveryType
|
|
398
|
+
MatterController
|
|
856
399
|
};
|
|
857
400
|
//# sourceMappingURL=MatterController.js.map
|