@project-chip/matter-node.js-examples 0.7.5 → 0.8.0-alpha.0-20240310-474da21a

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. package/README.md +44 -0
  2. package/dist/esm/examples/BridgedDeviceNode.js +147 -0
  3. package/dist/esm/examples/BridgedDeviceNode.js.map +7 -0
  4. package/dist/esm/examples/{BridgedDevicesNode.js → BridgedDevicesNodeLegacy.js} +2 -2
  5. package/dist/esm/examples/BridgedDevicesNodeLegacy.js.map +7 -0
  6. package/dist/esm/examples/ComposedDeviceNode.js +116 -127
  7. package/dist/esm/examples/ComposedDeviceNode.js.map +3 -3
  8. package/dist/esm/examples/ComposedDeviceNodeLegacy.js +138 -0
  9. package/dist/esm/examples/ComposedDeviceNodeLegacy.js.map +7 -0
  10. package/dist/esm/examples/ControllerNode.js +24 -20
  11. package/dist/esm/examples/ControllerNode.js.map +2 -2
  12. package/dist/esm/examples/ControllerNodeLegacy.js +227 -0
  13. package/dist/esm/examples/ControllerNodeLegacy.js.map +7 -0
  14. package/dist/esm/examples/DeviceNode.js +118 -159
  15. package/dist/esm/examples/DeviceNode.js.map +2 -2
  16. package/dist/esm/examples/DeviceNodeFull.js +253 -0
  17. package/dist/esm/examples/DeviceNodeFull.js.map +7 -0
  18. package/dist/esm/examples/DeviceNodeFullLegacy.js +171 -0
  19. package/dist/esm/examples/DeviceNodeFullLegacy.js.map +7 -0
  20. package/dist/esm/examples/IlluminatedRollerShade.js +85 -0
  21. package/dist/esm/examples/IlluminatedRollerShade.js.map +7 -0
  22. package/dist/esm/examples/LightDevice.js +34 -0
  23. package/dist/esm/examples/LightDevice.js.map +7 -0
  24. package/dist/esm/examples/MultiDeviceNode.js +133 -134
  25. package/dist/esm/examples/MultiDeviceNode.js.map +3 -3
  26. package/dist/esm/examples/MultiDeviceNodeLegacy.js +146 -0
  27. package/dist/esm/examples/MultiDeviceNodeLegacy.js.map +7 -0
  28. package/dist/esm/examples/SensorDeviceNode.js +167 -0
  29. package/dist/esm/examples/SensorDeviceNode.js.map +7 -0
  30. package/dist/esm/examples/cluster/DummyThreadNetworkCommissioningServer.js +115 -0
  31. package/dist/esm/examples/cluster/DummyThreadNetworkCommissioningServer.js.map +7 -0
  32. package/dist/esm/examples/cluster/DummyWifiNetworkCommissioningServer.js +115 -0
  33. package/dist/esm/examples/cluster/DummyWifiNetworkCommissioningServer.js.map +7 -0
  34. package/dist/esm/examples/cluster/{DummyWifiNetworkCommissioningClusterServer.js → DummyWifiNetworkCommissioningServerLegacy.js} +16 -14
  35. package/dist/esm/examples/cluster/DummyWifiNetworkCommissioningServerLegacy.js.map +7 -0
  36. package/dist/esm/examples/cluster/MyFancyOwnFunctionality.js +110 -0
  37. package/dist/esm/examples/cluster/MyFancyOwnFunctionality.js.map +7 -0
  38. package/dist/esm/tutorial/example01.js +5 -0
  39. package/dist/esm/tutorial/example01.js.map +7 -0
  40. package/dist/esm/tutorial/example02.js +7 -0
  41. package/dist/esm/tutorial/example02.js.map +7 -0
  42. package/dist/esm/tutorial/example03.js +15 -0
  43. package/dist/esm/tutorial/example03.js.map +7 -0
  44. package/dist/esm/tutorial/example04.js +10 -0
  45. package/dist/esm/tutorial/example04.js.map +7 -0
  46. package/dist/esm/tutorial/example05.js +14 -0
  47. package/dist/esm/tutorial/example05.js.map +7 -0
  48. package/package.json +9 -6
  49. package/src/examples/BridgedDeviceNode.ts +260 -0
  50. package/src/examples/{BridgedDevicesNode.ts → BridgedDevicesNodeLegacy.ts} +6 -1
  51. package/src/examples/ComposedDeviceNode.ts +173 -223
  52. package/src/examples/ComposedDeviceNodeLegacy.ts +252 -0
  53. package/src/examples/ControllerNode.ts +28 -22
  54. package/src/examples/ControllerNodeLegacy.ts +354 -0
  55. package/src/examples/DeviceNode.ts +173 -273
  56. package/src/examples/DeviceNodeFull.ts +440 -0
  57. package/src/examples/DeviceNodeFullLegacy.ts +307 -0
  58. package/src/examples/IlluminatedRollerShade.ts +130 -0
  59. package/src/examples/LightDevice.ts +60 -0
  60. package/src/examples/MultiDeviceNode.ts +184 -236
  61. package/src/examples/MultiDeviceNodeLegacy.ts +267 -0
  62. package/src/examples/SensorDeviceNode.ts +236 -0
  63. package/src/examples/cluster/DummyThreadNetworkCommissioningServer.ts +156 -0
  64. package/src/examples/cluster/DummyWifiNetworkCommissioningServer.ts +153 -0
  65. package/src/examples/cluster/{DummyWifiNetworkCommissioningClusterServer.ts → DummyWifiNetworkCommissioningServerLegacy.ts} +16 -13
  66. package/src/examples/cluster/MyFancyOwnFunctionality.ts +185 -0
  67. package/src/tsconfig.json +9 -1
  68. package/src/tutorial/example01.ts +6 -0
  69. package/src/tutorial/example02.ts +9 -0
  70. package/src/tutorial/example03.ts +19 -0
  71. package/src/tutorial/example04.ts +13 -0
  72. package/src/tutorial/example05.ts +19 -0
  73. package/dist/esm/examples/BridgedDevicesNode.js.map +0 -7
  74. package/dist/esm/examples/cluster/DummyWifiNetworkCommissioningClusterServer.js.map +0 -7
