@project-chip/matter-node.js-examples 0.5.0 → 0.5.1-alpha.0-20231005-d8f807e

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. package/README.md +7 -1
  2. package/dist/cjs/examples/BridgedDevicesNode.d.ts +8 -0
  3. package/dist/cjs/examples/BridgedDevicesNode.d.ts.map +1 -0
  4. package/dist/cjs/examples/BridgedDevicesNode.js +135 -0
  5. package/dist/cjs/examples/BridgedDevicesNode.js.map +7 -0
  6. package/dist/cjs/examples/ComposedDeviceNode.d.ts +8 -0
  7. package/dist/cjs/examples/ComposedDeviceNode.d.ts.map +1 -0
  8. package/dist/cjs/examples/ComposedDeviceNode.js +132 -0
  9. package/dist/cjs/examples/ComposedDeviceNode.js.map +7 -0
  10. package/dist/cjs/examples/ControllerNode.d.ts +8 -0
  11. package/dist/cjs/examples/ControllerNode.d.ts.map +1 -0
  12. package/dist/cjs/examples/ControllerNode.js +172 -0
  13. package/dist/cjs/examples/ControllerNode.js.map +7 -0
  14. package/dist/cjs/examples/DeviceNode.d.ts +8 -0
  15. package/dist/cjs/examples/DeviceNode.d.ts.map +1 -0
  16. package/dist/cjs/examples/DeviceNode.js +174 -0
  17. package/dist/cjs/examples/DeviceNode.js.map +7 -0
  18. package/dist/cjs/examples/MultiDeviceNode.d.ts +8 -0
  19. package/dist/cjs/examples/MultiDeviceNode.d.ts.map +1 -0
  20. package/dist/cjs/examples/MultiDeviceNode.js +140 -0
  21. package/dist/cjs/examples/MultiDeviceNode.js.map +7 -0
  22. package/dist/cjs/examples/cluster/DummyWifiNetworkCommissioningClusterServer.d.ts +18 -0
  23. package/dist/cjs/examples/cluster/DummyWifiNetworkCommissioningClusterServer.d.ts.map +1 -0
  24. package/dist/cjs/examples/cluster/DummyWifiNetworkCommissioningClusterServer.js +166 -0
  25. package/dist/cjs/examples/cluster/DummyWifiNetworkCommissioningClusterServer.js.map +7 -0
  26. package/dist/cjs/package.json +1 -0
  27. package/package.json +21 -25
  28. package/src/examples/BridgedDevicesNode.ts +247 -0
  29. package/src/examples/ComposedDeviceNode.ts +245 -0
  30. package/src/examples/ControllerNode.ts +294 -0
  31. package/src/examples/DeviceNode.ts +288 -0
  32. package/src/examples/MultiDeviceNode.ts +260 -0
  33. package/src/examples/cluster/DummyWifiNetworkCommissioningClusterServer.ts +181 -0
  34. package/dist/examples/BridgedDevicesNode.js +0 -218
  35. package/dist/examples/BridgedDevicesNode.js.map +0 -1
  36. package/dist/examples/ComposedDeviceNode.js +0 -217
  37. package/dist/examples/ComposedDeviceNode.js.map +0 -1
  38. package/dist/examples/ControllerNode.js +0 -261
  39. package/dist/examples/ControllerNode.js.map +0 -1
  40. package/dist/examples/DeviceNode.js +0 -358
  41. package/dist/examples/DeviceNode.js.map +0 -1
  42. package/dist/examples/MultiDeviceNode.js +0 -228
  43. package/dist/examples/MultiDeviceNode.js.map +0 -1
