matterbridge 2.0.0 → 2.1.0-dev.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -1
- package/README.md +1 -1
- package/dist/cli.js +0 -26
- package/dist/cluster/export.js +0 -2
- package/dist/defaultConfigSchema.js +0 -23
- package/dist/deviceManager.js +1 -26
- package/dist/frontend.js +57 -242
- package/dist/index.js +1 -30
- package/dist/logger/export.js +0 -1
- package/dist/matter/export.js +0 -7
- package/dist/matterbridge.js +77 -702
- package/dist/matterbridgeAccessoryPlatform.js +0 -33
- package/dist/matterbridgeBehaviors.js +33 -38
- package/dist/matterbridgeDeviceTypes.js +11 -112
- package/dist/matterbridgeDynamicPlatform.js +0 -33
- package/dist/matterbridgeEndpoint.js +970 -2524
- package/dist/matterbridgePlatform.js +3 -123
- package/dist/matterbridgeTypes.js +0 -28
- package/dist/pluginManager.js +3 -240
- 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/frontend/build/asset-manifest.json +3 -3
- package/frontend/build/index.html +1 -1
- package/frontend/build/static/js/{main.6df4ebe4.js → main.26dbf9b9.js} +3 -3
- package/frontend/build/static/js/{main.6df4ebe4.js.map → main.26dbf9b9.js.map} +1 -1
- package/npm-shrinkwrap.json +83 -68
- package/package.json +2 -4
- 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 -98
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/index.d.ts +0 -34
- 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/export.d.ts +0 -10
- package/dist/matter/export.d.ts.map +0 -1
- package/dist/matter/export.js.map +0 -1
- package/dist/matterbridge.d.ts +0 -356
- 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 -963
- 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 -10254
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointDefault.d.ts +0 -2
- package/dist/matterbridgeEndpointDefault.d.ts.map +0 -1
- package/dist/matterbridgeEndpointDefault.js +0 -159
- package/dist/matterbridgeEndpointDefault.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 -167
- 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/frontend/build/static/js/{main.6df4ebe4.js.LICENSE.txt → main.26dbf9b9.js.LICENSE.txt} +0 -0
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, wait, waiter, 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
|
-
import { FabricAction, PaseClient } from '@matter/main/protocol';
|
|
17
|
+
import { DeviceCommissioner, FabricAction, MdnsService, 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: '',
|
|
@@ -83,7 +53,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
83
53
|
restartMode: '',
|
|
84
54
|
readOnly: hasParameter('readonly'),
|
|
85
55
|
profile: getParameter('profile'),
|
|
86
|
-
loggerLevel: "info"
|
|
56
|
+
loggerLevel: "info",
|
|
87
57
|
fileLogger: false,
|
|
88
58
|
matterLoggerLevel: MatterLogLevel.INFO,
|
|
89
59
|
matterFileLogger: false,
|
|
@@ -105,9 +75,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
105
75
|
matterbridgeLatestVersion = '';
|
|
106
76
|
matterbridgeQrPairingCode = undefined;
|
|
107
77
|
matterbridgeManualPairingCode = undefined;
|
|
108
|
-
matterbridgeFabricInformations =
|
|
109
|
-
matterbridgeSessionInformations =
|
|
110
|
-
matterbridgePaired =
|
|
78
|
+
matterbridgeFabricInformations = undefined;
|
|
79
|
+
matterbridgeSessionInformations = undefined;
|
|
80
|
+
matterbridgePaired = undefined;
|
|
111
81
|
bridgeMode = '';
|
|
112
82
|
restartMode = '';
|
|
113
83
|
profile = getParameter('profile');
|
|
@@ -118,11 +88,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
118
88
|
plugins;
|
|
119
89
|
devices;
|
|
120
90
|
frontend = new Frontend(this);
|
|
121
|
-
// Matterbridge storage
|
|
122
91
|
nodeStorage;
|
|
123
92
|
nodeContext;
|
|
124
93
|
nodeStorageName = 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
|
|
125
|
-
// Cleanup
|
|
126
94
|
hasCleanupStarted = false;
|
|
127
95
|
initialized = false;
|
|
128
96
|
execRunningCount = 0;
|
|
@@ -134,57 +102,34 @@ export class Matterbridge extends EventEmitter {
|
|
|
134
102
|
sigtermHandler;
|
|
135
103
|
exceptionHandler;
|
|
136
104
|
rejectionHandler;
|
|
137
|
-
// Matter environment
|
|
138
105
|
environment = Environment.default;
|
|
139
|
-
// Matter storage
|
|
140
106
|
matterStorageName = 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
|
|
141
107
|
matterStorageService;
|
|
142
108
|
matterStorageManager;
|
|
143
109
|
matterbridgeContext;
|
|
144
110
|
mattercontrollerContext;
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
discriminator; // first server node discriminator
|
|
111
|
+
mdnsInterface;
|
|
112
|
+
ipv4address;
|
|
113
|
+
ipv6address;
|
|
114
|
+
port;
|
|
115
|
+
passcode;
|
|
116
|
+
discriminator;
|
|
152
117
|
serverNode;
|
|
153
118
|
aggregatorNode;
|
|
154
119
|
aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
|
|
155
120
|
aggregatorProductId = getIntParameter('productId') ?? 0x8000;
|
|
156
121
|
static instance;
|
|
157
|
-
// We load asyncronously so is private
|
|
158
122
|
constructor() {
|
|
159
123
|
super();
|
|
160
124
|
}
|
|
161
|
-
/**
|
|
162
|
-
* Retrieves the list of Matterbridge devices.
|
|
163
|
-
* @returns {MatterbridgeEndpoint[]} An array of MatterbridgeDevice objects.
|
|
164
|
-
*/
|
|
165
125
|
getDevices() {
|
|
166
126
|
return this.devices.array();
|
|
167
127
|
}
|
|
168
|
-
/**
|
|
169
|
-
* Retrieves the list of registered plugins.
|
|
170
|
-
* @returns {RegisteredPlugin[]} An array of RegisteredPlugin objects.
|
|
171
|
-
*/
|
|
172
128
|
getPlugins() {
|
|
173
129
|
return this.plugins.array();
|
|
174
130
|
}
|
|
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
131
|
static async loadInstance(initialize = false) {
|
|
186
132
|
if (!Matterbridge.instance) {
|
|
187
|
-
// eslint-disable-next-line no-console
|
|
188
133
|
if (hasParameter('debug'))
|
|
189
134
|
console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
|
|
190
135
|
Matterbridge.instance = new Matterbridge();
|
|
@@ -193,11 +138,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
193
138
|
}
|
|
194
139
|
return Matterbridge.instance;
|
|
195
140
|
}
|
|
196
|
-
/**
|
|
197
|
-
* Call cleanup().
|
|
198
|
-
* @deprecated This method is deprecated and is only used for jest tests.
|
|
199
|
-
*
|
|
200
|
-
*/
|
|
201
141
|
async destroyInstance() {
|
|
202
142
|
await this.cleanup('destroying instance...', false);
|
|
203
143
|
await waiter('destroying instance...', () => {
|
|
@@ -205,68 +145,45 @@ export class Matterbridge extends EventEmitter {
|
|
|
205
145
|
}, false, 60000, 100, false);
|
|
206
146
|
await wait(1000, 'Wait for the global node_modules and matterbridge version', false);
|
|
207
147
|
}
|
|
208
|
-
/**
|
|
209
|
-
* Initializes the Matterbridge application.
|
|
210
|
-
*
|
|
211
|
-
* @remarks
|
|
212
|
-
* This method performs the necessary setup and initialization steps for the Matterbridge application.
|
|
213
|
-
* It displays the help information if the 'help' parameter is provided, sets up the logger, checks the
|
|
214
|
-
* node version, registers signal handlers, initializes storage, and parses the command line.
|
|
215
|
-
*
|
|
216
|
-
* @returns A Promise that resolves when the initialization is complete.
|
|
217
|
-
*/
|
|
218
148
|
async initialize() {
|
|
219
|
-
// Set the restart mode
|
|
220
149
|
if (hasParameter('service'))
|
|
221
150
|
this.restartMode = 'service';
|
|
222
151
|
if (hasParameter('docker'))
|
|
223
152
|
this.restartMode = 'docker';
|
|
224
|
-
// Set the matterbridge directory
|
|
225
153
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
226
154
|
this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
|
|
227
|
-
// Setup matter environment
|
|
228
155
|
this.environment.vars.set('log.level', MatterLogLevel.INFO);
|
|
229
156
|
this.environment.vars.set('log.format', MatterLogFormat.ANSI);
|
|
230
157
|
this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, this.matterStorageName));
|
|
231
158
|
this.environment.vars.set('runtime.signals', false);
|
|
232
159
|
this.environment.vars.set('runtime.exitcode', false);
|
|
233
|
-
|
|
234
|
-
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
|
|
235
|
-
// Register process handlers
|
|
160
|
+
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
236
161
|
this.registerProcessHandlers();
|
|
237
|
-
// Initialize nodeStorage and nodeContext
|
|
238
162
|
try {
|
|
239
163
|
this.log.debug(`Creating node storage manager: ${CYAN}${this.nodeStorageName}${db}`);
|
|
240
164
|
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, this.nodeStorageName), writeQueue: false, expiredInterval: undefined, logging: false });
|
|
241
165
|
this.log.debug('Creating node storage context for matterbridge');
|
|
242
166
|
this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
|
|
243
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
244
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
245
167
|
const keys = (await this.nodeStorage?.storage.keys());
|
|
246
168
|
for (const key of keys) {
|
|
247
169
|
this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
|
|
248
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
249
170
|
await this.nodeStorage?.storage.get(key);
|
|
250
171
|
}
|
|
251
172
|
const storages = await this.nodeStorage.getStorageNames();
|
|
252
173
|
for (const storage of storages) {
|
|
253
174
|
this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
|
|
254
175
|
const nodeContext = await this.nodeStorage?.createStorage(storage);
|
|
255
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
256
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
257
176
|
const keys = (await nodeContext?.storage.keys());
|
|
258
177
|
keys.forEach(async (key) => {
|
|
259
178
|
this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
|
|
260
179
|
await nodeContext?.get(key);
|
|
261
180
|
});
|
|
262
181
|
}
|
|
263
|
-
// Creating a backup of the node storage since it is not corrupted
|
|
264
182
|
this.log.debug('Creating node storage backup...');
|
|
265
183
|
await copyDirectory(path.join(this.matterbridgeDirectory, this.nodeStorageName), path.join(this.matterbridgeDirectory, this.nodeStorageName + '.backup'));
|
|
266
184
|
this.log.debug('Created node storage backup');
|
|
267
185
|
}
|
|
268
186
|
catch (error) {
|
|
269
|
-
// Restoring the backup of the node storage since it is corrupted
|
|
270
187
|
this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
|
|
271
188
|
if (hasParameter('norestore')) {
|
|
272
189
|
this.log.fatal(`The matterbridge node storage is corrupted. Parameter -norestore found: exiting...`);
|
|
@@ -281,51 +198,45 @@ export class Matterbridge extends EventEmitter {
|
|
|
281
198
|
this.log.fatal('Fatal error creating node storage manager and context for matterbridge');
|
|
282
199
|
throw new Error('Fatal error creating node storage manager and context for matterbridge');
|
|
283
200
|
}
|
|
284
|
-
// Set the first port to use for the commissioning server (will be incremented in childbridge mode)
|
|
285
201
|
this.port = getIntParameter('port') ?? (await this.nodeContext.get('matterport', 5540)) ?? 5540;
|
|
286
|
-
// Set the first passcode to use for the commissioning server (will be incremented in childbridge mode)
|
|
287
202
|
this.passcode = getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode')) ?? PaseClient.generateRandomPasscode();
|
|
288
|
-
// Set the first discriminator to use for the commissioning server (will be incremented in childbridge mode)
|
|
289
203
|
this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator();
|
|
290
204
|
this.log.debug(`Initializing commissioning server for Matterbridge... on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
|
|
291
|
-
// Set matterbridge logger level (context: matterbridgeLogLevel)
|
|
292
205
|
if (hasParameter('logger')) {
|
|
293
206
|
const level = getParameter('logger');
|
|
294
207
|
if (level === 'debug') {
|
|
295
|
-
this.log.logLevel = "debug"
|
|
208
|
+
this.log.logLevel = "debug";
|
|
296
209
|
}
|
|
297
210
|
else if (level === 'info') {
|
|
298
|
-
this.log.logLevel = "info"
|
|
211
|
+
this.log.logLevel = "info";
|
|
299
212
|
}
|
|
300
213
|
else if (level === 'notice') {
|
|
301
|
-
this.log.logLevel = "notice"
|
|
214
|
+
this.log.logLevel = "notice";
|
|
302
215
|
}
|
|
303
216
|
else if (level === 'warn') {
|
|
304
|
-
this.log.logLevel = "warn"
|
|
217
|
+
this.log.logLevel = "warn";
|
|
305
218
|
}
|
|
306
219
|
else if (level === 'error') {
|
|
307
|
-
this.log.logLevel = "error"
|
|
220
|
+
this.log.logLevel = "error";
|
|
308
221
|
}
|
|
309
222
|
else if (level === 'fatal') {
|
|
310
|
-
this.log.logLevel = "fatal"
|
|
223
|
+
this.log.logLevel = "fatal";
|
|
311
224
|
}
|
|
312
225
|
else {
|
|
313
226
|
this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
|
|
314
|
-
this.log.logLevel = "info"
|
|
227
|
+
this.log.logLevel = "info";
|
|
315
228
|
}
|
|
316
229
|
}
|
|
317
230
|
else {
|
|
318
|
-
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', "info"
|
|
231
|
+
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', "info");
|
|
319
232
|
}
|
|
320
233
|
MatterbridgeEndpoint.logLevel = this.log.logLevel;
|
|
321
|
-
// Create the file logger for matterbridge (context: matterbridgeFileLog)
|
|
322
234
|
if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
|
|
323
235
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), this.log.logLevel, true);
|
|
324
236
|
this.matterbridgeInformation.fileLogger = true;
|
|
325
237
|
}
|
|
326
238
|
this.log.notice('Matterbridge is starting...');
|
|
327
239
|
this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.matterbridgeInformation.fileLogger}.`);
|
|
328
|
-
// Set matter.js logger level, format and logger (context: matterLogLevel)
|
|
329
240
|
if (hasParameter('matterlogger')) {
|
|
330
241
|
const level = getParameter('matterlogger');
|
|
331
242
|
if (level === 'debug') {
|
|
@@ -356,7 +267,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
356
267
|
}
|
|
357
268
|
Logger.format = MatterLogFormat.ANSI;
|
|
358
269
|
Logger.setLogger('default', this.createMatterLogger());
|
|
359
|
-
// Create the file logger for matter.js (context: matterFileLog)
|
|
360
270
|
if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
|
|
361
271
|
this.matterbridgeInformation.matterFileLogger = true;
|
|
362
272
|
Logger.addLogger('matterfilelogger', await this.createMatterFileLogger(path.join(this.matterbridgeDirectory, this.matterLoggerFile), true), {
|
|
@@ -365,7 +275,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
365
275
|
});
|
|
366
276
|
}
|
|
367
277
|
this.log.debug(`Matter logLevel: ${Logger.defaultLogLevel} fileLoger: ${this.matterbridgeInformation.matterFileLogger}.`);
|
|
368
|
-
// Set the interface to use for matter server node mdnsInterface
|
|
369
278
|
if (hasParameter('mdnsinterface')) {
|
|
370
279
|
this.mdnsInterface = getParameter('mdnsinterface');
|
|
371
280
|
}
|
|
@@ -374,7 +283,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
374
283
|
if (this.mdnsInterface === '')
|
|
375
284
|
this.mdnsInterface = undefined;
|
|
376
285
|
}
|
|
377
|
-
// Validate mdnsInterface
|
|
378
286
|
if (this.mdnsInterface) {
|
|
379
287
|
const networkInterfaces = os.networkInterfaces();
|
|
380
288
|
const availableInterfaces = Object.keys(networkInterfaces);
|
|
@@ -388,7 +296,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
388
296
|
}
|
|
389
297
|
if (this.mdnsInterface)
|
|
390
298
|
this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
|
|
391
|
-
// Set the listeningAddressIpv4 for the matter commissioning server
|
|
392
299
|
if (hasParameter('ipv4address')) {
|
|
393
300
|
this.ipv4address = getParameter('ipv4address');
|
|
394
301
|
}
|
|
@@ -397,7 +304,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
397
304
|
if (this.ipv4address === '')
|
|
398
305
|
this.ipv4address = undefined;
|
|
399
306
|
}
|
|
400
|
-
// Set the listeningAddressIpv6 for the matter commissioning server
|
|
401
307
|
if (hasParameter('ipv6address')) {
|
|
402
308
|
this.ipv6address = getParameter('ipv6address');
|
|
403
309
|
}
|
|
@@ -406,17 +312,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
406
312
|
if (this.ipv6address === '')
|
|
407
313
|
this.ipv6address = undefined;
|
|
408
314
|
}
|
|
409
|
-
// Initialize PluginManager
|
|
410
315
|
this.plugins = new PluginManager(this);
|
|
411
316
|
await this.plugins.loadFromStorage();
|
|
412
|
-
// Initialize DeviceManager
|
|
413
317
|
this.devices = new DeviceManager(this, this.nodeContext);
|
|
414
|
-
// Get the plugins from node storage and create the plugins node storage contexts
|
|
415
318
|
for (const plugin of this.plugins) {
|
|
416
319
|
const packageJson = await this.plugins.parse(plugin);
|
|
417
320
|
if (packageJson === null && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
|
|
418
|
-
// Try to reinstall the plugin from npm (for Docker pull and external plugins)
|
|
419
|
-
// We don't do this when the add and other parameters are set because we shut down the process after adding the plugin
|
|
420
321
|
this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
|
|
421
322
|
try {
|
|
422
323
|
await this.spawnCommand('npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose']);
|
|
@@ -438,7 +339,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
438
339
|
await plugin.nodeContext.set('description', plugin.description);
|
|
439
340
|
await plugin.nodeContext.set('author', plugin.author);
|
|
440
341
|
}
|
|
441
|
-
// Log system info and create .matterbridge directory
|
|
442
342
|
await this.logNodeAndSystemInfo();
|
|
443
343
|
this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
|
|
444
344
|
`${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
|
|
@@ -446,7 +346,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
446
346
|
`${hasParameter('controller') ? 'mode controller ' : ''}` +
|
|
447
347
|
`${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
|
|
448
348
|
`running on ${this.systemInformation.osType} (v.${this.systemInformation.osRelease}) platform ${this.systemInformation.osPlatform} arch ${this.systemInformation.osArch}`);
|
|
449
|
-
// Check node version and throw error
|
|
450
349
|
const minNodeVersion = 18;
|
|
451
350
|
const nodeVersion = process.versions.node;
|
|
452
351
|
const versionMajor = parseInt(nodeVersion.split('.')[0]);
|
|
@@ -454,15 +353,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
454
353
|
this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
455
354
|
throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
456
355
|
}
|
|
457
|
-
// Parse command line
|
|
458
356
|
await this.parseCommandLine();
|
|
459
357
|
this.initialized = true;
|
|
460
358
|
}
|
|
461
|
-
/**
|
|
462
|
-
* Parses the command line arguments and performs the corresponding actions.
|
|
463
|
-
* @private
|
|
464
|
-
* @returns {Promise<void>} A promise that resolves when the command line arguments have been processed, or the process exits.
|
|
465
|
-
*/
|
|
466
359
|
async parseCommandLine() {
|
|
467
360
|
if (hasParameter('help')) {
|
|
468
361
|
this.log.info(`\nUsage: matterbridge [options]\n
|
|
@@ -572,7 +465,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
572
465
|
await this.shutdownProcessAndFactoryReset();
|
|
573
466
|
return;
|
|
574
467
|
}
|
|
575
|
-
// Start the matter storage and create the matterbridge context
|
|
576
468
|
try {
|
|
577
469
|
await this.startMatterStorage();
|
|
578
470
|
}
|
|
@@ -580,12 +472,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
580
472
|
this.log.fatal(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
581
473
|
throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
582
474
|
}
|
|
583
|
-
// Clear the matterbridge context if the reset parameter is set
|
|
584
475
|
if (hasParameter('reset') && getParameter('reset') === undefined) {
|
|
585
476
|
await this.shutdownProcessAndReset();
|
|
586
477
|
return;
|
|
587
478
|
}
|
|
588
|
-
// Clear matterbridge plugin context if the reset parameter is set
|
|
589
479
|
if (hasParameter('reset') && getParameter('reset') !== undefined) {
|
|
590
480
|
this.log.debug(`Reset plugin ${getParameter('reset')}`);
|
|
591
481
|
const plugin = this.plugins.get(getParameter('reset'));
|
|
@@ -607,10 +497,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
607
497
|
this.emit('shutdown');
|
|
608
498
|
return;
|
|
609
499
|
}
|
|
610
|
-
// Initialize frontend
|
|
611
500
|
if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
|
|
612
501
|
await this.frontend.start(getIntParameter('frontend'));
|
|
613
|
-
// Check each 60 minutes the latest versions
|
|
614
502
|
this.checkUpdateInterval = setInterval(() => {
|
|
615
503
|
this.getMatterbridgeLatestVersion();
|
|
616
504
|
for (const plugin of this.plugins) {
|
|
@@ -618,24 +506,20 @@ export class Matterbridge extends EventEmitter {
|
|
|
618
506
|
}
|
|
619
507
|
this.frontend.wssSendRefreshRequired();
|
|
620
508
|
}, 60 * 60 * 1000);
|
|
621
|
-
// Start the matterbridge in mode test
|
|
622
509
|
if (hasParameter('test')) {
|
|
623
510
|
this.bridgeMode = 'bridge';
|
|
624
511
|
MatterbridgeEndpoint.bridgeMode = 'bridge';
|
|
625
512
|
return;
|
|
626
513
|
}
|
|
627
|
-
// Start the matterbridge in mode controller
|
|
628
514
|
if (hasParameter('controller')) {
|
|
629
515
|
this.bridgeMode = 'controller';
|
|
630
516
|
await this.startController();
|
|
631
517
|
return;
|
|
632
518
|
}
|
|
633
|
-
// Check if the bridge mode is set and start matterbridge in bridge mode if not set
|
|
634
519
|
if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
|
|
635
520
|
this.log.info('Setting default matterbridge start mode to bridge');
|
|
636
521
|
await this.nodeContext?.set('bridgeMode', 'bridge');
|
|
637
522
|
}
|
|
638
|
-
// Start matterbridge in bridge mode
|
|
639
523
|
if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
|
|
640
524
|
this.bridgeMode = 'bridge';
|
|
641
525
|
MatterbridgeEndpoint.bridgeMode = 'bridge';
|
|
@@ -643,7 +527,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
643
527
|
await this.startBridge();
|
|
644
528
|
return;
|
|
645
529
|
}
|
|
646
|
-
// Start matterbridge in childbridge mode
|
|
647
530
|
if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
|
|
648
531
|
this.bridgeMode = 'childbridge';
|
|
649
532
|
MatterbridgeEndpoint.bridgeMode = 'childbridge';
|
|
@@ -652,28 +535,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
652
535
|
return;
|
|
653
536
|
}
|
|
654
537
|
}
|
|
655
|
-
/**
|
|
656
|
-
* Asynchronously loads and starts the registered plugins.
|
|
657
|
-
*
|
|
658
|
-
* This method is responsible for initializing and staarting all enabled plugins.
|
|
659
|
-
* It ensures that each plugin is properly loaded and started before the bridge starts.
|
|
660
|
-
*
|
|
661
|
-
* @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
|
|
662
|
-
*/
|
|
663
538
|
async startPlugins() {
|
|
664
|
-
// Check, load and start the plugins
|
|
665
539
|
for (const plugin of this.plugins) {
|
|
666
540
|
plugin.configJson = await this.plugins.loadConfig(plugin);
|
|
667
541
|
plugin.schemaJson = await this.plugins.loadSchema(plugin);
|
|
668
|
-
// Check if the plugin is available
|
|
669
542
|
if (!(await this.plugins.resolve(plugin.path))) {
|
|
670
543
|
this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
|
|
671
544
|
plugin.enabled = false;
|
|
672
545
|
plugin.error = true;
|
|
673
546
|
continue;
|
|
674
547
|
}
|
|
675
|
-
// Check if the plugin has a new version
|
|
676
|
-
// this.getPluginLatestVersion(plugin); // No await do it asyncronously
|
|
677
548
|
if (!plugin.enabled) {
|
|
678
549
|
this.log.info(`Plugin ${plg}${plugin.name}${nf} not enabled`);
|
|
679
550
|
continue;
|
|
@@ -687,26 +558,20 @@ export class Matterbridge extends EventEmitter {
|
|
|
687
558
|
plugin.addedDevices = undefined;
|
|
688
559
|
plugin.qrPairingCode = undefined;
|
|
689
560
|
plugin.manualPairingCode = undefined;
|
|
690
|
-
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
561
|
+
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
691
562
|
}
|
|
692
563
|
this.frontend.wssSendRefreshRequired();
|
|
693
564
|
}
|
|
694
|
-
/**
|
|
695
|
-
* Registers the process handlers for uncaughtException, unhandledRejection, SIGINT and SIGTERM.
|
|
696
|
-
* When either of these signals are received, the cleanup method is called with an appropriate message.
|
|
697
|
-
*/
|
|
698
565
|
registerProcessHandlers() {
|
|
699
566
|
this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
|
|
700
567
|
process.removeAllListeners('uncaughtException');
|
|
701
568
|
process.removeAllListeners('unhandledRejection');
|
|
702
569
|
this.exceptionHandler = async (error) => {
|
|
703
570
|
this.log.fatal('Unhandled Exception detected at:', error.stack || error, rs);
|
|
704
|
-
// await this.cleanup('Unhandled Exception detected, cleaning up...');
|
|
705
571
|
};
|
|
706
572
|
process.on('uncaughtException', this.exceptionHandler);
|
|
707
573
|
this.rejectionHandler = async (reason, promise) => {
|
|
708
574
|
this.log.fatal('Unhandled Rejection detected at:', promise, 'reason:', reason instanceof Error ? reason.stack : reason, rs);
|
|
709
|
-
// await this.cleanup('Unhandled Rejection detected, cleaning up...');
|
|
710
575
|
};
|
|
711
576
|
process.on('unhandledRejection', this.rejectionHandler);
|
|
712
577
|
this.log.debug(`Registering SIGINT and SIGTERM signal handlers...`);
|
|
@@ -719,9 +584,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
719
584
|
};
|
|
720
585
|
process.on('SIGTERM', this.sigtermHandler);
|
|
721
586
|
}
|
|
722
|
-
/**
|
|
723
|
-
* Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
|
|
724
|
-
*/
|
|
725
587
|
deregisterProcesslHandlers() {
|
|
726
588
|
this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
|
|
727
589
|
if (this.exceptionHandler)
|
|
@@ -738,17 +600,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
738
600
|
process.off('SIGTERM', this.sigtermHandler);
|
|
739
601
|
this.sigtermHandler = undefined;
|
|
740
602
|
}
|
|
741
|
-
/**
|
|
742
|
-
* Logs the node and system information.
|
|
743
|
-
*/
|
|
744
603
|
async logNodeAndSystemInfo() {
|
|
745
|
-
// IP address information
|
|
746
604
|
const networkInterfaces = os.networkInterfaces();
|
|
747
605
|
this.systemInformation.interfaceName = '';
|
|
748
606
|
this.systemInformation.ipv4Address = '';
|
|
749
607
|
this.systemInformation.ipv6Address = '';
|
|
750
608
|
for (const [interfaceName, interfaceDetails] of Object.entries(networkInterfaces)) {
|
|
751
|
-
// this.log.debug(`Checking interface: '${interfaceName}' for '${this.mdnsInterface}'`);
|
|
752
609
|
if (this.mdnsInterface && interfaceName !== this.mdnsInterface)
|
|
753
610
|
continue;
|
|
754
611
|
if (!interfaceDetails) {
|
|
@@ -774,22 +631,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
774
631
|
break;
|
|
775
632
|
}
|
|
776
633
|
}
|
|
777
|
-
// Node information
|
|
778
634
|
this.systemInformation.nodeVersion = process.versions.node;
|
|
779
635
|
const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
|
|
780
636
|
const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
|
|
781
637
|
const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
|
|
782
|
-
// Host system information
|
|
783
638
|
this.systemInformation.hostname = os.hostname();
|
|
784
639
|
this.systemInformation.user = os.userInfo().username;
|
|
785
|
-
this.systemInformation.osType = os.type();
|
|
786
|
-
this.systemInformation.osRelease = os.release();
|
|
787
|
-
this.systemInformation.osPlatform = os.platform();
|
|
788
|
-
this.systemInformation.osArch = os.arch();
|
|
789
|
-
this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
790
|
-
this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
791
|
-
this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
|
|
792
|
-
// Log the system information
|
|
640
|
+
this.systemInformation.osType = os.type();
|
|
641
|
+
this.systemInformation.osRelease = os.release();
|
|
642
|
+
this.systemInformation.osPlatform = os.platform();
|
|
643
|
+
this.systemInformation.osArch = os.arch();
|
|
644
|
+
this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
645
|
+
this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
646
|
+
this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
|
|
793
647
|
this.log.debug('Host System Information:');
|
|
794
648
|
this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
|
|
795
649
|
this.log.debug(`- User: ${this.systemInformation.user}`);
|
|
@@ -805,19 +659,15 @@ export class Matterbridge extends EventEmitter {
|
|
|
805
659
|
this.log.debug(`- Total Memory: ${this.systemInformation.totalMemory}`);
|
|
806
660
|
this.log.debug(`- Free Memory: ${this.systemInformation.freeMemory}`);
|
|
807
661
|
this.log.debug(`- System Uptime: ${this.systemInformation.systemUptime}`);
|
|
808
|
-
// Home directory
|
|
809
662
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
810
663
|
this.matterbridgeInformation.homeDirectory = this.homeDirectory;
|
|
811
664
|
this.log.debug(`Home Directory: ${this.homeDirectory}`);
|
|
812
|
-
// Package root directory
|
|
813
665
|
const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
814
666
|
this.rootDirectory = path.resolve(currentFileDirectory, '../');
|
|
815
667
|
this.matterbridgeInformation.rootDirectory = this.rootDirectory;
|
|
816
668
|
this.log.debug(`Root Directory: ${this.rootDirectory}`);
|
|
817
|
-
// Global node_modules directory
|
|
818
669
|
if (this.nodeContext)
|
|
819
670
|
this.globalModulesDirectory = this.matterbridgeInformation.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
|
|
820
|
-
// First run of Matterbridge so the node storage is empty
|
|
821
671
|
if (this.globalModulesDirectory === '') {
|
|
822
672
|
try {
|
|
823
673
|
this.globalModulesDirectory = await this.getGlobalNodeModules();
|
|
@@ -831,20 +681,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
831
681
|
}
|
|
832
682
|
else
|
|
833
683
|
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
834
|
-
/* removed cause is too expensive for the shelly board and not really needed. Why should it change the globalModulesDirectory?
|
|
835
|
-
else {
|
|
836
|
-
this.getGlobalNodeModules()
|
|
837
|
-
.then(async (globalModulesDirectory) => {
|
|
838
|
-
this.globalModulesDirectory = globalModulesDirectory;
|
|
839
|
-
this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory;
|
|
840
|
-
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
841
|
-
await this.nodeContext?.set<string>('globalModulesDirectory', this.globalModulesDirectory);
|
|
842
|
-
})
|
|
843
|
-
.catch((error) => {
|
|
844
|
-
this.log.error(`Error getting global node_modules directory: ${error}`);
|
|
845
|
-
});
|
|
846
|
-
}*/
|
|
847
|
-
// Create the data directory .matterbridge in the home directory
|
|
848
684
|
this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
|
|
849
685
|
this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
|
|
850
686
|
try {
|
|
@@ -868,7 +704,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
868
704
|
}
|
|
869
705
|
}
|
|
870
706
|
this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
|
|
871
|
-
// Create the plugin directory Matterbridge in the home directory
|
|
872
707
|
this.matterbridgePluginDirectory = path.join(this.homeDirectory, 'Matterbridge');
|
|
873
708
|
this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
|
|
874
709
|
try {
|
|
@@ -892,28 +727,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
892
727
|
}
|
|
893
728
|
}
|
|
894
729
|
this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
|
|
895
|
-
// Matterbridge version
|
|
896
730
|
const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
|
|
897
731
|
this.matterbridgeVersion = this.matterbridgeLatestVersion = packageJson.version;
|
|
898
732
|
this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeInformation.matterbridgeLatestVersion = this.matterbridgeVersion;
|
|
899
733
|
this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
|
|
900
|
-
// Matterbridge latest version
|
|
901
734
|
if (this.nodeContext)
|
|
902
735
|
this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
|
|
903
736
|
this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
|
|
904
|
-
// this.getMatterbridgeLatestVersion();
|
|
905
|
-
// Current working directory
|
|
906
737
|
const currentDir = process.cwd();
|
|
907
738
|
this.log.debug(`Current Working Directory: ${currentDir}`);
|
|
908
|
-
// Command line arguments (excluding 'node' and the script name)
|
|
909
739
|
const cmdArgs = process.argv.slice(2).join(' ');
|
|
910
740
|
this.log.debug(`Command Line Arguments: ${cmdArgs}`);
|
|
911
741
|
}
|
|
912
|
-
/**
|
|
913
|
-
* Retrieves the latest version of a package from the npm registry.
|
|
914
|
-
* @param packageName - The name of the package.
|
|
915
|
-
* @returns A Promise that resolves to the latest version of the package.
|
|
916
|
-
*/
|
|
917
742
|
async getLatestVersion(packageName) {
|
|
918
743
|
return new Promise((resolve, reject) => {
|
|
919
744
|
this.execRunningCount++;
|
|
@@ -928,16 +753,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
928
753
|
});
|
|
929
754
|
});
|
|
930
755
|
}
|
|
931
|
-
/**
|
|
932
|
-
* Retrieves the path to the global Node.js modules directory.
|
|
933
|
-
* @returns A promise that resolves to the path of the global Node.js modules directory.
|
|
934
|
-
*/
|
|
935
756
|
async getGlobalNodeModules() {
|
|
936
|
-
/*
|
|
937
|
-
const { Module } = await import('module'); // Dynamic import to access the `Module` class
|
|
938
|
-
const globalPaths = 'globalPaths' in Module && Array.isArray(Module.globalPaths) && typeof Module.globalPaths[0] === 'string' ? (Module.globalPaths as string[])[0] : '';
|
|
939
|
-
this.log.debug('Module.globalPaths:', globalPaths);
|
|
940
|
-
*/
|
|
941
757
|
return new Promise((resolve, reject) => {
|
|
942
758
|
this.execRunningCount++;
|
|
943
759
|
exec('npm root -g', (error, stdout) => {
|
|
@@ -951,11 +767,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
951
767
|
});
|
|
952
768
|
});
|
|
953
769
|
}
|
|
954
|
-
/**
|
|
955
|
-
* Retrieves the latest version of Matterbridge and performs necessary actions based on the version comparison.
|
|
956
|
-
* @private
|
|
957
|
-
* @returns {Promise<void>} A promise that resolves when the latest version is retrieved and actions are performed.
|
|
958
|
-
*/
|
|
959
770
|
async getMatterbridgeLatestVersion() {
|
|
960
771
|
this.getLatestVersion('matterbridge')
|
|
961
772
|
.then(async (matterbridgeLatestVersion) => {
|
|
@@ -972,19 +783,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
972
783
|
})
|
|
973
784
|
.catch((error) => {
|
|
974
785
|
this.log.error(`Error getting Matterbridge latest version: ${error.message}`);
|
|
975
|
-
// error.stack && this.log.debug(error.stack);
|
|
976
786
|
});
|
|
977
787
|
}
|
|
978
|
-
/**
|
|
979
|
-
* Retrieves the latest version of a plugin and updates the plugin's latestVersion property.
|
|
980
|
-
* If the plugin's version is different from the latest version, logs a warning message.
|
|
981
|
-
* If the plugin's version is the same as the latest version, logs an info message.
|
|
982
|
-
* If there is an error retrieving the latest version, logs an error message.
|
|
983
|
-
*
|
|
984
|
-
* @private
|
|
985
|
-
* @param {RegisteredPlugin} plugin - The plugin for which to retrieve the latest version.
|
|
986
|
-
* @returns {Promise<void>} A promise that resolves when the latest version is retrieved and actions are performed.
|
|
987
|
-
*/
|
|
988
788
|
async getPluginLatestVersion(plugin) {
|
|
989
789
|
this.getLatestVersion(plugin.name)
|
|
990
790
|
.then(async (latestVersion) => {
|
|
@@ -996,54 +796,40 @@ export class Matterbridge extends EventEmitter {
|
|
|
996
796
|
})
|
|
997
797
|
.catch((error) => {
|
|
998
798
|
this.log.error(`Error getting ${plg}${plugin.name}${er} latest version: ${error.message}`);
|
|
999
|
-
// error.stack && this.log.debug(error.stack);
|
|
1000
799
|
});
|
|
1001
800
|
}
|
|
1002
|
-
/**
|
|
1003
|
-
* Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (for the frontend).
|
|
1004
|
-
*
|
|
1005
|
-
* @returns {Function} The MatterLogger function.
|
|
1006
|
-
*/
|
|
1007
801
|
createMatterLogger() {
|
|
1008
|
-
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4
|
|
802
|
+
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
|
|
1009
803
|
return (_level, formattedLog) => {
|
|
1010
804
|
const logger = formattedLog.slice(44, 44 + 20).trim();
|
|
1011
805
|
const message = formattedLog.slice(65);
|
|
1012
806
|
matterLogger.logName = logger;
|
|
1013
807
|
switch (_level) {
|
|
1014
808
|
case MatterLogLevel.DEBUG:
|
|
1015
|
-
matterLogger.log("debug"
|
|
809
|
+
matterLogger.log("debug", message);
|
|
1016
810
|
break;
|
|
1017
811
|
case MatterLogLevel.INFO:
|
|
1018
|
-
matterLogger.log("info"
|
|
812
|
+
matterLogger.log("info", message);
|
|
1019
813
|
break;
|
|
1020
814
|
case MatterLogLevel.NOTICE:
|
|
1021
|
-
matterLogger.log("notice"
|
|
815
|
+
matterLogger.log("notice", message);
|
|
1022
816
|
break;
|
|
1023
817
|
case MatterLogLevel.WARN:
|
|
1024
|
-
matterLogger.log("warn"
|
|
818
|
+
matterLogger.log("warn", message);
|
|
1025
819
|
break;
|
|
1026
820
|
case MatterLogLevel.ERROR:
|
|
1027
|
-
matterLogger.log("error"
|
|
821
|
+
matterLogger.log("error", message);
|
|
1028
822
|
break;
|
|
1029
823
|
case MatterLogLevel.FATAL:
|
|
1030
|
-
matterLogger.log("fatal"
|
|
824
|
+
matterLogger.log("fatal", message);
|
|
1031
825
|
break;
|
|
1032
826
|
default:
|
|
1033
|
-
matterLogger.log("debug"
|
|
827
|
+
matterLogger.log("debug", message);
|
|
1034
828
|
break;
|
|
1035
829
|
}
|
|
1036
830
|
};
|
|
1037
831
|
}
|
|
1038
|
-
/**
|
|
1039
|
-
* Creates a Matter File Logger.
|
|
1040
|
-
*
|
|
1041
|
-
* @param {string} filePath - The path to the log file.
|
|
1042
|
-
* @param {boolean} [unlink=false] - Whether to unlink the log file before creating a new one.
|
|
1043
|
-
* @returns {Function} - A function that logs formatted messages to the log file.
|
|
1044
|
-
*/
|
|
1045
832
|
async createMatterFileLogger(filePath, unlink = false) {
|
|
1046
|
-
// 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
|
|
1047
833
|
let fileSize = 0;
|
|
1048
834
|
if (unlink) {
|
|
1049
835
|
try {
|
|
@@ -1092,21 +878,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1092
878
|
}
|
|
1093
879
|
};
|
|
1094
880
|
}
|
|
1095
|
-
/**
|
|
1096
|
-
* Restarts the process by exiting the current instance and loading a new instance.
|
|
1097
|
-
*/
|
|
1098
881
|
async restartProcess() {
|
|
1099
882
|
await this.cleanup('restarting...', true);
|
|
1100
883
|
}
|
|
1101
|
-
/**
|
|
1102
|
-
* Shut down the process by exiting the current process.
|
|
1103
|
-
*/
|
|
1104
884
|
async shutdownProcess() {
|
|
1105
885
|
await this.cleanup('shutting down...', false);
|
|
1106
886
|
}
|
|
1107
|
-
/**
|
|
1108
|
-
* Update matterbridge and and shut down the process.
|
|
1109
|
-
*/
|
|
1110
887
|
async updateProcess() {
|
|
1111
888
|
this.log.info('Updating matterbridge...');
|
|
1112
889
|
try {
|
|
@@ -1119,9 +896,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1119
896
|
this.frontend.wssSendRestartRequired();
|
|
1120
897
|
await this.cleanup('updating...', false);
|
|
1121
898
|
}
|
|
1122
|
-
/**
|
|
1123
|
-
* Unregister all devices and shut down the process.
|
|
1124
|
-
*/
|
|
1125
899
|
async unregisterAndShutdownProcess() {
|
|
1126
900
|
this.log.info('Unregistering all devices and shutting down...');
|
|
1127
901
|
for (const plugin of this.plugins) {
|
|
@@ -1129,9 +903,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1129
903
|
}
|
|
1130
904
|
await this.cleanup('unregistered all devices and shutting down...', false);
|
|
1131
905
|
}
|
|
1132
|
-
/**
|
|
1133
|
-
* Reset commissioning and shut down the process.
|
|
1134
|
-
*/
|
|
1135
906
|
async shutdownProcessAndReset() {
|
|
1136
907
|
this.log.info('Resetting Matterbridge commissioning information...');
|
|
1137
908
|
await this.matterStorageManager?.createContext('events')?.clearAll();
|
|
@@ -1143,12 +914,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1143
914
|
this.log.info('Matter storage reset done! Remove the bridge from the controller.');
|
|
1144
915
|
await this.cleanup('shutting down with reset...', false);
|
|
1145
916
|
}
|
|
1146
|
-
/**
|
|
1147
|
-
* Factory reset and shut down the process.
|
|
1148
|
-
*/
|
|
1149
917
|
async shutdownProcessAndFactoryReset() {
|
|
1150
918
|
try {
|
|
1151
|
-
// Delete old matter storage file and backup
|
|
1152
919
|
const file = path.join(this.matterbridgeDirectory, 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.json');
|
|
1153
920
|
this.log.info(`Unlinking old matter storage file: ${file}`);
|
|
1154
921
|
await fs.unlink(file);
|
|
@@ -1162,7 +929,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1162
929
|
}
|
|
1163
930
|
}
|
|
1164
931
|
try {
|
|
1165
|
-
// Delete matter node storage directory with its subdirectories and backup
|
|
1166
932
|
const dir = path.join(this.matterbridgeDirectory, 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
|
|
1167
933
|
this.log.info(`Removing matter node storage directory: ${dir}`);
|
|
1168
934
|
await fs.rm(dir, { recursive: true });
|
|
@@ -1176,7 +942,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1176
942
|
}
|
|
1177
943
|
}
|
|
1178
944
|
try {
|
|
1179
|
-
// Delete node storage directory with its subdirectories and backup
|
|
1180
945
|
const dir = path.join(this.matterbridgeDirectory, 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
|
|
1181
946
|
this.log.info(`Removing storage directory: ${dir}`);
|
|
1182
947
|
await fs.rm(dir, { recursive: true });
|
|
@@ -1196,41 +961,30 @@ export class Matterbridge extends EventEmitter {
|
|
|
1196
961
|
this.devices.clear();
|
|
1197
962
|
await this.cleanup('shutting down with factory reset...', false);
|
|
1198
963
|
}
|
|
1199
|
-
/**
|
|
1200
|
-
* Cleans up the Matterbridge instance.
|
|
1201
|
-
* @param message - The cleanup message.
|
|
1202
|
-
* @param restart - Indicates whether to restart the instance after cleanup. Default is `false`.
|
|
1203
|
-
* @returns A promise that resolves when the cleanup is completed.
|
|
1204
|
-
*/
|
|
1205
964
|
async cleanup(message, restart = false) {
|
|
1206
965
|
if (this.initialized && !this.hasCleanupStarted) {
|
|
1207
966
|
this.hasCleanupStarted = true;
|
|
1208
967
|
this.log.info(message);
|
|
1209
|
-
// Clear the start matter interval
|
|
1210
968
|
if (this.startMatterInterval) {
|
|
1211
969
|
clearInterval(this.startMatterInterval);
|
|
1212
970
|
this.startMatterInterval = undefined;
|
|
1213
971
|
this.log.debug('Start matter interval cleared');
|
|
1214
972
|
}
|
|
1215
|
-
// Clear the check update interval
|
|
1216
973
|
if (this.checkUpdateInterval) {
|
|
1217
974
|
clearInterval(this.checkUpdateInterval);
|
|
1218
975
|
this.checkUpdateInterval = undefined;
|
|
1219
976
|
this.log.debug('Check update interval cleared');
|
|
1220
977
|
}
|
|
1221
|
-
// Clear the configure timeout
|
|
1222
978
|
if (this.configureTimeout) {
|
|
1223
979
|
clearTimeout(this.configureTimeout);
|
|
1224
980
|
this.configureTimeout = undefined;
|
|
1225
981
|
this.log.debug('Matterbridge configure timeout cleared');
|
|
1226
982
|
}
|
|
1227
|
-
// Clear the reachability timeout
|
|
1228
983
|
if (this.reachabilityTimeout) {
|
|
1229
984
|
clearTimeout(this.reachabilityTimeout);
|
|
1230
985
|
this.reachabilityTimeout = undefined;
|
|
1231
986
|
this.log.debug('Matterbridge reachability timeout cleared');
|
|
1232
987
|
}
|
|
1233
|
-
// Calling the shutdown method of each plugin and clear the plugins reachability timeout
|
|
1234
988
|
for (const plugin of this.plugins) {
|
|
1235
989
|
if (!plugin.enabled || plugin.error)
|
|
1236
990
|
continue;
|
|
@@ -1241,13 +995,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1241
995
|
this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
|
|
1242
996
|
}
|
|
1243
997
|
}
|
|
1244
|
-
// Stop the frontend
|
|
1245
998
|
this.frontend.stop();
|
|
1246
|
-
// Stopping matter server nodes
|
|
1247
999
|
this.log.info(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
|
|
1248
1000
|
if (this.bridgeMode === 'bridge') {
|
|
1249
1001
|
if (this.serverNode) {
|
|
1250
1002
|
await this.stopServerNode(this.serverNode);
|
|
1003
|
+
this.serverNode = undefined;
|
|
1251
1004
|
this.log.info(`Stopped matter server node for Matterbridge`);
|
|
1252
1005
|
}
|
|
1253
1006
|
}
|
|
@@ -1255,41 +1008,31 @@ export class Matterbridge extends EventEmitter {
|
|
|
1255
1008
|
for (const plugin of this.plugins.array()) {
|
|
1256
1009
|
if (plugin.serverNode) {
|
|
1257
1010
|
await this.stopServerNode(plugin.serverNode);
|
|
1011
|
+
plugin.serverNode = undefined;
|
|
1258
1012
|
this.log.info(`Stopped matter server node for ${plugin.name}`);
|
|
1259
1013
|
}
|
|
1260
1014
|
}
|
|
1261
1015
|
}
|
|
1262
1016
|
this.log.info('Stopped matter server nodes');
|
|
1263
|
-
// Stop matter MdnsService
|
|
1264
|
-
// await this.environment.get(MdnsService)[Symbol.asyncDispose]();
|
|
1265
|
-
// this.log.info('Stopped MdnsService');
|
|
1266
|
-
// Stop matter storage
|
|
1267
1017
|
await this.stopMatterStorage();
|
|
1268
|
-
// Remove the matterfilelogger
|
|
1269
1018
|
try {
|
|
1270
1019
|
Logger.removeLogger('matterfilelogger');
|
|
1271
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1272
1020
|
}
|
|
1273
1021
|
catch (error) {
|
|
1274
|
-
// this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridgeDirectory, this.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
|
|
1275
1022
|
}
|
|
1276
|
-
// Serialize registeredDevices
|
|
1277
1023
|
if (this.nodeStorage && this.nodeContext) {
|
|
1278
1024
|
this.log.info('Saving registered devices...');
|
|
1279
1025
|
const serializedRegisteredDevices = [];
|
|
1280
1026
|
this.devices.forEach(async (device) => {
|
|
1281
|
-
const serializedMatterbridgeDevice =
|
|
1282
|
-
// this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
|
|
1027
|
+
const serializedMatterbridgeDevice = MatterbridgeEndpoint.serialize(device);
|
|
1283
1028
|
if (serializedMatterbridgeDevice)
|
|
1284
1029
|
serializedRegisteredDevices.push(serializedMatterbridgeDevice);
|
|
1285
1030
|
});
|
|
1286
1031
|
await this.nodeContext.set('devices', serializedRegisteredDevices);
|
|
1287
1032
|
this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
|
|
1288
|
-
// Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
|
|
1289
1033
|
this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
|
|
1290
1034
|
await this.nodeContext.close();
|
|
1291
1035
|
this.nodeContext = undefined;
|
|
1292
|
-
// Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
|
|
1293
1036
|
for (const plugin of this.plugins) {
|
|
1294
1037
|
if (plugin.nodeContext) {
|
|
1295
1038
|
this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
|
|
@@ -1306,13 +1049,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1306
1049
|
}
|
|
1307
1050
|
this.plugins.clear();
|
|
1308
1051
|
this.devices.clear();
|
|
1309
|
-
// Deregisters the process handlers
|
|
1310
1052
|
this.deregisterProcesslHandlers();
|
|
1311
1053
|
if (restart) {
|
|
1312
1054
|
if (message === 'updating...') {
|
|
1313
1055
|
this.log.info('Cleanup completed. Updating...');
|
|
1314
1056
|
Matterbridge.instance = undefined;
|
|
1315
|
-
this.emit('update');
|
|
1057
|
+
this.emit('update');
|
|
1316
1058
|
}
|
|
1317
1059
|
else if (message === 'restarting...') {
|
|
1318
1060
|
this.log.info('Cleanup completed. Restarting...');
|
|
@@ -1329,14 +1071,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1329
1071
|
this.initialized = false;
|
|
1330
1072
|
}
|
|
1331
1073
|
}
|
|
1332
|
-
/**
|
|
1333
|
-
* Creates and configures the server node for an accessory plugin for a given device.
|
|
1334
|
-
*
|
|
1335
|
-
* @param {RegisteredPlugin} plugin - The plugin to configure.
|
|
1336
|
-
* @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
|
|
1337
|
-
* @param {boolean} [start=false] - Whether to start the server node after adding the device.
|
|
1338
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
|
|
1339
|
-
*/
|
|
1340
1074
|
async createAccessoryPlugin(plugin, device, start = false) {
|
|
1341
1075
|
if (!plugin.locked && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
|
|
1342
1076
|
plugin.locked = true;
|
|
@@ -1348,13 +1082,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1348
1082
|
await this.startServerNode(plugin.serverNode);
|
|
1349
1083
|
}
|
|
1350
1084
|
}
|
|
1351
|
-
/**
|
|
1352
|
-
* Creates and configures the server node for a dynamic plugin.
|
|
1353
|
-
*
|
|
1354
|
-
* @param {RegisteredPlugin} plugin - The plugin to configure.
|
|
1355
|
-
* @param {boolean} [start=false] - Whether to start the server node after adding the aggregator node.
|
|
1356
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the dynamic plugin is created and configured.
|
|
1357
|
-
*/
|
|
1358
1085
|
async createDynamicPlugin(plugin, start = false) {
|
|
1359
1086
|
if (!plugin.locked) {
|
|
1360
1087
|
plugin.locked = true;
|
|
@@ -1366,13 +1093,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1366
1093
|
await this.startServerNode(plugin.serverNode);
|
|
1367
1094
|
}
|
|
1368
1095
|
}
|
|
1369
|
-
/**
|
|
1370
|
-
* Starts the Matterbridge in bridge mode.
|
|
1371
|
-
* @private
|
|
1372
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1373
|
-
*/
|
|
1374
1096
|
async startBridge() {
|
|
1375
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1376
1097
|
if (!this.matterStorageManager)
|
|
1377
1098
|
throw new Error('No storage manager initialized');
|
|
1378
1099
|
if (!this.matterbridgeContext)
|
|
@@ -1409,15 +1130,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1409
1130
|
clearInterval(this.startMatterInterval);
|
|
1410
1131
|
this.startMatterInterval = undefined;
|
|
1411
1132
|
this.log.debug('Cleared startMatterInterval interval for Matterbridge');
|
|
1412
|
-
// Start the Matter server node
|
|
1413
1133
|
this.startServerNode(this.serverNode);
|
|
1414
|
-
// Configure the plugins
|
|
1415
1134
|
this.configureTimeout = setTimeout(async () => {
|
|
1416
1135
|
for (const plugin of this.plugins) {
|
|
1417
1136
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
1418
1137
|
continue;
|
|
1419
1138
|
try {
|
|
1420
|
-
await this.plugins.configure(plugin);
|
|
1139
|
+
await this.plugins.configure(plugin);
|
|
1421
1140
|
}
|
|
1422
1141
|
catch (error) {
|
|
1423
1142
|
plugin.error = true;
|
|
@@ -1426,7 +1145,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1426
1145
|
}
|
|
1427
1146
|
this.frontend.wssSendRefreshRequired();
|
|
1428
1147
|
}, 30 * 1000);
|
|
1429
|
-
// Setting reachability to true
|
|
1430
1148
|
this.reachabilityTimeout = setTimeout(() => {
|
|
1431
1149
|
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1432
1150
|
if (this.serverNode)
|
|
@@ -1437,14 +1155,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1437
1155
|
}, 60 * 1000);
|
|
1438
1156
|
}, 1000);
|
|
1439
1157
|
}
|
|
1440
|
-
/**
|
|
1441
|
-
* Starts the Matterbridge in childbridge mode.
|
|
1442
|
-
* @private
|
|
1443
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1444
|
-
*/
|
|
1445
1158
|
async startChildbridge() {
|
|
1446
|
-
// Matterbridge.addBridgedDevice creates the commissionig servers and add the devices to the the commissioning server or to the aggregator
|
|
1447
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1448
1159
|
if (!this.matterStorageManager)
|
|
1449
1160
|
throw new Error('No storage manager initialized');
|
|
1450
1161
|
for (const plugin of this.plugins) {
|
|
@@ -1491,13 +1202,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1491
1202
|
clearInterval(this.startMatterInterval);
|
|
1492
1203
|
this.startMatterInterval = undefined;
|
|
1493
1204
|
this.log.debug('Cleared startMatterInterval interval in childbridge mode');
|
|
1494
|
-
// Configure the plugins
|
|
1495
1205
|
this.configureTimeout = setTimeout(async () => {
|
|
1496
1206
|
for (const plugin of this.plugins) {
|
|
1497
1207
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
1498
1208
|
continue;
|
|
1499
1209
|
try {
|
|
1500
|
-
await this.plugins.configure(plugin);
|
|
1210
|
+
await this.plugins.configure(plugin);
|
|
1501
1211
|
}
|
|
1502
1212
|
catch (error) {
|
|
1503
1213
|
plugin.error = true;
|
|
@@ -1525,9 +1235,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1525
1235
|
this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
|
|
1526
1236
|
continue;
|
|
1527
1237
|
}
|
|
1528
|
-
// Start the Matter server node
|
|
1529
1238
|
this.startServerNode(plugin.serverNode);
|
|
1530
|
-
// Setting reachability to true
|
|
1531
1239
|
plugin.reachabilityTimeout = setTimeout(() => {
|
|
1532
1240
|
this.log.info(`Setting reachability to true for ${plg}${plugin.name}${db}`);
|
|
1533
1241
|
if (plugin.serverNode)
|
|
@@ -1541,219 +1249,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
1541
1249
|
}
|
|
1542
1250
|
}, 1000);
|
|
1543
1251
|
}
|
|
1544
|
-
/**
|
|
1545
|
-
* Starts the Matterbridge controller.
|
|
1546
|
-
* @private
|
|
1547
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1548
|
-
*/
|
|
1549
1252
|
async startController() {
|
|
1550
|
-
/*
|
|
1551
|
-
if (!this.storageManager) {
|
|
1552
|
-
this.log.error('No storage manager initialized');
|
|
1553
|
-
await this.cleanup('No storage manager initialized');
|
|
1554
|
-
return;
|
|
1555
|
-
}
|
|
1556
|
-
this.log.info('Creating context: mattercontrollerContext');
|
|
1557
|
-
this.mattercontrollerContext = this.storageManager.createContext('mattercontrollerContext');
|
|
1558
|
-
if (!this.mattercontrollerContext) {
|
|
1559
|
-
this.log.error('No storage context mattercontrollerContext initialized');
|
|
1560
|
-
await this.cleanup('No storage context mattercontrollerContext initialized');
|
|
1561
|
-
return;
|
|
1562
|
-
}
|
|
1563
|
-
|
|
1564
|
-
this.log.debug('Starting matterbridge in mode', this.bridgeMode);
|
|
1565
|
-
this.matterServer = await this.createMatterServer(this.storageManager);
|
|
1566
|
-
this.log.info('Creating matter commissioning controller');
|
|
1567
|
-
this.commissioningController = new CommissioningController({
|
|
1568
|
-
autoConnect: false,
|
|
1569
|
-
});
|
|
1570
|
-
this.log.info('Adding matter commissioning controller to matter server');
|
|
1571
|
-
await this.matterServer.addCommissioningController(this.commissioningController);
|
|
1572
|
-
|
|
1573
|
-
this.log.info('Starting matter server');
|
|
1574
|
-
await this.matterServer.start();
|
|
1575
|
-
this.log.info('Matter server started');
|
|
1576
|
-
|
|
1577
|
-
if (hasParameter('pairingcode')) {
|
|
1578
|
-
this.log.info('Pairing device with pairingcode:', getParameter('pairingcode'));
|
|
1579
|
-
const pairingCode = getParameter('pairingcode');
|
|
1580
|
-
const ip = this.mattercontrollerContext.has('ip') ? this.mattercontrollerContext.get<string>('ip') : undefined;
|
|
1581
|
-
const port = this.mattercontrollerContext.has('port') ? this.mattercontrollerContext.get<number>('port') : undefined;
|
|
1582
|
-
|
|
1583
|
-
let longDiscriminator, setupPin, shortDiscriminator;
|
|
1584
|
-
if (pairingCode !== undefined) {
|
|
1585
|
-
const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
|
|
1586
|
-
shortDiscriminator = pairingCodeCodec.shortDiscriminator;
|
|
1587
|
-
longDiscriminator = undefined;
|
|
1588
|
-
setupPin = pairingCodeCodec.passcode;
|
|
1589
|
-
this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
|
|
1590
|
-
} else {
|
|
1591
|
-
longDiscriminator = await this.mattercontrollerContext.get('longDiscriminator', 3840);
|
|
1592
|
-
if (longDiscriminator > 4095) throw new Error('Discriminator value must be less than 4096');
|
|
1593
|
-
setupPin = this.mattercontrollerContext.get('pin', 20202021);
|
|
1594
|
-
}
|
|
1595
|
-
if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
|
|
1596
|
-
throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
|
|
1597
|
-
}
|
|
1598
|
-
|
|
1599
|
-
const commissioningOptions: ControllerCommissioningFlowOptions = {
|
|
1600
|
-
regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
|
|
1601
|
-
regulatoryCountryCode: 'XX',
|
|
1602
|
-
};
|
|
1603
|
-
const options = {
|
|
1604
|
-
commissioning: commissioningOptions,
|
|
1605
|
-
discovery: {
|
|
1606
|
-
knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
|
|
1607
|
-
identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
|
|
1608
|
-
},
|
|
1609
|
-
passcode: setupPin,
|
|
1610
|
-
} as NodeCommissioningOptions;
|
|
1611
|
-
this.log.info('Commissioning with options:', options);
|
|
1612
|
-
const nodeId = await this.commissioningController.commissionNode(options);
|
|
1613
|
-
this.log.info(`Commissioning successfully done with nodeId: ${nodeId}`);
|
|
1614
|
-
this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
|
|
1615
|
-
} // (hasParameter('pairingcode'))
|
|
1616
|
-
|
|
1617
|
-
if (hasParameter('unpairall')) {
|
|
1618
|
-
this.log.info('***Commissioning controller unpairing all nodes...');
|
|
1619
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1620
|
-
for (const nodeId of nodeIds) {
|
|
1621
|
-
this.log.info('***Commissioning controller unpairing node:', nodeId);
|
|
1622
|
-
await this.commissioningController.removeNode(nodeId);
|
|
1623
|
-
}
|
|
1624
|
-
return;
|
|
1625
|
-
}
|
|
1626
|
-
|
|
1627
|
-
if (hasParameter('discover')) {
|
|
1628
|
-
// const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
|
|
1629
|
-
// console.log(discover);
|
|
1630
|
-
}
|
|
1631
|
-
|
|
1632
|
-
if (!this.commissioningController.isCommissioned()) {
|
|
1633
|
-
this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
|
|
1634
|
-
return;
|
|
1635
|
-
}
|
|
1636
|
-
|
|
1637
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1638
|
-
this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
|
|
1639
|
-
for (const nodeId of nodeIds) {
|
|
1640
|
-
this.log.info(`***Connecting to commissioned node: ${nodeId}`);
|
|
1641
|
-
|
|
1642
|
-
const node = await this.commissioningController.connectNode(nodeId, {
|
|
1643
|
-
autoSubscribe: false,
|
|
1644
|
-
attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) =>
|
|
1645
|
-
this.log.info(`***Commissioning controller attributeChangedCallback ${peerNodeId}: attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
|
|
1646
|
-
eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) =>
|
|
1647
|
-
this.log.info(`***Commissioning controller eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
|
|
1648
|
-
stateInformationCallback: (peerNodeId, info) => {
|
|
1649
|
-
switch (info) {
|
|
1650
|
-
case NodeStateInformation.Connected:
|
|
1651
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
|
|
1652
|
-
break;
|
|
1653
|
-
case NodeStateInformation.Disconnected:
|
|
1654
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
|
|
1655
|
-
break;
|
|
1656
|
-
case NodeStateInformation.Reconnecting:
|
|
1657
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
|
|
1658
|
-
break;
|
|
1659
|
-
case NodeStateInformation.WaitingForDeviceDiscovery:
|
|
1660
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
|
|
1661
|
-
break;
|
|
1662
|
-
case NodeStateInformation.StructureChanged:
|
|
1663
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
|
|
1664
|
-
break;
|
|
1665
|
-
case NodeStateInformation.Decommissioned:
|
|
1666
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
|
|
1667
|
-
break;
|
|
1668
|
-
default:
|
|
1669
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
|
|
1670
|
-
break;
|
|
1671
|
-
}
|
|
1672
|
-
},
|
|
1673
|
-
});
|
|
1674
|
-
|
|
1675
|
-
node.logStructure();
|
|
1676
|
-
|
|
1677
|
-
// Get the interaction client
|
|
1678
|
-
this.log.info('Getting the interaction client');
|
|
1679
|
-
const interactionClient = await node.getInteractionClient();
|
|
1680
|
-
let cluster;
|
|
1681
|
-
let attributes;
|
|
1682
|
-
|
|
1683
|
-
// Log BasicInformationCluster
|
|
1684
|
-
cluster = BasicInformationCluster;
|
|
1685
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1686
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1687
|
-
});
|
|
1688
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1689
|
-
attributes.forEach((attribute) => {
|
|
1690
|
-
this.log.info(
|
|
1691
|
-
`- 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}`,
|
|
1692
|
-
);
|
|
1693
|
-
});
|
|
1694
|
-
|
|
1695
|
-
// Log PowerSourceCluster
|
|
1696
|
-
cluster = PowerSourceCluster;
|
|
1697
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1698
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1699
|
-
});
|
|
1700
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1701
|
-
attributes.forEach((attribute) => {
|
|
1702
|
-
this.log.info(
|
|
1703
|
-
`- 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}`,
|
|
1704
|
-
);
|
|
1705
|
-
});
|
|
1706
|
-
|
|
1707
|
-
// Log ThreadNetworkDiagnostics
|
|
1708
|
-
cluster = ThreadNetworkDiagnosticsCluster;
|
|
1709
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1710
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1711
|
-
});
|
|
1712
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1713
|
-
attributes.forEach((attribute) => {
|
|
1714
|
-
this.log.info(
|
|
1715
|
-
`- 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}`,
|
|
1716
|
-
);
|
|
1717
|
-
});
|
|
1718
|
-
|
|
1719
|
-
// Log SwitchCluster
|
|
1720
|
-
cluster = SwitchCluster;
|
|
1721
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1722
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1723
|
-
});
|
|
1724
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1725
|
-
attributes.forEach((attribute) => {
|
|
1726
|
-
this.log.info(
|
|
1727
|
-
`- 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}`,
|
|
1728
|
-
);
|
|
1729
|
-
});
|
|
1730
|
-
|
|
1731
|
-
this.log.info('Subscribing to all attributes and events');
|
|
1732
|
-
await node.subscribeAllAttributesAndEvents({
|
|
1733
|
-
ignoreInitialTriggers: false,
|
|
1734
|
-
attributeChangedCallback: ({ path: { nodeId, clusterId, endpointId, attributeName }, version, value }) =>
|
|
1735
|
-
this.log.info(
|
|
1736
|
-
`***${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}`,
|
|
1737
|
-
),
|
|
1738
|
-
eventTriggeredCallback: ({ path: { nodeId, clusterId, endpointId, eventName }, events }) => {
|
|
1739
|
-
this.log.info(
|
|
1740
|
-
`***${db}Commissioning controller eventTriggeredCallback: event ${BLUE}${nodeId}${db}/${or}${endpointId}${db}/${hk}${getClusterNameById(clusterId)}${db}/${zb}${eventName}${db} triggered with ${debugStringify(events ?? { none: true })}`,
|
|
1741
|
-
);
|
|
1742
|
-
},
|
|
1743
|
-
});
|
|
1744
|
-
this.log.info('Subscribed to all attributes and events');
|
|
1745
|
-
}
|
|
1746
|
-
*/
|
|
1747
1253
|
}
|
|
1748
|
-
/** ***********************************************************************************************************************************/
|
|
1749
|
-
/** Matter.js methods */
|
|
1750
|
-
/** ***********************************************************************************************************************************/
|
|
1751
|
-
/**
|
|
1752
|
-
* Starts the matter storage process based on the specified storage type and name.
|
|
1753
|
-
* @returns {Promise<void>} - A promise that resolves when the storage process is started.
|
|
1754
|
-
*/
|
|
1755
1254
|
async startMatterStorage() {
|
|
1756
|
-
// Setup Matter storage
|
|
1757
1255
|
this.log.info(`Starting matter node storage...`);
|
|
1758
1256
|
this.matterStorageService = this.environment.get(StorageService);
|
|
1759
1257
|
this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
|
|
@@ -1761,24 +1259,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1761
1259
|
this.log.info('Matter node storage manager "Matterbridge" created');
|
|
1762
1260
|
this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', bridge.code, this.aggregatorVendorId, 'Matterbridge', this.aggregatorProductId, 'Matterbridge aggregator');
|
|
1763
1261
|
this.log.info('Matter node storage started');
|
|
1764
|
-
// Backup matter storage since it is created/opened correctly
|
|
1765
1262
|
await this.backupMatterStorage(path.join(this.matterbridgeDirectory, this.matterStorageName), path.join(this.matterbridgeDirectory, this.matterStorageName + '.backup'));
|
|
1766
1263
|
}
|
|
1767
|
-
/**
|
|
1768
|
-
* Makes a backup copy of the specified matter storage directory.
|
|
1769
|
-
*
|
|
1770
|
-
* @param storageName - The name of the storage file to be backed up.
|
|
1771
|
-
* @param backupName - The name of the backup file to be created.
|
|
1772
|
-
*/
|
|
1773
1264
|
async backupMatterStorage(storageName, backupName) {
|
|
1774
1265
|
this.log.info('Creating matter node storage backup...');
|
|
1775
1266
|
await copyDirectory(storageName, backupName);
|
|
1776
1267
|
this.log.info('Created matter node storage backup');
|
|
1777
1268
|
}
|
|
1778
|
-
/**
|
|
1779
|
-
* Stops the matter storage.
|
|
1780
|
-
* @returns {Promise<void>} A promise that resolves when the storage is stopped.
|
|
1781
|
-
*/
|
|
1782
1269
|
async stopMatterStorage() {
|
|
1783
1270
|
this.log.info('Closing matter node storage...');
|
|
1784
1271
|
this.matterStorageManager?.close();
|
|
@@ -1787,24 +1274,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1787
1274
|
this.matterbridgeContext = undefined;
|
|
1788
1275
|
this.log.info('Matter node storage closed');
|
|
1789
1276
|
}
|
|
1790
|
-
/**
|
|
1791
|
-
* Creates a server node storage context.
|
|
1792
|
-
*
|
|
1793
|
-
* @param pluginName - The name of the plugin.
|
|
1794
|
-
* @param deviceName - The name of the device.
|
|
1795
|
-
* @param deviceType - The deviceType of the device.
|
|
1796
|
-
* @param vendorId - The vendor ID.
|
|
1797
|
-
* @param vendorName - The vendor name.
|
|
1798
|
-
* @param productId - The product ID.
|
|
1799
|
-
* @param productName - The product name.
|
|
1800
|
-
* @param serialNumber - The serial number of the device (optional).
|
|
1801
|
-
* @param uniqueId - The unique ID of the device (optional).
|
|
1802
|
-
* @param softwareVersion - The software version of the device (optional).
|
|
1803
|
-
* @param softwareVersionString - The software version string of the device (optional).
|
|
1804
|
-
* @param hardwareVersion - The hardware version of the device (optional).
|
|
1805
|
-
* @param hardwareVersionString - The hardware version string of the device (optional).
|
|
1806
|
-
* @returns The storage context for the commissioning server.
|
|
1807
|
-
*/
|
|
1808
1277
|
async createServerNodeContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber) {
|
|
1809
1278
|
if (!this.matterStorageService)
|
|
1810
1279
|
throw new Error('No storage service initialized');
|
|
@@ -1846,33 +1315,21 @@ export class Matterbridge extends EventEmitter {
|
|
|
1846
1315
|
this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
|
|
1847
1316
|
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
1848
1317
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1849
|
-
/**
|
|
1850
|
-
* Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration
|
|
1851
|
-
*/
|
|
1852
1318
|
const serverNode = await ServerNode.create({
|
|
1853
|
-
// Required: Give the Node a unique ID which is used to store the state of this node
|
|
1854
1319
|
id: storeId,
|
|
1855
|
-
// Provide Network relevant configuration like the port
|
|
1856
|
-
// Optional when operating only one device on a host, Default port is 5540
|
|
1857
1320
|
network: {
|
|
1858
1321
|
listeningAddressIpv4: this.ipv4address,
|
|
1859
1322
|
listeningAddressIpv6: this.ipv6address,
|
|
1860
1323
|
port,
|
|
1861
1324
|
},
|
|
1862
|
-
// Provide Commissioning relevant settings
|
|
1863
|
-
// Optional for development/testing purposes
|
|
1864
1325
|
commissioning: {
|
|
1865
1326
|
passcode,
|
|
1866
1327
|
discriminator,
|
|
1867
1328
|
},
|
|
1868
|
-
// Provide Node announcement settings
|
|
1869
|
-
// Optional: If Ommitted some development defaults are used
|
|
1870
1329
|
productDescription: {
|
|
1871
1330
|
name: await storageContext.get('deviceName'),
|
|
1872
1331
|
deviceType: DeviceTypeId(await storageContext.get('deviceType')),
|
|
1873
1332
|
},
|
|
1874
|
-
// Provide defaults for the BasicInformation cluster on the Root endpoint
|
|
1875
|
-
// Optional: If Omitted some development defaults are used
|
|
1876
1333
|
basicInformation: {
|
|
1877
1334
|
vendorId: VendorId(await storageContext.get('vendorId')),
|
|
1878
1335
|
vendorName: await storageContext.get('vendorName'),
|
|
@@ -1889,13 +1346,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1889
1346
|
},
|
|
1890
1347
|
});
|
|
1891
1348
|
const sanitizeFabrics = (fabrics, resetSessions = false) => {
|
|
1892
|
-
// New type of fabric information: Record<FabricIndex, ExposedFabricInformation>
|
|
1893
1349
|
const sanitizedFabrics = this.sanitizeFabricInformations(Array.from(Object.values(fabrics)));
|
|
1894
1350
|
this.log.info(`Fabrics: ${debugStringify(sanitizedFabrics)}`);
|
|
1895
1351
|
if (this.bridgeMode === 'bridge') {
|
|
1896
1352
|
this.matterbridgeFabricInformations = sanitizedFabrics;
|
|
1897
1353
|
if (resetSessions)
|
|
1898
|
-
this.matterbridgeSessionInformations =
|
|
1354
|
+
this.matterbridgeSessionInformations = undefined;
|
|
1899
1355
|
this.matterbridgePaired = true;
|
|
1900
1356
|
}
|
|
1901
1357
|
if (this.bridgeMode === 'childbridge') {
|
|
@@ -1903,20 +1359,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
1903
1359
|
if (plugin) {
|
|
1904
1360
|
plugin.fabricInformations = sanitizedFabrics;
|
|
1905
1361
|
if (resetSessions)
|
|
1906
|
-
plugin.sessionInformations =
|
|
1362
|
+
plugin.sessionInformations = undefined;
|
|
1907
1363
|
plugin.paired = true;
|
|
1908
1364
|
}
|
|
1909
1365
|
}
|
|
1910
1366
|
};
|
|
1911
|
-
/**
|
|
1912
|
-
* This event is triggered when the device is initially commissioned successfully.
|
|
1913
|
-
* This means: It is added to the first fabric.
|
|
1914
|
-
*/
|
|
1915
1367
|
serverNode.lifecycle.commissioned.on(() => this.log.notice(`Server node for ${storeId} was initially commissioned successfully!`));
|
|
1916
|
-
/** This event is triggered when all fabrics are removed from the device, usually it also does a factory reset then. */
|
|
1917
1368
|
serverNode.lifecycle.decommissioned.on(() => this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`));
|
|
1918
|
-
|
|
1919
|
-
serverNode.lifecycle.online.on(() => {
|
|
1369
|
+
serverNode.lifecycle.online.on(async () => {
|
|
1920
1370
|
this.log.notice(`Server node for ${storeId} is online`);
|
|
1921
1371
|
if (!serverNode.lifecycle.isCommissioned) {
|
|
1922
1372
|
this.log.notice(`Server node for ${storeId} is not commissioned. Pair to commission ...`);
|
|
@@ -1924,22 +1374,34 @@ export class Matterbridge extends EventEmitter {
|
|
|
1924
1374
|
if (this.bridgeMode === 'bridge') {
|
|
1925
1375
|
this.matterbridgeQrPairingCode = qrPairingCode;
|
|
1926
1376
|
this.matterbridgeManualPairingCode = manualPairingCode;
|
|
1927
|
-
this.matterbridgeFabricInformations =
|
|
1928
|
-
this.matterbridgeSessionInformations =
|
|
1377
|
+
this.matterbridgeFabricInformations = undefined;
|
|
1378
|
+
this.matterbridgeSessionInformations = undefined;
|
|
1929
1379
|
this.matterbridgePaired = false;
|
|
1930
1380
|
this.log.notice(`QR Code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=${qrPairingCode}`);
|
|
1931
1381
|
this.log.notice(`Manual pairing code: ${manualPairingCode}`);
|
|
1382
|
+
if (this.matterStorageService) {
|
|
1383
|
+
const storageManager = await this.matterStorageService.open(storeId);
|
|
1384
|
+
const storageContext = storageManager.createContext('persist');
|
|
1385
|
+
await storageContext.set('qrPairingCode', qrPairingCode);
|
|
1386
|
+
await storageContext.set('manualPairingCode', manualPairingCode);
|
|
1387
|
+
}
|
|
1932
1388
|
}
|
|
1933
1389
|
if (this.bridgeMode === 'childbridge') {
|
|
1934
1390
|
const plugin = this.plugins.get(storeId);
|
|
1935
1391
|
if (plugin) {
|
|
1936
1392
|
plugin.qrPairingCode = qrPairingCode;
|
|
1937
1393
|
plugin.manualPairingCode = manualPairingCode;
|
|
1938
|
-
plugin.fabricInformations =
|
|
1939
|
-
plugin.sessionInformations =
|
|
1394
|
+
plugin.fabricInformations = undefined;
|
|
1395
|
+
plugin.sessionInformations = undefined;
|
|
1940
1396
|
plugin.paired = false;
|
|
1941
1397
|
this.log.notice(`QR Code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=${qrPairingCode}`);
|
|
1942
1398
|
this.log.notice(`Manual pairing code: ${manualPairingCode}`);
|
|
1399
|
+
if (this.matterStorageService) {
|
|
1400
|
+
const storageManager = await this.matterStorageService.open(storeId);
|
|
1401
|
+
const storageContext = storageManager.createContext('persist');
|
|
1402
|
+
await storageContext.set('qrPairingCode', qrPairingCode);
|
|
1403
|
+
await storageContext.set('manualPairingCode', manualPairingCode);
|
|
1404
|
+
}
|
|
1943
1405
|
}
|
|
1944
1406
|
}
|
|
1945
1407
|
}
|
|
@@ -1949,32 +1411,27 @@ export class Matterbridge extends EventEmitter {
|
|
|
1949
1411
|
}
|
|
1950
1412
|
this.frontend.wssSendRefreshRequired();
|
|
1951
1413
|
});
|
|
1952
|
-
/** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
|
|
1953
1414
|
serverNode.lifecycle.offline.on(() => {
|
|
1954
1415
|
this.log.notice(`Server node for ${storeId} is offline`);
|
|
1955
1416
|
if (this.bridgeMode === 'bridge') {
|
|
1956
1417
|
this.matterbridgeQrPairingCode = undefined;
|
|
1957
1418
|
this.matterbridgeManualPairingCode = undefined;
|
|
1958
|
-
this.matterbridgeFabricInformations =
|
|
1959
|
-
this.matterbridgeSessionInformations =
|
|
1960
|
-
this.matterbridgePaired =
|
|
1419
|
+
this.matterbridgeFabricInformations = undefined;
|
|
1420
|
+
this.matterbridgeSessionInformations = undefined;
|
|
1421
|
+
this.matterbridgePaired = undefined;
|
|
1961
1422
|
}
|
|
1962
1423
|
if (this.bridgeMode === 'childbridge') {
|
|
1963
1424
|
const plugin = this.plugins.get(storeId);
|
|
1964
1425
|
if (plugin) {
|
|
1965
1426
|
plugin.qrPairingCode = undefined;
|
|
1966
1427
|
plugin.manualPairingCode = undefined;
|
|
1967
|
-
plugin.fabricInformations =
|
|
1968
|
-
plugin.sessionInformations =
|
|
1969
|
-
plugin.paired =
|
|
1428
|
+
plugin.fabricInformations = undefined;
|
|
1429
|
+
plugin.sessionInformations = undefined;
|
|
1430
|
+
plugin.paired = undefined;
|
|
1970
1431
|
}
|
|
1971
1432
|
}
|
|
1972
1433
|
this.frontend.wssSendRefreshRequired();
|
|
1973
1434
|
});
|
|
1974
|
-
/**
|
|
1975
|
-
* This event is triggered when a fabric is added, removed or updated on the device. Use this if more granular
|
|
1976
|
-
* information is needed.
|
|
1977
|
-
*/
|
|
1978
1435
|
serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
|
|
1979
1436
|
let action = '';
|
|
1980
1437
|
switch (fabricAction) {
|
|
@@ -2008,24 +1465,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
2008
1465
|
}
|
|
2009
1466
|
}
|
|
2010
1467
|
};
|
|
2011
|
-
/**
|
|
2012
|
-
* This event is triggered when an operative new session was opened by a Controller.
|
|
2013
|
-
* It is not triggered for the initial commissioning process, just afterwards for real connections.
|
|
2014
|
-
*/
|
|
2015
1468
|
serverNode.events.sessions.opened.on((session) => {
|
|
2016
1469
|
this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2017
1470
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
2018
1471
|
this.frontend.wssSendRefreshRequired();
|
|
2019
1472
|
});
|
|
2020
|
-
/**
|
|
2021
|
-
* This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
|
|
2022
|
-
*/
|
|
2023
1473
|
serverNode.events.sessions.closed.on((session) => {
|
|
2024
1474
|
this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2025
1475
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
2026
1476
|
this.frontend.wssSendRefreshRequired();
|
|
2027
1477
|
});
|
|
2028
|
-
/** This event is triggered when a subscription gets added or removed on an operative session. */
|
|
2029
1478
|
serverNode.events.sessions.subscriptionsChanged.on((session) => {
|
|
2030
1479
|
this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2031
1480
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
@@ -2045,6 +1494,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
2045
1494
|
return;
|
|
2046
1495
|
this.log.notice(`Closing ${matterServerNode.id} server node`);
|
|
2047
1496
|
await matterServerNode.close();
|
|
1497
|
+
await matterServerNode.env.get(MdnsService)[Symbol.asyncDispose]();
|
|
1498
|
+
}
|
|
1499
|
+
async advertiseServerNode(matterServerNode) {
|
|
1500
|
+
if (matterServerNode && matterServerNode.lifecycle.isCommissioned) {
|
|
1501
|
+
await matterServerNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
|
|
1502
|
+
const { qrPairingCode, manualPairingCode } = matterServerNode.state.commissioning.pairingCodes;
|
|
1503
|
+
this.log.notice(`Advertising for ${matterServerNode.id} is now started with the following pairing codes: qrPairingCode ${qrPairingCode}, manualPairingCode ${manualPairingCode}`);
|
|
1504
|
+
return { qrPairingCode, manualPairingCode };
|
|
1505
|
+
}
|
|
1506
|
+
return undefined;
|
|
2048
1507
|
}
|
|
2049
1508
|
async createAggregatorNode(storageContext) {
|
|
2050
1509
|
this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator `);
|
|
@@ -2052,13 +1511,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
2052
1511
|
return aggregatorNode;
|
|
2053
1512
|
}
|
|
2054
1513
|
async addBridgedEndpoint(pluginName, device) {
|
|
2055
|
-
// Check if the plugin is registered
|
|
2056
1514
|
const plugin = this.plugins.get(pluginName);
|
|
2057
1515
|
if (!plugin) {
|
|
2058
1516
|
this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
|
|
2059
1517
|
return;
|
|
2060
1518
|
}
|
|
2061
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2062
1519
|
if (this.bridgeMode === 'bridge') {
|
|
2063
1520
|
this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
|
|
2064
1521
|
if (!this.aggregatorNode)
|
|
@@ -2081,19 +1538,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
2081
1538
|
plugin.registeredDevices++;
|
|
2082
1539
|
if (plugin.addedDevices !== undefined)
|
|
2083
1540
|
plugin.addedDevices++;
|
|
2084
|
-
// Add the device to the DeviceManager
|
|
2085
1541
|
this.devices.set(device);
|
|
2086
1542
|
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}`);
|
|
2087
1543
|
}
|
|
2088
1544
|
async removeBridgedEndpoint(pluginName, device) {
|
|
2089
1545
|
this.log.debug(`Removing bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
2090
|
-
// Check if the plugin is registered
|
|
2091
1546
|
const plugin = this.plugins.get(pluginName);
|
|
2092
1547
|
if (!plugin) {
|
|
2093
1548
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
2094
1549
|
return;
|
|
2095
1550
|
}
|
|
2096
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2097
1551
|
if (this.bridgeMode === 'bridge') {
|
|
2098
1552
|
if (!this.aggregatorNode) {
|
|
2099
1553
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
|
|
@@ -2108,7 +1562,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2108
1562
|
}
|
|
2109
1563
|
else if (this.bridgeMode === 'childbridge') {
|
|
2110
1564
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2111
|
-
// Nothing to do here since the server node has no aggregator node but only the device itself
|
|
2112
1565
|
}
|
|
2113
1566
|
else if (plugin.type === 'DynamicPlatform') {
|
|
2114
1567
|
if (!plugin.aggregatorNode) {
|
|
@@ -2122,7 +1575,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2122
1575
|
plugin.registeredDevices--;
|
|
2123
1576
|
if (plugin.addedDevices !== undefined)
|
|
2124
1577
|
plugin.addedDevices--;
|
|
2125
|
-
// Close the server node TODO check if this is correct
|
|
2126
1578
|
if (plugin.registeredDevices === 0 && plugin.addedDevices === 0) {
|
|
2127
1579
|
if (plugin.serverNode) {
|
|
2128
1580
|
await this.stopServerNode(plugin.serverNode);
|
|
@@ -2133,7 +1585,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2133
1585
|
}
|
|
2134
1586
|
}
|
|
2135
1587
|
}
|
|
2136
|
-
// Remove the device from the DeviceManager
|
|
2137
1588
|
this.devices.remove(device);
|
|
2138
1589
|
}
|
|
2139
1590
|
async removeAllBridgedEndpoints(pluginName) {
|
|
@@ -2142,12 +1593,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2142
1593
|
await this.removeBridgedEndpoint(pluginName, device);
|
|
2143
1594
|
}
|
|
2144
1595
|
}
|
|
2145
|
-
/**
|
|
2146
|
-
* Sanitizes the fabric information by converting bigint properties to string cause res.json doesn't know bigint.
|
|
2147
|
-
*
|
|
2148
|
-
* @param fabricInfo - The array of exposed fabric information objects.
|
|
2149
|
-
* @returns An array of sanitized exposed fabric information objects.
|
|
2150
|
-
*/
|
|
2151
1596
|
sanitizeFabricInformations(fabricInfo) {
|
|
2152
1597
|
return fabricInfo.map((info) => {
|
|
2153
1598
|
return {
|
|
@@ -2161,12 +1606,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2161
1606
|
};
|
|
2162
1607
|
});
|
|
2163
1608
|
}
|
|
2164
|
-
/**
|
|
2165
|
-
* Sanitizes the session information by converting bigint properties to string.
|
|
2166
|
-
*
|
|
2167
|
-
* @param sessionInfo - The array of session information objects.
|
|
2168
|
-
* @returns An array of sanitized session information objects.
|
|
2169
|
-
*/
|
|
2170
1609
|
sanitizeSessionInformation(sessionInfo) {
|
|
2171
1610
|
return sessionInfo
|
|
2172
1611
|
.filter((session) => session.isPeerActive)
|
|
@@ -2194,51 +1633,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
2194
1633
|
};
|
|
2195
1634
|
});
|
|
2196
1635
|
}
|
|
2197
|
-
/**
|
|
2198
|
-
* Sets the reachability of a matter server node and trigger ReachableChanged event.
|
|
2199
|
-
*
|
|
2200
|
-
* @param {ServerNode<ServerNode.RootEndpoint>} serverNode - The commissioning server to set the reachability for.
|
|
2201
|
-
* @param {boolean} reachable - The new reachability status.
|
|
2202
|
-
*/
|
|
2203
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2204
1636
|
setServerNodeReachability(serverNode, reachable) {
|
|
2205
|
-
/*
|
|
2206
|
-
const basicInformationCluster = commissioningServer?.getRootClusterServer(BasicInformationCluster);
|
|
2207
|
-
if (basicInformationCluster && basicInformationCluster.attributes.reachable !== undefined) basicInformationCluster.setReachableAttribute(reachable);
|
|
2208
|
-
if (basicInformationCluster && basicInformationCluster.triggerReachableChangedEvent) basicInformationCluster.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
2209
|
-
*/
|
|
2210
1637
|
}
|
|
2211
|
-
/**
|
|
2212
|
-
* Sets the reachability of the specified matter aggregator and its bridged devices and trigger.
|
|
2213
|
-
* @param {EndpointNode<AggregatorEndpoint>} aggregatorNode - The matter aggregator to set the reachability for.
|
|
2214
|
-
* @param {boolean} reachable - A boolean indicating the reachability status to set.
|
|
2215
|
-
*/
|
|
2216
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2217
1638
|
setAggregatorReachability(aggregatorNode, reachable) {
|
|
2218
|
-
/*
|
|
2219
|
-
const basicInformationCluster = matterAggregator.getClusterServer(BasicInformationCluster);
|
|
2220
|
-
if (basicInformationCluster && basicInformationCluster.attributes.reachable !== undefined) basicInformationCluster.setReachableAttribute(reachable);
|
|
2221
|
-
if (basicInformationCluster && basicInformationCluster.triggerReachableChangedEvent) basicInformationCluster.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
2222
|
-
matterAggregator.getBridgedDevices().forEach((device) => {
|
|
2223
|
-
this.log.debug(`Setting reachability to true for bridged device: ${dev}${device.name}${nf}`);
|
|
2224
|
-
device.getClusterServer(BridgedDeviceBasicInformationCluster)?.setReachableAttribute(reachable);
|
|
2225
|
-
device.getClusterServer(BridgedDeviceBasicInformationCluster)?.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
2226
|
-
});
|
|
2227
|
-
*/
|
|
2228
1639
|
}
|
|
2229
|
-
/**
|
|
2230
|
-
* Sets the reachability of a device and trigger.
|
|
2231
|
-
*
|
|
2232
|
-
* @param {MatterbridgeEndpoint} device - The device to set the reachability for.
|
|
2233
|
-
* @param {boolean} reachable - The new reachability status of the device.
|
|
2234
|
-
*/
|
|
2235
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2236
1640
|
setDeviceReachability(device, reachable) {
|
|
2237
|
-
/*
|
|
2238
|
-
const basicInformationCluster = device.getClusterServer(BasicInformationCluster);
|
|
2239
|
-
if (basicInformationCluster && basicInformationCluster.attributes.reachable !== undefined) basicInformationCluster.setReachableAttribute(reachable);
|
|
2240
|
-
if (basicInformationCluster && basicInformationCluster.triggerReachableChangedEvent) basicInformationCluster.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
2241
|
-
*/
|
|
2242
1641
|
}
|
|
2243
1642
|
getVendorIdName = (vendorId) => {
|
|
2244
1643
|
if (!vendorId)
|
|
@@ -2281,36 +1680,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2281
1680
|
}
|
|
2282
1681
|
return vendorName;
|
|
2283
1682
|
};
|
|
2284
|
-
/**
|
|
2285
|
-
* Spawns a child process with the given command and arguments.
|
|
2286
|
-
* @param {string} command - The command to execute.
|
|
2287
|
-
* @param {string[]} args - The arguments to pass to the command (default: []).
|
|
2288
|
-
* @returns {Promise<boolean>} A promise that resolves when the child process exits successfully, or rejects if there is an error.
|
|
2289
|
-
*/
|
|
2290
1683
|
async spawnCommand(command, args = []) {
|
|
2291
|
-
/*
|
|
2292
|
-
npm > npm.cmd on windows
|
|
2293
|
-
cmd.exe ['dir'] on windows
|
|
2294
|
-
await this.spawnCommand('npm', ['install', '-g', 'matterbridge']);
|
|
2295
|
-
process.on('unhandledRejection', (reason, promise) => {
|
|
2296
|
-
this.log.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
2297
|
-
});
|
|
2298
|
-
|
|
2299
|
-
spawn - [14:27:21.125] [Matterbridge:spawn]: changed 38 packages in 4s
|
|
2300
|
-
spawn - [14:27:21.125] [Matterbridge:spawn]: 10 packages are looking for funding run `npm fund` for details
|
|
2301
|
-
debug - [14:27:21.131] [Matterbridge]: Child process exited with code 0 and signal null
|
|
2302
|
-
debug - [14:27:21.131] [Matterbridge]: Child process stdio streams have closed with code 0
|
|
2303
|
-
*/
|
|
2304
1684
|
const cmdLine = command + ' ' + args.join(' ');
|
|
2305
1685
|
if (process.platform === 'win32' && command === 'npm') {
|
|
2306
|
-
// Must be spawn('cmd.exe', ['/c', 'npm -g install <package>']);
|
|
2307
1686
|
const argstring = 'npm ' + args.join(' ');
|
|
2308
1687
|
args.splice(0, args.length, '/c', argstring);
|
|
2309
1688
|
command = 'cmd.exe';
|
|
2310
1689
|
}
|
|
2311
|
-
// Decide when using sudo on linux
|
|
2312
|
-
// When you need sudo: Spawn stderr: npm error Error: EACCES: permission denied
|
|
2313
|
-
// When you don't need sudo: Failed to start child process "npm install -g matterbridge-eve-door": spawn sudo ENOENT
|
|
2314
1690
|
if (hasParameter('sudo') || (process.platform === 'linux' && command === 'npm' && !hasParameter('docker') && !hasParameter('nosudo'))) {
|
|
2315
1691
|
args.unshift(command);
|
|
2316
1692
|
command = 'sudo';
|
|
@@ -2369,4 +1745,3 @@ export class Matterbridge extends EventEmitter {
|
|
|
2369
1745
|
});
|
|
2370
1746
|
}
|
|
2371
1747
|
}
|
|
2372
|
-
//# sourceMappingURL=matterbridge.js.map
|