@@ -1,14 +1,13 @@
1
- #!/usr/bin/env node
2
1
  /**
3
2
  * @license
4
- * Copyright 2022 The node-matter Authors
3
+ * Copyright 2022-2024 Matter.js Authors
5
4
  * SPDX-License-Identifier: Apache-2.0
6
5
  */
7
6
 
8
7
  /**
9
8
  * This example shows how to create a new device node that is composed of multiple devices.
10
- * It creates multiple endpoints on the server. When you want to add a composed devices to a Aggregator you need to
11
- * add all endpoints of the composed device to an "ComposedDevice" instance! (not shown in this example).
9
+ * It creates multiple endpoints on the server. For information on how to add a composed device to a bridge please
10
+ * refer to the bridge example!
12
11
  * It can be used as CLI script and starting point for your own device node implementation.
13
12
  */
14
13
 
@@ -16,247 +15,196 @@
16
15
  * Import needed modules from @project-chip/matter-node.js
17
16
  */
18
17
  // Include this first to auto-register Crypto, Network and Time Node.js implementations
19
- import { CommissioningServer, MatterServer } from "@project-chip/matter-node.js";
20
-
21
- import { DeviceTypes, OnOffLightDevice, OnOffPluginUnitDevice } from "@project-chip/matter-node.js/device";
22
- import { Format, Level, Logger } from "@project-chip/matter-node.js/log";
23
- import { QrCode } from "@project-chip/matter-node.js/schema";
24
- import { StorageBackendDisk, StorageManager } from "@project-chip/matter-node.js/storage";
25
- import { Time } from "@project-chip/matter-node.js/time";
26
- import {
27
- commandExecutor,
28
- getIntParameter,
29
- getParameter,
30
- hasParameter,
31
- requireMinNodeVersion,
32
- } from "@project-chip/matter-node.js/util";
33
- import { VendorId } from "@project-chip/matter.js/datatype";
34
-
35
- const logger = Logger.get("MultiDevice");
18
+ import "@project-chip/matter-node.js";
19
+
20
+ import { requireMinNodeVersion } from "@project-chip/matter-node.js/util";
21
+ import { DeviceTypeId, VendorId } from "@project-chip/matter.js/datatype";
22
+ import { logEndpoint } from "@project-chip/matter.js/device";
23
+ import { OnOffLightDevice } from "@project-chip/matter.js/devices/OnOffLightDevice";
24
+ import { OnOffPlugInUnitDevice } from "@project-chip/matter.js/devices/OnOffPlugInUnitDevice";
25
+ import { Endpoint, EndpointServer } from "@project-chip/matter.js/endpoint";
26
+ import { Environment, StorageService } from "@project-chip/matter.js/environment";
27
+ import { ServerNode } from "@project-chip/matter.js/node";
28
+ import { Time } from "@project-chip/matter.js/time";
29
+ import { execSync } from "child_process";
36
30
 