@@ -0,0 +1,294 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * @license
5
+ * Copyright 2022-2023 Project CHIP Authors
6
+ * SPDX-License-Identifier: Apache-2.0
7
+ */
8
+
9
+ /**
10
+ * This example shows how to create a Matter controller to pair with a device and interfact with it.
11
+ * It can be used as CLI script, but is more thought as a starting point for your own controller implementation
12
+ * because you need to adjust the code in any way depending on your use case.
13
+ */
14
+
15
+ /**
16
+ * Import needed modules from @project-chip/matter-node.js
17
+ */
18
+ // Include this first to auto-register Crypto, Network and Time Node.js implementations
19
+ import { CommissioningController, MatterServer } from "@project-chip/matter-node.js";
20
+
21
+ import { BleNode } from "@project-chip/matter-node-ble.js/ble";
22
+ import { Ble } from "@project-chip/matter-node.js/ble";
23
+ import {
24
+ BasicInformationCluster,
25
+ DescriptorCluster,
26
+ GeneralCommissioning,
27
+ OnOffCluster,
28
+ } from "@project-chip/matter-node.js/cluster";
29
+ import { logEndpoint } from "@project-chip/matter-node.js/device";
30
+ import { Format, Level, Logger } from "@project-chip/matter-node.js/log";
31
+ import { CommissioningOptions } from "@project-chip/matter-node.js/protocol";
32
+ import { ManualPairingCodeCodec } from "@project-chip/matter-node.js/schema";
33
+ import { StorageBackendDisk, StorageManager } from "@project-chip/matter-node.js/storage";
34
+ import {
35
+ getIntParameter,
36
+ getParameter,
37
+ hasParameter,
38
+ requireMinNodeVersion,
39
+ singleton,
40
+ } from "@project-chip/matter-node.js/util";
41
+
42
+ const logger = Logger.get("Controller");
43
+
44
+ requireMinNodeVersion(16);
45
+
46
+ /** Configure logging */
47
+ switch (getParameter("loglevel")) {
48
+ case "fatal":
49
+ Logger.defaultLogLevel = Level.FATAL;
50
+ break;
51
+ case "error":
52
+ Logger.defaultLogLevel = Level.ERROR;
53
+ break;
54
+ case "warn":
55
+ Logger.defaultLogLevel = Level.WARN;
56
+ break;
57
+ case "info":
58
+ Logger.defaultLogLevel = Level.INFO;
59
+ break;
60
+ }
61
+
62
+ switch (getParameter("logformat")) {
63
+ case "plain":
64
+ Logger.format = Format.PLAIN;
65
+ break;
66
+ case "html":
67
+ Logger.format = Format.HTML;
68
+ break;
69
+ default:
70
+ if (process.stdin?.isTTY) Logger.format = Format.ANSI;
71
+ }
72
+
73
+ if (hasParameter("ble")) {
74
+ // Initialize Ble
75
+ Ble.get = singleton(
76
+ () =>
77
+ new BleNode({
78
+ hciId: getIntParameter("ble-hci-id"),
79
+ }),
80
+ );
81
+ }
82
+
83
+ const storageLocation = getParameter("store") ?? ".controller-node";
84
+ const storage = new StorageBackendDisk(storageLocation, hasParameter("clearstorage"));
85
+ logger.info(`Storage location: ${storageLocation} (Directory)`);
86
+ logger.info(
87
+ 'Use the parameter "-store NAME" to specify a different storage location, use -clearstorage to start with an empty storage.',
88
+ );
89
+
90
+ class ControllerNode {
91
+ async start() {
92
+ logger.info(`node-matter Controller started`);
93
+
94
+ /**
95
+ * Initialize the storage system.
96
+ *
97
+ * The storage manager is then also used by the Matter server, so this code block in general is required,
98
+ * but you can choose a different storage backend as long as it implements the required API.
99
+ */
100
+
101
+ const storageManager = new StorageManager(storage);
102
+ await storageManager.initialize();
103
+
104
+ /**
105
+ * Collect all needed data
106
+ *
107
+ * This block makes sure to collect all needed data from cli or storage. Replace this with where ever your data
108
+ * come from.
109
+ *
110
+ * Note: This example also uses the initialized storage system to store the device parameter data for convenience
111
+ * and easy reuse. When you also do that be careful to not overlap with Matter-Server own contexts
112
+ * (so maybe better not ;-)).
113
+ */
114
+
115
+ const controllerStorage = storageManager.createContext("Controller");
116
+ const ip = controllerStorage.has("ip") ? controllerStorage.get<string>("ip") : getParameter("ip");
117
+ const port = controllerStorage.has("port") ? controllerStorage.get<number>("port") : getIntParameter("port");
118
+
119
+ const pairingCode = getParameter("pairingcode");
120
+ let longDiscriminator, setupPin, shortDiscriminator;
121
+ if (pairingCode !== undefined) {
122
+ const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
123
+ shortDiscriminator = pairingCodeCodec.shortDiscriminator;
124
+ longDiscriminator = undefined;
125
+ setupPin = pairingCodeCodec.passcode;
126
+ logger.debug(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
127
+ } else {
128
+ longDiscriminator =
129
+ getIntParameter("longDiscriminator") ?? controllerStorage.get("longDiscriminator", 3840);
130
+ if (longDiscriminator > 4095) throw new Error("Discriminator value must be less than 4096");
131
+ setupPin = getIntParameter("pin") ?? controllerStorage.get("pin", 20202021);
132
+ }
133
+ if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
134
+ throw new Error(
135
+ "Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode",
136
+ );
137
+ }
138
+
139
+ // Collect commissioning options from commandline parameters
140
+ const commissioningOptions: CommissioningOptions = {
141
+ regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
142
+ regulatoryCountryCode: "XX",
143
+ };
144
+ if (hasParameter("ble")) {
145
+ const wifiSsid = getParameter("ble-wifi-ssid");
146
+ const wifiCredentials = getParameter("ble-wifi-credentials");
147
+ const threadNetworkName = getParameter("ble-thread-networkname");
148
+ const threadOperationalDataset = getParameter("ble-thread-operationaldataset");
149
+ if (wifiSsid !== undefined && wifiCredentials !== undefined) {
150
+ logger.info(`Registering Commissioning over BLE with WiFi: ${wifiSsid}`);
151
+ commissioningOptions.wifiNetwork = {
152
+ wifiSsid: wifiSsid,
153
+ wifiCredentials: wifiCredentials,
154
+ };
155
+ }
156
+ if (threadNetworkName !== undefined && threadOperationalDataset !== undefined) {
157
+ logger.info(`Registering Commissioning over BLE with Thread: ${threadNetworkName}`);
158
+ commissioningOptions.threadNetwork = {
159
+ networkName: threadNetworkName,
160
+ operationalDataset: threadOperationalDataset,
161
+ };
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Create Matter Server and Controller Node
167
+ *
168
+ * To allow the device to be announced, found, paired and operated we need a MatterServer instance and add a
169
+ * CommissioningController to it and add the just created device instance to it.
170
+ * The Controller node defines the port where the server listens for the UDP packages of the Matter protocol
171
+ * and initializes deice specific certificates and such.
172
+ *
173
+ * The below logic also adds command handlers for commands of clusters that normally are handled internally
174
+ * like testEventTrigger (General Diagnostic Cluster) that can be implemented with the logic when these commands
175
+ * are called.
176
+ */
177
+
178
+ const matterServer = new MatterServer(storageManager);
179
+ const commissioningController = new CommissioningController({
180
+ serverAddress: ip !== undefined && port !== undefined ? { ip, port, type: "udp" } : undefined,
181
+ longDiscriminator,
182
+ shortDiscriminator,
183
+ passcode: setupPin,
184
+ delayedPairing: true,
185
+ commissioningOptions,
186
+ subscribeAllAttributes: true,
187
+ });
188
+ matterServer.addCommissioningController(commissioningController);
189
+
190
+ /**
191
+ * Start the Matter Server
192
+ *
193
+ * After everything was plugged together we can start the server. When not delayed announcement is set for the
194
+ * CommissioningServer node then this command also starts the announcement of the device into the network.
195
+ */
196
+
197
+ await matterServer.start();
198
+
199
+ /**
200
+ * TBD
201
+ */
202
+ try {
203
+ await commissioningController.connect();
204
+
205
+ if (commissioningController.serverAddress !== undefined) {
206
+ const { ip, port } = commissioningController.serverAddress;
207
+ controllerStorage.set("ip", ip);
208
+ controllerStorage.set("port", port);
209
+ }
210
+ if (longDiscriminator !== undefined) {
211
+ controllerStorage.set("longDiscriminator", longDiscriminator);
212
+ }
213
+ controllerStorage.set("pin", setupPin);
214
+
215
+ // Important: This is a temporary API to proof the methods working and this will change soon and is NOT stable!
216
+ // It is provided to proof the concept
217
+
218
+ logEndpoint(commissioningController.getRootEndpoint());
219
+
220
+ // Example to initialize a ClusterClient and access concrete fields as API methods
221
+ const descriptor = commissioningController.getRootClusterClient(DescriptorCluster);
222
+ if (descriptor !== undefined) {
223
+ console.log(await descriptor.attributes.deviceTypeList.get()); // you can call that way
224
+ console.log(await descriptor.getServerListAttribute()); // or more convenient that way
225
+ } else {
226
+ console.log("No Descriptor Cluster found. This should never happen!");
227
+ }
228
+
229
+ // Example to subscribe to a field and get the value
230
+ const info = commissioningController.getRootClusterClient(BasicInformationCluster);
231
+ if (info !== undefined) {
232
+ console.log(await info.getProductNameAttribute()); // This call is executed remotely
233
+ //console.log(await info.subscribeProductNameAttribute(value => console.log("productName", value), 5, 30));
234
+ //console.log(await info.getProductNameAttribute()); // This call is resolved locally because we have subscribed to the value!
235
+ } else {
236
+ console.log("No BasicInformation Cluster found. This should never happen!");
237
+ }
238
+
239
+ // Example to get all Attributes of the commissioned node: */*/*
240
+ //const attributesAll = await interactionClient.getAllAttributes();
241
+ //console.log("Attributes-All:", Logger.toJSON(attributesAll));
242
+
243
+ // Example to get all Attributes of all Descriptor Clusters of the commissioned node: */DescriptorCluster/*
244
+ //const attributesAllDescriptor = await interactionClient.getMultipleAttributes([{ clusterId: DescriptorCluster.id} ]);
245
+ //console.log("Attributes-Descriptor:", JSON.stringify(attributesAllDescriptor, null, 2));
246
+
247
+ // Example to get all Attributes of the Basic Information Cluster of endpoint 0 of the commissioned node: 0/BasicInformationCluster/*
248
+ //const attributesBasicInformation = await interactionClient.getMultipleAttributes([{ endpointId: 0, clusterId: BasicInformationCluster.id} ]);
249
+ //console.log("Attributes-BasicInformation:", JSON.stringify(attributesBasicInformation, null, 2));
250
+
251
+ const devices = commissioningController.getDevices();
252
+ if (devices[0] && devices[0].id === 1) {
253
+ // Example to subscribe to all Attributes of endpoint 1 of the commissioned node: */*/*
254
+ //await interactionClient.subscribeMultipleAttributes([{ endpointId: 1, /* subscribe anything from endpoint 1 */ }], 0, 180, data => {
255
+ // console.log("Subscribe-All Data:", Logger.toJSON(data));
256
+ //});
257
+
258
+ const onOff = devices[0].getClusterClient(OnOffCluster);
259
+ if (onOff !== undefined) {
260
+ let onOffStatus = await onOff.getOnOffAttribute();
261
+ console.log("initial onOffStatus", onOffStatus);
262
+
263
+ onOff.addOnOffAttributeListener(value => {
264
+ console.log("subscription onOffStatus", value);
265
+ onOffStatus = value;
266
+ });
267
+ // read data every minute to keep up the connection to show the subscription is working
268
+ setInterval(() => {
269
+ onOff
270
+ .toggle()
271
+ .then(() => {
272
+ onOffStatus = !onOffStatus;
273
+ console.log("onOffStatus", onOffStatus);
274
+ })
275
+ .catch(error => logger.error(error));
276
+ }, 60000);
277
+ }
278
+ }
279
+ } finally {
280
+ //await matterServer.close(); // Comment out when subscribes are used, else the connection will be closed
281
+ setTimeout(() => process.exit(0), 1000000);
282
+ }
283
+ }
284
+ }
285
+
286
+ new ControllerNode().start().catch(error => logger.error(error));
287
+
288
+ process.on("SIGINT", () => {
289
+ // Pragmatic way to make sure the storage is correctly closed before the process ends.
290
+ storage
291
+ .close()
292
+ .then(() => process.exit(0))
293
+ .catch(() => process.exit(1));
294
+ });
@@ -0,0 +1,288 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @license
4
+ * Copyright 2022 The node-matter Authors
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+
8
+ /**
9
+ * This example shows how to create a simple on-off Matter device.
10
+ * It can be used as CLI script and starting point for your own device node implementation.
11
+ */
12
+
13
+ /**
14
+ * Import needed modules from @project-chip/matter-node.js
15
+ */
16
+ // Include this first to auto-register Crypto, Network and Time Node.js implementations
17
+ import { CommissioningServer, MatterServer } from "@project-chip/matter-node.js";
18
+
19
+ import { BleNode } from "@project-chip/matter-node-ble.js/ble";
20
+ import { Ble } from "@project-chip/matter-node.js/ble";
21
+ import { OnOffLightDevice, OnOffPluginUnitDevice, logEndpoint } from "@project-chip/matter-node.js/device";
22
+ import { Format, Level, Logger } from "@project-chip/matter-node.js/log";
23
+ import { StorageBackendDisk, StorageManager } from "@project-chip/matter-node.js/storage";
24
+ import { Time } from "@project-chip/matter-node.js/time";
25
+ import {
26
+ commandExecutor,
27
+ getIntParameter,
28
+ getParameter,
29
+ hasParameter,
30
+ requireMinNodeVersion,
31
+ singleton,
32
+ } from "@project-chip/matter-node.js/util";
33
+ import { DeviceTypeId, VendorId } from "@project-chip/matter.js/datatype";
34
+ import DummyWifiNetworkCommissioningClusterServer from "./cluster/DummyWifiNetworkCommissioningClusterServer.js";
35
+
36
+ const logger = Logger.get("Device");
37
+
38
+ requireMinNodeVersion(16);
39
+
40
+ /** Configure logging */
41
+ switch (getParameter("loglevel")) {
42
+ case "fatal":
43
+ Logger.defaultLogLevel = Level.FATAL;
44
+ break;
45
+ case "error":
46
+ Logger.defaultLogLevel = Level.ERROR;
47
+ break;
48
+ case "warn":
49
+ Logger.defaultLogLevel = Level.WARN;
50
+ break;
51
+ case "info":
52
+ Logger.defaultLogLevel = Level.INFO;
53
+ break;
54
+ }
55
+
56
+ switch (getParameter("logformat")) {
57
+ case "plain":
58
+ Logger.format = Format.PLAIN;
59
+ break;
60
+ case "html":
61
+ Logger.format = Format.HTML;
62
+ break;
63
+ default:
64
+ if (process.stdin?.isTTY) Logger.format = Format.ANSI;
65
+ }
66
+
67
+ if (hasParameter("ble")) {
68
+ // Initialize Ble
69
+ Ble.get = singleton(
70
+ () =>
71
+ new BleNode({
72
+ hciId: getIntParameter("ble-hci-id"),
73
+ }),
74
+ );
75
+ }
76
+
77
+ const storageLocation = getParameter("store") ?? ".device-node";
78
+ const storage = new StorageBackendDisk(storageLocation, hasParameter("clearstorage"));
79
+ logger.info(`Storage location: ${storageLocation} (Directory)`);
80
+ logger.info(
81
+ 'Use the parameter "-store NAME" to specify a different storage location, use -clearstorage to start with an empty storage.',
82
+ );
83
+
84
+ class Device {
85
+ private matterServer: MatterServer | undefined;
86
+
87
+ async start() {
88
+ logger.info(`node-matter`);
89
+
90
+ /**
91
+ * Initialize the storage system.
92
+ *
93
+ * The storage manager is then also used by the Matter server, so this code block in general is required,
94
+ * but you can choose a different storage backend as long as it implements the required API.
95
+ */
96
+
97
+ const storageManager = new StorageManager(storage);
98
+ await storageManager.initialize();
99
+
100
+ /**
101
+ * Collect all needed data
102
+ *
103
+ * This block makes sure to collect all needed data from cli or storage. Replace this with where ever your data
104
+ * come from.
105
+ *
106
+ * Note: This example also uses the initialized storage system to store the device parameter data for convenience
107
+ * and easy reuse. When you also do that be careful to not overlap with Matter-Server own contexts
108
+ * (so maybe better not ;-)).
109
+ */
110
+
111
+ const deviceStorage = storageManager.createContext("Device");
112
+
113
+ if (deviceStorage.has("isSocket")) {
114
+ logger.info("Device type found in storage. -type parameter is ignored.");
115
+ }
116
+ const isSocket = deviceStorage.get("isSocket", getParameter("type") === "socket");
117
+ const deviceName = "Matter test device";
118
+ const vendorName = "matter-node.js";
119
+ const passcode = getIntParameter("passcode") ?? deviceStorage.get("passcode", 20202021);
120
+ const discriminator = getIntParameter("discriminator") ?? deviceStorage.get("discriminator", 3840);
121
+ // product name / id and vendor id should match what is in the device certificate
122
+ const vendorId = getIntParameter("vendorid") ?? deviceStorage.get("vendorid", 0xfff1);
123
+ const productName = `node-matter OnOff ${isSocket ? "Socket" : "Light"}`;
124
+ const productId = getIntParameter("productid") ?? deviceStorage.get("productid", 0x8000);
125
+
126
+ const netAnnounceInterface = getParameter("announceinterface");
127
+ const port = getIntParameter("port") ?? 5540;
128
+
129
+ const uniqueId = getIntParameter("uniqueid") ?? deviceStorage.get("uniqueid", Time.nowMs());
130
+
131
+ deviceStorage.set("passcode", passcode);
132
+ deviceStorage.set("discriminator", discriminator);
133
+ deviceStorage.set("vendorid", vendorId);
134
+ deviceStorage.set("productid", productId);
135
+ deviceStorage.set("isSocket", isSocket);
136
+ deviceStorage.set("uniqueid", uniqueId);
137
+
138
+ /**
139
+ * Create Device instance and add needed Listener
140
+ *
141
+ * Create an instance of the matter device class you want to use.
142
+ * This example uses the OnOffLightDevice or OnOffPluginUnitDevice depending on the value of the type parameter.
143
+ * To execute the on/off scripts defined as parameters a listener for the onOff attribute is registered via the
144
+ * device specific API.
145
+ *
146
+ * The below logic also adds command handlers for commands of clusters that normally are handled device internally
147
+ * like identify that can be implemented with the logic when these commands are called.
148
+ */
149
+
150
+ const onOffDevice = isSocket ? new OnOffPluginUnitDevice() : new OnOffLightDevice();
151
+ onOffDevice.addOnOffListener(on => commandExecutor(on ? "on" : "off")?.());
152
+
153
+ onOffDevice.addCommandHandler("identify", async ({ request: { identifyTime } }) =>
154
+ logger.info(`Identify called for OnOffDevice: ${identifyTime}`),
155
+ );
156
+
157
+ /**
158
+ * Create Matter Server and CommissioningServer Node
159
+ *
160
+ * To allow the device to be announced, found, paired and operated we need a MatterServer instance and add a
161
+ * commissioningServer to it and add the just created device instance to it.
162
+ * The CommissioningServer node defines the port where the server listens for the UDP packages of the Matter protocol
163
+ * and initializes deice specific certificates and such.
164
+ *
165
+ * The below logic also adds command handlers for commands of clusters that normally are handled internally
166
+ * like testEventTrigger (General Diagnostic Cluster) that can be implemented with the logic when these commands
167
+ * are called.
168
+ */
169
+
170
+ this.matterServer = new MatterServer(storageManager, { mdnsAnnounceInterface: netAnnounceInterface });
171
+
172
+ const commissioningServer = new CommissioningServer({
173
+ port,
174
+ deviceName,
175
+ deviceType: DeviceTypeId(onOffDevice.deviceType),
176
+ passcode,
177
+ discriminator,
178
+ basicInformation: {
179
+ vendorName,
180
+ vendorId: VendorId(vendorId),
181
+ nodeLabel: productName,
182
+ productName,
183
+ productLabel: productName,
184
+ productId,
185
+ serialNumber: `node-matter-${uniqueId}`,
186
+ },
187
+ delayedAnnouncement: hasParameter("ble"), // Delay announcement when BLE is used to show how limited advertisement works
188
+ });
189
+
190
+ // optionally add a listener for the testEventTrigger command from the GeneralDiagnostics cluster
191
+ commissioningServer.addCommandHandler("testEventTrigger", async ({ request: { enableKey, eventTrigger } }) =>
192
+ logger.info(`testEventTrigger called on GeneralDiagnostic cluster: ${enableKey} ${eventTrigger}`),
193
+ );
194
+
195
+ /**
196
+ * Modify automatically added clusters of the Root endpoint if needed
197
+ * In this example we change the networkCommissioning cluster into one for "Wifi only" devices when BLE is used
198
+ * for commissioning, to demonstrate how to do this.
199
+ * If you want to implement Ethernet only devices that get connected to the network via LAN/Ethernet cable,
200
+ * then all this is not needed.
201
+ * The same as shown here for Wi-Fi is also possible theoretical for Thread only or combined devices.
202
+ */
203
+
204
+ if (hasParameter("ble")) {
205
+ // matter.js will create a Ethernet-only device by default when ut comes to Network Commissioning Features.
206
+ // To offer e.g. a "Wi-Fi only device" (or any other combination) we need to override the Network Commissioning
207
+ // cluster and implement all the need handling here. This is a "static implementation" for pure demonstration
208
+ // purposes and just "simulates" the actions to be done. In a real world implementation this would be done by
209
+ // the device implementor based on the relevant networking stack.
210
+ // The NetworkCommissioningCluster and all logics are described in Matter Core Specifications section 11.8
211
+ commissioningServer.addRootClusterServer(DummyWifiNetworkCommissioningClusterServer);
212
+ }
213
+
214
+ commissioningServer.addDevice(onOffDevice);
215
+
216
+ this.matterServer.addCommissioningServer(commissioningServer);
217
+
218
+ /**
219
+ * Start the Matter Server
220
+ *
221
+ * After everything was plugged together we can start the server. When not delayed announcement is set for the
222
+ * CommissioningServer node then this command also starts the announcement of the device into the network.
223
+ */
224
+
225
+ await this.matterServer.start();
226
+
227
+ logEndpoint(commissioningServer.getRootEndpoint());
228
+
229
+ // When we want to limit the initial announcement to one medium (e.g. BLE) then we need to delay the
230
+ // announcement and provide the limiting information.
231
+ // Without delaying the announcement is directly triggered with the above "start()" call.
232
+ if (hasParameter("ble")) {
233
+ // Announce operational in BLE network only if we have ble enabled, else everywhere
234
+ await commissioningServer.advertise({ ble: true });
235
+ }
236
+
237
+ /**
238
+ * Print Pairing Information
239
+ *
240
+ * If the device is not already commissioned (this info is stored in the storage system) then get and print the
241
+ * pairing details. This includes the QR code that can be scanned by the Matter app to pair the device.
242
+ */
243
+
244
+ logger.info("Listening");
245
+ if (!commissioningServer.isCommissioned()) {
246
+ const pairingData = commissioningServer.getPairingCode({
247
+ ble: hasParameter("ble"),
248
+ softAccessPoint: false,
249
+ onIpNetwork: false,
250
+ });
251
+
252
+ const { qrCode, qrPairingCode, manualPairingCode } = pairingData;
253
+
254
+ console.log(qrCode);
255
+ logger.info(
256
+ `QR Code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=${qrPairingCode}`,
257
+ );
258
+ logger.info(`Manual pairing code: ${manualPairingCode}`);
259
+ } else {
260
+ logger.info("Device is already commissioned. Waiting for controllers to connect ...");
261
+ }
262
+ }
263
+
264
+ async stop() {
265
+ await this.matterServer?.close();
266
+ }
267
+ }
268
+
269
+ const device = new Device();
270
+ device
271
+ .start()
272
+ .then(() => {
273
+ /* done */
274
+ })
275
+ .catch(err => console.error(err));
276
+
277
+ process.on("SIGINT", () => {
278
+ device
279
+ .stop()
280
+ .then(() => {
281
+ // Pragmatic way to make sure the storage is correctly closed before the process ends.
282
+ storage
283
+ .close()
284
+ .then(() => process.exit(0))
285
+ .catch(err => console.error(err));
286
+ })
287
+ .catch(err => console.error(err));
288
+ });