@matter/nodejs-shell 0.11.0-alpha.0-20241007-547af42a8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +192 -0
  3. package/dist/cjs/MatterNode.js +115 -0
  4. package/dist/cjs/MatterNode.js.map +6 -0
  5. package/dist/cjs/app.js +162 -0
  6. package/dist/cjs/app.js.map +6 -0
  7. package/dist/cjs/package.json +7 -0
  8. package/dist/cjs/shell/Shell.js +195 -0
  9. package/dist/cjs/shell/Shell.js.map +6 -0
  10. package/dist/cjs/shell/cmd_cluster-attributes.js +310 -0
  11. package/dist/cjs/shell/cmd_cluster-attributes.js.map +6 -0
  12. package/dist/cjs/shell/cmd_cluster-commands.js +158 -0
  13. package/dist/cjs/shell/cmd_cluster-commands.js.map +6 -0
  14. package/dist/cjs/shell/cmd_cluster-events.js +104 -0
  15. package/dist/cjs/shell/cmd_cluster-events.js.map +6 -0
  16. package/dist/cjs/shell/cmd_commission.js +237 -0
  17. package/dist/cjs/shell/cmd_commission.js.map +6 -0
  18. package/dist/cjs/shell/cmd_config.js +315 -0
  19. package/dist/cjs/shell/cmd_config.js.map +6 -0
  20. package/dist/cjs/shell/cmd_discover.js +123 -0
  21. package/dist/cjs/shell/cmd_discover.js.map +6 -0
  22. package/dist/cjs/shell/cmd_identify.js +66 -0
  23. package/dist/cjs/shell/cmd_identify.js.map +6 -0
  24. package/dist/cjs/shell/cmd_nodes.js +244 -0
  25. package/dist/cjs/shell/cmd_nodes.js.map +6 -0
  26. package/dist/cjs/shell/cmd_session.js +43 -0
  27. package/dist/cjs/shell/cmd_session.js.map +6 -0
  28. package/dist/cjs/shell/cmd_subscribe.js +59 -0
  29. package/dist/cjs/shell/cmd_subscribe.js.map +6 -0
  30. package/dist/cjs/shell/cmd_tlv.js +175 -0
  31. package/dist/cjs/shell/cmd_tlv.js.map +6 -0
  32. package/dist/cjs/util/CommandlineParser.js +106 -0
  33. package/dist/cjs/util/CommandlineParser.js.map +6 -0
  34. package/dist/cjs/util/Json.js +66 -0
  35. package/dist/cjs/util/Json.js.map +6 -0
  36. package/dist/cjs/util/String.js +32 -0
  37. package/dist/cjs/util/String.js.map +6 -0
  38. package/package.json +58 -0
  39. package/src/MatterNode.ts +141 -0
  40. package/src/app.ts +158 -0
  41. package/src/shell/Shell.ts +184 -0
  42. package/src/shell/cmd_cluster-attributes.ts +340 -0
  43. package/src/shell/cmd_cluster-commands.ts +174 -0
  44. package/src/shell/cmd_cluster-events.ts +99 -0
  45. package/src/shell/cmd_commission.ts +273 -0
  46. package/src/shell/cmd_config.ts +366 -0
  47. package/src/shell/cmd_discover.ts +127 -0
  48. package/src/shell/cmd_identify.ts +51 -0
  49. package/src/shell/cmd_nodes.ts +249 -0
  50. package/src/shell/cmd_session.ts +23 -0
  51. package/src/shell/cmd_subscribe.ts +43 -0
  52. package/src/shell/cmd_tlv.ts +169 -0
  53. package/src/tsconfig.json +22 -0
  54. package/src/util/CommandlineParser.ts +123 -0
  55. package/src/util/Json.ts +50 -0
  56. package/src/util/String.ts +9 -0