37
31
  requireMinNodeVersion(16);
38
32
 
39
- /** Configure logging */
40
- switch (getParameter("loglevel")) {
41
- case "fatal":
42
- Logger.defaultLogLevel = Level.FATAL;
43
- break;
44
- case "error":
45
- Logger.defaultLogLevel = Level.ERROR;
46
- break;
47
- case "warn":
48
- Logger.defaultLogLevel = Level.WARN;
49
- break;
50
- case "info":
51
- Logger.defaultLogLevel = Level.INFO;
52
- break;
53
- }
54
-
55
- switch (getParameter("logformat")) {
56
- case "plain":
57
- Logger.format = Format.PLAIN;
58
- break;
59
- case "html":
60
- Logger.format = Format.HTML;
61
- break;
62
- default:
63
- if (process.stdin?.isTTY) Logger.format = Format.ANSI;
33
+ const devices = await getConfiguration();
34
+ for (let idx = 1; idx < devices.length; idx++) {
35
+ const {
36
+ isSocket,
37
+ deviceName,
38
+ vendorName,
39
+ passcode,
40
+ discriminator,
41
+ vendorId,
42
+ productName,
43
+ productId,
44
+ port,
45
+ uniqueId,
46
+ } = devices[idx];
47
+ const i = idx + 1;
48
+
49
+ /**
50
+ * Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration
51
+ */
52
+ const server = await ServerNode.create({
53
+ // Required: Give the Node a unique ID which is used to store the state of this node
54
+ id: uniqueId,
55
+
56
+ // Provide Network relevant configuration like the port
57
+ // Optional when operating only one device on a host, Default port is 5540
58
+ network: {
59
+ port,
60
+ },
61
+
62
+ // Provide Commissioning relevant settings
63
+ // Optional for development/testing purposes
64
+ commissioning: {
65
+ passcode,
66
+ discriminator,
67
+ },
68
+
69
+ // Provide Node announcement settings
70
+ // Optional: If Ommitted some development defaults are used
71
+ productDescription: {
72
+ name: deviceName,
73
+ deviceType: DeviceTypeId(isSocket ? OnOffPlugInUnitDevice.deviceType : OnOffLightDevice.deviceType),
74
+ },
75
+
76
+ // Provide defaults for the BasicInformation cluster on the Root endpoint
77
+ // Optional: If Omitted some development defaults are used
78
+ basicInformation: {
79
+ vendorName,
80
+ vendorId: VendorId(vendorId),
81
+ nodeLabel: productName,
82
+ productName,
83
+ productLabel: productName,
84
+ productId,
85
+ serialNumber: `matterjs-${uniqueId}`,
86
+ uniqueId,
87
+ },
88
+ });
89
+
90
+ console.log(
91
+ `Added device ${i} on port ${port} and unique id ${uniqueId}: Passcode: ${passcode}, Discriminator: ${discriminator}`,
92
+ );
93
+
94
+ const endpoint = new Endpoint(isSocket ? OnOffPlugInUnitDevice : OnOffLightDevice, { id: "onoff" });
95
+ await server.add(endpoint);
96
+
97
+ /**
98
+ * Register state change handlers of the node for identify and onoff states to react to the commands.
99
+ * If the code in these change handlers fail then the change is also rolled back and not executed and an error is
100
+ * reported back to the controller.
101
+ */
102
+ let isIdentifying = false;
103
+ endpoint.events.identify.identifyTime$Change.on(value => {
104
+ // identifyTime is set when an identify commandf is called and then decreased every second while indenitfy logic runs.
105
+ if (value > 0 && !isIdentifying) {
106
+ isIdentifying = true;
107
+ console.log(`Run identify logic, ideally blink a light every 0.5s ...`);
108
+ } else if (value === 0) {
109
+ isIdentifying = false;
110
+ console.log(`Stop identify logic ...`);
111
+ }
112
+ });
113
+
114
+ endpoint.events.onOff.onOff$Change.on(value => {
115
+ executeCommand(value ? `on${i}` : `off${i}`);
116
+ console.log(`OnOff ${i} is now ${value ? "ON" : "OFF"}`);
117
+ });
118
+
119
+ /**
120
+ * Log the endpoint structure for debugging reasons and to allow to verify anything is correct
121
+ */
122
+ logEndpoint(EndpointServer.forEndpoint(server));
123
+
124
+ console.log("----------------------------");
125
+ console.log(`QR Code for Device ${i} on port ${port}:`);
126
+ console.log("----------------------------");
127
+
128
+ /**
129
+ * In order to start the node and announce it into the network we use the run method which resolves when the node goes
130
+ * offline again because we do not need anything more here. See the Full example for other starting options.
131
+ * The QR Code is printed automatically.
132
+ */
133
+ await server.bringOnline();
64
134
  }
65
135
 
66
- const storageLocation = getParameter("store") ?? ".device-node";
67
- const storage = new StorageBackendDisk(storageLocation, hasParameter("clearstorage"));
68
- logger.info(`Storage location: ${storageLocation} (Directory)`);
69
- logger.info(
70
- 'Use the parameter "-store NAME" to specify a different storage location, use -clearstorage to start with an empty storage.',
71
- );
72
-
73
- class Device {
74
- private matterServer: MatterServer | undefined;
75
-
76
- async start() {
77
- logger.info(`node-matter`);
78
-
79
- /**
80
- * Initialize the storage system.
81
- *
82
- * The storage manager is then also used by the Matter server, so this code block in general is required,
83
- * but you can choose a different storage backend as long as it implements the required API.
84
- */
85
-
86
- const storageManager = new StorageManager(storage);
87
- await storageManager.initialize();
88
-
89
- /**
90
- * Collect all needed data
91
- *
92
- * This block makes sure to collect all needed data from cli or storage. Replace this with where ever your data
93
- * come from.
94
- *
95
- * Note: This example also uses the initialized storage system to store the device parameter data for convenience
96
- * and easy reuse. When you also do that be careful to not overlap with Matter-Server own contexts
97
- * (so maybe better not ;-)).
98
- */
99
- const netInterface = getParameter("netinterface");
100
-
101
- const deviceStorage = storageManager.createContext("Device");
102
-
103
- /**
104
- * Create Matter Server and CommissioningServer Node
105
- *
106
- * To allow the device to be announced, found, paired and operated we need a MatterServer instance and add a
107
- * commissioningServer to it and add the just created device instance to it.
108
- * The CommissioningServer node defines the port where the server listens for the UDP packages of the Matter protocol
109
- * and initializes deice specific certificates and such.
110
- *
111
- * The below logic also adds command handlers for commands of clusters that normally are handled internally
112
- * like testEventTrigger (General Diagnostic Cluster) that can be implemented with the logic when these commands
113
- * are called.
114
- */
115
-
116
- this.matterServer = new MatterServer(storageManager, { mdnsInterface: netInterface });
117
-
118
- /**
119
- * Create Device instance and add needed Listener
120
- *
121
- * Create an instance of the matter device class you want to use.
122
- * This example uses the OnOffLightDevice or OnOffPluginUnitDevice depending on the value of the type parameter.
123
- * To execute the on/off scripts defined as parameters a listener for the onOff attribute is registered via the
124
- * device specific API.
125
- *
126
- * The below logic also adds command handlers for commands of clusters that normally are handled device internally
127
- * like identify that can be implemented with the logic when these commands are called.
128
- */
129
-
130
- const commissioningServers = new Array<CommissioningServer>();
131
-
132
- let defaultPasscode = 20202021;
133
- let defaultDiscriminator = 3840;
134
- let defaultPort = 5550;
135
-
136
- const numDevices = getIntParameter("num") || 2;
137
- for (let i = 1; i <= numDevices; i++) {
138
- if (deviceStorage.has(`isSocket${i}`)) {
139
- logger.info("Device type found in storage. -type parameter is ignored.");
140
- }
141
- const isSocket = deviceStorage.get(`isSocket${i}`, getParameter(`type${i}`) === "socket");
142
- const deviceName = `Matter ${getParameter(`type${i}`) ?? "light"} device ${i}`;
143
- const deviceType =
144
- getParameter(`type${i}`) === "socket"
145
- ? DeviceTypes.ON_OFF_PLUGIN_UNIT.code
146
- : DeviceTypes.ON_OFF_LIGHT.code;
147
- const vendorName = "matter-node.js";
148
- const passcode = getIntParameter(`passcode${i}`) ?? deviceStorage.get(`passcode${i}`, defaultPasscode++);
149
- const discriminator =
150
- getIntParameter(`discriminator${i}`) ?? deviceStorage.get(`discriminator${i}`, defaultDiscriminator++);
151
- // product name / id and vendor id should match what is in the device certificate
152
- const vendorId = getIntParameter(`vendorid${i}`) ?? deviceStorage.get(`vendorid${i}`, 0xfff1);
153
- const productName = `node-matter OnOff-Device ${i}`;
154
- const productId = getIntParameter(`productid${i}`) ?? deviceStorage.get(`productid${i}`, 0x8000);
136
+ /*********************************************************************************************************
137
+ * Convenience Methods
138
+ *********************************************************************************************************/
155
139
 
156
- const port = getIntParameter(`port${i}`) ?? defaultPort++;
157
-
158
- const uniqueId =
159
- getIntParameter(`uniqueid${i}`) ?? deviceStorage.get(`uniqueid${i}`, `${i}-${Time.nowMs()}`);
160
-
161
- deviceStorage.set(`passcode${i}`, passcode);
162
- deviceStorage.set(`discriminator${i}`, discriminator);
163
- deviceStorage.set(`vendorid${i}`, vendorId);
164
- deviceStorage.set(`productid${i}`, productId);
165
- deviceStorage.set(`isSocket${i}`, isSocket);
166
- deviceStorage.set(`uniqueid${i}`, uniqueId);
167
-
168
- const commissioningServer = new CommissioningServer({
169
- port,
170
- deviceName,
171
- deviceType,
172
- passcode,
173
- discriminator,
174
- basicInformation: {
175
- vendorName,
176
- vendorId: VendorId(vendorId),
177
- nodeLabel: productName,
178
- productName,
179
- productLabel: productName,
180
- productId,
181
- serialNumber: `node-matter-${uniqueId}`,
182
- },
183
- });
184
-
185
- console.log(
186
- `Added device ${i} on port ${port} and unique id ${uniqueId}: Passcode: ${passcode}, Discriminator: ${discriminator}`,
187
- );
188
-
189
- const onOffDevice =
190
- getParameter(`type${i}`) === "socket" ? new OnOffPluginUnitDevice() : new OnOffLightDevice();
191
- onOffDevice.addFixedLabel("orientation", getParameter(`orientation${i}`) ?? `orientation ${i}`);
192
- onOffDevice.addOnOffListener(on => commandExecutor(on ? `on${i}` : `off${i}`)?.());
193
- commissioningServer.addDevice(onOffDevice);
194
-
195
- await this.matterServer.addCommissioningServer(commissioningServer);
140
+ /**
141
+ * Defines a shell command from an environment variable and execute it and log the response
142
+ */
143
+ function executeCommand(scriptParamName: string) {
144
+ const script = Environment.default.vars.string(scriptParamName);
145
+ if (script === undefined) return undefined;
146
+ console.log(`${scriptParamName}: ${execSync(script).toString().slice(0, -1)}`);
147
+ }
196
148
 
197
- commissioningServers.push(commissioningServer);
149
+ async function getConfiguration() {
150
+ const environment = Environment.default;
151
+
152
+ const storageService = environment.get(StorageService);
153
+ console.log(`Storage location: ${storageService.location} (Directory)`);
154
+ console.log(
155
+ 'Use the parameter "--storage-path=NAME-OR-PATH" to specify a different storage location in this directory, use --storage-clear to start with an empty storage.',
156
+ );
157
+ const deviceStorage = (await storageService.open("device")).createContext("data");
158
+
159
+ let defaultPasscode = 20202021;
160
+ let defaultDiscriminator = 3840;
161
+ let defaultPort = 5550;
162
+
163
+ const devices = [];
164
+ const numDevices = environment.vars.number("num") ?? 2;
165
+ for (let i = 1; i <= numDevices; i++) {
166
+ const isSocket = deviceStorage.get(`isSocket${i}`, environment.vars.string(`type${i}`) === "socket");
167
+ if (deviceStorage.has(`isSocket${i}`)) {
168
+ console.log(`Device type ${isSocket ? "socket" : "light"} found in storage. --type parameter is ignored.`);
198
169
  }
199
-
200
- /**
201
- * Start the Matter Server
202
- *
203
- * After everything was plugged together we can start the server. When not delayed announcement is set for the
204
- * CommissioningServer node then this command also starts the announcement of the device into the network.
205
- */
206
-
207
- await this.matterServer.start();
208
-
209
- /**
210
- * Print Pairing Information
211
- *
212
- * If the device is not already commissioned (this info is stored in the storage system) then get and print the
213
- * pairing details. This includes the QR code that can be scanned by the Matter app to pair the device.
214
- */
215
-
216
- logger.info("Listening");
217
- console.log();
218
- commissioningServers.forEach((commissioningServer, index) => {
219
- console.log("----------------------------");
220
- console.log(`Device ${index + 1}:`);
221
- if (!commissioningServer.isCommissioned()) {
222
- const pairingData = commissioningServer.getPairingCode();
223
- const { qrPairingCode, manualPairingCode } = pairingData;
224
-
225
- console.log(QrCode.get(qrPairingCode));
226
- console.log(
227
- `QR Code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=${qrPairingCode}`,
228
- );
229
- console.log(`Manual pairing code: ${manualPairingCode}`);
230
- } else {
231
- console.log("Device is already commissioned. Waiting for controllers to connect ...");
232
- }
233
- console.log();
170
+ const deviceName = `Matter ${environment.vars.string(`type${i}`) ?? "light"} device ${i}`;
171
+ const vendorName = "matter-node.js";
172
+ const passcode =
173
+ environment.vars.number(`passcode${i}`) ?? deviceStorage.get(`passcode${i}`, defaultPasscode++);
174
+ const discriminator =
175
+ environment.vars.number(`discriminator${i}`) ??
176
+ deviceStorage.get(`discriminator${i}`, defaultDiscriminator++);
177
+ // product name / id and vendor id should match what is in the device certificate
178
+ const vendorId = environment.vars.number(`vendorid${i}`) ?? deviceStorage.get(`vendorid${i}`, 0xfff1);
179
+ const productName = `node-matter OnOff-Device ${i}`;
180
+ const productId = environment.vars.number(`productid${i}`) ?? deviceStorage.get(`productid${i}`, 0x8000);
181
+
182
+ const port = environment.vars.number(`port${i}`) ?? defaultPort++;
183
+
184
+ const uniqueId =
185
+ environment.vars.string(`uniqueid${i}`) ?? deviceStorage.get(`uniqueid${i}`, `${i}-${Time.nowMs()}`);
186
+
187
+ // Persist basic data to keep them also on restart
188
+ deviceStorage.set(`passcode${i}`, passcode);
189
+ deviceStorage.set(`discriminator${i}`, discriminator);
190
+ deviceStorage.set(`vendorid${i}`, vendorId);
191
+ deviceStorage.set(`productid${i}`, productId);
192
+ deviceStorage.set(`isSocket${i}`, isSocket);
193
+ deviceStorage.set(`uniqueid${i}`, uniqueId);
194
+
195
+ devices.push({
196
+ isSocket,
197
+ deviceName,
198
+ vendorName,
199
+ passcode,
200
+ discriminator,
201
+ vendorId,
202
+ productName,
203
+ productId,
204
+ port,
205
+ uniqueId,
234
206
  });
235
207
  }
236
208
 
237
- async stop() {
238
- await this.matterServer?.close();
239
- }
209
+ return devices;
240
210
  }
241
-
242
- const device = new Device();
243
- device
244
- .start()
245
- .then(() => {
246
- /* done */
247
- })
248
- .catch(err => console.error(err));
249
-
250
- process.on("SIGINT", () => {
251
- // Clean up on CTRL-C
252
- device
253
- .stop()
254
- .then(() => {
255
- // Pragmatic way to make sure the storage is correctly closed before the process ends.
256
- storage
257
- .close()
258
- .then(() => process.exit(0))
259
- .catch(err => console.error(err));
260
- })
261
- .catch(err => console.error(err));
262
- });