@project-chip/matter-node.js-examples 0.7.5 → 0.8.0-alpha.0-20240309-64eaef67

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,307 @@
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 simple on-off Matter device as a light or as a socket.
15
+ * It can be used as CLI script and starting point for your own device node implementation.
16
+ */
17
+
18
+ /**
19
+ * Import needed modules from @project-chip/matter-node.js
20
+ */
21
+ // Include this first to auto-register Crypto, Network and Time Node.js implementations
22
+ import { CommissioningServer, MatterServer } from "@project-chip/matter-node.js";
23
+
24
+ import { BleNode } from "@project-chip/matter-node-ble.js/ble";
25
+ import { Ble } from "@project-chip/matter-node.js/ble";
26
+ import { OnOffLightDevice, OnOffPluginUnitDevice, logEndpoint } 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
+ singleton,
38
+ } from "@project-chip/matter-node.js/util";
39
+ import { DeviceTypeId, VendorId } from "@project-chip/matter.js/datatype";
40
+ import DummyWifiNetworkCommissioningClusterServer from "./cluster/DummyWifiNetworkCommissioningServerLegacy.js";
41
+
42
+ const logger = Logger.get("Device");
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") ?? ".device-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 Device {
91
+ private matterServer: MatterServer | undefined;
92
+
93
+ async start() {
94
+ logger.info(`node-matter`);
95
+
96
+ /**
97
+ * Initialize the storage system.
98
+ *
99
+ * The storage manager is then also used by the Matter server, so this code block in general is required,
100
+ * but you can choose a different storage backend as long as it implements the required API.
101
+ */
102
+
103
+ const storageManager = new StorageManager(storage);
104
+ await storageManager.initialize();
105
+
106
+ /**
107
+ * Collect all needed data
108
+ *
109
+ * This block makes sure to collect all needed data from cli or storage. Replace this with where ever your data
110
+ * come from.
111
+ *
112
+ * Note: This example also uses the initialized storage system to store the device parameter data for convenience
113
+ * and easy reuse. When you also do that be careful to not overlap with Matter-Server own contexts
114
+ * (so maybe better not ;-)).
115
+ */
116
+
117
+ const deviceStorage = storageManager.createContext("Device");
118
+
119
+ if (deviceStorage.has("isSocket")) {
120
+ logger.info("Device type found in storage. -type parameter is ignored.");
121
+ }
122
+ const isSocket = deviceStorage.get("isSocket", getParameter("type") === "socket");
123
+ const deviceName = "Matter test device";
124
+ const vendorName = "matter-node.js";
125
+ const passcode = getIntParameter("passcode") ?? deviceStorage.get("passcode", 20202021);
126
+ const discriminator = getIntParameter("discriminator") ?? deviceStorage.get("discriminator", 3840);
127
+ // product name / id and vendor id should match what is in the device certificate
128
+ const vendorId = getIntParameter("vendorid") ?? deviceStorage.get("vendorid", 0xfff1);
129
+ const productName = `node-matter OnOff ${isSocket ? "Socket" : "Light"}`;
130
+ const productId = getIntParameter("productid") ?? deviceStorage.get("productid", 0x8000);
131
+
132
+ const netInterface = getParameter("netinterface");
133
+ const port = getIntParameter("port") ?? 5540;
134
+
135
+ const uniqueId = getIntParameter("uniqueid") ?? deviceStorage.get("uniqueid", Time.nowMs());
136
+
137
+ deviceStorage.set("passcode", passcode);
138
+ deviceStorage.set("discriminator", discriminator);
139
+ deviceStorage.set("vendorid", vendorId);
140
+ deviceStorage.set("productid", productId);
141
+ deviceStorage.set("isSocket", isSocket);
142
+ deviceStorage.set("uniqueid", uniqueId);
143
+
144
+ /**
145
+ * Create Device instance and add needed Listener
146
+ *
147
+ * Create an instance of the matter device class you want to use.
148
+ * This example uses the OnOffLightDevice or OnOffPluginUnitDevice depending on the value of the type parameter.
149
+ * To execute the on/off scripts defined as parameters a listener for the onOff attribute is registered via the
150
+ * device specific API.
151
+ *
152
+ * The below logic also adds command handlers for commands of clusters that normally are handled device internally
153
+ * like identify that can be implemented with the logic when these commands are called.
154
+ */
155
+
156
+ const onOffDevice = isSocket ? new OnOffPluginUnitDevice() : new OnOffLightDevice();
157
+ onOffDevice.addOnOffListener(on => commandExecutor(on ? "on" : "off")?.());
158
+
159
+ onOffDevice.addCommandHandler("identify", async ({ request: { identifyTime } }) =>
160
+ logger.info(`Identify called for OnOffDevice: ${identifyTime}`),
161
+ );
162
+
163
+ /**
164
+ * Create Matter Server and CommissioningServer Node
165
+ *
166
+ * To allow the device to be announced, found, paired and operated we need a MatterServer instance and add a
167
+ * commissioningServer to it and add the just created device instance to it.
168
+ * The CommissioningServer node defines the port where the server listens for the UDP packages of the Matter protocol
169
+ * and initializes deice specific certificates and such.
170
+ *
171
+ * The below logic also adds command handlers for commands of clusters that normally are handled internally
172
+ * like testEventTrigger (General Diagnostic Cluster) that can be implemented with the logic when these commands
173
+ * are called.
174
+ */
175
+
176
+ this.matterServer = new MatterServer(storageManager, { mdnsInterface: netInterface });
177
+
178
+ const commissioningServer = new CommissioningServer({
179
+ port,
180
+ deviceName,
181
+ deviceType: DeviceTypeId(onOffDevice.deviceType),
182
+ passcode,
183
+ discriminator,
184
+ basicInformation: {
185
+ vendorName,
186
+ vendorId: VendorId(vendorId),
187
+ nodeLabel: productName,
188
+ productName,
189
+ productLabel: productName,
190
+ productId,
191
+ serialNumber: `node-matter-${uniqueId}`,
192
+ },
193
+ delayedAnnouncement: hasParameter("ble"), // Delay announcement when BLE is used to show how limited advertisement works
194
+ activeSessionsChangedCallback: fabricIndex => {
195
+ console.log(
196
+ `activeSessionsChangedCallback: Active sessions changed on Fabric ${fabricIndex}`,
197
+ commissioningServer.getActiveSessionInformation(fabricIndex),
198
+ );
199
+ },
200
+ commissioningChangedCallback: fabricIndex => {
201
+ console.log(
202
+ `commissioningChangedCallback: Commissioning changed on Fabric ${fabricIndex}`,
203
+ commissioningServer.getCommissionedFabricInformation(fabricIndex)[0],
204
+ );
205
+ },
206
+ });
207
+
208
+ // optionally add a listener for the testEventTrigger command from the GeneralDiagnostics cluster
209
+ commissioningServer.addCommandHandler("testEventTrigger", async ({ request: { enableKey, eventTrigger } }) =>
210
+ logger.info(`testEventTrigger called on GeneralDiagnostic cluster: ${enableKey} ${eventTrigger}`),
211
+ );
212
+
213
+ /**
214
+ * Modify automatically added clusters of the Root endpoint if needed
215
+ * In this example we change the networkCommissioning cluster into one for "Wifi only" devices when BLE is used
216
+ * for commissioning, to demonstrate how to do this.
217
+ * If you want to implement Ethernet only devices that get connected to the network via LAN/Ethernet cable,
218
+ * then all this is not needed.
219
+ * The same as shown here for Wi-Fi is also possible theoretical for Thread only or combined devices.
220
+ */
221
+
222
+ if (hasParameter("ble")) {
223
+ // matter.js will create a Ethernet-only device by default when ut comes to Network Commissioning Features.
224
+ // To offer e.g. a "Wi-Fi only device" (or any other combination) we need to override the Network Commissioning
225
+ // cluster and implement all the need handling here. This is a "static implementation" for pure demonstration
226
+ // purposes and just "simulates" the actions to be done. In a real world implementation this would be done by
227
+ // the device implementor based on the relevant networking stack.
228
+ // The NetworkCommissioningCluster and all logics are described in Matter Core Specifications section 11.8
229
+ commissioningServer.addRootClusterServer(DummyWifiNetworkCommissioningClusterServer);
230
+ }
231
+
232
+ commissioningServer.addDevice(onOffDevice);
233
+
234
+ await this.matterServer.addCommissioningServer(commissioningServer);
235
+
236
+ /**
237
+ * Start the Matter Server
238
+ *
239
+ * After everything was plugged together we can start the server. When not delayed announcement is set for the
240
+ * CommissioningServer node then this command also starts the announcement of the device into the network.
241
+ */
242
+
243
+ await this.matterServer.start();
244
+
245
+ logEndpoint(commissioningServer.getRootEndpoint());
246
+
247
+ // When we want to limit the initial announcement to one medium (e.g. BLE) then we need to delay the
248
+ // announcement and provide the limiting information.
249
+ // Without delaying the announcement is directly triggered with the above "start()" call.
250
+ if (hasParameter("ble")) {
251
+ // Announce operational in BLE network only if we have ble enabled, else everywhere
252
+ await commissioningServer.advertise({ ble: true });
253
+ }
254
+
255
+ /**
256
+ * Print Pairing Information
257
+ *
258
+ * If the device is not already commissioned (this info is stored in the storage system) then get and print the
259
+ * pairing details. This includes the QR code that can be scanned by the Matter app to pair the device.
260
+ */
261
+
262
+ logger.info("Listening");
263
+ if (!commissioningServer.isCommissioned()) {
264
+ const pairingData = commissioningServer.getPairingCode({
265
+ ble: hasParameter("ble"),
266
+ softAccessPoint: false,
267
+ onIpNetwork: false,
268
+ });
269
+
270
+ const { qrPairingCode, manualPairingCode } = pairingData;
271
+
272
+ console.log(QrCode.get(qrPairingCode));
273
+ logger.info(
274
+ `QR Code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=${qrPairingCode}`,
275
+ );
276
+ logger.info(`Manual pairing code: ${manualPairingCode}`);
277
+ } else {
278
+ logger.info("Device is already commissioned. Waiting for controllers to connect ...");
279
+ }
280
+ }
281
+
282
+ async stop() {
283
+ await this.matterServer?.close();
284
+ }
285
+ }
286
+
287
+ const device = new Device();
288
+ device
289
+ .start()
290
+ .then(() => {
291
+ /* done */
292
+ })
293
+ .catch(err => console.error(err));
294
+
295
+ process.on("SIGINT", () => {
296
+ // Clean up on CTRL-C
297
+ device
298
+ .stop()
299
+ .then(() => {
300
+ // Pragmatic way to make sure the storage is correctly closed before the process ends.
301
+ storage
302
+ .close()
303
+ .then(() => process.exit(0))
304
+ .catch(err => console.error(err));
305
+ })
306
+ .catch(err => console.error(err));
307
+ });
@@ -0,0 +1,130 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2024 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ // Include this first to auto-register Crypto, Network and Time Node.js implementations
8
+ import "@project-chip/matter-node.js";
9
+
10
+ import { GoToLiftPercentageRequest, WindowCoveringServer } from "@project-chip/matter.js/behaviors/window-covering";
11
+ import { OnOffLightDevice, OnOffLightRequirements } from "@project-chip/matter.js/devices/OnOffLightDevice";
12
+ import { WindowCoveringDevice } from "@project-chip/matter.js/devices/WindowCoveringDevice";
13
+ import { ServerNode } from "@project-chip/matter.js/node";
14
+
15
+ /**
16
+ * This example demonstrates implementation of a "composed" device comprising multiple sub-devices
17
+ *
18
+ * Our example device, the Excelsior 1000 EZ-Nite™, is a roller shade with an illuminated valance.
19
+ */
20
+
21
+ const LiftingWindowCoveringServer = WindowCoveringServer.with("Lift", "AbsolutePosition", "PositionAwareLift");
22
+
23
+ /**
24
+ * Implementation of the Matter WindowCovering cluster for the shade motor.
25
+ *
26
+ * TODO - some of this should probably move to WindowCoveringServer
27
+ */
28
+ class RollerShade extends LiftingWindowCoveringServer {
29
+ get currentPos() {
30
+ return this.state.currentPositionLiftPercent100ths ?? 0;
31
+ }
32
+
33
+ get targetPos() {
34
+ return this.state.targetPositionLiftPercent100ths ?? 0;
35
+ }
36
+
37
+ set targetPos(position: number) {
38
+ this.state.targetPositionLiftPercent100ths = position ?? 0;
39
+ }
40
+
41
+ override async initialize() {
42
+ this.reactTo(this.events.targetPositionLiftPercent100ths$Change, this.writeTargetToMotor);
43
+
44
+ await this.readTargetFromMotor();
45
+ if (this.targetPos === null) {
46
+ this.targetPos = this.currentPos;
47
+ }
48
+ }
49
+
50
+ override upOrOpen() {
51
+ // 0 = 0%, fully open
52
+ this.targetPos = 0;
53
+ }
54
+
55
+ override downOrClose() {
56
+ // 10000 = 100%, fully closed
57
+ this.targetPos = 10000;
58
+ }
59
+
60
+ override stopMotion() {
61
+ this.targetPos = this.currentPos;
62
+ }
63
+
64
+ override goToLiftPercentage(this: RollerShade, request: GoToLiftPercentageRequest) {
65
+ this.targetPos = request.liftPercent100thsValue;
66
+ }
67
+
68
+ protected async writeTargetToMotor() {
69
+ // For this contrived example we just log the target position and don't actually animate our fake roller shade
70
+ console.log("Window covering target position is now", `${this.targetPos / 100}%`);
71
+ }
72
+
73
+ protected async readTargetFromMotor() {
74
+ // Our fake shade is stuck open
75
+ this.state.currentPositionLiftPercent100ths = 0;
76
+ }
77
+
78
+ protected set currentPos(value: number) {
79
+ this.state.currentPositionLiftPercent100ths = value;
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Implementation of the OnOff cluster for our valance light.
85
+ */
86
+ class ValanceLight extends OnOffLightRequirements.OnOffServer {
87
+ override initialize() {
88
+ this.reactTo(this.events.onOff$Change, this.#stateChanged);
89
+ }
90
+
91
+ #stateChanged(value: boolean) {
92
+ console.log(`Valance is now ${value ? "illuminated" : "dark"}`);
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Our Matter node.
98
+ */
99
+ const node = new ServerNode({
100
+ id: "excelsior1000",
101
+
102
+ productDescription: {},
103
+
104
+ commissioning: {
105
+ passcode: 20202021,
106
+ discriminator: 3840,
107
+ },
108
+
109
+ basicInformation: {
110
+ vendorName: "Acme Corporation",
111
+ productName: "Excelsior 1000 EZ-Nite™",
112
+ vendorId: 0xfff1,
113
+ productId: 0x8000,
114
+ serialNumber: "1234-12345-123",
115
+ },
116
+
117
+ parts: [
118
+ {
119
+ type: WindowCoveringDevice.with(RollerShade),
120
+ id: "shade",
121
+ },
122
+
123
+ {
124
+ type: OnOffLightDevice.with(ValanceLight),
125
+ id: "valance",
126
+ },
127
+ ],
128
+ });
129
+
130
+ await node.run();
@@ -0,0 +1,60 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2024 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ // This demonstrates bringing a "light" device online with matter.js.
8
+
9
+ // Include this first to auto-register Crypto, Network and Time Node.js implementations
10
+ import "@project-chip/matter-node.js";
11
+
12
+ import { OnOffLightDevice, OnOffLightRequirements } from "@project-chip/matter.js/devices/OnOffLightDevice";
13
+ import { ServerNode } from "@project-chip/matter.js/node";
14
+
15
+ // Matter exposes functionality in groups called "clusters". For this example device we override the matter.js "On/Off"
16
+ // cluster implementation to print status to the console.
17
+ class ReportingOnOffServer extends OnOffLightRequirements.OnOffServer {
18
+ // Intercept the "on" command to the Matter On/Off cluster to print a log message.
19
+ override async on() {
20
+ console.log("Turning light ON");
21
+ await super.on();
22
+ }
23
+
24
+ // This is the functional inverse of on() above.
25
+ //
26
+ // For demonstration purposes we update state ourselves rather than deferring to matter.js's default "off" handler
27
+ // via super.off().
28
+ override off() {
29
+ console.log("Turning light OFF");
30
+ this.state.onOff = false;
31
+ }
32
+
33
+ // Use event handlers to log on/off state reactively, after it changes.
34
+ override initialize() {
35
+ this.events.onOff$Change.on(value => {
36
+ console.log(`Light is now ${value ? "ON" : "OFF"}`);
37
+ });
38
+ }
39
+ }
40
+
41
+ // Devices are compositions of behaviors like OnOffServer above. To extend an existing device you use builder methods.
42
+ //
43
+ // In this case we are using with() to install our On/Off cluster behavior.
44
+ const ExampleLight = OnOffLightDevice.with(ReportingOnOffServer);
45
+
46
+ // Physical devices appear as "nodes" on a Matter network. As a device implementer you use a NodeServer to bring a
47
+ // device online.
48
+ //
49
+ // Note there are a large number of options to NodeServer that we are allowing to take default values here. See
50
+ // IlluminatedRollerShade.ts for a more detailed example.
51
+ const node = await ServerNode.create();
52
+
53
+ // Nodes are a composition of endpoints. Add a single endpoint to the node, our example light device.
54
+ await node.add(ExampleLight);
55
+
56
+ // Our device is now built and we can bring the node online.
57
+ //
58
+ // Note that you may serve multiple nodes from a single process. We only have one, however, so we can use the run()
59
+ // method of the node.
60
+ await node.run();