@@ -0,0 +1,366 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2024 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { Logger } from "@matter/general";
8
+ import { Argv } from "yargs";
9
+ import { MatterNode } from "../MatterNode";
10
+ import { setLogLevel } from "../app";
11
+
12
+ export default function commands(theNode: MatterNode) {
13
+ return {
14
+ command: "config",
15
+ describe: "Manage global configuration",
16
+ builder: (yargs: Argv) =>
17
+ yargs
18
+ // Console LogLevel
19
+ .command("loglevel", "Manage Console and File LogLevels", yargs => {
20
+ return yargs
21
+ .command(
22
+ ["* [action]", "* [type] [ action]"],
23
+ "get/delete console or file log level",
24
+ yargs => {
25
+ return yargs
26
+ .positional("type", {
27
+ describe: "type to set the loglevel for",
28
+ choices: ["console", "file"] as const,
29
+ default: "console",
30
+ type: "string",
31
+ })
32
+ .positional("action", {
33
+ describe: "get/delete",
34
+ choices: ["get", "delete"] as const,
35
+ default: "get",
36
+ type: "string",
37
+ });
38
+ },
39
+ async argv => doLogLevel(theNode, argv),
40
+ )
41
+ .command(
42
+ "set <value>",
43
+ "set console log level",
44
+ yargs => {
45
+ return yargs
46
+ .positional("type", {
47
+ describe: "type to set the loglevel for",
48
+ choices: ["console", "file"] as const,
49
+ default: "console",
50
+ type: "string",
51
+ })
52
+ .positional("value", {
53
+ describe: "log level to set",
54
+ type: "string",
55
+ choices: ["fatal", "error", "warn", "info", "debug"] as const,
56
+ demandOption: true,
57
+ });
58
+ },
59
+ async argv => doLogLevel(theNode, { action: "set", ...argv }),
60
+ );
61
+ })
62
+
63
+ // LogFile name
64
+ .command("logfile", "Manage Logfile path", yargs => {
65
+ return yargs
66
+ .command(
67
+ "* [action]",
68
+ "get/delete logfile path",
69
+ yargs => {
70
+ return yargs.positional("action", {
71
+ describe: "get/delete",
72
+ choices: ["get", "delete"] as const,
73
+ default: "get",
74
+ type: "string",
75
+ });
76
+ },
77
+ async argv => doLogfilePath(theNode, argv),
78
+ )
79
+ .command(
80
+ "set <value>",
81
+ "set logfile path",
82
+ yargs => {
83
+ return yargs.positional("value", {
84
+ describe: "logfile path to set",
85
+ type: "string",
86
+ demandOption: true,
87
+ });
88
+ },
89
+ async argv => doLogfilePath(theNode, { action: "set", ...argv }),
90
+ );
91
+ })
92
+
93
+ // BLE HCI number (Linux)
94
+ .command("ble-hci", "Manage BLE HCI ID (Linux)", yargs => {
95
+ return yargs
96
+ .command(
97
+ "* [action]",
98
+ "get/delete BLE HCI ID of the device (Linux only)",
99
+ yargs => {
100
+ return yargs.positional("action", {
101
+ describe: "get/delete",
102
+ choices: ["get", "delete"] as const,
103
+ default: "get",
104
+ type: "string",
105
+ });
106
+ },
107
+ async argv => doBleHci(theNode, argv),
108
+ )
109
+ .command(
110
+ "set <value>",
111
+ "set BLE HCI ID of the device (Linux only)",
112
+ yargs => {
113
+ return yargs.positional("value", {
114
+ describe: "HCI ID to set",
115
+ type: "number",
116
+ demandOption: true,
117
+ });
118
+ },
119
+ async argv => doBleHci(theNode, { action: "set", ...argv }),
120
+ );
121
+ })
122
+
123
+ // Commissioning Wi-Fi credentials
124
+ .command("wifi-credentials", "Manage Wi-Fi credentials used in commissioning process", yargs => {
125
+ return yargs
126
+ .command(
127
+ "* [action]",
128
+ "get/set Wi-Fi credentials",
129
+ yargs => {
130
+ return yargs.positional("action", {
131
+ describe: "get/delete",
132
+ choices: ["get", "delete"] as const,
133
+ default: "get",
134
+ type: "string",
135
+ });
136
+ },
137
+ async argv => doWifiCredentials(theNode, argv),
138
+ )
139
+ .command(
140
+ "set <wifi-ssid> <wifi-password>",
141
+ "set Wi-Fi credentials",
142
+ yargs => {
143
+ return yargs
144
+ .positional("wifi-ssid", {
145
+ describe: "SSID of the Wifi network to commission",
146
+ type: "string",
147
+ demandOption: true,
148
+ })
149
+ .positional("wifi-password", {
150
+ describe: "Password of the Wifi network to commission",
151
+ type: "string",
152
+ demandOption: true,
153
+ });
154
+ },
155
+ async argv => doWifiCredentials(theNode, { action: "set", ...argv }),
156
+ );
157
+ })
158
+
159
+ // Commissioning Thread credentials
160
+ .command("thread-credentials", "Manage Thread credentials used in commissioning process", yargs => {
161
+ return yargs
162
+ .command(
163
+ "* [action]",
164
+ "get/set thread network credentials",
165
+ yargs => {
166
+ return yargs.positional("action", {
167
+ describe: "get/delete",
168
+ choices: ["get", "delete"] as const,
169
+ default: "get",
170
+ type: "string",
171
+ });
172
+ },
173
+ async argv => doThreadCredentials(theNode, argv),
174
+ )
175
+ .command(
176
+ "set <thread-name> <thread-operational-dataset>",
177
+ "set thread networkcredentials",
178
+ yargs => {
179
+ return yargs
180
+ .positional("thread-name", {
181
+ describe: "Thread network name to commission",
182
+ type: "string",
183
+ demandOption: true,
184
+ })
185
+ .positional("thread-operational-dataset", {
186
+ describe: "Thread network operational dataset to commission",
187
+ type: "string",
188
+ demandOption: true,
189
+ });
190
+ },
191
+ argv => doThreadCredentials(theNode, { action: "set", ...argv }),
192
+ );
193
+ }),
194
+ handler: async (argv: any) => {
195
+ argv.unhandled = true;
196
+ },
197
+ };
198
+ }
199
+
200
+ async function doLogLevel(
201
+ theNode: MatterNode,
202
+ args: {
203
+ action: string;
204
+ type: string;
205
+ value?: string;
206
+ },
207
+ ) {
208
+ const { action, value } = args;
209
+ const storageKey = args.type === "console" ? "LogLevel" : "LogLevelFile";
210
+ const logtype = args.type === "console" ? "Console" : "File";
211
+ switch (action) {
212
+ case "get":
213
+ console.log(`Current Loglevel for ${logtype}: ${await theNode.Store.get<string>(storageKey, "info")}`);
214
+ break;
215
+ case "set":
216
+ if (value === undefined) {
217
+ console.log(`Cannot change Loglevel for ${logtype}: New Loglevel value not provided`);
218
+ return;
219
+ }
220
+ await theNode.Store.set(storageKey, value);
221
+ console.log(`New Loglevel for ${logtype}:" ${value}"`);
222
+ setLogLevel(args.type === "console" ? "default" : "file", value);
223
+ break;
224
+ case "delete":
225
+ await theNode.Store.delete(storageKey);
226
+ console.log(`Loglevel for ${logtype}: Reset to "info"`);
227
+ setLogLevel(args.type === "console" ? "default" : "file", "info");
228
+ break;
229
+ }
230
+ }
231
+
232
+ async function doLogfilePath(
233
+ theNode: MatterNode,
234
+ args: {
235
+ action: string;
236
+ value?: string;
237
+ },
238
+ ) {
239
+ const { action, value } = args;
240
+ switch (action) {
241
+ case "get":
242
+ console.log(`Current Logfile Path: ${await theNode.Store.get<string>("LogFile", "-")}`);
243
+ break;
244
+ case "set":
245
+ if (value === undefined) {
246
+ console.log(`Cannot change Logfile path: new path not provided`);
247
+ return;
248
+ }
249
+ await theNode.Store.set("LogFile", value);
250
+ console.log(`New LogFile path:" ${value}". Please restart the shell for teh changes to take effect.`);
251
+ break;
252
+ case "delete":
253
+ await theNode.Store.delete("LogFile");
254
+ console.log(`LogFile path removed. Please restart the shell for teh changes to take effect.`);
255
+ break;
256
+ }
257
+ }
258
+
259
+ async function doBleHci(
260
+ theNode: MatterNode,
261
+ args: {
262
+ action: string;
263
+ value?: number;
264
+ },
265
+ ) {
266
+ const { action, value } = args;
267
+ switch (action) {
268
+ case "get":
269
+ console.log(`Current BLE HCI ID: ${await theNode.Store.get<number>("BleHciId", 0)}`);
270
+ break;
271
+ case "set":
272
+ if (value === undefined) {
273
+ console.log(`Cannot change HCI ID: New HCI ID value not provided`);
274
+ return;
275
+ }
276
+ await theNode.Store.set("BleHciId", value);
277
+ console.log(`New HCI ID:" ${value}". Please restart the shell for teh changes to take effect.`);
278
+ break;
279
+ case "delete":
280
+ await theNode.Store.delete("BleHciId");
281
+ console.log(`BLE HCI ID reset to default (0). Please restart the shell for teh changes to take effect.`);
282
+ break;
283
+ }
284
+ }
285
+
286
+ async function doWifiCredentials(
287
+ theNode: MatterNode,
288
+ args: {
289
+ action: string;
290
+ wifiSsid?: string;
291
+ wifiPassword?: string;
292
+ },
293
+ ) {
294
+ const { action, wifiSsid, wifiPassword } = args;
295
+ switch (action) {
296
+ case "get":
297
+ console.log(
298
+ `Current Wifi-Credentials: SSID="${await theNode.Store.get<string>(
299
+ "WiFiSsid",
300
+ "-",
301
+ )}", Password="${Logger.maskString(await theNode.Store.get<string>("WiFiPassword", ""))}"`,
302
+ );
303
+ break;
304
+ case "set":
305
+ if (wifiSsid === undefined || wifiPassword === undefined) {
306
+ console.log(`Cannot change Wi-Fi credentials: New values not provided`);
307
+ return;
308
+ }
309
+ await theNode.Store.set("WiFiSsid", wifiSsid);
310
+ await theNode.Store.set("WiFiPassword", wifiPassword);
311
+ console.log(
312
+ `New Wifi-Credentials: SSID="${theNode.Store.get<string>(
313
+ "WiFiSsid",
314
+ "-",
315
+ )}", Password="${Logger.maskString(await theNode.Store.get<string>("WiFiPassword"))}"`,
316
+ );
317
+ break;
318
+ case "delete":
319
+ await theNode.Store.delete("WiFiSsid");
320
+ await theNode.Store.delete("WiFiPassword");
321
+ console.log(`Wi-Fi credentials were deleted`);
322
+ break;
323
+ }
324
+ }
325
+
326
+ async function doThreadCredentials(
327
+ theNode: MatterNode,
328
+ args: {
329
+ action: string;
330
+ threadName?: string;
331
+ threadOperationalDataset?: string;
332
+ },
333
+ ) {
334
+ const { action, threadName, threadOperationalDataset } = args;
335
+ switch (action) {
336
+ case "get":
337
+ console.log(
338
+ `Current Thread network credentials: name="${await theNode.Store.get<string>(
339
+ "ThreadName",
340
+ "-",
341
+ )}", Operational-Dataset="${Logger.maskString(
342
+ await theNode.Store.get<string>("ThreadOperationalDataset", ""),
343
+ )}"`,
344
+ );
345
+ break;
346
+ case "set":
347
+ if (threadName === undefined || threadOperationalDataset === undefined) {
348
+ console.log(`Cannot change Thread network credentials: New values not provided`);
349
+ return;
350
+ }
351
+ await theNode.Store.set("ThreadName", threadName);
352
+ await theNode.Store.set("ThreadOperationalDataset", threadOperationalDataset);
353
+ console.log(
354
+ `New Wifi-Credentials: SSID="${await theNode.Store.get<string>(
355
+ "ThreadName",
356
+ "-",
357
+ )}", OperationalDataset="${Logger.maskString(await theNode.Store.get<string>("ThreadOperationalDataset"))}"`,
358
+ );
359
+ break;
360
+ case "delete":
361
+ await theNode.Store.delete("ThreadName");
362
+ await theNode.Store.delete("ThreadOperationalDataset");
363
+ console.log(`Thread network credentials were deleted`);
364
+ break;
365
+ }
366
+ }
@@ -0,0 +1,127 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2024 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { Logger } from "@matter/general";
8
+ import { CommissionableDeviceIdentifiers } from "@project-chip/matter.js/common";
9
+ import { VendorId } from "@project-chip/matter.js/datatype";
10
+ import { ManualPairingCodeCodec } from "@project-chip/matter.js/schema";
11
+ import type { Argv } from "yargs";
12
+ import { MatterNode } from "../MatterNode";
13
+
14
+ export default function commands(theNode: MatterNode) {
15
+ return {
16
+ command: "discover",
17
+ describe: "Handle device discovery",
18
+ builder: (yargs: Argv) =>
19
+ yargs
20
+ // Pair
21
+ .command(
22
+ "commissionable [timeout-seconds]",
23
+ "Discover commissionable devices",
24
+ () => {
25
+ return yargs
26
+ .positional("timeout-seconds", {
27
+ describe: "Discovery timeout in seconds",
28
+ default: 900,
29
+ type: "number",
30
+ })
31
+ .options({
32
+ pairingCode: {
33
+ describe: "pairing code",
34
+ default: undefined,
35
+ type: "string",
36
+ },
37
+ discriminator: {
38
+ alias: "d",
39
+ description: "Long discriminator",
40
+ default: undefined,
41
+ type: "number",
42
+ },
43
+ shortDiscriminator: {
44
+ alias: "s",
45
+ description: "Short discriminator",
46
+ default: undefined,
47
+ type: "number",
48
+ },
49
+ vendorId: {
50
+ alias: "v",
51
+ description: "Vendor ID",
52
+ default: undefined,
53
+ type: "number",
54
+ },
55
+ productId: {
56
+ alias: "p",
57
+ description: "Product ID",
58
+ default: undefined,
59
+ type: "number",
60
+ },
61
+ deviceType: {
62
+ alias: "t",
63
+ description: "Device Type",
64
+ default: undefined,
65
+ type: "number",
66
+ },
67
+ ble: {
68
+ alias: "b",
69
+ description: "Also discover over BLE",
70
+ default: false,
71
+ type: "boolean",
72
+ },
73
+ });
74
+ },
75
+ async argv => {
76
+ const { ble = false, pairingCode, vendorId, productId, deviceType, timeoutSeconds } = argv;
77
+ let { discriminator, shortDiscriminator } = argv;
78
+
79
+ if (typeof pairingCode === "string") {
80
+ const { shortDiscriminator: pairingCodeShortDiscriminator } =
81
+ ManualPairingCodeCodec.decode(pairingCode);
82
+ shortDiscriminator = pairingCodeShortDiscriminator;
83
+ discriminator = undefined;
84
+ }
85
+
86
+ await theNode.start();
87
+ if (theNode.commissioningController === undefined) {
88
+ throw new Error("CommissioningController not initialized");
89
+ }
90
+
91
+ const identifierData: CommissionableDeviceIdentifiers =
92
+ discriminator !== undefined
93
+ ? { longDiscriminator: discriminator }
94
+ : shortDiscriminator !== undefined
95
+ ? { shortDiscriminator }
96
+ : vendorId !== undefined
97
+ ? { vendorId: VendorId(vendorId) }
98
+ : productId !== undefined
99
+ ? { productId }
100
+ : deviceType !== undefined
101
+ ? { deviceType }
102
+ : {};
103
+
104
+ console.log(
105
+ `Discover devices with identifier ${Logger.toJSON(
106
+ identifierData,
107
+ )} for ${timeoutSeconds} seconds.`,
108
+ );
109
+
110
+ const results = await theNode.commissioningController.discoverCommissionableDevices(
111
+ identifierData,
112
+ {
113
+ ble,
114
+ onIpNetwork: true,
115
+ },
116
+ device => console.log(`Discovered device ${Logger.toJSON(device)}`),
117
+ timeoutSeconds,
118
+ );
119
+
120
+ console.log(`Discovered ${results.length} devices`, results);
121
+ },
122
+ ),
123
+ handler: async (argv: any) => {
124
+ argv.unhandled = true;
125
+ },
126
+ };
127
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2024 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { IdentifyCluster } from "@project-chip/matter.js/cluster";
8
+ import type { Argv } from "yargs";
9
+ import { MatterNode } from "../MatterNode.js";
10
+
11
+ export default function commands(theNode: MatterNode) {
12
+ return {
13
+ command: "identify [time] [node-id] [endpoint-id]",
14
+ describe:
15
+ "Trigger Identify command with given time (default 10s). Execute on one node or endpoint, else all onoff clusters will be controlled",
16
+ builder: (yargs: Argv) => {
17
+ return yargs
18
+ .positional("time", {
19
+ describe: "time in seconds",
20
+ default: 10,
21
+ type: "number",
22
+ })
23
+ .positional("node-id", {
24
+ describe: "node id",
25
+ default: undefined,
26
+ type: "string",
27
+ })
28
+ .positional("endpoint-id", {
29
+ describe: "endpoint id",
30
+ default: undefined,
31
+ type: "number",
32
+ });
33
+ },
34
+
35
+ handler: async (argv: any) => {
36
+ const { nodeId, time = 10, endpointId } = argv;
37
+ await theNode.iterateNodeDevices(
38
+ await theNode.connectAndGetNodes(nodeId),
39
+ async (device, node) => {
40
+ const identifyCluster = device.getClusterClient(IdentifyCluster);
41
+ if (identifyCluster === undefined) {
42
+ return;
43
+ }
44
+ console.log("Invoke Identify for", node.nodeId.toString());
45
+ await identifyCluster.identify({ identifyTime: time });
46
+ },
47
+ endpointId,
48
+ );
49
+ },
50
+ };
51
+ }