matterbridge 3.0.2 → 3.0.3-dev-20250517-720018f
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -3
- package/dist/cli.js +2 -37
- package/dist/cluster/export.js +0 -2
- package/dist/defaultConfigSchema.js +0 -23
- package/dist/deviceManager.js +1 -94
- package/dist/frontend.js +71 -369
- package/dist/helpers.js +25 -56
- package/dist/index.js +2 -31
- package/dist/logger/export.js +0 -1
- package/dist/matter/behaviors.js +0 -2
- package/dist/matter/clusters.js +0 -2
- package/dist/matter/devices.js +0 -2
- package/dist/matter/endpoints.js +0 -2
- package/dist/matter/export.js +0 -2
- package/dist/matter/types.js +0 -2
- package/dist/matterbridge.js +143 -873
- package/dist/matterbridgeAccessoryPlatform.js +0 -34
- package/dist/matterbridgeBehaviors.js +4 -53
- package/dist/matterbridgeDeviceTypes.js +34 -431
- package/dist/matterbridgeDynamicPlatform.js +0 -34
- package/dist/matterbridgeEndpoint.js +11 -807
- package/dist/matterbridgeEndpointHelpers.js +9 -147
- package/dist/matterbridgePlatform.js +7 -225
- package/dist/matterbridgeTypes.js +0 -24
- package/dist/pluginManager.js +3 -264
- package/dist/roboticVacuumCleaner.js +3 -39
- package/dist/shelly.js +7 -155
- package/dist/storage/export.js +0 -1
- package/dist/update.js +0 -53
- package/dist/utils/colorUtils.js +2 -205
- package/dist/utils/commandLine.js +0 -53
- package/dist/utils/copyDirectory.js +1 -37
- package/dist/utils/createZip.js +2 -42
- package/dist/utils/deepCopy.js +0 -38
- package/dist/utils/deepEqual.js +1 -71
- package/dist/utils/export.js +0 -1
- package/dist/utils/hex.js +0 -57
- package/dist/utils/isvalid.js +0 -100
- package/dist/utils/network.js +5 -76
- package/dist/utils/wait.js +20 -56
- package/frontend/build/asset-manifest.json +3 -3
- package/frontend/build/index.html +1 -1
- package/frontend/build/static/js/{main.f6e0f736.js → main.2486c3e3.js} +3 -3
- package/frontend/build/static/js/{main.f6e0f736.js.map → main.2486c3e3.js.map} +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -2
- package/dist/cli.d.ts +0 -29
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cluster/export.d.ts +0 -2
- package/dist/cluster/export.d.ts.map +0 -1
- package/dist/cluster/export.js.map +0 -1
- package/dist/defaultConfigSchema.d.ts +0 -27
- package/dist/defaultConfigSchema.d.ts.map +0 -1
- package/dist/defaultConfigSchema.js.map +0 -1
- package/dist/deviceManager.d.ts +0 -114
- package/dist/deviceManager.d.ts.map +0 -1
- package/dist/deviceManager.js.map +0 -1
- package/dist/frontend.d.ts +0 -241
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/helpers.d.ts +0 -46
- package/dist/helpers.d.ts.map +0 -1
- package/dist/helpers.js.map +0 -1
- package/dist/index.d.ts +0 -36
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/logger/export.d.ts +0 -2
- package/dist/logger/export.d.ts.map +0 -1
- package/dist/logger/export.js.map +0 -1
- package/dist/matter/behaviors.d.ts +0 -2
- package/dist/matter/behaviors.d.ts.map +0 -1
- package/dist/matter/behaviors.js.map +0 -1
- package/dist/matter/clusters.d.ts +0 -2
- package/dist/matter/clusters.d.ts.map +0 -1
- package/dist/matter/clusters.js.map +0 -1
- package/dist/matter/devices.d.ts +0 -2
- package/dist/matter/devices.d.ts.map +0 -1
- package/dist/matter/devices.js.map +0 -1
- package/dist/matter/endpoints.d.ts +0 -2
- package/dist/matter/endpoints.d.ts.map +0 -1
- package/dist/matter/endpoints.js.map +0 -1
- package/dist/matter/export.d.ts +0 -5
- package/dist/matter/export.d.ts.map +0 -1
- package/dist/matter/export.js.map +0 -1
- package/dist/matter/types.d.ts +0 -3
- package/dist/matter/types.d.ts.map +0 -1
- package/dist/matter/types.js.map +0 -1
- package/dist/matterbridge.d.ts +0 -435
- package/dist/matterbridge.d.ts.map +0 -1
- package/dist/matterbridge.js.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.d.ts +0 -40
- package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
- package/dist/matterbridgeBehaviors.d.ts +0 -1188
- package/dist/matterbridgeBehaviors.d.ts.map +0 -1
- package/dist/matterbridgeBehaviors.js.map +0 -1
- package/dist/matterbridgeDeviceTypes.d.ts +0 -494
- package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
- package/dist/matterbridgeDeviceTypes.js.map +0 -1
- package/dist/matterbridgeDynamicPlatform.d.ts +0 -40
- package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
- package/dist/matterbridgeDynamicPlatform.js.map +0 -1
- package/dist/matterbridgeEndpoint.d.ts +0 -965
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -2728
- package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
- package/dist/matterbridgeEndpointHelpers.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts +0 -294
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts +0 -187
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/pluginManager.d.ts +0 -273
- package/dist/pluginManager.d.ts.map +0 -1
- package/dist/pluginManager.js.map +0 -1
- package/dist/roboticVacuumCleaner.d.ts +0 -43
- package/dist/roboticVacuumCleaner.d.ts.map +0 -1
- package/dist/roboticVacuumCleaner.js.map +0 -1
- package/dist/shelly.d.ts +0 -153
- package/dist/shelly.d.ts.map +0 -1
- package/dist/shelly.js.map +0 -1
- package/dist/storage/export.d.ts +0 -2
- package/dist/storage/export.d.ts.map +0 -1
- package/dist/storage/export.js.map +0 -1
- package/dist/update.d.ts +0 -58
- package/dist/update.d.ts.map +0 -1
- package/dist/update.js.map +0 -1
- package/dist/utils/colorUtils.d.ts +0 -61
- package/dist/utils/colorUtils.d.ts.map +0 -1
- package/dist/utils/colorUtils.js.map +0 -1
- package/dist/utils/commandLine.d.ts +0 -58
- package/dist/utils/commandLine.d.ts.map +0 -1
- package/dist/utils/commandLine.js.map +0 -1
- package/dist/utils/copyDirectory.d.ts +0 -32
- package/dist/utils/copyDirectory.d.ts.map +0 -1
- package/dist/utils/copyDirectory.js.map +0 -1
- package/dist/utils/createZip.d.ts +0 -38
- package/dist/utils/createZip.d.ts.map +0 -1
- package/dist/utils/createZip.js.map +0 -1
- package/dist/utils/deepCopy.d.ts +0 -31
- package/dist/utils/deepCopy.d.ts.map +0 -1
- package/dist/utils/deepCopy.js.map +0 -1
- package/dist/utils/deepEqual.d.ts +0 -53
- package/dist/utils/deepEqual.d.ts.map +0 -1
- package/dist/utils/deepEqual.js.map +0 -1
- package/dist/utils/export.d.ts +0 -11
- package/dist/utils/export.d.ts.map +0 -1
- package/dist/utils/export.js.map +0 -1
- package/dist/utils/hex.d.ts +0 -48
- package/dist/utils/hex.d.ts.map +0 -1
- package/dist/utils/hex.js.map +0 -1
- package/dist/utils/isvalid.d.ts +0 -102
- package/dist/utils/isvalid.d.ts.map +0 -1
- package/dist/utils/isvalid.js.map +0 -1
- package/dist/utils/network.d.ts +0 -69
- package/dist/utils/network.d.ts.map +0 -1
- package/dist/utils/network.js.map +0 -1
- package/dist/utils/wait.d.ts +0 -51
- package/dist/utils/wait.d.ts.map +0 -1
- package/dist/utils/wait.js.map +0 -1
- /package/frontend/build/static/js/{main.f6e0f736.js.LICENSE.txt → main.2486c3e3.js.LICENSE.txt} +0 -0
package/dist/matterbridge.js
CHANGED
|
@@ -1,36 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This file contains the class Matterbridge.
|
|
3
|
-
*
|
|
4
|
-
* @file matterbridge.ts
|
|
5
|
-
* @author Luca Liguori
|
|
6
|
-
* @date 2023-12-29
|
|
7
|
-
* @version 1.5.3
|
|
8
|
-
*
|
|
9
|
-
* Copyright 2023, 2024, 2025 Luca Liguori.
|
|
10
|
-
*
|
|
11
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
12
|
-
* you may not use this file except in compliance with the License.
|
|
13
|
-
* You may obtain a copy of the License at
|
|
14
|
-
*
|
|
15
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
16
|
-
*
|
|
17
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
18
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
19
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
20
|
-
* See the License for the specific language governing permissions and
|
|
21
|
-
* limitations under the License. *
|
|
22
|
-
*/
|
|
23
|
-
// Node.js modules
|
|
24
1
|
import os from 'node:os';
|
|
25
2
|
import path from 'node:path';
|
|
26
3
|
import { promises as fs } from 'node:fs';
|
|
27
4
|
import EventEmitter from 'node:events';
|
|
28
5
|
import { inspect } from 'node:util';
|
|
29
|
-
// AnsiLogger module
|
|
30
6
|
import { AnsiLogger, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN } from './logger/export.js';
|
|
31
|
-
// NodeStorage module
|
|
32
7
|
import { NodeStorageManager } from './storage/export.js';
|
|
33
|
-
// Matterbridge
|
|
34
8
|
import { getParameter, getIntParameter, hasParameter, copyDirectory, withTimeout, waiter, isValidString, parseVersionString, isValidNumber } from './utils/export.js';
|
|
35
9
|
import { logInterfaces, getGlobalNodeModules } from './utils/network.js';
|
|
36
10
|
import { dev, plg, typ } from './matterbridgeTypes.js';
|
|
@@ -40,15 +14,11 @@ import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
|
|
|
40
14
|
import { bridge } from './matterbridgeDeviceTypes.js';
|
|
41
15
|
import { Frontend } from './frontend.js';
|
|
42
16
|
import { addVirtualDevices } from './helpers.js';
|
|
43
|
-
// @matter
|
|
44
17
|
import { DeviceTypeId, Endpoint, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, VendorId, StorageService, Environment, ServerNode, UINT32_MAX, UINT16_MAX, } from '@matter/main';
|
|
45
18
|
import { DeviceCommissioner, FabricAction, MdnsService, PaseClient } from '@matter/main/protocol';
|
|
46
19
|
import { AggregatorEndpoint } from '@matter/main/endpoints';
|
|
47
20
|
import { BasicInformationServer } from '@matter/main/behaviors/basic-information';
|
|
48
21
|
import { BridgedDeviceBasicInformationServer } from '@matter/main/behaviors/bridged-device-basic-information';
|
|
49
|
-
/**
|
|
50
|
-
* Represents the Matterbridge application.
|
|
51
|
-
*/
|
|
52
22
|
export class Matterbridge extends EventEmitter {
|
|
53
23
|
systemInformation = {
|
|
54
24
|
interfaceName: '',
|
|
@@ -90,12 +60,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
90
60
|
matterbridgeAdvertise: false,
|
|
91
61
|
bridgeMode: '',
|
|
92
62
|
restartMode: '',
|
|
63
|
+
virtualMode: 'outlet',
|
|
93
64
|
readOnly: hasParameter('readonly') || hasParameter('shelly'),
|
|
94
65
|
shellyBoard: hasParameter('shelly'),
|
|
95
66
|
shellySysUpdate: false,
|
|
96
67
|
shellyMainUpdate: false,
|
|
97
68
|
profile: getParameter('profile'),
|
|
98
|
-
loggerLevel: "info"
|
|
69
|
+
loggerLevel: "info",
|
|
99
70
|
fileLogger: false,
|
|
100
71
|
matterLoggerLevel: MatterLogLevel.INFO,
|
|
101
72
|
matterFileLogger: false,
|
|
@@ -134,11 +105,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
134
105
|
plugins;
|
|
135
106
|
devices;
|
|
136
107
|
frontend = new Frontend(this);
|
|
137
|
-
// Matterbridge storage
|
|
138
108
|
nodeStorage;
|
|
139
109
|
nodeContext;
|
|
140
110
|
nodeStorageName = 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
|
|
141
|
-
// Cleanup
|
|
142
111
|
hasCleanupStarted = false;
|
|
143
112
|
initialized = false;
|
|
144
113
|
execRunningCount = 0;
|
|
@@ -151,22 +120,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
151
120
|
sigtermHandler;
|
|
152
121
|
exceptionHandler;
|
|
153
122
|
rejectionHandler;
|
|
154
|
-
// Matter environment
|
|
155
123
|
environment = Environment.default;
|
|
156
|
-
// Matter storage
|
|
157
124
|
matterStorageName = 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
|
|
158
125
|
matterStorageService;
|
|
159
126
|
matterStorageManager;
|
|
160
127
|
matterbridgeContext;
|
|
161
128
|
controllerContext;
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
certification; // device certification
|
|
129
|
+
mdnsInterface;
|
|
130
|
+
ipv4address;
|
|
131
|
+
ipv6address;
|
|
132
|
+
port;
|
|
133
|
+
passcode;
|
|
134
|
+
discriminator;
|
|
135
|
+
certification;
|
|
170
136
|
serverNode;
|
|
171
137
|
aggregatorNode;
|
|
172
138
|
aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
|
|
@@ -174,50 +140,21 @@ export class Matterbridge extends EventEmitter {
|
|
|
174
140
|
aggregatorProductId = getIntParameter('productId') ?? 0x8000;
|
|
175
141
|
aggregatorProductName = getParameter('productName') ?? 'Matterbridge aggregator';
|
|
176
142
|
static instance;
|
|
177
|
-
// We load asyncronously so is private
|
|
178
143
|
constructor() {
|
|
179
144
|
super();
|
|
180
145
|
}
|
|
181
|
-
/**
|
|
182
|
-
* Emits an event of the specified type with the provided arguments.
|
|
183
|
-
*
|
|
184
|
-
* @template K - The type of the event.
|
|
185
|
-
* @param {K} eventName - The name of the event to emit.
|
|
186
|
-
* @param {...MatterbridgeEvent[K]} args - The arguments to pass to the event listeners.
|
|
187
|
-
* @returns {boolean} - Returns true if the event had listeners, false otherwise.
|
|
188
|
-
*/
|
|
189
146
|
emit(eventName, ...args) {
|
|
190
147
|
return super.emit(eventName, ...args);
|
|
191
148
|
}
|
|
192
|
-
/**
|
|
193
|
-
* Registers an event listener for the specified event type.
|
|
194
|
-
*
|
|
195
|
-
* @template K - The type of the event.
|
|
196
|
-
* @param {K} eventName - The name of the event to listen for.
|
|
197
|
-
* @param {(...args: MatterbridgeEvent[K]) => void} listener - The callback function to invoke when the event is emitted.
|
|
198
|
-
* @returns {this} - Returns the instance of the Matterbridge class.
|
|
199
|
-
*/
|
|
200
149
|
on(eventName, listener) {
|
|
201
150
|
return super.on(eventName, listener);
|
|
202
151
|
}
|
|
203
|
-
/**
|
|
204
|
-
* Retrieves the list of Matterbridge devices.
|
|
205
|
-
* @returns {MatterbridgeEndpoint[]} An array of MatterbridgeDevice objects.
|
|
206
|
-
*/
|
|
207
152
|
getDevices() {
|
|
208
153
|
return this.devices.array();
|
|
209
154
|
}
|
|
210
|
-
/**
|
|
211
|
-
* Retrieves the list of registered plugins.
|
|
212
|
-
* @returns {RegisteredPlugin[]} An array of RegisteredPlugin objects.
|
|
213
|
-
*/
|
|
214
155
|
getPlugins() {
|
|
215
156
|
return this.plugins.array();
|
|
216
157
|
}
|
|
217
|
-
/**
|
|
218
|
-
* Set the logger logLevel for the Matterbridge classes.
|
|
219
|
-
* @param {LogLevel} logLevel The logger logLevel to set.
|
|
220
|
-
*/
|
|
221
158
|
async setLogLevel(logLevel) {
|
|
222
159
|
if (this.log)
|
|
223
160
|
this.log.logLevel = logLevel;
|
|
@@ -231,31 +168,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
231
168
|
for (const plugin of this.plugins) {
|
|
232
169
|
if (!plugin.platform || !plugin.platform.log || !plugin.platform.config)
|
|
233
170
|
continue;
|
|
234
|
-
plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug"
|
|
235
|
-
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug"
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
171
|
+
plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" : this.log.logLevel;
|
|
172
|
+
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" : this.log.logLevel);
|
|
173
|
+
}
|
|
174
|
+
let callbackLogLevel = "notice";
|
|
175
|
+
if (this.matterbridgeInformation.loggerLevel === "info" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
|
|
176
|
+
callbackLogLevel = "info";
|
|
177
|
+
if (this.matterbridgeInformation.loggerLevel === "debug" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
|
|
178
|
+
callbackLogLevel = "debug";
|
|
243
179
|
AnsiLogger.setGlobalCallback(this.frontend.wssSendMessage.bind(this.frontend), callbackLogLevel);
|
|
244
180
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
245
181
|
}
|
|
246
|
-
/** ***********************************************************************************************************************************/
|
|
247
|
-
/** loadInstance() and cleanup() methods */
|
|
248
|
-
/** ***********************************************************************************************************************************/
|
|
249
|
-
/**
|
|
250
|
-
* Loads an instance of the Matterbridge class.
|
|
251
|
-
* If an instance already exists, return that instance.
|
|
252
|
-
*
|
|
253
|
-
* @param initialize - Whether to initialize the Matterbridge instance after loading.
|
|
254
|
-
* @returns The loaded Matterbridge instance.
|
|
255
|
-
*/
|
|
256
182
|
static async loadInstance(initialize = false) {
|
|
257
183
|
if (!Matterbridge.instance) {
|
|
258
|
-
// eslint-disable-next-line no-console
|
|
259
184
|
if (hasParameter('debug'))
|
|
260
185
|
console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
|
|
261
186
|
Matterbridge.instance = new Matterbridge();
|
|
@@ -264,14 +189,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
264
189
|
}
|
|
265
190
|
return Matterbridge.instance;
|
|
266
191
|
}
|
|
267
|
-
/**
|
|
268
|
-
* Call cleanup().
|
|
269
|
-
* @deprecated This method is deprecated and is only used for jest tests.
|
|
270
|
-
*
|
|
271
|
-
*/
|
|
272
192
|
async destroyInstance() {
|
|
273
193
|
this.log.info(`Destroy instance...`);
|
|
274
|
-
// Save server nodes to close
|
|
275
194
|
const servers = [];
|
|
276
195
|
if (this.bridgeMode === 'bridge') {
|
|
277
196
|
if (this.serverNode)
|
|
@@ -283,81 +202,71 @@ export class Matterbridge extends EventEmitter {
|
|
|
283
202
|
servers.push(plugin.serverNode);
|
|
284
203
|
}
|
|
285
204
|
}
|
|
286
|
-
// Cleanup
|
|
287
205
|
await this.cleanup('destroying instance...', false);
|
|
288
|
-
// Close servers mdns service
|
|
289
206
|
this.log.info(`Dispose ${servers.length} MdnsService...`);
|
|
290
207
|
for (const server of servers) {
|
|
291
208
|
await server.env.get(MdnsService)[Symbol.asyncDispose]();
|
|
292
209
|
this.log.info(`Closed ${server.id} MdnsService`);
|
|
293
210
|
}
|
|
294
|
-
// Wait for the cleanup to finish
|
|
295
211
|
await new Promise((resolve) => {
|
|
296
212
|
setTimeout(resolve, 1000);
|
|
297
213
|
});
|
|
298
214
|
}
|
|
299
|
-
/**
|
|
300
|
-
* Initializes the Matterbridge application.
|
|
301
|
-
*
|
|
302
|
-
* @remarks
|
|
303
|
-
* This method performs the necessary setup and initialization steps for the Matterbridge application.
|
|
304
|
-
* It displays the help information if the 'help' parameter is provided, sets up the logger, checks the
|
|
305
|
-
* node version, registers signal handlers, initializes storage, and parses the command line.
|
|
306
|
-
*
|
|
307
|
-
* @returns A Promise that resolves when the initialization is complete.
|
|
308
|
-
*/
|
|
309
215
|
async initialize() {
|
|
310
|
-
|
|
216
|
+
this.emit('initialize_started');
|
|
311
217
|
if (hasParameter('service'))
|
|
312
218
|
this.restartMode = 'service';
|
|
313
219
|
if (hasParameter('docker'))
|
|
314
220
|
this.restartMode = 'docker';
|
|
315
|
-
// Set the matterbridge directory
|
|
316
221
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
222
|
+
this.matterbridgeInformation.homeDirectory = this.homeDirectory;
|
|
223
|
+
await this.createDirectory(this.homeDirectory, 'Matterbridge Home Directory');
|
|
317
224
|
this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
|
|
318
|
-
|
|
225
|
+
this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
|
|
226
|
+
await this.createDirectory(this.matterbridgeDirectory, 'Matterbridge Directory');
|
|
227
|
+
await this.createDirectory(path.join(this.matterbridgeDirectory, 'certs'), 'Matterbridge Frontend Certificate Directory');
|
|
228
|
+
this.matterbridgePluginDirectory = path.join(this.homeDirectory, 'Matterbridge');
|
|
229
|
+
this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
|
|
230
|
+
await this.createDirectory(this.matterbridgePluginDirectory, 'Matterbridge Plugin Directory');
|
|
231
|
+
this.matterbridgeCertDirectory = path.join(this.homeDirectory, '.mattercert');
|
|
232
|
+
this.matterbridgeInformation.matterbridgeCertDirectory = this.matterbridgeCertDirectory;
|
|
233
|
+
await this.createDirectory(this.matterbridgeCertDirectory, 'Matterbridge Matter Certificate Directory');
|
|
234
|
+
const { fileURLToPath } = await import('node:url');
|
|
235
|
+
const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
236
|
+
this.rootDirectory = path.resolve(currentFileDirectory, '../');
|
|
237
|
+
this.matterbridgeInformation.rootDirectory = this.rootDirectory;
|
|
319
238
|
this.environment.vars.set('log.level', MatterLogLevel.INFO);
|
|
320
239
|
this.environment.vars.set('log.format', MatterLogFormat.ANSI);
|
|
321
240
|
this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, this.matterStorageName));
|
|
322
241
|
this.environment.vars.set('runtime.signals', false);
|
|
323
242
|
this.environment.vars.set('runtime.exitcode', false);
|
|
324
|
-
|
|
325
|
-
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
|
|
326
|
-
// Register process handlers
|
|
243
|
+
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
327
244
|
this.registerProcessHandlers();
|
|
328
|
-
// Initialize nodeStorage and nodeContext
|
|
329
245
|
try {
|
|
330
246
|
this.log.debug(`Creating node storage manager: ${CYAN}${this.nodeStorageName}${db}`);
|
|
331
247
|
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, this.nodeStorageName), writeQueue: false, expiredInterval: undefined, logging: false });
|
|
332
248
|
this.log.debug('Creating node storage context for matterbridge');
|
|
333
249
|
this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
|
|
334
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
335
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
336
250
|
const keys = (await this.nodeStorage?.storage.keys());
|
|
337
251
|
for (const key of keys) {
|
|
338
252
|
this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
|
|
339
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
340
253
|
await this.nodeStorage?.storage.get(key);
|
|
341
254
|
}
|
|
342
255
|
const storages = await this.nodeStorage.getStorageNames();
|
|
343
256
|
for (const storage of storages) {
|
|
344
257
|
this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
|
|
345
258
|
const nodeContext = await this.nodeStorage?.createStorage(storage);
|
|
346
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
347
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
348
259
|
const keys = (await nodeContext?.storage.keys());
|
|
349
260
|
keys.forEach(async (key) => {
|
|
350
261
|
this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
|
|
351
262
|
await nodeContext?.get(key);
|
|
352
263
|
});
|
|
353
264
|
}
|
|
354
|
-
// Creating a backup of the node storage since it is not corrupted
|
|
355
265
|
this.log.debug('Creating node storage backup...');
|
|
356
266
|
await copyDirectory(path.join(this.matterbridgeDirectory, this.nodeStorageName), path.join(this.matterbridgeDirectory, this.nodeStorageName + '.backup'));
|
|
357
267
|
this.log.debug('Created node storage backup');
|
|
358
268
|
}
|
|
359
269
|
catch (error) {
|
|
360
|
-
// Restoring the backup of the node storage since it is corrupted
|
|
361
270
|
this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
|
|
362
271
|
if (hasParameter('norestore')) {
|
|
363
272
|
this.log.fatal(`The matterbridge node storage is corrupted. Parameter -norestore found: exiting...`);
|
|
@@ -372,32 +281,24 @@ export class Matterbridge extends EventEmitter {
|
|
|
372
281
|
this.log.fatal('Fatal error creating node storage manager and context for matterbridge');
|
|
373
282
|
throw new Error('Fatal error creating node storage manager and context for matterbridge');
|
|
374
283
|
}
|
|
375
|
-
// Set the first port to use for the commissioning server (will be incremented in childbridge mode)
|
|
376
284
|
this.port = getIntParameter('port') ?? (await this.nodeContext.get('matterport', 5540)) ?? 5540;
|
|
377
|
-
// Set the first passcode to use for the commissioning server (will be incremented in childbridge mode)
|
|
378
285
|
this.passcode = getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode')) ?? PaseClient.generateRandomPasscode();
|
|
379
|
-
// Set the first discriminator to use for the commissioning server (will be incremented in childbridge mode)
|
|
380
286
|
this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator();
|
|
381
|
-
|
|
382
|
-
const pairingFilePath = path.join(this.homeDirectory, '.mattercert', 'pairing.json');
|
|
287
|
+
const pairingFilePath = path.join(this.matterbridgeCertDirectory, 'pairing.json');
|
|
383
288
|
try {
|
|
384
289
|
await fs.access(pairingFilePath, fs.constants.R_OK);
|
|
385
290
|
const pairingFileContent = await fs.readFile(pairingFilePath, 'utf8');
|
|
386
291
|
const pairingFileJson = JSON.parse(pairingFileContent);
|
|
387
|
-
// Override the passcode and discriminator if they are present in the pairing file
|
|
388
292
|
if (pairingFileJson.passcode && pairingFileJson.discriminator) {
|
|
389
293
|
this.passcode = pairingFileJson.passcode;
|
|
390
294
|
this.discriminator = pairingFileJson.discriminator;
|
|
391
|
-
this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using passcode ${CYAN}${this.passcode}${nf} and discriminator ${CYAN}${this.discriminator}${nf}
|
|
295
|
+
this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using passcode ${CYAN}${this.passcode}${nf} and discriminator ${CYAN}${this.discriminator}${nf} from pairing file.`);
|
|
392
296
|
}
|
|
393
|
-
// Set the certification if it is present in the pairing file
|
|
394
297
|
if (pairingFileJson.privateKey && pairingFileJson.certificate && pairingFileJson.intermediateCertificate && pairingFileJson.declaration) {
|
|
395
298
|
const hexStringToUint8Array = (hexString) => {
|
|
396
299
|
const matches = hexString.match(/.{1,2}/g);
|
|
397
300
|
return matches ? new Uint8Array(matches.map((byte) => parseInt(byte, 16))) : new Uint8Array();
|
|
398
301
|
};
|
|
399
|
-
// const hexString = Buffer.from('Test string', 'utf-8').toString('hex');
|
|
400
|
-
// console.log(hexString, Buffer.from(hexStringToUint8Array(hexString)).toString('utf-8'));
|
|
401
302
|
this.certification = {
|
|
402
303
|
privateKey: hexStringToUint8Array(pairingFileJson.privateKey),
|
|
403
304
|
certificate: hexStringToUint8Array(pairingFileJson.certificate),
|
|
@@ -410,44 +311,41 @@ export class Matterbridge extends EventEmitter {
|
|
|
410
311
|
catch (error) {
|
|
411
312
|
this.log.debug(`Pairing file ${CYAN}${pairingFilePath}${db} not found: ${error instanceof Error ? error.message : error}`);
|
|
412
313
|
}
|
|
413
|
-
// Store the passcode, discriminator and port in the node context
|
|
414
314
|
await this.nodeContext.set('matterport', this.port);
|
|
415
315
|
await this.nodeContext.set('matterpasscode', this.passcode);
|
|
416
316
|
await this.nodeContext.set('matterdiscriminator', this.discriminator);
|
|
417
|
-
this.log.debug(`Initializing server node for Matterbridge
|
|
418
|
-
// Set matterbridge logger level (context: matterbridgeLogLevel)
|
|
317
|
+
this.log.debug(`Initializing server node for Matterbridge on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
|
|
419
318
|
if (hasParameter('logger')) {
|
|
420
319
|
const level = getParameter('logger');
|
|
421
320
|
if (level === 'debug') {
|
|
422
|
-
this.log.logLevel = "debug"
|
|
321
|
+
this.log.logLevel = "debug";
|
|
423
322
|
}
|
|
424
323
|
else if (level === 'info') {
|
|
425
|
-
this.log.logLevel = "info"
|
|
324
|
+
this.log.logLevel = "info";
|
|
426
325
|
}
|
|
427
326
|
else if (level === 'notice') {
|
|
428
|
-
this.log.logLevel = "notice"
|
|
327
|
+
this.log.logLevel = "notice";
|
|
429
328
|
}
|
|
430
329
|
else if (level === 'warn') {
|
|
431
|
-
this.log.logLevel = "warn"
|
|
330
|
+
this.log.logLevel = "warn";
|
|
432
331
|
}
|
|
433
332
|
else if (level === 'error') {
|
|
434
|
-
this.log.logLevel = "error"
|
|
333
|
+
this.log.logLevel = "error";
|
|
435
334
|
}
|
|
436
335
|
else if (level === 'fatal') {
|
|
437
|
-
this.log.logLevel = "fatal"
|
|
336
|
+
this.log.logLevel = "fatal";
|
|
438
337
|
}
|
|
439
338
|
else {
|
|
440
339
|
this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
|
|
441
|
-
this.log.logLevel = "info"
|
|
340
|
+
this.log.logLevel = "info";
|
|
442
341
|
}
|
|
443
342
|
}
|
|
444
343
|
else {
|
|
445
|
-
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice"
|
|
344
|
+
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice" : "info");
|
|
446
345
|
}
|
|
447
346
|
this.frontend.logLevel = this.log.logLevel;
|
|
448
347
|
MatterbridgeEndpoint.logLevel = this.log.logLevel;
|
|
449
348
|
this.matterbridgeInformation.loggerLevel = this.log.logLevel;
|
|
450
|
-
// Create the file logger for matterbridge (context: matterbridgeFileLog)
|
|
451
349
|
if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
|
|
452
350
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), this.log.logLevel, true);
|
|
453
351
|
this.matterbridgeInformation.fileLogger = true;
|
|
@@ -456,7 +354,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
456
354
|
this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.matterbridgeInformation.fileLogger}.`);
|
|
457
355
|
if (this.profile !== undefined)
|
|
458
356
|
this.log.debug(`Matterbridge profile: ${this.profile}.`);
|
|
459
|
-
// Set matter.js logger level, format and logger (context: matterLogLevel)
|
|
460
357
|
if (hasParameter('matterlogger')) {
|
|
461
358
|
const level = getParameter('matterlogger');
|
|
462
359
|
if (level === 'debug') {
|
|
@@ -488,7 +385,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
488
385
|
Logger.format = MatterLogFormat.ANSI;
|
|
489
386
|
Logger.setLogger('default', this.createMatterLogger());
|
|
490
387
|
this.matterbridgeInformation.matterLoggerLevel = Logger.defaultLogLevel;
|
|
491
|
-
// Create the file logger for matter.js (context: matterFileLog)
|
|
492
388
|
if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
|
|
493
389
|
this.matterbridgeInformation.matterFileLogger = true;
|
|
494
390
|
Logger.addLogger('matterfilelogger', await this.createMatterFileLogger(path.join(this.matterbridgeDirectory, this.matterLoggerFile), true), {
|
|
@@ -497,7 +393,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
497
393
|
});
|
|
498
394
|
}
|
|
499
395
|
this.log.debug(`Matter logLevel: ${Logger.defaultLogLevel} fileLoger: ${this.matterbridgeInformation.matterFileLogger}.`);
|
|
500
|
-
// Log network interfaces
|
|
501
396
|
const networkInterfaces = os.networkInterfaces();
|
|
502
397
|
const availableAddresses = Object.entries(networkInterfaces);
|
|
503
398
|
const availableInterfaces = Object.keys(networkInterfaces);
|
|
@@ -509,7 +404,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
509
404
|
});
|
|
510
405
|
}
|
|
511
406
|
}
|
|
512
|
-
// Set the interface to use for matter server node mdnsInterface
|
|
513
407
|
if (hasParameter('mdnsinterface')) {
|
|
514
408
|
this.mdnsInterface = getParameter('mdnsinterface');
|
|
515
409
|
}
|
|
@@ -518,7 +412,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
518
412
|
if (this.mdnsInterface === '')
|
|
519
413
|
this.mdnsInterface = undefined;
|
|
520
414
|
}
|
|
521
|
-
// Validate mdnsInterface
|
|
522
415
|
if (this.mdnsInterface) {
|
|
523
416
|
if (!availableInterfaces.includes(this.mdnsInterface)) {
|
|
524
417
|
this.log.error(`Invalid mdnsInterface: ${this.mdnsInterface}. Available interfaces are: ${availableInterfaces.join(', ')}. Using all available interfaces.`);
|
|
@@ -531,7 +424,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
531
424
|
}
|
|
532
425
|
if (this.mdnsInterface)
|
|
533
426
|
this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
|
|
534
|
-
// Set the listeningAddressIpv4 for the matter commissioning server
|
|
535
427
|
if (hasParameter('ipv4address')) {
|
|
536
428
|
this.ipv4address = getParameter('ipv4address');
|
|
537
429
|
}
|
|
@@ -540,7 +432,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
540
432
|
if (this.ipv4address === '')
|
|
541
433
|
this.ipv4address = undefined;
|
|
542
434
|
}
|
|
543
|
-
// Validate ipv4address
|
|
544
435
|
if (this.ipv4address) {
|
|
545
436
|
let isValid = false;
|
|
546
437
|
for (const [ifaceName, ifaces] of availableAddresses) {
|
|
@@ -556,7 +447,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
556
447
|
await this.nodeContext.remove('matteripv4address');
|
|
557
448
|
}
|
|
558
449
|
}
|
|
559
|
-
// Set the listeningAddressIpv6 for the matter commissioning server
|
|
560
450
|
if (hasParameter('ipv6address')) {
|
|
561
451
|
this.ipv6address = getParameter('ipv6address');
|
|
562
452
|
}
|
|
@@ -565,7 +455,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
565
455
|
if (this.ipv6address === '')
|
|
566
456
|
this.ipv6address = undefined;
|
|
567
457
|
}
|
|
568
|
-
// Validate ipv6address
|
|
569
458
|
if (this.ipv6address) {
|
|
570
459
|
let isValid = false;
|
|
571
460
|
for (const [ifaceName, ifaces] of availableAddresses) {
|
|
@@ -586,19 +475,22 @@ export class Matterbridge extends EventEmitter {
|
|
|
586
475
|
await this.nodeContext.remove('matteripv6address');
|
|
587
476
|
}
|
|
588
477
|
}
|
|
589
|
-
|
|
478
|
+
if (hasParameter('novirtual')) {
|
|
479
|
+
this.matterbridgeInformation.virtualMode = 'disabled';
|
|
480
|
+
await this.nodeContext.set('virtualmode', 'disabled');
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
this.matterbridgeInformation.virtualMode = (await this.nodeContext.get('virtualmode', 'outlet'));
|
|
484
|
+
}
|
|
485
|
+
this.log.debug(`Virtual mode ${this.matterbridgeInformation.virtualMode}.`);
|
|
590
486
|
this.plugins = new PluginManager(this);
|
|
591
487
|
await this.plugins.loadFromStorage();
|
|
592
488
|
this.plugins.logLevel = this.log.logLevel;
|
|
593
|
-
// Initialize DeviceManager
|
|
594
489
|
this.devices = new DeviceManager(this, this.nodeContext);
|
|
595
490
|
this.devices.logLevel = this.log.logLevel;
|
|
596
|
-
// Get the plugins from node storage and create the plugins node storage contexts
|
|
597
491
|
for (const plugin of this.plugins) {
|
|
598
492
|
const packageJson = await this.plugins.parse(plugin);
|
|
599
493
|
if (packageJson === null && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
|
|
600
|
-
// Try to reinstall the plugin from npm (for Docker pull and external plugins)
|
|
601
|
-
// We don't do this when the add and other parameters are set because we shut down the process after adding the plugin
|
|
602
494
|
this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
|
|
603
495
|
try {
|
|
604
496
|
await this.spawnCommand('npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose']);
|
|
@@ -620,7 +512,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
620
512
|
await plugin.nodeContext.set('description', plugin.description);
|
|
621
513
|
await plugin.nodeContext.set('author', plugin.author);
|
|
622
514
|
}
|
|
623
|
-
// Log system info and create .matterbridge directory
|
|
624
515
|
await this.logNodeAndSystemInfo();
|
|
625
516
|
this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
|
|
626
517
|
`${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
|
|
@@ -628,7 +519,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
628
519
|
`${hasParameter('controller') ? 'mode controller ' : ''}` +
|
|
629
520
|
`${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
|
|
630
521
|
`running on ${this.systemInformation.osType} (v.${this.systemInformation.osRelease}) platform ${this.systemInformation.osPlatform} arch ${this.systemInformation.osArch}`);
|
|
631
|
-
// Check node version and throw error
|
|
632
522
|
const minNodeVersion = 18;
|
|
633
523
|
const nodeVersion = process.versions.node;
|
|
634
524
|
const versionMajor = parseInt(nodeVersion.split('.')[0]);
|
|
@@ -636,15 +526,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
636
526
|
this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
637
527
|
throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
638
528
|
}
|
|
639
|
-
// Parse command line
|
|
640
529
|
await this.parseCommandLine();
|
|
530
|
+
this.emit('initialize_completed');
|
|
641
531
|
this.initialized = true;
|
|
642
532
|
}
|
|
643
|
-
/**
|
|
644
|
-
* Parses the command line arguments and performs the corresponding actions.
|
|
645
|
-
* @private
|
|
646
|
-
* @returns {Promise<void>} A promise that resolves when the command line arguments have been processed, or the process exits.
|
|
647
|
-
*/
|
|
648
533
|
async parseCommandLine() {
|
|
649
534
|
if (hasParameter('help')) {
|
|
650
535
|
this.log.info(`\nUsage: matterbridge [options]\n
|
|
@@ -658,7 +543,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
658
543
|
- ipv6address [address]: set the ipv6 interface address to use for the matter listener (default all interfaces)
|
|
659
544
|
- frontend [port]: start the frontend on the given port (default 8283)
|
|
660
545
|
- logger: set the matterbridge logger level: debug | info | notice | warn | error | fatal (default info)
|
|
546
|
+
- filelogger enable the matterbridge file logger (matterbridge.log)
|
|
661
547
|
- matterlogger: set the matter.js logger level: debug | info | notice | warn | error | fatal (default info)
|
|
548
|
+
- matterfilelogger enable the matter.js file logger (matter.log)
|
|
662
549
|
- reset: remove the commissioning for Matterbridge (bridge mode). Shutdown Matterbridge before using it!
|
|
663
550
|
- factoryreset: remove all commissioning information and reset all internal storages. Shutdown Matterbridge before using it!
|
|
664
551
|
- list: list the registered plugins
|
|
@@ -667,13 +554,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
667
554
|
- sudo: force the use of sudo to install or update packages if the internal logic fails
|
|
668
555
|
- nosudo: force not to use sudo to install or update packages if the internal logic fails
|
|
669
556
|
- norestore: force not to automatically restore the matterbridge node storage and the matter storage from backup if it is corrupted
|
|
557
|
+
- novirtual: disable the creation of the virtual devices Restart, Update and Reboot Matterbridge
|
|
670
558
|
- ssl: enable SSL for the frontend and WebSockerServer (certificates in .matterbridge/certs directory cert.pem, key.pem and ca.pem (optional))
|
|
671
559
|
- vendorId: override the default vendorId 0xfff1
|
|
672
560
|
- vendorName: override the default vendorName "Matterbridge"
|
|
673
561
|
- productId: override the default productId 0x8000
|
|
674
562
|
- productName: override the default productName "Matterbridge aggregator"
|
|
675
563
|
- service: enable the service mode (used in the systemctl configuration file)
|
|
676
|
-
- docker: enable the docker mode (used in the
|
|
564
|
+
- docker: enable the docker mode (used in the docker image)
|
|
677
565
|
- homedir: override the home directory (default: os.homedir())
|
|
678
566
|
- add [plugin path]: register the plugin from the given absolute or relative path
|
|
679
567
|
- add [plugin name]: register the globally installed plugin with the given name
|
|
@@ -763,7 +651,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
763
651
|
this.shutdown = true;
|
|
764
652
|
return;
|
|
765
653
|
}
|
|
766
|
-
// Start the matter storage and create the matterbridge context
|
|
767
654
|
try {
|
|
768
655
|
await this.startMatterStorage();
|
|
769
656
|
}
|
|
@@ -771,14 +658,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
771
658
|
this.log.fatal(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
772
659
|
throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
773
660
|
}
|
|
774
|
-
// Clear the matterbridge context if the reset parameter is set
|
|
775
661
|
if (hasParameter('reset') && getParameter('reset') === undefined) {
|
|
776
662
|
this.initialized = true;
|
|
777
663
|
await this.shutdownProcessAndReset();
|
|
778
664
|
this.shutdown = true;
|
|
779
665
|
return;
|
|
780
666
|
}
|
|
781
|
-
// Clear matterbridge plugin context if the reset parameter is set
|
|
782
667
|
if (hasParameter('reset') && getParameter('reset') !== undefined) {
|
|
783
668
|
this.log.debug(`Reset plugin ${getParameter('reset')}`);
|
|
784
669
|
const plugin = this.plugins.get(getParameter('reset'));
|
|
@@ -788,11 +673,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
788
673
|
this.log.error(`Plugin ${plg}${plugin.name}${er} storageManager not found`);
|
|
789
674
|
}
|
|
790
675
|
else {
|
|
791
|
-
await matterStorageManager
|
|
792
|
-
await matterStorageManager
|
|
793
|
-
await matterStorageManager
|
|
794
|
-
await matterStorageManager
|
|
795
|
-
await matterStorageManager
|
|
676
|
+
await matterStorageManager.createContext('events')?.clearAll();
|
|
677
|
+
await matterStorageManager.createContext('fabrics')?.clearAll();
|
|
678
|
+
await matterStorageManager.createContext('root')?.clearAll();
|
|
679
|
+
await matterStorageManager.createContext('sessions')?.clearAll();
|
|
680
|
+
await matterStorageManager.createContext('persist')?.clearAll();
|
|
796
681
|
this.log.info(`Reset commissionig for plugin ${plg}${plugin.name}${nf} done! Remove the device from the controller.`);
|
|
797
682
|
}
|
|
798
683
|
}
|
|
@@ -803,37 +688,30 @@ export class Matterbridge extends EventEmitter {
|
|
|
803
688
|
this.shutdown = true;
|
|
804
689
|
return;
|
|
805
690
|
}
|
|
806
|
-
// Initialize frontend
|
|
807
691
|
if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
|
|
808
692
|
await this.frontend.start(getIntParameter('frontend'));
|
|
809
|
-
// Check in 30 seconds the latest versions
|
|
810
693
|
this.checkUpdateTimeout = setTimeout(async () => {
|
|
811
694
|
const { checkUpdates } = await import('./update.js');
|
|
812
695
|
checkUpdates(this);
|
|
813
696
|
}, 30 * 1000).unref();
|
|
814
|
-
// Check each 24 hours the latest versions
|
|
815
697
|
this.checkUpdateInterval = setInterval(async () => {
|
|
816
698
|
const { checkUpdates } = await import('./update.js');
|
|
817
699
|
checkUpdates(this);
|
|
818
700
|
}, 12 * 60 * 60 * 1000).unref();
|
|
819
|
-
// Start the matterbridge in mode test
|
|
820
701
|
if (hasParameter('test')) {
|
|
821
702
|
this.bridgeMode = 'bridge';
|
|
822
703
|
MatterbridgeEndpoint.bridgeMode = 'bridge';
|
|
823
704
|
return;
|
|
824
705
|
}
|
|
825
|
-
// Start the matterbridge in mode controller
|
|
826
706
|
if (hasParameter('controller')) {
|
|
827
707
|
this.bridgeMode = 'controller';
|
|
828
708
|
await this.startController();
|
|
829
709
|
return;
|
|
830
710
|
}
|
|
831
|
-
// Check if the bridge mode is set and start matterbridge in bridge mode if not set
|
|
832
711
|
if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
|
|
833
712
|
this.log.info('Setting default matterbridge start mode to bridge');
|
|
834
713
|
await this.nodeContext?.set('bridgeMode', 'bridge');
|
|
835
714
|
}
|
|
836
|
-
// Start matterbridge in bridge mode
|
|
837
715
|
if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
|
|
838
716
|
this.bridgeMode = 'bridge';
|
|
839
717
|
MatterbridgeEndpoint.bridgeMode = 'bridge';
|
|
@@ -841,7 +719,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
841
719
|
await this.startBridge();
|
|
842
720
|
return;
|
|
843
721
|
}
|
|
844
|
-
// Start matterbridge in childbridge mode
|
|
845
722
|
if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
|
|
846
723
|
this.bridgeMode = 'childbridge';
|
|
847
724
|
MatterbridgeEndpoint.bridgeMode = 'childbridge';
|
|
@@ -850,20 +727,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
850
727
|
return;
|
|
851
728
|
}
|
|
852
729
|
}
|
|
853
|
-
/**
|
|
854
|
-
* Asynchronously loads and starts the registered plugins.
|
|
855
|
-
*
|
|
856
|
-
* This method is responsible for initializing and staarting all enabled plugins.
|
|
857
|
-
* It ensures that each plugin is properly loaded and started before the bridge starts.
|
|
858
|
-
*
|
|
859
|
-
* @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
|
|
860
|
-
*/
|
|
861
730
|
async startPlugins() {
|
|
862
|
-
// Check, load and start the plugins
|
|
863
731
|
for (const plugin of this.plugins) {
|
|
864
732
|
plugin.configJson = await this.plugins.loadConfig(plugin);
|
|
865
733
|
plugin.schemaJson = await this.plugins.loadSchema(plugin);
|
|
866
|
-
// Check if the plugin is available
|
|
867
734
|
if (!(await this.plugins.resolve(plugin.path))) {
|
|
868
735
|
this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
|
|
869
736
|
plugin.enabled = false;
|
|
@@ -883,14 +750,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
883
750
|
plugin.addedDevices = undefined;
|
|
884
751
|
plugin.qrPairingCode = undefined;
|
|
885
752
|
plugin.manualPairingCode = undefined;
|
|
886
|
-
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
753
|
+
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
887
754
|
}
|
|
888
755
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
889
756
|
}
|
|
890
|
-
/**
|
|
891
|
-
* Registers the process handlers for uncaughtException, unhandledRejection, SIGINT and SIGTERM.
|
|
892
|
-
* When either of these signals are received, the cleanup method is called with an appropriate message.
|
|
893
|
-
*/
|
|
894
757
|
registerProcessHandlers() {
|
|
895
758
|
this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
|
|
896
759
|
process.removeAllListeners('uncaughtException');
|
|
@@ -917,10 +780,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
917
780
|
};
|
|
918
781
|
process.on('SIGTERM', this.sigtermHandler);
|
|
919
782
|
}
|
|
920
|
-
|
|
921
|
-
* Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
|
|
922
|
-
*/
|
|
923
|
-
deregisterProcesslHandlers() {
|
|
783
|
+
deregisterProcessHandlers() {
|
|
924
784
|
this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
|
|
925
785
|
if (this.exceptionHandler)
|
|
926
786
|
process.off('uncaughtException', this.exceptionHandler);
|
|
@@ -936,17 +796,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
936
796
|
process.off('SIGTERM', this.sigtermHandler);
|
|
937
797
|
this.sigtermHandler = undefined;
|
|
938
798
|
}
|
|
939
|
-
/**
|
|
940
|
-
* Logs the node and system information.
|
|
941
|
-
*/
|
|
942
799
|
async logNodeAndSystemInfo() {
|
|
943
|
-
// IP address information
|
|
944
800
|
const networkInterfaces = os.networkInterfaces();
|
|
945
801
|
this.systemInformation.interfaceName = '';
|
|
946
802
|
this.systemInformation.ipv4Address = '';
|
|
947
803
|
this.systemInformation.ipv6Address = '';
|
|
948
804
|
for (const [interfaceName, interfaceDetails] of Object.entries(networkInterfaces)) {
|
|
949
|
-
// this.log.debug(`Checking interface: '${interfaceName}' for '${this.mdnsInterface}'`);
|
|
950
805
|
if (this.mdnsInterface && interfaceName !== this.mdnsInterface)
|
|
951
806
|
continue;
|
|
952
807
|
if (!interfaceDetails) {
|
|
@@ -972,22 +827,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
972
827
|
break;
|
|
973
828
|
}
|
|
974
829
|
}
|
|
975
|
-
// Node information
|
|
976
830
|
this.systemInformation.nodeVersion = process.versions.node;
|
|
977
831
|
const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
|
|
978
832
|
const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
|
|
979
833
|
const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
|
|
980
|
-
// Host system information
|
|
981
834
|
this.systemInformation.hostname = os.hostname();
|
|
982
835
|
this.systemInformation.user = os.userInfo().username;
|
|
983
|
-
this.systemInformation.osType = os.type();
|
|
984
|
-
this.systemInformation.osRelease = os.release();
|
|
985
|
-
this.systemInformation.osPlatform = os.platform();
|
|
986
|
-
this.systemInformation.osArch = os.arch();
|
|
987
|
-
this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
988
|
-
this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
989
|
-
this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
|
|
990
|
-
// Log the system information
|
|
836
|
+
this.systemInformation.osType = os.type();
|
|
837
|
+
this.systemInformation.osRelease = os.release();
|
|
838
|
+
this.systemInformation.osPlatform = os.platform();
|
|
839
|
+
this.systemInformation.osArch = os.arch();
|
|
840
|
+
this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
841
|
+
this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
842
|
+
this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
|
|
991
843
|
this.log.debug('Host System Information:');
|
|
992
844
|
this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
|
|
993
845
|
this.log.debug(`- User: ${this.systemInformation.user}`);
|
|
@@ -1003,26 +855,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
1003
855
|
this.log.debug(`- Total Memory: ${this.systemInformation.totalMemory}`);
|
|
1004
856
|
this.log.debug(`- Free Memory: ${this.systemInformation.freeMemory}`);
|
|
1005
857
|
this.log.debug(`- System Uptime: ${this.systemInformation.systemUptime}`);
|
|
1006
|
-
// Home directory
|
|
1007
|
-
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
1008
|
-
this.matterbridgeInformation.homeDirectory = this.homeDirectory;
|
|
1009
|
-
this.log.debug(`Home Directory: ${this.homeDirectory}`);
|
|
1010
|
-
// Package root directory
|
|
1011
|
-
const { fileURLToPath } = await import('node:url');
|
|
1012
|
-
const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
1013
|
-
this.rootDirectory = path.resolve(currentFileDirectory, '../');
|
|
1014
|
-
this.matterbridgeInformation.rootDirectory = this.rootDirectory;
|
|
1015
858
|
this.log.debug(`Root Directory: ${this.rootDirectory}`);
|
|
1016
|
-
|
|
859
|
+
this.log.debug(`Home Directory: ${this.homeDirectory}`);
|
|
860
|
+
this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
|
|
861
|
+
this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
|
|
862
|
+
this.log.debug(`Matterbridge Matter Certificate Directory: ${this.matterbridgeCertDirectory}`);
|
|
1017
863
|
if (this.nodeContext)
|
|
1018
864
|
this.globalModulesDirectory = this.matterbridgeInformation.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
|
|
1019
|
-
// First run of Matterbridge so the node storage is empty
|
|
1020
865
|
if (this.globalModulesDirectory === '') {
|
|
1021
866
|
try {
|
|
1022
867
|
this.execRunningCount++;
|
|
1023
|
-
this.globalModulesDirectory = await getGlobalNodeModules();
|
|
868
|
+
this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory = await getGlobalNodeModules();
|
|
1024
869
|
this.execRunningCount--;
|
|
1025
|
-
this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory;
|
|
1026
870
|
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
1027
871
|
await this.nodeContext?.set('globalModulesDirectory', this.globalModulesDirectory);
|
|
1028
872
|
}
|
|
@@ -1032,153 +876,53 @@ export class Matterbridge extends EventEmitter {
|
|
|
1032
876
|
}
|
|
1033
877
|
else
|
|
1034
878
|
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
1035
|
-
/* removed cause is too expensive for the shelly board and not really needed. Why should it change the globalModulesDirectory?
|
|
1036
|
-
else {
|
|
1037
|
-
this.getGlobalNodeModules()
|
|
1038
|
-
.then(async (globalModulesDirectory) => {
|
|
1039
|
-
this.globalModulesDirectory = globalModulesDirectory;
|
|
1040
|
-
this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory;
|
|
1041
|
-
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
1042
|
-
await this.nodeContext?.set<string>('globalModulesDirectory', this.globalModulesDirectory);
|
|
1043
|
-
})
|
|
1044
|
-
.catch((error) => {
|
|
1045
|
-
this.log.error(`Error getting global node_modules directory: ${error}`);
|
|
1046
|
-
});
|
|
1047
|
-
}*/
|
|
1048
|
-
// Create the data directory .matterbridge in the home directory
|
|
1049
|
-
this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
|
|
1050
|
-
this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
|
|
1051
|
-
try {
|
|
1052
|
-
await fs.access(this.matterbridgeDirectory);
|
|
1053
|
-
}
|
|
1054
|
-
catch (err) {
|
|
1055
|
-
if (err instanceof Error) {
|
|
1056
|
-
const nodeErr = err;
|
|
1057
|
-
if (nodeErr.code === 'ENOENT') {
|
|
1058
|
-
try {
|
|
1059
|
-
await fs.mkdir(this.matterbridgeDirectory, { recursive: true });
|
|
1060
|
-
this.log.info(`Created Matterbridge Directory: ${this.matterbridgeDirectory}`);
|
|
1061
|
-
}
|
|
1062
|
-
catch (err) {
|
|
1063
|
-
this.log.error(`Error creating directory: ${err}`);
|
|
1064
|
-
}
|
|
1065
|
-
}
|
|
1066
|
-
else {
|
|
1067
|
-
this.log.error(`Error accessing directory: ${err}`);
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
}
|
|
1071
|
-
this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
|
|
1072
|
-
// Create the plugin directory Matterbridge in the home directory
|
|
1073
|
-
this.matterbridgePluginDirectory = path.join(this.homeDirectory, 'Matterbridge');
|
|
1074
|
-
this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
|
|
1075
|
-
try {
|
|
1076
|
-
await fs.access(this.matterbridgePluginDirectory);
|
|
1077
|
-
}
|
|
1078
|
-
catch (err) {
|
|
1079
|
-
if (err instanceof Error) {
|
|
1080
|
-
const nodeErr = err;
|
|
1081
|
-
if (nodeErr.code === 'ENOENT') {
|
|
1082
|
-
try {
|
|
1083
|
-
await fs.mkdir(this.matterbridgePluginDirectory, { recursive: true });
|
|
1084
|
-
this.log.info(`Created Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
|
|
1085
|
-
}
|
|
1086
|
-
catch (err) {
|
|
1087
|
-
this.log.error(`Error creating directory: ${err}`);
|
|
1088
|
-
}
|
|
1089
|
-
}
|
|
1090
|
-
else {
|
|
1091
|
-
this.log.error(`Error accessing directory: ${err}`);
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
}
|
|
1095
|
-
this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
|
|
1096
|
-
// Create the matter cert directory in the home directory
|
|
1097
|
-
this.matterbridgeCertDirectory = path.join(this.homeDirectory, '.mattercert');
|
|
1098
|
-
this.matterbridgeInformation.matterbridgeCertDirectory = this.matterbridgeCertDirectory;
|
|
1099
|
-
try {
|
|
1100
|
-
await fs.access(this.matterbridgeCertDirectory);
|
|
1101
|
-
}
|
|
1102
|
-
catch (err) {
|
|
1103
|
-
if (err instanceof Error) {
|
|
1104
|
-
const nodeErr = err;
|
|
1105
|
-
if (nodeErr.code === 'ENOENT') {
|
|
1106
|
-
try {
|
|
1107
|
-
await fs.mkdir(this.matterbridgeCertDirectory, { recursive: true });
|
|
1108
|
-
this.log.info(`Created .mattercert directory: ${this.matterbridgeCertDirectory}`);
|
|
1109
|
-
}
|
|
1110
|
-
catch (err) {
|
|
1111
|
-
this.log.error(`Error creating .mattercert directory: ${err}`);
|
|
1112
|
-
}
|
|
1113
|
-
}
|
|
1114
|
-
else {
|
|
1115
|
-
this.log.error(`Error accessing .mattercert directory: ${err}`);
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1118
|
-
}
|
|
1119
|
-
this.log.debug(`Matterbridge Matter Cert Directory: ${this.matterbridgeCertDirectory}`);
|
|
1120
|
-
// Matterbridge version
|
|
1121
879
|
const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
|
|
1122
|
-
this.matterbridgeVersion = this.matterbridgeLatestVersion = packageJson.version;
|
|
1123
|
-
this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeInformation.matterbridgeLatestVersion = this.
|
|
880
|
+
this.matterbridgeVersion = this.matterbridgeLatestVersion = this.matterbridgeDevVersion = packageJson.version;
|
|
881
|
+
this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeInformation.matterbridgeLatestVersion = this.matterbridgeInformation.matterbridgeDevVersion = packageJson.version;
|
|
1124
882
|
this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
|
|
1125
|
-
// Matterbridge latest version
|
|
1126
883
|
if (this.nodeContext)
|
|
1127
|
-
this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
|
|
884
|
+
this.matterbridgeLatestVersion = this.matterbridgeInformation.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
|
|
1128
885
|
this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
|
|
1129
|
-
|
|
1130
|
-
|
|
886
|
+
if (this.nodeContext)
|
|
887
|
+
this.matterbridgeDevVersion = this.matterbridgeInformation.matterbridgeDevVersion = await this.nodeContext.get('matterbridgeDevVersion', this.matterbridgeVersion);
|
|
888
|
+
this.log.debug(`Matterbridge Dev Version: ${this.matterbridgeDevVersion}`);
|
|
1131
889
|
const currentDir = process.cwd();
|
|
1132
890
|
this.log.debug(`Current Working Directory: ${currentDir}`);
|
|
1133
|
-
// Command line arguments (excluding 'node' and the script name)
|
|
1134
891
|
const cmdArgs = process.argv.slice(2).join(' ');
|
|
1135
892
|
this.log.debug(`Command Line Arguments: ${cmdArgs}`);
|
|
1136
893
|
}
|
|
1137
|
-
/**
|
|
1138
|
-
* Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (for the frontend).
|
|
1139
|
-
*
|
|
1140
|
-
* @returns {Function} The MatterLogger function.
|
|
1141
|
-
*/
|
|
1142
894
|
createMatterLogger() {
|
|
1143
|
-
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4
|
|
1144
|
-
return (
|
|
895
|
+
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
|
|
896
|
+
return (level, formattedLog) => {
|
|
1145
897
|
const logger = formattedLog.slice(44, 44 + 20).trim();
|
|
1146
898
|
const message = formattedLog.slice(65);
|
|
1147
899
|
matterLogger.logName = logger;
|
|
1148
|
-
switch (
|
|
900
|
+
switch (level) {
|
|
1149
901
|
case MatterLogLevel.DEBUG:
|
|
1150
|
-
matterLogger.log("debug"
|
|
902
|
+
matterLogger.log("debug", message);
|
|
1151
903
|
break;
|
|
1152
904
|
case MatterLogLevel.INFO:
|
|
1153
|
-
matterLogger.log("info"
|
|
905
|
+
matterLogger.log("info", message);
|
|
1154
906
|
break;
|
|
1155
907
|
case MatterLogLevel.NOTICE:
|
|
1156
|
-
matterLogger.log("notice"
|
|
908
|
+
matterLogger.log("notice", message);
|
|
1157
909
|
break;
|
|
1158
910
|
case MatterLogLevel.WARN:
|
|
1159
|
-
matterLogger.log("warn"
|
|
911
|
+
matterLogger.log("warn", message);
|
|
1160
912
|
break;
|
|
1161
913
|
case MatterLogLevel.ERROR:
|
|
1162
|
-
matterLogger.log("error"
|
|
914
|
+
matterLogger.log("error", message);
|
|
1163
915
|
break;
|
|
1164
916
|
case MatterLogLevel.FATAL:
|
|
1165
|
-
matterLogger.log("fatal"
|
|
917
|
+
matterLogger.log("fatal", message);
|
|
1166
918
|
break;
|
|
1167
919
|
default:
|
|
1168
|
-
matterLogger.log("debug"
|
|
920
|
+
matterLogger.log("debug", message);
|
|
1169
921
|
break;
|
|
1170
922
|
}
|
|
1171
923
|
};
|
|
1172
924
|
}
|
|
1173
|
-
/**
|
|
1174
|
-
* Creates a Matter File Logger.
|
|
1175
|
-
*
|
|
1176
|
-
* @param {string} filePath - The path to the log file.
|
|
1177
|
-
* @param {boolean} [unlink=false] - Whether to unlink the log file before creating a new one.
|
|
1178
|
-
* @returns {Function} - A function that logs formatted messages to the log file.
|
|
1179
|
-
*/
|
|
1180
925
|
async createMatterFileLogger(filePath, unlink = false) {
|
|
1181
|
-
// 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
|
|
1182
926
|
let fileSize = 0;
|
|
1183
927
|
if (unlink) {
|
|
1184
928
|
try {
|
|
@@ -1188,7 +932,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1188
932
|
this.log.debug(`Error unlinking the log file ${CYAN}${filePath}${db}: ${error instanceof Error ? error.message : error}`);
|
|
1189
933
|
}
|
|
1190
934
|
}
|
|
1191
|
-
return async (
|
|
935
|
+
return async (level, formattedLog) => {
|
|
1192
936
|
if (fileSize > 100000000)
|
|
1193
937
|
return;
|
|
1194
938
|
fileSize += formattedLog.length;
|
|
@@ -1202,7 +946,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1202
946
|
const parts = message.split(' ');
|
|
1203
947
|
const logger = parts[1];
|
|
1204
948
|
const finalMessage = parts.slice(2).join(' ') + os.EOL;
|
|
1205
|
-
switch (
|
|
949
|
+
switch (level) {
|
|
1206
950
|
case MatterLogLevel.DEBUG:
|
|
1207
951
|
await fs.appendFile(filePath, `[${timestamp}] [${logger}] [debug] ${finalMessage}`);
|
|
1208
952
|
break;
|
|
@@ -1227,21 +971,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1227
971
|
}
|
|
1228
972
|
};
|
|
1229
973
|
}
|
|
1230
|
-
/**
|
|
1231
|
-
* Restarts the process by exiting the current instance and loading a new instance.
|
|
1232
|
-
*/
|
|
1233
974
|
async restartProcess() {
|
|
1234
975
|
await this.cleanup('restarting...', true);
|
|
1235
976
|
}
|
|
1236
|
-
/**
|
|
1237
|
-
* Shut down the process by exiting the current process.
|
|
1238
|
-
*/
|
|
1239
977
|
async shutdownProcess() {
|
|
1240
978
|
await this.cleanup('shutting down...', false);
|
|
1241
979
|
}
|
|
1242
|
-
/**
|
|
1243
|
-
* Update matterbridge and and shut down the process.
|
|
1244
|
-
*/
|
|
1245
980
|
async updateProcess() {
|
|
1246
981
|
this.log.info('Updating matterbridge...');
|
|
1247
982
|
try {
|
|
@@ -1254,73 +989,52 @@ export class Matterbridge extends EventEmitter {
|
|
|
1254
989
|
this.frontend.wssSendRestartRequired();
|
|
1255
990
|
await this.cleanup('updating...', false);
|
|
1256
991
|
}
|
|
1257
|
-
/**
|
|
1258
|
-
* Unregister all devices and shut down the process.
|
|
1259
|
-
*/
|
|
1260
992
|
async unregisterAndShutdownProcess() {
|
|
1261
993
|
this.log.info('Unregistering all devices and shutting down...');
|
|
1262
994
|
for (const plugin of this.plugins) {
|
|
1263
995
|
await this.removeAllBridgedEndpoints(plugin.name, 250);
|
|
1264
996
|
}
|
|
1265
997
|
this.log.debug('Waiting for the MessageExchange to finish...');
|
|
1266
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
998
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1267
999
|
this.log.debug('Cleaning up and shutting down...');
|
|
1268
1000
|
await this.cleanup('unregistered all devices and shutting down...', false);
|
|
1269
1001
|
}
|
|
1270
|
-
/**
|
|
1271
|
-
* Reset commissioning and shut down the process.
|
|
1272
|
-
*/
|
|
1273
1002
|
async shutdownProcessAndReset() {
|
|
1274
1003
|
await this.cleanup('shutting down with reset...', false);
|
|
1275
1004
|
}
|
|
1276
|
-
/**
|
|
1277
|
-
* Factory reset and shut down the process.
|
|
1278
|
-
*/
|
|
1279
1005
|
async shutdownProcessAndFactoryReset() {
|
|
1280
1006
|
await this.cleanup('shutting down with factory reset...', false);
|
|
1281
1007
|
}
|
|
1282
|
-
/**
|
|
1283
|
-
* Cleans up the Matterbridge instance.
|
|
1284
|
-
* @param message - The cleanup message.
|
|
1285
|
-
* @param restart - Indicates whether to restart the instance after cleanup. Default is `false`.
|
|
1286
|
-
* @returns A promise that resolves when the cleanup is completed.
|
|
1287
|
-
*/
|
|
1288
1008
|
async cleanup(message, restart = false) {
|
|
1289
1009
|
if (this.initialized && !this.hasCleanupStarted) {
|
|
1290
1010
|
this.emit('cleanup_started');
|
|
1291
1011
|
this.hasCleanupStarted = true;
|
|
1292
1012
|
this.log.info(message);
|
|
1293
|
-
// Clear the start matter interval
|
|
1294
1013
|
if (this.startMatterInterval) {
|
|
1295
1014
|
clearInterval(this.startMatterInterval);
|
|
1296
1015
|
this.startMatterInterval = undefined;
|
|
1297
1016
|
this.log.debug('Start matter interval cleared');
|
|
1298
1017
|
}
|
|
1299
|
-
// Clear the check update timeout
|
|
1300
1018
|
if (this.checkUpdateTimeout) {
|
|
1301
1019
|
clearInterval(this.checkUpdateTimeout);
|
|
1302
1020
|
this.checkUpdateTimeout = undefined;
|
|
1303
1021
|
this.log.debug('Check update timeout cleared');
|
|
1304
1022
|
}
|
|
1305
|
-
// Clear the check update interval
|
|
1306
1023
|
if (this.checkUpdateInterval) {
|
|
1307
1024
|
clearInterval(this.checkUpdateInterval);
|
|
1308
1025
|
this.checkUpdateInterval = undefined;
|
|
1309
1026
|
this.log.debug('Check update interval cleared');
|
|
1310
1027
|
}
|
|
1311
|
-
// Clear the configure timeout
|
|
1312
1028
|
if (this.configureTimeout) {
|
|
1313
1029
|
clearTimeout(this.configureTimeout);
|
|
1314
1030
|
this.configureTimeout = undefined;
|
|
1315
1031
|
this.log.debug('Matterbridge configure timeout cleared');
|
|
1316
1032
|
}
|
|
1317
|
-
// Clear the reachability timeout
|
|
1318
1033
|
if (this.reachabilityTimeout) {
|
|
1319
1034
|
clearTimeout(this.reachabilityTimeout);
|
|
1320
1035
|
this.reachabilityTimeout = undefined;
|
|
1321
1036
|
this.log.debug('Matterbridge reachability timeout cleared');
|
|
1322
1037
|
}
|
|
1323
|
-
// Calling the shutdown method of each plugin and clear the plugins reachability timeout
|
|
1324
1038
|
for (const plugin of this.plugins) {
|
|
1325
1039
|
if (!plugin.enabled || plugin.error)
|
|
1326
1040
|
continue;
|
|
@@ -1331,10 +1045,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
1331
1045
|
this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
|
|
1332
1046
|
}
|
|
1333
1047
|
}
|
|
1334
|
-
// Stop matter server nodes
|
|
1335
1048
|
this.log.notice(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
|
|
1336
1049
|
this.log.debug('Waiting for the MessageExchange to finish...');
|
|
1337
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1050
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1338
1051
|
if (this.bridgeMode === 'bridge') {
|
|
1339
1052
|
if (this.serverNode) {
|
|
1340
1053
|
await this.stopServerNode(this.serverNode);
|
|
@@ -1350,7 +1063,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1350
1063
|
}
|
|
1351
1064
|
}
|
|
1352
1065
|
this.log.notice('Stopped matter server nodes');
|
|
1353
|
-
// Matter commisioning reset
|
|
1354
1066
|
if (message === 'shutting down with reset...') {
|
|
1355
1067
|
this.log.info('Resetting Matterbridge commissioning information...');
|
|
1356
1068
|
await this.matterStorageManager?.createContext('events')?.clearAll();
|
|
@@ -1360,37 +1072,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
1360
1072
|
await this.matterbridgeContext?.clearAll();
|
|
1361
1073
|
this.log.info('Matter storage reset done! Remove the bridge from the controller.');
|
|
1362
1074
|
}
|
|
1363
|
-
|
|
1364
|
-
await this.
|
|
1365
|
-
// Stop the frontend
|
|
1366
|
-
await this.frontend.stop();
|
|
1367
|
-
// Remove the matterfilelogger
|
|
1075
|
+
await withTimeout(this.stopMatterStorage(), 10000, false);
|
|
1076
|
+
await withTimeout(this.frontend.stop(), 10000, false);
|
|
1368
1077
|
try {
|
|
1369
1078
|
Logger.removeLogger('matterfilelogger');
|
|
1370
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1371
1079
|
}
|
|
1372
1080
|
catch (error) {
|
|
1373
|
-
|
|
1081
|
+
this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridgeDirectory, this.matterLoggerFile)}${db}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1374
1082
|
}
|
|
1375
|
-
// Serialize registeredDevices
|
|
1376
1083
|
if (this.nodeStorage && this.nodeContext) {
|
|
1377
|
-
/*
|
|
1378
|
-
TODO: Implement serialization of registered devices in edge mode
|
|
1379
|
-
this.log.info('Saving registered devices...');
|
|
1380
|
-
const serializedRegisteredDevices: SerializedMatterbridgeEndpoint[] = [];
|
|
1381
|
-
this.devices.forEach(async (device) => {
|
|
1382
|
-
const serializedMatterbridgeDevice = MatterbridgeEndpoint.serialize(device);
|
|
1383
|
-
// this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
|
|
1384
|
-
if (serializedMatterbridgeDevice) serializedRegisteredDevices.push(serializedMatterbridgeDevice);
|
|
1385
|
-
});
|
|
1386
|
-
await this.nodeContext.set<SerializedMatterbridgeEndpoint[]>('devices', serializedRegisteredDevices);
|
|
1387
|
-
this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
|
|
1388
|
-
*/
|
|
1389
|
-
// Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
|
|
1390
1084
|
this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
|
|
1391
1085
|
await this.nodeContext.close();
|
|
1392
1086
|
this.nodeContext = undefined;
|
|
1393
|
-
// Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
|
|
1394
1087
|
for (const plugin of this.plugins) {
|
|
1395
1088
|
if (plugin.nodeContext) {
|
|
1396
1089
|
this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
|
|
@@ -1403,63 +1096,45 @@ export class Matterbridge extends EventEmitter {
|
|
|
1403
1096
|
this.nodeStorage = undefined;
|
|
1404
1097
|
}
|
|
1405
1098
|
else {
|
|
1406
|
-
this.log.error('Error
|
|
1099
|
+
this.log.error('Error close the matterbridge node storage and context: nodeStorage or nodeContext not found!');
|
|
1407
1100
|
}
|
|
1408
1101
|
this.plugins.clear();
|
|
1409
1102
|
this.devices.clear();
|
|
1410
|
-
// Factory reset
|
|
1411
1103
|
if (message === 'shutting down with factory reset...') {
|
|
1412
1104
|
try {
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
this.log.info(`Unlinking old matter storage file: ${file}`);
|
|
1416
|
-
await fs.unlink(file);
|
|
1417
|
-
const backup = path.join(this.matterbridgeDirectory, 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.backup.json');
|
|
1418
|
-
this.log.info(`Unlinking old matter storage backup file: ${backup}`);
|
|
1419
|
-
await fs.unlink(backup);
|
|
1420
|
-
}
|
|
1421
|
-
catch (err) {
|
|
1422
|
-
if (err instanceof Error && err.code !== 'ENOENT') {
|
|
1423
|
-
this.log.error(`Error unlinking old matter storage file: ${err}`);
|
|
1424
|
-
}
|
|
1425
|
-
}
|
|
1426
|
-
try {
|
|
1427
|
-
// Delete matter node storage directory with its subdirectories and backup
|
|
1428
|
-
const dir = path.join(this.matterbridgeDirectory, 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
|
|
1429
|
-
this.log.info(`Removing matter node storage directory: ${dir}`);
|
|
1105
|
+
const dir = path.join(this.matterbridgeDirectory, this.matterStorageName);
|
|
1106
|
+
this.log.info(`Removing matter storage directory: ${dir}`);
|
|
1430
1107
|
await fs.rm(dir, { recursive: true });
|
|
1431
|
-
const backup = path.join(this.matterbridgeDirectory,
|
|
1432
|
-
this.log.info(`Removing matter
|
|
1108
|
+
const backup = path.join(this.matterbridgeDirectory, this.matterStorageName + '.backup');
|
|
1109
|
+
this.log.info(`Removing matter storage backup directory: ${backup}`);
|
|
1433
1110
|
await fs.rm(backup, { recursive: true });
|
|
1434
1111
|
}
|
|
1435
|
-
catch (
|
|
1436
|
-
if (
|
|
1437
|
-
this.log.error(`Error removing matter storage directory: ${
|
|
1112
|
+
catch (error) {
|
|
1113
|
+
if (error instanceof Error && error.code !== 'ENOENT') {
|
|
1114
|
+
this.log.error(`Error removing matter storage directory: ${error}`);
|
|
1438
1115
|
}
|
|
1439
1116
|
}
|
|
1440
1117
|
try {
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
this.log.info(`Removing storage directory: ${dir}`);
|
|
1118
|
+
const dir = path.join(this.matterbridgeDirectory, this.nodeStorageName);
|
|
1119
|
+
this.log.info(`Removing matterbridge storage directory: ${dir}`);
|
|
1444
1120
|
await fs.rm(dir, { recursive: true });
|
|
1445
|
-
const backup = path.join(this.matterbridgeDirectory,
|
|
1446
|
-
this.log.info(`Removing storage backup directory: ${backup}`);
|
|
1121
|
+
const backup = path.join(this.matterbridgeDirectory, this.nodeStorageName + '.backup');
|
|
1122
|
+
this.log.info(`Removing matterbridge storage backup directory: ${backup}`);
|
|
1447
1123
|
await fs.rm(backup, { recursive: true });
|
|
1448
1124
|
}
|
|
1449
|
-
catch (
|
|
1450
|
-
if (
|
|
1451
|
-
this.log.error(`Error removing storage directory: ${
|
|
1125
|
+
catch (error) {
|
|
1126
|
+
if (error instanceof Error && error.code !== 'ENOENT') {
|
|
1127
|
+
this.log.error(`Error removing matterbridge storage directory: ${error}`);
|
|
1452
1128
|
}
|
|
1453
1129
|
}
|
|
1454
1130
|
this.log.info('Factory reset done! Remove all paired fabrics from the controllers.');
|
|
1455
1131
|
}
|
|
1456
|
-
|
|
1457
|
-
this.deregisterProcesslHandlers();
|
|
1132
|
+
this.deregisterProcessHandlers();
|
|
1458
1133
|
if (restart) {
|
|
1459
1134
|
if (message === 'updating...') {
|
|
1460
1135
|
this.log.info('Cleanup completed. Updating...');
|
|
1461
1136
|
Matterbridge.instance = undefined;
|
|
1462
|
-
this.emit('update');
|
|
1137
|
+
this.emit('update');
|
|
1463
1138
|
}
|
|
1464
1139
|
else if (message === 'restarting...') {
|
|
1465
1140
|
this.log.info('Cleanup completed. Restarting...');
|
|
@@ -1480,14 +1155,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1480
1155
|
this.log.debug('Cleanup already started...');
|
|
1481
1156
|
}
|
|
1482
1157
|
}
|
|
1483
|
-
/**
|
|
1484
|
-
* Creates and configures the server node for an accessory plugin for a given device.
|
|
1485
|
-
*
|
|
1486
|
-
* @param {RegisteredPlugin} plugin - The plugin to configure.
|
|
1487
|
-
* @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
|
|
1488
|
-
* @param {boolean} [start=false] - Whether to start the server node after adding the device.
|
|
1489
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
|
|
1490
|
-
*/
|
|
1491
1158
|
async createAccessoryPlugin(plugin, device, start = false) {
|
|
1492
1159
|
if (!plugin.locked && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
|
|
1493
1160
|
plugin.locked = true;
|
|
@@ -1501,13 +1168,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1501
1168
|
await this.startServerNode(plugin.serverNode);
|
|
1502
1169
|
}
|
|
1503
1170
|
}
|
|
1504
|
-
/**
|
|
1505
|
-
* Creates and configures the server node for a dynamic plugin.
|
|
1506
|
-
*
|
|
1507
|
-
* @param {RegisteredPlugin} plugin - The plugin to configure.
|
|
1508
|
-
* @param {boolean} [start=false] - Whether to start the server node after adding the aggregator node.
|
|
1509
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the dynamic plugin is created and configured.
|
|
1510
|
-
*/
|
|
1511
1171
|
async createDynamicPlugin(plugin, start = false) {
|
|
1512
1172
|
if (!plugin.locked) {
|
|
1513
1173
|
plugin.locked = true;
|
|
@@ -1520,13 +1180,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1520
1180
|
await this.startServerNode(plugin.serverNode);
|
|
1521
1181
|
}
|
|
1522
1182
|
}
|
|
1523
|
-
/**
|
|
1524
|
-
* Starts the Matterbridge in bridge mode.
|
|
1525
|
-
* @private
|
|
1526
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1527
|
-
*/
|
|
1528
1183
|
async startBridge() {
|
|
1529
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1530
1184
|
if (!this.matterStorageManager)
|
|
1531
1185
|
throw new Error('No storage manager initialized');
|
|
1532
1186
|
if (!this.matterbridgeContext)
|
|
@@ -1565,9 +1219,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1565
1219
|
clearInterval(this.startMatterInterval);
|
|
1566
1220
|
this.startMatterInterval = undefined;
|
|
1567
1221
|
this.log.debug('Cleared startMatterInterval interval for Matterbridge');
|
|
1568
|
-
// Start the Matter server node
|
|
1569
1222
|
this.startServerNode(this.serverNode);
|
|
1570
|
-
// Configure the plugins
|
|
1571
1223
|
this.configureTimeout = setTimeout(async () => {
|
|
1572
1224
|
for (const plugin of this.plugins) {
|
|
1573
1225
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1585,7 +1237,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1585
1237
|
}
|
|
1586
1238
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
1587
1239
|
}, 30 * 1000);
|
|
1588
|
-
// Setting reachability to true
|
|
1589
1240
|
this.reachabilityTimeout = setTimeout(() => {
|
|
1590
1241
|
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1591
1242
|
if (this.aggregatorNode)
|
|
@@ -1594,11 +1245,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1594
1245
|
}, 60 * 1000);
|
|
1595
1246
|
}, 1000);
|
|
1596
1247
|
}
|
|
1597
|
-
/**
|
|
1598
|
-
* Starts the Matterbridge in childbridge mode.
|
|
1599
|
-
* @private
|
|
1600
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1601
|
-
*/
|
|
1602
1248
|
async startChildbridge() {
|
|
1603
1249
|
if (!this.matterStorageManager)
|
|
1604
1250
|
throw new Error('No storage manager initialized');
|
|
@@ -1636,7 +1282,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1636
1282
|
clearInterval(this.startMatterInterval);
|
|
1637
1283
|
this.startMatterInterval = undefined;
|
|
1638
1284
|
this.log.debug('Cleared startMatterInterval interval in childbridge mode');
|
|
1639
|
-
// Configure the plugins
|
|
1640
1285
|
this.configureTimeout = setTimeout(async () => {
|
|
1641
1286
|
for (const plugin of this.plugins) {
|
|
1642
1287
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1673,9 +1318,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1673
1318
|
this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
|
|
1674
1319
|
continue;
|
|
1675
1320
|
}
|
|
1676
|
-
// Start the Matter server node
|
|
1677
1321
|
this.startServerNode(plugin.serverNode);
|
|
1678
|
-
// Setting reachability to true
|
|
1679
1322
|
plugin.reachabilityTimeout = setTimeout(() => {
|
|
1680
1323
|
this.log.info(`Setting reachability to true for ${plg}${plugin.name}${nf} type ${plugin.type} server node ${plugin.serverNode !== undefined} aggregator node ${plugin.aggregatorNode !== undefined} device ${plugin.device !== undefined}`);
|
|
1681
1324
|
if (plugin.type === 'DynamicPlatform' && plugin.aggregatorNode)
|
|
@@ -1685,11 +1328,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1685
1328
|
}
|
|
1686
1329
|
}, 1000);
|
|
1687
1330
|
}
|
|
1688
|
-
/**
|
|
1689
|
-
* Starts the Matterbridge controller.
|
|
1690
|
-
* @private
|
|
1691
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1692
|
-
*/
|
|
1693
1331
|
async startController() {
|
|
1694
1332
|
if (!this.matterStorageManager) {
|
|
1695
1333
|
this.log.error('No storage manager initialized');
|
|
@@ -1704,207 +1342,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1704
1342
|
return;
|
|
1705
1343
|
}
|
|
1706
1344
|
this.log.debug('Starting matterbridge in mode', this.bridgeMode);
|
|
1707
|
-
/*
|
|
1708
|
-
this.matterServer = await this.createMatterServer(this.storageManager);
|
|
1709
|
-
this.log.info('Creating matter commissioning controller');
|
|
1710
|
-
this.commissioningController = new CommissioningController({
|
|
1711
|
-
autoConnect: false,
|
|
1712
|
-
});
|
|
1713
|
-
this.log.info('Adding matter commissioning controller to matter server');
|
|
1714
|
-
await this.matterServer.addCommissioningController(this.commissioningController);
|
|
1715
|
-
|
|
1716
|
-
this.log.info('Starting matter server');
|
|
1717
|
-
await this.matterServer.start();
|
|
1718
|
-
this.log.info('Matter server started');
|
|
1719
|
-
const commissioningOptions: ControllerCommissioningFlowOptions = {
|
|
1720
|
-
regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
|
|
1721
|
-
regulatoryCountryCode: 'XX',
|
|
1722
|
-
};
|
|
1723
|
-
const commissioningController = new CommissioningController({
|
|
1724
|
-
environment: {
|
|
1725
|
-
environment,
|
|
1726
|
-
id: uniqueId,
|
|
1727
|
-
},
|
|
1728
|
-
autoConnect: false, // Do not auto connect to the commissioned nodes
|
|
1729
|
-
adminFabricLabel,
|
|
1730
|
-
});
|
|
1731
|
-
|
|
1732
|
-
if (hasParameter('pairingcode')) {
|
|
1733
|
-
this.log.info('Pairing device with pairingcode:', getParameter('pairingcode'));
|
|
1734
|
-
const pairingCode = getParameter('pairingcode');
|
|
1735
|
-
const ip = this.controllerContext.has('ip') ? this.controllerContext.get<string>('ip') : undefined;
|
|
1736
|
-
const port = this.controllerContext.has('port') ? this.controllerContext.get<number>('port') : undefined;
|
|
1737
|
-
|
|
1738
|
-
let longDiscriminator, setupPin, shortDiscriminator;
|
|
1739
|
-
if (pairingCode !== undefined) {
|
|
1740
|
-
const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
|
|
1741
|
-
shortDiscriminator = pairingCodeCodec.shortDiscriminator;
|
|
1742
|
-
longDiscriminator = undefined;
|
|
1743
|
-
setupPin = pairingCodeCodec.passcode;
|
|
1744
|
-
this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
|
|
1745
|
-
} else {
|
|
1746
|
-
longDiscriminator = await this.controllerContext.get('longDiscriminator', 3840);
|
|
1747
|
-
if (longDiscriminator > 4095) throw new Error('Discriminator value must be less than 4096');
|
|
1748
|
-
setupPin = this.controllerContext.get('pin', 20202021);
|
|
1749
|
-
}
|
|
1750
|
-
if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
|
|
1751
|
-
throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
|
|
1752
|
-
}
|
|
1753
|
-
|
|
1754
|
-
const options = {
|
|
1755
|
-
commissioning: commissioningOptions,
|
|
1756
|
-
discovery: {
|
|
1757
|
-
knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
|
|
1758
|
-
identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
|
|
1759
|
-
},
|
|
1760
|
-
passcode: setupPin,
|
|
1761
|
-
} as NodeCommissioningOptions;
|
|
1762
|
-
this.log.info('Commissioning with options:', options);
|
|
1763
|
-
const nodeId = await this.commissioningController.commissionNode(options);
|
|
1764
|
-
this.log.info(`Commissioning successfully done with nodeId: ${nodeId}`);
|
|
1765
|
-
this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
|
|
1766
|
-
} // (hasParameter('pairingcode'))
|
|
1767
|
-
|
|
1768
|
-
if (hasParameter('unpairall')) {
|
|
1769
|
-
this.log.info('***Commissioning controller unpairing all nodes...');
|
|
1770
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1771
|
-
for (const nodeId of nodeIds) {
|
|
1772
|
-
this.log.info('***Commissioning controller unpairing node:', nodeId);
|
|
1773
|
-
await this.commissioningController.removeNode(nodeId);
|
|
1774
|
-
}
|
|
1775
|
-
return;
|
|
1776
|
-
}
|
|
1777
|
-
|
|
1778
|
-
if (hasParameter('discover')) {
|
|
1779
|
-
// const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
|
|
1780
|
-
// console.log(discover);
|
|
1781
|
-
}
|
|
1782
|
-
|
|
1783
|
-
if (!this.commissioningController.isCommissioned()) {
|
|
1784
|
-
this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
|
|
1785
|
-
return;
|
|
1786
|
-
}
|
|
1787
|
-
|
|
1788
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1789
|
-
this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
|
|
1790
|
-
for (const nodeId of nodeIds) {
|
|
1791
|
-
this.log.info(`***Connecting to commissioned node: ${nodeId}`);
|
|
1792
|
-
|
|
1793
|
-
const node = await this.commissioningController.connectNode(nodeId, {
|
|
1794
|
-
autoSubscribe: false,
|
|
1795
|
-
attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) =>
|
|
1796
|
-
this.log.info(`***Commissioning controller attributeChangedCallback ${peerNodeId}: attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
|
|
1797
|
-
eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) =>
|
|
1798
|
-
this.log.info(`***Commissioning controller eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
|
|
1799
|
-
stateInformationCallback: (peerNodeId, info) => {
|
|
1800
|
-
switch (info) {
|
|
1801
|
-
case NodeStateInformation.Connected:
|
|
1802
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
|
|
1803
|
-
break;
|
|
1804
|
-
case NodeStateInformation.Disconnected:
|
|
1805
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
|
|
1806
|
-
break;
|
|
1807
|
-
case NodeStateInformation.Reconnecting:
|
|
1808
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
|
|
1809
|
-
break;
|
|
1810
|
-
case NodeStateInformation.WaitingForDeviceDiscovery:
|
|
1811
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
|
|
1812
|
-
break;
|
|
1813
|
-
case NodeStateInformation.StructureChanged:
|
|
1814
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
|
|
1815
|
-
break;
|
|
1816
|
-
case NodeStateInformation.Decommissioned:
|
|
1817
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
|
|
1818
|
-
break;
|
|
1819
|
-
default:
|
|
1820
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
|
|
1821
|
-
break;
|
|
1822
|
-
}
|
|
1823
|
-
},
|
|
1824
|
-
});
|
|
1825
|
-
|
|
1826
|
-
node.logStructure();
|
|
1827
|
-
|
|
1828
|
-
// Get the interaction client
|
|
1829
|
-
this.log.info('Getting the interaction client');
|
|
1830
|
-
const interactionClient = await node.getInteractionClient();
|
|
1831
|
-
let cluster;
|
|
1832
|
-
let attributes;
|
|
1833
|
-
|
|
1834
|
-
// Log BasicInformationCluster
|
|
1835
|
-
cluster = BasicInformationCluster;
|
|
1836
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1837
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1838
|
-
});
|
|
1839
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1840
|
-
attributes.forEach((attribute) => {
|
|
1841
|
-
this.log.info(
|
|
1842
|
-
`- endpoint ${or}${attribute.path.endpointId}${nf} cluster ${hk}${getClusterNameById(attribute.path.clusterId)}${nf} (${hk}0x${attribute.path.clusterId.toString(16)}${nf}) attribute ${zb}${attribute.path.attributeName}${nf} (${zb}0x${attribute.path.attributeId.toString(16)}${nf}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`,
|
|
1843
|
-
);
|
|
1844
|
-
});
|
|
1845
|
-
|
|
1846
|
-
// Log PowerSourceCluster
|
|
1847
|
-
cluster = PowerSourceCluster;
|
|
1848
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1849
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1850
|
-
});
|
|
1851
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1852
|
-
attributes.forEach((attribute) => {
|
|
1853
|
-
this.log.info(
|
|
1854
|
-
`- endpoint ${or}${attribute.path.endpointId}${nf} cluster ${hk}${getClusterNameById(attribute.path.clusterId)}${nf} (${hk}0x${attribute.path.clusterId.toString(16)}${nf}) attribute ${zb}${attribute.path.attributeName}${nf} (${zb}0x${attribute.path.attributeId.toString(16)}${nf}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`,
|
|
1855
|
-
);
|
|
1856
|
-
});
|
|
1857
|
-
|
|
1858
|
-
// Log ThreadNetworkDiagnostics
|
|
1859
|
-
cluster = ThreadNetworkDiagnosticsCluster;
|
|
1860
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1861
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1862
|
-
});
|
|
1863
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1864
|
-
attributes.forEach((attribute) => {
|
|
1865
|
-
this.log.info(
|
|
1866
|
-
`- endpoint ${or}${attribute.path.endpointId}${nf} cluster ${hk}${getClusterNameById(attribute.path.clusterId)}${nf} (${hk}0x${attribute.path.clusterId.toString(16)}${nf}) attribute ${zb}${attribute.path.attributeName}${nf} (${zb}0x${attribute.path.attributeId.toString(16)}${nf}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`,
|
|
1867
|
-
);
|
|
1868
|
-
});
|
|
1869
|
-
|
|
1870
|
-
// Log SwitchCluster
|
|
1871
|
-
cluster = SwitchCluster;
|
|
1872
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1873
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1874
|
-
});
|
|
1875
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1876
|
-
attributes.forEach((attribute) => {
|
|
1877
|
-
this.log.info(
|
|
1878
|
-
`- endpoint ${or}${attribute.path.endpointId}${nf} cluster ${hk}${getClusterNameById(attribute.path.clusterId)}${nf} (${hk}0x${attribute.path.clusterId.toString(16)}${nf}) attribute ${zb}${attribute.path.attributeName}${nf} (${zb}0x${attribute.path.attributeId.toString(16)}${nf}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`,
|
|
1879
|
-
);
|
|
1880
|
-
});
|
|
1881
|
-
|
|
1882
|
-
this.log.info('Subscribing to all attributes and events');
|
|
1883
|
-
await node.subscribeAllAttributesAndEvents({
|
|
1884
|
-
ignoreInitialTriggers: false,
|
|
1885
|
-
attributeChangedCallback: ({ path: { nodeId, clusterId, endpointId, attributeName }, version, value }) =>
|
|
1886
|
-
this.log.info(
|
|
1887
|
-
`***${db}Commissioning controller attributeChangedCallback version ${version}: attribute ${BLUE}${nodeId}${db}/${or}${endpointId}${db}/${hk}${getClusterNameById(clusterId)}${db}/${zb}${attributeName}${db} changed to ${typeof value === 'object' ? debugStringify(value ?? { none: true }) : value}`,
|
|
1888
|
-
),
|
|
1889
|
-
eventTriggeredCallback: ({ path: { nodeId, clusterId, endpointId, eventName }, events }) => {
|
|
1890
|
-
this.log.info(
|
|
1891
|
-
`***${db}Commissioning controller eventTriggeredCallback: event ${BLUE}${nodeId}${db}/${or}${endpointId}${db}/${hk}${getClusterNameById(clusterId)}${db}/${zb}${eventName}${db} triggered with ${debugStringify(events ?? { none: true })}`,
|
|
1892
|
-
);
|
|
1893
|
-
},
|
|
1894
|
-
});
|
|
1895
|
-
this.log.info('Subscribed to all attributes and events');
|
|
1896
|
-
}
|
|
1897
|
-
*/
|
|
1898
1345
|
}
|
|
1899
|
-
/** ***********************************************************************************************************************************/
|
|
1900
|
-
/** Matter.js methods */
|
|
1901
|
-
/** ***********************************************************************************************************************************/
|
|
1902
|
-
/**
|
|
1903
|
-
* Starts the matter storage process with name Matterbridge.
|
|
1904
|
-
* @returns {Promise<void>} - A promise that resolves when the storage process is started.
|
|
1905
|
-
*/
|
|
1906
1346
|
async startMatterStorage() {
|
|
1907
|
-
// Setup Matter storage
|
|
1908
1347
|
this.log.info(`Starting matter node storage...`);
|
|
1909
1348
|
this.matterStorageService = this.environment.get(StorageService);
|
|
1910
1349
|
this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
|
|
@@ -1913,25 +1352,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1913
1352
|
this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', bridge.code, this.aggregatorVendorId, this.aggregatorVendorName, this.aggregatorProductId, this.aggregatorProductName);
|
|
1914
1353
|
this.matterbridgeInformation.matterbridgeSerialNumber = await this.matterbridgeContext.get('serialNumber', '');
|
|
1915
1354
|
this.log.info('Matter node storage started');
|
|
1916
|
-
// Backup matter storage since it is created/opened correctly
|
|
1917
1355
|
await this.backupMatterStorage(path.join(this.matterbridgeDirectory, this.matterStorageName), path.join(this.matterbridgeDirectory, this.matterStorageName + '.backup'));
|
|
1918
1356
|
}
|
|
1919
|
-
/**
|
|
1920
|
-
* Makes a backup copy of the specified matter storage directory.
|
|
1921
|
-
*
|
|
1922
|
-
* @param storageName - The name of the storage directory to be backed up.
|
|
1923
|
-
* @param backupName - The name of the backup directory to be created.
|
|
1924
|
-
* @returns {Promise<void>} A promise that resolves when the has been done.
|
|
1925
|
-
*/
|
|
1926
1357
|
async backupMatterStorage(storageName, backupName) {
|
|
1927
1358
|
this.log.info('Creating matter node storage backup...');
|
|
1928
1359
|
await copyDirectory(storageName, backupName);
|
|
1929
1360
|
this.log.info('Created matter node storage backup');
|
|
1930
1361
|
}
|
|
1931
|
-
/**
|
|
1932
|
-
* Stops the matter storage.
|
|
1933
|
-
* @returns {Promise<void>} A promise that resolves when the storage is stopped.
|
|
1934
|
-
*/
|
|
1935
1362
|
async stopMatterStorage() {
|
|
1936
1363
|
this.log.info('Closing matter node storage...');
|
|
1937
1364
|
await this.matterStorageManager?.close();
|
|
@@ -1940,19 +1367,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1940
1367
|
this.matterbridgeContext = undefined;
|
|
1941
1368
|
this.log.info('Matter node storage closed');
|
|
1942
1369
|
}
|
|
1943
|
-
/**
|
|
1944
|
-
* Creates a server node storage context.
|
|
1945
|
-
*
|
|
1946
|
-
* @param {string} pluginName - The name of the plugin.
|
|
1947
|
-
* @param {string} deviceName - The name of the device.
|
|
1948
|
-
* @param {DeviceTypeId} deviceType - The device type of the device.
|
|
1949
|
-
* @param {number} vendorId - The vendor ID.
|
|
1950
|
-
* @param {string} vendorName - The vendor name.
|
|
1951
|
-
* @param {number} productId - The product ID.
|
|
1952
|
-
* @param {string} productName - The product name.
|
|
1953
|
-
* @param {string} [serialNumber] - The serial number of the device (optional).
|
|
1954
|
-
* @returns {Promise<StorageContext>} The storage context for the commissioning server.
|
|
1955
|
-
*/
|
|
1956
1370
|
async createServerNodeContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber) {
|
|
1957
1371
|
const { randomBytes } = await import('node:crypto');
|
|
1958
1372
|
if (!this.matterStorageService)
|
|
@@ -1986,15 +1400,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1986
1400
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1987
1401
|
return storageContext;
|
|
1988
1402
|
}
|
|
1989
|
-
/**
|
|
1990
|
-
* Creates a server node.
|
|
1991
|
-
*
|
|
1992
|
-
* @param {StorageContext} storageContext - The storage context for the server node.
|
|
1993
|
-
* @param {number} [port=5540] - The port number for the server node. Defaults to 5540.
|
|
1994
|
-
* @param {number} [passcode=20242025] - The passcode for the server node. Defaults to 20242025.
|
|
1995
|
-
* @param {number} [discriminator=3850] - The discriminator for the server node. Defaults to 3850.
|
|
1996
|
-
* @returns {Promise<ServerNode<ServerNode.RootEndpoint>>} A promise that resolves to the created server node.
|
|
1997
|
-
*/
|
|
1998
1403
|
async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
|
|
1999
1404
|
const storeId = await storageContext.get('storeId');
|
|
2000
1405
|
this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
|
|
@@ -2004,37 +1409,24 @@ export class Matterbridge extends EventEmitter {
|
|
|
2004
1409
|
this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
|
|
2005
1410
|
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
2006
1411
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
2007
|
-
/**
|
|
2008
|
-
* Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration
|
|
2009
|
-
*/
|
|
2010
1412
|
const serverNode = await ServerNode.create({
|
|
2011
|
-
// Required: Give the Node a unique ID which is used to store the state of this node
|
|
2012
1413
|
id: storeId,
|
|
2013
|
-
// Provide Network relevant configuration like the port
|
|
2014
|
-
// Optional when operating only one device on a host, Default port is 5540
|
|
2015
1414
|
network: {
|
|
2016
1415
|
listeningAddressIpv4: this.ipv4address,
|
|
2017
1416
|
listeningAddressIpv6: this.ipv6address,
|
|
2018
1417
|
port,
|
|
2019
1418
|
},
|
|
2020
|
-
// Provide the certificate for the device
|
|
2021
1419
|
operationalCredentials: {
|
|
2022
1420
|
certification: this.certification,
|
|
2023
1421
|
},
|
|
2024
|
-
// Provide Commissioning relevant settings
|
|
2025
|
-
// Optional for development/testing purposes
|
|
2026
1422
|
commissioning: {
|
|
2027
1423
|
passcode,
|
|
2028
1424
|
discriminator,
|
|
2029
1425
|
},
|
|
2030
|
-
// Provide Node announcement settings
|
|
2031
|
-
// Optional: If Ommitted some development defaults are used
|
|
2032
1426
|
productDescription: {
|
|
2033
1427
|
name: await storageContext.get('deviceName'),
|
|
2034
1428
|
deviceType: DeviceTypeId(await storageContext.get('deviceType')),
|
|
2035
1429
|
},
|
|
2036
|
-
// Provide defaults for the BasicInformation cluster on the Root endpoint
|
|
2037
|
-
// Optional: If Omitted some development defaults are used
|
|
2038
1430
|
basicInformation: {
|
|
2039
1431
|
vendorId: VendorId(await storageContext.get('vendorId')),
|
|
2040
1432
|
vendorName: await storageContext.get('vendorName'),
|
|
@@ -2052,13 +1444,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2052
1444
|
},
|
|
2053
1445
|
});
|
|
2054
1446
|
const sanitizeFabrics = (fabrics, resetSessions = false) => {
|
|
2055
|
-
// New type of fabric information: Record<FabricIndex, ExposedFabricInformation>
|
|
2056
1447
|
const sanitizedFabrics = this.sanitizeFabricInformations(Array.from(Object.values(fabrics)));
|
|
2057
1448
|
this.log.info(`Fabrics: ${debugStringify(sanitizedFabrics)}`);
|
|
2058
1449
|
if (this.bridgeMode === 'bridge') {
|
|
2059
1450
|
this.matterbridgeFabricInformations = sanitizedFabrics;
|
|
2060
1451
|
if (resetSessions)
|
|
2061
|
-
this.matterbridgeSessionInformations = undefined;
|
|
1452
|
+
this.matterbridgeSessionInformations = undefined;
|
|
2062
1453
|
this.matterbridgePaired = true;
|
|
2063
1454
|
}
|
|
2064
1455
|
if (this.bridgeMode === 'childbridge') {
|
|
@@ -2066,19 +1457,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2066
1457
|
if (plugin) {
|
|
2067
1458
|
plugin.fabricInformations = sanitizedFabrics;
|
|
2068
1459
|
if (resetSessions)
|
|
2069
|
-
plugin.sessionInformations = undefined;
|
|
1460
|
+
plugin.sessionInformations = undefined;
|
|
2070
1461
|
plugin.paired = true;
|
|
2071
1462
|
}
|
|
2072
1463
|
}
|
|
2073
1464
|
};
|
|
2074
|
-
/**
|
|
2075
|
-
* This event is triggered when the device is initially commissioned successfully.
|
|
2076
|
-
* This means: It is added to the first fabric.
|
|
2077
|
-
*/
|
|
2078
1465
|
serverNode.lifecycle.commissioned.on(() => this.log.notice(`Server node for ${storeId} was initially commissioned successfully!`));
|
|
2079
|
-
/** This event is triggered when all fabrics are removed from the device, usually it also does a factory reset then. */
|
|
2080
1466
|
serverNode.lifecycle.decommissioned.on(() => this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`));
|
|
2081
|
-
/** This event is triggered when the device went online. This means that it is discoverable in the network. */
|
|
2082
1467
|
serverNode.lifecycle.online.on(async () => {
|
|
2083
1468
|
this.log.notice(`Server node for ${storeId} is online`);
|
|
2084
1469
|
if (!serverNode.lifecycle.isCommissioned) {
|
|
@@ -2147,7 +1532,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2147
1532
|
this.frontend.wssSendRefreshRequired('settings');
|
|
2148
1533
|
this.frontend.wssSendSnackbarMessage(`${storeId} is online`, 5, 'success');
|
|
2149
1534
|
});
|
|
2150
|
-
/** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
|
|
2151
1535
|
serverNode.lifecycle.offline.on(() => {
|
|
2152
1536
|
this.log.notice(`Server node for ${storeId} is offline`);
|
|
2153
1537
|
if (this.bridgeMode === 'bridge') {
|
|
@@ -2171,10 +1555,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2171
1555
|
this.frontend.wssSendRefreshRequired('settings');
|
|
2172
1556
|
this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
|
|
2173
1557
|
});
|
|
2174
|
-
/**
|
|
2175
|
-
* This event is triggered when a fabric is added, removed or updated on the device. Use this if more granular
|
|
2176
|
-
* information is needed.
|
|
2177
|
-
*/
|
|
2178
1558
|
serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
|
|
2179
1559
|
let action = '';
|
|
2180
1560
|
switch (fabricAction) {
|
|
@@ -2208,24 +1588,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
2208
1588
|
}
|
|
2209
1589
|
}
|
|
2210
1590
|
};
|
|
2211
|
-
/**
|
|
2212
|
-
* This event is triggered when an operative new session was opened by a Controller.
|
|
2213
|
-
* It is not triggered for the initial commissioning process, just afterwards for real connections.
|
|
2214
|
-
*/
|
|
2215
1591
|
serverNode.events.sessions.opened.on((session) => {
|
|
2216
1592
|
this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2217
1593
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
2218
1594
|
this.frontend.wssSendRefreshRequired('sessions');
|
|
2219
1595
|
});
|
|
2220
|
-
/**
|
|
2221
|
-
* This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
|
|
2222
|
-
*/
|
|
2223
1596
|
serverNode.events.sessions.closed.on((session) => {
|
|
2224
1597
|
this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2225
1598
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
2226
1599
|
this.frontend.wssSendRefreshRequired('sessions');
|
|
2227
1600
|
});
|
|
2228
|
-
/** This event is triggered when a subscription gets added or removed on an operative session. */
|
|
2229
1601
|
serverNode.events.sessions.subscriptionsChanged.on((session) => {
|
|
2230
1602
|
this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2231
1603
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
@@ -2234,42 +1606,24 @@ export class Matterbridge extends EventEmitter {
|
|
|
2234
1606
|
this.log.info(`Created server node for ${storeId}`);
|
|
2235
1607
|
return serverNode;
|
|
2236
1608
|
}
|
|
2237
|
-
/**
|
|
2238
|
-
* Starts the specified server node.
|
|
2239
|
-
*
|
|
2240
|
-
* @param {ServerNode} [matterServerNode] - The server node to start.
|
|
2241
|
-
* @returns {Promise<void>} A promise that resolves when the server node has started.
|
|
2242
|
-
*/
|
|
2243
1609
|
async startServerNode(matterServerNode) {
|
|
2244
1610
|
if (!matterServerNode)
|
|
2245
1611
|
return;
|
|
2246
1612
|
this.log.notice(`Starting ${matterServerNode.id} server node`);
|
|
2247
1613
|
await matterServerNode.start();
|
|
2248
1614
|
}
|
|
2249
|
-
/**
|
|
2250
|
-
* Stops the specified server node.
|
|
2251
|
-
*
|
|
2252
|
-
* @param {ServerNode} matterServerNode - The server node to stop.
|
|
2253
|
-
* @returns {Promise<void>} A promise that resolves when the server node has stopped.
|
|
2254
|
-
*/
|
|
2255
1615
|
async stopServerNode(matterServerNode) {
|
|
2256
1616
|
if (!matterServerNode)
|
|
2257
1617
|
return;
|
|
2258
1618
|
this.log.notice(`Closing ${matterServerNode.id} server node`);
|
|
2259
1619
|
try {
|
|
2260
|
-
await withTimeout(matterServerNode.close(), 30000);
|
|
1620
|
+
await withTimeout(matterServerNode.close(), 30000);
|
|
2261
1621
|
this.log.info(`Closed ${matterServerNode.id} server node`);
|
|
2262
1622
|
}
|
|
2263
1623
|
catch (error) {
|
|
2264
1624
|
this.log.error(`Failed to close ${matterServerNode.id} server node: ${error instanceof Error ? error.message : error}`);
|
|
2265
1625
|
}
|
|
2266
1626
|
}
|
|
2267
|
-
/**
|
|
2268
|
-
* Advertises the specified server node.
|
|
2269
|
-
*
|
|
2270
|
-
* @param {ServerNode} [matterServerNode] - The server node to advertise.
|
|
2271
|
-
* @returns {Promise<{ qrPairingCode: string, manualPairingCode: string } | undefined>} A promise that resolves to the pairing codes if the server node is advertised, or undefined if not.
|
|
2272
|
-
*/
|
|
2273
1627
|
async advertiseServerNode(matterServerNode) {
|
|
2274
1628
|
if (matterServerNode) {
|
|
2275
1629
|
await matterServerNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
|
|
@@ -2278,45 +1632,24 @@ export class Matterbridge extends EventEmitter {
|
|
|
2278
1632
|
return { qrPairingCode, manualPairingCode };
|
|
2279
1633
|
}
|
|
2280
1634
|
}
|
|
2281
|
-
/**
|
|
2282
|
-
* Stop advertise the specified server node.
|
|
2283
|
-
*
|
|
2284
|
-
* @param {ServerNode} [matterServerNode] - The server node to advertise.
|
|
2285
|
-
* @returns {Promise<void>} A promise that resolves when the server node has stopped advertising.
|
|
2286
|
-
*/
|
|
2287
1635
|
async stopAdvertiseServerNode(matterServerNode) {
|
|
2288
1636
|
if (matterServerNode && matterServerNode.lifecycle.isOnline) {
|
|
2289
1637
|
await matterServerNode.env.get(DeviceCommissioner)?.endCommissioning();
|
|
2290
1638
|
this.log.notice(`Stopped advertising for ${matterServerNode.id}`);
|
|
2291
1639
|
}
|
|
2292
1640
|
}
|
|
2293
|
-
/**
|
|
2294
|
-
* Creates an aggregator node with the specified storage context.
|
|
2295
|
-
*
|
|
2296
|
-
* @param {StorageContext} storageContext - The storage context for the aggregator node.
|
|
2297
|
-
* @returns {Promise<Endpoint<AggregatorEndpoint>>} A promise that resolves to the created aggregator node.
|
|
2298
|
-
*/
|
|
2299
1641
|
async createAggregatorNode(storageContext) {
|
|
2300
|
-
this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator
|
|
1642
|
+
this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator...`);
|
|
2301
1643
|
const aggregatorNode = new Endpoint(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
|
|
2302
1644
|
return aggregatorNode;
|
|
2303
1645
|
}
|
|
2304
|
-
/**
|
|
2305
|
-
* Adds a MatterbridgeEndpoint to the specified plugin.
|
|
2306
|
-
*
|
|
2307
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2308
|
-
* @param {MatterbridgeEndpoint} device - The device to add as a bridged endpoint.
|
|
2309
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been added.
|
|
2310
|
-
*/
|
|
2311
1646
|
async addBridgedEndpoint(pluginName, device) {
|
|
2312
|
-
// Check if the plugin is registered
|
|
2313
1647
|
const plugin = this.plugins.get(pluginName);
|
|
2314
1648
|
if (!plugin) {
|
|
2315
1649
|
this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
|
|
2316
1650
|
return;
|
|
2317
1651
|
}
|
|
2318
1652
|
if (this.bridgeMode === 'bridge') {
|
|
2319
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2320
1653
|
this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
|
|
2321
1654
|
if (!this.aggregatorNode) {
|
|
2322
1655
|
this.log.error('Aggregator node not found for Matterbridge');
|
|
@@ -2333,7 +1666,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2333
1666
|
}
|
|
2334
1667
|
}
|
|
2335
1668
|
else if (this.bridgeMode === 'childbridge') {
|
|
2336
|
-
// Register and add the device to the plugin server node
|
|
2337
1669
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2338
1670
|
try {
|
|
2339
1671
|
this.log.debug(`Creating endpoint ${dev}${device.deviceName}${db} for AccessoryPlatform plugin ${plg}${plugin.name}${db} server node`);
|
|
@@ -2350,12 +1682,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
2350
1682
|
return;
|
|
2351
1683
|
}
|
|
2352
1684
|
}
|
|
2353
|
-
// Register and add the device to the plugin aggregator node
|
|
2354
1685
|
if (plugin.type === 'DynamicPlatform') {
|
|
2355
1686
|
try {
|
|
2356
1687
|
this.log.debug(`Adding bridged endpoint ${dev}${device.deviceName}${db} for DynamicPlatform plugin ${plg}${plugin.name}${db} aggregator node`);
|
|
2357
1688
|
await this.createDynamicPlugin(plugin);
|
|
2358
|
-
// Fast plugins can add another device before the server node is created
|
|
2359
1689
|
await waiter(`createDynamicPlugin(${plugin.name})`, () => plugin.serverNode?.hasParts === true);
|
|
2360
1690
|
if (!plugin.aggregatorNode) {
|
|
2361
1691
|
this.log.error(`Aggregator node not found for plugin ${plg}${plugin.name}${er}`);
|
|
@@ -2375,28 +1705,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
2375
1705
|
plugin.registeredDevices++;
|
|
2376
1706
|
if (plugin.addedDevices !== undefined)
|
|
2377
1707
|
plugin.addedDevices++;
|
|
2378
|
-
// Add the device to the DeviceManager
|
|
2379
1708
|
this.devices.set(device);
|
|
2380
|
-
// Subscribe to the reachable$Changed event
|
|
2381
1709
|
await this.subscribeAttributeChanged(plugin, device);
|
|
2382
1710
|
this.log.info(`Added and registered bridged endpoint (${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
2383
1711
|
}
|
|
2384
|
-
/**
|
|
2385
|
-
* Removes a MatterbridgeEndpoint from the specified plugin.
|
|
2386
|
-
*
|
|
2387
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2388
|
-
* @param {MatterbridgeEndpoint} device - The device to remove as a bridged endpoint.
|
|
2389
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been removed.
|
|
2390
|
-
*/
|
|
2391
1712
|
async removeBridgedEndpoint(pluginName, device) {
|
|
2392
1713
|
this.log.debug(`Removing bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
2393
|
-
// Check if the plugin is registered
|
|
2394
1714
|
const plugin = this.plugins.get(pluginName);
|
|
2395
1715
|
if (!plugin) {
|
|
2396
1716
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
2397
1717
|
return;
|
|
2398
1718
|
}
|
|
2399
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2400
1719
|
if (this.bridgeMode === 'bridge') {
|
|
2401
1720
|
if (!this.aggregatorNode) {
|
|
2402
1721
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
|
|
@@ -2411,7 +1730,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2411
1730
|
}
|
|
2412
1731
|
else if (this.bridgeMode === 'childbridge') {
|
|
2413
1732
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2414
|
-
// Nothing to do here since the server node has no aggregator node but only the device itself
|
|
2415
1733
|
}
|
|
2416
1734
|
else if (plugin.type === 'DynamicPlatform') {
|
|
2417
1735
|
if (!plugin.aggregatorNode) {
|
|
@@ -2426,21 +1744,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2426
1744
|
if (plugin.addedDevices !== undefined)
|
|
2427
1745
|
plugin.addedDevices--;
|
|
2428
1746
|
}
|
|
2429
|
-
// Remove the device from the DeviceManager
|
|
2430
1747
|
this.devices.remove(device);
|
|
2431
1748
|
}
|
|
2432
|
-
/**
|
|
2433
|
-
* Removes all bridged endpoints from the specified plugin.
|
|
2434
|
-
*
|
|
2435
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2436
|
-
* @param {number} [delay=0] - The delay in milliseconds between removing each bridged endpoint (default: 0).
|
|
2437
|
-
* @returns {Promise<void>} A promise that resolves when all bridged endpoints have been removed.
|
|
2438
|
-
*
|
|
2439
|
-
* @remarks
|
|
2440
|
-
* This method iterates through all devices in the DeviceManager and removes each bridged endpoint associated with the specified plugin.
|
|
2441
|
-
* It also applies a delay between each removal if specified.
|
|
2442
|
-
* The delay is useful to allow the controllers to receive a single subscription for each device removed.
|
|
2443
|
-
*/
|
|
2444
1749
|
async removeAllBridgedEndpoints(pluginName, delay = 0) {
|
|
2445
1750
|
this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}${delay > 0 ? ` with delay ${delay} ms` : ''}`);
|
|
2446
1751
|
for (const device of this.devices.array().filter((device) => device.plugin === pluginName)) {
|
|
@@ -2451,25 +1756,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
2451
1756
|
if (delay > 0)
|
|
2452
1757
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2453
1758
|
}
|
|
2454
|
-
/**
|
|
2455
|
-
* Subscribes to the attribute change event for the given device and plugin.
|
|
2456
|
-
* Specifically, it listens for changes in the 'reachable' attribute of the
|
|
2457
|
-
* BridgedDeviceBasicInformationServer cluster server of the bridged device or BasicInformationServer cluster server of server node.
|
|
2458
|
-
*
|
|
2459
|
-
* @param {RegisteredPlugin} plugin - The plugin associated with the device.
|
|
2460
|
-
* @param {MatterbridgeEndpoint} device - The device to subscribe to attribute changes for.
|
|
2461
|
-
* @returns {Promise<void>} A promise that resolves when the subscription is set up.
|
|
2462
|
-
*/
|
|
2463
1759
|
async subscribeAttributeChanged(plugin, device) {
|
|
2464
1760
|
this.log.info(`Subscribing attributes for endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) plugin ${plg}${plugin.name}${nf}`);
|
|
2465
1761
|
if (this.bridgeMode === 'childbridge' && plugin.type === 'AccessoryPlatform' && plugin.serverNode) {
|
|
2466
|
-
/*
|
|
2467
|
-
this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) subscribed to reachable$Changed`);
|
|
2468
|
-
setTimeout(async () => {
|
|
2469
|
-
this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) changed to reachable false`);
|
|
2470
|
-
await plugin.serverNode?.setStateOf(BasicInformationServer, { reachable: false });
|
|
2471
|
-
}, 60000).unref();
|
|
2472
|
-
*/
|
|
2473
1762
|
plugin.serverNode.eventsOf(BasicInformationServer).reachable$Changed?.on((reachable) => {
|
|
2474
1763
|
this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) is ${reachable ? 'reachable' : 'unreachable'}`);
|
|
2475
1764
|
this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, 'BasicInformationServer', 'reachable', reachable);
|
|
@@ -2482,12 +1771,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2482
1771
|
});
|
|
2483
1772
|
}
|
|
2484
1773
|
}
|
|
2485
|
-
/**
|
|
2486
|
-
* Sanitizes the fabric information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2487
|
-
*
|
|
2488
|
-
* @param {ExposedFabricInformation[]} fabricInfo - The array of exposed fabric information objects.
|
|
2489
|
-
* @returns {SanitizedExposedFabricInformation[]} An array of sanitized exposed fabric information objects.
|
|
2490
|
-
*/
|
|
2491
1774
|
sanitizeFabricInformations(fabricInfo) {
|
|
2492
1775
|
return fabricInfo.map((info) => {
|
|
2493
1776
|
return {
|
|
@@ -2501,12 +1784,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2501
1784
|
};
|
|
2502
1785
|
});
|
|
2503
1786
|
}
|
|
2504
|
-
/**
|
|
2505
|
-
* Sanitizes the session information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2506
|
-
*
|
|
2507
|
-
* @param {SessionInformation[]} sessionInfo - The array of session information objects.
|
|
2508
|
-
* @returns {SanitizedSessionInformation[]} An array of sanitized session information objects.
|
|
2509
|
-
*/
|
|
2510
1787
|
sanitizeSessionInformation(sessionInfo) {
|
|
2511
1788
|
return sessionInfo
|
|
2512
1789
|
.filter((session) => session.isPeerActive)
|
|
@@ -2534,20 +1811,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2534
1811
|
};
|
|
2535
1812
|
});
|
|
2536
1813
|
}
|
|
2537
|
-
/**
|
|
2538
|
-
* Sets the reachability of the specified aggregator node bridged devices and trigger.
|
|
2539
|
-
* @param {Endpoint<AggregatorEndpoint>} aggregatorNode - The aggregator node to set the reachability for.
|
|
2540
|
-
* @param {boolean} reachable - A boolean indicating the reachability status to set.
|
|
2541
|
-
*/
|
|
2542
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2543
1814
|
async setAggregatorReachability(aggregatorNode, reachable) {
|
|
2544
|
-
/*
|
|
2545
|
-
for (const child of aggregatorNode.parts) {
|
|
2546
|
-
this.log.debug(`Setting reachability of ${(child as unknown as MatterbridgeEndpoint)?.deviceName} to ${reachable}`);
|
|
2547
|
-
await child.setStateOf(BridgedDeviceBasicInformationServer, { reachable });
|
|
2548
|
-
child.act((agent) => child.eventsOf(BridgedDeviceBasicInformationServer).reachableChanged.emit({ reachableNewValue: true }, agent.context));
|
|
2549
|
-
}
|
|
2550
|
-
*/
|
|
2551
1815
|
}
|
|
2552
1816
|
getVendorIdName = (vendorId) => {
|
|
2553
1817
|
if (!vendorId)
|
|
@@ -2590,29 +1854,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
2590
1854
|
}
|
|
2591
1855
|
return vendorName;
|
|
2592
1856
|
};
|
|
2593
|
-
/**
|
|
2594
|
-
* Spawns a child process with the given command and arguments.
|
|
2595
|
-
* @param {string} command - The command to execute.
|
|
2596
|
-
* @param {string[]} args - The arguments to pass to the command (default: []).
|
|
2597
|
-
* @returns {Promise<boolean>} A promise that resolves when the child process exits successfully, or rejects if there is an error.
|
|
2598
|
-
*/
|
|
2599
1857
|
async spawnCommand(command, args = []) {
|
|
2600
1858
|
const { spawn } = await import('node:child_process');
|
|
2601
|
-
/*
|
|
2602
|
-
npm > npm.cmd on windows
|
|
2603
|
-
cmd.exe ['dir'] on windows
|
|
2604
|
-
await this.spawnCommand('npm', ['install', '-g', 'matterbridge']);
|
|
2605
|
-
*/
|
|
2606
1859
|
const cmdLine = command + ' ' + args.join(' ');
|
|
2607
1860
|
if (process.platform === 'win32' && command === 'npm') {
|
|
2608
|
-
// Must be spawn('cmd.exe', ['/c', 'npm -g install <package>']);
|
|
2609
1861
|
const argstring = 'npm ' + args.join(' ');
|
|
2610
1862
|
args.splice(0, args.length, '/c', argstring);
|
|
2611
1863
|
command = 'cmd.exe';
|
|
2612
1864
|
}
|
|
2613
|
-
// Decide when using sudo on linux
|
|
2614
|
-
// When you need sudo: Spawn stderr: npm error Error: EACCES: permission denied
|
|
2615
|
-
// When you don't need sudo: Failed to start child process "npm install -g matterbridge-eve-door": spawn sudo ENOENT
|
|
2616
1865
|
if (hasParameter('sudo') || (process.platform === 'linux' && command === 'npm' && !hasParameter('docker') && !hasParameter('nosudo'))) {
|
|
2617
1866
|
args.unshift(command);
|
|
2618
1867
|
command = 'sudo';
|
|
@@ -2670,5 +1919,26 @@ export class Matterbridge extends EventEmitter {
|
|
|
2670
1919
|
}
|
|
2671
1920
|
});
|
|
2672
1921
|
}
|
|
1922
|
+
async createDirectory(path, name) {
|
|
1923
|
+
try {
|
|
1924
|
+
await fs.access(path);
|
|
1925
|
+
}
|
|
1926
|
+
catch (err) {
|
|
1927
|
+
if (err instanceof Error) {
|
|
1928
|
+
const nodeErr = err;
|
|
1929
|
+
if (nodeErr.code === 'ENOENT') {
|
|
1930
|
+
try {
|
|
1931
|
+
await fs.mkdir(path, { recursive: true });
|
|
1932
|
+
this.log.info(`Created ${name}: ${path}`);
|
|
1933
|
+
}
|
|
1934
|
+
catch (err) {
|
|
1935
|
+
this.log.error(`Error creating ${name}: ${err}`);
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
else {
|
|
1939
|
+
this.log.error(`Error accessing ${name}: ${err}`);
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
2673
1944
|
}
|
|
2674
|
-
//# sourceMappingURL=matterbridge.js.map
|