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