matterbridge 3.0.2 → 3.0.3-dev-20250517-bcc5d13
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 +20 -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 +62 -369
- package/dist/helpers.js +0 -49
- 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 +134 -873
- package/dist/matterbridgeAccessoryPlatform.js +0 -34
- package/dist/matterbridgeBehaviors.js +4 -53
- package/dist/matterbridgeDeviceTypes.js +12 -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/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/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: '',
|
|
@@ -95,7 +65,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
95
65
|
shellySysUpdate: false,
|
|
96
66
|
shellyMainUpdate: false,
|
|
97
67
|
profile: getParameter('profile'),
|
|
98
|
-
loggerLevel: "info"
|
|
68
|
+
loggerLevel: "info",
|
|
99
69
|
fileLogger: false,
|
|
100
70
|
matterLoggerLevel: MatterLogLevel.INFO,
|
|
101
71
|
matterFileLogger: false,
|
|
@@ -134,11 +104,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
134
104
|
plugins;
|
|
135
105
|
devices;
|
|
136
106
|
frontend = new Frontend(this);
|
|
137
|
-
// Matterbridge storage
|
|
138
107
|
nodeStorage;
|
|
139
108
|
nodeContext;
|
|
140
109
|
nodeStorageName = 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
|
|
141
|
-
// Cleanup
|
|
142
110
|
hasCleanupStarted = false;
|
|
143
111
|
initialized = false;
|
|
144
112
|
execRunningCount = 0;
|
|
@@ -151,22 +119,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
151
119
|
sigtermHandler;
|
|
152
120
|
exceptionHandler;
|
|
153
121
|
rejectionHandler;
|
|
154
|
-
// Matter environment
|
|
155
122
|
environment = Environment.default;
|
|
156
|
-
// Matter storage
|
|
157
123
|
matterStorageName = 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
|
|
158
124
|
matterStorageService;
|
|
159
125
|
matterStorageManager;
|
|
160
126
|
matterbridgeContext;
|
|
161
127
|
controllerContext;
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
certification; // device certification
|
|
128
|
+
mdnsInterface;
|
|
129
|
+
ipv4address;
|
|
130
|
+
ipv6address;
|
|
131
|
+
port;
|
|
132
|
+
passcode;
|
|
133
|
+
discriminator;
|
|
134
|
+
certification;
|
|
170
135
|
serverNode;
|
|
171
136
|
aggregatorNode;
|
|
172
137
|
aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
|
|
@@ -174,50 +139,21 @@ export class Matterbridge extends EventEmitter {
|
|
|
174
139
|
aggregatorProductId = getIntParameter('productId') ?? 0x8000;
|
|
175
140
|
aggregatorProductName = getParameter('productName') ?? 'Matterbridge aggregator';
|
|
176
141
|
static instance;
|
|
177
|
-
// We load asyncronously so is private
|
|
178
142
|
constructor() {
|
|
179
143
|
super();
|
|
180
144
|
}
|
|
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
145
|
emit(eventName, ...args) {
|
|
190
146
|
return super.emit(eventName, ...args);
|
|
191
147
|
}
|
|
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
148
|
on(eventName, listener) {
|
|
201
149
|
return super.on(eventName, listener);
|
|
202
150
|
}
|
|
203
|
-
/**
|
|
204
|
-
* Retrieves the list of Matterbridge devices.
|
|
205
|
-
* @returns {MatterbridgeEndpoint[]} An array of MatterbridgeDevice objects.
|
|
206
|
-
*/
|
|
207
151
|
getDevices() {
|
|
208
152
|
return this.devices.array();
|
|
209
153
|
}
|
|
210
|
-
/**
|
|
211
|
-
* Retrieves the list of registered plugins.
|
|
212
|
-
* @returns {RegisteredPlugin[]} An array of RegisteredPlugin objects.
|
|
213
|
-
*/
|
|
214
154
|
getPlugins() {
|
|
215
155
|
return this.plugins.array();
|
|
216
156
|
}
|
|
217
|
-
/**
|
|
218
|
-
* Set the logger logLevel for the Matterbridge classes.
|
|
219
|
-
* @param {LogLevel} logLevel The logger logLevel to set.
|
|
220
|
-
*/
|
|
221
157
|
async setLogLevel(logLevel) {
|
|
222
158
|
if (this.log)
|
|
223
159
|
this.log.logLevel = logLevel;
|
|
@@ -231,31 +167,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
231
167
|
for (const plugin of this.plugins) {
|
|
232
168
|
if (!plugin.platform || !plugin.platform.log || !plugin.platform.config)
|
|
233
169
|
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 */;
|
|
170
|
+
plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" : this.log.logLevel;
|
|
171
|
+
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" : this.log.logLevel);
|
|
172
|
+
}
|
|
173
|
+
let callbackLogLevel = "notice";
|
|
174
|
+
if (this.matterbridgeInformation.loggerLevel === "info" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
|
|
175
|
+
callbackLogLevel = "info";
|
|
176
|
+
if (this.matterbridgeInformation.loggerLevel === "debug" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
|
|
177
|
+
callbackLogLevel = "debug";
|
|
243
178
|
AnsiLogger.setGlobalCallback(this.frontend.wssSendMessage.bind(this.frontend), callbackLogLevel);
|
|
244
179
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
245
180
|
}
|
|
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
181
|
static async loadInstance(initialize = false) {
|
|
257
182
|
if (!Matterbridge.instance) {
|
|
258
|
-
// eslint-disable-next-line no-console
|
|
259
183
|
if (hasParameter('debug'))
|
|
260
184
|
console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
|
|
261
185
|
Matterbridge.instance = new Matterbridge();
|
|
@@ -264,14 +188,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
264
188
|
}
|
|
265
189
|
return Matterbridge.instance;
|
|
266
190
|
}
|
|
267
|
-
/**
|
|
268
|
-
* Call cleanup().
|
|
269
|
-
* @deprecated This method is deprecated and is only used for jest tests.
|
|
270
|
-
*
|
|
271
|
-
*/
|
|
272
191
|
async destroyInstance() {
|
|
273
192
|
this.log.info(`Destroy instance...`);
|
|
274
|
-
// Save server nodes to close
|
|
275
193
|
const servers = [];
|
|
276
194
|
if (this.bridgeMode === 'bridge') {
|
|
277
195
|
if (this.serverNode)
|
|
@@ -283,81 +201,71 @@ export class Matterbridge extends EventEmitter {
|
|
|
283
201
|
servers.push(plugin.serverNode);
|
|
284
202
|
}
|
|
285
203
|
}
|
|
286
|
-
// Cleanup
|
|
287
204
|
await this.cleanup('destroying instance...', false);
|
|
288
|
-
// Close servers mdns service
|
|
289
205
|
this.log.info(`Dispose ${servers.length} MdnsService...`);
|
|
290
206
|
for (const server of servers) {
|
|
291
207
|
await server.env.get(MdnsService)[Symbol.asyncDispose]();
|
|
292
208
|
this.log.info(`Closed ${server.id} MdnsService`);
|
|
293
209
|
}
|
|
294
|
-
// Wait for the cleanup to finish
|
|
295
210
|
await new Promise((resolve) => {
|
|
296
211
|
setTimeout(resolve, 1000);
|
|
297
212
|
});
|
|
298
213
|
}
|
|
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
214
|
async initialize() {
|
|
310
|
-
|
|
215
|
+
this.emit('initialize_started');
|
|
311
216
|
if (hasParameter('service'))
|
|
312
217
|
this.restartMode = 'service';
|
|
313
218
|
if (hasParameter('docker'))
|
|
314
219
|
this.restartMode = 'docker';
|
|
315
|
-
// Set the matterbridge directory
|
|
316
220
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
221
|
+
this.matterbridgeInformation.homeDirectory = this.homeDirectory;
|
|
222
|
+
await this.createDirectory(this.homeDirectory, 'Matterbridge Home Directory');
|
|
317
223
|
this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
|
|
318
|
-
|
|
224
|
+
this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
|
|
225
|
+
await this.createDirectory(this.matterbridgeDirectory, 'Matterbridge Directory');
|
|
226
|
+
await this.createDirectory(path.join(this.matterbridgeDirectory, 'certs'), 'Matterbridge Frontend Certificate Directory');
|
|
227
|
+
this.matterbridgePluginDirectory = path.join(this.homeDirectory, 'Matterbridge');
|
|
228
|
+
this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
|
|
229
|
+
await this.createDirectory(this.matterbridgePluginDirectory, 'Matterbridge Plugin Directory');
|
|
230
|
+
this.matterbridgeCertDirectory = path.join(this.homeDirectory, '.mattercert');
|
|
231
|
+
this.matterbridgeInformation.matterbridgeCertDirectory = this.matterbridgeCertDirectory;
|
|
232
|
+
await this.createDirectory(this.matterbridgeCertDirectory, 'Matterbridge Matter Certificate Directory');
|
|
233
|
+
const { fileURLToPath } = await import('node:url');
|
|
234
|
+
const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
235
|
+
this.rootDirectory = path.resolve(currentFileDirectory, '../');
|
|
236
|
+
this.matterbridgeInformation.rootDirectory = this.rootDirectory;
|
|
319
237
|
this.environment.vars.set('log.level', MatterLogLevel.INFO);
|
|
320
238
|
this.environment.vars.set('log.format', MatterLogFormat.ANSI);
|
|
321
239
|
this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, this.matterStorageName));
|
|
322
240
|
this.environment.vars.set('runtime.signals', false);
|
|
323
241
|
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
|
|
242
|
+
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
327
243
|
this.registerProcessHandlers();
|
|
328
|
-
// Initialize nodeStorage and nodeContext
|
|
329
244
|
try {
|
|
330
245
|
this.log.debug(`Creating node storage manager: ${CYAN}${this.nodeStorageName}${db}`);
|
|
331
246
|
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, this.nodeStorageName), writeQueue: false, expiredInterval: undefined, logging: false });
|
|
332
247
|
this.log.debug('Creating node storage context for matterbridge');
|
|
333
248
|
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
249
|
const keys = (await this.nodeStorage?.storage.keys());
|
|
337
250
|
for (const key of keys) {
|
|
338
251
|
this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
|
|
339
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
340
252
|
await this.nodeStorage?.storage.get(key);
|
|
341
253
|
}
|
|
342
254
|
const storages = await this.nodeStorage.getStorageNames();
|
|
343
255
|
for (const storage of storages) {
|
|
344
256
|
this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
|
|
345
257
|
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
258
|
const keys = (await nodeContext?.storage.keys());
|
|
349
259
|
keys.forEach(async (key) => {
|
|
350
260
|
this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
|
|
351
261
|
await nodeContext?.get(key);
|
|
352
262
|
});
|
|
353
263
|
}
|
|
354
|
-
// Creating a backup of the node storage since it is not corrupted
|
|
355
264
|
this.log.debug('Creating node storage backup...');
|
|
356
265
|
await copyDirectory(path.join(this.matterbridgeDirectory, this.nodeStorageName), path.join(this.matterbridgeDirectory, this.nodeStorageName + '.backup'));
|
|
357
266
|
this.log.debug('Created node storage backup');
|
|
358
267
|
}
|
|
359
268
|
catch (error) {
|
|
360
|
-
// Restoring the backup of the node storage since it is corrupted
|
|
361
269
|
this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
|
|
362
270
|
if (hasParameter('norestore')) {
|
|
363
271
|
this.log.fatal(`The matterbridge node storage is corrupted. Parameter -norestore found: exiting...`);
|
|
@@ -372,32 +280,24 @@ export class Matterbridge extends EventEmitter {
|
|
|
372
280
|
this.log.fatal('Fatal error creating node storage manager and context for matterbridge');
|
|
373
281
|
throw new Error('Fatal error creating node storage manager and context for matterbridge');
|
|
374
282
|
}
|
|
375
|
-
// Set the first port to use for the commissioning server (will be incremented in childbridge mode)
|
|
376
283
|
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
284
|
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
285
|
this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator();
|
|
381
|
-
|
|
382
|
-
const pairingFilePath = path.join(this.homeDirectory, '.mattercert', 'pairing.json');
|
|
286
|
+
const pairingFilePath = path.join(this.matterbridgeCertDirectory, 'pairing.json');
|
|
383
287
|
try {
|
|
384
288
|
await fs.access(pairingFilePath, fs.constants.R_OK);
|
|
385
289
|
const pairingFileContent = await fs.readFile(pairingFilePath, 'utf8');
|
|
386
290
|
const pairingFileJson = JSON.parse(pairingFileContent);
|
|
387
|
-
// Override the passcode and discriminator if they are present in the pairing file
|
|
388
291
|
if (pairingFileJson.passcode && pairingFileJson.discriminator) {
|
|
389
292
|
this.passcode = pairingFileJson.passcode;
|
|
390
293
|
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}
|
|
294
|
+
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
295
|
}
|
|
393
|
-
// Set the certification if it is present in the pairing file
|
|
394
296
|
if (pairingFileJson.privateKey && pairingFileJson.certificate && pairingFileJson.intermediateCertificate && pairingFileJson.declaration) {
|
|
395
297
|
const hexStringToUint8Array = (hexString) => {
|
|
396
298
|
const matches = hexString.match(/.{1,2}/g);
|
|
397
299
|
return matches ? new Uint8Array(matches.map((byte) => parseInt(byte, 16))) : new Uint8Array();
|
|
398
300
|
};
|
|
399
|
-
// const hexString = Buffer.from('Test string', 'utf-8').toString('hex');
|
|
400
|
-
// console.log(hexString, Buffer.from(hexStringToUint8Array(hexString)).toString('utf-8'));
|
|
401
301
|
this.certification = {
|
|
402
302
|
privateKey: hexStringToUint8Array(pairingFileJson.privateKey),
|
|
403
303
|
certificate: hexStringToUint8Array(pairingFileJson.certificate),
|
|
@@ -410,44 +310,41 @@ export class Matterbridge extends EventEmitter {
|
|
|
410
310
|
catch (error) {
|
|
411
311
|
this.log.debug(`Pairing file ${CYAN}${pairingFilePath}${db} not found: ${error instanceof Error ? error.message : error}`);
|
|
412
312
|
}
|
|
413
|
-
// Store the passcode, discriminator and port in the node context
|
|
414
313
|
await this.nodeContext.set('matterport', this.port);
|
|
415
314
|
await this.nodeContext.set('matterpasscode', this.passcode);
|
|
416
315
|
await this.nodeContext.set('matterdiscriminator', this.discriminator);
|
|
417
|
-
this.log.debug(`Initializing server node for Matterbridge
|
|
418
|
-
// Set matterbridge logger level (context: matterbridgeLogLevel)
|
|
316
|
+
this.log.debug(`Initializing server node for Matterbridge on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
|
|
419
317
|
if (hasParameter('logger')) {
|
|
420
318
|
const level = getParameter('logger');
|
|
421
319
|
if (level === 'debug') {
|
|
422
|
-
this.log.logLevel = "debug"
|
|
320
|
+
this.log.logLevel = "debug";
|
|
423
321
|
}
|
|
424
322
|
else if (level === 'info') {
|
|
425
|
-
this.log.logLevel = "info"
|
|
323
|
+
this.log.logLevel = "info";
|
|
426
324
|
}
|
|
427
325
|
else if (level === 'notice') {
|
|
428
|
-
this.log.logLevel = "notice"
|
|
326
|
+
this.log.logLevel = "notice";
|
|
429
327
|
}
|
|
430
328
|
else if (level === 'warn') {
|
|
431
|
-
this.log.logLevel = "warn"
|
|
329
|
+
this.log.logLevel = "warn";
|
|
432
330
|
}
|
|
433
331
|
else if (level === 'error') {
|
|
434
|
-
this.log.logLevel = "error"
|
|
332
|
+
this.log.logLevel = "error";
|
|
435
333
|
}
|
|
436
334
|
else if (level === 'fatal') {
|
|
437
|
-
this.log.logLevel = "fatal"
|
|
335
|
+
this.log.logLevel = "fatal";
|
|
438
336
|
}
|
|
439
337
|
else {
|
|
440
338
|
this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
|
|
441
|
-
this.log.logLevel = "info"
|
|
339
|
+
this.log.logLevel = "info";
|
|
442
340
|
}
|
|
443
341
|
}
|
|
444
342
|
else {
|
|
445
|
-
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice"
|
|
343
|
+
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice" : "info");
|
|
446
344
|
}
|
|
447
345
|
this.frontend.logLevel = this.log.logLevel;
|
|
448
346
|
MatterbridgeEndpoint.logLevel = this.log.logLevel;
|
|
449
347
|
this.matterbridgeInformation.loggerLevel = this.log.logLevel;
|
|
450
|
-
// Create the file logger for matterbridge (context: matterbridgeFileLog)
|
|
451
348
|
if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
|
|
452
349
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), this.log.logLevel, true);
|
|
453
350
|
this.matterbridgeInformation.fileLogger = true;
|
|
@@ -456,7 +353,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
456
353
|
this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.matterbridgeInformation.fileLogger}.`);
|
|
457
354
|
if (this.profile !== undefined)
|
|
458
355
|
this.log.debug(`Matterbridge profile: ${this.profile}.`);
|
|
459
|
-
// Set matter.js logger level, format and logger (context: matterLogLevel)
|
|
460
356
|
if (hasParameter('matterlogger')) {
|
|
461
357
|
const level = getParameter('matterlogger');
|
|
462
358
|
if (level === 'debug') {
|
|
@@ -488,7 +384,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
488
384
|
Logger.format = MatterLogFormat.ANSI;
|
|
489
385
|
Logger.setLogger('default', this.createMatterLogger());
|
|
490
386
|
this.matterbridgeInformation.matterLoggerLevel = Logger.defaultLogLevel;
|
|
491
|
-
// Create the file logger for matter.js (context: matterFileLog)
|
|
492
387
|
if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
|
|
493
388
|
this.matterbridgeInformation.matterFileLogger = true;
|
|
494
389
|
Logger.addLogger('matterfilelogger', await this.createMatterFileLogger(path.join(this.matterbridgeDirectory, this.matterLoggerFile), true), {
|
|
@@ -497,7 +392,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
497
392
|
});
|
|
498
393
|
}
|
|
499
394
|
this.log.debug(`Matter logLevel: ${Logger.defaultLogLevel} fileLoger: ${this.matterbridgeInformation.matterFileLogger}.`);
|
|
500
|
-
// Log network interfaces
|
|
501
395
|
const networkInterfaces = os.networkInterfaces();
|
|
502
396
|
const availableAddresses = Object.entries(networkInterfaces);
|
|
503
397
|
const availableInterfaces = Object.keys(networkInterfaces);
|
|
@@ -509,7 +403,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
509
403
|
});
|
|
510
404
|
}
|
|
511
405
|
}
|
|
512
|
-
// Set the interface to use for matter server node mdnsInterface
|
|
513
406
|
if (hasParameter('mdnsinterface')) {
|
|
514
407
|
this.mdnsInterface = getParameter('mdnsinterface');
|
|
515
408
|
}
|
|
@@ -518,7 +411,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
518
411
|
if (this.mdnsInterface === '')
|
|
519
412
|
this.mdnsInterface = undefined;
|
|
520
413
|
}
|
|
521
|
-
// Validate mdnsInterface
|
|
522
414
|
if (this.mdnsInterface) {
|
|
523
415
|
if (!availableInterfaces.includes(this.mdnsInterface)) {
|
|
524
416
|
this.log.error(`Invalid mdnsInterface: ${this.mdnsInterface}. Available interfaces are: ${availableInterfaces.join(', ')}. Using all available interfaces.`);
|
|
@@ -531,7 +423,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
531
423
|
}
|
|
532
424
|
if (this.mdnsInterface)
|
|
533
425
|
this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
|
|
534
|
-
// Set the listeningAddressIpv4 for the matter commissioning server
|
|
535
426
|
if (hasParameter('ipv4address')) {
|
|
536
427
|
this.ipv4address = getParameter('ipv4address');
|
|
537
428
|
}
|
|
@@ -540,7 +431,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
540
431
|
if (this.ipv4address === '')
|
|
541
432
|
this.ipv4address = undefined;
|
|
542
433
|
}
|
|
543
|
-
// Validate ipv4address
|
|
544
434
|
if (this.ipv4address) {
|
|
545
435
|
let isValid = false;
|
|
546
436
|
for (const [ifaceName, ifaces] of availableAddresses) {
|
|
@@ -556,7 +446,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
556
446
|
await this.nodeContext.remove('matteripv4address');
|
|
557
447
|
}
|
|
558
448
|
}
|
|
559
|
-
// Set the listeningAddressIpv6 for the matter commissioning server
|
|
560
449
|
if (hasParameter('ipv6address')) {
|
|
561
450
|
this.ipv6address = getParameter('ipv6address');
|
|
562
451
|
}
|
|
@@ -565,7 +454,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
565
454
|
if (this.ipv6address === '')
|
|
566
455
|
this.ipv6address = undefined;
|
|
567
456
|
}
|
|
568
|
-
// Validate ipv6address
|
|
569
457
|
if (this.ipv6address) {
|
|
570
458
|
let isValid = false;
|
|
571
459
|
for (const [ifaceName, ifaces] of availableAddresses) {
|
|
@@ -586,19 +474,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
586
474
|
await this.nodeContext.remove('matteripv6address');
|
|
587
475
|
}
|
|
588
476
|
}
|
|
589
|
-
// Initialize PluginManager
|
|
590
477
|
this.plugins = new PluginManager(this);
|
|
591
478
|
await this.plugins.loadFromStorage();
|
|
592
479
|
this.plugins.logLevel = this.log.logLevel;
|
|
593
|
-
// Initialize DeviceManager
|
|
594
480
|
this.devices = new DeviceManager(this, this.nodeContext);
|
|
595
481
|
this.devices.logLevel = this.log.logLevel;
|
|
596
|
-
// Get the plugins from node storage and create the plugins node storage contexts
|
|
597
482
|
for (const plugin of this.plugins) {
|
|
598
483
|
const packageJson = await this.plugins.parse(plugin);
|
|
599
484
|
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
485
|
this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
|
|
603
486
|
try {
|
|
604
487
|
await this.spawnCommand('npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose']);
|
|
@@ -620,7 +503,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
620
503
|
await plugin.nodeContext.set('description', plugin.description);
|
|
621
504
|
await plugin.nodeContext.set('author', plugin.author);
|
|
622
505
|
}
|
|
623
|
-
// Log system info and create .matterbridge directory
|
|
624
506
|
await this.logNodeAndSystemInfo();
|
|
625
507
|
this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
|
|
626
508
|
`${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
|
|
@@ -628,7 +510,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
628
510
|
`${hasParameter('controller') ? 'mode controller ' : ''}` +
|
|
629
511
|
`${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
|
|
630
512
|
`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
513
|
const minNodeVersion = 18;
|
|
633
514
|
const nodeVersion = process.versions.node;
|
|
634
515
|
const versionMajor = parseInt(nodeVersion.split('.')[0]);
|
|
@@ -636,15 +517,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
636
517
|
this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
637
518
|
throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
638
519
|
}
|
|
639
|
-
// Parse command line
|
|
640
520
|
await this.parseCommandLine();
|
|
521
|
+
this.emit('initialize_completed');
|
|
641
522
|
this.initialized = true;
|
|
642
523
|
}
|
|
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
524
|
async parseCommandLine() {
|
|
649
525
|
if (hasParameter('help')) {
|
|
650
526
|
this.log.info(`\nUsage: matterbridge [options]\n
|
|
@@ -658,7 +534,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
658
534
|
- ipv6address [address]: set the ipv6 interface address to use for the matter listener (default all interfaces)
|
|
659
535
|
- frontend [port]: start the frontend on the given port (default 8283)
|
|
660
536
|
- logger: set the matterbridge logger level: debug | info | notice | warn | error | fatal (default info)
|
|
537
|
+
- filelogger enable the matterbridge file logger (matterbridge.log)
|
|
661
538
|
- matterlogger: set the matter.js logger level: debug | info | notice | warn | error | fatal (default info)
|
|
539
|
+
- matterfilelogger enable the matter.js file logger (matter.log)
|
|
662
540
|
- reset: remove the commissioning for Matterbridge (bridge mode). Shutdown Matterbridge before using it!
|
|
663
541
|
- factoryreset: remove all commissioning information and reset all internal storages. Shutdown Matterbridge before using it!
|
|
664
542
|
- list: list the registered plugins
|
|
@@ -667,13 +545,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
667
545
|
- sudo: force the use of sudo to install or update packages if the internal logic fails
|
|
668
546
|
- nosudo: force not to use sudo to install or update packages if the internal logic fails
|
|
669
547
|
- norestore: force not to automatically restore the matterbridge node storage and the matter storage from backup if it is corrupted
|
|
548
|
+
- novirtual: disable the creation of the virtual devices Restart, Update and Reboot Matterbridge
|
|
670
549
|
- ssl: enable SSL for the frontend and WebSockerServer (certificates in .matterbridge/certs directory cert.pem, key.pem and ca.pem (optional))
|
|
671
550
|
- vendorId: override the default vendorId 0xfff1
|
|
672
551
|
- vendorName: override the default vendorName "Matterbridge"
|
|
673
552
|
- productId: override the default productId 0x8000
|
|
674
553
|
- productName: override the default productName "Matterbridge aggregator"
|
|
675
554
|
- service: enable the service mode (used in the systemctl configuration file)
|
|
676
|
-
- docker: enable the docker mode (used in the
|
|
555
|
+
- docker: enable the docker mode (used in the docker image)
|
|
677
556
|
- homedir: override the home directory (default: os.homedir())
|
|
678
557
|
- add [plugin path]: register the plugin from the given absolute or relative path
|
|
679
558
|
- add [plugin name]: register the globally installed plugin with the given name
|
|
@@ -763,7 +642,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
763
642
|
this.shutdown = true;
|
|
764
643
|
return;
|
|
765
644
|
}
|
|
766
|
-
// Start the matter storage and create the matterbridge context
|
|
767
645
|
try {
|
|
768
646
|
await this.startMatterStorage();
|
|
769
647
|
}
|
|
@@ -771,14 +649,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
771
649
|
this.log.fatal(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
772
650
|
throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
773
651
|
}
|
|
774
|
-
// Clear the matterbridge context if the reset parameter is set
|
|
775
652
|
if (hasParameter('reset') && getParameter('reset') === undefined) {
|
|
776
653
|
this.initialized = true;
|
|
777
654
|
await this.shutdownProcessAndReset();
|
|
778
655
|
this.shutdown = true;
|
|
779
656
|
return;
|
|
780
657
|
}
|
|
781
|
-
// Clear matterbridge plugin context if the reset parameter is set
|
|
782
658
|
if (hasParameter('reset') && getParameter('reset') !== undefined) {
|
|
783
659
|
this.log.debug(`Reset plugin ${getParameter('reset')}`);
|
|
784
660
|
const plugin = this.plugins.get(getParameter('reset'));
|
|
@@ -788,11 +664,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
788
664
|
this.log.error(`Plugin ${plg}${plugin.name}${er} storageManager not found`);
|
|
789
665
|
}
|
|
790
666
|
else {
|
|
791
|
-
await matterStorageManager
|
|
792
|
-
await matterStorageManager
|
|
793
|
-
await matterStorageManager
|
|
794
|
-
await matterStorageManager
|
|
795
|
-
await matterStorageManager
|
|
667
|
+
await matterStorageManager.createContext('events')?.clearAll();
|
|
668
|
+
await matterStorageManager.createContext('fabrics')?.clearAll();
|
|
669
|
+
await matterStorageManager.createContext('root')?.clearAll();
|
|
670
|
+
await matterStorageManager.createContext('sessions')?.clearAll();
|
|
671
|
+
await matterStorageManager.createContext('persist')?.clearAll();
|
|
796
672
|
this.log.info(`Reset commissionig for plugin ${plg}${plugin.name}${nf} done! Remove the device from the controller.`);
|
|
797
673
|
}
|
|
798
674
|
}
|
|
@@ -803,37 +679,30 @@ export class Matterbridge extends EventEmitter {
|
|
|
803
679
|
this.shutdown = true;
|
|
804
680
|
return;
|
|
805
681
|
}
|
|
806
|
-
// Initialize frontend
|
|
807
682
|
if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
|
|
808
683
|
await this.frontend.start(getIntParameter('frontend'));
|
|
809
|
-
// Check in 30 seconds the latest versions
|
|
810
684
|
this.checkUpdateTimeout = setTimeout(async () => {
|
|
811
685
|
const { checkUpdates } = await import('./update.js');
|
|
812
686
|
checkUpdates(this);
|
|
813
687
|
}, 30 * 1000).unref();
|
|
814
|
-
// Check each 24 hours the latest versions
|
|
815
688
|
this.checkUpdateInterval = setInterval(async () => {
|
|
816
689
|
const { checkUpdates } = await import('./update.js');
|
|
817
690
|
checkUpdates(this);
|
|
818
691
|
}, 12 * 60 * 60 * 1000).unref();
|
|
819
|
-
// Start the matterbridge in mode test
|
|
820
692
|
if (hasParameter('test')) {
|
|
821
693
|
this.bridgeMode = 'bridge';
|
|
822
694
|
MatterbridgeEndpoint.bridgeMode = 'bridge';
|
|
823
695
|
return;
|
|
824
696
|
}
|
|
825
|
-
// Start the matterbridge in mode controller
|
|
826
697
|
if (hasParameter('controller')) {
|
|
827
698
|
this.bridgeMode = 'controller';
|
|
828
699
|
await this.startController();
|
|
829
700
|
return;
|
|
830
701
|
}
|
|
831
|
-
// Check if the bridge mode is set and start matterbridge in bridge mode if not set
|
|
832
702
|
if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
|
|
833
703
|
this.log.info('Setting default matterbridge start mode to bridge');
|
|
834
704
|
await this.nodeContext?.set('bridgeMode', 'bridge');
|
|
835
705
|
}
|
|
836
|
-
// Start matterbridge in bridge mode
|
|
837
706
|
if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
|
|
838
707
|
this.bridgeMode = 'bridge';
|
|
839
708
|
MatterbridgeEndpoint.bridgeMode = 'bridge';
|
|
@@ -841,7 +710,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
841
710
|
await this.startBridge();
|
|
842
711
|
return;
|
|
843
712
|
}
|
|
844
|
-
// Start matterbridge in childbridge mode
|
|
845
713
|
if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
|
|
846
714
|
this.bridgeMode = 'childbridge';
|
|
847
715
|
MatterbridgeEndpoint.bridgeMode = 'childbridge';
|
|
@@ -850,20 +718,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
850
718
|
return;
|
|
851
719
|
}
|
|
852
720
|
}
|
|
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
721
|
async startPlugins() {
|
|
862
|
-
// Check, load and start the plugins
|
|
863
722
|
for (const plugin of this.plugins) {
|
|
864
723
|
plugin.configJson = await this.plugins.loadConfig(plugin);
|
|
865
724
|
plugin.schemaJson = await this.plugins.loadSchema(plugin);
|
|
866
|
-
// Check if the plugin is available
|
|
867
725
|
if (!(await this.plugins.resolve(plugin.path))) {
|
|
868
726
|
this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
|
|
869
727
|
plugin.enabled = false;
|
|
@@ -883,14 +741,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
883
741
|
plugin.addedDevices = undefined;
|
|
884
742
|
plugin.qrPairingCode = undefined;
|
|
885
743
|
plugin.manualPairingCode = undefined;
|
|
886
|
-
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
744
|
+
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
887
745
|
}
|
|
888
746
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
889
747
|
}
|
|
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
748
|
registerProcessHandlers() {
|
|
895
749
|
this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
|
|
896
750
|
process.removeAllListeners('uncaughtException');
|
|
@@ -917,10 +771,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
917
771
|
};
|
|
918
772
|
process.on('SIGTERM', this.sigtermHandler);
|
|
919
773
|
}
|
|
920
|
-
|
|
921
|
-
* Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
|
|
922
|
-
*/
|
|
923
|
-
deregisterProcesslHandlers() {
|
|
774
|
+
deregisterProcessHandlers() {
|
|
924
775
|
this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
|
|
925
776
|
if (this.exceptionHandler)
|
|
926
777
|
process.off('uncaughtException', this.exceptionHandler);
|
|
@@ -936,17 +787,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
936
787
|
process.off('SIGTERM', this.sigtermHandler);
|
|
937
788
|
this.sigtermHandler = undefined;
|
|
938
789
|
}
|
|
939
|
-
/**
|
|
940
|
-
* Logs the node and system information.
|
|
941
|
-
*/
|
|
942
790
|
async logNodeAndSystemInfo() {
|
|
943
|
-
// IP address information
|
|
944
791
|
const networkInterfaces = os.networkInterfaces();
|
|
945
792
|
this.systemInformation.interfaceName = '';
|
|
946
793
|
this.systemInformation.ipv4Address = '';
|
|
947
794
|
this.systemInformation.ipv6Address = '';
|
|
948
795
|
for (const [interfaceName, interfaceDetails] of Object.entries(networkInterfaces)) {
|
|
949
|
-
// this.log.debug(`Checking interface: '${interfaceName}' for '${this.mdnsInterface}'`);
|
|
950
796
|
if (this.mdnsInterface && interfaceName !== this.mdnsInterface)
|
|
951
797
|
continue;
|
|
952
798
|
if (!interfaceDetails) {
|
|
@@ -972,22 +818,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
972
818
|
break;
|
|
973
819
|
}
|
|
974
820
|
}
|
|
975
|
-
// Node information
|
|
976
821
|
this.systemInformation.nodeVersion = process.versions.node;
|
|
977
822
|
const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
|
|
978
823
|
const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
|
|
979
824
|
const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
|
|
980
|
-
// Host system information
|
|
981
825
|
this.systemInformation.hostname = os.hostname();
|
|
982
826
|
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
|
|
827
|
+
this.systemInformation.osType = os.type();
|
|
828
|
+
this.systemInformation.osRelease = os.release();
|
|
829
|
+
this.systemInformation.osPlatform = os.platform();
|
|
830
|
+
this.systemInformation.osArch = os.arch();
|
|
831
|
+
this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
832
|
+
this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
833
|
+
this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
|
|
991
834
|
this.log.debug('Host System Information:');
|
|
992
835
|
this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
|
|
993
836
|
this.log.debug(`- User: ${this.systemInformation.user}`);
|
|
@@ -1003,26 +846,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
1003
846
|
this.log.debug(`- Total Memory: ${this.systemInformation.totalMemory}`);
|
|
1004
847
|
this.log.debug(`- Free Memory: ${this.systemInformation.freeMemory}`);
|
|
1005
848
|
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
849
|
this.log.debug(`Root Directory: ${this.rootDirectory}`);
|
|
1016
|
-
|
|
850
|
+
this.log.debug(`Home Directory: ${this.homeDirectory}`);
|
|
851
|
+
this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
|
|
852
|
+
this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
|
|
853
|
+
this.log.debug(`Matterbridge Matter Certificate Directory: ${this.matterbridgeCertDirectory}`);
|
|
1017
854
|
if (this.nodeContext)
|
|
1018
855
|
this.globalModulesDirectory = this.matterbridgeInformation.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
|
|
1019
|
-
// First run of Matterbridge so the node storage is empty
|
|
1020
856
|
if (this.globalModulesDirectory === '') {
|
|
1021
857
|
try {
|
|
1022
858
|
this.execRunningCount++;
|
|
1023
|
-
this.globalModulesDirectory = await getGlobalNodeModules();
|
|
859
|
+
this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory = await getGlobalNodeModules();
|
|
1024
860
|
this.execRunningCount--;
|
|
1025
|
-
this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory;
|
|
1026
861
|
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
1027
862
|
await this.nodeContext?.set('globalModulesDirectory', this.globalModulesDirectory);
|
|
1028
863
|
}
|
|
@@ -1032,153 +867,53 @@ export class Matterbridge extends EventEmitter {
|
|
|
1032
867
|
}
|
|
1033
868
|
else
|
|
1034
869
|
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
870
|
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.
|
|
871
|
+
this.matterbridgeVersion = this.matterbridgeLatestVersion = this.matterbridgeDevVersion = packageJson.version;
|
|
872
|
+
this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeInformation.matterbridgeLatestVersion = this.matterbridgeInformation.matterbridgeDevVersion = packageJson.version;
|
|
1124
873
|
this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
|
|
1125
|
-
// Matterbridge latest version
|
|
1126
874
|
if (this.nodeContext)
|
|
1127
|
-
this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
|
|
875
|
+
this.matterbridgeLatestVersion = this.matterbridgeInformation.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
|
|
1128
876
|
this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
|
|
1129
|
-
|
|
1130
|
-
|
|
877
|
+
if (this.nodeContext)
|
|
878
|
+
this.matterbridgeDevVersion = this.matterbridgeInformation.matterbridgeDevVersion = await this.nodeContext.get('matterbridgeDevVersion', this.matterbridgeVersion);
|
|
879
|
+
this.log.debug(`Matterbridge Dev Version: ${this.matterbridgeDevVersion}`);
|
|
1131
880
|
const currentDir = process.cwd();
|
|
1132
881
|
this.log.debug(`Current Working Directory: ${currentDir}`);
|
|
1133
|
-
// Command line arguments (excluding 'node' and the script name)
|
|
1134
882
|
const cmdArgs = process.argv.slice(2).join(' ');
|
|
1135
883
|
this.log.debug(`Command Line Arguments: ${cmdArgs}`);
|
|
1136
884
|
}
|
|
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
885
|
createMatterLogger() {
|
|
1143
|
-
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4
|
|
1144
|
-
return (
|
|
886
|
+
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
|
|
887
|
+
return (level, formattedLog) => {
|
|
1145
888
|
const logger = formattedLog.slice(44, 44 + 20).trim();
|
|
1146
889
|
const message = formattedLog.slice(65);
|
|
1147
890
|
matterLogger.logName = logger;
|
|
1148
|
-
switch (
|
|
891
|
+
switch (level) {
|
|
1149
892
|
case MatterLogLevel.DEBUG:
|
|
1150
|
-
matterLogger.log("debug"
|
|
893
|
+
matterLogger.log("debug", message);
|
|
1151
894
|
break;
|
|
1152
895
|
case MatterLogLevel.INFO:
|
|
1153
|
-
matterLogger.log("info"
|
|
896
|
+
matterLogger.log("info", message);
|
|
1154
897
|
break;
|
|
1155
898
|
case MatterLogLevel.NOTICE:
|
|
1156
|
-
matterLogger.log("notice"
|
|
899
|
+
matterLogger.log("notice", message);
|
|
1157
900
|
break;
|
|
1158
901
|
case MatterLogLevel.WARN:
|
|
1159
|
-
matterLogger.log("warn"
|
|
902
|
+
matterLogger.log("warn", message);
|
|
1160
903
|
break;
|
|
1161
904
|
case MatterLogLevel.ERROR:
|
|
1162
|
-
matterLogger.log("error"
|
|
905
|
+
matterLogger.log("error", message);
|
|
1163
906
|
break;
|
|
1164
907
|
case MatterLogLevel.FATAL:
|
|
1165
|
-
matterLogger.log("fatal"
|
|
908
|
+
matterLogger.log("fatal", message);
|
|
1166
909
|
break;
|
|
1167
910
|
default:
|
|
1168
|
-
matterLogger.log("debug"
|
|
911
|
+
matterLogger.log("debug", message);
|
|
1169
912
|
break;
|
|
1170
913
|
}
|
|
1171
914
|
};
|
|
1172
915
|
}
|
|
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
916
|
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
917
|
let fileSize = 0;
|
|
1183
918
|
if (unlink) {
|
|
1184
919
|
try {
|
|
@@ -1188,7 +923,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1188
923
|
this.log.debug(`Error unlinking the log file ${CYAN}${filePath}${db}: ${error instanceof Error ? error.message : error}`);
|
|
1189
924
|
}
|
|
1190
925
|
}
|
|
1191
|
-
return async (
|
|
926
|
+
return async (level, formattedLog) => {
|
|
1192
927
|
if (fileSize > 100000000)
|
|
1193
928
|
return;
|
|
1194
929
|
fileSize += formattedLog.length;
|
|
@@ -1202,7 +937,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1202
937
|
const parts = message.split(' ');
|
|
1203
938
|
const logger = parts[1];
|
|
1204
939
|
const finalMessage = parts.slice(2).join(' ') + os.EOL;
|
|
1205
|
-
switch (
|
|
940
|
+
switch (level) {
|
|
1206
941
|
case MatterLogLevel.DEBUG:
|
|
1207
942
|
await fs.appendFile(filePath, `[${timestamp}] [${logger}] [debug] ${finalMessage}`);
|
|
1208
943
|
break;
|
|
@@ -1227,21 +962,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1227
962
|
}
|
|
1228
963
|
};
|
|
1229
964
|
}
|
|
1230
|
-
/**
|
|
1231
|
-
* Restarts the process by exiting the current instance and loading a new instance.
|
|
1232
|
-
*/
|
|
1233
965
|
async restartProcess() {
|
|
1234
966
|
await this.cleanup('restarting...', true);
|
|
1235
967
|
}
|
|
1236
|
-
/**
|
|
1237
|
-
* Shut down the process by exiting the current process.
|
|
1238
|
-
*/
|
|
1239
968
|
async shutdownProcess() {
|
|
1240
969
|
await this.cleanup('shutting down...', false);
|
|
1241
970
|
}
|
|
1242
|
-
/**
|
|
1243
|
-
* Update matterbridge and and shut down the process.
|
|
1244
|
-
*/
|
|
1245
971
|
async updateProcess() {
|
|
1246
972
|
this.log.info('Updating matterbridge...');
|
|
1247
973
|
try {
|
|
@@ -1254,73 +980,52 @@ export class Matterbridge extends EventEmitter {
|
|
|
1254
980
|
this.frontend.wssSendRestartRequired();
|
|
1255
981
|
await this.cleanup('updating...', false);
|
|
1256
982
|
}
|
|
1257
|
-
/**
|
|
1258
|
-
* Unregister all devices and shut down the process.
|
|
1259
|
-
*/
|
|
1260
983
|
async unregisterAndShutdownProcess() {
|
|
1261
984
|
this.log.info('Unregistering all devices and shutting down...');
|
|
1262
985
|
for (const plugin of this.plugins) {
|
|
1263
986
|
await this.removeAllBridgedEndpoints(plugin.name, 250);
|
|
1264
987
|
}
|
|
1265
988
|
this.log.debug('Waiting for the MessageExchange to finish...');
|
|
1266
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
989
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1267
990
|
this.log.debug('Cleaning up and shutting down...');
|
|
1268
991
|
await this.cleanup('unregistered all devices and shutting down...', false);
|
|
1269
992
|
}
|
|
1270
|
-
/**
|
|
1271
|
-
* Reset commissioning and shut down the process.
|
|
1272
|
-
*/
|
|
1273
993
|
async shutdownProcessAndReset() {
|
|
1274
994
|
await this.cleanup('shutting down with reset...', false);
|
|
1275
995
|
}
|
|
1276
|
-
/**
|
|
1277
|
-
* Factory reset and shut down the process.
|
|
1278
|
-
*/
|
|
1279
996
|
async shutdownProcessAndFactoryReset() {
|
|
1280
997
|
await this.cleanup('shutting down with factory reset...', false);
|
|
1281
998
|
}
|
|
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
999
|
async cleanup(message, restart = false) {
|
|
1289
1000
|
if (this.initialized && !this.hasCleanupStarted) {
|
|
1290
1001
|
this.emit('cleanup_started');
|
|
1291
1002
|
this.hasCleanupStarted = true;
|
|
1292
1003
|
this.log.info(message);
|
|
1293
|
-
// Clear the start matter interval
|
|
1294
1004
|
if (this.startMatterInterval) {
|
|
1295
1005
|
clearInterval(this.startMatterInterval);
|
|
1296
1006
|
this.startMatterInterval = undefined;
|
|
1297
1007
|
this.log.debug('Start matter interval cleared');
|
|
1298
1008
|
}
|
|
1299
|
-
// Clear the check update timeout
|
|
1300
1009
|
if (this.checkUpdateTimeout) {
|
|
1301
1010
|
clearInterval(this.checkUpdateTimeout);
|
|
1302
1011
|
this.checkUpdateTimeout = undefined;
|
|
1303
1012
|
this.log.debug('Check update timeout cleared');
|
|
1304
1013
|
}
|
|
1305
|
-
// Clear the check update interval
|
|
1306
1014
|
if (this.checkUpdateInterval) {
|
|
1307
1015
|
clearInterval(this.checkUpdateInterval);
|
|
1308
1016
|
this.checkUpdateInterval = undefined;
|
|
1309
1017
|
this.log.debug('Check update interval cleared');
|
|
1310
1018
|
}
|
|
1311
|
-
// Clear the configure timeout
|
|
1312
1019
|
if (this.configureTimeout) {
|
|
1313
1020
|
clearTimeout(this.configureTimeout);
|
|
1314
1021
|
this.configureTimeout = undefined;
|
|
1315
1022
|
this.log.debug('Matterbridge configure timeout cleared');
|
|
1316
1023
|
}
|
|
1317
|
-
// Clear the reachability timeout
|
|
1318
1024
|
if (this.reachabilityTimeout) {
|
|
1319
1025
|
clearTimeout(this.reachabilityTimeout);
|
|
1320
1026
|
this.reachabilityTimeout = undefined;
|
|
1321
1027
|
this.log.debug('Matterbridge reachability timeout cleared');
|
|
1322
1028
|
}
|
|
1323
|
-
// Calling the shutdown method of each plugin and clear the plugins reachability timeout
|
|
1324
1029
|
for (const plugin of this.plugins) {
|
|
1325
1030
|
if (!plugin.enabled || plugin.error)
|
|
1326
1031
|
continue;
|
|
@@ -1331,10 +1036,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
1331
1036
|
this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
|
|
1332
1037
|
}
|
|
1333
1038
|
}
|
|
1334
|
-
// Stop matter server nodes
|
|
1335
1039
|
this.log.notice(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
|
|
1336
1040
|
this.log.debug('Waiting for the MessageExchange to finish...');
|
|
1337
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1041
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1338
1042
|
if (this.bridgeMode === 'bridge') {
|
|
1339
1043
|
if (this.serverNode) {
|
|
1340
1044
|
await this.stopServerNode(this.serverNode);
|
|
@@ -1350,7 +1054,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1350
1054
|
}
|
|
1351
1055
|
}
|
|
1352
1056
|
this.log.notice('Stopped matter server nodes');
|
|
1353
|
-
// Matter commisioning reset
|
|
1354
1057
|
if (message === 'shutting down with reset...') {
|
|
1355
1058
|
this.log.info('Resetting Matterbridge commissioning information...');
|
|
1356
1059
|
await this.matterStorageManager?.createContext('events')?.clearAll();
|
|
@@ -1360,37 +1063,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
1360
1063
|
await this.matterbridgeContext?.clearAll();
|
|
1361
1064
|
this.log.info('Matter storage reset done! Remove the bridge from the controller.');
|
|
1362
1065
|
}
|
|
1363
|
-
|
|
1364
|
-
await this.
|
|
1365
|
-
// Stop the frontend
|
|
1366
|
-
await this.frontend.stop();
|
|
1367
|
-
// Remove the matterfilelogger
|
|
1066
|
+
await withTimeout(this.stopMatterStorage(), 10000, false);
|
|
1067
|
+
await withTimeout(this.frontend.stop(), 10000, false);
|
|
1368
1068
|
try {
|
|
1369
1069
|
Logger.removeLogger('matterfilelogger');
|
|
1370
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1371
1070
|
}
|
|
1372
1071
|
catch (error) {
|
|
1373
|
-
|
|
1072
|
+
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
1073
|
}
|
|
1375
|
-
// Serialize registeredDevices
|
|
1376
1074
|
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
1075
|
this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
|
|
1391
1076
|
await this.nodeContext.close();
|
|
1392
1077
|
this.nodeContext = undefined;
|
|
1393
|
-
// Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
|
|
1394
1078
|
for (const plugin of this.plugins) {
|
|
1395
1079
|
if (plugin.nodeContext) {
|
|
1396
1080
|
this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
|
|
@@ -1403,63 +1087,45 @@ export class Matterbridge extends EventEmitter {
|
|
|
1403
1087
|
this.nodeStorage = undefined;
|
|
1404
1088
|
}
|
|
1405
1089
|
else {
|
|
1406
|
-
this.log.error('Error
|
|
1090
|
+
this.log.error('Error close the matterbridge node storage and context: nodeStorage or nodeContext not found!');
|
|
1407
1091
|
}
|
|
1408
1092
|
this.plugins.clear();
|
|
1409
1093
|
this.devices.clear();
|
|
1410
|
-
// Factory reset
|
|
1411
1094
|
if (message === 'shutting down with factory reset...') {
|
|
1412
1095
|
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}`);
|
|
1096
|
+
const dir = path.join(this.matterbridgeDirectory, this.matterStorageName);
|
|
1097
|
+
this.log.info(`Removing matter storage directory: ${dir}`);
|
|
1430
1098
|
await fs.rm(dir, { recursive: true });
|
|
1431
|
-
const backup = path.join(this.matterbridgeDirectory,
|
|
1432
|
-
this.log.info(`Removing matter
|
|
1099
|
+
const backup = path.join(this.matterbridgeDirectory, this.matterStorageName + '.backup');
|
|
1100
|
+
this.log.info(`Removing matter storage backup directory: ${backup}`);
|
|
1433
1101
|
await fs.rm(backup, { recursive: true });
|
|
1434
1102
|
}
|
|
1435
|
-
catch (
|
|
1436
|
-
if (
|
|
1437
|
-
this.log.error(`Error removing matter storage directory: ${
|
|
1103
|
+
catch (error) {
|
|
1104
|
+
if (error instanceof Error && error.code !== 'ENOENT') {
|
|
1105
|
+
this.log.error(`Error removing matter storage directory: ${error}`);
|
|
1438
1106
|
}
|
|
1439
1107
|
}
|
|
1440
1108
|
try {
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
this.log.info(`Removing storage directory: ${dir}`);
|
|
1109
|
+
const dir = path.join(this.matterbridgeDirectory, this.nodeStorageName);
|
|
1110
|
+
this.log.info(`Removing matterbridge storage directory: ${dir}`);
|
|
1444
1111
|
await fs.rm(dir, { recursive: true });
|
|
1445
|
-
const backup = path.join(this.matterbridgeDirectory,
|
|
1446
|
-
this.log.info(`Removing storage backup directory: ${backup}`);
|
|
1112
|
+
const backup = path.join(this.matterbridgeDirectory, this.nodeStorageName + '.backup');
|
|
1113
|
+
this.log.info(`Removing matterbridge storage backup directory: ${backup}`);
|
|
1447
1114
|
await fs.rm(backup, { recursive: true });
|
|
1448
1115
|
}
|
|
1449
|
-
catch (
|
|
1450
|
-
if (
|
|
1451
|
-
this.log.error(`Error removing storage directory: ${
|
|
1116
|
+
catch (error) {
|
|
1117
|
+
if (error instanceof Error && error.code !== 'ENOENT') {
|
|
1118
|
+
this.log.error(`Error removing matterbridge storage directory: ${error}`);
|
|
1452
1119
|
}
|
|
1453
1120
|
}
|
|
1454
1121
|
this.log.info('Factory reset done! Remove all paired fabrics from the controllers.');
|
|
1455
1122
|
}
|
|
1456
|
-
|
|
1457
|
-
this.deregisterProcesslHandlers();
|
|
1123
|
+
this.deregisterProcessHandlers();
|
|
1458
1124
|
if (restart) {
|
|
1459
1125
|
if (message === 'updating...') {
|
|
1460
1126
|
this.log.info('Cleanup completed. Updating...');
|
|
1461
1127
|
Matterbridge.instance = undefined;
|
|
1462
|
-
this.emit('update');
|
|
1128
|
+
this.emit('update');
|
|
1463
1129
|
}
|
|
1464
1130
|
else if (message === 'restarting...') {
|
|
1465
1131
|
this.log.info('Cleanup completed. Restarting...');
|
|
@@ -1480,14 +1146,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1480
1146
|
this.log.debug('Cleanup already started...');
|
|
1481
1147
|
}
|
|
1482
1148
|
}
|
|
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
1149
|
async createAccessoryPlugin(plugin, device, start = false) {
|
|
1492
1150
|
if (!plugin.locked && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
|
|
1493
1151
|
plugin.locked = true;
|
|
@@ -1501,13 +1159,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1501
1159
|
await this.startServerNode(plugin.serverNode);
|
|
1502
1160
|
}
|
|
1503
1161
|
}
|
|
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
1162
|
async createDynamicPlugin(plugin, start = false) {
|
|
1512
1163
|
if (!plugin.locked) {
|
|
1513
1164
|
plugin.locked = true;
|
|
@@ -1520,13 +1171,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1520
1171
|
await this.startServerNode(plugin.serverNode);
|
|
1521
1172
|
}
|
|
1522
1173
|
}
|
|
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
1174
|
async startBridge() {
|
|
1529
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1530
1175
|
if (!this.matterStorageManager)
|
|
1531
1176
|
throw new Error('No storage manager initialized');
|
|
1532
1177
|
if (!this.matterbridgeContext)
|
|
@@ -1565,9 +1210,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1565
1210
|
clearInterval(this.startMatterInterval);
|
|
1566
1211
|
this.startMatterInterval = undefined;
|
|
1567
1212
|
this.log.debug('Cleared startMatterInterval interval for Matterbridge');
|
|
1568
|
-
// Start the Matter server node
|
|
1569
1213
|
this.startServerNode(this.serverNode);
|
|
1570
|
-
// Configure the plugins
|
|
1571
1214
|
this.configureTimeout = setTimeout(async () => {
|
|
1572
1215
|
for (const plugin of this.plugins) {
|
|
1573
1216
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1585,7 +1228,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1585
1228
|
}
|
|
1586
1229
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
1587
1230
|
}, 30 * 1000);
|
|
1588
|
-
// Setting reachability to true
|
|
1589
1231
|
this.reachabilityTimeout = setTimeout(() => {
|
|
1590
1232
|
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1591
1233
|
if (this.aggregatorNode)
|
|
@@ -1594,11 +1236,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1594
1236
|
}, 60 * 1000);
|
|
1595
1237
|
}, 1000);
|
|
1596
1238
|
}
|
|
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
1239
|
async startChildbridge() {
|
|
1603
1240
|
if (!this.matterStorageManager)
|
|
1604
1241
|
throw new Error('No storage manager initialized');
|
|
@@ -1636,7 +1273,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1636
1273
|
clearInterval(this.startMatterInterval);
|
|
1637
1274
|
this.startMatterInterval = undefined;
|
|
1638
1275
|
this.log.debug('Cleared startMatterInterval interval in childbridge mode');
|
|
1639
|
-
// Configure the plugins
|
|
1640
1276
|
this.configureTimeout = setTimeout(async () => {
|
|
1641
1277
|
for (const plugin of this.plugins) {
|
|
1642
1278
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1673,9 +1309,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1673
1309
|
this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
|
|
1674
1310
|
continue;
|
|
1675
1311
|
}
|
|
1676
|
-
// Start the Matter server node
|
|
1677
1312
|
this.startServerNode(plugin.serverNode);
|
|
1678
|
-
// Setting reachability to true
|
|
1679
1313
|
plugin.reachabilityTimeout = setTimeout(() => {
|
|
1680
1314
|
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
1315
|
if (plugin.type === 'DynamicPlatform' && plugin.aggregatorNode)
|
|
@@ -1685,11 +1319,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1685
1319
|
}
|
|
1686
1320
|
}, 1000);
|
|
1687
1321
|
}
|
|
1688
|
-
/**
|
|
1689
|
-
* Starts the Matterbridge controller.
|
|
1690
|
-
* @private
|
|
1691
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1692
|
-
*/
|
|
1693
1322
|
async startController() {
|
|
1694
1323
|
if (!this.matterStorageManager) {
|
|
1695
1324
|
this.log.error('No storage manager initialized');
|
|
@@ -1704,207 +1333,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1704
1333
|
return;
|
|
1705
1334
|
}
|
|
1706
1335
|
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
1336
|
}
|
|
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
1337
|
async startMatterStorage() {
|
|
1907
|
-
// Setup Matter storage
|
|
1908
1338
|
this.log.info(`Starting matter node storage...`);
|
|
1909
1339
|
this.matterStorageService = this.environment.get(StorageService);
|
|
1910
1340
|
this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
|
|
@@ -1913,25 +1343,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1913
1343
|
this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', bridge.code, this.aggregatorVendorId, this.aggregatorVendorName, this.aggregatorProductId, this.aggregatorProductName);
|
|
1914
1344
|
this.matterbridgeInformation.matterbridgeSerialNumber = await this.matterbridgeContext.get('serialNumber', '');
|
|
1915
1345
|
this.log.info('Matter node storage started');
|
|
1916
|
-
// Backup matter storage since it is created/opened correctly
|
|
1917
1346
|
await this.backupMatterStorage(path.join(this.matterbridgeDirectory, this.matterStorageName), path.join(this.matterbridgeDirectory, this.matterStorageName + '.backup'));
|
|
1918
1347
|
}
|
|
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
1348
|
async backupMatterStorage(storageName, backupName) {
|
|
1927
1349
|
this.log.info('Creating matter node storage backup...');
|
|
1928
1350
|
await copyDirectory(storageName, backupName);
|
|
1929
1351
|
this.log.info('Created matter node storage backup');
|
|
1930
1352
|
}
|
|
1931
|
-
/**
|
|
1932
|
-
* Stops the matter storage.
|
|
1933
|
-
* @returns {Promise<void>} A promise that resolves when the storage is stopped.
|
|
1934
|
-
*/
|
|
1935
1353
|
async stopMatterStorage() {
|
|
1936
1354
|
this.log.info('Closing matter node storage...');
|
|
1937
1355
|
await this.matterStorageManager?.close();
|
|
@@ -1940,19 +1358,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1940
1358
|
this.matterbridgeContext = undefined;
|
|
1941
1359
|
this.log.info('Matter node storage closed');
|
|
1942
1360
|
}
|
|
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
1361
|
async createServerNodeContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber) {
|
|
1957
1362
|
const { randomBytes } = await import('node:crypto');
|
|
1958
1363
|
if (!this.matterStorageService)
|
|
@@ -1986,15 +1391,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1986
1391
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1987
1392
|
return storageContext;
|
|
1988
1393
|
}
|
|
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
1394
|
async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
|
|
1999
1395
|
const storeId = await storageContext.get('storeId');
|
|
2000
1396
|
this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
|
|
@@ -2004,37 +1400,24 @@ export class Matterbridge extends EventEmitter {
|
|
|
2004
1400
|
this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
|
|
2005
1401
|
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
2006
1402
|
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
1403
|
const serverNode = await ServerNode.create({
|
|
2011
|
-
// Required: Give the Node a unique ID which is used to store the state of this node
|
|
2012
1404
|
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
1405
|
network: {
|
|
2016
1406
|
listeningAddressIpv4: this.ipv4address,
|
|
2017
1407
|
listeningAddressIpv6: this.ipv6address,
|
|
2018
1408
|
port,
|
|
2019
1409
|
},
|
|
2020
|
-
// Provide the certificate for the device
|
|
2021
1410
|
operationalCredentials: {
|
|
2022
1411
|
certification: this.certification,
|
|
2023
1412
|
},
|
|
2024
|
-
// Provide Commissioning relevant settings
|
|
2025
|
-
// Optional for development/testing purposes
|
|
2026
1413
|
commissioning: {
|
|
2027
1414
|
passcode,
|
|
2028
1415
|
discriminator,
|
|
2029
1416
|
},
|
|
2030
|
-
// Provide Node announcement settings
|
|
2031
|
-
// Optional: If Ommitted some development defaults are used
|
|
2032
1417
|
productDescription: {
|
|
2033
1418
|
name: await storageContext.get('deviceName'),
|
|
2034
1419
|
deviceType: DeviceTypeId(await storageContext.get('deviceType')),
|
|
2035
1420
|
},
|
|
2036
|
-
// Provide defaults for the BasicInformation cluster on the Root endpoint
|
|
2037
|
-
// Optional: If Omitted some development defaults are used
|
|
2038
1421
|
basicInformation: {
|
|
2039
1422
|
vendorId: VendorId(await storageContext.get('vendorId')),
|
|
2040
1423
|
vendorName: await storageContext.get('vendorName'),
|
|
@@ -2052,13 +1435,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2052
1435
|
},
|
|
2053
1436
|
});
|
|
2054
1437
|
const sanitizeFabrics = (fabrics, resetSessions = false) => {
|
|
2055
|
-
// New type of fabric information: Record<FabricIndex, ExposedFabricInformation>
|
|
2056
1438
|
const sanitizedFabrics = this.sanitizeFabricInformations(Array.from(Object.values(fabrics)));
|
|
2057
1439
|
this.log.info(`Fabrics: ${debugStringify(sanitizedFabrics)}`);
|
|
2058
1440
|
if (this.bridgeMode === 'bridge') {
|
|
2059
1441
|
this.matterbridgeFabricInformations = sanitizedFabrics;
|
|
2060
1442
|
if (resetSessions)
|
|
2061
|
-
this.matterbridgeSessionInformations = undefined;
|
|
1443
|
+
this.matterbridgeSessionInformations = undefined;
|
|
2062
1444
|
this.matterbridgePaired = true;
|
|
2063
1445
|
}
|
|
2064
1446
|
if (this.bridgeMode === 'childbridge') {
|
|
@@ -2066,19 +1448,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2066
1448
|
if (plugin) {
|
|
2067
1449
|
plugin.fabricInformations = sanitizedFabrics;
|
|
2068
1450
|
if (resetSessions)
|
|
2069
|
-
plugin.sessionInformations = undefined;
|
|
1451
|
+
plugin.sessionInformations = undefined;
|
|
2070
1452
|
plugin.paired = true;
|
|
2071
1453
|
}
|
|
2072
1454
|
}
|
|
2073
1455
|
};
|
|
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
1456
|
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
1457
|
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
1458
|
serverNode.lifecycle.online.on(async () => {
|
|
2083
1459
|
this.log.notice(`Server node for ${storeId} is online`);
|
|
2084
1460
|
if (!serverNode.lifecycle.isCommissioned) {
|
|
@@ -2147,7 +1523,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2147
1523
|
this.frontend.wssSendRefreshRequired('settings');
|
|
2148
1524
|
this.frontend.wssSendSnackbarMessage(`${storeId} is online`, 5, 'success');
|
|
2149
1525
|
});
|
|
2150
|
-
/** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
|
|
2151
1526
|
serverNode.lifecycle.offline.on(() => {
|
|
2152
1527
|
this.log.notice(`Server node for ${storeId} is offline`);
|
|
2153
1528
|
if (this.bridgeMode === 'bridge') {
|
|
@@ -2171,10 +1546,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2171
1546
|
this.frontend.wssSendRefreshRequired('settings');
|
|
2172
1547
|
this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
|
|
2173
1548
|
});
|
|
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
1549
|
serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
|
|
2179
1550
|
let action = '';
|
|
2180
1551
|
switch (fabricAction) {
|
|
@@ -2208,24 +1579,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
2208
1579
|
}
|
|
2209
1580
|
}
|
|
2210
1581
|
};
|
|
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
1582
|
serverNode.events.sessions.opened.on((session) => {
|
|
2216
1583
|
this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2217
1584
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
2218
1585
|
this.frontend.wssSendRefreshRequired('sessions');
|
|
2219
1586
|
});
|
|
2220
|
-
/**
|
|
2221
|
-
* This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
|
|
2222
|
-
*/
|
|
2223
1587
|
serverNode.events.sessions.closed.on((session) => {
|
|
2224
1588
|
this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2225
1589
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
2226
1590
|
this.frontend.wssSendRefreshRequired('sessions');
|
|
2227
1591
|
});
|
|
2228
|
-
/** This event is triggered when a subscription gets added or removed on an operative session. */
|
|
2229
1592
|
serverNode.events.sessions.subscriptionsChanged.on((session) => {
|
|
2230
1593
|
this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2231
1594
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
@@ -2234,42 +1597,24 @@ export class Matterbridge extends EventEmitter {
|
|
|
2234
1597
|
this.log.info(`Created server node for ${storeId}`);
|
|
2235
1598
|
return serverNode;
|
|
2236
1599
|
}
|
|
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
1600
|
async startServerNode(matterServerNode) {
|
|
2244
1601
|
if (!matterServerNode)
|
|
2245
1602
|
return;
|
|
2246
1603
|
this.log.notice(`Starting ${matterServerNode.id} server node`);
|
|
2247
1604
|
await matterServerNode.start();
|
|
2248
1605
|
}
|
|
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
1606
|
async stopServerNode(matterServerNode) {
|
|
2256
1607
|
if (!matterServerNode)
|
|
2257
1608
|
return;
|
|
2258
1609
|
this.log.notice(`Closing ${matterServerNode.id} server node`);
|
|
2259
1610
|
try {
|
|
2260
|
-
await withTimeout(matterServerNode.close(), 30000);
|
|
1611
|
+
await withTimeout(matterServerNode.close(), 30000);
|
|
2261
1612
|
this.log.info(`Closed ${matterServerNode.id} server node`);
|
|
2262
1613
|
}
|
|
2263
1614
|
catch (error) {
|
|
2264
1615
|
this.log.error(`Failed to close ${matterServerNode.id} server node: ${error instanceof Error ? error.message : error}`);
|
|
2265
1616
|
}
|
|
2266
1617
|
}
|
|
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
1618
|
async advertiseServerNode(matterServerNode) {
|
|
2274
1619
|
if (matterServerNode) {
|
|
2275
1620
|
await matterServerNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
|
|
@@ -2278,45 +1623,24 @@ export class Matterbridge extends EventEmitter {
|
|
|
2278
1623
|
return { qrPairingCode, manualPairingCode };
|
|
2279
1624
|
}
|
|
2280
1625
|
}
|
|
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
1626
|
async stopAdvertiseServerNode(matterServerNode) {
|
|
2288
1627
|
if (matterServerNode && matterServerNode.lifecycle.isOnline) {
|
|
2289
1628
|
await matterServerNode.env.get(DeviceCommissioner)?.endCommissioning();
|
|
2290
1629
|
this.log.notice(`Stopped advertising for ${matterServerNode.id}`);
|
|
2291
1630
|
}
|
|
2292
1631
|
}
|
|
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
1632
|
async createAggregatorNode(storageContext) {
|
|
2300
|
-
this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator
|
|
1633
|
+
this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator...`);
|
|
2301
1634
|
const aggregatorNode = new Endpoint(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
|
|
2302
1635
|
return aggregatorNode;
|
|
2303
1636
|
}
|
|
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
1637
|
async addBridgedEndpoint(pluginName, device) {
|
|
2312
|
-
// Check if the plugin is registered
|
|
2313
1638
|
const plugin = this.plugins.get(pluginName);
|
|
2314
1639
|
if (!plugin) {
|
|
2315
1640
|
this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
|
|
2316
1641
|
return;
|
|
2317
1642
|
}
|
|
2318
1643
|
if (this.bridgeMode === 'bridge') {
|
|
2319
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2320
1644
|
this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
|
|
2321
1645
|
if (!this.aggregatorNode) {
|
|
2322
1646
|
this.log.error('Aggregator node not found for Matterbridge');
|
|
@@ -2333,7 +1657,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2333
1657
|
}
|
|
2334
1658
|
}
|
|
2335
1659
|
else if (this.bridgeMode === 'childbridge') {
|
|
2336
|
-
// Register and add the device to the plugin server node
|
|
2337
1660
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2338
1661
|
try {
|
|
2339
1662
|
this.log.debug(`Creating endpoint ${dev}${device.deviceName}${db} for AccessoryPlatform plugin ${plg}${plugin.name}${db} server node`);
|
|
@@ -2350,12 +1673,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
2350
1673
|
return;
|
|
2351
1674
|
}
|
|
2352
1675
|
}
|
|
2353
|
-
// Register and add the device to the plugin aggregator node
|
|
2354
1676
|
if (plugin.type === 'DynamicPlatform') {
|
|
2355
1677
|
try {
|
|
2356
1678
|
this.log.debug(`Adding bridged endpoint ${dev}${device.deviceName}${db} for DynamicPlatform plugin ${plg}${plugin.name}${db} aggregator node`);
|
|
2357
1679
|
await this.createDynamicPlugin(plugin);
|
|
2358
|
-
// Fast plugins can add another device before the server node is created
|
|
2359
1680
|
await waiter(`createDynamicPlugin(${plugin.name})`, () => plugin.serverNode?.hasParts === true);
|
|
2360
1681
|
if (!plugin.aggregatorNode) {
|
|
2361
1682
|
this.log.error(`Aggregator node not found for plugin ${plg}${plugin.name}${er}`);
|
|
@@ -2375,28 +1696,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
2375
1696
|
plugin.registeredDevices++;
|
|
2376
1697
|
if (plugin.addedDevices !== undefined)
|
|
2377
1698
|
plugin.addedDevices++;
|
|
2378
|
-
// Add the device to the DeviceManager
|
|
2379
1699
|
this.devices.set(device);
|
|
2380
|
-
// Subscribe to the reachable$Changed event
|
|
2381
1700
|
await this.subscribeAttributeChanged(plugin, device);
|
|
2382
1701
|
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
1702
|
}
|
|
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
1703
|
async removeBridgedEndpoint(pluginName, device) {
|
|
2392
1704
|
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
1705
|
const plugin = this.plugins.get(pluginName);
|
|
2395
1706
|
if (!plugin) {
|
|
2396
1707
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
2397
1708
|
return;
|
|
2398
1709
|
}
|
|
2399
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2400
1710
|
if (this.bridgeMode === 'bridge') {
|
|
2401
1711
|
if (!this.aggregatorNode) {
|
|
2402
1712
|
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 +1721,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2411
1721
|
}
|
|
2412
1722
|
else if (this.bridgeMode === 'childbridge') {
|
|
2413
1723
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2414
|
-
// Nothing to do here since the server node has no aggregator node but only the device itself
|
|
2415
1724
|
}
|
|
2416
1725
|
else if (plugin.type === 'DynamicPlatform') {
|
|
2417
1726
|
if (!plugin.aggregatorNode) {
|
|
@@ -2426,21 +1735,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2426
1735
|
if (plugin.addedDevices !== undefined)
|
|
2427
1736
|
plugin.addedDevices--;
|
|
2428
1737
|
}
|
|
2429
|
-
// Remove the device from the DeviceManager
|
|
2430
1738
|
this.devices.remove(device);
|
|
2431
1739
|
}
|
|
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
1740
|
async removeAllBridgedEndpoints(pluginName, delay = 0) {
|
|
2445
1741
|
this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}${delay > 0 ? ` with delay ${delay} ms` : ''}`);
|
|
2446
1742
|
for (const device of this.devices.array().filter((device) => device.plugin === pluginName)) {
|
|
@@ -2451,25 +1747,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
2451
1747
|
if (delay > 0)
|
|
2452
1748
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2453
1749
|
}
|
|
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
1750
|
async subscribeAttributeChanged(plugin, device) {
|
|
2464
1751
|
this.log.info(`Subscribing attributes for endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) plugin ${plg}${plugin.name}${nf}`);
|
|
2465
1752
|
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
1753
|
plugin.serverNode.eventsOf(BasicInformationServer).reachable$Changed?.on((reachable) => {
|
|
2474
1754
|
this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) is ${reachable ? 'reachable' : 'unreachable'}`);
|
|
2475
1755
|
this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, 'BasicInformationServer', 'reachable', reachable);
|
|
@@ -2482,12 +1762,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2482
1762
|
});
|
|
2483
1763
|
}
|
|
2484
1764
|
}
|
|
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
1765
|
sanitizeFabricInformations(fabricInfo) {
|
|
2492
1766
|
return fabricInfo.map((info) => {
|
|
2493
1767
|
return {
|
|
@@ -2501,12 +1775,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2501
1775
|
};
|
|
2502
1776
|
});
|
|
2503
1777
|
}
|
|
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
1778
|
sanitizeSessionInformation(sessionInfo) {
|
|
2511
1779
|
return sessionInfo
|
|
2512
1780
|
.filter((session) => session.isPeerActive)
|
|
@@ -2534,20 +1802,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2534
1802
|
};
|
|
2535
1803
|
});
|
|
2536
1804
|
}
|
|
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
1805
|
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
1806
|
}
|
|
2552
1807
|
getVendorIdName = (vendorId) => {
|
|
2553
1808
|
if (!vendorId)
|
|
@@ -2590,29 +1845,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
2590
1845
|
}
|
|
2591
1846
|
return vendorName;
|
|
2592
1847
|
};
|
|
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
1848
|
async spawnCommand(command, args = []) {
|
|
2600
1849
|
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
1850
|
const cmdLine = command + ' ' + args.join(' ');
|
|
2607
1851
|
if (process.platform === 'win32' && command === 'npm') {
|
|
2608
|
-
// Must be spawn('cmd.exe', ['/c', 'npm -g install <package>']);
|
|
2609
1852
|
const argstring = 'npm ' + args.join(' ');
|
|
2610
1853
|
args.splice(0, args.length, '/c', argstring);
|
|
2611
1854
|
command = 'cmd.exe';
|
|
2612
1855
|
}
|
|
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
1856
|
if (hasParameter('sudo') || (process.platform === 'linux' && command === 'npm' && !hasParameter('docker') && !hasParameter('nosudo'))) {
|
|
2617
1857
|
args.unshift(command);
|
|
2618
1858
|
command = 'sudo';
|
|
@@ -2670,5 +1910,26 @@ export class Matterbridge extends EventEmitter {
|
|
|
2670
1910
|
}
|
|
2671
1911
|
});
|
|
2672
1912
|
}
|
|
1913
|
+
async createDirectory(path, name) {
|
|
1914
|
+
try {
|
|
1915
|
+
await fs.access(path);
|
|
1916
|
+
}
|
|
1917
|
+
catch (err) {
|
|
1918
|
+
if (err instanceof Error) {
|
|
1919
|
+
const nodeErr = err;
|
|
1920
|
+
if (nodeErr.code === 'ENOENT') {
|
|
1921
|
+
try {
|
|
1922
|
+
await fs.mkdir(path, { recursive: true });
|
|
1923
|
+
this.log.info(`Created ${name}: ${path}`);
|
|
1924
|
+
}
|
|
1925
|
+
catch (err) {
|
|
1926
|
+
this.log.error(`Error creating ${name}: ${err}`);
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
else {
|
|
1930
|
+
this.log.error(`Error accessing ${name}: ${err}`);
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
2673
1935
|
}
|
|
2674
|
-
//# sourceMappingURL=matterbridge.js.map
|