@project-chip/matter-node.js-examples 0.7.5-alpha.0-20240222-8696097f → 0.8.0-alpha.1-20240308-033110a3

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
@@ -0,0 +1,267 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @license
4
+ * Copyright 2022-2024 Matter.js Authors
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+
8
+ /**
9
+ * IMPORTANT: This example uses a Legacy API which will be deprecated in the future.
10
+ * It is just still here to support developers in converting their code to the new API!
11
+ */
12
+
13
+ /**
14
+ * This example shows how to create a new device node that is composed of multiple devices.
15
+ * It creates multiple endpoints on the server. When you want to add a composed devices to a Aggregator you need to
16
+ * add all endpoints of the composed device to an "ComposedDevice" instance! (not shown in this example).
17
+ * It can be used as CLI script and starting point for your own device node implementation.
18
+ */
19
+
20
+ /**
21
+ * Import needed modules from @project-chip/matter-node.js
22
+ */
23
+ // Include this first to auto-register Crypto, Network and Time Node.js implementations
24
+ import { CommissioningServer, MatterServer } from "@project-chip/matter-node.js";
25
+
26
+ import { DeviceTypes, OnOffLightDevice, OnOffPluginUnitDevice } from "@project-chip/matter-node.js/device";
27
+ import { Format, Level, Logger } from "@project-chip/matter-node.js/log";
28
+ import { QrCode } from "@project-chip/matter-node.js/schema";
29
+ import { StorageBackendDisk, StorageManager } from "@project-chip/matter-node.js/storage";
30
+ import { Time } from "@project-chip/matter-node.js/time";
31
+ import {
32
+ commandExecutor,
33
+ getIntParameter,
34
+ getParameter,
35
+ hasParameter,
36
+ requireMinNodeVersion,
37
+ } from "@project-chip/matter-node.js/util";
38
+ import { VendorId } from "@project-chip/matter.js/datatype";
39
+
40
+ const logger = Logger.get("MultiDevice");
41
+
42
+ requireMinNodeVersion(16);
43
+
44
+ /** Configure logging */
45
+ switch (getParameter("loglevel")) {
46
+ case "fatal":
47
+ Logger.defaultLogLevel = Level.FATAL;
48
+ break;
49
+ case "error":
50
+ Logger.defaultLogLevel = Level.ERROR;
51
+ break;
52
+ case "warn":
53
+ Logger.defaultLogLevel = Level.WARN;
54
+ break;
55
+ case "info":
56
+ Logger.defaultLogLevel = Level.INFO;
57
+ break;
58
+ }
59
+
60
+ switch (getParameter("logformat")) {
61
+ case "plain":
62
+ Logger.format = Format.PLAIN;
63
+ break;
64
+ case "html":
65
+ Logger.format = Format.HTML;
66
+ break;
67
+ default:
68
+ if (process.stdin?.isTTY) Logger.format = Format.ANSI;
69
+ }
70
+
71
+ const storageLocation = getParameter("store") ?? ".device-node";
72
+ const storage = new StorageBackendDisk(storageLocation, hasParameter("clearstorage"));
73
+ logger.info(`Storage location: ${storageLocation} (Directory)`);
74
+ logger.info(
75
+ 'Use the parameter "-store NAME" to specify a different storage location, use -clearstorage to start with an empty storage.',
76
+ );
77
+
78
+ class Device {
79
+ private matterServer: MatterServer | undefined;
80
+
81
+ async start() {
82
+ logger.info(`node-matter`);
83
+
84
+ /**
85
+ * Initialize the storage system.
86
+ *
87
+ * The storage manager is then also used by the Matter server, so this code block in general is required,
88
+ * but you can choose a different storage backend as long as it implements the required API.
89
+ */
90
+
91
+ const storageManager = new StorageManager(storage);
92
+ await storageManager.initialize();
93
+
94
+ /**
95
+ * Collect all needed data
96
+ *
97
+ * This block makes sure to collect all needed data from cli or storage. Replace this with where ever your data
98
+ * come from.
99
+ *
100
+ * Note: This example also uses the initialized storage system to store the device parameter data for convenience
101
+ * and easy reuse. When you also do that be careful to not overlap with Matter-Server own contexts
102
+ * (so maybe better not ;-)).
103
+ */
104
+ const netInterface = getParameter("netinterface");
105
+
106
+ const deviceStorage = storageManager.createContext("Device");
107
+
108
+ /**
109
+ * Create Matter Server and CommissioningServer Node
110
+ *
111
+ * To allow the device to be announced, found, paired and operated we need a MatterServer instance and add a
112
+ * commissioningServer to it and add the just created device instance to it.
113
+ * The CommissioningServer node defines the port where the server listens for the UDP packages of the Matter protocol
114
+ * and initializes deice specific certificates and such.
115
+ *
116
+ * The below logic also adds command handlers for commands of clusters that normally are handled internally
117
+ * like testEventTrigger (General Diagnostic Cluster) that can be implemented with the logic when these commands
118
+ * are called.
119
+ */
120
+
121
+ this.matterServer = new MatterServer(storageManager, { mdnsInterface: netInterface });
122
+
123
+ /**
124
+ * Create Device instance and add needed Listener
125
+ *
126
+ * Create an instance of the matter device class you want to use.
127
+ * This example uses the OnOffLightDevice or OnOffPluginUnitDevice depending on the value of the type parameter.
128
+ * To execute the on/off scripts defined as parameters a listener for the onOff attribute is registered via the
129
+ * device specific API.
130
+ *
131
+ * The below logic also adds command handlers for commands of clusters that normally are handled device internally
132
+ * like identify that can be implemented with the logic when these commands are called.
133
+ */
134
+
135
+ const commissioningServers = new Array<CommissioningServer>();
136
+
137
+ let defaultPasscode = 20202021;
138
+ let defaultDiscriminator = 3840;
139
+ let defaultPort = 5550;
140
+
141
+ const numDevices = getIntParameter("num") || 2;
142
+ for (let i = 1; i <= numDevices; i++) {
143
+ if (deviceStorage.has(`isSocket${i}`)) {
144
+ logger.info("Device type found in storage. -type parameter is ignored.");
145
+ }
146
+ const isSocket = deviceStorage.get(`isSocket${i}`, getParameter(`type${i}`) === "socket");
147
+ const deviceName = `Matter ${getParameter(`type${i}`) ?? "light"} device ${i}`;
148
+ const deviceType =
149
+ getParameter(`type${i}`) === "socket"
150
+ ? DeviceTypes.ON_OFF_PLUGIN_UNIT.code
151
+ : DeviceTypes.ON_OFF_LIGHT.code;
152
+ const vendorName = "matter-node.js";
153
+ const passcode = getIntParameter(`passcode${i}`) ?? deviceStorage.get(`passcode${i}`, defaultPasscode++);
154
+ const discriminator =
155
+ getIntParameter(`discriminator${i}`) ?? deviceStorage.get(`discriminator${i}`, defaultDiscriminator++);
156
+ // product name / id and vendor id should match what is in the device certificate
157
+ const vendorId = getIntParameter(`vendorid${i}`) ?? deviceStorage.get(`vendorid${i}`, 0xfff1);
158
+ const productName = `node-matter OnOff-Device ${i}`;
159
+ const productId = getIntParameter(`productid${i}`) ?? deviceStorage.get(`productid${i}`, 0x8000);
160
+
161
+ const port = getIntParameter(`port${i}`) ?? defaultPort++;
162
+
163
+ const uniqueId =
164
+ getIntParameter(`uniqueid${i}`) ?? deviceStorage.get(`uniqueid${i}`, `${i}-${Time.nowMs()}`);
165
+
166
+ deviceStorage.set(`passcode${i}`, passcode);
167
+ deviceStorage.set(`discriminator${i}`, discriminator);
168
+ deviceStorage.set(`vendorid${i}`, vendorId);
169
+ deviceStorage.set(`productid${i}`, productId);
170
+ deviceStorage.set(`isSocket${i}`, isSocket);
171
+ deviceStorage.set(`uniqueid${i}`, uniqueId);
172
+
173
+ const commissioningServer = new CommissioningServer({
174
+ port,
175
+ deviceName,
176
+ deviceType,
177
+ passcode,
178
+ discriminator,
179
+ basicInformation: {
180
+ vendorName,
181
+ vendorId: VendorId(vendorId),
182
+ nodeLabel: productName,
183
+ productName,
184
+ productLabel: productName,
185
+ productId,
186
+ serialNumber: `node-matter-${uniqueId}`,
187
+ },
188
+ });
189
+
190
+ console.log(
191
+ `Added device ${i} on port ${port} and unique id ${uniqueId}: Passcode: ${passcode}, Discriminator: ${discriminator}`,
192
+ );
193
+
194
+ const onOffDevice =
195
+ getParameter(`type${i}`) === "socket" ? new OnOffPluginUnitDevice() : new OnOffLightDevice();
196
+ onOffDevice.addFixedLabel("orientation", getParameter(`orientation${i}`) ?? `orientation ${i}`);
197
+ onOffDevice.addOnOffListener(on => commandExecutor(on ? `on${i}` : `off${i}`)?.());
198
+ commissioningServer.addDevice(onOffDevice);
199
+
200
+ await this.matterServer.addCommissioningServer(commissioningServer);
201
+
202
+ commissioningServers.push(commissioningServer);
203
+ }
204
+
205
+ /**
206
+ * Start the Matter Server
207
+ *
208
+ * After everything was plugged together we can start the server. When not delayed announcement is set for the
209
+ * CommissioningServer node then this command also starts the announcement of the device into the network.
210
+ */
211
+
212
+ await this.matterServer.start();
213
+
214
+ /**
215
+ * Print Pairing Information
216
+ *
217
+ * If the device is not already commissioned (this info is stored in the storage system) then get and print the
218
+ * pairing details. This includes the QR code that can be scanned by the Matter app to pair the device.
219
+ */
220
+
221
+ logger.info("Listening");
222
+ console.log();
223
+ commissioningServers.forEach((commissioningServer, index) => {
224
+ console.log("----------------------------");
225
+ console.log(`Device ${index + 1}:`);
226
+ if (!commissioningServer.isCommissioned()) {
227
+ const pairingData = commissioningServer.getPairingCode();
228
+ const { qrPairingCode, manualPairingCode } = pairingData;
229
+
230
+ console.log(QrCode.get(qrPairingCode));
231
+ console.log(
232
+ `QR Code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=${qrPairingCode}`,
233
+ );
234
+ console.log(`Manual pairing code: ${manualPairingCode}`);
235
+ } else {
236
+ console.log("Device is already commissioned. Waiting for controllers to connect ...");
237
+ }
238
+ console.log();
239
+ });
240
+ }
241
+
242
+ async stop() {
243
+ await this.matterServer?.close();
244
+ }
245
+ }
246
+
247
+ const device = new Device();
248
+ device
249
+ .start()
250
+ .then(() => {
251
+ /* done */
252
+ })
253
+ .catch(err => console.error(err));
254
+
255
+ process.on("SIGINT", () => {
256
+ // Clean up on CTRL-C
257
+ device
258
+ .stop()
259
+ .then(() => {
260
+ // Pragmatic way to make sure the storage is correctly closed before the process ends.
261
+ storage
262
+ .close()
263
+ .then(() => process.exit(0))
264
+ .catch(err => console.error(err));
265
+ })
266
+ .catch(err => console.error(err));
267
+ });
@@ -0,0 +1,236 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2024 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ /**
8
+ * This example shows how to create a simple Sensor Matter device as temperature or humidity device.
9
+ * It can be used as CLI script and starting point for your own device node implementation.
10
+ * This example is CJS conform and do not use top level await's.
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 "@project-chip/matter-node.js";
18
+
19
+ import { requireMinNodeVersion } from "@project-chip/matter-node.js/util";
20
+ import { DeviceTypeId, VendorId } from "@project-chip/matter.js/datatype";
21
+ import { logEndpoint } from "@project-chip/matter.js/device";
22
+ import { HumiditySensorDevice } from "@project-chip/matter.js/devices/HumiditySensorDevice";
23
+ import { TemperatureSensorDevice } from "@project-chip/matter.js/devices/TemperatureSensorDevice";
24
+ import { Endpoint, EndpointServer } from "@project-chip/matter.js/endpoint";
25
+ import { Environment, StorageService } from "@project-chip/matter.js/environment";
26
+ import { ServerNode } from "@project-chip/matter.js/node";
27
+ import { Time } from "@project-chip/matter.js/time";
28
+ import { execSync } from "child_process";
29
+
30
+ requireMinNodeVersion(16);
31
+
32
+ async function main() {
33
+ /** Initialize configuration values */
34
+ const {
35
+ isTemperature,
36
+ interval,
37
+ deviceName,
38
+ vendorName,
39
+ passcode,
40
+ discriminator,
41
+ vendorId,
42
+ productName,
43
+ productId,
44
+ port,
45
+ uniqueId,
46
+ } = await getConfiguration();
47
+
48
+ /**
49
+ * Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration
50
+ */
51
+ const server = await ServerNode.create({
52
+ // Required: Give the Node a unique ID which is used to store the state of this node
53
+ id: uniqueId,
54
+
55
+ // Provide Network relevant configuration like the port
56
+ // Optional when operating only one device on a host, Default port is 5540
57
+ network: {
58
+ port,
59
+ },
60
+
61
+ // Provide Commissioning relevant settings
62
+ // Optional for development/testing purposes
63
+ commissioning: {
64
+ passcode,
65
+ discriminator,
66
+ },
67
+
68
+ // Provide Node announcement settings
69
+ // Optional: If Ommitted some development defaults are used
70
+ productDescription: {
71
+ name: deviceName,
72
+ deviceType: DeviceTypeId(
73
+ isTemperature ? TemperatureSensorDevice.deviceType : HumiditySensorDevice.deviceType,
74
+ ),
75
+ },
76
+
77
+ // Provide defaults for the BasicInformation cluster on the Root endpoint
78
+ // Optional: If Omitted some development defaults are used
79
+ basicInformation: {
80
+ vendorName,
81
+ vendorId: VendorId(vendorId),
82
+ nodeLabel: productName,
83
+ productName,
84
+ productLabel: productName,
85
+ productId,
86
+ serialNumber: `matterjs-${uniqueId}`,
87
+ uniqueId,
88
+ },
89
+ });
90
+
91
+ /**
92
+ * Matter Nodes are a composition of endpoints. Create and add a single endpoint to the node. This example uses the
93
+ * OnOffLightDevice or OnOffPlugInUnitDevice depending on the value of the type parameter. It also assigns this Part a
94
+ * unique ID to store the endpoint number for it in the storage to restore the device on restart.
95
+ * In this case we directly use the default command implementation from matter.js. Check out the DeviceNodeFull example
96
+ * to see how to customize the command handlers.
97
+ */
98
+ let endpoint: Endpoint;
99
+ if (isTemperature) {
100
+ endpoint = new Endpoint(TemperatureSensorDevice, {
101
+ id: "tempsensor",
102
+ temperatureMeasurement: { measuredValue: getIntValueFromCommandOrRandom("value") },
103
+ });
104
+ } else {
105
+ endpoint = new Endpoint(HumiditySensorDevice, {
106
+ id: "humsensor",
107
+ relativeHumidityMeasurement: { measuredValue: getIntValueFromCommandOrRandom("value", false) },
108
+ });
109
+ }
110
+
111
+ await server.add(endpoint);
112
+
113
+ /**
114
+ * Log the endpoint structure for debugging reasons and to allow to verify anything is correct
115
+ */
116
+ logEndpoint(EndpointServer.forEndpoint(server));
117
+
118
+ const updateInterval = setInterval(() => {
119
+ let setter: Promise<void>;
120
+ if (isTemperature) {
121
+ setter = endpoint.set({
122
+ temperatureMeasurement: {
123
+ measuredValue: getIntValueFromCommandOrRandom("value"),
124
+ },
125
+ });
126
+ } else {
127
+ setter = endpoint.set({
128
+ relativeHumidityMeasurement: {
129
+ measuredValue: getIntValueFromCommandOrRandom("value", false),
130
+ },
131
+ });
132
+ }
133
+ setter.catch(error => console.error("Error updating measured value:", error));
134
+ }, interval * 1000);
135
+
136
+ // Cleanup our update interval when node goes offline
137
+ server.lifecycle.offline.on(() => clearTimeout(updateInterval));
138
+
139
+ /**
140
+ * In order to start the node and announce it into the network we use the run method which resolves when the node goes
141
+ * offline again because we do not need anything more here. See the Full example for other starting options.
142
+ * The QR Code is printed automatically.
143
+ */
144
+ await server.run();
145
+ }
146
+
147
+ main().catch(error => console.error(error));
148
+
149
+ /*********************************************************************************************************
150
+ * Convenience Methods
151
+ *********************************************************************************************************/
152
+
153
+ /** Defined a shell command from an environment variable and execute it and log the response. */
154
+
155
+ function getIntValueFromCommandOrRandom(scriptParamName: string, allowNegativeValues = true) {
156
+ const script = Environment.default.vars.string(scriptParamName);
157
+ if (script === undefined) {
158
+ if (!allowNegativeValues) return Math.round(Math.random() * 100);
159
+ return (Math.round(Math.random() * 100) - 50) * 100;
160
+ }
161
+ let result = execSync(script).toString().trim();
162
+ if ((result.startsWith("'") && result.endsWith("'")) || (result.startsWith('"') && result.endsWith('"')))
163
+ result = result.slice(1, -1);
164
+ console.log(`Command result: ${result}`);
165
+ let value = Math.round(parseFloat(result));
166
+ if (!allowNegativeValues && value < 0) value = 0;
167
+ return value;
168
+ }
169
+
170
+ async function getConfiguration() {
171
+ /**
172
+ * Collect all needed data
173
+ *
174
+ * This block collects all needed data from cli, environment or storage. Replace this with where ever your data come from.
175
+ *
176
+ * Note: This example uses the matter.js process storage system to store the device parameter data for convenience
177
+ * and easy reuse. When you also do that be careful to not overlap with Matter-Server own storage contexts
178
+ * (so maybe better not do it ;-)).
179
+ */
180
+ const environment = Environment.default;
181
+
182
+ const storageService = environment.get(StorageService);
183
+ console.log(`Storage location: ${storageService.location} (Directory)`);
184
+ console.log(
185
+ '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.',
186
+ );
187
+ const deviceStorage = (await storageService.open("device")).createContext("data");
188
+
189
+ const isTemperature = deviceStorage.get("isTemperature", environment.vars.get("type") !== "humidity");
190
+ if (deviceStorage.has("isTemperature")) {
191
+ console.log(
192
+ `Device type ${isTemperature ? "temperature" : "humidity"} found in storage. --type parameter is ignored.`,
193
+ );
194
+ }
195
+ let interval = environment.vars.number("interval") ?? deviceStorage.get("interval", 60);
196
+ if (interval < 1) {
197
+ console.log(`Invalid Interval ${interval}, set to 60s`);
198
+ interval = 60;
199
+ }
200
+
201
+ const deviceName = "Matter test device";
202
+ const vendorName = "matter-node.js";
203
+ const passcode = environment.vars.number("passcode") ?? deviceStorage.get("passcode", 20202021);
204
+ const discriminator = environment.vars.number("discriminator") ?? deviceStorage.get("discriminator", 3840);
205
+ // product name / id and vendor id should match what is in the device certificate
206
+ const vendorId = environment.vars.number("vendorid") ?? deviceStorage.get("vendorid", 0xfff1);
207
+ const productName = `node-matter OnOff ${isTemperature ? "Temperature" : "Humidity"}`;
208
+ const productId = environment.vars.number("productid") ?? deviceStorage.get("productid", 0x8000);
209
+
210
+ const port = environment.vars.number("port") ?? 5540;
211
+
212
+ const uniqueId = environment.vars.string("uniqueid") ?? deviceStorage.get("uniqueid", Time.nowMs().toString());
213
+
214
+ // Persist basic data to keep them also on restart
215
+ deviceStorage.set("passcode", passcode);
216
+ deviceStorage.set("discriminator", discriminator);
217
+ deviceStorage.set("vendorid", vendorId);
218
+ deviceStorage.set("productid", productId);
219
+ deviceStorage.set("interval", interval);
220
+ deviceStorage.set("isTemperature", isTemperature);
221
+ deviceStorage.set("uniqueid", uniqueId);
222
+
223
+ return {
224
+ isTemperature,
225
+ interval,
226
+ deviceName,
227
+ vendorName,
228
+ passcode,
229
+ discriminator,
230
+ vendorId,
231
+ productName,
232
+ productId,
233
+ port,
234
+ uniqueId,
235
+ };
236
+ }
@@ -0,0 +1,156 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2024 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { GeneralCommissioningBehavior } from "@project-chip/matter.js/behavior/definitions/general-commissioning";
8
+ import {
9
+ AddOrUpdateThreadNetworkRequest,
10
+ ConnectNetworkRequest,
11
+ NetworkCommissioningBehavior,
12
+ RemoveNetworkRequest,
13
+ ReorderNetworkRequest,
14
+ ScanNetworksRequest,
15
+ ScanNetworksResponse,
16
+ } from "@project-chip/matter.js/behavior/definitions/network-commissioning";
17
+ import { NetworkCommissioning } from "@project-chip/matter.js/cluster";
18
+ import { Logger } from "@project-chip/matter.js/log";
19
+ import { ByteArray } from "@project-chip/matter.js/util";
20
+
21
+ const firstNetworkId = new ByteArray(32);
22
+
23
+ /**
24
+ * This represents a Dummy version of a Wifi Network Commissioning Cluster Server without real Wifi related logic, beside
25
+ * returning some values provided as CLI parameters. This dummy implementation is only there for tests/as showcase for BLE
26
+ * commissioning of a device.
27
+ */
28
+ export class DummyThreadNetworkCommissioningServer extends NetworkCommissioningBehavior.with(
29
+ NetworkCommissioning.Feature.ThreadNetworkInterface,
30
+ ) {
31
+ override scanNetworks({ breadcrumb }: ScanNetworksRequest): ScanNetworksResponse {
32
+ console.log(`---> scanNetworks called on NetworkCommissioning cluster: ${breadcrumb}`);
33
+
34
+ // Simulate successful scan
35
+ if (breadcrumb !== undefined) {
36
+ const generalCommissioningCluster = this.agent.get(GeneralCommissioningBehavior);
37
+ generalCommissioningCluster.state.breadcrumb = breadcrumb;
38
+ }
39
+
40
+ const networkingStatus = NetworkCommissioning.NetworkCommissioningStatus.Success;
41
+ this.state.lastNetworkingStatus = networkingStatus;
42
+
43
+ const threadScanResults = [
44
+ {
45
+ panId: this.endpoint.env.vars.number("ble.thread.panId"),
46
+ extendedPanId: BigInt(this.endpoint.env.vars.string("ble.thread.extendedPanId")),
47
+ networkName: this.endpoint.env.vars.string("ble.thread.networkName"),
48
+ channel: this.endpoint.env.vars.number("ble.thread.channel"),
49
+ version: 130,
50
+ extendedAddress: ByteArray.fromString(
51
+ (this.endpoint.env.vars.string("ble.thread.address") ?? "000000000000").toLowerCase(),
52
+ ),
53
+ rssi: -50,
54
+ lqi: 50,
55
+ },
56
+ ];
57
+ console.log(Logger.toJSON(threadScanResults));
58
+
59
+ return {
60
+ networkingStatus,
61
+ threadScanResults,
62
+ };
63
+ }
64
+
65
+ override addOrUpdateThreadNetwork({ operationalDataset, breadcrumb }: AddOrUpdateThreadNetworkRequest) {
66
+ console.log(
67
+ `---> addOrUpdateWiFiNetwork called on NetworkCommissioning cluster: ${operationalDataset.toHex()} ${breadcrumb}`,
68
+ );
69
+
70
+ this.session.context.assertFailSafeArmed("Failsafe timer needs to be armed to add or update networks.");
71
+
72
+ // Simulate successful add or update
73
+ if (breadcrumb !== undefined) {
74
+ const generalCommissioningCluster = this.agent.get(GeneralCommissioningBehavior);
75
+ generalCommissioningCluster.state.breadcrumb = breadcrumb;
76
+ }
77
+
78
+ const networkingStatus = NetworkCommissioning.NetworkCommissioningStatus.Success;
79
+ this.state.lastNetworkingStatus = networkingStatus;
80
+ this.state.lastNetworkId = firstNetworkId;
81
+
82
+ return {
83
+ networkingStatus,
84
+ networkIndex: 0,
85
+ };
86
+ }
87
+
88
+ override removeNetwork({ networkId, breadcrumb }: RemoveNetworkRequest) {
89
+ console.log(`---> removeNetwork called on NetworkCommissioning cluster: ${networkId.toHex()} ${breadcrumb}`);
90
+
91
+ this.session.context.assertFailSafeArmed("Failsafe timer needs to be armed to add or update networks.");
92
+
93
+ // Simulate successful add or update
94
+ if (breadcrumb !== undefined) {
95
+ const generalCommissioningCluster = this.agent.get(GeneralCommissioningBehavior);
96
+ generalCommissioningCluster.state.breadcrumb = breadcrumb;
97
+ }
98
+
99
+ const networkingStatus = NetworkCommissioning.NetworkCommissioningStatus.Success;
100
+ this.state.lastNetworkingStatus = networkingStatus;
101
+ this.state.lastNetworkId = firstNetworkId;
102
+
103
+ return {
104
+ networkingStatus,
105
+ networkIndex: 0,
106
+ };
107
+ }
108
+
109
+ override async connectNetwork({ networkId, breadcrumb }: ConnectNetworkRequest) {
110
+ console.log(`---> connectNetwork called on NetworkCommissioning cluster: ${networkId.toHex()} ${breadcrumb}`);
111
+
112
+ this.session.context.assertFailSafeArmed("Failsafe timer needs to be armed to add or update networks.");
113
+
114
+ // Simulate successful connection
115
+ if (breadcrumb !== undefined) {
116
+ const generalCommissioningCluster = this.agent.get(GeneralCommissioningBehavior);
117
+ generalCommissioningCluster.state.breadcrumb = breadcrumb;
118
+ }
119
+
120
+ this.state.networks[0].connected = true;
121
+
122
+ const networkingStatus = NetworkCommissioning.NetworkCommissioningStatus.Success;
123
+ this.state.lastNetworkingStatus = networkingStatus;
124
+ this.state.lastNetworkId = firstNetworkId;
125
+ this.state.lastConnectErrorValue = null;
126
+
127
+ // Announce operational in IP network
128
+ const device = this.session.context;
129
+ await device.startAnnouncement();
130
+
131
+ return {
132
+ networkingStatus,
133
+ errorValue: null,
134
+ };
135
+ }
136
+
137
+ override reorderNetwork({ networkId, networkIndex, breadcrumb }: ReorderNetworkRequest) {
138
+ console.log(
139
+ `---> reorderNetwork called on NetworkCommissioning cluster: ${networkId.toHex()} ${networkIndex} ${breadcrumb}`,
140
+ );
141
+
142
+ // Simulate successful connection
143
+ if (breadcrumb !== undefined) {
144
+ const generalCommissioningCluster = this.agent.get(GeneralCommissioningBehavior);
145
+ generalCommissioningCluster.state.breadcrumb = breadcrumb;
146
+ }
147
+
148
+ const networkingStatus = NetworkCommissioning.NetworkCommissioningStatus.Success;
149
+ this.state.lastNetworkingStatus = networkingStatus;
150
+
151
+ return {
152
+ networkingStatus,
153
+ networkIndex: 0,
154
+ };
155
+ }
156
+ }