@project-chip/matter-node.js-examples 0.8.0-alpha.0-20240311-6a811d05 → 0.8.0-alpha.0-20240317-b07eaba6
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +17 -0
- package/dist/esm/examples/ControllerNode.js +22 -55
- package/dist/esm/examples/ControllerNode.js.map +2 -2
- package/dist/esm/examples/DeviceNode.js +1 -1
- package/dist/esm/examples/DeviceNode.js.map +2 -2
- package/dist/esm/examples/LegacyStorageConverter.js +114 -0
- package/dist/esm/examples/LegacyStorageConverter.js.map +7 -0
- package/dist/esm/examples/cluster/DummyThreadNetworkCommissioningServer.js +1 -1
- package/dist/esm/examples/cluster/DummyThreadNetworkCommissioningServer.js.map +2 -2
- package/package.json +26 -10
- package/src/examples/ControllerNode.ts +25 -70
- package/src/examples/DeviceNode.ts +1 -1
- package/src/examples/LegacyStorageConverter.ts +144 -0
- package/src/examples/cluster/DummyThreadNetworkCommissioningServer.ts +2 -2
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 {
|
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 {
|
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
|
-
|
30
|
-
|
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:
|
31
|
+
hciId: environment.vars.number("ble-hci-id")
|
58
32
|
})
|
59
33
|
);
|
60
34
|
}
|
61
|
-
const
|
62
|
-
|
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 "-
|
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
|
71
|
-
|
72
|
-
const
|
73
|
-
const
|
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 =
|
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 =
|
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 =
|
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 (
|
72
|
+
if (environment.vars.get("ble")) {
|
102
73
|
ble = true;
|
103
|
-
const wifiSsid =
|
104
|
-
const wifiCredentials =
|
105
|
-
const threadNetworkName =
|
106
|
-
const threadOperationalDataset =
|
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(
|
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": ";AAEA;AAAA;AAAA;AAAA;AAAA;AAiBA,OAAO;AAEP,SAAS,eAAe;AACxB,SAAS,
|
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()
|
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": "AAAA;AAAA;AAAA;AAAA;AAAA;AAgBA,OAAO;AAEP,SAAS,6BAA6B;AACtC,SAAS,cAAc,gBAAgB;AACvC,SAAS,mBAAmB;AAC5B,SAAS,wBAAwB;AACjC,SAAS,6BAA6B;AACtC,SAAS,UAAU,sBAAsB;AACzC,SAAS,aAAa,sBAAsB;AAC5C,SAAS,kBAAkB;AAC3B,SAAS,YAAY;AACrB,SAAS,gBAAgB;AAEzB,sBAAsB,EAAE;AAExB,eAAe,OAAO;AAElB,QAAM;AAAA,IACF;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACJ,IAAI,MAAM,iBAAiB;AAK3B,QAAM,SAAS,MAAM,WAAW,OAAO;AAAA;AAAA,IAEnC,IAAI;AAAA;AAAA;AAAA,IAIJ,SAAS;AAAA,MACL;AAAA,IACJ;AAAA;AAAA;AAAA,IAIA,eAAe;AAAA,MACX;AAAA,MACA;AAAA,IACJ;AAAA;AAAA;AAAA,IAIA,oBAAoB;AAAA,MAChB,MAAM;AAAA,MACN,YAAY,aAAa,WAAW,sBAAsB,aAAa,iBAAiB,UAAU;AAAA,IACtG;AAAA;AAAA;AAAA,IAIA,kBAAkB;AAAA,MACd;AAAA,MACA,UAAU,SAAS,QAAQ;AAAA,MAC3B,WAAW;AAAA,MACX;AAAA,MACA,cAAc;AAAA,MACd;AAAA,MACA,cAAc,YAAY,QAAQ;AAAA,MAClC;AAAA,IACJ;AAAA,EACJ,CAAC;AASD,QAAM,WAAW,IAAI,SAAS,WAAW,wBAAwB,kBAAkB,EAAE,IAAI,QAAQ,CAAC;AAClG,QAAM,OAAO,IAAI,QAAQ;AAOzB,MAAI,gBAAgB;AACpB,WAAS,OAAO,SAAS,oBAAoB,GAAG,WAAS;AAErD,QAAI,QAAQ,KAAK,CAAC,eAAe;AAC7B,sBAAgB;AAChB,cAAQ,IAAI,0DAA0D;AAAA,IAC1E,WAAW,UAAU,GAAG;AACpB,sBAAgB;AAChB,cAAQ,IAAI,yBAAyB;AAAA,IACzC;AAAA,EACJ,CAAC;AAED,WAAS,OAAO,MAAM,aAAa,GAAG,WAAS;AAC3C,mBAAe,QAAQ,OAAO,KAAK;AACnC,YAAQ,IAAI,gBAAgB,QAAQ,OAAO,KAAK,EAAE;AAAA,EACtD,CAAC;AAKD,cAAY,eAAe,YAAY,MAAM,CAAC;AAO9C,QAAM,OAAO,IAAI;AACrB;AAEA,KAAK,EAAE,MAAM,WAAS,QAAQ,MAAM,KAAK,CAAC;AAO1C,SAAS,eAAe,iBAAyB;AAC7C,QAAM,SAAS,YAAY,QAAQ,KAAK,OAAO,eAAe;AAC9D,MAAI,WAAW;AAAW,WAAO;AACjC,UAAQ,IAAI,GAAG,eAAe,KAAK,SAAS,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,EAAE,CAAC,EAAE;AACjF;AAEA,eAAe,mBAAmB;AAU9B,QAAM,cAAc,YAAY;AAEhC,QAAM,iBAAiB,YAAY,IAAI,cAAc;AACrD,UAAQ,IAAI,qBAAqB,eAAe,QAAQ,cAAc;AACtE,UAAQ;AAAA,IACJ;AAAA,EACJ;AACA,QAAM,iBAAiB,MAAM,eAAe,KAAK,QAAQ,GAAG,cAAc,MAAM;AAEhF,QAAM,WAAW,cAAc,IAAI,YAAY,YAAY,KAAK,IAAI,MAAM,MAAM,QAAQ;AACxF,MAAI,cAAc,IAAI,UAAU,GAAG;AAC/B,YAAQ,IAAI,eAAe,WAAW,WAAW,OAAO,iDAAiD;AAAA,EAC7G;AACA,QAAM,aAAa;AACnB,QAAM,aAAa;AACnB,QAAM,WAAW,YAAY,KAAK,OAAO,UAAU,KAAK,cAAc,IAAI,YAAY,QAAQ;AAC9F,QAAM,gBAAgB,YAAY,KAAK,OAAO,eAAe,KAAK,cAAc,IAAI,iBAAiB,IAAI;AAEzG,QAAM,WAAW,YAAY,KAAK,OAAO,UAAU,KAAK,cAAc,IAAI,YAAY,KAAM;AAC5F,QAAM,cAAc,qBAAqB,WAAW,WAAW,OAAO;AACtE,QAAM,YAAY,YAAY,KAAK,OAAO,WAAW,KAAK,cAAc,IAAI,aAAa,KAAM;AAE/F,QAAM,OAAO,YAAY,KAAK,OAAO,MAAM,KAAK;AAEhD,QAAM,WAAW,YAAY,KAAK,OAAO,UAAU,KAAK,cAAc,IAAI,YAAY,KAAK,MAAM,CAAC,EAAE,SAAS;AAG7G,gBAAc,IAAI,YAAY,QAAQ;AACtC,gBAAc,IAAI,iBAAiB,aAAa;AAChD,gBAAc,IAAI,YAAY,QAAQ;AACtC,gBAAc,IAAI,aAAa,SAAS;AACxC,gBAAc,IAAI,YAAY,QAAQ;AACtC,gBAAc,IAAI,YAAY,QAAQ;AAEtC,SAAO;AAAA,IACH;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACJ;AACJ;",
|
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": "AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,OAAO;AACP,SAAS,0BAA0B;AACnC,SAAS,aAAa,sBAAsB;AAC5C,SAAS,YAAY;AACrB,SAAS,oBAAoB;AAE7B,MAAM,cAAc,YAAY;AAEhC,MAAM,oBAAoB,YAAY,KAAK,OAAO,qBAAqB;AACvE,MAAM,iBAAiB,YAAY,KAAK,OAAO,cAAc;AAE7D,IAAI,CAAC,qBAAqB,CAAC,gBAAgB;AACvC,UAAQ,MAAM,0FAA0F;AACxG,UAAQ,KAAK,CAAC;AAClB;AAEA,MAAM,qBAAqB,IAAI,aAAa,iBAAiB;AAE7D,MAAM,cAAc,IAAI,MAAc;AACtC,OAAO,KAAK,kBAAkB,EAAE,QAAQ,SAAO;AAC3C,QAAM,aAAa,IAAI,MAAM,GAAG,EAAE,CAAC;AACnC,MAAI,CAAC,YAAY,SAAS,UAAU,GAAG;AACnC,gBAAY,KAAK,UAAU;AAAA,EAC/B;AACJ,CAAC;AAED,MAAM,iBAAiB,YAAY,IAAI,cAAc;AAErD,MAAM,gBAAgB,IAAI,mBAAmB,iBAAiB;AAC9D,MAAM,cAAc,WAAW;AAE/B,MAAM,YAAoC,CAAC;AAC3C,IAAI,YAAY,SAAS,QAAQ,GAAG;AAChC,UAAQ,IAAI,0BAA0B;AACtC,cAAY,OAAO,YAAY,QAAQ,QAAQ,GAAG,CAAC;AAEnD,QAAM,oBAAoB,MAAM,eAAe,KAAK,QAAQ,GAAG,cAAc,MAAM;AAEnF,gBAAc,KAAK,CAAC,QAAQ,CAAC,EAAE,QAAQ,SAAO;AAC1C,YAAQ,IAAI,mBAAmB,GAAG;AAClC,UAAM,QAAQ,cAAc,IAAI,CAAC,QAAQ,GAAG,GAAG;AAC/C,qBAAiB,IAAI,KAAK,KAAK;AAC/B,QAAI,QAAQ,YAAY;AACpB,gBAAU,GAAG,IAAI,OAAO,KAAK;AAC7B,uBAAiB,IAAI,KAAK,OAAO,KAAK,CAAC;AAAA,IAC3C,WAAW,IAAI,WAAW,UAAU,GAAG;AACnC,YAAM,KAAK,SAAS,IAAI,UAAU,CAAC,CAAC;AACpC,gBAAU,KAAK,CAAC,IAAI,OAAO,KAAK;AAChC,uBAAiB,IAAI,KAAK,OAAO,KAAK,CAAC;AAAA,IAC3C;AAAA,EACJ,CAAC;AACL;AAEA,IAAI,YAAY,SAAS,YAAY,GAAG;AACpC,UAAQ,IAAI,8BAA8B;AAC1C,cAAY,OAAO,YAAY,QAAQ,YAAY,GAAG,CAAC;AAEvD,QAAM,wBAAwB,MAAM,eAAe,KAAK,YAAY,GAAG,cAAc,MAAM;AAE3F,gBAAc,KAAK,CAAC,YAAY,CAAC,EAAE,QAAQ,SAAO;AAC9C,YAAQ,IAAI,uBAAuB,GAAG;AACtC,UAAM,QAAQ,cAAc,IAAI,CAAC,YAAY,GAAG,GAAG;AACnD,yBAAqB,IAAI,KAAK,KAAK;AACnC,QAAI,QAAQ,YAAY;AACpB,gBAAU,GAAG,IAAI,OAAO,KAAK;AAC7B,2BAAqB,IAAI,KAAK,OAAO,KAAK,CAAC;AAAA,IAC/C;AAAA,EACJ,CAAC;AACL;AAEA,QAAQ,IAAI,SAAS;AAErB,IAAI,CAAC,OAAO,KAAK,SAAS,EAAE,QAAQ;AAChC,UAAQ,MAAM,2EAA2E;AACzF,UAAQ,KAAK,CAAC;AAClB;AAEA,WAAW,UAAU,aAAa;AAC9B,MAAI,CAAC,UAAU,MAAM,GAAG;AACpB,UAAM,aAAa,cAAc,IAAI,CAAC,KAAK,wBAAwB,GAAG,YAAY;AAElF,QAAI,WAAW,OAAO,eAAe,QAAW;AAC5C,cAAQ,MAAM,8BAA8B,MAAM,qCAAqC;AACvF;AAAA,IACJ;AAGA,UAAM,wBAAwB,MAAM,eAAe,KAAK,YAAY,GAAG,cAAc,MAAM;AAC3F,UAAM,WAAW,KAAK,MAAM,EAAE,SAAS;AACvC,yBAAqB,IAAI,YAAY,QAAQ;AAE7C,UAAM,iBAAiB,MAAM,eAAe,KAAK,QAAQ;AAEzD,UAAM,qBAAqB,eAAe,cAAc,aAAa;AACrE,uBAAmB,IAAI,cAAc,UAAU;AAC/C,uBAAmB;AAAA,MACf;AAAA,MACA,cAAc,IAAI,CAAC,KAAK,wBAAwB,GAAG,mBAAmB;AAAA,IAC1E;AACA,uBAAmB,IAAI,iBAAiB,cAAc,IAAI,CAAC,KAAK,wBAAwB,GAAG,eAAe,CAAC;AAC3G,uBAAmB;AAAA,MACf;AAAA,MACA,cAAc,IAAI,CAAC,KAAK,wBAAwB,GAAG,mBAAmB;AAAA,IAC1E;AACA,uBAAmB,IAAI,eAAe,cAAc,IAAI,CAAC,KAAK,wBAAwB,GAAG,aAAa,CAAC;AACvG,uBAAmB,IAAI,UAAU,cAAc,IAAI,CAAC,KAAK,kBAAkB,GAAG,QAAQ,CAAC;AAEvF,UAAM,kBAAkB,eAAe,cAAc,UAAU;AAC/D,oBAAgB,IAAI,qBAAqB,cAAc,IAAI,CAAC,QAAQ,gBAAgB,GAAG,mBAAmB,CAAC;AAE3G,UAAM,eAAe,eAAe,cAAc,OAAO;AACzD,iBAAa,IAAI,qBAAqB,cAAc,IAAI,CAAC,QAAQ,kBAAkB,GAAG,mBAAmB,CAAC;AAE1G,YAAQ,IAAI,mBAAmB,MAAM,uBAAuB,QAAQ,yBAAyB;AAAA,EACjG,OAAO;AAEH,UAAM,iBAAiB,MAAM,eAAe,KAAK,UAAU,MAAM,CAAC;AAElE,UAAM,qBAAqB,cAAc,IAAY,CAAC,QAAQ,mBAAmB,GAAG,gBAAgB;AACpG,QAAI,uBAAuB,UAAa,qBAAqB,GAAG;AAC5D,cAAQ;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AAEA,UAAM,gBAAgB,eAAe,cAAc,QAAQ;AAC3D,kBAAc,IAAI,mBAAmB,cAAc,IAAI,CAAC,QAAQ,cAAc,GAAG,iBAAiB,CAAC;AAEnG,UAAM,iBAAiB,eAAe,cAAc,SAAS;AAC7D,mBAAe,IAAI,WAAW,cAAc,IAAI,CAAC,QAAQ,eAAe,GAAG,SAAS,CAAC;AACrF,mBAAe,IAAI,mBAAmB,cAAc,IAAI,CAAC,QAAQ,eAAe,GAAG,iBAAiB,CAAC;AAErG,UAAM,kBAAkB,eAAe,cAAc,UAAU;AAC/D,oBAAgB,IAAI,qBAAqB,cAAc,IAAI,CAAC,QAAQ,gBAAgB,GAAG,mBAAmB,CAAC;AAE3G,YAAQ,IAAI,eAAe,MAAM,mBAAmB,UAAU,MAAM,CAAC,yBAAyB;AAAA,EAClG;AACJ;",
|
6
|
+
"names": []
|
7
|
+
}
|
@@ -44,7 +44,7 @@ class DummyThreadNetworkCommissioningServer extends NetworkCommissioningBehavior
|
|
44
44
|
}
|
45
45
|
addOrUpdateThreadNetwork({ operationalDataset, breadcrumb }) {
|
46
46
|
console.log(
|
47
|
-
`--->
|
47
|
+
`---> addOrUpdateThreadNetwork called on NetworkCommissioning cluster: ${operationalDataset.toHex()} ${breadcrumb}`
|
48
48
|
);
|
49
49
|
this.session.context.assertFailSafeArmed("Failsafe timer needs to be armed to add or update networks.");
|
50
50
|
if (breadcrumb !== void 0) {
|
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"version": 3,
|
3
3
|
"sources": ["../../../../src/examples/cluster/DummyThreadNetworkCommissioningServer.ts"],
|
4
|
-
"sourcesContent": ["/**\n * @license\n * Copyright 2022-2024 Matter.js Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { GeneralCommissioningBehavior } from \"@project-chip/matter.js/behavior/definitions/general-commissioning\";\nimport {\n AddOrUpdateThreadNetworkRequest,\n ConnectNetworkRequest,\n NetworkCommissioningBehavior,\n RemoveNetworkRequest,\n ReorderNetworkRequest,\n ScanNetworksRequest,\n ScanNetworksResponse,\n} from \"@project-chip/matter.js/behavior/definitions/network-commissioning\";\nimport { NetworkCommissioning } from \"@project-chip/matter.js/cluster\";\nimport { Logger } from \"@project-chip/matter.js/log\";\nimport { ByteArray } from \"@project-chip/matter.js/util\";\n\nconst firstNetworkId = new ByteArray(32);\n\n/**\n * This represents a Dummy version of a
|
5
|
-
"mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,SAAS,oCAAoC;AAC7C;AAAA,EAGI;AAAA,OAKG;AACP,SAAS,4BAA4B;AACrC,SAAS,cAAc;AACvB,SAAS,iBAAiB;AAE1B,MAAM,iBAAiB,IAAI,UAAU,EAAE;AAOhC,MAAM,8CAA8C,6BAA6B;AAAA,EACpF,qBAAqB,QAAQ;AACjC,EAAE;AAAA,EACW,aAAa,EAAE,WAAW,GAA8C;AAC7E,YAAQ,IAAI,6DAA6D,UAAU,EAAE;AAGrF,QAAI,eAAe,QAAW;AAC1B,YAAM,8BAA8B,KAAK,MAAM,IAAI,4BAA4B;AAC/E,kCAA4B,MAAM,aAAa;AAAA,IACnD;AAEA,UAAM,mBAAmB,qBAAqB,2BAA2B;AACzE,SAAK,MAAM,uBAAuB;AAElC,UAAM,oBAAoB;AAAA,MACtB;AAAA,QACI,OAAO,KAAK,SAAS,IAAI,KAAK,OAAO,kBAAkB;AAAA,QACvD,eAAe,OAAO,KAAK,SAAS,IAAI,KAAK,OAAO,0BAA0B,CAAC;AAAA,QAC/E,aAAa,KAAK,SAAS,IAAI,KAAK,OAAO,wBAAwB;AAAA,QACnE,SAAS,KAAK,SAAS,IAAI,KAAK,OAAO,oBAAoB;AAAA,QAC3D,SAAS;AAAA,QACT,iBAAiB,UAAU;AAAA,WACtB,KAAK,SAAS,IAAI,KAAK,OAAO,oBAAoB,KAAK,gBAAgB,YAAY;AAAA,QACxF;AAAA,QACA,MAAM;AAAA,QACN,KAAK;AAAA,MACT;AAAA,IACJ;AACA,YAAQ,IAAI,OAAO,OAAO,iBAAiB,CAAC;AAE5C,WAAO;AAAA,MACH;AAAA,MACA;AAAA,IACJ;AAAA,EACJ;AAAA,EAES,yBAAyB,EAAE,oBAAoB,WAAW,GAAoC;AACnG,YAAQ;AAAA,MACJ,
|
4
|
+
"sourcesContent": ["/**\n * @license\n * Copyright 2022-2024 Matter.js Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { GeneralCommissioningBehavior } from \"@project-chip/matter.js/behavior/definitions/general-commissioning\";\nimport {\n AddOrUpdateThreadNetworkRequest,\n ConnectNetworkRequest,\n NetworkCommissioningBehavior,\n RemoveNetworkRequest,\n ReorderNetworkRequest,\n ScanNetworksRequest,\n ScanNetworksResponse,\n} from \"@project-chip/matter.js/behavior/definitions/network-commissioning\";\nimport { NetworkCommissioning } from \"@project-chip/matter.js/cluster\";\nimport { Logger } from \"@project-chip/matter.js/log\";\nimport { ByteArray } from \"@project-chip/matter.js/util\";\n\nconst firstNetworkId = new ByteArray(32);\n\n/**\n * This represents a Dummy version of a Thread Network Commissioning Cluster Server without real thread related logic, beside\n * returning some values provided as CLI parameters. This dummy implementation is only there for tests/as showcase for BLE\n * commissioning of a device.\n */\nexport class DummyThreadNetworkCommissioningServer extends NetworkCommissioningBehavior.with(\n NetworkCommissioning.Feature.ThreadNetworkInterface,\n) {\n override scanNetworks({ breadcrumb }: ScanNetworksRequest): ScanNetworksResponse {\n console.log(`---> scanNetworks called on NetworkCommissioning cluster: ${breadcrumb}`);\n\n // Simulate successful scan\n if (breadcrumb !== undefined) {\n const generalCommissioningCluster = this.agent.get(GeneralCommissioningBehavior);\n generalCommissioningCluster.state.breadcrumb = breadcrumb;\n }\n\n const networkingStatus = NetworkCommissioning.NetworkCommissioningStatus.Success;\n this.state.lastNetworkingStatus = networkingStatus;\n\n const threadScanResults = [\n {\n panId: this.endpoint.env.vars.number(\"ble.thread.panId\"),\n extendedPanId: BigInt(this.endpoint.env.vars.string(\"ble.thread.extendedPanId\")),\n networkName: this.endpoint.env.vars.string(\"ble.thread.networkName\"),\n channel: this.endpoint.env.vars.number(\"ble.thread.channel\"),\n version: 130,\n extendedAddress: ByteArray.fromString(\n (this.endpoint.env.vars.string(\"ble.thread.address\") ?? \"000000000000\").toLowerCase(),\n ),\n rssi: -50,\n lqi: 50,\n },\n ];\n console.log(Logger.toJSON(threadScanResults));\n\n return {\n networkingStatus,\n threadScanResults,\n };\n }\n\n override addOrUpdateThreadNetwork({ operationalDataset, breadcrumb }: AddOrUpdateThreadNetworkRequest) {\n console.log(\n `---> addOrUpdateThreadNetwork called on NetworkCommissioning cluster: ${operationalDataset.toHex()} ${breadcrumb}`,\n );\n\n this.session.context.assertFailSafeArmed(\"Failsafe timer needs to be armed to add or update networks.\");\n\n // Simulate successful add or update\n if (breadcrumb !== undefined) {\n const generalCommissioningCluster = this.agent.get(GeneralCommissioningBehavior);\n generalCommissioningCluster.state.breadcrumb = breadcrumb;\n }\n\n const networkingStatus = NetworkCommissioning.NetworkCommissioningStatus.Success;\n this.state.lastNetworkingStatus = networkingStatus;\n this.state.lastNetworkId = firstNetworkId;\n\n return {\n networkingStatus,\n networkIndex: 0,\n };\n }\n\n override removeNetwork({ networkId, breadcrumb }: RemoveNetworkRequest) {\n console.log(`---> removeNetwork called on NetworkCommissioning cluster: ${networkId.toHex()} ${breadcrumb}`);\n\n this.session.context.assertFailSafeArmed(\"Failsafe timer needs to be armed to add or update networks.\");\n\n // Simulate successful add or update\n if (breadcrumb !== undefined) {\n const generalCommissioningCluster = this.agent.get(GeneralCommissioningBehavior);\n generalCommissioningCluster.state.breadcrumb = breadcrumb;\n }\n\n const networkingStatus = NetworkCommissioning.NetworkCommissioningStatus.Success;\n this.state.lastNetworkingStatus = networkingStatus;\n this.state.lastNetworkId = firstNetworkId;\n\n return {\n networkingStatus,\n networkIndex: 0,\n };\n }\n\n override async connectNetwork({ networkId, breadcrumb }: ConnectNetworkRequest) {\n console.log(`---> connectNetwork called on NetworkCommissioning cluster: ${networkId.toHex()} ${breadcrumb}`);\n\n this.session.context.assertFailSafeArmed(\"Failsafe timer needs to be armed to add or update networks.\");\n\n // Simulate successful connection\n if (breadcrumb !== undefined) {\n const generalCommissioningCluster = this.agent.get(GeneralCommissioningBehavior);\n generalCommissioningCluster.state.breadcrumb = breadcrumb;\n }\n\n this.state.networks[0].connected = true;\n\n const networkingStatus = NetworkCommissioning.NetworkCommissioningStatus.Success;\n this.state.lastNetworkingStatus = networkingStatus;\n this.state.lastNetworkId = firstNetworkId;\n this.state.lastConnectErrorValue = null;\n\n // Announce operational in IP network\n const device = this.session.context;\n await device.startAnnouncement();\n\n return {\n networkingStatus,\n errorValue: null,\n };\n }\n\n override reorderNetwork({ networkId, networkIndex, breadcrumb }: ReorderNetworkRequest) {\n console.log(\n `---> reorderNetwork called on NetworkCommissioning cluster: ${networkId.toHex()} ${networkIndex} ${breadcrumb}`,\n );\n\n // Simulate successful connection\n if (breadcrumb !== undefined) {\n const generalCommissioningCluster = this.agent.get(GeneralCommissioningBehavior);\n generalCommissioningCluster.state.breadcrumb = breadcrumb;\n }\n\n const networkingStatus = NetworkCommissioning.NetworkCommissioningStatus.Success;\n this.state.lastNetworkingStatus = networkingStatus;\n\n return {\n networkingStatus,\n networkIndex: 0,\n };\n }\n}\n"],
|
5
|
+
"mappings": "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",
|
6
6
|
"names": []
|
7
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-
|
3
|
+
"version": "0.8.0-alpha.0-20240317-b07eaba6",
|
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
|
-
"
|
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
|
-
"typescript": "^5.
|
64
|
+
"typescript": "^5.4.2"
|
49
65
|
},
|
50
66
|
"dependencies": {
|
51
|
-
"@project-chip/matter-node-ble.js": "0.8.0-alpha.0-
|
52
|
-
"@project-chip/matter-node.js": "0.8.0-alpha.0-
|
53
|
-
"@project-chip/matter.js": "0.8.0-alpha.0-
|
54
|
-
"@project-chip/matter.js-tools": "0.8.0-alpha.0-
|
67
|
+
"@project-chip/matter-node-ble.js": "0.8.0-alpha.0-20240317-b07eaba6",
|
68
|
+
"@project-chip/matter-node.js": "0.8.0-alpha.0-20240317-b07eaba6",
|
69
|
+
"@project-chip/matter.js": "0.8.0-alpha.0-20240317-b07eaba6",
|
70
|
+
"@project-chip/matter.js-tools": "0.8.0-alpha.0-20240317-b07eaba6"
|
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": "
|
86
|
+
"gitHead": "305be42a8427187c577d668071124357491b0abd"
|
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 {
|
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 {
|
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
|
-
|
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 (
|
47
|
+
if (environment.vars.get("ble")) {
|
75
48
|
// Initialize Ble
|
76
49
|
Ble.get = singleton(
|
77
50
|
() =>
|
78
51
|
new BleNode({
|
79
|
-
hciId:
|
52
|
+
hciId: environment.vars.number("ble-hci-id"),
|
80
53
|
}),
|
81
54
|
);
|
82
55
|
}
|
83
56
|
|
84
|
-
const
|
85
|
-
|
86
|
-
|
57
|
+
const storageService = environment.get(StorageService);
|
58
|
+
|
59
|
+
console.log(`Storage location: ${storageService.location} (Directory)`);
|
87
60
|
logger.info(
|
88
|
-
'Use the parameter "-
|
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 =
|
117
|
-
const ip = controllerStorage.has("ip") ? controllerStorage.get<string>("ip") :
|
118
|
-
const port = controllerStorage.has("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
|
-
:
|
86
|
+
: environment.vars.string("uniqueid") ?? Time.nowMs().toString();
|
122
87
|
controllerStorage.set("uniqueid", uniqueId);
|
123
88
|
|
124
|
-
const 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
|
-
|
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 =
|
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 (
|
116
|
+
if (environment.vars.get("ble")) {
|
152
117
|
ble = true;
|
153
|
-
const wifiSsid =
|
154
|
-
const wifiCredentials =
|
155
|
-
const threadNetworkName =
|
156
|
-
const threadOperationalDataset =
|
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(
|
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
|
+
}
|
@@ -21,7 +21,7 @@ import { ByteArray } from "@project-chip/matter.js/util";
|
|
21
21
|
const firstNetworkId = new ByteArray(32);
|
22
22
|
|
23
23
|
/**
|
24
|
-
* This represents a Dummy version of a
|
24
|
+
* This represents a Dummy version of a Thread Network Commissioning Cluster Server without real thread related logic, beside
|
25
25
|
* returning some values provided as CLI parameters. This dummy implementation is only there for tests/as showcase for BLE
|
26
26
|
* commissioning of a device.
|
27
27
|
*/
|
@@ -64,7 +64,7 @@ export class DummyThreadNetworkCommissioningServer extends NetworkCommissioningB
|
|
64
64
|
|
65
65
|
override addOrUpdateThreadNetwork({ operationalDataset, breadcrumb }: AddOrUpdateThreadNetworkRequest) {
|
66
66
|
console.log(
|
67
|
-
`--->
|
67
|
+
`---> addOrUpdateThreadNetwork called on NetworkCommissioning cluster: ${operationalDataset.toHex()} ${breadcrumb}`,
|
68
68
|
);
|
69
69
|
|
70
70
|
this.session.context.assertFailSafeArmed("Failsafe timer needs to be armed to add or update networks.");
|