matterbridge 2.1.2 → 2.1.3-dev.1
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 +24 -10
- package/dist/cli.js +0 -26
- package/dist/cluster/export.js +0 -2
- package/dist/defaultConfigSchema.js +0 -23
- package/dist/deviceManager.js +1 -94
- package/dist/frontend.js +23 -231
- package/dist/index.js +0 -28
- 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 +57 -741
- package/dist/matterbridgeAccessoryPlatform.js +0 -33
- package/dist/matterbridgeBehaviors.js +1 -32
- package/dist/matterbridgeDeviceTypes.js +11 -112
- package/dist/matterbridgeDynamicPlatform.js +0 -33
- package/dist/matterbridgeEndpoint.js +6 -690
- package/dist/matterbridgeEndpointHelpers.js +0 -96
- package/dist/matterbridgePlatform.js +3 -111
- package/dist/matterbridgeTypes.js +0 -24
- package/dist/pluginManager.js +3 -230
- package/dist/storage/export.js +0 -1
- package/dist/utils/colorUtils.js +2 -205
- package/dist/utils/export.js +0 -1
- package/dist/utils/utils.js +7 -251
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -2
- package/dist/cli.d.ts +0 -25
- 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 -110
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/index.d.ts +0 -35
- 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 -409
- package/dist/matterbridge.d.ts.map +0 -1
- package/dist/matterbridge.js.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.d.ts +0 -39
- package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
- package/dist/matterbridgeBehaviors.d.ts +0 -1056
- package/dist/matterbridgeBehaviors.d.ts.map +0 -1
- package/dist/matterbridgeBehaviors.js.map +0 -1
- package/dist/matterbridgeDeviceTypes.d.ts +0 -177
- package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
- package/dist/matterbridgeDeviceTypes.js.map +0 -1
- package/dist/matterbridgeDynamicPlatform.d.ts +0 -39
- package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
- package/dist/matterbridgeDynamicPlatform.js.map +0 -1
- package/dist/matterbridgeEndpoint.d.ts +0 -834
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -2262
- package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
- package/dist/matterbridgeEndpointHelpers.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts +0 -152
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts +0 -167
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/pluginManager.d.ts +0 -236
- package/dist/pluginManager.d.ts.map +0 -1
- package/dist/pluginManager.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/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/export.d.ts +0 -3
- package/dist/utils/export.d.ts.map +0 -1
- package/dist/utils/export.js.map +0 -1
- package/dist/utils/utils.d.ts +0 -221
- package/dist/utils/utils.d.ts.map +0 -1
- package/dist/utils/utils.js.map +0 -1
package/dist/matterbridge.js
CHANGED
|
@@ -1,26 +1,3 @@
|
|
|
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.2
|
|
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 { fileURLToPath } from 'url';
|
|
25
2
|
import { promises as fs } from 'fs';
|
|
26
3
|
import { exec, spawn } from 'child_process';
|
|
@@ -28,27 +5,20 @@ import EventEmitter from 'events';
|
|
|
28
5
|
import os from 'os';
|
|
29
6
|
import path from 'path';
|
|
30
7
|
import { randomBytes } from 'crypto';
|
|
31
|
-
// NodeStorage and AnsiLogger modules
|
|
32
8
|
import { NodeStorageManager } from './storage/export.js';
|
|
33
9
|
import { AnsiLogger, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN, nt } from './logger/export.js';
|
|
34
|
-
// Matterbridge
|
|
35
10
|
import { logInterfaces, copyDirectory, getParameter, getIntParameter, hasParameter } from './utils/utils.js';
|
|
36
11
|
import { PluginManager } from './pluginManager.js';
|
|
37
12
|
import { DeviceManager } from './deviceManager.js';
|
|
38
13
|
import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
|
|
39
14
|
import { bridge } from './matterbridgeDeviceTypes.js';
|
|
40
15
|
import { Frontend } from './frontend.js';
|
|
41
|
-
// @matter
|
|
42
16
|
import { DeviceTypeId, Endpoint as EndpointNode, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, VendorId, StorageService, Environment, ServerNode } from '@matter/main';
|
|
43
17
|
import { DeviceCommissioner, FabricAction, PaseClient } from '@matter/main/protocol';
|
|
44
18
|
import { AggregatorEndpoint } from '@matter/main/endpoints';
|
|
45
|
-
// Default colors
|
|
46
19
|
const plg = '\u001B[38;5;33m';
|
|
47
20
|
const dev = '\u001B[38;5;79m';
|
|
48
21
|
const typ = '\u001B[38;5;207m';
|
|
49
|
-
/**
|
|
50
|
-
* Represents the Matterbridge application.
|
|
51
|
-
*/
|
|
52
22
|
export class Matterbridge extends EventEmitter {
|
|
53
23
|
systemInformation = {
|
|
54
24
|
interfaceName: '',
|
|
@@ -85,7 +55,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
85
55
|
restartMode: '',
|
|
86
56
|
readOnly: hasParameter('readonly'),
|
|
87
57
|
profile: getParameter('profile'),
|
|
88
|
-
loggerLevel: "info"
|
|
58
|
+
loggerLevel: "info",
|
|
89
59
|
fileLogger: false,
|
|
90
60
|
matterLoggerLevel: MatterLogLevel.INFO,
|
|
91
61
|
matterFileLogger: false,
|
|
@@ -120,11 +90,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
120
90
|
plugins;
|
|
121
91
|
devices;
|
|
122
92
|
frontend = new Frontend(this);
|
|
123
|
-
// Matterbridge storage
|
|
124
93
|
nodeStorage;
|
|
125
94
|
nodeContext;
|
|
126
95
|
nodeStorageName = 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
|
|
127
|
-
// Cleanup
|
|
128
96
|
hasCleanupStarted = false;
|
|
129
97
|
initialized = false;
|
|
130
98
|
execRunningCount = 0;
|
|
@@ -136,57 +104,34 @@ export class Matterbridge extends EventEmitter {
|
|
|
136
104
|
sigtermHandler;
|
|
137
105
|
exceptionHandler;
|
|
138
106
|
rejectionHandler;
|
|
139
|
-
// Matter environment
|
|
140
107
|
environment = Environment.default;
|
|
141
|
-
// Matter storage
|
|
142
108
|
matterStorageName = 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
|
|
143
109
|
matterStorageService;
|
|
144
110
|
matterStorageManager;
|
|
145
111
|
matterbridgeContext;
|
|
146
112
|
mattercontrollerContext;
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
discriminator; // first server node discriminator
|
|
113
|
+
mdnsInterface;
|
|
114
|
+
ipv4address;
|
|
115
|
+
ipv6address;
|
|
116
|
+
port;
|
|
117
|
+
passcode;
|
|
118
|
+
discriminator;
|
|
154
119
|
serverNode;
|
|
155
120
|
aggregatorNode;
|
|
156
121
|
aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
|
|
157
122
|
aggregatorProductId = getIntParameter('productId') ?? 0x8000;
|
|
158
123
|
static instance;
|
|
159
|
-
// We load asyncronously so is private
|
|
160
124
|
constructor() {
|
|
161
125
|
super();
|
|
162
126
|
}
|
|
163
|
-
/**
|
|
164
|
-
* Retrieves the list of Matterbridge devices.
|
|
165
|
-
* @returns {MatterbridgeEndpoint[]} An array of MatterbridgeDevice objects.
|
|
166
|
-
*/
|
|
167
127
|
getDevices() {
|
|
168
128
|
return this.devices.array();
|
|
169
129
|
}
|
|
170
|
-
/**
|
|
171
|
-
* Retrieves the list of registered plugins.
|
|
172
|
-
* @returns {RegisteredPlugin[]} An array of RegisteredPlugin objects.
|
|
173
|
-
*/
|
|
174
130
|
getPlugins() {
|
|
175
131
|
return this.plugins.array();
|
|
176
132
|
}
|
|
177
|
-
/** ***********************************************************************************************************************************/
|
|
178
|
-
/** loadInstance() and cleanup() methods */
|
|
179
|
-
/** ***********************************************************************************************************************************/
|
|
180
|
-
/**
|
|
181
|
-
* Loads an instance of the Matterbridge class.
|
|
182
|
-
* If an instance already exists, return that instance.
|
|
183
|
-
*
|
|
184
|
-
* @param initialize - Whether to initialize the Matterbridge instance after loading.
|
|
185
|
-
* @returns The loaded Matterbridge instance.
|
|
186
|
-
*/
|
|
187
133
|
static async loadInstance(initialize = false) {
|
|
188
134
|
if (!Matterbridge.instance) {
|
|
189
|
-
// eslint-disable-next-line no-console
|
|
190
135
|
if (hasParameter('debug'))
|
|
191
136
|
console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
|
|
192
137
|
Matterbridge.instance = new Matterbridge();
|
|
@@ -195,79 +140,51 @@ export class Matterbridge extends EventEmitter {
|
|
|
195
140
|
}
|
|
196
141
|
return Matterbridge.instance;
|
|
197
142
|
}
|
|
198
|
-
/**
|
|
199
|
-
* Call cleanup().
|
|
200
|
-
* @deprecated This method is deprecated and is only used for jest tests.
|
|
201
|
-
*
|
|
202
|
-
*/
|
|
203
143
|
async destroyInstance() {
|
|
204
144
|
await this.cleanup('destroying instance...', false);
|
|
205
145
|
await new Promise((resolve) => {
|
|
206
146
|
setTimeout(resolve, 1000);
|
|
207
147
|
});
|
|
208
148
|
}
|
|
209
|
-
/**
|
|
210
|
-
* Initializes the Matterbridge application.
|
|
211
|
-
*
|
|
212
|
-
* @remarks
|
|
213
|
-
* This method performs the necessary setup and initialization steps for the Matterbridge application.
|
|
214
|
-
* It displays the help information if the 'help' parameter is provided, sets up the logger, checks the
|
|
215
|
-
* node version, registers signal handlers, initializes storage, and parses the command line.
|
|
216
|
-
*
|
|
217
|
-
* @returns A Promise that resolves when the initialization is complete.
|
|
218
|
-
*/
|
|
219
149
|
async initialize() {
|
|
220
|
-
// Set the restart mode
|
|
221
150
|
if (hasParameter('service'))
|
|
222
151
|
this.restartMode = 'service';
|
|
223
152
|
if (hasParameter('docker'))
|
|
224
153
|
this.restartMode = 'docker';
|
|
225
|
-
// Set the matterbridge directory
|
|
226
154
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
227
155
|
this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
|
|
228
|
-
// Setup the matter environment
|
|
229
156
|
this.environment.vars.set('log.level', MatterLogLevel.INFO);
|
|
230
157
|
this.environment.vars.set('log.format', MatterLogFormat.ANSI);
|
|
231
158
|
this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, this.matterStorageName));
|
|
232
159
|
this.environment.vars.set('runtime.signals', false);
|
|
233
160
|
this.environment.vars.set('runtime.exitcode', false);
|
|
234
|
-
|
|
235
|
-
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
|
|
236
|
-
// Register process handlers
|
|
161
|
+
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
237
162
|
this.registerProcessHandlers();
|
|
238
|
-
// Initialize nodeStorage and nodeContext
|
|
239
163
|
try {
|
|
240
164
|
this.log.debug(`Creating node storage manager: ${CYAN}${this.nodeStorageName}${db}`);
|
|
241
165
|
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, this.nodeStorageName), writeQueue: false, expiredInterval: undefined, logging: false });
|
|
242
166
|
this.log.debug('Creating node storage context for matterbridge');
|
|
243
167
|
this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
|
|
244
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
245
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
246
168
|
const keys = (await this.nodeStorage?.storage.keys());
|
|
247
169
|
for (const key of keys) {
|
|
248
170
|
this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
|
|
249
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
250
171
|
await this.nodeStorage?.storage.get(key);
|
|
251
172
|
}
|
|
252
173
|
const storages = await this.nodeStorage.getStorageNames();
|
|
253
174
|
for (const storage of storages) {
|
|
254
175
|
this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
|
|
255
176
|
const nodeContext = await this.nodeStorage?.createStorage(storage);
|
|
256
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
257
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
258
177
|
const keys = (await nodeContext?.storage.keys());
|
|
259
178
|
keys.forEach(async (key) => {
|
|
260
179
|
this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
|
|
261
180
|
await nodeContext?.get(key);
|
|
262
181
|
});
|
|
263
182
|
}
|
|
264
|
-
// Creating a backup of the node storage since it is not corrupted
|
|
265
183
|
this.log.debug('Creating node storage backup...');
|
|
266
184
|
await copyDirectory(path.join(this.matterbridgeDirectory, this.nodeStorageName), path.join(this.matterbridgeDirectory, this.nodeStorageName + '.backup'));
|
|
267
185
|
this.log.debug('Created node storage backup');
|
|
268
186
|
}
|
|
269
187
|
catch (error) {
|
|
270
|
-
// Restoring the backup of the node storage since it is corrupted
|
|
271
188
|
this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
|
|
272
189
|
if (hasParameter('norestore')) {
|
|
273
190
|
this.log.fatal(`The matterbridge node storage is corrupted. Parameter -norestore found: exiting...`);
|
|
@@ -282,51 +199,45 @@ export class Matterbridge extends EventEmitter {
|
|
|
282
199
|
this.log.fatal('Fatal error creating node storage manager and context for matterbridge');
|
|
283
200
|
throw new Error('Fatal error creating node storage manager and context for matterbridge');
|
|
284
201
|
}
|
|
285
|
-
// Set the first port to use for the commissioning server (will be incremented in childbridge mode)
|
|
286
202
|
this.port = getIntParameter('port') ?? (await this.nodeContext.get('matterport', 5540)) ?? 5540;
|
|
287
|
-
// Set the first passcode to use for the commissioning server (will be incremented in childbridge mode)
|
|
288
203
|
this.passcode = getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode')) ?? PaseClient.generateRandomPasscode();
|
|
289
|
-
// Set the first discriminator to use for the commissioning server (will be incremented in childbridge mode)
|
|
290
204
|
this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator();
|
|
291
205
|
this.log.debug(`Initializing commissioning server for Matterbridge... on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
|
|
292
|
-
// Set matterbridge logger level (context: matterbridgeLogLevel)
|
|
293
206
|
if (hasParameter('logger')) {
|
|
294
207
|
const level = getParameter('logger');
|
|
295
208
|
if (level === 'debug') {
|
|
296
|
-
this.log.logLevel = "debug"
|
|
209
|
+
this.log.logLevel = "debug";
|
|
297
210
|
}
|
|
298
211
|
else if (level === 'info') {
|
|
299
|
-
this.log.logLevel = "info"
|
|
212
|
+
this.log.logLevel = "info";
|
|
300
213
|
}
|
|
301
214
|
else if (level === 'notice') {
|
|
302
|
-
this.log.logLevel = "notice"
|
|
215
|
+
this.log.logLevel = "notice";
|
|
303
216
|
}
|
|
304
217
|
else if (level === 'warn') {
|
|
305
|
-
this.log.logLevel = "warn"
|
|
218
|
+
this.log.logLevel = "warn";
|
|
306
219
|
}
|
|
307
220
|
else if (level === 'error') {
|
|
308
|
-
this.log.logLevel = "error"
|
|
221
|
+
this.log.logLevel = "error";
|
|
309
222
|
}
|
|
310
223
|
else if (level === 'fatal') {
|
|
311
|
-
this.log.logLevel = "fatal"
|
|
224
|
+
this.log.logLevel = "fatal";
|
|
312
225
|
}
|
|
313
226
|
else {
|
|
314
227
|
this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
|
|
315
|
-
this.log.logLevel = "info"
|
|
228
|
+
this.log.logLevel = "info";
|
|
316
229
|
}
|
|
317
230
|
}
|
|
318
231
|
else {
|
|
319
|
-
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', "info"
|
|
232
|
+
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', "info");
|
|
320
233
|
}
|
|
321
234
|
MatterbridgeEndpoint.logLevel = this.log.logLevel;
|
|
322
|
-
// Create the file logger for matterbridge (context: matterbridgeFileLog)
|
|
323
235
|
if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
|
|
324
236
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), this.log.logLevel, true);
|
|
325
237
|
this.matterbridgeInformation.fileLogger = true;
|
|
326
238
|
}
|
|
327
239
|
this.log.notice('Matterbridge is starting...');
|
|
328
240
|
this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.matterbridgeInformation.fileLogger}.`);
|
|
329
|
-
// Set matter.js logger level, format and logger (context: matterLogLevel)
|
|
330
241
|
if (hasParameter('matterlogger')) {
|
|
331
242
|
const level = getParameter('matterlogger');
|
|
332
243
|
if (level === 'debug') {
|
|
@@ -357,7 +268,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
357
268
|
}
|
|
358
269
|
Logger.format = MatterLogFormat.ANSI;
|
|
359
270
|
Logger.setLogger('default', this.createMatterLogger());
|
|
360
|
-
// Create the file logger for matter.js (context: matterFileLog)
|
|
361
271
|
if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
|
|
362
272
|
this.matterbridgeInformation.matterFileLogger = true;
|
|
363
273
|
Logger.addLogger('matterfilelogger', await this.createMatterFileLogger(path.join(this.matterbridgeDirectory, this.matterLoggerFile), true), {
|
|
@@ -366,7 +276,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
366
276
|
});
|
|
367
277
|
}
|
|
368
278
|
this.log.debug(`Matter logLevel: ${Logger.defaultLogLevel} fileLoger: ${this.matterbridgeInformation.matterFileLogger}.`);
|
|
369
|
-
// Set the interface to use for matter server node mdnsInterface
|
|
370
279
|
if (hasParameter('mdnsinterface')) {
|
|
371
280
|
this.mdnsInterface = getParameter('mdnsinterface');
|
|
372
281
|
}
|
|
@@ -375,7 +284,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
375
284
|
if (this.mdnsInterface === '')
|
|
376
285
|
this.mdnsInterface = undefined;
|
|
377
286
|
}
|
|
378
|
-
// Validate mdnsInterface
|
|
379
287
|
if (this.mdnsInterface) {
|
|
380
288
|
const networkInterfaces = os.networkInterfaces();
|
|
381
289
|
const availableInterfaces = Object.keys(networkInterfaces);
|
|
@@ -389,7 +297,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
389
297
|
}
|
|
390
298
|
if (this.mdnsInterface)
|
|
391
299
|
this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
|
|
392
|
-
// Set the listeningAddressIpv4 for the matter commissioning server
|
|
393
300
|
if (hasParameter('ipv4address')) {
|
|
394
301
|
this.ipv4address = getParameter('ipv4address');
|
|
395
302
|
}
|
|
@@ -398,7 +305,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
398
305
|
if (this.ipv4address === '')
|
|
399
306
|
this.ipv4address = undefined;
|
|
400
307
|
}
|
|
401
|
-
// Set the listeningAddressIpv6 for the matter commissioning server
|
|
402
308
|
if (hasParameter('ipv6address')) {
|
|
403
309
|
this.ipv6address = getParameter('ipv6address');
|
|
404
310
|
}
|
|
@@ -407,19 +313,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
407
313
|
if (this.ipv6address === '')
|
|
408
314
|
this.ipv6address = undefined;
|
|
409
315
|
}
|
|
410
|
-
// Initialize PluginManager
|
|
411
316
|
this.plugins = new PluginManager(this);
|
|
412
317
|
await this.plugins.loadFromStorage();
|
|
413
318
|
this.plugins.logLevel = this.log.logLevel;
|
|
414
|
-
// Initialize DeviceManager
|
|
415
319
|
this.devices = new DeviceManager(this, this.nodeContext);
|
|
416
320
|
this.devices.logLevel = this.log.logLevel;
|
|
417
|
-
// Get the plugins from node storage and create the plugins node storage contexts
|
|
418
321
|
for (const plugin of this.plugins) {
|
|
419
322
|
const packageJson = await this.plugins.parse(plugin);
|
|
420
323
|
if (packageJson === null && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
|
|
421
|
-
// Try to reinstall the plugin from npm (for Docker pull and external plugins)
|
|
422
|
-
// We don't do this when the add and other parameters are set because we shut down the process after adding the plugin
|
|
423
324
|
this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
|
|
424
325
|
try {
|
|
425
326
|
await this.spawnCommand('npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose']);
|
|
@@ -441,7 +342,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
441
342
|
await plugin.nodeContext.set('description', plugin.description);
|
|
442
343
|
await plugin.nodeContext.set('author', plugin.author);
|
|
443
344
|
}
|
|
444
|
-
// Log system info and create .matterbridge directory
|
|
445
345
|
await this.logNodeAndSystemInfo();
|
|
446
346
|
this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
|
|
447
347
|
`${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
|
|
@@ -449,7 +349,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
449
349
|
`${hasParameter('controller') ? 'mode controller ' : ''}` +
|
|
450
350
|
`${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
|
|
451
351
|
`running on ${this.systemInformation.osType} (v.${this.systemInformation.osRelease}) platform ${this.systemInformation.osPlatform} arch ${this.systemInformation.osArch}`);
|
|
452
|
-
// Check node version and throw error
|
|
453
352
|
const minNodeVersion = 18;
|
|
454
353
|
const nodeVersion = process.versions.node;
|
|
455
354
|
const versionMajor = parseInt(nodeVersion.split('.')[0]);
|
|
@@ -457,15 +356,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
457
356
|
this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
458
357
|
throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
459
358
|
}
|
|
460
|
-
// Parse command line
|
|
461
359
|
await this.parseCommandLine();
|
|
462
360
|
this.initialized = true;
|
|
463
361
|
}
|
|
464
|
-
/**
|
|
465
|
-
* Parses the command line arguments and performs the corresponding actions.
|
|
466
|
-
* @private
|
|
467
|
-
* @returns {Promise<void>} A promise that resolves when the command line arguments have been processed, or the process exits.
|
|
468
|
-
*/
|
|
469
362
|
async parseCommandLine() {
|
|
470
363
|
if (hasParameter('help')) {
|
|
471
364
|
this.log.info(`\nUsage: matterbridge [options]\n
|
|
@@ -575,7 +468,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
575
468
|
await this.shutdownProcessAndFactoryReset();
|
|
576
469
|
return;
|
|
577
470
|
}
|
|
578
|
-
// Start the matter storage and create the matterbridge context
|
|
579
471
|
try {
|
|
580
472
|
await this.startMatterStorage();
|
|
581
473
|
}
|
|
@@ -583,12 +475,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
583
475
|
this.log.fatal(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
584
476
|
throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
585
477
|
}
|
|
586
|
-
// Clear the matterbridge context if the reset parameter is set
|
|
587
478
|
if (hasParameter('reset') && getParameter('reset') === undefined) {
|
|
588
479
|
await this.shutdownProcessAndReset();
|
|
589
480
|
return;
|
|
590
481
|
}
|
|
591
|
-
// Clear matterbridge plugin context if the reset parameter is set
|
|
592
482
|
if (hasParameter('reset') && getParameter('reset') !== undefined) {
|
|
593
483
|
this.log.debug(`Reset plugin ${getParameter('reset')}`);
|
|
594
484
|
const plugin = this.plugins.get(getParameter('reset'));
|
|
@@ -610,11 +500,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
610
500
|
this.emit('shutdown');
|
|
611
501
|
return;
|
|
612
502
|
}
|
|
613
|
-
// Initialize frontend
|
|
614
503
|
if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
|
|
615
504
|
await this.frontend.start(getIntParameter('frontend'));
|
|
616
505
|
this.frontend.logLevel = this.log.logLevel;
|
|
617
|
-
// Check each 60 minutes the latest versions
|
|
618
506
|
this.checkUpdateInterval = setInterval(() => {
|
|
619
507
|
this.getMatterbridgeLatestVersion();
|
|
620
508
|
for (const plugin of this.plugins) {
|
|
@@ -622,24 +510,20 @@ export class Matterbridge extends EventEmitter {
|
|
|
622
510
|
}
|
|
623
511
|
this.frontend.wssSendRefreshRequired();
|
|
624
512
|
}, 60 * 60 * 1000);
|
|
625
|
-
// Start the matterbridge in mode test
|
|
626
513
|
if (hasParameter('test')) {
|
|
627
514
|
this.bridgeMode = 'bridge';
|
|
628
515
|
MatterbridgeEndpoint.bridgeMode = 'bridge';
|
|
629
516
|
return;
|
|
630
517
|
}
|
|
631
|
-
// Start the matterbridge in mode controller
|
|
632
518
|
if (hasParameter('controller')) {
|
|
633
519
|
this.bridgeMode = 'controller';
|
|
634
520
|
await this.startController();
|
|
635
521
|
return;
|
|
636
522
|
}
|
|
637
|
-
// Check if the bridge mode is set and start matterbridge in bridge mode if not set
|
|
638
523
|
if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
|
|
639
524
|
this.log.info('Setting default matterbridge start mode to bridge');
|
|
640
525
|
await this.nodeContext?.set('bridgeMode', 'bridge');
|
|
641
526
|
}
|
|
642
|
-
// Start matterbridge in bridge mode
|
|
643
527
|
if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
|
|
644
528
|
this.bridgeMode = 'bridge';
|
|
645
529
|
MatterbridgeEndpoint.bridgeMode = 'bridge';
|
|
@@ -647,7 +531,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
647
531
|
await this.startBridge();
|
|
648
532
|
return;
|
|
649
533
|
}
|
|
650
|
-
// Start matterbridge in childbridge mode
|
|
651
534
|
if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
|
|
652
535
|
this.bridgeMode = 'childbridge';
|
|
653
536
|
MatterbridgeEndpoint.bridgeMode = 'childbridge';
|
|
@@ -656,28 +539,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
656
539
|
return;
|
|
657
540
|
}
|
|
658
541
|
}
|
|
659
|
-
/**
|
|
660
|
-
* Asynchronously loads and starts the registered plugins.
|
|
661
|
-
*
|
|
662
|
-
* This method is responsible for initializing and staarting all enabled plugins.
|
|
663
|
-
* It ensures that each plugin is properly loaded and started before the bridge starts.
|
|
664
|
-
*
|
|
665
|
-
* @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
|
|
666
|
-
*/
|
|
667
542
|
async startPlugins() {
|
|
668
|
-
// Check, load and start the plugins
|
|
669
543
|
for (const plugin of this.plugins) {
|
|
670
544
|
plugin.configJson = await this.plugins.loadConfig(plugin);
|
|
671
545
|
plugin.schemaJson = await this.plugins.loadSchema(plugin);
|
|
672
|
-
// Check if the plugin is available
|
|
673
546
|
if (!(await this.plugins.resolve(plugin.path))) {
|
|
674
547
|
this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
|
|
675
548
|
plugin.enabled = false;
|
|
676
549
|
plugin.error = true;
|
|
677
550
|
continue;
|
|
678
551
|
}
|
|
679
|
-
// Check if the plugin has a new version
|
|
680
|
-
// this.getPluginLatestVersion(plugin); // No await do it asyncronously
|
|
681
552
|
if (!plugin.enabled) {
|
|
682
553
|
this.log.info(`Plugin ${plg}${plugin.name}${nf} not enabled`);
|
|
683
554
|
continue;
|
|
@@ -691,26 +562,20 @@ export class Matterbridge extends EventEmitter {
|
|
|
691
562
|
plugin.addedDevices = undefined;
|
|
692
563
|
plugin.qrPairingCode = undefined;
|
|
693
564
|
plugin.manualPairingCode = undefined;
|
|
694
|
-
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
565
|
+
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
695
566
|
}
|
|
696
567
|
this.frontend.wssSendRefreshRequired();
|
|
697
568
|
}
|
|
698
|
-
/**
|
|
699
|
-
* Registers the process handlers for uncaughtException, unhandledRejection, SIGINT and SIGTERM.
|
|
700
|
-
* When either of these signals are received, the cleanup method is called with an appropriate message.
|
|
701
|
-
*/
|
|
702
569
|
registerProcessHandlers() {
|
|
703
570
|
this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
|
|
704
571
|
process.removeAllListeners('uncaughtException');
|
|
705
572
|
process.removeAllListeners('unhandledRejection');
|
|
706
573
|
this.exceptionHandler = async (error) => {
|
|
707
574
|
this.log.fatal('Unhandled Exception detected at:', error.stack || error, rs);
|
|
708
|
-
// await this.cleanup('Unhandled Exception detected, cleaning up...');
|
|
709
575
|
};
|
|
710
576
|
process.on('uncaughtException', this.exceptionHandler);
|
|
711
577
|
this.rejectionHandler = async (reason, promise) => {
|
|
712
578
|
this.log.fatal('Unhandled Rejection detected at:', promise, 'reason:', reason instanceof Error ? reason.stack : reason, rs);
|
|
713
|
-
// await this.cleanup('Unhandled Rejection detected, cleaning up...');
|
|
714
579
|
};
|
|
715
580
|
process.on('unhandledRejection', this.rejectionHandler);
|
|
716
581
|
this.log.debug(`Registering SIGINT and SIGTERM signal handlers...`);
|
|
@@ -723,9 +588,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
723
588
|
};
|
|
724
589
|
process.on('SIGTERM', this.sigtermHandler);
|
|
725
590
|
}
|
|
726
|
-
/**
|
|
727
|
-
* Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
|
|
728
|
-
*/
|
|
729
591
|
deregisterProcesslHandlers() {
|
|
730
592
|
this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
|
|
731
593
|
if (this.exceptionHandler)
|
|
@@ -742,17 +604,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
742
604
|
process.off('SIGTERM', this.sigtermHandler);
|
|
743
605
|
this.sigtermHandler = undefined;
|
|
744
606
|
}
|
|
745
|
-
/**
|
|
746
|
-
* Logs the node and system information.
|
|
747
|
-
*/
|
|
748
607
|
async logNodeAndSystemInfo() {
|
|
749
|
-
// IP address information
|
|
750
608
|
const networkInterfaces = os.networkInterfaces();
|
|
751
609
|
this.systemInformation.interfaceName = '';
|
|
752
610
|
this.systemInformation.ipv4Address = '';
|
|
753
611
|
this.systemInformation.ipv6Address = '';
|
|
754
612
|
for (const [interfaceName, interfaceDetails] of Object.entries(networkInterfaces)) {
|
|
755
|
-
// this.log.debug(`Checking interface: '${interfaceName}' for '${this.mdnsInterface}'`);
|
|
756
613
|
if (this.mdnsInterface && interfaceName !== this.mdnsInterface)
|
|
757
614
|
continue;
|
|
758
615
|
if (!interfaceDetails) {
|
|
@@ -778,22 +635,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
778
635
|
break;
|
|
779
636
|
}
|
|
780
637
|
}
|
|
781
|
-
// Node information
|
|
782
638
|
this.systemInformation.nodeVersion = process.versions.node;
|
|
783
639
|
const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
|
|
784
640
|
const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
|
|
785
641
|
const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
|
|
786
|
-
// Host system information
|
|
787
642
|
this.systemInformation.hostname = os.hostname();
|
|
788
643
|
this.systemInformation.user = os.userInfo().username;
|
|
789
|
-
this.systemInformation.osType = os.type();
|
|
790
|
-
this.systemInformation.osRelease = os.release();
|
|
791
|
-
this.systemInformation.osPlatform = os.platform();
|
|
792
|
-
this.systemInformation.osArch = os.arch();
|
|
793
|
-
this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
794
|
-
this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
795
|
-
this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
|
|
796
|
-
// Log the system information
|
|
644
|
+
this.systemInformation.osType = os.type();
|
|
645
|
+
this.systemInformation.osRelease = os.release();
|
|
646
|
+
this.systemInformation.osPlatform = os.platform();
|
|
647
|
+
this.systemInformation.osArch = os.arch();
|
|
648
|
+
this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
649
|
+
this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
650
|
+
this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
|
|
797
651
|
this.log.debug('Host System Information:');
|
|
798
652
|
this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
|
|
799
653
|
this.log.debug(`- User: ${this.systemInformation.user}`);
|
|
@@ -809,19 +663,15 @@ export class Matterbridge extends EventEmitter {
|
|
|
809
663
|
this.log.debug(`- Total Memory: ${this.systemInformation.totalMemory}`);
|
|
810
664
|
this.log.debug(`- Free Memory: ${this.systemInformation.freeMemory}`);
|
|
811
665
|
this.log.debug(`- System Uptime: ${this.systemInformation.systemUptime}`);
|
|
812
|
-
// Home directory
|
|
813
666
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
814
667
|
this.matterbridgeInformation.homeDirectory = this.homeDirectory;
|
|
815
668
|
this.log.debug(`Home Directory: ${this.homeDirectory}`);
|
|
816
|
-
// Package root directory
|
|
817
669
|
const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
818
670
|
this.rootDirectory = path.resolve(currentFileDirectory, '../');
|
|
819
671
|
this.matterbridgeInformation.rootDirectory = this.rootDirectory;
|
|
820
672
|
this.log.debug(`Root Directory: ${this.rootDirectory}`);
|
|
821
|
-
// Global node_modules directory
|
|
822
673
|
if (this.nodeContext)
|
|
823
674
|
this.globalModulesDirectory = this.matterbridgeInformation.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
|
|
824
|
-
// First run of Matterbridge so the node storage is empty
|
|
825
675
|
if (this.globalModulesDirectory === '') {
|
|
826
676
|
try {
|
|
827
677
|
this.globalModulesDirectory = await this.getGlobalNodeModules();
|
|
@@ -835,20 +685,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
835
685
|
}
|
|
836
686
|
else
|
|
837
687
|
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
838
|
-
/* removed cause is too expensive for the shelly board and not really needed. Why should it change the globalModulesDirectory?
|
|
839
|
-
else {
|
|
840
|
-
this.getGlobalNodeModules()
|
|
841
|
-
.then(async (globalModulesDirectory) => {
|
|
842
|
-
this.globalModulesDirectory = globalModulesDirectory;
|
|
843
|
-
this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory;
|
|
844
|
-
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
845
|
-
await this.nodeContext?.set<string>('globalModulesDirectory', this.globalModulesDirectory);
|
|
846
|
-
})
|
|
847
|
-
.catch((error) => {
|
|
848
|
-
this.log.error(`Error getting global node_modules directory: ${error}`);
|
|
849
|
-
});
|
|
850
|
-
}*/
|
|
851
|
-
// Create the data directory .matterbridge in the home directory
|
|
852
688
|
this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
|
|
853
689
|
this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
|
|
854
690
|
try {
|
|
@@ -872,7 +708,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
872
708
|
}
|
|
873
709
|
}
|
|
874
710
|
this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
|
|
875
|
-
// Create the plugin directory Matterbridge in the home directory
|
|
876
711
|
this.matterbridgePluginDirectory = path.join(this.homeDirectory, 'Matterbridge');
|
|
877
712
|
this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
|
|
878
713
|
try {
|
|
@@ -896,28 +731,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
896
731
|
}
|
|
897
732
|
}
|
|
898
733
|
this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
|
|
899
|
-
// Matterbridge version
|
|
900
734
|
const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
|
|
901
735
|
this.matterbridgeVersion = this.matterbridgeLatestVersion = packageJson.version;
|
|
902
736
|
this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeInformation.matterbridgeLatestVersion = this.matterbridgeVersion;
|
|
903
737
|
this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
|
|
904
|
-
// Matterbridge latest version
|
|
905
738
|
if (this.nodeContext)
|
|
906
739
|
this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
|
|
907
740
|
this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
|
|
908
|
-
// this.getMatterbridgeLatestVersion();
|
|
909
|
-
// Current working directory
|
|
910
741
|
const currentDir = process.cwd();
|
|
911
742
|
this.log.debug(`Current Working Directory: ${currentDir}`);
|
|
912
|
-
// Command line arguments (excluding 'node' and the script name)
|
|
913
743
|
const cmdArgs = process.argv.slice(2).join(' ');
|
|
914
744
|
this.log.debug(`Command Line Arguments: ${cmdArgs}`);
|
|
915
745
|
}
|
|
916
|
-
/**
|
|
917
|
-
* Retrieves the latest version of a package from the npm registry.
|
|
918
|
-
* @param packageName - The name of the package.
|
|
919
|
-
* @returns A Promise that resolves to the latest version of the package.
|
|
920
|
-
*/
|
|
921
746
|
async getLatestVersion(packageName) {
|
|
922
747
|
return new Promise((resolve, reject) => {
|
|
923
748
|
this.execRunningCount++;
|
|
@@ -932,10 +757,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
932
757
|
});
|
|
933
758
|
});
|
|
934
759
|
}
|
|
935
|
-
/**
|
|
936
|
-
* Retrieves the path to the global Node.js modules directory.
|
|
937
|
-
* @returns A promise that resolves to the path of the global Node.js modules directory.
|
|
938
|
-
*/
|
|
939
760
|
async getGlobalNodeModules() {
|
|
940
761
|
return new Promise((resolve, reject) => {
|
|
941
762
|
this.execRunningCount++;
|
|
@@ -950,11 +771,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
950
771
|
});
|
|
951
772
|
});
|
|
952
773
|
}
|
|
953
|
-
/**
|
|
954
|
-
* Retrieves the latest version of Matterbridge and performs necessary actions based on the version comparison.
|
|
955
|
-
* @private
|
|
956
|
-
* @returns {Promise<void>} A promise that resolves when the latest version is retrieved and actions are performed.
|
|
957
|
-
*/
|
|
958
774
|
async getMatterbridgeLatestVersion() {
|
|
959
775
|
this.getLatestVersion('matterbridge')
|
|
960
776
|
.then(async (matterbridgeLatestVersion) => {
|
|
@@ -971,19 +787,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
971
787
|
})
|
|
972
788
|
.catch((error) => {
|
|
973
789
|
this.log.error(`Error getting Matterbridge latest version: ${error.message}`);
|
|
974
|
-
// error.stack && this.log.debug(error.stack);
|
|
975
790
|
});
|
|
976
791
|
}
|
|
977
|
-
/**
|
|
978
|
-
* Retrieves the latest version of a plugin and updates the plugin's latestVersion property.
|
|
979
|
-
* If the plugin's version is different from the latest version, logs a warning message.
|
|
980
|
-
* If the plugin's version is the same as the latest version, logs an info message.
|
|
981
|
-
* If there is an error retrieving the latest version, logs an error message.
|
|
982
|
-
*
|
|
983
|
-
* @private
|
|
984
|
-
* @param {RegisteredPlugin} plugin - The plugin for which to retrieve the latest version.
|
|
985
|
-
* @returns {Promise<void>} A promise that resolves when the latest version is retrieved and actions are performed.
|
|
986
|
-
*/
|
|
987
792
|
async getPluginLatestVersion(plugin) {
|
|
988
793
|
this.getLatestVersion(plugin.name)
|
|
989
794
|
.then(async (latestVersion) => {
|
|
@@ -995,54 +800,40 @@ export class Matterbridge extends EventEmitter {
|
|
|
995
800
|
})
|
|
996
801
|
.catch((error) => {
|
|
997
802
|
this.log.error(`Error getting ${plg}${plugin.name}${er} latest version: ${error.message}`);
|
|
998
|
-
// error.stack && this.log.debug(error.stack);
|
|
999
803
|
});
|
|
1000
804
|
}
|
|
1001
|
-
/**
|
|
1002
|
-
* Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (for the frontend).
|
|
1003
|
-
*
|
|
1004
|
-
* @returns {Function} The MatterLogger function.
|
|
1005
|
-
*/
|
|
1006
805
|
createMatterLogger() {
|
|
1007
|
-
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4
|
|
806
|
+
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
|
|
1008
807
|
return (_level, formattedLog) => {
|
|
1009
808
|
const logger = formattedLog.slice(44, 44 + 20).trim();
|
|
1010
809
|
const message = formattedLog.slice(65);
|
|
1011
810
|
matterLogger.logName = logger;
|
|
1012
811
|
switch (_level) {
|
|
1013
812
|
case MatterLogLevel.DEBUG:
|
|
1014
|
-
matterLogger.log("debug"
|
|
813
|
+
matterLogger.log("debug", message);
|
|
1015
814
|
break;
|
|
1016
815
|
case MatterLogLevel.INFO:
|
|
1017
|
-
matterLogger.log("info"
|
|
816
|
+
matterLogger.log("info", message);
|
|
1018
817
|
break;
|
|
1019
818
|
case MatterLogLevel.NOTICE:
|
|
1020
|
-
matterLogger.log("notice"
|
|
819
|
+
matterLogger.log("notice", message);
|
|
1021
820
|
break;
|
|
1022
821
|
case MatterLogLevel.WARN:
|
|
1023
|
-
matterLogger.log("warn"
|
|
822
|
+
matterLogger.log("warn", message);
|
|
1024
823
|
break;
|
|
1025
824
|
case MatterLogLevel.ERROR:
|
|
1026
|
-
matterLogger.log("error"
|
|
825
|
+
matterLogger.log("error", message);
|
|
1027
826
|
break;
|
|
1028
827
|
case MatterLogLevel.FATAL:
|
|
1029
|
-
matterLogger.log("fatal"
|
|
828
|
+
matterLogger.log("fatal", message);
|
|
1030
829
|
break;
|
|
1031
830
|
default:
|
|
1032
|
-
matterLogger.log("debug"
|
|
831
|
+
matterLogger.log("debug", message);
|
|
1033
832
|
break;
|
|
1034
833
|
}
|
|
1035
834
|
};
|
|
1036
835
|
}
|
|
1037
|
-
/**
|
|
1038
|
-
* Creates a Matter File Logger.
|
|
1039
|
-
*
|
|
1040
|
-
* @param {string} filePath - The path to the log file.
|
|
1041
|
-
* @param {boolean} [unlink=false] - Whether to unlink the log file before creating a new one.
|
|
1042
|
-
* @returns {Function} - A function that logs formatted messages to the log file.
|
|
1043
|
-
*/
|
|
1044
836
|
async createMatterFileLogger(filePath, unlink = false) {
|
|
1045
|
-
// 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
|
|
1046
837
|
let fileSize = 0;
|
|
1047
838
|
if (unlink) {
|
|
1048
839
|
try {
|
|
@@ -1091,21 +882,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1091
882
|
}
|
|
1092
883
|
};
|
|
1093
884
|
}
|
|
1094
|
-
/**
|
|
1095
|
-
* Restarts the process by exiting the current instance and loading a new instance.
|
|
1096
|
-
*/
|
|
1097
885
|
async restartProcess() {
|
|
1098
886
|
await this.cleanup('restarting...', true);
|
|
1099
887
|
}
|
|
1100
|
-
/**
|
|
1101
|
-
* Shut down the process by exiting the current process.
|
|
1102
|
-
*/
|
|
1103
888
|
async shutdownProcess() {
|
|
1104
889
|
await this.cleanup('shutting down...', false);
|
|
1105
890
|
}
|
|
1106
|
-
/**
|
|
1107
|
-
* Update matterbridge and and shut down the process.
|
|
1108
|
-
*/
|
|
1109
891
|
async updateProcess() {
|
|
1110
892
|
this.log.info('Updating matterbridge...');
|
|
1111
893
|
try {
|
|
@@ -1118,9 +900,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1118
900
|
this.frontend.wssSendRestartRequired();
|
|
1119
901
|
await this.cleanup('updating...', false);
|
|
1120
902
|
}
|
|
1121
|
-
/**
|
|
1122
|
-
* Unregister all devices and shut down the process.
|
|
1123
|
-
*/
|
|
1124
903
|
async unregisterAndShutdownProcess() {
|
|
1125
904
|
this.log.info('Unregistering all devices and shutting down...');
|
|
1126
905
|
for (const plugin of this.plugins) {
|
|
@@ -1128,9 +907,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1128
907
|
}
|
|
1129
908
|
await this.cleanup('unregistered all devices and shutting down...', false);
|
|
1130
909
|
}
|
|
1131
|
-
/**
|
|
1132
|
-
* Reset commissioning and shut down the process.
|
|
1133
|
-
*/
|
|
1134
910
|
async shutdownProcessAndReset() {
|
|
1135
911
|
this.log.info('Resetting Matterbridge commissioning information...');
|
|
1136
912
|
await this.matterStorageManager?.createContext('events')?.clearAll();
|
|
@@ -1142,12 +918,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1142
918
|
this.log.info('Matter storage reset done! Remove the bridge from the controller.');
|
|
1143
919
|
await this.cleanup('shutting down with reset...', false);
|
|
1144
920
|
}
|
|
1145
|
-
/**
|
|
1146
|
-
* Factory reset and shut down the process.
|
|
1147
|
-
*/
|
|
1148
921
|
async shutdownProcessAndFactoryReset() {
|
|
1149
922
|
try {
|
|
1150
|
-
// Delete old matter storage file and backup
|
|
1151
923
|
const file = path.join(this.matterbridgeDirectory, 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.json');
|
|
1152
924
|
this.log.info(`Unlinking old matter storage file: ${file}`);
|
|
1153
925
|
await fs.unlink(file);
|
|
@@ -1161,7 +933,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1161
933
|
}
|
|
1162
934
|
}
|
|
1163
935
|
try {
|
|
1164
|
-
// Delete matter node storage directory with its subdirectories and backup
|
|
1165
936
|
const dir = path.join(this.matterbridgeDirectory, 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
|
|
1166
937
|
this.log.info(`Removing matter node storage directory: ${dir}`);
|
|
1167
938
|
await fs.rm(dir, { recursive: true });
|
|
@@ -1175,7 +946,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1175
946
|
}
|
|
1176
947
|
}
|
|
1177
948
|
try {
|
|
1178
|
-
// Delete node storage directory with its subdirectories and backup
|
|
1179
949
|
const dir = path.join(this.matterbridgeDirectory, 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
|
|
1180
950
|
this.log.info(`Removing storage directory: ${dir}`);
|
|
1181
951
|
await fs.rm(dir, { recursive: true });
|
|
@@ -1195,41 +965,30 @@ export class Matterbridge extends EventEmitter {
|
|
|
1195
965
|
this.devices.clear();
|
|
1196
966
|
await this.cleanup('shutting down with factory reset...', false);
|
|
1197
967
|
}
|
|
1198
|
-
/**
|
|
1199
|
-
* Cleans up the Matterbridge instance.
|
|
1200
|
-
* @param message - The cleanup message.
|
|
1201
|
-
* @param restart - Indicates whether to restart the instance after cleanup. Default is `false`.
|
|
1202
|
-
* @returns A promise that resolves when the cleanup is completed.
|
|
1203
|
-
*/
|
|
1204
968
|
async cleanup(message, restart = false) {
|
|
1205
969
|
if (this.initialized && !this.hasCleanupStarted) {
|
|
1206
970
|
this.hasCleanupStarted = true;
|
|
1207
971
|
this.log.info(message);
|
|
1208
|
-
// Clear the start matter interval
|
|
1209
972
|
if (this.startMatterInterval) {
|
|
1210
973
|
clearInterval(this.startMatterInterval);
|
|
1211
974
|
this.startMatterInterval = undefined;
|
|
1212
975
|
this.log.debug('Start matter interval cleared');
|
|
1213
976
|
}
|
|
1214
|
-
// Clear the check update interval
|
|
1215
977
|
if (this.checkUpdateInterval) {
|
|
1216
978
|
clearInterval(this.checkUpdateInterval);
|
|
1217
979
|
this.checkUpdateInterval = undefined;
|
|
1218
980
|
this.log.debug('Check update interval cleared');
|
|
1219
981
|
}
|
|
1220
|
-
// Clear the configure timeout
|
|
1221
982
|
if (this.configureTimeout) {
|
|
1222
983
|
clearTimeout(this.configureTimeout);
|
|
1223
984
|
this.configureTimeout = undefined;
|
|
1224
985
|
this.log.debug('Matterbridge configure timeout cleared');
|
|
1225
986
|
}
|
|
1226
|
-
// Clear the reachability timeout
|
|
1227
987
|
if (this.reachabilityTimeout) {
|
|
1228
988
|
clearTimeout(this.reachabilityTimeout);
|
|
1229
989
|
this.reachabilityTimeout = undefined;
|
|
1230
990
|
this.log.debug('Matterbridge reachability timeout cleared');
|
|
1231
991
|
}
|
|
1232
|
-
// Calling the shutdown method of each plugin and clear the plugins reachability timeout
|
|
1233
992
|
for (const plugin of this.plugins) {
|
|
1234
993
|
if (!plugin.enabled || plugin.error)
|
|
1235
994
|
continue;
|
|
@@ -1240,9 +999,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1240
999
|
this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
|
|
1241
1000
|
}
|
|
1242
1001
|
}
|
|
1243
|
-
// Stop the frontend
|
|
1244
1002
|
await this.frontend.stop();
|
|
1245
|
-
// Stopping matter server nodes
|
|
1246
1003
|
this.log.notice(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
|
|
1247
1004
|
if (this.bridgeMode === 'bridge') {
|
|
1248
1005
|
if (this.serverNode) {
|
|
@@ -1259,35 +1016,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
1259
1016
|
}
|
|
1260
1017
|
}
|
|
1261
1018
|
this.log.notice('Stopped matter server nodes');
|
|
1262
|
-
// Stop matter storage
|
|
1263
1019
|
await this.stopMatterStorage();
|
|
1264
|
-
// Remove the matterfilelogger
|
|
1265
1020
|
try {
|
|
1266
1021
|
Logger.removeLogger('matterfilelogger');
|
|
1267
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1268
1022
|
}
|
|
1269
1023
|
catch (error) {
|
|
1270
|
-
// this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridgeDirectory, this.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
|
|
1271
1024
|
}
|
|
1272
|
-
// Serialize registeredDevices
|
|
1273
1025
|
if (this.nodeStorage && this.nodeContext) {
|
|
1274
|
-
/*
|
|
1275
|
-
TODO: Implement serialization of registered devices in edge mode
|
|
1276
|
-
this.log.info('Saving registered devices...');
|
|
1277
|
-
const serializedRegisteredDevices: SerializedMatterbridgeEndpoint[] = [];
|
|
1278
|
-
this.devices.forEach(async (device) => {
|
|
1279
|
-
const serializedMatterbridgeDevice = MatterbridgeEndpoint.serialize(device);
|
|
1280
|
-
// this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
|
|
1281
|
-
if (serializedMatterbridgeDevice) serializedRegisteredDevices.push(serializedMatterbridgeDevice);
|
|
1282
|
-
});
|
|
1283
|
-
await this.nodeContext.set<SerializedMatterbridgeEndpoint[]>('devices', serializedRegisteredDevices);
|
|
1284
|
-
this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
|
|
1285
|
-
*/
|
|
1286
|
-
// Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
|
|
1287
1026
|
this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
|
|
1288
1027
|
await this.nodeContext.close();
|
|
1289
1028
|
this.nodeContext = undefined;
|
|
1290
|
-
// Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
|
|
1291
1029
|
for (const plugin of this.plugins) {
|
|
1292
1030
|
if (plugin.nodeContext) {
|
|
1293
1031
|
this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
|
|
@@ -1304,13 +1042,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1304
1042
|
}
|
|
1305
1043
|
this.plugins.clear();
|
|
1306
1044
|
this.devices.clear();
|
|
1307
|
-
// Deregisters the process handlers
|
|
1308
1045
|
this.deregisterProcesslHandlers();
|
|
1309
1046
|
if (restart) {
|
|
1310
1047
|
if (message === 'updating...') {
|
|
1311
1048
|
this.log.info('Cleanup completed. Updating...');
|
|
1312
1049
|
Matterbridge.instance = undefined;
|
|
1313
|
-
this.emit('update');
|
|
1050
|
+
this.emit('update');
|
|
1314
1051
|
}
|
|
1315
1052
|
else if (message === 'restarting...') {
|
|
1316
1053
|
this.log.info('Cleanup completed. Restarting...');
|
|
@@ -1327,14 +1064,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1327
1064
|
this.initialized = false;
|
|
1328
1065
|
}
|
|
1329
1066
|
}
|
|
1330
|
-
/**
|
|
1331
|
-
* Creates and configures the server node for an accessory plugin for a given device.
|
|
1332
|
-
*
|
|
1333
|
-
* @param {RegisteredPlugin} plugin - The plugin to configure.
|
|
1334
|
-
* @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
|
|
1335
|
-
* @param {boolean} [start=false] - Whether to start the server node after adding the device.
|
|
1336
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
|
|
1337
|
-
*/
|
|
1338
1067
|
async createAccessoryPlugin(plugin, device, start = false) {
|
|
1339
1068
|
if (!plugin.locked && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
|
|
1340
1069
|
plugin.locked = true;
|
|
@@ -1346,13 +1075,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1346
1075
|
await this.startServerNode(plugin.serverNode);
|
|
1347
1076
|
}
|
|
1348
1077
|
}
|
|
1349
|
-
/**
|
|
1350
|
-
* Creates and configures the server node for a dynamic plugin.
|
|
1351
|
-
*
|
|
1352
|
-
* @param {RegisteredPlugin} plugin - The plugin to configure.
|
|
1353
|
-
* @param {boolean} [start=false] - Whether to start the server node after adding the aggregator node.
|
|
1354
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the dynamic plugin is created and configured.
|
|
1355
|
-
*/
|
|
1356
1078
|
async createDynamicPlugin(plugin, start = false) {
|
|
1357
1079
|
if (!plugin.locked) {
|
|
1358
1080
|
plugin.locked = true;
|
|
@@ -1364,13 +1086,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1364
1086
|
await this.startServerNode(plugin.serverNode);
|
|
1365
1087
|
}
|
|
1366
1088
|
}
|
|
1367
|
-
/**
|
|
1368
|
-
* Starts the Matterbridge in bridge mode.
|
|
1369
|
-
* @private
|
|
1370
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1371
|
-
*/
|
|
1372
1089
|
async startBridge() {
|
|
1373
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1374
1090
|
if (!this.matterStorageManager)
|
|
1375
1091
|
throw new Error('No storage manager initialized');
|
|
1376
1092
|
if (!this.matterbridgeContext)
|
|
@@ -1407,9 +1123,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1407
1123
|
clearInterval(this.startMatterInterval);
|
|
1408
1124
|
this.startMatterInterval = undefined;
|
|
1409
1125
|
this.log.debug('Cleared startMatterInterval interval for Matterbridge');
|
|
1410
|
-
// Start the Matter server node
|
|
1411
1126
|
this.startServerNode(this.serverNode);
|
|
1412
|
-
// Configure the plugins
|
|
1413
1127
|
this.configureTimeout = setTimeout(async () => {
|
|
1414
1128
|
for (const plugin of this.plugins) {
|
|
1415
1129
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1424,7 +1138,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1424
1138
|
}
|
|
1425
1139
|
this.frontend.wssSendRefreshRequired();
|
|
1426
1140
|
}, 30 * 1000);
|
|
1427
|
-
// Setting reachability to true
|
|
1428
1141
|
this.reachabilityTimeout = setTimeout(() => {
|
|
1429
1142
|
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1430
1143
|
if (this.serverNode)
|
|
@@ -1435,14 +1148,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1435
1148
|
}, 60 * 1000);
|
|
1436
1149
|
}, 1000);
|
|
1437
1150
|
}
|
|
1438
|
-
/**
|
|
1439
|
-
* Starts the Matterbridge in childbridge mode.
|
|
1440
|
-
* @private
|
|
1441
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1442
|
-
*/
|
|
1443
1151
|
async startChildbridge() {
|
|
1444
|
-
// Matterbridge.addBridgedDevice creates the commissionig servers and add the devices to the the commissioning server or to the aggregator
|
|
1445
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1446
1152
|
if (!this.matterStorageManager)
|
|
1447
1153
|
throw new Error('No storage manager initialized');
|
|
1448
1154
|
for (const plugin of this.plugins) {
|
|
@@ -1489,13 +1195,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1489
1195
|
clearInterval(this.startMatterInterval);
|
|
1490
1196
|
this.startMatterInterval = undefined;
|
|
1491
1197
|
this.log.debug('Cleared startMatterInterval interval in childbridge mode');
|
|
1492
|
-
// Configure the plugins
|
|
1493
1198
|
this.configureTimeout = setTimeout(async () => {
|
|
1494
1199
|
for (const plugin of this.plugins) {
|
|
1495
1200
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
1496
1201
|
continue;
|
|
1497
1202
|
try {
|
|
1498
|
-
await this.plugins.configure(plugin);
|
|
1203
|
+
await this.plugins.configure(plugin);
|
|
1499
1204
|
}
|
|
1500
1205
|
catch (error) {
|
|
1501
1206
|
plugin.error = true;
|
|
@@ -1523,9 +1228,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1523
1228
|
this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
|
|
1524
1229
|
continue;
|
|
1525
1230
|
}
|
|
1526
|
-
// Start the Matter server node
|
|
1527
1231
|
this.startServerNode(plugin.serverNode);
|
|
1528
|
-
// Setting reachability to true
|
|
1529
1232
|
plugin.reachabilityTimeout = setTimeout(() => {
|
|
1530
1233
|
this.log.info(`Setting reachability to true for ${plg}${plugin.name}${db}`);
|
|
1531
1234
|
if (plugin.serverNode)
|
|
@@ -1539,219 +1242,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
1539
1242
|
}
|
|
1540
1243
|
}, 1000);
|
|
1541
1244
|
}
|
|
1542
|
-
/**
|
|
1543
|
-
* Starts the Matterbridge controller.
|
|
1544
|
-
* @private
|
|
1545
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1546
|
-
*/
|
|
1547
1245
|
async startController() {
|
|
1548
|
-
/*
|
|
1549
|
-
if (!this.storageManager) {
|
|
1550
|
-
this.log.error('No storage manager initialized');
|
|
1551
|
-
await this.cleanup('No storage manager initialized');
|
|
1552
|
-
return;
|
|
1553
|
-
}
|
|
1554
|
-
this.log.info('Creating context: mattercontrollerContext');
|
|
1555
|
-
this.mattercontrollerContext = this.storageManager.createContext('mattercontrollerContext');
|
|
1556
|
-
if (!this.mattercontrollerContext) {
|
|
1557
|
-
this.log.error('No storage context mattercontrollerContext initialized');
|
|
1558
|
-
await this.cleanup('No storage context mattercontrollerContext initialized');
|
|
1559
|
-
return;
|
|
1560
|
-
}
|
|
1561
|
-
|
|
1562
|
-
this.log.debug('Starting matterbridge in mode', this.bridgeMode);
|
|
1563
|
-
this.matterServer = await this.createMatterServer(this.storageManager);
|
|
1564
|
-
this.log.info('Creating matter commissioning controller');
|
|
1565
|
-
this.commissioningController = new CommissioningController({
|
|
1566
|
-
autoConnect: false,
|
|
1567
|
-
});
|
|
1568
|
-
this.log.info('Adding matter commissioning controller to matter server');
|
|
1569
|
-
await this.matterServer.addCommissioningController(this.commissioningController);
|
|
1570
|
-
|
|
1571
|
-
this.log.info('Starting matter server');
|
|
1572
|
-
await this.matterServer.start();
|
|
1573
|
-
this.log.info('Matter server started');
|
|
1574
|
-
|
|
1575
|
-
if (hasParameter('pairingcode')) {
|
|
1576
|
-
this.log.info('Pairing device with pairingcode:', getParameter('pairingcode'));
|
|
1577
|
-
const pairingCode = getParameter('pairingcode');
|
|
1578
|
-
const ip = this.mattercontrollerContext.has('ip') ? this.mattercontrollerContext.get<string>('ip') : undefined;
|
|
1579
|
-
const port = this.mattercontrollerContext.has('port') ? this.mattercontrollerContext.get<number>('port') : undefined;
|
|
1580
|
-
|
|
1581
|
-
let longDiscriminator, setupPin, shortDiscriminator;
|
|
1582
|
-
if (pairingCode !== undefined) {
|
|
1583
|
-
const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
|
|
1584
|
-
shortDiscriminator = pairingCodeCodec.shortDiscriminator;
|
|
1585
|
-
longDiscriminator = undefined;
|
|
1586
|
-
setupPin = pairingCodeCodec.passcode;
|
|
1587
|
-
this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
|
|
1588
|
-
} else {
|
|
1589
|
-
longDiscriminator = await this.mattercontrollerContext.get('longDiscriminator', 3840);
|
|
1590
|
-
if (longDiscriminator > 4095) throw new Error('Discriminator value must be less than 4096');
|
|
1591
|
-
setupPin = this.mattercontrollerContext.get('pin', 20202021);
|
|
1592
|
-
}
|
|
1593
|
-
if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
|
|
1594
|
-
throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
|
|
1595
|
-
}
|
|
1596
|
-
|
|
1597
|
-
const commissioningOptions: ControllerCommissioningFlowOptions = {
|
|
1598
|
-
regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
|
|
1599
|
-
regulatoryCountryCode: 'XX',
|
|
1600
|
-
};
|
|
1601
|
-
const options = {
|
|
1602
|
-
commissioning: commissioningOptions,
|
|
1603
|
-
discovery: {
|
|
1604
|
-
knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
|
|
1605
|
-
identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
|
|
1606
|
-
},
|
|
1607
|
-
passcode: setupPin,
|
|
1608
|
-
} as NodeCommissioningOptions;
|
|
1609
|
-
this.log.info('Commissioning with options:', options);
|
|
1610
|
-
const nodeId = await this.commissioningController.commissionNode(options);
|
|
1611
|
-
this.log.info(`Commissioning successfully done with nodeId: ${nodeId}`);
|
|
1612
|
-
this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
|
|
1613
|
-
} // (hasParameter('pairingcode'))
|
|
1614
|
-
|
|
1615
|
-
if (hasParameter('unpairall')) {
|
|
1616
|
-
this.log.info('***Commissioning controller unpairing all nodes...');
|
|
1617
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1618
|
-
for (const nodeId of nodeIds) {
|
|
1619
|
-
this.log.info('***Commissioning controller unpairing node:', nodeId);
|
|
1620
|
-
await this.commissioningController.removeNode(nodeId);
|
|
1621
|
-
}
|
|
1622
|
-
return;
|
|
1623
|
-
}
|
|
1624
|
-
|
|
1625
|
-
if (hasParameter('discover')) {
|
|
1626
|
-
// const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
|
|
1627
|
-
// console.log(discover);
|
|
1628
|
-
}
|
|
1629
|
-
|
|
1630
|
-
if (!this.commissioningController.isCommissioned()) {
|
|
1631
|
-
this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
|
|
1632
|
-
return;
|
|
1633
|
-
}
|
|
1634
|
-
|
|
1635
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1636
|
-
this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
|
|
1637
|
-
for (const nodeId of nodeIds) {
|
|
1638
|
-
this.log.info(`***Connecting to commissioned node: ${nodeId}`);
|
|
1639
|
-
|
|
1640
|
-
const node = await this.commissioningController.connectNode(nodeId, {
|
|
1641
|
-
autoSubscribe: false,
|
|
1642
|
-
attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) =>
|
|
1643
|
-
this.log.info(`***Commissioning controller attributeChangedCallback ${peerNodeId}: attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
|
|
1644
|
-
eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) =>
|
|
1645
|
-
this.log.info(`***Commissioning controller eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
|
|
1646
|
-
stateInformationCallback: (peerNodeId, info) => {
|
|
1647
|
-
switch (info) {
|
|
1648
|
-
case NodeStateInformation.Connected:
|
|
1649
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
|
|
1650
|
-
break;
|
|
1651
|
-
case NodeStateInformation.Disconnected:
|
|
1652
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
|
|
1653
|
-
break;
|
|
1654
|
-
case NodeStateInformation.Reconnecting:
|
|
1655
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
|
|
1656
|
-
break;
|
|
1657
|
-
case NodeStateInformation.WaitingForDeviceDiscovery:
|
|
1658
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
|
|
1659
|
-
break;
|
|
1660
|
-
case NodeStateInformation.StructureChanged:
|
|
1661
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
|
|
1662
|
-
break;
|
|
1663
|
-
case NodeStateInformation.Decommissioned:
|
|
1664
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
|
|
1665
|
-
break;
|
|
1666
|
-
default:
|
|
1667
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
|
|
1668
|
-
break;
|
|
1669
|
-
}
|
|
1670
|
-
},
|
|
1671
|
-
});
|
|
1672
|
-
|
|
1673
|
-
node.logStructure();
|
|
1674
|
-
|
|
1675
|
-
// Get the interaction client
|
|
1676
|
-
this.log.info('Getting the interaction client');
|
|
1677
|
-
const interactionClient = await node.getInteractionClient();
|
|
1678
|
-
let cluster;
|
|
1679
|
-
let attributes;
|
|
1680
|
-
|
|
1681
|
-
// Log BasicInformationCluster
|
|
1682
|
-
cluster = BasicInformationCluster;
|
|
1683
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1684
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1685
|
-
});
|
|
1686
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1687
|
-
attributes.forEach((attribute) => {
|
|
1688
|
-
this.log.info(
|
|
1689
|
-
`- 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}`,
|
|
1690
|
-
);
|
|
1691
|
-
});
|
|
1692
|
-
|
|
1693
|
-
// Log PowerSourceCluster
|
|
1694
|
-
cluster = PowerSourceCluster;
|
|
1695
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1696
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1697
|
-
});
|
|
1698
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1699
|
-
attributes.forEach((attribute) => {
|
|
1700
|
-
this.log.info(
|
|
1701
|
-
`- 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}`,
|
|
1702
|
-
);
|
|
1703
|
-
});
|
|
1704
|
-
|
|
1705
|
-
// Log ThreadNetworkDiagnostics
|
|
1706
|
-
cluster = ThreadNetworkDiagnosticsCluster;
|
|
1707
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1708
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1709
|
-
});
|
|
1710
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1711
|
-
attributes.forEach((attribute) => {
|
|
1712
|
-
this.log.info(
|
|
1713
|
-
`- 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}`,
|
|
1714
|
-
);
|
|
1715
|
-
});
|
|
1716
|
-
|
|
1717
|
-
// Log SwitchCluster
|
|
1718
|
-
cluster = SwitchCluster;
|
|
1719
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1720
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1721
|
-
});
|
|
1722
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1723
|
-
attributes.forEach((attribute) => {
|
|
1724
|
-
this.log.info(
|
|
1725
|
-
`- 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}`,
|
|
1726
|
-
);
|
|
1727
|
-
});
|
|
1728
|
-
|
|
1729
|
-
this.log.info('Subscribing to all attributes and events');
|
|
1730
|
-
await node.subscribeAllAttributesAndEvents({
|
|
1731
|
-
ignoreInitialTriggers: false,
|
|
1732
|
-
attributeChangedCallback: ({ path: { nodeId, clusterId, endpointId, attributeName }, version, value }) =>
|
|
1733
|
-
this.log.info(
|
|
1734
|
-
`***${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}`,
|
|
1735
|
-
),
|
|
1736
|
-
eventTriggeredCallback: ({ path: { nodeId, clusterId, endpointId, eventName }, events }) => {
|
|
1737
|
-
this.log.info(
|
|
1738
|
-
`***${db}Commissioning controller eventTriggeredCallback: event ${BLUE}${nodeId}${db}/${or}${endpointId}${db}/${hk}${getClusterNameById(clusterId)}${db}/${zb}${eventName}${db} triggered with ${debugStringify(events ?? { none: true })}`,
|
|
1739
|
-
);
|
|
1740
|
-
},
|
|
1741
|
-
});
|
|
1742
|
-
this.log.info('Subscribed to all attributes and events');
|
|
1743
|
-
}
|
|
1744
|
-
*/
|
|
1745
1246
|
}
|
|
1746
|
-
/** ***********************************************************************************************************************************/
|
|
1747
|
-
/** Matter.js methods */
|
|
1748
|
-
/** ***********************************************************************************************************************************/
|
|
1749
|
-
/**
|
|
1750
|
-
* Starts the matter storage process with name Matterbridge.
|
|
1751
|
-
* @returns {Promise<void>} - A promise that resolves when the storage process is started.
|
|
1752
|
-
*/
|
|
1753
1247
|
async startMatterStorage() {
|
|
1754
|
-
// Setup Matter storage
|
|
1755
1248
|
this.log.info(`Starting matter node storage...`);
|
|
1756
1249
|
this.matterStorageService = this.environment.get(StorageService);
|
|
1757
1250
|
this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
|
|
@@ -1759,25 +1252,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1759
1252
|
this.log.info('Matter node storage manager "Matterbridge" created');
|
|
1760
1253
|
this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', bridge.code, this.aggregatorVendorId, 'Matterbridge', this.aggregatorProductId, 'Matterbridge aggregator');
|
|
1761
1254
|
this.log.info('Matter node storage started');
|
|
1762
|
-
// Backup matter storage since it is created/opened correctly
|
|
1763
1255
|
await this.backupMatterStorage(path.join(this.matterbridgeDirectory, this.matterStorageName), path.join(this.matterbridgeDirectory, this.matterStorageName + '.backup'));
|
|
1764
1256
|
}
|
|
1765
|
-
/**
|
|
1766
|
-
* Makes a backup copy of the specified matter storage directory.
|
|
1767
|
-
*
|
|
1768
|
-
* @param storageName - The name of the storage directory to be backed up.
|
|
1769
|
-
* @param backupName - The name of the backup directory to be created.
|
|
1770
|
-
* @returns {Promise<void>} A promise that resolves when the has been done.
|
|
1771
|
-
*/
|
|
1772
1257
|
async backupMatterStorage(storageName, backupName) {
|
|
1773
1258
|
this.log.info('Creating matter node storage backup...');
|
|
1774
1259
|
await copyDirectory(storageName, backupName);
|
|
1775
1260
|
this.log.info('Created matter node storage backup');
|
|
1776
1261
|
}
|
|
1777
|
-
/**
|
|
1778
|
-
* Stops the matter storage.
|
|
1779
|
-
* @returns {Promise<void>} A promise that resolves when the storage is stopped.
|
|
1780
|
-
*/
|
|
1781
1262
|
async stopMatterStorage() {
|
|
1782
1263
|
this.log.info('Closing matter node storage...');
|
|
1783
1264
|
this.matterStorageManager?.close();
|
|
@@ -1786,19 +1267,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1786
1267
|
this.matterbridgeContext = undefined;
|
|
1787
1268
|
this.log.info('Matter node storage closed');
|
|
1788
1269
|
}
|
|
1789
|
-
/**
|
|
1790
|
-
* Creates a server node storage context.
|
|
1791
|
-
*
|
|
1792
|
-
* @param {string} pluginName - The name of the plugin.
|
|
1793
|
-
* @param {string} deviceName - The name of the device.
|
|
1794
|
-
* @param {DeviceTypeId} deviceType - The device type of the device.
|
|
1795
|
-
* @param {number} vendorId - The vendor ID.
|
|
1796
|
-
* @param {string} vendorName - The vendor name.
|
|
1797
|
-
* @param {number} productId - The product ID.
|
|
1798
|
-
* @param {string} productName - The product name.
|
|
1799
|
-
* @param {string} [serialNumber] - The serial number of the device (optional).
|
|
1800
|
-
* @returns {Promise<StorageContext>} The storage context for the commissioning server.
|
|
1801
|
-
*/
|
|
1802
1270
|
async createServerNodeContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber) {
|
|
1803
1271
|
if (!this.matterStorageService)
|
|
1804
1272
|
throw new Error('No storage service initialized');
|
|
@@ -1831,15 +1299,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1831
1299
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1832
1300
|
return storageContext;
|
|
1833
1301
|
}
|
|
1834
|
-
/**
|
|
1835
|
-
* Creates a server node.
|
|
1836
|
-
*
|
|
1837
|
-
* @param {StorageContext} storageContext - The storage context for the server node.
|
|
1838
|
-
* @param {number} [port=5540] - The port number for the server node. Defaults to 5540.
|
|
1839
|
-
* @param {number} [passcode=20242025] - The passcode for the server node. Defaults to 20242025.
|
|
1840
|
-
* @param {number} [discriminator=3850] - The discriminator for the server node. Defaults to 3850.
|
|
1841
|
-
* @returns {Promise<ServerNode<ServerNode.RootEndpoint>>} A promise that resolves to the created server node.
|
|
1842
|
-
*/
|
|
1843
1302
|
async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
|
|
1844
1303
|
const storeId = await storageContext.get('storeId');
|
|
1845
1304
|
this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
|
|
@@ -1849,33 +1308,21 @@ export class Matterbridge extends EventEmitter {
|
|
|
1849
1308
|
this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
|
|
1850
1309
|
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
1851
1310
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1852
|
-
/**
|
|
1853
|
-
* Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration
|
|
1854
|
-
*/
|
|
1855
1311
|
const serverNode = await ServerNode.create({
|
|
1856
|
-
// Required: Give the Node a unique ID which is used to store the state of this node
|
|
1857
1312
|
id: storeId,
|
|
1858
|
-
// Provide Network relevant configuration like the port
|
|
1859
|
-
// Optional when operating only one device on a host, Default port is 5540
|
|
1860
1313
|
network: {
|
|
1861
1314
|
listeningAddressIpv4: this.ipv4address,
|
|
1862
1315
|
listeningAddressIpv6: this.ipv6address,
|
|
1863
1316
|
port,
|
|
1864
1317
|
},
|
|
1865
|
-
// Provide Commissioning relevant settings
|
|
1866
|
-
// Optional for development/testing purposes
|
|
1867
1318
|
commissioning: {
|
|
1868
1319
|
passcode,
|
|
1869
1320
|
discriminator,
|
|
1870
1321
|
},
|
|
1871
|
-
// Provide Node announcement settings
|
|
1872
|
-
// Optional: If Ommitted some development defaults are used
|
|
1873
1322
|
productDescription: {
|
|
1874
1323
|
name: await storageContext.get('deviceName'),
|
|
1875
1324
|
deviceType: DeviceTypeId(await storageContext.get('deviceType')),
|
|
1876
1325
|
},
|
|
1877
|
-
// Provide defaults for the BasicInformation cluster on the Root endpoint
|
|
1878
|
-
// Optional: If Omitted some development defaults are used
|
|
1879
1326
|
basicInformation: {
|
|
1880
1327
|
vendorId: VendorId(await storageContext.get('vendorId')),
|
|
1881
1328
|
vendorName: await storageContext.get('vendorName'),
|
|
@@ -1892,13 +1339,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1892
1339
|
},
|
|
1893
1340
|
});
|
|
1894
1341
|
const sanitizeFabrics = (fabrics, resetSessions = false) => {
|
|
1895
|
-
// New type of fabric information: Record<FabricIndex, ExposedFabricInformation>
|
|
1896
1342
|
const sanitizedFabrics = this.sanitizeFabricInformations(Array.from(Object.values(fabrics)));
|
|
1897
1343
|
this.log.info(`Fabrics: ${debugStringify(sanitizedFabrics)}`);
|
|
1898
1344
|
if (this.bridgeMode === 'bridge') {
|
|
1899
1345
|
this.matterbridgeFabricInformations = sanitizedFabrics;
|
|
1900
1346
|
if (resetSessions)
|
|
1901
|
-
this.matterbridgeSessionInformations = undefined;
|
|
1347
|
+
this.matterbridgeSessionInformations = undefined;
|
|
1902
1348
|
this.matterbridgePaired = true;
|
|
1903
1349
|
}
|
|
1904
1350
|
if (this.bridgeMode === 'childbridge') {
|
|
@@ -1906,19 +1352,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1906
1352
|
if (plugin) {
|
|
1907
1353
|
plugin.fabricInformations = sanitizedFabrics;
|
|
1908
1354
|
if (resetSessions)
|
|
1909
|
-
plugin.sessionInformations = undefined;
|
|
1355
|
+
plugin.sessionInformations = undefined;
|
|
1910
1356
|
plugin.paired = true;
|
|
1911
1357
|
}
|
|
1912
1358
|
}
|
|
1913
1359
|
};
|
|
1914
|
-
/**
|
|
1915
|
-
* This event is triggered when the device is initially commissioned successfully.
|
|
1916
|
-
* This means: It is added to the first fabric.
|
|
1917
|
-
*/
|
|
1918
1360
|
serverNode.lifecycle.commissioned.on(() => this.log.notice(`Server node for ${storeId} was initially commissioned successfully!`));
|
|
1919
|
-
/** This event is triggered when all fabrics are removed from the device, usually it also does a factory reset then. */
|
|
1920
1361
|
serverNode.lifecycle.decommissioned.on(() => this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`));
|
|
1921
|
-
/** This event is triggered when the device went online. This means that it is discoverable in the network. */
|
|
1922
1362
|
serverNode.lifecycle.online.on(async () => {
|
|
1923
1363
|
this.log.notice(`Server node for ${storeId} is online`);
|
|
1924
1364
|
if (!serverNode.lifecycle.isCommissioned) {
|
|
@@ -1964,7 +1404,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1964
1404
|
}
|
|
1965
1405
|
this.frontend.wssSendRefreshRequired();
|
|
1966
1406
|
});
|
|
1967
|
-
/** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
|
|
1968
1407
|
serverNode.lifecycle.offline.on(() => {
|
|
1969
1408
|
this.log.notice(`Server node for ${storeId} is offline`);
|
|
1970
1409
|
if (this.bridgeMode === 'bridge') {
|
|
@@ -1986,10 +1425,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1986
1425
|
}
|
|
1987
1426
|
this.frontend.wssSendRefreshRequired();
|
|
1988
1427
|
});
|
|
1989
|
-
/**
|
|
1990
|
-
* This event is triggered when a fabric is added, removed or updated on the device. Use this if more granular
|
|
1991
|
-
* information is needed.
|
|
1992
|
-
*/
|
|
1993
1428
|
serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
|
|
1994
1429
|
let action = '';
|
|
1995
1430
|
switch (fabricAction) {
|
|
@@ -2023,24 +1458,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
2023
1458
|
}
|
|
2024
1459
|
}
|
|
2025
1460
|
};
|
|
2026
|
-
/**
|
|
2027
|
-
* This event is triggered when an operative new session was opened by a Controller.
|
|
2028
|
-
* It is not triggered for the initial commissioning process, just afterwards for real connections.
|
|
2029
|
-
*/
|
|
2030
1461
|
serverNode.events.sessions.opened.on((session) => {
|
|
2031
1462
|
this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2032
1463
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
2033
1464
|
this.frontend.wssSendRefreshRequired();
|
|
2034
1465
|
});
|
|
2035
|
-
/**
|
|
2036
|
-
* This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
|
|
2037
|
-
*/
|
|
2038
1466
|
serverNode.events.sessions.closed.on((session) => {
|
|
2039
1467
|
this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2040
1468
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
2041
1469
|
this.frontend.wssSendRefreshRequired();
|
|
2042
1470
|
});
|
|
2043
|
-
/** This event is triggered when a subscription gets added or removed on an operative session. */
|
|
2044
1471
|
serverNode.events.sessions.subscriptionsChanged.on((session) => {
|
|
2045
1472
|
this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2046
1473
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
@@ -2049,39 +1476,38 @@ export class Matterbridge extends EventEmitter {
|
|
|
2049
1476
|
this.log.info(`Created server node for ${storeId}`);
|
|
2050
1477
|
return serverNode;
|
|
2051
1478
|
}
|
|
2052
|
-
/**
|
|
2053
|
-
* Starts the specified server node.
|
|
2054
|
-
*
|
|
2055
|
-
* @param {ServerNode} [matterServerNode] - The server node to start.
|
|
2056
|
-
* @returns {Promise<void>} A promise that resolves when the server node has started.
|
|
2057
|
-
*/
|
|
2058
1479
|
async startServerNode(matterServerNode) {
|
|
2059
1480
|
if (!matterServerNode)
|
|
2060
1481
|
return;
|
|
2061
1482
|
this.log.notice(`Starting ${matterServerNode.id} server node`);
|
|
2062
1483
|
await matterServerNode.start();
|
|
2063
1484
|
}
|
|
2064
|
-
/**
|
|
2065
|
-
* Stops the specified server node.
|
|
2066
|
-
*
|
|
2067
|
-
* @param {ServerNode} matterServerNode - The server node to stop.
|
|
2068
|
-
* @returns {Promise<void>} A promise that resolves when the server node has stopped.
|
|
2069
|
-
*/
|
|
2070
1485
|
async stopServerNode(matterServerNode) {
|
|
2071
1486
|
if (!matterServerNode)
|
|
2072
1487
|
return;
|
|
2073
1488
|
this.log.notice(`Closing ${matterServerNode.id} server node`);
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
1489
|
+
const withTimeout = (promise, ms) => {
|
|
1490
|
+
return new Promise((resolve, reject) => {
|
|
1491
|
+
const timer = setTimeout(() => reject(new Error('Operation timed out')), ms);
|
|
1492
|
+
promise
|
|
1493
|
+
.then((result) => {
|
|
1494
|
+
clearTimeout(timer);
|
|
1495
|
+
resolve(result);
|
|
1496
|
+
})
|
|
1497
|
+
.catch((error) => {
|
|
1498
|
+
clearTimeout(timer);
|
|
1499
|
+
reject(error);
|
|
1500
|
+
});
|
|
1501
|
+
});
|
|
1502
|
+
};
|
|
1503
|
+
try {
|
|
1504
|
+
await withTimeout(matterServerNode.close(), 5000);
|
|
1505
|
+
this.log.info(`Closed ${matterServerNode.id} server node`);
|
|
1506
|
+
}
|
|
1507
|
+
catch (error) {
|
|
1508
|
+
this.log.error(`Failed to close ${matterServerNode.id} server node: ${error instanceof Error ? error.message : error}`);
|
|
1509
|
+
}
|
|
2078
1510
|
}
|
|
2079
|
-
/**
|
|
2080
|
-
* Advertises the specified server node if it is commissioned.
|
|
2081
|
-
*
|
|
2082
|
-
* @param {ServerNode} [matterServerNode] - The server node to advertise.
|
|
2083
|
-
* @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.
|
|
2084
|
-
*/
|
|
2085
1511
|
async advertiseServerNode(matterServerNode) {
|
|
2086
1512
|
if (matterServerNode && matterServerNode.lifecycle.isCommissioned) {
|
|
2087
1513
|
await matterServerNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
|
|
@@ -2091,32 +1517,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
2091
1517
|
}
|
|
2092
1518
|
return undefined;
|
|
2093
1519
|
}
|
|
2094
|
-
/**
|
|
2095
|
-
* Creates an aggregator node with the specified storage context.
|
|
2096
|
-
*
|
|
2097
|
-
* @param {StorageContext} storageContext - The storage context for the aggregator node.
|
|
2098
|
-
* @returns {Promise<EndpointNode<AggregatorEndpoint>>} A promise that resolves to the created aggregator node.
|
|
2099
|
-
*/
|
|
2100
1520
|
async createAggregatorNode(storageContext) {
|
|
2101
1521
|
this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator `);
|
|
2102
1522
|
const aggregatorNode = new EndpointNode(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
|
|
2103
1523
|
return aggregatorNode;
|
|
2104
1524
|
}
|
|
2105
|
-
/**
|
|
2106
|
-
* Adds a MatterbridgeEndpoint to the specified plugin.
|
|
2107
|
-
*
|
|
2108
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2109
|
-
* @param {MatterbridgeEndpoint} device - The device to add as a bridged endpoint.
|
|
2110
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been added.
|
|
2111
|
-
*/
|
|
2112
1525
|
async addBridgedEndpoint(pluginName, device) {
|
|
2113
|
-
// Check if the plugin is registered
|
|
2114
1526
|
const plugin = this.plugins.get(pluginName);
|
|
2115
1527
|
if (!plugin) {
|
|
2116
1528
|
this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
|
|
2117
1529
|
return;
|
|
2118
1530
|
}
|
|
2119
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2120
1531
|
if (this.bridgeMode === 'bridge') {
|
|
2121
1532
|
this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
|
|
2122
1533
|
if (!this.aggregatorNode)
|
|
@@ -2139,26 +1550,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
2139
1550
|
plugin.registeredDevices++;
|
|
2140
1551
|
if (plugin.addedDevices !== undefined)
|
|
2141
1552
|
plugin.addedDevices++;
|
|
2142
|
-
// Add the device to the DeviceManager
|
|
2143
1553
|
this.devices.set(device);
|
|
2144
1554
|
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}`);
|
|
2145
1555
|
}
|
|
2146
|
-
/**
|
|
2147
|
-
* Removes a MatterbridgeEndpoint from the specified plugin.
|
|
2148
|
-
*
|
|
2149
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2150
|
-
* @param {MatterbridgeEndpoint} device - The device to remove as a bridged endpoint.
|
|
2151
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been removed.
|
|
2152
|
-
*/
|
|
2153
1556
|
async removeBridgedEndpoint(pluginName, device) {
|
|
2154
1557
|
this.log.debug(`Removing bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
2155
|
-
// Check if the plugin is registered
|
|
2156
1558
|
const plugin = this.plugins.get(pluginName);
|
|
2157
1559
|
if (!plugin) {
|
|
2158
1560
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
2159
1561
|
return;
|
|
2160
1562
|
}
|
|
2161
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2162
1563
|
if (this.bridgeMode === 'bridge') {
|
|
2163
1564
|
if (!this.aggregatorNode) {
|
|
2164
1565
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
|
|
@@ -2173,7 +1574,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2173
1574
|
}
|
|
2174
1575
|
else if (this.bridgeMode === 'childbridge') {
|
|
2175
1576
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2176
|
-
// Nothing to do here since the server node has no aggregator node but only the device itself
|
|
2177
1577
|
}
|
|
2178
1578
|
else if (plugin.type === 'DynamicPlatform') {
|
|
2179
1579
|
if (!plugin.aggregatorNode) {
|
|
@@ -2187,7 +1587,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2187
1587
|
plugin.registeredDevices--;
|
|
2188
1588
|
if (plugin.addedDevices !== undefined)
|
|
2189
1589
|
plugin.addedDevices--;
|
|
2190
|
-
// Close the server node TODO check if this is correct
|
|
2191
1590
|
if (plugin.registeredDevices === 0 && plugin.addedDevices === 0) {
|
|
2192
1591
|
if (plugin.serverNode) {
|
|
2193
1592
|
await this.stopServerNode(plugin.serverNode);
|
|
@@ -2198,27 +1597,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
2198
1597
|
}
|
|
2199
1598
|
}
|
|
2200
1599
|
}
|
|
2201
|
-
// Remove the device from the DeviceManager
|
|
2202
1600
|
this.devices.remove(device);
|
|
2203
1601
|
}
|
|
2204
|
-
/**
|
|
2205
|
-
* Removes all bridged endpoints from the specified plugin.
|
|
2206
|
-
*
|
|
2207
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2208
|
-
* @returns {Promise<void>} A promise that resolves when all bridged endpoints have been removed.
|
|
2209
|
-
*/
|
|
2210
1602
|
async removeAllBridgedEndpoints(pluginName) {
|
|
2211
1603
|
this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}`);
|
|
2212
1604
|
for (const device of this.devices.array().filter((device) => device.plugin === pluginName)) {
|
|
2213
1605
|
await this.removeBridgedEndpoint(pluginName, device);
|
|
2214
1606
|
}
|
|
2215
1607
|
}
|
|
2216
|
-
/**
|
|
2217
|
-
* Sanitizes the fabric information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2218
|
-
*
|
|
2219
|
-
* @param {ExposedFabricInformation[]} fabricInfo - The array of exposed fabric information objects.
|
|
2220
|
-
* @returns {SanitizedExposedFabricInformation[]} An array of sanitized exposed fabric information objects.
|
|
2221
|
-
*/
|
|
2222
1608
|
sanitizeFabricInformations(fabricInfo) {
|
|
2223
1609
|
return fabricInfo.map((info) => {
|
|
2224
1610
|
return {
|
|
@@ -2232,12 +1618,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2232
1618
|
};
|
|
2233
1619
|
});
|
|
2234
1620
|
}
|
|
2235
|
-
/**
|
|
2236
|
-
* Sanitizes the session information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2237
|
-
*
|
|
2238
|
-
* @param {SessionInformation[]} sessionInfo - The array of session information objects.
|
|
2239
|
-
* @returns {SanitizedSessionInformation[]} An array of sanitized session information objects.
|
|
2240
|
-
*/
|
|
2241
1621
|
sanitizeSessionInformation(sessionInfo) {
|
|
2242
1622
|
return sessionInfo
|
|
2243
1623
|
.filter((session) => session.isPeerActive)
|
|
@@ -2265,51 +1645,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
2265
1645
|
};
|
|
2266
1646
|
});
|
|
2267
1647
|
}
|
|
2268
|
-
/**
|
|
2269
|
-
* Sets the reachability of a matter server node and trigger ReachableChanged event.
|
|
2270
|
-
*
|
|
2271
|
-
* @param {ServerNode<ServerNode.RootEndpoint>} serverNode - The commissioning server to set the reachability for.
|
|
2272
|
-
* @param {boolean} reachable - The new reachability status.
|
|
2273
|
-
*/
|
|
2274
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2275
1648
|
setServerNodeReachability(serverNode, reachable) {
|
|
2276
|
-
/*
|
|
2277
|
-
const basicInformationCluster = commissioningServer?.getRootClusterServer(BasicInformationCluster);
|
|
2278
|
-
if (basicInformationCluster && basicInformationCluster.attributes.reachable !== undefined) basicInformationCluster.setReachableAttribute(reachable);
|
|
2279
|
-
if (basicInformationCluster && basicInformationCluster.triggerReachableChangedEvent) basicInformationCluster.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
2280
|
-
*/
|
|
2281
1649
|
}
|
|
2282
|
-
/**
|
|
2283
|
-
* Sets the reachability of the specified matter aggregator and its bridged devices and trigger.
|
|
2284
|
-
* @param {EndpointNode<AggregatorEndpoint>} aggregatorNode - The matter aggregator to set the reachability for.
|
|
2285
|
-
* @param {boolean} reachable - A boolean indicating the reachability status to set.
|
|
2286
|
-
*/
|
|
2287
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2288
1650
|
setAggregatorReachability(aggregatorNode, reachable) {
|
|
2289
|
-
/*
|
|
2290
|
-
const basicInformationCluster = matterAggregator.getClusterServer(BasicInformationCluster);
|
|
2291
|
-
if (basicInformationCluster && basicInformationCluster.attributes.reachable !== undefined) basicInformationCluster.setReachableAttribute(reachable);
|
|
2292
|
-
if (basicInformationCluster && basicInformationCluster.triggerReachableChangedEvent) basicInformationCluster.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
2293
|
-
matterAggregator.getBridgedDevices().forEach((device) => {
|
|
2294
|
-
this.log.debug(`Setting reachability to true for bridged device: ${dev}${device.name}${nf}`);
|
|
2295
|
-
device.getClusterServer(BridgedDeviceBasicInformationCluster)?.setReachableAttribute(reachable);
|
|
2296
|
-
device.getClusterServer(BridgedDeviceBasicInformationCluster)?.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
2297
|
-
});
|
|
2298
|
-
*/
|
|
2299
1651
|
}
|
|
2300
|
-
/**
|
|
2301
|
-
* Sets the reachability of a device and trigger.
|
|
2302
|
-
*
|
|
2303
|
-
* @param {MatterbridgeEndpoint} device - The device to set the reachability for.
|
|
2304
|
-
* @param {boolean} reachable - The new reachability status of the device.
|
|
2305
|
-
*/
|
|
2306
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2307
1652
|
setDeviceReachability(device, reachable) {
|
|
2308
|
-
/*
|
|
2309
|
-
const basicInformationCluster = device.getClusterServer(BasicInformationCluster);
|
|
2310
|
-
if (basicInformationCluster && basicInformationCluster.attributes.reachable !== undefined) basicInformationCluster.setReachableAttribute(reachable);
|
|
2311
|
-
if (basicInformationCluster && basicInformationCluster.triggerReachableChangedEvent) basicInformationCluster.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
2312
|
-
*/
|
|
2313
1653
|
}
|
|
2314
1654
|
getVendorIdName = (vendorId) => {
|
|
2315
1655
|
if (!vendorId)
|
|
@@ -2352,36 +1692,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2352
1692
|
}
|
|
2353
1693
|
return vendorName;
|
|
2354
1694
|
};
|
|
2355
|
-
/**
|
|
2356
|
-
* Spawns a child process with the given command and arguments.
|
|
2357
|
-
* @param {string} command - The command to execute.
|
|
2358
|
-
* @param {string[]} args - The arguments to pass to the command (default: []).
|
|
2359
|
-
* @returns {Promise<boolean>} A promise that resolves when the child process exits successfully, or rejects if there is an error.
|
|
2360
|
-
*/
|
|
2361
1695
|
async spawnCommand(command, args = []) {
|
|
2362
|
-
/*
|
|
2363
|
-
npm > npm.cmd on windows
|
|
2364
|
-
cmd.exe ['dir'] on windows
|
|
2365
|
-
await this.spawnCommand('npm', ['install', '-g', 'matterbridge']);
|
|
2366
|
-
process.on('unhandledRejection', (reason, promise) => {
|
|
2367
|
-
this.log.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
2368
|
-
});
|
|
2369
|
-
|
|
2370
|
-
spawn - [14:27:21.125] [Matterbridge:spawn]: changed 38 packages in 4s
|
|
2371
|
-
spawn - [14:27:21.125] [Matterbridge:spawn]: 10 packages are looking for funding run `npm fund` for details
|
|
2372
|
-
debug - [14:27:21.131] [Matterbridge]: Child process exited with code 0 and signal null
|
|
2373
|
-
debug - [14:27:21.131] [Matterbridge]: Child process stdio streams have closed with code 0
|
|
2374
|
-
*/
|
|
2375
1696
|
const cmdLine = command + ' ' + args.join(' ');
|
|
2376
1697
|
if (process.platform === 'win32' && command === 'npm') {
|
|
2377
|
-
// Must be spawn('cmd.exe', ['/c', 'npm -g install <package>']);
|
|
2378
1698
|
const argstring = 'npm ' + args.join(' ');
|
|
2379
1699
|
args.splice(0, args.length, '/c', argstring);
|
|
2380
1700
|
command = 'cmd.exe';
|
|
2381
1701
|
}
|
|
2382
|
-
// Decide when using sudo on linux
|
|
2383
|
-
// When you need sudo: Spawn stderr: npm error Error: EACCES: permission denied
|
|
2384
|
-
// When you don't need sudo: Failed to start child process "npm install -g matterbridge-eve-door": spawn sudo ENOENT
|
|
2385
1702
|
if (hasParameter('sudo') || (process.platform === 'linux' && command === 'npm' && !hasParameter('docker') && !hasParameter('nosudo'))) {
|
|
2386
1703
|
args.unshift(command);
|
|
2387
1704
|
command = 'sudo';
|
|
@@ -2440,4 +1757,3 @@ export class Matterbridge extends EventEmitter {
|
|
|
2440
1757
|
});
|
|
2441
1758
|
}
|
|
2442
1759
|
}
|
|
2443
|
-
//# sourceMappingURL=matterbridge.js.map
|