@matter/nodejs-shell 0.17.9 → 0.18.0-alpha.0-20260812-4d7f2790e
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/README.md +11 -1
- package/dist/esm/MatterNode.js +237 -134
- package/dist/esm/MatterNode.js.map +1 -1
- package/dist/esm/app.js +13 -7
- package/dist/esm/app.js.map +1 -1
- package/dist/esm/shell/cmd_cluster-attributes.js +64 -45
- package/dist/esm/shell/cmd_cluster-attributes.js.map +2 -2
- package/dist/esm/shell/cmd_cluster-commands.js +11 -9
- package/dist/esm/shell/cmd_cluster-commands.js.map +1 -1
- package/dist/esm/shell/cmd_cluster-events.js +36 -9
- package/dist/esm/shell/cmd_cluster-events.js.map +1 -1
- package/dist/esm/shell/cmd_commission.js +119 -93
- package/dist/esm/shell/cmd_commission.js.map +1 -1
- package/dist/esm/shell/cmd_discover.js +23 -21
- package/dist/esm/shell/cmd_discover.js.map +1 -1
- package/dist/esm/shell/cmd_icd.js +46 -30
- package/dist/esm/shell/cmd_icd.js.map +1 -1
- package/dist/esm/shell/cmd_identify.js +11 -7
- package/dist/esm/shell/cmd_identify.js.map +1 -1
- package/dist/esm/shell/cmd_nodes.js +189 -168
- package/dist/esm/shell/cmd_nodes.js.map +1 -1
- package/dist/esm/shell/cmd_session.js +3 -4
- package/dist/esm/shell/cmd_session.js.map +1 -1
- package/dist/esm/shell/cmd_subscribe.js +26 -12
- package/dist/esm/shell/cmd_subscribe.js.map +1 -1
- package/dist/esm/util/ClusterEndpoint.js +61 -0
- package/dist/esm/util/ClusterEndpoint.js.map +6 -0
- package/dist/esm/util/awaitSeeded.js +31 -0
- package/dist/esm/util/awaitSeeded.js.map +6 -0
- package/dist/esm/util/diagnosticLogging.js +99 -0
- package/dist/esm/util/diagnosticLogging.js.map +6 -0
- package/dist/esm/util/legacyStorageMigration.js +188 -0
- package/dist/esm/util/legacyStorageMigration.js.map +6 -0
- package/package.json +10 -11
- package/src/MatterNode.ts +298 -159
- package/src/app.ts +17 -9
- package/src/shell/cmd_cluster-attributes.ts +77 -53
- package/src/shell/cmd_cluster-commands.ts +12 -13
- package/src/shell/cmd_cluster-events.ts +38 -11
- package/src/shell/cmd_commission.ts +136 -113
- package/src/shell/cmd_discover.ts +36 -22
- package/src/shell/cmd_icd.ts +52 -32
- package/src/shell/cmd_identify.ts +11 -7
- package/src/shell/cmd_nodes.ts +226 -199
- package/src/shell/cmd_session.ts +3 -5
- package/src/shell/cmd_subscribe.ts +37 -14
- package/src/shell/webassets/index.html +1 -1
- package/src/tsconfig.json +0 -3
- package/src/util/ClusterEndpoint.ts +107 -0
- package/src/util/awaitSeeded.ts +43 -0
- package/src/util/diagnosticLogging.ts +117 -0
- package/src/util/legacyStorageMigration.ts +275 -0
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* Copyright 2022-2026 Matter.js Authors
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { IdentifyClient } from "@matter/node/behaviors/identify";
|
|
7
|
+
import { awaitSeeded } from "../util/awaitSeeded.js";
|
|
7
8
|
function commands(theNode) {
|
|
8
9
|
return {
|
|
9
10
|
command: "identify [time] [node-id] [endpoint-id]",
|
|
@@ -25,17 +26,20 @@ function commands(theNode) {
|
|
|
25
26
|
},
|
|
26
27
|
handler: async (argv) => {
|
|
27
28
|
const { nodeId, time = 10, endpointId } = argv;
|
|
29
|
+
const nodes = await theNode.connectAndGetNodes(nodeId);
|
|
30
|
+
for (const node of nodes) {
|
|
31
|
+
await awaitSeeded(node);
|
|
32
|
+
}
|
|
28
33
|
await theNode.iterateNodeDevices(
|
|
29
|
-
|
|
34
|
+
nodes,
|
|
30
35
|
async (device, node) => {
|
|
31
|
-
|
|
32
|
-
if (identifyCluster === void 0) {
|
|
36
|
+
if (device.number === 0 || !device.behaviors.has(IdentifyClient)) {
|
|
33
37
|
return;
|
|
34
38
|
}
|
|
35
|
-
console.log("Invoke Identify for", node.nodeId.toString());
|
|
36
|
-
await
|
|
39
|
+
console.log("Invoke Identify for", node.peerAddress?.nodeId.toString());
|
|
40
|
+
await device.act((agent) => agent.get(IdentifyClient).identify({ identifyTime: time }));
|
|
37
41
|
},
|
|
38
|
-
endpointId
|
|
42
|
+
endpointId !== void 0 ? [endpointId] : void 0
|
|
39
43
|
);
|
|
40
44
|
}
|
|
41
45
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/shell/cmd_identify.ts"],
|
|
4
|
-
"mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,SAAS,
|
|
4
|
+
"mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,SAAS,sBAAsB;AAG/B,SAAS,mBAAmB;AAEb,SAAR,SAA0B,SAAqB;AAClD,SAAO;AAAA,IACH,SAAS;AAAA,IACT,UACI;AAAA,IACJ,SAAS,CAAC,UAAgB;AACtB,aAAO,MACF,WAAW,QAAQ;AAAA,QAChB,UAAU;AAAA,QACV,SAAS;AAAA,QACT,MAAM;AAAA,MACV,CAAC,EACA,WAAW,WAAW;AAAA,QACnB,UAAU;AAAA,QACV,SAAS;AAAA,QACT,MAAM;AAAA,MACV,CAAC,EACA,WAAW,eAAe;AAAA,QACvB,UAAU;AAAA,QACV,SAAS;AAAA,QACT,MAAM;AAAA,MACV,CAAC;AAAA,IACT;AAAA,IAEA,SAAS,OAAO,SAAc;AAC1B,YAAM,EAAE,QAAQ,OAAO,IAAI,WAAW,IAAI;AAC1C,YAAM,QAAQ,MAAM,QAAQ,mBAAmB,MAAM;AACrD,iBAAW,QAAQ,OAAO;AACtB,cAAM,YAAY,IAAI;AAAA,MAC1B;AACA,YAAM,QAAQ;AAAA,QACV;AAAA,QACA,OAAO,QAAQ,SAAS;AACpB,cAAI,OAAO,WAAW,KAAK,CAAC,OAAO,UAAU,IAAI,cAAc,GAAG;AAC9D;AAAA,UACJ;AACA,kBAAQ,IAAI,uBAAuB,KAAK,aAAa,OAAO,SAAS,CAAC;AACtE,gBAAM,OAAO,IAAI,WAAS,MAAM,IAAI,cAAc,EAAE,SAAS,EAAE,cAAc,KAAK,CAAC,CAAC;AAAA,QACxF;AAAA,QACA,eAAe,SAAY,CAAC,UAAU,IAAI;AAAA,MAC9C;AAAA,IACJ;AAAA,EACJ;AACJ;",
|
|
5
5
|
"names": []
|
|
6
6
|
}
|
|
@@ -3,17 +3,38 @@
|
|
|
3
3
|
* Copyright 2022-2026 Matter.js Authors
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
import {
|
|
7
|
+
capitalize,
|
|
8
|
+
ChannelType,
|
|
9
|
+
decamelize,
|
|
10
|
+
Diagnostic,
|
|
11
|
+
ImplementationError,
|
|
12
|
+
InternalError,
|
|
13
|
+
Logger,
|
|
14
|
+
Millis,
|
|
15
|
+
ServerAddress
|
|
16
|
+
} from "@matter/general";
|
|
17
|
+
import {
|
|
18
|
+
CommissioningClient,
|
|
19
|
+
NetworkClient,
|
|
20
|
+
NodeConnectionState,
|
|
21
|
+
SoftwareUpdateManager
|
|
22
|
+
} from "@matter/node";
|
|
23
|
+
import { BasicInformationClient } from "@matter/node/behaviors/basic-information";
|
|
24
|
+
import { FabricAuthority, PeerAddress, PeerSet } from "@matter/protocol";
|
|
9
25
|
import { FabricIndex, NodeId } from "@matter/types";
|
|
10
|
-
import {
|
|
11
|
-
import { createDiagnosticCallbacks } from "../MatterNode.js";
|
|
26
|
+
import { awaitSeeded } from "../util/awaitSeeded.js";
|
|
12
27
|
import { resolveDclMode, withDclModeOption } from "./ota-dcl-mode.js";
|
|
28
|
+
const logger = Logger.get("cmd_nodes");
|
|
29
|
+
function findCommissionedNode(theNode, nodeId) {
|
|
30
|
+
return theNode.node.peers.commissioned.find((peer) => peer.peerAddress?.nodeId === nodeId);
|
|
31
|
+
}
|
|
13
32
|
function parseFallbackAddress(input) {
|
|
14
33
|
const match = /^(udp|tcp):\/\/(.+)$/i.exec(input);
|
|
15
34
|
if (!match) {
|
|
16
|
-
throw new
|
|
35
|
+
throw new ImplementationError(
|
|
36
|
+
`Invalid address "${input}". Expected udp://<host>:<port> or tcp://<host>:<port>`
|
|
37
|
+
);
|
|
17
38
|
}
|
|
18
39
|
const type = match[1].toLowerCase() === "tcp" ? "tcp" : "udp";
|
|
19
40
|
const rest = match[2];
|
|
@@ -22,21 +43,23 @@ function parseFallbackAddress(input) {
|
|
|
22
43
|
if (rest.startsWith("[")) {
|
|
23
44
|
const end = rest.indexOf("]");
|
|
24
45
|
if (end === -1 || rest[end + 1] !== ":") {
|
|
25
|
-
throw new
|
|
46
|
+
throw new ImplementationError(`Invalid IPv6 address "${input}". Expected ${type}://[<ipv6>]:<port>`);
|
|
26
47
|
}
|
|
27
48
|
ip = rest.slice(1, end);
|
|
28
49
|
portStr = rest.slice(end + 2);
|
|
29
50
|
} else {
|
|
30
51
|
const idx = rest.lastIndexOf(":");
|
|
31
52
|
if (idx === -1) {
|
|
32
|
-
throw new
|
|
53
|
+
throw new ImplementationError(`Missing port in "${input}". Expected ${type}://<host>:<port>`);
|
|
33
54
|
}
|
|
34
55
|
ip = rest.slice(0, idx);
|
|
35
56
|
portStr = rest.slice(idx + 1);
|
|
36
57
|
}
|
|
37
58
|
const port = Number(portStr);
|
|
38
59
|
if (!ip.length || !Number.isInteger(port) || port < 1 || port > 65535) {
|
|
39
|
-
throw new
|
|
60
|
+
throw new ImplementationError(
|
|
61
|
+
`Invalid host/port in "${input}". Expected ${type}://<host>:<port> with port 1-65535`
|
|
62
|
+
);
|
|
40
63
|
}
|
|
41
64
|
return { ip, port, type };
|
|
42
65
|
}
|
|
@@ -58,23 +81,23 @@ function commands(theNode) {
|
|
|
58
81
|
async (argv) => {
|
|
59
82
|
const { status } = argv;
|
|
60
83
|
await theNode.start();
|
|
61
|
-
|
|
62
|
-
throw new Error("CommissioningController not initialized");
|
|
63
|
-
}
|
|
84
|
+
const peers = theNode.node.peers.commissioned;
|
|
64
85
|
switch (status) {
|
|
65
86
|
case "commissioned": {
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
87
|
+
for (const peer of peers) {
|
|
88
|
+
const { addresses, deviceName } = peer.state.commissioning;
|
|
89
|
+
console.log({
|
|
90
|
+
nodeId: peer.peerAddress?.nodeId.toString(),
|
|
91
|
+
operationalAddress: addresses?.length ? ServerAddress.urlFor(addresses[0]) : void 0,
|
|
92
|
+
advertisedName: deviceName,
|
|
93
|
+
basicInformation: peer.maybeStateOf(BasicInformationClient)
|
|
94
|
+
});
|
|
95
|
+
}
|
|
73
96
|
break;
|
|
74
97
|
}
|
|
75
98
|
case "connected": {
|
|
76
|
-
const nodeIds =
|
|
77
|
-
console.log(nodeIds.map((nodeId) => nodeId
|
|
99
|
+
const nodeIds = peers.filter((peer) => peer.lifecycle.isConnected).map((peer) => peer.peerAddress?.nodeId);
|
|
100
|
+
console.log(nodeIds.map((nodeId) => nodeId?.toString()));
|
|
78
101
|
break;
|
|
79
102
|
}
|
|
80
103
|
}
|
|
@@ -92,8 +115,11 @@ function commands(theNode) {
|
|
|
92
115
|
async (argv) => {
|
|
93
116
|
const { nodeId } = argv;
|
|
94
117
|
const node = (await theNode.connectAndGetNodes(nodeId))[0];
|
|
95
|
-
|
|
96
|
-
|
|
118
|
+
if (!await awaitSeeded(node)) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
console.log("Logging structure of Node ", node.peerAddress?.nodeId.toString());
|
|
122
|
+
logger.info(node);
|
|
97
123
|
}
|
|
98
124
|
).command(
|
|
99
125
|
"descriptor <node-id>",
|
|
@@ -108,11 +134,12 @@ function commands(theNode) {
|
|
|
108
134
|
async (argv) => {
|
|
109
135
|
const { nodeId: nodeIdStr } = argv;
|
|
110
136
|
await theNode.start();
|
|
111
|
-
if (theNode.commissioningController === void 0) {
|
|
112
|
-
throw new Error("CommissioningController not initialized");
|
|
113
|
-
}
|
|
114
137
|
const nodeId = NodeId(BigInt(nodeIdStr));
|
|
115
|
-
const peerAddress = theNode
|
|
138
|
+
const peerAddress = findCommissionedNode(theNode, nodeId)?.peerAddress;
|
|
139
|
+
if (peerAddress === void 0) {
|
|
140
|
+
console.log(`Node ${nodeIdStr} not commissioned`);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
116
143
|
const peerSet = theNode.node.env.get(PeerSet);
|
|
117
144
|
const peer = peerSet.for(peerAddress);
|
|
118
145
|
if (!peer) {
|
|
@@ -181,28 +208,12 @@ Peer Descriptor for node ${nodeIdStr}:`);
|
|
|
181
208
|
},
|
|
182
209
|
async (argv) => {
|
|
183
210
|
const { nodeId: nodeIdStr, maxSubscriptionInterval, minSubscriptionInterval } = argv;
|
|
184
|
-
await theNode.start();
|
|
185
|
-
if (theNode.commissioningController === void 0) {
|
|
186
|
-
throw new Error("CommissioningController not initialized");
|
|
187
|
-
}
|
|
188
|
-
let nodeIds = theNode.commissioningController.getCommissionedNodes();
|
|
189
|
-
if (nodeIdStr !== "all") {
|
|
190
|
-
const cmdNodeId = NodeId(BigInt(nodeIdStr));
|
|
191
|
-
nodeIds = nodeIds.filter((nodeId) => nodeId === cmdNodeId);
|
|
192
|
-
if (!nodeIds.length) {
|
|
193
|
-
throw new Error(`Node ${nodeIdStr} not commissioned`);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
211
|
const autoSubscribe = minSubscriptionInterval !== void 0;
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
subscribeMaxIntervalCeilingSeconds: autoSubscribe ? maxSubscriptionInterval : void 0,
|
|
203
|
-
...createDiagnosticCallbacks()
|
|
204
|
-
});
|
|
205
|
-
}
|
|
212
|
+
await theNode.connectAndGetNodes(nodeIdStr !== "all" ? nodeIdStr : void 0, {
|
|
213
|
+
autoSubscribe,
|
|
214
|
+
subscribeMinIntervalFloorSeconds: autoSubscribe ? minSubscriptionInterval : void 0,
|
|
215
|
+
subscribeMaxIntervalCeilingSeconds: autoSubscribe ? maxSubscriptionInterval : void 0
|
|
216
|
+
});
|
|
206
217
|
}
|
|
207
218
|
).command(
|
|
208
219
|
"disconnect [node-id]",
|
|
@@ -216,25 +227,17 @@ Peer Descriptor for node ${nodeIdStr}:`);
|
|
|
216
227
|
},
|
|
217
228
|
async (argv) => {
|
|
218
229
|
const { nodeId: nodeIdStr } = argv;
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
let nodeIds = theNode.commissioningController.getCommissionedNodes();
|
|
230
|
+
await theNode.start();
|
|
231
|
+
let nodes = theNode.node.peers.commissioned;
|
|
224
232
|
if (nodeIdStr !== "all") {
|
|
225
233
|
const cmdNodeId = NodeId(BigInt(nodeIdStr));
|
|
226
|
-
|
|
227
|
-
if (!
|
|
228
|
-
throw new
|
|
234
|
+
nodes = nodes.filter((node) => node.peerAddress?.nodeId === cmdNodeId);
|
|
235
|
+
if (!nodes.length) {
|
|
236
|
+
throw new ImplementationError(`Node ${nodeIdStr} not commissioned`);
|
|
229
237
|
}
|
|
230
238
|
}
|
|
231
|
-
for (const
|
|
232
|
-
|
|
233
|
-
if (node === void 0) {
|
|
234
|
-
console.log(`Node ${nodeIdToProcess} not connected`);
|
|
235
|
-
continue;
|
|
236
|
-
}
|
|
237
|
-
await node.disconnect();
|
|
239
|
+
for (const node of nodes) {
|
|
240
|
+
await node.stop();
|
|
238
241
|
}
|
|
239
242
|
}
|
|
240
243
|
).command(
|
|
@@ -250,31 +253,24 @@ Peer Descriptor for node ${nodeIdStr}:`);
|
|
|
250
253
|
async (argv) => {
|
|
251
254
|
const { nodeIds: nodeIdStr } = argv;
|
|
252
255
|
await theNode.start();
|
|
253
|
-
|
|
254
|
-
throw new Error("CommissioningController not initialized");
|
|
255
|
-
}
|
|
256
|
-
let nodeIds = theNode.commissioningController.getCommissionedNodes();
|
|
256
|
+
let nodes = theNode.node.peers.commissioned;
|
|
257
257
|
if (nodeIdStr !== "all") {
|
|
258
258
|
const nodeIdList = nodeIdStr.split(",").map((nodeId) => NodeId(BigInt(nodeId)));
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
259
|
+
nodes = nodes.filter(
|
|
260
|
+
(node) => node.peerAddress !== void 0 && nodeIdList.includes(node.peerAddress.nodeId)
|
|
261
|
+
);
|
|
262
|
+
if (!nodes.length) {
|
|
263
|
+
throw new ImplementationError(`Node ${nodeIdStr} not commissioned`);
|
|
262
264
|
}
|
|
263
265
|
}
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
} else {
|
|
273
|
-
const basicInfo = node.basicInformation;
|
|
274
|
-
console.log(
|
|
275
|
-
`Node ${nodeIdToProcess}: Node Status: ${capitalize(decamelize(NodeStateInformation[node.connectionState], " "))}${basicInfo !== void 0 ? ` (${basicInfo.vendorName} ${basicInfo.productName})` : ""}`
|
|
276
|
-
);
|
|
277
|
-
}
|
|
266
|
+
for (const node of nodes) {
|
|
267
|
+
const basicInfo = node.maybeStateOf(BasicInformationClient);
|
|
268
|
+
const status = capitalize(
|
|
269
|
+
decamelize(NodeConnectionState[node.lifecycle.connectionState], " ")
|
|
270
|
+
);
|
|
271
|
+
console.log(
|
|
272
|
+
`Node ${node.peerAddress?.nodeId}: Node Status: ${status}${basicInfo !== void 0 ? ` (${basicInfo.vendorName} ${basicInfo.productName})` : ""}`
|
|
273
|
+
);
|
|
278
274
|
}
|
|
279
275
|
}
|
|
280
276
|
).command(
|
|
@@ -295,14 +291,18 @@ Peer Descriptor for node ${nodeIdStr}:`);
|
|
|
295
291
|
async (argv) => {
|
|
296
292
|
const { nodeId: nodeIdStr, preference } = argv;
|
|
297
293
|
await theNode.start();
|
|
298
|
-
if (theNode.commissioningController === void 0) {
|
|
299
|
-
throw new Error("CommissioningController not initialized");
|
|
300
|
-
}
|
|
301
294
|
const nodeId = NodeId(BigInt(nodeIdStr));
|
|
302
|
-
const node =
|
|
295
|
+
const node = findCommissionedNode(theNode, nodeId);
|
|
296
|
+
if (node === void 0) {
|
|
297
|
+
throw new ImplementationError(`Node ${nodeIdStr} not commissioned`);
|
|
298
|
+
}
|
|
299
|
+
const peerAddress = node.peerAddress;
|
|
300
|
+
if (peerAddress === void 0) {
|
|
301
|
+
throw new ImplementationError(`Node ${nodeIdStr} has no peer address`);
|
|
302
|
+
}
|
|
303
303
|
const pref = preference === "on" ? "tcp" : preference === "off" ? "udp" : void 0;
|
|
304
|
-
await node.
|
|
305
|
-
const peer = theNode.node.env.get(PeerSet).for(
|
|
304
|
+
await node.setStateOf(NetworkClient, { transportPreference: pref });
|
|
305
|
+
const peer = theNode.node.env.get(PeerSet).for(peerAddress);
|
|
306
306
|
if (peer) {
|
|
307
307
|
peer.transportPreference = pref === "tcp" ? ChannelType.TCP : void 0;
|
|
308
308
|
}
|
|
@@ -326,12 +326,12 @@ Peer Descriptor for node ${nodeIdStr}:`);
|
|
|
326
326
|
async (argv) => {
|
|
327
327
|
const { nodeId: nodeIdStr } = argv;
|
|
328
328
|
await theNode.start();
|
|
329
|
-
if (theNode.commissioningController === void 0) {
|
|
330
|
-
throw new Error("CommissioningController not initialized");
|
|
331
|
-
}
|
|
332
329
|
const nodeId = NodeId(BigInt(nodeIdStr));
|
|
333
|
-
const node =
|
|
334
|
-
|
|
330
|
+
const node = findCommissionedNode(theNode, nodeId);
|
|
331
|
+
if (node === void 0) {
|
|
332
|
+
throw new ImplementationError(`Node ${nodeIdStr} not commissioned`);
|
|
333
|
+
}
|
|
334
|
+
const addresses = node.state.commissioning.addresses;
|
|
335
335
|
if (!addresses?.length) {
|
|
336
336
|
console.log(`Node ${nodeIdStr} has no fallback addresses stored`);
|
|
337
337
|
return;
|
|
@@ -358,18 +358,18 @@ Peer Descriptor for node ${nodeIdStr}:`);
|
|
|
358
358
|
async (argv) => {
|
|
359
359
|
const { nodeId: nodeIdStr, address: addressStr } = argv;
|
|
360
360
|
await theNode.start();
|
|
361
|
-
if (theNode.commissioningController === void 0) {
|
|
362
|
-
throw new Error("CommissioningController not initialized");
|
|
363
|
-
}
|
|
364
361
|
const nodeId = NodeId(BigInt(nodeIdStr));
|
|
365
|
-
const node =
|
|
362
|
+
const node = findCommissionedNode(theNode, nodeId);
|
|
363
|
+
if (node === void 0) {
|
|
364
|
+
throw new ImplementationError(`Node ${nodeIdStr} not commissioned`);
|
|
365
|
+
}
|
|
366
366
|
if (addressStr === "drop") {
|
|
367
|
-
await node.
|
|
367
|
+
await node.setStateOf(CommissioningClient, { addresses: void 0 });
|
|
368
368
|
console.log(`Fallback address for node ${nodeIdStr} dropped`);
|
|
369
369
|
return;
|
|
370
370
|
}
|
|
371
371
|
const address = parseFallbackAddress(addressStr);
|
|
372
|
-
await node.
|
|
372
|
+
await node.setStateOf(CommissioningClient, { addresses: [address] });
|
|
373
373
|
console.log(
|
|
374
374
|
`Fallback address for node ${nodeIdStr} set to ${ServerAddress.urlFor(address)}`
|
|
375
375
|
);
|
|
@@ -398,25 +398,20 @@ Peer Descriptor for node ${nodeIdStr}:`);
|
|
|
398
398
|
async (argv) => {
|
|
399
399
|
const { nodeId: nodeIdStr, maxSubscriptionInterval, minSubscriptionInterval } = argv;
|
|
400
400
|
await theNode.start();
|
|
401
|
-
if (theNode.commissioningController === void 0) {
|
|
402
|
-
throw new Error("CommissioningController not initialized");
|
|
403
|
-
}
|
|
404
|
-
let nodeIds = theNode.commissioningController.getCommissionedNodes();
|
|
405
401
|
const cmdNodeId = NodeId(BigInt(nodeIdStr));
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
throw new Error(`Node ${nodeIdStr} already known`);
|
|
402
|
+
if (findCommissionedNode(theNode, cmdNodeId) !== void 0) {
|
|
403
|
+
throw new ImplementationError(`Node ${nodeIdStr} already known`);
|
|
409
404
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
405
|
+
const fabric = theNode.node.env.get(FabricAuthority).fabrics[0];
|
|
406
|
+
if (fabric === void 0) {
|
|
407
|
+
throw new InternalError("No controller fabric present after start");
|
|
408
|
+
}
|
|
409
|
+
await theNode.node.peers.forAddress(fabric.addressOf(cmdNodeId));
|
|
413
410
|
const autoSubscribe = minSubscriptionInterval !== void 0;
|
|
414
|
-
|
|
415
|
-
node.connect({
|
|
411
|
+
await theNode.connectAndGetNodes(nodeIdStr, {
|
|
416
412
|
autoSubscribe,
|
|
417
413
|
subscribeMinIntervalFloorSeconds: autoSubscribe ? minSubscriptionInterval : void 0,
|
|
418
|
-
subscribeMaxIntervalCeilingSeconds: autoSubscribe ? maxSubscriptionInterval : void 0
|
|
419
|
-
...createDiagnosticCallbacks()
|
|
414
|
+
subscribeMaxIntervalCeilingSeconds: autoSubscribe ? maxSubscriptionInterval : void 0
|
|
420
415
|
});
|
|
421
416
|
}
|
|
422
417
|
).command(
|
|
@@ -439,15 +434,15 @@ Peer Descriptor for node ${nodeIdStr}:`);
|
|
|
439
434
|
async (argv) => {
|
|
440
435
|
const { nodeId: nodeIdStr, local } = argv;
|
|
441
436
|
await theNode.start();
|
|
442
|
-
if (theNode.commissioningController === void 0) {
|
|
443
|
-
throw new Error("CommissioningController not initialized");
|
|
444
|
-
}
|
|
445
437
|
let peerToCheck = void 0;
|
|
446
438
|
if (nodeIdStr !== void 0) {
|
|
447
439
|
const nodeId = NodeId(BigInt(nodeIdStr));
|
|
448
|
-
peerToCheck = (
|
|
440
|
+
peerToCheck = findCommissionedNode(theNode, nodeId);
|
|
441
|
+
if (peerToCheck === void 0) {
|
|
442
|
+
throw new ImplementationError(`Node ${nodeIdStr} not commissioned`);
|
|
443
|
+
}
|
|
449
444
|
}
|
|
450
|
-
const updatesAvailable = await theNode.
|
|
445
|
+
const updatesAvailable = await theNode.otaProviderEndpoint.act(
|
|
451
446
|
(agent) => agent.get(SoftwareUpdateManager).queryUpdates({ peerToCheck, includeStoredUpdates: local })
|
|
452
447
|
);
|
|
453
448
|
if (updatesAvailable.length) {
|
|
@@ -482,24 +477,24 @@ Peer Descriptor for node ${nodeIdStr}:`);
|
|
|
482
477
|
const { nodeId: nodeIdStr, mode, local } = argv;
|
|
483
478
|
const { label: dclMode, isProduction } = resolveDclMode(theNode, mode);
|
|
484
479
|
await theNode.start();
|
|
485
|
-
if (theNode.commissioningController === void 0) {
|
|
486
|
-
throw new Error("CommissioningController not initialized");
|
|
487
|
-
}
|
|
488
480
|
const nodeId = NodeId(BigInt(nodeIdStr));
|
|
489
|
-
const
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
481
|
+
const node = findCommissionedNode(theNode, nodeId);
|
|
482
|
+
if (node === void 0) {
|
|
483
|
+
throw new ImplementationError(`Node ${nodeIdStr} not commissioned`);
|
|
484
|
+
}
|
|
485
|
+
const basicInfo = node.maybeStateOf(BasicInformationClient);
|
|
486
|
+
if (basicInfo === void 0) {
|
|
487
|
+
throw new ImplementationError(
|
|
488
|
+
`Node ${nodeIdStr} has no basic information available`
|
|
489
|
+
);
|
|
493
490
|
}
|
|
494
491
|
if (basicInfo.vendorId === void 0 || basicInfo.productId === void 0 || basicInfo.softwareVersion === void 0) {
|
|
495
|
-
throw new
|
|
496
|
-
`Node ${nodeIdStr} is
|
|
492
|
+
throw new ImplementationError(
|
|
493
|
+
`Node ${nodeIdStr} BasicInformation is incomplete; connect the node first`
|
|
497
494
|
);
|
|
498
495
|
}
|
|
499
496
|
console.log(`Checking for OTA updates for node ${nodeIdStr}...`);
|
|
500
|
-
console.log(
|
|
501
|
-
` Vendor ID: ${Diagnostic.hex(basicInfo.vendorId, 4).toUpperCase()}`
|
|
502
|
-
);
|
|
497
|
+
console.log(` Vendor ID: ${Diagnostic.hex(basicInfo.vendorId, 4).toUpperCase()}`);
|
|
503
498
|
console.log(
|
|
504
499
|
` Product ID: ${Diagnostic.hex(basicInfo.productId, 4).toUpperCase()}`
|
|
505
500
|
);
|
|
@@ -561,24 +556,24 @@ Run "nodes ota download ${nodeIdStr}${mode === "auto" ? "" : ` --mode ${mode}`}"
|
|
|
561
556
|
const { label: dclMode, isProduction } = resolveDclMode(theNode, mode);
|
|
562
557
|
const forceDownload = force === true;
|
|
563
558
|
await theNode.start();
|
|
564
|
-
if (theNode.commissioningController === void 0) {
|
|
565
|
-
throw new Error("CommissioningController not initialized");
|
|
566
|
-
}
|
|
567
559
|
const nodeId = NodeId(BigInt(nodeIdStr));
|
|
568
|
-
const
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
560
|
+
const node = findCommissionedNode(theNode, nodeId);
|
|
561
|
+
if (node === void 0) {
|
|
562
|
+
throw new ImplementationError(`Node ${nodeIdStr} not commissioned`);
|
|
563
|
+
}
|
|
564
|
+
const basicInfo = node.maybeStateOf(BasicInformationClient);
|
|
565
|
+
if (basicInfo === void 0) {
|
|
566
|
+
throw new ImplementationError(
|
|
567
|
+
`Node ${nodeIdStr} has no basic information available`
|
|
568
|
+
);
|
|
572
569
|
}
|
|
573
570
|
if (basicInfo.vendorId === void 0 || basicInfo.productId === void 0 || basicInfo.softwareVersion === void 0) {
|
|
574
|
-
throw new
|
|
575
|
-
`Node ${nodeIdStr} is
|
|
571
|
+
throw new ImplementationError(
|
|
572
|
+
`Node ${nodeIdStr} BasicInformation is incomplete; connect the node first`
|
|
576
573
|
);
|
|
577
574
|
}
|
|
578
575
|
console.log(`Checking for OTA updates for node ${nodeIdStr}...`);
|
|
579
|
-
console.log(
|
|
580
|
-
` Vendor ID: ${Diagnostic.hex(basicInfo.vendorId, 4).toUpperCase()}`
|
|
581
|
-
);
|
|
576
|
+
console.log(` Vendor ID: ${Diagnostic.hex(basicInfo.vendorId, 4).toUpperCase()}`);
|
|
582
577
|
console.log(
|
|
583
578
|
` Product ID: ${Diagnostic.hex(basicInfo.productId, 4).toUpperCase()}`
|
|
584
579
|
);
|
|
@@ -634,31 +629,47 @@ You can now apply this update to the device using your device's OTA mechanism.`
|
|
|
634
629
|
describe: "Apply update from local file",
|
|
635
630
|
type: "boolean",
|
|
636
631
|
default: false
|
|
632
|
+
}).option("max-block-size", {
|
|
633
|
+
describe: "Cap the BDX block size for this transfer, in bytes (default: whatever the device requests)",
|
|
634
|
+
type: "number"
|
|
635
|
+
}).option("mrp-margin", {
|
|
636
|
+
describe: "Additive MRP retransmission margin for this transfer, in milliseconds (default: derived from the device's medium)",
|
|
637
|
+
type: "number"
|
|
638
|
+
}).check((argv) => {
|
|
639
|
+
const blockSize = argv.maxBlockSize;
|
|
640
|
+
const margin = argv.mrpMargin;
|
|
641
|
+
if (blockSize !== void 0 && (!Number.isInteger(blockSize) || blockSize <= 0)) {
|
|
642
|
+
throw new Error("--max-block-size must be a positive integer");
|
|
643
|
+
}
|
|
644
|
+
if (margin !== void 0 && (!Number.isFinite(margin) || margin < 0)) {
|
|
645
|
+
throw new Error("--mrp-margin must be a non-negative number of ms");
|
|
646
|
+
}
|
|
647
|
+
return true;
|
|
637
648
|
});
|
|
638
649
|
},
|
|
639
650
|
async (argv) => {
|
|
640
|
-
const { nodeId: nodeIdStr, mode, force, local } = argv;
|
|
651
|
+
const { nodeId: nodeIdStr, mode, force, local, maxBlockSize, mrpMargin } = argv;
|
|
641
652
|
const { label: dclMode, isProduction } = resolveDclMode(theNode, mode);
|
|
642
653
|
const forceDownload = force === true;
|
|
643
654
|
await theNode.start();
|
|
644
|
-
if (theNode.commissioningController === void 0) {
|
|
645
|
-
throw new Error("CommissioningController not initialized");
|
|
646
|
-
}
|
|
647
655
|
const nodeId = NodeId(BigInt(nodeIdStr));
|
|
648
|
-
const
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
656
|
+
const node = findCommissionedNode(theNode, nodeId);
|
|
657
|
+
if (node === void 0) {
|
|
658
|
+
throw new ImplementationError(`Node ${nodeIdStr} not commissioned`);
|
|
659
|
+
}
|
|
660
|
+
const basicInfo = node.maybeStateOf(BasicInformationClient);
|
|
661
|
+
if (basicInfo === void 0) {
|
|
662
|
+
throw new ImplementationError(
|
|
663
|
+
`Node ${nodeIdStr} has no basic information available`
|
|
664
|
+
);
|
|
652
665
|
}
|
|
653
666
|
if (basicInfo.vendorId === void 0 || basicInfo.productId === void 0 || basicInfo.softwareVersion === void 0) {
|
|
654
|
-
throw new
|
|
655
|
-
`Node ${nodeIdStr} is
|
|
667
|
+
throw new ImplementationError(
|
|
668
|
+
`Node ${nodeIdStr} BasicInformation is incomplete; connect the node first`
|
|
656
669
|
);
|
|
657
670
|
}
|
|
658
671
|
console.log(`Checking for OTA updates for node ${nodeIdStr}...`);
|
|
659
|
-
console.log(
|
|
660
|
-
` Vendor ID: ${Diagnostic.hex(basicInfo.vendorId, 4).toUpperCase()}`
|
|
661
|
-
);
|
|
672
|
+
console.log(` Vendor ID: ${Diagnostic.hex(basicInfo.vendorId, 4).toUpperCase()}`);
|
|
662
673
|
console.log(
|
|
663
674
|
` Product ID: ${Diagnostic.hex(basicInfo.productId, 4).toUpperCase()}`
|
|
664
675
|
);
|
|
@@ -709,17 +720,27 @@ You can now apply this update to the device using your device's OTA mechanism.`
|
|
|
709
720
|
`Update to version ${updateVersion} (${localUpdates[0].softwareVersionString}) found in local storage: ${localUpdates[0].filename}`
|
|
710
721
|
);
|
|
711
722
|
}
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
+
if (!node.lifecycle.isConnected) {
|
|
724
|
+
throw new ImplementationError(`Node ${nodeIdStr} not connected`);
|
|
725
|
+
}
|
|
726
|
+
const peerAddress = node.peerAddress;
|
|
727
|
+
if (peerAddress === void 0) {
|
|
728
|
+
throw new ImplementationError(`Node ${nodeIdStr} has no peer address`);
|
|
729
|
+
}
|
|
730
|
+
if (maxBlockSize !== void 0) {
|
|
731
|
+
console.log(`Capping BDX block size to ${maxBlockSize} bytes`);
|
|
732
|
+
}
|
|
733
|
+
if (mrpMargin !== void 0) {
|
|
734
|
+
console.log(`Using an MRP retransmission margin of ${mrpMargin}ms`);
|
|
735
|
+
}
|
|
736
|
+
await theNode.otaProviderEndpoint.act((agent) => {
|
|
737
|
+
return agent.get(SoftwareUpdateManager).forceUpdate(PeerAddress({ nodeId, fabricIndex: FabricIndex(1) }), {
|
|
738
|
+
vendorId: basicInfo.vendorId,
|
|
739
|
+
productId: basicInfo.productId,
|
|
740
|
+
targetSoftwareVersion: updateVersion,
|
|
741
|
+
maxBdxBlockSize: maxBlockSize,
|
|
742
|
+
bdxAdditionalMrpDelay: mrpMargin === void 0 ? void 0 : Millis(mrpMargin)
|
|
743
|
+
});
|
|
723
744
|
});
|
|
724
745
|
}
|
|
725
746
|
).demandCommand(1, "Please specify an OTA subcommand"),
|