@project-chip/matter-node.js-examples 0.8.0-alpha.0-20240314-ed7b831b → 0.8.0-alpha.0-20240319-3d88df96

Sign up to get free protection for your applications and to get access to all the features.
package/README.md CHANGED
@@ -15,6 +15,11 @@ For BLE usage please also see the [matter-node-ble.js README.md](../matter-node-
15
15
  ## Examples Overview
16
16
  The examples provided by this repository show different use cases demonstrating how to build different device types and show various ways to implement and interact with the matter.js structures. This section gives an overview regarding what the different examples demonstrate.
17
17
 
18
+ > [!IMPORTANT]
19
+ > With matter.js 0.8 we introduced a new API that is not compatible with the old API. The new API is more flexible and allows for more complex use cases. The old API is still supported but will be removed in a future release. The examples are provided for both APIs. The new API examples are named without "Legacy" in the filename and the npm run script is named without "-legacy". The old API examples are named with "Legacy" in the filename and the npm run script is named with "-legacy".
20
+ > If you used the old Example scripts and want to just switch to use the new scripts you need to convert the device storage once. Please see below in the section about the ["Legacy Storage Converter"](#legacy-storage-converter).
21
+
22
+
18
23
  ### Legacy Device Examples
19
24
  These examples use the matter.js API up to 0.7, which is considered "Legacy" now because it will be replaced by a new API starting with 0.8. The functionality is still working, but should be considered deprecated and will be removed with a later release!
20
25
  The filenames of these examples were adjusted in 0.8 and a "Legacy" was added to the end of the filename and "-legacy" to the npm run script name (exception: DeviceNode is now called DeviceNodeFullLegacy). Other than that they are fully compatible with the pre-0.7 versions also regarding storage location and structure and can be directly used as before.
@@ -55,6 +60,18 @@ Additionally, these two examples are not directly configurable by CLI and mainly
55
60
  ## Controller example
56
61
  * **ControllerNode**: This example shows basically how a controller could be implemented by showing pairing and connections to a paired device. When there is an OnOff Endpoint with ID 1 then this is controlled and toggled.
57
62
 
63
+ ## Legacy Storage Converter
64
+ The Legacy storage converter can convert the Device and Controller storages from the legacy examples. It will convert all relevant storage keys, but not the persisted cluster specific data (which should be acceptable because near to irrelevant for the device types we had examples for).
65
+
66
+ To use the converter, you need to run the following command:
67
+ ```bash
68
+ npm run matter-legacystorageconverter -- --legacy-storage-path=<Path-to-Legcy-Storage-Dir> --storage-path=<Path-to-New-Storage-Dir>
69
+ ```
70
+
71
+ > [!NOTICE]
72
+ > The Conbverter might tell you to make sure to use the full parameters when starting the new example after conversion. This is needed in some cases because some data were not stored in the storage prior the new versions and are so missing in the storage.
73
+
74
+ After this you can use the new Device example script to start it.
58
75
 
59
76
  ## Installation
60
77
 
@@ -6,8 +6,7 @@
6
6
  */
7
7
  import "@project-chip/matter-node.js";
8
8
  import { BleNode } from "@project-chip/matter-node-ble.js/ble";
9
- import { StorageBackendDisk } from "@project-chip/matter-node.js/storage";
10
- import { getIntParameter, getParameter, hasParameter, requireMinNodeVersion } from "@project-chip/matter-node.js/util";
9
+ import { requireMinNodeVersion } from "@project-chip/matter-node.js/util";
11
10
  import { CommissioningController } from "@project-chip/matter.js";
12
11
  import { Ble } from "@project-chip/matter.js/ble";
13
12
  import {
@@ -18,63 +17,35 @@ import {
18
17
  } from "@project-chip/matter.js/cluster";
19
18
  import { NodeId } from "@project-chip/matter.js/datatype";
20
19
  import { NodeStateInformation } from "@project-chip/matter.js/device";
21
- import { Environment } from "@project-chip/matter.js/environment";
22
- import { Format, Level, Logger } from "@project-chip/matter.js/log";
20
+ import { Environment, StorageService } from "@project-chip/matter.js/environment";
21
+ import { Logger } from "@project-chip/matter.js/log";
23
22
  import { ManualPairingCodeCodec } from "@project-chip/matter.js/schema";
24
- import { StorageManager } from "@project-chip/matter.js/storage";
25
23
  import { Time } from "@project-chip/matter.js/time";
26
24
  import { singleton } from "@project-chip/matter.js/util";
27
25
  const logger = Logger.get("Controller");
28
26
  requireMinNodeVersion(16);
29
- switch (getParameter("loglevel")) {
30
- case "fatal":
31
- Logger.defaultLogLevel = Level.FATAL;
32
- break;
33
- case "error":
34
- Logger.defaultLogLevel = Level.ERROR;
35
- break;
36
- case "warn":
37
- Logger.defaultLogLevel = Level.WARN;
38
- break;
39
- case "info":
40
- Logger.defaultLogLevel = Level.INFO;
41
- break;
42
- }
43
- switch (getParameter("logformat")) {
44
- case "plain":
45
- Logger.format = Format.PLAIN;
46
- break;
47
- case "html":
48
- Logger.format = Format.HTML;
49
- break;
50
- default:
51
- if (process.stdin?.isTTY)
52
- Logger.format = Format.ANSI;
53
- }
54
- if (hasParameter("ble")) {
27
+ const environment = Environment.default;
28
+ if (environment.vars.get("ble")) {
55
29
  Ble.get = singleton(
56
30
  () => new BleNode({
57
- hciId: getIntParameter("ble-hci-id")
31
+ hciId: environment.vars.number("ble-hci-id")
58
32
  })
59
33
  );
60
34
  }
61
- const storageLocation = getParameter("store") ?? ".controller-node";
62
- const storage = new StorageBackendDisk(storageLocation, hasParameter("clearstorage"));
63
- logger.info(`Storage location: ${storageLocation} (Directory)`);
35
+ const storageService = environment.get(StorageService);
36
+ console.log(`Storage location: ${storageService.location} (Directory)`);
64
37
  logger.info(
65
- 'Use the parameter "-store NAME" to specify a different storage location, use -clearstorage to start with an empty storage.'
38
+ '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.'
66
39
  );
67
40
  class ControllerNode {
68
41
  async start() {
69
42
  logger.info(`node-matter Controller started`);
70
- const storageManager = new StorageManager(storage);
71
- await storageManager.initialize();
72
- const controllerStorage = storageManager.createContext("Controller");
73
- const ip = controllerStorage.has("ip") ? controllerStorage.get("ip") : getParameter("ip");
74
- const port = controllerStorage.has("port") ? controllerStorage.get("port") : getIntParameter("port");
75
- const uniqueId = controllerStorage.has("uniqueid") ? controllerStorage.get("uniqueid") : getParameter("uniqueid") ?? Time.nowMs().toString();
43
+ const controllerStorage = (await storageService.open("controller")).createContext("data");
44
+ const ip = controllerStorage.has("ip") ? controllerStorage.get("ip") : environment.vars.string("ip");
45
+ const port = controllerStorage.has("port") ? controllerStorage.get("port") : environment.vars.number("port");
46
+ const uniqueId = controllerStorage.has("uniqueid") ? controllerStorage.get("uniqueid") : environment.vars.string("uniqueid") ?? Time.nowMs().toString();
76
47
  controllerStorage.set("uniqueid", uniqueId);
77
- const pairingCode = getParameter("pairingcode");
48
+ const pairingCode = environment.vars.string("pairingcode");
78
49
  let longDiscriminator, setupPin, shortDiscriminator;
79
50
  if (pairingCode !== void 0) {
80
51
  const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
@@ -83,10 +54,10 @@ class ControllerNode {
83
54
  setupPin = pairingCodeCodec.passcode;
84
55
  logger.debug(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
85
56
  } else {
86
- longDiscriminator = getIntParameter("longDiscriminator") ?? controllerStorage.get("longDiscriminator", 3840);
57
+ longDiscriminator = environment.vars.number("longDiscriminator") ?? controllerStorage.get("longDiscriminator", 3840);
87
58
  if (longDiscriminator > 4095)
88
59
  throw new Error("Discriminator value must be less than 4096");
89
- setupPin = getIntParameter("pin") ?? controllerStorage.get("pin", 20202021);
60
+ setupPin = environment.vars.number("pin") ?? controllerStorage.get("pin", 20202021);
90
61
  }
91
62
  if (shortDiscriminator === void 0 && longDiscriminator === void 0 || setupPin === void 0) {
92
63
  throw new Error(
@@ -98,12 +69,12 @@ class ControllerNode {
98
69
  regulatoryCountryCode: "XX"
99
70
  };
100
71
  let ble = false;
101
- if (hasParameter("ble")) {
72
+ if (environment.vars.get("ble")) {
102
73
  ble = true;
103
- const wifiSsid = getParameter("ble-wifi-ssid");
104
- const wifiCredentials = getParameter("ble-wifi-credentials");
105
- const threadNetworkName = getParameter("ble-thread-networkname");
106
- const threadOperationalDataset = getParameter("ble-thread-operationaldataset");
74
+ const wifiSsid = environment.vars.string("ble-wifi-ssid");
75
+ const wifiCredentials = environment.vars.string("ble-wifi-credentials");
76
+ const threadNetworkName = environment.vars.string("ble-thread-networkname");
77
+ const threadOperationalDataset = environment.vars.string("ble-thread-operationaldataset");
107
78
  if (wifiSsid !== void 0 && wifiCredentials !== void 0) {
108
79
  logger.info(`Registering Commissioning over BLE with WiFi: ${wifiSsid}`);
109
80
  commissioningOptions.wifiNetwork = {
@@ -119,7 +90,6 @@ class ControllerNode {
119
90
  };
120
91
  }
121
92
  }
122
- const environment = Environment.default;
123
93
  const commissioningController = new CommissioningController({
124
94
  environment: {
125
95
  environment,
@@ -147,7 +117,7 @@ class ControllerNode {
147
117
  try {
148
118
  const nodes = commissioningController.getCommissionedNodes();
149
119
  console.log("Found commissioned nodes:", Logger.toJSON(nodes));
150
- const nodeId = NodeId(getIntParameter("nodeid") ?? nodes[0]);
120
+ const nodeId = NodeId(environment.vars.number("nodeid") ?? nodes[0]);
151
121
  if (!nodes.includes(nodeId)) {
152
122
  throw new Error(`Node ${nodeId} not found in commissioned nodes`);
153
123
  }
@@ -225,7 +195,4 @@ class ControllerNode {
225
195
  }
226
196
  }
227
197
  new ControllerNode().start().catch((error) => logger.error(error));
228
- process.on("SIGINT", () => {
229
- storage.close().then(() => process.exit(0)).catch(() => process.exit(1));
230
- });
231
198
  //# sourceMappingURL=ControllerNode.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/examples/ControllerNode.ts"],
4
- "sourcesContent": ["#!/usr/bin/env node\n\n/**\n * @license\n * Copyright 2022-2024 Matter.js Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * This example shows how to create a Matter controller to pair with a device and interfact with it.\n * It can be used as CLI script, but is more thought as a starting point for your own controller implementation\n * because you need to adjust the code in any way depending on your use case.\n */\n\n/**\n * Import needed modules from @project-chip/matter-node.js\n */\n// Include this first to auto-register Crypto, Network and Time Node.js implementations\n// Include this first to auto-register Crypto, Network and Time Node.js implementations\nimport \"@project-chip/matter-node.js\";\n\nimport { BleNode } from \"@project-chip/matter-node-ble.js/ble\";\nimport { StorageBackendDisk } from \"@project-chip/matter-node.js/storage\";\nimport { getIntParameter, getParameter, hasParameter, requireMinNodeVersion } from \"@project-chip/matter-node.js/util\";\nimport { CommissioningController, NodeCommissioningOptions } from \"@project-chip/matter.js\";\nimport { Ble } from \"@project-chip/matter.js/ble\";\nimport {\n BasicInformationCluster,\n DescriptorCluster,\n GeneralCommissioning,\n OnOffCluster,\n} from \"@project-chip/matter.js/cluster\";\nimport { NodeId } from \"@project-chip/matter.js/datatype\";\nimport { NodeStateInformation } from \"@project-chip/matter.js/device\";\nimport { Environment } from \"@project-chip/matter.js/environment\";\nimport { Format, Level, Logger } from \"@project-chip/matter.js/log\";\nimport { CommissioningOptions } from \"@project-chip/matter.js/protocol\";\nimport { ManualPairingCodeCodec } from \"@project-chip/matter.js/schema\";\nimport { StorageManager } from \"@project-chip/matter.js/storage\";\nimport { Time } from \"@project-chip/matter.js/time\";\nimport { singleton } from \"@project-chip/matter.js/util\";\n\nconst logger = Logger.get(\"Controller\");\n\nrequireMinNodeVersion(16);\n\n/** Configure logging */\nswitch (getParameter(\"loglevel\")) {\n case \"fatal\":\n Logger.defaultLogLevel = Level.FATAL;\n break;\n case \"error\":\n Logger.defaultLogLevel = Level.ERROR;\n break;\n case \"warn\":\n Logger.defaultLogLevel = Level.WARN;\n break;\n case \"info\":\n Logger.defaultLogLevel = Level.INFO;\n break;\n}\n\nswitch (getParameter(\"logformat\")) {\n case \"plain\":\n Logger.format = Format.PLAIN;\n break;\n case \"html\":\n Logger.format = Format.HTML;\n break;\n default:\n if (process.stdin?.isTTY) Logger.format = Format.ANSI;\n}\n\nif (hasParameter(\"ble\")) {\n // Initialize Ble\n Ble.get = singleton(\n () =>\n new BleNode({\n hciId: getIntParameter(\"ble-hci-id\"),\n }),\n );\n}\n\nconst storageLocation = getParameter(\"store\") ?? \".controller-node\";\nconst storage = new StorageBackendDisk(storageLocation, hasParameter(\"clearstorage\"));\nlogger.info(`Storage location: ${storageLocation} (Directory)`);\nlogger.info(\n 'Use the parameter \"-store NAME\" to specify a different storage location, use -clearstorage to start with an empty storage.',\n);\n\nclass ControllerNode {\n async start() {\n logger.info(`node-matter Controller started`);\n\n /**\n * Initialize the storage system.\n *\n * The storage manager is then also used by the Matter server, so this code block in general is required,\n * but you can choose a different storage backend as long as it implements the required API.\n */\n\n const storageManager = new StorageManager(storage);\n await storageManager.initialize();\n\n /**\n * Collect all needed data\n *\n * This block makes sure to collect all needed data from cli or storage. Replace this with where ever your data\n * come from.\n *\n * Note: This example also uses the initialized storage system to store the device parameter data for convenience\n * and easy reuse. When you also do that be careful to not overlap with Matter-Server own contexts\n * (so maybe better not ;-)).\n */\n\n const controllerStorage = storageManager.createContext(\"Controller\");\n const ip = controllerStorage.has(\"ip\") ? controllerStorage.get<string>(\"ip\") : getParameter(\"ip\");\n const port = controllerStorage.has(\"port\") ? controllerStorage.get<number>(\"port\") : getIntParameter(\"port\");\n const uniqueId = controllerStorage.has(\"uniqueid\")\n ? controllerStorage.get<string>(\"uniqueid\")\n : getParameter(\"uniqueid\") ?? Time.nowMs().toString();\n controllerStorage.set(\"uniqueid\", uniqueId);\n\n const pairingCode = getParameter(\"pairingcode\");\n let longDiscriminator, setupPin, shortDiscriminator;\n if (pairingCode !== undefined) {\n const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);\n shortDiscriminator = pairingCodeCodec.shortDiscriminator;\n longDiscriminator = undefined;\n setupPin = pairingCodeCodec.passcode;\n logger.debug(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);\n } else {\n longDiscriminator =\n getIntParameter(\"longDiscriminator\") ?? controllerStorage.get(\"longDiscriminator\", 3840);\n if (longDiscriminator > 4095) throw new Error(\"Discriminator value must be less than 4096\");\n setupPin = getIntParameter(\"pin\") ?? controllerStorage.get(\"pin\", 20202021);\n }\n if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {\n throw new Error(\n \"Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode\",\n );\n }\n\n // Collect commissioning options from commandline parameters\n const commissioningOptions: CommissioningOptions = {\n regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,\n regulatoryCountryCode: \"XX\",\n };\n\n let ble = false;\n if (hasParameter(\"ble\")) {\n ble = true;\n const wifiSsid = getParameter(\"ble-wifi-ssid\");\n const wifiCredentials = getParameter(\"ble-wifi-credentials\");\n const threadNetworkName = getParameter(\"ble-thread-networkname\");\n const threadOperationalDataset = getParameter(\"ble-thread-operationaldataset\");\n if (wifiSsid !== undefined && wifiCredentials !== undefined) {\n logger.info(`Registering Commissioning over BLE with WiFi: ${wifiSsid}`);\n commissioningOptions.wifiNetwork = {\n wifiSsid: wifiSsid,\n wifiCredentials: wifiCredentials,\n };\n }\n if (threadNetworkName !== undefined && threadOperationalDataset !== undefined) {\n logger.info(`Registering Commissioning over BLE with Thread: ${threadNetworkName}`);\n commissioningOptions.threadNetwork = {\n networkName: threadNetworkName,\n operationalDataset: threadOperationalDataset,\n };\n }\n }\n\n /**\n * Create Matter Server and Controller Node\n *\n * To allow the device to be announced, found, paired and operated we need a MatterServer instance and add a\n * CommissioningController to it and add the just created device instance to it.\n * The Controller node defines the port where the server listens for the UDP packages of the Matter protocol\n * and initializes deice specific certificates and such.\n *\n * The below logic also adds command handlers for commands of clusters that normally are handled internally\n * like testEventTrigger (General Diagnostic Cluster) that can be implemented with the logic when these commands\n * are called.\n */\n\n const environment = Environment.default;\n const commissioningController = new CommissioningController({\n environment: {\n environment,\n id: uniqueId,\n },\n autoConnect: false,\n });\n\n /**\n * Start the Matter Server\n *\n * After everything was plugged together we can start the server. When not delayed announcement is set for the\n * CommissioningServer node then this command also starts the announcement of the device into the network.\n */\n await commissioningController.start();\n\n if (!commissioningController.isCommissioned()) {\n const options = {\n commissioning: commissioningOptions,\n discovery: {\n knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: \"udp\" } : undefined,\n identifierData:\n longDiscriminator !== undefined\n ? { longDiscriminator }\n : shortDiscriminator !== undefined\n ? { shortDiscriminator }\n : {},\n discoveryCapabilities: {\n ble,\n },\n },\n passcode: setupPin,\n } as NodeCommissioningOptions;\n logger.info(`Commissioning ... ${JSON.stringify(options)}`);\n const nodeId = await commissioningController.commissionNode(options);\n\n console.log(`Commissioning successfully done with nodeId ${nodeId}`);\n }\n\n /**\n * TBD\n */\n try {\n const nodes = commissioningController.getCommissionedNodes();\n console.log(\"Found commissioned nodes:\", Logger.toJSON(nodes));\n\n const nodeId = NodeId(getIntParameter(\"nodeid\") ?? nodes[0]);\n if (!nodes.includes(nodeId)) {\n throw new Error(`Node ${nodeId} not found in commissioned nodes`);\n }\n\n const node = await commissioningController.connectNode(nodeId, {\n attributeChangedCallback: (\n peerNodeId,\n { path: { nodeId, clusterId, endpointId, attributeName }, value },\n ) =>\n console.log(\n `attributeChangedCallback ${peerNodeId}: Attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(\n value,\n )}`,\n ),\n eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) =>\n console.log(\n `eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(\n events,\n )}`,\n ),\n stateInformationCallback: (peerNodeId, info) => {\n switch (info) {\n case NodeStateInformation.Connected:\n console.log(`stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);\n break;\n case NodeStateInformation.Disconnected:\n console.log(`stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);\n break;\n case NodeStateInformation.Reconnecting:\n console.log(`stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);\n break;\n case NodeStateInformation.WaitingForDeviceDiscovery:\n console.log(\n `stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`,\n );\n break;\n case NodeStateInformation.StructureChanged:\n console.log(`stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);\n break;\n case NodeStateInformation.Decommissioned:\n console.log(`stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);\n break;\n }\n },\n });\n\n // Important: This is a temporary API to proof the methods working and this will change soon and is NOT stable!\n // It is provided to proof the concept\n\n node.logStructure();\n\n // Example to initialize a ClusterClient and access concrete fields as API methods\n const descriptor = node.getRootClusterClient(DescriptorCluster);\n if (descriptor !== undefined) {\n console.log(await descriptor.attributes.deviceTypeList.get()); // you can call that way\n console.log(await descriptor.getServerListAttribute()); // or more convenient that way\n } else {\n console.log(\"No Descriptor Cluster found. This should never happen!\");\n }\n\n // Example to subscribe to a field and get the value\n const info = node.getRootClusterClient(BasicInformationCluster);\n if (info !== undefined) {\n console.log(await info.getProductNameAttribute()); // This call is executed remotely\n //console.log(await info.subscribeProductNameAttribute(value => console.log(\"productName\", value), 5, 30));\n //console.log(await info.getProductNameAttribute()); // This call is resolved locally because we have subscribed to the value!\n } else {\n console.log(\"No BasicInformation Cluster found. This should never happen!\");\n }\n\n // Example to get all Attributes of the commissioned node: */*/*\n //const attributesAll = await interactionClient.getAllAttributes();\n //console.log(\"Attributes-All:\", Logger.toJSON(attributesAll));\n\n // Example to get all Attributes of all Descriptor Clusters of the commissioned node: */DescriptorCluster/*\n //const attributesAllDescriptor = await interactionClient.getMultipleAttributes([{ clusterId: DescriptorCluster.id} ]);\n //console.log(\"Attributes-Descriptor:\", JSON.stringify(attributesAllDescriptor, null, 2));\n\n // Example to get all Attributes of the Basic Information Cluster of endpoint 0 of the commissioned node: 0/BasicInformationCluster/*\n //const attributesBasicInformation = await interactionClient.getMultipleAttributes([{ endpointId: 0, clusterId: BasicInformationCluster.id} ]);\n //console.log(\"Attributes-BasicInformation:\", JSON.stringify(attributesBasicInformation, null, 2));\n\n const devices = node.getDevices();\n if (devices[0] && devices[0].number === 1) {\n // Example to subscribe to all Attributes of endpoint 1 of the commissioned node: */*/*\n //await interactionClient.subscribeMultipleAttributes([{ endpointId: 1, /* subscribe anything from endpoint 1 */ }], 0, 180, data => {\n // console.log(\"Subscribe-All Data:\", Logger.toJSON(data));\n //});\n\n const onOff = devices[0].getClusterClient(OnOffCluster);\n if (onOff !== undefined) {\n let onOffStatus = await onOff.getOnOffAttribute();\n console.log(\"initial onOffStatus\", onOffStatus);\n\n onOff.addOnOffAttributeListener(value => {\n console.log(\"subscription onOffStatus\", value);\n onOffStatus = value;\n });\n // read data every minute to keep up the connection to show the subscription is working\n setInterval(() => {\n onOff\n .toggle()\n .then(() => {\n onOffStatus = !onOffStatus;\n console.log(\"onOffStatus\", onOffStatus);\n })\n .catch(error => logger.error(error));\n }, 60000);\n }\n }\n } finally {\n //await matterServer.close(); // Comment out when subscribes are used, else the connection will be closed\n setTimeout(() => process.exit(0), 1000000);\n }\n }\n}\n\nnew ControllerNode().start().catch(error => logger.error(error));\n\nprocess.on(\"SIGINT\", () => {\n // Clean up on CTRL-C\n // Pragmatic way to make sure the storage is correctly closed before the process ends.\n storage\n .close()\n .then(() => process.exit(0))\n .catch(() => process.exit(1));\n});\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["#!/usr/bin/env node\n\n/**\n * @license\n * Copyright 2022-2024 Matter.js Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * This example shows how to create a Matter controller to pair with a device and interfact with it.\n * It can be used as CLI script, but is more thought as a starting point for your own controller implementation\n * because you need to adjust the code in any way depending on your use case.\n */\n\n/**\n * Import needed modules from @project-chip/matter-node.js\n */\n// Include this first to auto-register Crypto, Network and Time Node.js implementations\n// Include this first to auto-register Crypto, Network and Time Node.js implementations\nimport \"@project-chip/matter-node.js\";\n\nimport { BleNode } from \"@project-chip/matter-node-ble.js/ble\";\nimport { requireMinNodeVersion } from \"@project-chip/matter-node.js/util\";\nimport { CommissioningController, NodeCommissioningOptions } from \"@project-chip/matter.js\";\nimport { Ble } from \"@project-chip/matter.js/ble\";\nimport {\n BasicInformationCluster,\n DescriptorCluster,\n GeneralCommissioning,\n OnOffCluster,\n} from \"@project-chip/matter.js/cluster\";\nimport { NodeId } from \"@project-chip/matter.js/datatype\";\nimport { NodeStateInformation } from \"@project-chip/matter.js/device\";\nimport { Environment, StorageService } from \"@project-chip/matter.js/environment\";\nimport { Logger } from \"@project-chip/matter.js/log\";\nimport { CommissioningOptions } from \"@project-chip/matter.js/protocol\";\nimport { ManualPairingCodeCodec } from \"@project-chip/matter.js/schema\";\nimport { Time } from \"@project-chip/matter.js/time\";\nimport { singleton } from \"@project-chip/matter.js/util\";\n\nconst logger = Logger.get(\"Controller\");\n\nrequireMinNodeVersion(16);\n\nconst environment = Environment.default;\n\nif (environment.vars.get(\"ble\")) {\n // Initialize Ble\n Ble.get = singleton(\n () =>\n new BleNode({\n hciId: environment.vars.number(\"ble-hci-id\"),\n }),\n );\n}\n\nconst storageService = environment.get(StorageService);\n\nconsole.log(`Storage location: ${storageService.location} (Directory)`);\nlogger.info(\n '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.',\n);\n\nclass ControllerNode {\n async start() {\n logger.info(`node-matter Controller started`);\n\n /**\n * Collect all needed data\n *\n * This block makes sure to collect all needed data from cli or storage. Replace this with where ever your data\n * come from.\n *\n * Note: This example also uses the initialized storage system to store the device parameter data for convenience\n * and easy reuse. When you also do that be careful to not overlap with Matter-Server own contexts\n * (so maybe better not ;-)).\n */\n\n const controllerStorage = (await storageService.open(\"controller\")).createContext(\"data\");\n const ip = controllerStorage.has(\"ip\") ? controllerStorage.get<string>(\"ip\") : environment.vars.string(\"ip\");\n const port = controllerStorage.has(\"port\")\n ? controllerStorage.get<number>(\"port\")\n : environment.vars.number(\"port\");\n const uniqueId = controllerStorage.has(\"uniqueid\")\n ? controllerStorage.get<string>(\"uniqueid\")\n : environment.vars.string(\"uniqueid\") ?? Time.nowMs().toString();\n controllerStorage.set(\"uniqueid\", uniqueId);\n\n const pairingCode = environment.vars.string(\"pairingcode\");\n let longDiscriminator, setupPin, shortDiscriminator;\n if (pairingCode !== undefined) {\n const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);\n shortDiscriminator = pairingCodeCodec.shortDiscriminator;\n longDiscriminator = undefined;\n setupPin = pairingCodeCodec.passcode;\n logger.debug(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);\n } else {\n longDiscriminator =\n environment.vars.number(\"longDiscriminator\") ?? controllerStorage.get(\"longDiscriminator\", 3840);\n if (longDiscriminator > 4095) throw new Error(\"Discriminator value must be less than 4096\");\n setupPin = environment.vars.number(\"pin\") ?? controllerStorage.get(\"pin\", 20202021);\n }\n if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {\n throw new Error(\n \"Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode\",\n );\n }\n\n // Collect commissioning options from commandline parameters\n const commissioningOptions: CommissioningOptions = {\n regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,\n regulatoryCountryCode: \"XX\",\n };\n\n let ble = false;\n if (environment.vars.get(\"ble\")) {\n ble = true;\n const wifiSsid = environment.vars.string(\"ble-wifi-ssid\");\n const wifiCredentials = environment.vars.string(\"ble-wifi-credentials\");\n const threadNetworkName = environment.vars.string(\"ble-thread-networkname\");\n const threadOperationalDataset = environment.vars.string(\"ble-thread-operationaldataset\");\n if (wifiSsid !== undefined && wifiCredentials !== undefined) {\n logger.info(`Registering Commissioning over BLE with WiFi: ${wifiSsid}`);\n commissioningOptions.wifiNetwork = {\n wifiSsid: wifiSsid,\n wifiCredentials: wifiCredentials,\n };\n }\n if (threadNetworkName !== undefined && threadOperationalDataset !== undefined) {\n logger.info(`Registering Commissioning over BLE with Thread: ${threadNetworkName}`);\n commissioningOptions.threadNetwork = {\n networkName: threadNetworkName,\n operationalDataset: threadOperationalDataset,\n };\n }\n }\n\n /**\n * Create Matter Server and Controller Node\n *\n * To allow the device to be announced, found, paired and operated we need a MatterServer instance and add a\n * CommissioningController to it and add the just created device instance to it.\n * The Controller node defines the port where the server listens for the UDP packages of the Matter protocol\n * and initializes deice specific certificates and such.\n *\n * The below logic also adds command handlers for commands of clusters that normally are handled internally\n * like testEventTrigger (General Diagnostic Cluster) that can be implemented with the logic when these commands\n * are called.\n */\n\n const commissioningController = new CommissioningController({\n environment: {\n environment,\n id: uniqueId,\n },\n autoConnect: false,\n });\n\n /**\n * Start the Matter Server\n *\n * After everything was plugged together we can start the server. When not delayed announcement is set for the\n * CommissioningServer node then this command also starts the announcement of the device into the network.\n */\n await commissioningController.start();\n\n if (!commissioningController.isCommissioned()) {\n const options = {\n commissioning: commissioningOptions,\n discovery: {\n knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: \"udp\" } : undefined,\n identifierData:\n longDiscriminator !== undefined\n ? { longDiscriminator }\n : shortDiscriminator !== undefined\n ? { shortDiscriminator }\n : {},\n discoveryCapabilities: {\n ble,\n },\n },\n passcode: setupPin,\n } as NodeCommissioningOptions;\n logger.info(`Commissioning ... ${JSON.stringify(options)}`);\n const nodeId = await commissioningController.commissionNode(options);\n\n console.log(`Commissioning successfully done with nodeId ${nodeId}`);\n }\n\n /**\n * TBD\n */\n try {\n const nodes = commissioningController.getCommissionedNodes();\n console.log(\"Found commissioned nodes:\", Logger.toJSON(nodes));\n\n const nodeId = NodeId(environment.vars.number(\"nodeid\") ?? nodes[0]);\n if (!nodes.includes(nodeId)) {\n throw new Error(`Node ${nodeId} not found in commissioned nodes`);\n }\n\n const node = await commissioningController.connectNode(nodeId, {\n attributeChangedCallback: (\n peerNodeId,\n { path: { nodeId, clusterId, endpointId, attributeName }, value },\n ) =>\n console.log(\n `attributeChangedCallback ${peerNodeId}: Attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(\n value,\n )}`,\n ),\n eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) =>\n console.log(\n `eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(\n events,\n )}`,\n ),\n stateInformationCallback: (peerNodeId, info) => {\n switch (info) {\n case NodeStateInformation.Connected:\n console.log(`stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);\n break;\n case NodeStateInformation.Disconnected:\n console.log(`stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);\n break;\n case NodeStateInformation.Reconnecting:\n console.log(`stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);\n break;\n case NodeStateInformation.WaitingForDeviceDiscovery:\n console.log(\n `stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`,\n );\n break;\n case NodeStateInformation.StructureChanged:\n console.log(`stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);\n break;\n case NodeStateInformation.Decommissioned:\n console.log(`stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);\n break;\n }\n },\n });\n\n // Important: This is a temporary API to proof the methods working and this will change soon and is NOT stable!\n // It is provided to proof the concept\n\n node.logStructure();\n\n // Example to initialize a ClusterClient and access concrete fields as API methods\n const descriptor = node.getRootClusterClient(DescriptorCluster);\n if (descriptor !== undefined) {\n console.log(await descriptor.attributes.deviceTypeList.get()); // you can call that way\n console.log(await descriptor.getServerListAttribute()); // or more convenient that way\n } else {\n console.log(\"No Descriptor Cluster found. This should never happen!\");\n }\n\n // Example to subscribe to a field and get the value\n const info = node.getRootClusterClient(BasicInformationCluster);\n if (info !== undefined) {\n console.log(await info.getProductNameAttribute()); // This call is executed remotely\n //console.log(await info.subscribeProductNameAttribute(value => console.log(\"productName\", value), 5, 30));\n //console.log(await info.getProductNameAttribute()); // This call is resolved locally because we have subscribed to the value!\n } else {\n console.log(\"No BasicInformation Cluster found. This should never happen!\");\n }\n\n // Example to get all Attributes of the commissioned node: */*/*\n //const attributesAll = await interactionClient.getAllAttributes();\n //console.log(\"Attributes-All:\", Logger.toJSON(attributesAll));\n\n // Example to get all Attributes of all Descriptor Clusters of the commissioned node: */DescriptorCluster/*\n //const attributesAllDescriptor = await interactionClient.getMultipleAttributes([{ clusterId: DescriptorCluster.id} ]);\n //console.log(\"Attributes-Descriptor:\", JSON.stringify(attributesAllDescriptor, null, 2));\n\n // Example to get all Attributes of the Basic Information Cluster of endpoint 0 of the commissioned node: 0/BasicInformationCluster/*\n //const attributesBasicInformation = await interactionClient.getMultipleAttributes([{ endpointId: 0, clusterId: BasicInformationCluster.id} ]);\n //console.log(\"Attributes-BasicInformation:\", JSON.stringify(attributesBasicInformation, null, 2));\n\n const devices = node.getDevices();\n if (devices[0] && devices[0].number === 1) {\n // Example to subscribe to all Attributes of endpoint 1 of the commissioned node: */*/*\n //await interactionClient.subscribeMultipleAttributes([{ endpointId: 1, /* subscribe anything from endpoint 1 */ }], 0, 180, data => {\n // console.log(\"Subscribe-All Data:\", Logger.toJSON(data));\n //});\n\n const onOff = devices[0].getClusterClient(OnOffCluster);\n if (onOff !== undefined) {\n let onOffStatus = await onOff.getOnOffAttribute();\n console.log(\"initial onOffStatus\", onOffStatus);\n\n onOff.addOnOffAttributeListener(value => {\n console.log(\"subscription onOffStatus\", value);\n onOffStatus = value;\n });\n // read data every minute to keep up the connection to show the subscription is working\n setInterval(() => {\n onOff\n .toggle()\n .then(() => {\n onOffStatus = !onOffStatus;\n console.log(\"onOffStatus\", onOffStatus);\n })\n .catch(error => logger.error(error));\n }, 60000);\n }\n }\n } finally {\n //await matterServer.close(); // Comment out when subscribes are used, else the connection will be closed\n setTimeout(() => process.exit(0), 1000000);\n }\n }\n}\n\nnew ControllerNode().start().catch(error => logger.error(error));\n"],
5
+ "mappings": "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",
6
6
  "names": ["nodeId", "info"]
7
7
  }
@@ -107,7 +107,7 @@ async function getConfiguration() {
107
107
  const productName = `node-matter OnOff ${isSocket ? "Socket" : "Light"}`;
108
108
  const productId = environment.vars.number("productid") ?? deviceStorage.get("productid", 32768);
109
109
  const port = environment.vars.number("port") ?? 5540;
110
- const uniqueId = environment.vars.string("uniqueid") ?? deviceStorage.get("uniqueid", Time.nowMs().toString());
110
+ const uniqueId = environment.vars.string("uniqueid") ?? deviceStorage.get("uniqueid", Time.nowMs()).toString();
111
111
  deviceStorage.set("passcode", passcode);
112
112
  deviceStorage.set("discriminator", discriminator);
113
113
  deviceStorage.set("vendorid", vendorId);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/examples/DeviceNode.ts"],
4
- "sourcesContent": ["/**\n * @license\n * Copyright 2022-2024 Matter.js Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * This example shows how to create a simple on-off Matter device as a light or as a socket.\n * It can be used as CLI script and starting point for your own device node implementation.\n * This example is CJS conform and do not use top level await's.\n */\n\n/**\n * Import needed modules from @project-chip/matter-node.js\n */\n// Include this first to auto-register Crypto, Network and Time Node.js implementations\nimport \"@project-chip/matter-node.js\";\n\nimport { requireMinNodeVersion } from \"@project-chip/matter-node.js/util\";\nimport { DeviceTypeId, VendorId } from \"@project-chip/matter.js/datatype\";\nimport { logEndpoint } from \"@project-chip/matter.js/device\";\nimport { OnOffLightDevice } from \"@project-chip/matter.js/devices/OnOffLightDevice\";\nimport { OnOffPlugInUnitDevice } from \"@project-chip/matter.js/devices/OnOffPlugInUnitDevice\";\nimport { Endpoint, EndpointServer } from \"@project-chip/matter.js/endpoint\";\nimport { Environment, StorageService } from \"@project-chip/matter.js/environment\";\nimport { ServerNode } from \"@project-chip/matter.js/node\";\nimport { Time } from \"@project-chip/matter.js/time\";\nimport { execSync } from \"child_process\";\n\nrequireMinNodeVersion(16);\n\nasync function main() {\n /** Initialize configuration values */\n const {\n isSocket,\n deviceName,\n vendorName,\n passcode,\n discriminator,\n vendorId,\n productName,\n productId,\n port,\n uniqueId,\n } = await getConfiguration();\n\n /**\n * Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration\n */\n const server = await ServerNode.create({\n // Required: Give the Node a unique ID which is used to store the state of this node\n id: uniqueId,\n\n // Provide Network relevant configuration like the port\n // Optional when operating only one device on a host, Default port is 5540\n network: {\n port,\n },\n\n // Provide Commissioning relevant settings\n // Optional for development/testing purposes\n commissioning: {\n passcode,\n discriminator,\n },\n\n // Provide Node announcement settings\n // Optional: If Ommitted some development defaults are used\n productDescription: {\n name: deviceName,\n deviceType: DeviceTypeId(isSocket ? OnOffPlugInUnitDevice.deviceType : OnOffLightDevice.deviceType),\n },\n\n // Provide defaults for the BasicInformation cluster on the Root endpoint\n // Optional: If Omitted some development defaults are used\n basicInformation: {\n vendorName,\n vendorId: VendorId(vendorId),\n nodeLabel: productName,\n productName,\n productLabel: productName,\n productId,\n serialNumber: `matterjs-${uniqueId}`,\n uniqueId,\n },\n });\n\n /**\n * Matter Nodes are a composition of endpoints. Create and add a single endpoint to the node. This example uses the\n * OnOffLightDevice or OnOffPlugInUnitDevice depending on the value of the type parameter. It also assigns this Part a\n * unique ID to store the endpoint number for it in the storage to restore the device on restart.\n * In this case we directly use the default command implementation from matter.js. Check out the DeviceNodeFull example\n * to see how to customize the command handlers.\n */\n const endpoint = new Endpoint(isSocket ? OnOffPlugInUnitDevice : OnOffLightDevice, { id: \"onoff\" });\n await server.add(endpoint);\n\n /**\n * Register state change handlers of the node for identify and onoff states to react to the commands.\n * If the code in these change handlers fail then the change is also rolled back and not executed and an error is\n * reported back to the controller.\n */\n let isIdentifying = false;\n endpoint.events.identify.identifyTime$Change.on(value => {\n // identifyTime is set when an identify command is called and then decreased every second while indentify logic runs.\n if (value > 0 && !isIdentifying) {\n isIdentifying = true;\n console.log(`Run identify logic, ideally blink a light every 0.5s ...`);\n } else if (value === 0) {\n isIdentifying = false;\n console.log(`Stop identify logic ...`);\n }\n });\n\n endpoint.events.onOff.onOff$Change.on(value => {\n executeCommand(value ? \"on\" : \"off\");\n console.log(`OnOff is now ${value ? \"ON\" : \"OFF\"}`);\n });\n\n /**\n * Log the endpoint structure for debugging reasons and to allow to verify anything is correct\n */\n logEndpoint(EndpointServer.forEndpoint(server));\n\n /**\n * In order to start the node and announce it into the network we use the run method which resolves when the node goes\n * offline again because we do not need anything more here. See the Full example for other starting options.\n * The QR Code is printed automatically.\n */\n await server.run();\n}\n\nmain().catch(error => console.error(error));\n\n/*********************************************************************************************************\n * Convenience Methods\n *********************************************************************************************************/\n\n/** Defined a shell command from an environment variable and execute it and log the response. */\nfunction executeCommand(scriptParamName: string) {\n const script = Environment.default.vars.string(scriptParamName);\n if (script === undefined) return undefined;\n console.log(`${scriptParamName}: ${execSync(script).toString().slice(0, -1)}`);\n}\n\nasync function getConfiguration() {\n /**\n * Collect all needed data\n *\n * This block collects all needed data from cli, environment or storage. Replace this with where ever your data come from.\n *\n * Note: This example uses the matter.js process storage system to store the device parameter data for convenience\n * and easy reuse. When you also do that be careful to not overlap with Matter-Server own storage contexts\n * (so maybe better not do it ;-)).\n */\n const environment = Environment.default;\n\n const storageService = environment.get(StorageService);\n console.log(`Storage location: ${storageService.location} (Directory)`);\n console.log(\n '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.',\n );\n const deviceStorage = (await storageService.open(\"device\")).createContext(\"data\");\n\n const isSocket = deviceStorage.get(\"isSocket\", environment.vars.get(\"type\") === \"socket\");\n if (deviceStorage.has(\"isSocket\")) {\n console.log(`Device type ${isSocket ? \"socket\" : \"light\"} found in storage. --type parameter is ignored.`);\n }\n const deviceName = \"Matter test device\";\n const vendorName = \"matter-node.js\";\n const passcode = environment.vars.number(\"passcode\") ?? deviceStorage.get(\"passcode\", 20202021);\n const discriminator = environment.vars.number(\"discriminator\") ?? deviceStorage.get(\"discriminator\", 3840);\n // product name / id and vendor id should match what is in the device certificate\n const vendorId = environment.vars.number(\"vendorid\") ?? deviceStorage.get(\"vendorid\", 0xfff1);\n const productName = `node-matter OnOff ${isSocket ? \"Socket\" : \"Light\"}`;\n const productId = environment.vars.number(\"productid\") ?? deviceStorage.get(\"productid\", 0x8000);\n\n const port = environment.vars.number(\"port\") ?? 5540;\n\n const uniqueId = environment.vars.string(\"uniqueid\") ?? deviceStorage.get(\"uniqueid\", Time.nowMs().toString());\n\n // Persist basic data to keep them also on restart\n deviceStorage.set(\"passcode\", passcode);\n deviceStorage.set(\"discriminator\", discriminator);\n deviceStorage.set(\"vendorid\", vendorId);\n deviceStorage.set(\"productid\", productId);\n deviceStorage.set(\"isSocket\", isSocket);\n deviceStorage.set(\"uniqueid\", uniqueId);\n\n return {\n isSocket,\n deviceName,\n vendorName,\n passcode,\n discriminator,\n vendorId,\n productName,\n productId,\n port,\n uniqueId,\n };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * @license\n * Copyright 2022-2024 Matter.js Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * This example shows how to create a simple on-off Matter device as a light or as a socket.\n * It can be used as CLI script and starting point for your own device node implementation.\n * This example is CJS conform and do not use top level await's.\n */\n\n/**\n * Import needed modules from @project-chip/matter-node.js\n */\n// Include this first to auto-register Crypto, Network and Time Node.js implementations\nimport \"@project-chip/matter-node.js\";\n\nimport { requireMinNodeVersion } from \"@project-chip/matter-node.js/util\";\nimport { DeviceTypeId, VendorId } from \"@project-chip/matter.js/datatype\";\nimport { logEndpoint } from \"@project-chip/matter.js/device\";\nimport { OnOffLightDevice } from \"@project-chip/matter.js/devices/OnOffLightDevice\";\nimport { OnOffPlugInUnitDevice } from \"@project-chip/matter.js/devices/OnOffPlugInUnitDevice\";\nimport { Endpoint, EndpointServer } from \"@project-chip/matter.js/endpoint\";\nimport { Environment, StorageService } from \"@project-chip/matter.js/environment\";\nimport { ServerNode } from \"@project-chip/matter.js/node\";\nimport { Time } from \"@project-chip/matter.js/time\";\nimport { execSync } from \"child_process\";\n\nrequireMinNodeVersion(16);\n\nasync function main() {\n /** Initialize configuration values */\n const {\n isSocket,\n deviceName,\n vendorName,\n passcode,\n discriminator,\n vendorId,\n productName,\n productId,\n port,\n uniqueId,\n } = await getConfiguration();\n\n /**\n * Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration\n */\n const server = await ServerNode.create({\n // Required: Give the Node a unique ID which is used to store the state of this node\n id: uniqueId,\n\n // Provide Network relevant configuration like the port\n // Optional when operating only one device on a host, Default port is 5540\n network: {\n port,\n },\n\n // Provide Commissioning relevant settings\n // Optional for development/testing purposes\n commissioning: {\n passcode,\n discriminator,\n },\n\n // Provide Node announcement settings\n // Optional: If Ommitted some development defaults are used\n productDescription: {\n name: deviceName,\n deviceType: DeviceTypeId(isSocket ? OnOffPlugInUnitDevice.deviceType : OnOffLightDevice.deviceType),\n },\n\n // Provide defaults for the BasicInformation cluster on the Root endpoint\n // Optional: If Omitted some development defaults are used\n basicInformation: {\n vendorName,\n vendorId: VendorId(vendorId),\n nodeLabel: productName,\n productName,\n productLabel: productName,\n productId,\n serialNumber: `matterjs-${uniqueId}`,\n uniqueId,\n },\n });\n\n /**\n * Matter Nodes are a composition of endpoints. Create and add a single endpoint to the node. This example uses the\n * OnOffLightDevice or OnOffPlugInUnitDevice depending on the value of the type parameter. It also assigns this Part a\n * unique ID to store the endpoint number for it in the storage to restore the device on restart.\n * In this case we directly use the default command implementation from matter.js. Check out the DeviceNodeFull example\n * to see how to customize the command handlers.\n */\n const endpoint = new Endpoint(isSocket ? OnOffPlugInUnitDevice : OnOffLightDevice, { id: \"onoff\" });\n await server.add(endpoint);\n\n /**\n * Register state change handlers of the node for identify and onoff states to react to the commands.\n * If the code in these change handlers fail then the change is also rolled back and not executed and an error is\n * reported back to the controller.\n */\n let isIdentifying = false;\n endpoint.events.identify.identifyTime$Change.on(value => {\n // identifyTime is set when an identify command is called and then decreased every second while indentify logic runs.\n if (value > 0 && !isIdentifying) {\n isIdentifying = true;\n console.log(`Run identify logic, ideally blink a light every 0.5s ...`);\n } else if (value === 0) {\n isIdentifying = false;\n console.log(`Stop identify logic ...`);\n }\n });\n\n endpoint.events.onOff.onOff$Change.on(value => {\n executeCommand(value ? \"on\" : \"off\");\n console.log(`OnOff is now ${value ? \"ON\" : \"OFF\"}`);\n });\n\n /**\n * Log the endpoint structure for debugging reasons and to allow to verify anything is correct\n */\n logEndpoint(EndpointServer.forEndpoint(server));\n\n /**\n * In order to start the node and announce it into the network we use the run method which resolves when the node goes\n * offline again because we do not need anything more here. See the Full example for other starting options.\n * The QR Code is printed automatically.\n */\n await server.run();\n}\n\nmain().catch(error => console.error(error));\n\n/*********************************************************************************************************\n * Convenience Methods\n *********************************************************************************************************/\n\n/** Defined a shell command from an environment variable and execute it and log the response. */\nfunction executeCommand(scriptParamName: string) {\n const script = Environment.default.vars.string(scriptParamName);\n if (script === undefined) return undefined;\n console.log(`${scriptParamName}: ${execSync(script).toString().slice(0, -1)}`);\n}\n\nasync function getConfiguration() {\n /**\n * Collect all needed data\n *\n * This block collects all needed data from cli, environment or storage. Replace this with where ever your data come from.\n *\n * Note: This example uses the matter.js process storage system to store the device parameter data for convenience\n * and easy reuse. When you also do that be careful to not overlap with Matter-Server own storage contexts\n * (so maybe better not do it ;-)).\n */\n const environment = Environment.default;\n\n const storageService = environment.get(StorageService);\n console.log(`Storage location: ${storageService.location} (Directory)`);\n console.log(\n '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.',\n );\n const deviceStorage = (await storageService.open(\"device\")).createContext(\"data\");\n\n const isSocket = deviceStorage.get(\"isSocket\", environment.vars.get(\"type\") === \"socket\");\n if (deviceStorage.has(\"isSocket\")) {\n console.log(`Device type ${isSocket ? \"socket\" : \"light\"} found in storage. --type parameter is ignored.`);\n }\n const deviceName = \"Matter test device\";\n const vendorName = \"matter-node.js\";\n const passcode = environment.vars.number(\"passcode\") ?? deviceStorage.get(\"passcode\", 20202021);\n const discriminator = environment.vars.number(\"discriminator\") ?? deviceStorage.get(\"discriminator\", 3840);\n // product name / id and vendor id should match what is in the device certificate\n const vendorId = environment.vars.number(\"vendorid\") ?? deviceStorage.get(\"vendorid\", 0xfff1);\n const productName = `node-matter OnOff ${isSocket ? \"Socket\" : \"Light\"}`;\n const productId = environment.vars.number(\"productid\") ?? deviceStorage.get(\"productid\", 0x8000);\n\n const port = environment.vars.number(\"port\") ?? 5540;\n\n const uniqueId = environment.vars.string(\"uniqueid\") ?? deviceStorage.get(\"uniqueid\", Time.nowMs()).toString();\n\n // Persist basic data to keep them also on restart\n deviceStorage.set(\"passcode\", passcode);\n deviceStorage.set(\"discriminator\", discriminator);\n deviceStorage.set(\"vendorid\", vendorId);\n deviceStorage.set(\"productid\", productId);\n deviceStorage.set(\"isSocket\", isSocket);\n deviceStorage.set(\"uniqueid\", uniqueId);\n\n return {\n isSocket,\n deviceName,\n vendorName,\n passcode,\n discriminator,\n vendorId,\n productName,\n productId,\n port,\n uniqueId,\n };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,114 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2024 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import "@project-chip/matter-node.js";
7
+ import { StorageBackendDisk } from "@project-chip/matter-node.js/storage";
8
+ import { Environment, StorageService } from "@project-chip/matter.js/environment";
9
+ import { Time } from "@project-chip/matter.js/time";
10
+ import { LocalStorage } from "node-localstorage";
11
+ const environment = Environment.default;
12
+ const legacyStoragePath = environment.vars.string("legacy.storage.path");
13
+ const newStoragePath = environment.vars.string("storage.path");
14
+ if (!legacyStoragePath || !newStoragePath) {
15
+ console.error("Usage: node LegacyStorageConverter.js --legacy-storage-path=<path> --storage-path=<path>");
16
+ process.exit(1);
17
+ }
18
+ const legacyLocalStorage = new LocalStorage(legacyStoragePath);
19
+ const legacyNodes = new Array();
20
+ Object.keys(legacyLocalStorage).forEach((key) => {
21
+ const firstLevel = key.split(".")[0];
22
+ if (!legacyNodes.includes(firstLevel)) {
23
+ legacyNodes.push(firstLevel);
24
+ }
25
+ });
26
+ const storageService = environment.get(StorageService);
27
+ const legacyStorage = new StorageBackendDisk(legacyStoragePath);
28
+ await legacyStorage.initialize();
29
+ const uniqueIds = {};
30
+ if (legacyNodes.includes("Device")) {
31
+ console.log("Example Device found ...");
32
+ legacyNodes.splice(legacyNodes.indexOf("Device"), 1);
33
+ const newDeviceStorage = (await storageService.open("device")).createContext("data");
34
+ legacyStorage.keys(["Device"]).forEach((key) => {
35
+ console.log("Migrate Device.", key);
36
+ const value = legacyStorage.get(["Device"], key);
37
+ newDeviceStorage.set(key, value);
38
+ if (key === "uniqueid") {
39
+ uniqueIds["0"] = String(value);
40
+ newDeviceStorage.set(key, String(value));
41
+ } else if (key.startsWith("uniqueid")) {
42
+ const id = parseInt(key.substring(8));
43
+ uniqueIds[id - 1] = String(value);
44
+ newDeviceStorage.set(key, String(value));
45
+ }
46
+ });
47
+ }
48
+ if (legacyNodes.includes("Controller")) {
49
+ console.log("Example Controller found ...");
50
+ legacyNodes.splice(legacyNodes.indexOf("Controller"), 1);
51
+ const newControllerStorage = (await storageService.open("controller")).createContext("data");
52
+ legacyStorage.keys(["Controller"]).forEach((key) => {
53
+ console.log("Migrate Controller.", key);
54
+ const value = legacyStorage.get(["Controller"], key);
55
+ newControllerStorage.set(key, value);
56
+ if (key === "uniqueid") {
57
+ uniqueIds["0"] = String(value);
58
+ newControllerStorage.set(key, String(value));
59
+ }
60
+ });
61
+ }
62
+ console.log(uniqueIds);
63
+ if (!Object.keys(uniqueIds).length) {
64
+ console.error("No uniqueId(s) found in legacy storage. Can not convert the node storage.");
65
+ process.exit(1);
66
+ }
67
+ for (const nodeId of legacyNodes) {
68
+ if (!uniqueIds[nodeId]) {
69
+ const rootCertId = legacyStorage.get(["0", "RootCertificateManager"], "rootCertId");
70
+ if (nodeId !== "0" || rootCertId === void 0) {
71
+ console.error(`No uniqueId found for node ${nodeId}. Can not convert the node storage.`);
72
+ continue;
73
+ }
74
+ const newControllerStorage = (await storageService.open("controller")).createContext("data");
75
+ const uniqueId = Time.nowMs().toString();
76
+ newControllerStorage.set("uniqueid", uniqueId);
77
+ const newNodeStorage = await storageService.open(uniqueId);
78
+ const credentialsStorage = newNodeStorage.createContext("credentials");
79
+ credentialsStorage.set("rootCertId", rootCertId);
80
+ credentialsStorage.set(
81
+ "nextCertificateId",
82
+ legacyStorage.get(["0", "RootCertificateManager"], "nextCertificateId")
83
+ );
84
+ credentialsStorage.set("rootCertBytes", legacyStorage.get(["0", "RootCertificateManager"], "rootCertBytes"));
85
+ credentialsStorage.set(
86
+ "rootKeyIdentifier",
87
+ legacyStorage.get(["0", "RootCertificateManager"], "rootKeyIdentifier")
88
+ );
89
+ credentialsStorage.set("rootKeyPair", legacyStorage.get(["0", "RootCertificateManager"], "rootKeyPair"));
90
+ credentialsStorage.set("fabric", legacyStorage.get(["0", "MatterController"], "fabric"));
91
+ const sessionsStorage = newNodeStorage.createContext("sessions");
92
+ sessionsStorage.set("resumptionRecords", legacyStorage.get([nodeId, "SessionManager"], "resumptionRecords"));
93
+ const nodesStorage = newNodeStorage.createContext("nodes");
94
+ nodesStorage.set("resumptionRecords", legacyStorage.get([nodeId, "MatterController"], "commissionedNodes"));
95
+ console.log(`Controller Node ${nodeId} with new unique id ${uniqueId} migrated successfully.`);
96
+ } else {
97
+ const newNodeStorage = await storageService.open(uniqueIds[nodeId]);
98
+ const nextEndpointNumber = legacyStorage.get([nodeId, "EndpointStructure"], "nextEndpointId");
99
+ if (nextEndpointNumber !== void 0 && nextEndpointNumber > 2) {
100
+ console.log(
101
+ "It seems you used a bridged or composed example before, please make sure to use all details (type, ids,...) in the parameters when starting the new example. When using a bridge and you had changed devices after pairing it could happen that the new example introduces new devices to the controller. If you are unsure unpair the old example and start fresh."
102
+ );
103
+ }
104
+ const eventsStorage = newNodeStorage.createContext("events");
105
+ eventsStorage.set("lastEventNumber", legacyStorage.get([nodeId, "EventHandler"], "lastEventNumber"));
106
+ const fabricsStorage = newNodeStorage.createContext("fabrics");
107
+ fabricsStorage.set("fabrics", legacyStorage.get([nodeId, "FabricManager"], "fabrics"));
108
+ fabricsStorage.set("nextFabricIndex", legacyStorage.get([nodeId, "FabricManager"], "nextFabricIndex"));
109
+ const sessionsStorage = newNodeStorage.createContext("sessions");
110
+ sessionsStorage.set("resumptionRecords", legacyStorage.get([nodeId, "SessionManager"], "resumptionRecords"));
111
+ console.log(`Device Node ${nodeId} with unique id ${uniqueIds[nodeId]} migrated successfully.`);
112
+ }
113
+ }
114
+ //# sourceMappingURL=LegacyStorageConverter.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/examples/LegacyStorageConverter.ts"],
4
+ "sourcesContent": ["/**\n * @license\n * Copyright 2022-2024 Matter.js Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport \"@project-chip/matter-node.js\";\nimport { StorageBackendDisk } from \"@project-chip/matter-node.js/storage\";\nimport { Environment, StorageService } from \"@project-chip/matter.js/environment\";\nimport { Time } from \"@project-chip/matter.js/time\";\nimport { LocalStorage } from \"node-localstorage\";\n\nconst environment = Environment.default;\n\nconst legacyStoragePath = environment.vars.string(\"legacy.storage.path\");\nconst newStoragePath = environment.vars.string(\"storage.path\");\n\nif (!legacyStoragePath || !newStoragePath) {\n console.error(\"Usage: node LegacyStorageConverter.js --legacy-storage-path=<path> --storage-path=<path>\");\n process.exit(1);\n}\n\nconst legacyLocalStorage = new LocalStorage(legacyStoragePath);\n\nconst legacyNodes = new Array<string>();\nObject.keys(legacyLocalStorage).forEach(key => {\n const firstLevel = key.split(\".\")[0];\n if (!legacyNodes.includes(firstLevel)) {\n legacyNodes.push(firstLevel);\n }\n});\n\nconst storageService = environment.get(StorageService);\n\nconst legacyStorage = new StorageBackendDisk(legacyStoragePath);\nawait legacyStorage.initialize();\n\nconst uniqueIds: Record<string, string> = {};\nif (legacyNodes.includes(\"Device\")) {\n console.log(\"Example Device found ...\");\n legacyNodes.splice(legacyNodes.indexOf(\"Device\"), 1);\n\n const newDeviceStorage = (await storageService.open(\"device\")).createContext(\"data\");\n\n legacyStorage.keys([\"Device\"]).forEach(key => {\n console.log(\"Migrate Device.\", key);\n const value = legacyStorage.get([\"Device\"], key);\n newDeviceStorage.set(key, value);\n if (key === \"uniqueid\") {\n uniqueIds[\"0\"] = String(value);\n newDeviceStorage.set(key, String(value));\n } else if (key.startsWith(\"uniqueid\")) {\n const id = parseInt(key.substring(8));\n uniqueIds[id - 1] = String(value);\n newDeviceStorage.set(key, String(value));\n }\n });\n}\n\nif (legacyNodes.includes(\"Controller\")) {\n console.log(\"Example Controller found ...\");\n legacyNodes.splice(legacyNodes.indexOf(\"Controller\"), 1);\n\n const newControllerStorage = (await storageService.open(\"controller\")).createContext(\"data\");\n\n legacyStorage.keys([\"Controller\"]).forEach(key => {\n console.log(\"Migrate Controller.\", key);\n const value = legacyStorage.get([\"Controller\"], key);\n newControllerStorage.set(key, value);\n if (key === \"uniqueid\") {\n uniqueIds[\"0\"] = String(value);\n newControllerStorage.set(key, String(value));\n }\n });\n}\n\nconsole.log(uniqueIds);\n\nif (!Object.keys(uniqueIds).length) {\n console.error(\"No uniqueId(s) found in legacy storage. Can not convert the node storage.\");\n process.exit(1);\n}\n\nfor (const nodeId of legacyNodes) {\n if (!uniqueIds[nodeId]) {\n const rootCertId = legacyStorage.get([\"0\", \"RootCertificateManager\"], \"rootCertId\");\n\n if (nodeId !== \"0\" || rootCertId === undefined) {\n console.error(`No uniqueId found for node ${nodeId}. Can not convert the node storage.`);\n continue;\n }\n\n // Migrate the controller storage\n const newControllerStorage = (await storageService.open(\"controller\")).createContext(\"data\");\n const uniqueId = Time.nowMs().toString();\n newControllerStorage.set(\"uniqueid\", uniqueId);\n\n const newNodeStorage = await storageService.open(uniqueId);\n\n const credentialsStorage = newNodeStorage.createContext(\"credentials\");\n credentialsStorage.set(\"rootCertId\", rootCertId);\n credentialsStorage.set(\n \"nextCertificateId\",\n legacyStorage.get([\"0\", \"RootCertificateManager\"], \"nextCertificateId\"),\n );\n credentialsStorage.set(\"rootCertBytes\", legacyStorage.get([\"0\", \"RootCertificateManager\"], \"rootCertBytes\"));\n credentialsStorage.set(\n \"rootKeyIdentifier\",\n legacyStorage.get([\"0\", \"RootCertificateManager\"], \"rootKeyIdentifier\"),\n );\n credentialsStorage.set(\"rootKeyPair\", legacyStorage.get([\"0\", \"RootCertificateManager\"], \"rootKeyPair\"));\n credentialsStorage.set(\"fabric\", legacyStorage.get([\"0\", \"MatterController\"], \"fabric\"));\n\n const sessionsStorage = newNodeStorage.createContext(\"sessions\");\n sessionsStorage.set(\"resumptionRecords\", legacyStorage.get([nodeId, \"SessionManager\"], \"resumptionRecords\"));\n\n const nodesStorage = newNodeStorage.createContext(\"nodes\");\n nodesStorage.set(\"resumptionRecords\", legacyStorage.get([nodeId, \"MatterController\"], \"commissionedNodes\"));\n\n console.log(`Controller Node ${nodeId} with new unique id ${uniqueId} migrated successfully.`);\n } else {\n // Migrate the device storage\n const newNodeStorage = await storageService.open(uniqueIds[nodeId]);\n\n const nextEndpointNumber = legacyStorage.get<number>([nodeId, \"EndpointStructure\"], \"nextEndpointId\");\n if (nextEndpointNumber !== undefined && nextEndpointNumber > 2) {\n console.log(\n \"It seems you used a bridged or composed example before, please make sure to use all details (type, ids,...) in the parameters when starting the new example. When using a bridge and you had changed devices after pairing it could happen that the new example introduces new devices to the controller. If you are unsure unpair the old example and start fresh.\",\n );\n }\n\n const eventsStorage = newNodeStorage.createContext(\"events\");\n eventsStorage.set(\"lastEventNumber\", legacyStorage.get([nodeId, \"EventHandler\"], \"lastEventNumber\"));\n\n const fabricsStorage = newNodeStorage.createContext(\"fabrics\");\n fabricsStorage.set(\"fabrics\", legacyStorage.get([nodeId, \"FabricManager\"], \"fabrics\"));\n fabricsStorage.set(\"nextFabricIndex\", legacyStorage.get([nodeId, \"FabricManager\"], \"nextFabricIndex\"));\n\n const sessionsStorage = newNodeStorage.createContext(\"sessions\");\n sessionsStorage.set(\"resumptionRecords\", legacyStorage.get([nodeId, \"SessionManager\"], \"resumptionRecords\"));\n\n console.log(`Device Node ${nodeId} with unique id ${uniqueIds[nodeId]} migrated successfully.`);\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@project-chip/matter-node.js-examples",
3
- "version": "0.8.0-alpha.0-20240314-ed7b831b",
3
+ "version": "0.8.0-alpha.0-20240319-3d88df96",
4
4
  "description": "CLI/Reference implementation scripts for Matter protocol for node.js",
5
5
  "keywords": [
6
6
  "iot",
@@ -27,31 +27,47 @@
27
27
  "clean": "matter-build clean",
28
28
  "build": "matter-build",
29
29
  "build-clean": "matter-build --clean",
30
- "light": "matter-run src/examples/LightDevice.ts",
31
- "excelsior1000": "matter-run src/examples/IlluminatedRollerShade.ts",
30
+ "matter-light": "matter-run src/examples/LightDevice.ts",
31
+ "matter-sensor": "matter-run src/examples/SensorDeviceNode.ts",
32
+ "matter-excelsior1000": "matter-run src/examples/IlluminatedRollerShade.ts",
32
33
  "matter-device": "matter-run src/examples/DeviceNode.ts",
33
34
  "matter-bridge": "matter-run src/examples/BridgedDevicesNode.ts",
34
35
  "matter-composeddevice": "matter-run src/examples/ComposedDeviceNode.ts",
35
36
  "matter-multidevice": "matter-run src/examples/MultiDeviceNode.ts",
36
37
  "matter-controller": "matter-run src/examples/ControllerNode.ts",
38
+ "matter-device-legacy": "matter-run src/examples/DeviceNodeLegacy.ts",
39
+ "matter-bridge-legacy": "matter-run src/examples/BridgedDevicesNodeLegacy.ts",
40
+ "matter-composeddevice-legacy": "matter-run src/examples/ComposedDeviceNodeLegacy.ts",
41
+ "matter-multidevice-legacy": "matter-run src/examples/MultiDeviceNodeLegacy.ts",
42
+ "matter-controller-legacy": "matter-run src/examples/ControllerNodeLegacy.ts",
43
+ "matter-legacystorageconverter": "matter-run src/examples/LegacyStorageConverter.ts",
37
44
  "bundle-device": "esbuild src/examples/DeviceNode.ts --bundle --platform=node --conditions=esbuild --external:@stoprocent/bleno --external:@stoprocent/bluetooth-hci-socket --sourcemap --minify --outfile=build/bundle/DeviceNode.cjs",
38
45
  "matter-device-bundled": "node --enable-source-maps build/bundle/DeviceNode.cjs"
39
46
  },
40
47
  "bin": {
48
+ "matter-light": "./dist/esm/examples/LightDevice.ts",
49
+ "matter-excelsior1000": "./dist/esm/examples/IlluminatedRollerShade.ts",
50
+ "matter-sensor": "./dist/esm/examples/SensorDeviceNode.js",
41
51
  "matter-device": "./dist/esm/examples/DeviceNode.js",
42
52
  "matter-bridge": "./dist/esm/examples/BridgedDevicesNode.js",
43
53
  "matter-composeddevice": "./dist/esm/examples/ComposedDeviceNode.js",
44
54
  "matter-multidevice": "./dist/esm/examples/MultiDeviceNode.js",
45
- "matter-controller": "./dist/esm/examples/ControllerNode.js"
55
+ "matter-controller": "./dist/esm/examples/ControllerNode.js",
56
+ "matter-device-legacy": "./dist/esm/examples/DeviceNodeLegacy.js",
57
+ "matter-bridge-legacy": "./dist/esm/examples/BridgedDevicesNodeLegacy.js",
58
+ "matter-composeddevice-legacy": "./dist/esm/examples/ComposedDeviceNodeLegacy.js",
59
+ "matter-multidevice-legacy": "./dist/esm/examples/MultiDeviceNodeLegacy.js",
60
+ "matter-controller-legacy": "./dist/esm/examples/ControllerNodeLegacy.js",
61
+ "matter-legacystorageconverter": "./dist/esm/examples/LegacyStorageConverter.js"
46
62
  },
47
63
  "devDependencies": {
48
64
  "typescript": "^5.4.2"
49
65
  },
50
66
  "dependencies": {
51
- "@project-chip/matter-node-ble.js": "0.8.0-alpha.0-20240314-ed7b831b",
52
- "@project-chip/matter-node.js": "0.8.0-alpha.0-20240314-ed7b831b",
53
- "@project-chip/matter.js": "0.8.0-alpha.0-20240314-ed7b831b",
54
- "@project-chip/matter.js-tools": "0.8.0-alpha.0-20240314-ed7b831b"
67
+ "@project-chip/matter-node-ble.js": "0.8.0-alpha.0-20240319-3d88df96",
68
+ "@project-chip/matter-node.js": "0.8.0-alpha.0-20240319-3d88df96",
69
+ "@project-chip/matter.js": "0.8.0-alpha.0-20240319-3d88df96",
70
+ "@project-chip/matter.js-tools": "0.8.0-alpha.0-20240319-3d88df96"
55
71
  },
56
72
  "engines": {
57
73
  "_comment": "For Crypto.hkdf support",
@@ -67,5 +83,5 @@
67
83
  "publishConfig": {
68
84
  "access": "public"
69
85
  },
70
- "gitHead": "623686700b0b87f0622a913ca59701a58f15333a"
86
+ "gitHead": "75a744c54fb5412d02cd3ddc78fe1052f2da8c74"
71
87
  }
@@ -20,8 +20,7 @@
20
20
  import "@project-chip/matter-node.js";
21
21
 
22
22
  import { BleNode } from "@project-chip/matter-node-ble.js/ble";
23
- import { StorageBackendDisk } from "@project-chip/matter-node.js/storage";
24
- import { getIntParameter, getParameter, hasParameter, requireMinNodeVersion } from "@project-chip/matter-node.js/util";
23
+ import { requireMinNodeVersion } from "@project-chip/matter-node.js/util";
25
24
  import { CommissioningController, NodeCommissioningOptions } from "@project-chip/matter.js";
26
25
  import { Ble } from "@project-chip/matter.js/ble";
27
26
  import {
@@ -32,11 +31,10 @@ import {
32
31
  } from "@project-chip/matter.js/cluster";
33
32
  import { NodeId } from "@project-chip/matter.js/datatype";
34
33
  import { NodeStateInformation } from "@project-chip/matter.js/device";
35
- import { Environment } from "@project-chip/matter.js/environment";
36
- import { Format, Level, Logger } from "@project-chip/matter.js/log";
34
+ import { Environment, StorageService } from "@project-chip/matter.js/environment";
35
+ import { Logger } from "@project-chip/matter.js/log";
37
36
  import { CommissioningOptions } from "@project-chip/matter.js/protocol";
38
37
  import { ManualPairingCodeCodec } from "@project-chip/matter.js/schema";
39
- import { StorageManager } from "@project-chip/matter.js/storage";
40
38
  import { Time } from "@project-chip/matter.js/time";
41
39
  import { singleton } from "@project-chip/matter.js/util";
42
40
 
@@ -44,64 +42,29 @@ const logger = Logger.get("Controller");
44
42
 
45
43
  requireMinNodeVersion(16);
46
44
 
47
- /** Configure logging */
48
- switch (getParameter("loglevel")) {
49
- case "fatal":
50
- Logger.defaultLogLevel = Level.FATAL;
51
- break;
52
- case "error":
53
- Logger.defaultLogLevel = Level.ERROR;
54
- break;
55
- case "warn":
56
- Logger.defaultLogLevel = Level.WARN;
57
- break;
58
- case "info":
59
- Logger.defaultLogLevel = Level.INFO;
60
- break;
61
- }
62
-
63
- switch (getParameter("logformat")) {
64
- case "plain":
65
- Logger.format = Format.PLAIN;
66
- break;
67
- case "html":
68
- Logger.format = Format.HTML;
69
- break;
70
- default:
71
- if (process.stdin?.isTTY) Logger.format = Format.ANSI;
72
- }
45
+ const environment = Environment.default;
73
46
 
74
- if (hasParameter("ble")) {
47
+ if (environment.vars.get("ble")) {
75
48
  // Initialize Ble
76
49
  Ble.get = singleton(
77
50
  () =>
78
51
  new BleNode({
79
- hciId: getIntParameter("ble-hci-id"),
52
+ hciId: environment.vars.number("ble-hci-id"),
80
53
  }),
81
54
  );
82
55
  }
83
56
 
84
- const storageLocation = getParameter("store") ?? ".controller-node";
85
- const storage = new StorageBackendDisk(storageLocation, hasParameter("clearstorage"));
86
- logger.info(`Storage location: ${storageLocation} (Directory)`);
57
+ const storageService = environment.get(StorageService);
58
+
59
+ console.log(`Storage location: ${storageService.location} (Directory)`);
87
60
  logger.info(
88
- 'Use the parameter "-store NAME" to specify a different storage location, use -clearstorage to start with an empty storage.',
61
+ '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.',
89
62
  );
90
63
 
91
64
  class ControllerNode {
92
65
  async start() {
93
66
  logger.info(`node-matter Controller started`);
94
67
 
95
- /**
96
- * Initialize the storage system.
97
- *
98
- * The storage manager is then also used by the Matter server, so this code block in general is required,
99
- * but you can choose a different storage backend as long as it implements the required API.
100
- */
101
-
102
- const storageManager = new StorageManager(storage);
103
- await storageManager.initialize();
104
-
105
68
  /**
106
69
  * Collect all needed data
107
70
  *
@@ -113,15 +76,17 @@ class ControllerNode {
113
76
  * (so maybe better not ;-)).
114
77
  */
115
78
 
116
- const controllerStorage = storageManager.createContext("Controller");
117
- const ip = controllerStorage.has("ip") ? controllerStorage.get<string>("ip") : getParameter("ip");
118
- const port = controllerStorage.has("port") ? controllerStorage.get<number>("port") : getIntParameter("port");
79
+ const controllerStorage = (await storageService.open("controller")).createContext("data");
80
+ const ip = controllerStorage.has("ip") ? controllerStorage.get<string>("ip") : environment.vars.string("ip");
81
+ const port = controllerStorage.has("port")
82
+ ? controllerStorage.get<number>("port")
83
+ : environment.vars.number("port");
119
84
  const uniqueId = controllerStorage.has("uniqueid")
120
85
  ? controllerStorage.get<string>("uniqueid")
121
- : getParameter("uniqueid") ?? Time.nowMs().toString();
86
+ : environment.vars.string("uniqueid") ?? Time.nowMs().toString();
122
87
  controllerStorage.set("uniqueid", uniqueId);
123
88
 
124
- const pairingCode = getParameter("pairingcode");
89
+ const pairingCode = environment.vars.string("pairingcode");
125
90
  let longDiscriminator, setupPin, shortDiscriminator;
126
91
  if (pairingCode !== undefined) {
127
92
  const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
@@ -131,9 +96,9 @@ class ControllerNode {
131
96
  logger.debug(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
132
97
  } else {
133
98
  longDiscriminator =
134
- getIntParameter("longDiscriminator") ?? controllerStorage.get("longDiscriminator", 3840);
99
+ environment.vars.number("longDiscriminator") ?? controllerStorage.get("longDiscriminator", 3840);
135
100
  if (longDiscriminator > 4095) throw new Error("Discriminator value must be less than 4096");
136
- setupPin = getIntParameter("pin") ?? controllerStorage.get("pin", 20202021);
101
+ setupPin = environment.vars.number("pin") ?? controllerStorage.get("pin", 20202021);
137
102
  }
138
103
  if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
139
104
  throw new Error(
@@ -148,12 +113,12 @@ class ControllerNode {
148
113
  };
149
114
 
150
115
  let ble = false;
151
- if (hasParameter("ble")) {
116
+ if (environment.vars.get("ble")) {
152
117
  ble = true;
153
- const wifiSsid = getParameter("ble-wifi-ssid");
154
- const wifiCredentials = getParameter("ble-wifi-credentials");
155
- const threadNetworkName = getParameter("ble-thread-networkname");
156
- const threadOperationalDataset = getParameter("ble-thread-operationaldataset");
118
+ const wifiSsid = environment.vars.string("ble-wifi-ssid");
119
+ const wifiCredentials = environment.vars.string("ble-wifi-credentials");
120
+ const threadNetworkName = environment.vars.string("ble-thread-networkname");
121
+ const threadOperationalDataset = environment.vars.string("ble-thread-operationaldataset");
157
122
  if (wifiSsid !== undefined && wifiCredentials !== undefined) {
158
123
  logger.info(`Registering Commissioning over BLE with WiFi: ${wifiSsid}`);
159
124
  commissioningOptions.wifiNetwork = {
@@ -183,7 +148,6 @@ class ControllerNode {
183
148
  * are called.
184
149
  */
185
150
 
186
- const environment = Environment.default;
187
151
  const commissioningController = new CommissioningController({
188
152
  environment: {
189
153
  environment,
@@ -230,7 +194,7 @@ class ControllerNode {
230
194
  const nodes = commissioningController.getCommissionedNodes();
231
195
  console.log("Found commissioned nodes:", Logger.toJSON(nodes));
232
196
 
233
- const nodeId = NodeId(getIntParameter("nodeid") ?? nodes[0]);
197
+ const nodeId = NodeId(environment.vars.number("nodeid") ?? nodes[0]);
234
198
  if (!nodes.includes(nodeId)) {
235
199
  throw new Error(`Node ${nodeId} not found in commissioned nodes`);
236
200
  }
@@ -349,12 +313,3 @@ class ControllerNode {
349
313
  }
350
314
 
351
315
  new ControllerNode().start().catch(error => logger.error(error));
352
-
353
- process.on("SIGINT", () => {
354
- // Clean up on CTRL-C
355
- // Pragmatic way to make sure the storage is correctly closed before the process ends.
356
- storage
357
- .close()
358
- .then(() => process.exit(0))
359
- .catch(() => process.exit(1));
360
- });
@@ -177,7 +177,7 @@ async function getConfiguration() {
177
177
 
178
178
  const port = environment.vars.number("port") ?? 5540;
179
179
 
180
- const uniqueId = environment.vars.string("uniqueid") ?? deviceStorage.get("uniqueid", Time.nowMs().toString());
180
+ const uniqueId = environment.vars.string("uniqueid") ?? deviceStorage.get("uniqueid", Time.nowMs()).toString();
181
181
 
182
182
  // Persist basic data to keep them also on restart
183
183
  deviceStorage.set("passcode", passcode);
@@ -0,0 +1,144 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2024 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import "@project-chip/matter-node.js";
8
+ import { StorageBackendDisk } from "@project-chip/matter-node.js/storage";
9
+ import { Environment, StorageService } from "@project-chip/matter.js/environment";
10
+ import { Time } from "@project-chip/matter.js/time";
11
+ import { LocalStorage } from "node-localstorage";
12
+
13
+ const environment = Environment.default;
14
+
15
+ const legacyStoragePath = environment.vars.string("legacy.storage.path");
16
+ const newStoragePath = environment.vars.string("storage.path");
17
+
18
+ if (!legacyStoragePath || !newStoragePath) {
19
+ console.error("Usage: node LegacyStorageConverter.js --legacy-storage-path=<path> --storage-path=<path>");
20
+ process.exit(1);
21
+ }
22
+
23
+ const legacyLocalStorage = new LocalStorage(legacyStoragePath);
24
+
25
+ const legacyNodes = new Array<string>();
26
+ Object.keys(legacyLocalStorage).forEach(key => {
27
+ const firstLevel = key.split(".")[0];
28
+ if (!legacyNodes.includes(firstLevel)) {
29
+ legacyNodes.push(firstLevel);
30
+ }
31
+ });
32
+
33
+ const storageService = environment.get(StorageService);
34
+
35
+ const legacyStorage = new StorageBackendDisk(legacyStoragePath);
36
+ await legacyStorage.initialize();
37
+
38
+ const uniqueIds: Record<string, string> = {};
39
+ if (legacyNodes.includes("Device")) {
40
+ console.log("Example Device found ...");
41
+ legacyNodes.splice(legacyNodes.indexOf("Device"), 1);
42
+
43
+ const newDeviceStorage = (await storageService.open("device")).createContext("data");
44
+
45
+ legacyStorage.keys(["Device"]).forEach(key => {
46
+ console.log("Migrate Device.", key);
47
+ const value = legacyStorage.get(["Device"], key);
48
+ newDeviceStorage.set(key, value);
49
+ if (key === "uniqueid") {
50
+ uniqueIds["0"] = String(value);
51
+ newDeviceStorage.set(key, String(value));
52
+ } else if (key.startsWith("uniqueid")) {
53
+ const id = parseInt(key.substring(8));
54
+ uniqueIds[id - 1] = String(value);
55
+ newDeviceStorage.set(key, String(value));
56
+ }
57
+ });
58
+ }
59
+
60
+ if (legacyNodes.includes("Controller")) {
61
+ console.log("Example Controller found ...");
62
+ legacyNodes.splice(legacyNodes.indexOf("Controller"), 1);
63
+
64
+ const newControllerStorage = (await storageService.open("controller")).createContext("data");
65
+
66
+ legacyStorage.keys(["Controller"]).forEach(key => {
67
+ console.log("Migrate Controller.", key);
68
+ const value = legacyStorage.get(["Controller"], key);
69
+ newControllerStorage.set(key, value);
70
+ if (key === "uniqueid") {
71
+ uniqueIds["0"] = String(value);
72
+ newControllerStorage.set(key, String(value));
73
+ }
74
+ });
75
+ }
76
+
77
+ console.log(uniqueIds);
78
+
79
+ if (!Object.keys(uniqueIds).length) {
80
+ console.error("No uniqueId(s) found in legacy storage. Can not convert the node storage.");
81
+ process.exit(1);
82
+ }
83
+
84
+ for (const nodeId of legacyNodes) {
85
+ if (!uniqueIds[nodeId]) {
86
+ const rootCertId = legacyStorage.get(["0", "RootCertificateManager"], "rootCertId");
87
+
88
+ if (nodeId !== "0" || rootCertId === undefined) {
89
+ console.error(`No uniqueId found for node ${nodeId}. Can not convert the node storage.`);
90
+ continue;
91
+ }
92
+
93
+ // Migrate the controller storage
94
+ const newControllerStorage = (await storageService.open("controller")).createContext("data");
95
+ const uniqueId = Time.nowMs().toString();
96
+ newControllerStorage.set("uniqueid", uniqueId);
97
+
98
+ const newNodeStorage = await storageService.open(uniqueId);
99
+
100
+ const credentialsStorage = newNodeStorage.createContext("credentials");
101
+ credentialsStorage.set("rootCertId", rootCertId);
102
+ credentialsStorage.set(
103
+ "nextCertificateId",
104
+ legacyStorage.get(["0", "RootCertificateManager"], "nextCertificateId"),
105
+ );
106
+ credentialsStorage.set("rootCertBytes", legacyStorage.get(["0", "RootCertificateManager"], "rootCertBytes"));
107
+ credentialsStorage.set(
108
+ "rootKeyIdentifier",
109
+ legacyStorage.get(["0", "RootCertificateManager"], "rootKeyIdentifier"),
110
+ );
111
+ credentialsStorage.set("rootKeyPair", legacyStorage.get(["0", "RootCertificateManager"], "rootKeyPair"));
112
+ credentialsStorage.set("fabric", legacyStorage.get(["0", "MatterController"], "fabric"));
113
+
114
+ const sessionsStorage = newNodeStorage.createContext("sessions");
115
+ sessionsStorage.set("resumptionRecords", legacyStorage.get([nodeId, "SessionManager"], "resumptionRecords"));
116
+
117
+ const nodesStorage = newNodeStorage.createContext("nodes");
118
+ nodesStorage.set("resumptionRecords", legacyStorage.get([nodeId, "MatterController"], "commissionedNodes"));
119
+
120
+ console.log(`Controller Node ${nodeId} with new unique id ${uniqueId} migrated successfully.`);
121
+ } else {
122
+ // Migrate the device storage
123
+ const newNodeStorage = await storageService.open(uniqueIds[nodeId]);
124
+
125
+ const nextEndpointNumber = legacyStorage.get<number>([nodeId, "EndpointStructure"], "nextEndpointId");
126
+ if (nextEndpointNumber !== undefined && nextEndpointNumber > 2) {
127
+ console.log(
128
+ "It seems you used a bridged or composed example before, please make sure to use all details (type, ids,...) in the parameters when starting the new example. When using a bridge and you had changed devices after pairing it could happen that the new example introduces new devices to the controller. If you are unsure unpair the old example and start fresh.",
129
+ );
130
+ }
131
+
132
+ const eventsStorage = newNodeStorage.createContext("events");
133
+ eventsStorage.set("lastEventNumber", legacyStorage.get([nodeId, "EventHandler"], "lastEventNumber"));
134
+
135
+ const fabricsStorage = newNodeStorage.createContext("fabrics");
136
+ fabricsStorage.set("fabrics", legacyStorage.get([nodeId, "FabricManager"], "fabrics"));
137
+ fabricsStorage.set("nextFabricIndex", legacyStorage.get([nodeId, "FabricManager"], "nextFabricIndex"));
138
+
139
+ const sessionsStorage = newNodeStorage.createContext("sessions");
140
+ sessionsStorage.set("resumptionRecords", legacyStorage.get([nodeId, "SessionManager"], "resumptionRecords"));
141
+
142
+ console.log(`Device Node ${nodeId} with unique id ${uniqueIds[nodeId]} migrated successfully.`);
143
+ }
144
+ }