matterbridge 2.0.0 → 2.1.0-dev.10
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 +26 -4
- 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 +143 -313
- package/dist/index.js +2 -30
- package/dist/logger/export.js +0 -1
- package/dist/matter/behaviors.js +1 -0
- package/dist/matter/clusters.js +1 -0
- package/dist/matter/devices.js +1 -0
- package/dist/matter/endpoints.js +1 -0
- package/dist/matter/export.js +1 -11
- package/dist/matter/types.js +2 -0
- package/dist/matterbridge.js +77 -715
- package/dist/matterbridgeAccessoryPlatform.js +0 -33
- package/dist/matterbridgeBehaviors.js +38 -38
- package/dist/matterbridgeDeviceTypes.js +11 -112
- package/dist/matterbridgeDynamicPlatform.js +0 -33
- package/dist/matterbridgeEndpoint.js +664 -2563
- package/dist/matterbridgeEndpointHelpers.js +513 -0
- package/dist/matterbridgePlatform.js +5 -125
- 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 +86 -78
- package/package.json +22 -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
|
-
|
|
35
|
-
import { logInterfaces, wait, waiter, copyDirectory, getParameter, getIntParameter, hasParameter } from './utils/utils.js';
|
|
10
|
+
import { logInterfaces, copyDirectory, getParameter, getIntParameter, hasParameter } from './utils/utils.js';
|
|
36
11
|
import { PluginManager } from './pluginManager.js';
|
|
37
12
|
import { DeviceManager } from './deviceManager.js';
|
|
38
13
|
import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
|
|
39
14
|
import { bridge } from './matterbridgeDeviceTypes.js';
|
|
40
15
|
import { Frontend } from './frontend.js';
|
|
41
|
-
// @matter
|
|
42
16
|
import { DeviceTypeId, Endpoint as EndpointNode, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, VendorId, StorageService, Environment, ServerNode } from '@matter/main';
|
|
43
|
-
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,80 +138,48 @@ 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
|
-
await waiter('destroying instance...', () => {
|
|
204
|
-
return this.initialized === false && this.execRunningCount <= 0 ? true : false;
|
|
205
|
-
}, false, 60000, 100, false);
|
|
206
|
-
await wait(1000, 'Wait for the global node_modules and matterbridge version', false);
|
|
207
143
|
}
|
|
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
144
|
async initialize() {
|
|
219
|
-
// Set the restart mode
|
|
220
145
|
if (hasParameter('service'))
|
|
221
146
|
this.restartMode = 'service';
|
|
222
147
|
if (hasParameter('docker'))
|
|
223
148
|
this.restartMode = 'docker';
|
|
224
|
-
// Set the matterbridge directory
|
|
225
149
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
226
150
|
this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
|
|
227
|
-
// Setup matter environment
|
|
228
151
|
this.environment.vars.set('log.level', MatterLogLevel.INFO);
|
|
229
152
|
this.environment.vars.set('log.format', MatterLogFormat.ANSI);
|
|
230
153
|
this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, this.matterStorageName));
|
|
231
154
|
this.environment.vars.set('runtime.signals', false);
|
|
232
155
|
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
|
|
156
|
+
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
236
157
|
this.registerProcessHandlers();
|
|
237
|
-
// Initialize nodeStorage and nodeContext
|
|
238
158
|
try {
|
|
239
159
|
this.log.debug(`Creating node storage manager: ${CYAN}${this.nodeStorageName}${db}`);
|
|
240
160
|
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, this.nodeStorageName), writeQueue: false, expiredInterval: undefined, logging: false });
|
|
241
161
|
this.log.debug('Creating node storage context for matterbridge');
|
|
242
162
|
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
163
|
const keys = (await this.nodeStorage?.storage.keys());
|
|
246
164
|
for (const key of keys) {
|
|
247
165
|
this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
|
|
248
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
249
166
|
await this.nodeStorage?.storage.get(key);
|
|
250
167
|
}
|
|
251
168
|
const storages = await this.nodeStorage.getStorageNames();
|
|
252
169
|
for (const storage of storages) {
|
|
253
170
|
this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
|
|
254
171
|
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
172
|
const keys = (await nodeContext?.storage.keys());
|
|
258
173
|
keys.forEach(async (key) => {
|
|
259
174
|
this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
|
|
260
175
|
await nodeContext?.get(key);
|
|
261
176
|
});
|
|
262
177
|
}
|
|
263
|
-
// Creating a backup of the node storage since it is not corrupted
|
|
264
178
|
this.log.debug('Creating node storage backup...');
|
|
265
179
|
await copyDirectory(path.join(this.matterbridgeDirectory, this.nodeStorageName), path.join(this.matterbridgeDirectory, this.nodeStorageName + '.backup'));
|
|
266
180
|
this.log.debug('Created node storage backup');
|
|
267
181
|
}
|
|
268
182
|
catch (error) {
|
|
269
|
-
// Restoring the backup of the node storage since it is corrupted
|
|
270
183
|
this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
|
|
271
184
|
if (hasParameter('norestore')) {
|
|
272
185
|
this.log.fatal(`The matterbridge node storage is corrupted. Parameter -norestore found: exiting...`);
|
|
@@ -281,51 +194,45 @@ export class Matterbridge extends EventEmitter {
|
|
|
281
194
|
this.log.fatal('Fatal error creating node storage manager and context for matterbridge');
|
|
282
195
|
throw new Error('Fatal error creating node storage manager and context for matterbridge');
|
|
283
196
|
}
|
|
284
|
-
// Set the first port to use for the commissioning server (will be incremented in childbridge mode)
|
|
285
197
|
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
198
|
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
199
|
this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator();
|
|
290
200
|
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
201
|
if (hasParameter('logger')) {
|
|
293
202
|
const level = getParameter('logger');
|
|
294
203
|
if (level === 'debug') {
|
|
295
|
-
this.log.logLevel = "debug"
|
|
204
|
+
this.log.logLevel = "debug";
|
|
296
205
|
}
|
|
297
206
|
else if (level === 'info') {
|
|
298
|
-
this.log.logLevel = "info"
|
|
207
|
+
this.log.logLevel = "info";
|
|
299
208
|
}
|
|
300
209
|
else if (level === 'notice') {
|
|
301
|
-
this.log.logLevel = "notice"
|
|
210
|
+
this.log.logLevel = "notice";
|
|
302
211
|
}
|
|
303
212
|
else if (level === 'warn') {
|
|
304
|
-
this.log.logLevel = "warn"
|
|
213
|
+
this.log.logLevel = "warn";
|
|
305
214
|
}
|
|
306
215
|
else if (level === 'error') {
|
|
307
|
-
this.log.logLevel = "error"
|
|
216
|
+
this.log.logLevel = "error";
|
|
308
217
|
}
|
|
309
218
|
else if (level === 'fatal') {
|
|
310
|
-
this.log.logLevel = "fatal"
|
|
219
|
+
this.log.logLevel = "fatal";
|
|
311
220
|
}
|
|
312
221
|
else {
|
|
313
222
|
this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
|
|
314
|
-
this.log.logLevel = "info"
|
|
223
|
+
this.log.logLevel = "info";
|
|
315
224
|
}
|
|
316
225
|
}
|
|
317
226
|
else {
|
|
318
|
-
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', "info"
|
|
227
|
+
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', "info");
|
|
319
228
|
}
|
|
320
229
|
MatterbridgeEndpoint.logLevel = this.log.logLevel;
|
|
321
|
-
// Create the file logger for matterbridge (context: matterbridgeFileLog)
|
|
322
230
|
if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
|
|
323
231
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), this.log.logLevel, true);
|
|
324
232
|
this.matterbridgeInformation.fileLogger = true;
|
|
325
233
|
}
|
|
326
234
|
this.log.notice('Matterbridge is starting...');
|
|
327
235
|
this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.matterbridgeInformation.fileLogger}.`);
|
|
328
|
-
// Set matter.js logger level, format and logger (context: matterLogLevel)
|
|
329
236
|
if (hasParameter('matterlogger')) {
|
|
330
237
|
const level = getParameter('matterlogger');
|
|
331
238
|
if (level === 'debug') {
|
|
@@ -356,7 +263,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
356
263
|
}
|
|
357
264
|
Logger.format = MatterLogFormat.ANSI;
|
|
358
265
|
Logger.setLogger('default', this.createMatterLogger());
|
|
359
|
-
// Create the file logger for matter.js (context: matterFileLog)
|
|
360
266
|
if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
|
|
361
267
|
this.matterbridgeInformation.matterFileLogger = true;
|
|
362
268
|
Logger.addLogger('matterfilelogger', await this.createMatterFileLogger(path.join(this.matterbridgeDirectory, this.matterLoggerFile), true), {
|
|
@@ -365,7 +271,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
365
271
|
});
|
|
366
272
|
}
|
|
367
273
|
this.log.debug(`Matter logLevel: ${Logger.defaultLogLevel} fileLoger: ${this.matterbridgeInformation.matterFileLogger}.`);
|
|
368
|
-
// Set the interface to use for matter server node mdnsInterface
|
|
369
274
|
if (hasParameter('mdnsinterface')) {
|
|
370
275
|
this.mdnsInterface = getParameter('mdnsinterface');
|
|
371
276
|
}
|
|
@@ -374,7 +279,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
374
279
|
if (this.mdnsInterface === '')
|
|
375
280
|
this.mdnsInterface = undefined;
|
|
376
281
|
}
|
|
377
|
-
// Validate mdnsInterface
|
|
378
282
|
if (this.mdnsInterface) {
|
|
379
283
|
const networkInterfaces = os.networkInterfaces();
|
|
380
284
|
const availableInterfaces = Object.keys(networkInterfaces);
|
|
@@ -388,7 +292,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
388
292
|
}
|
|
389
293
|
if (this.mdnsInterface)
|
|
390
294
|
this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
|
|
391
|
-
// Set the listeningAddressIpv4 for the matter commissioning server
|
|
392
295
|
if (hasParameter('ipv4address')) {
|
|
393
296
|
this.ipv4address = getParameter('ipv4address');
|
|
394
297
|
}
|
|
@@ -397,7 +300,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
397
300
|
if (this.ipv4address === '')
|
|
398
301
|
this.ipv4address = undefined;
|
|
399
302
|
}
|
|
400
|
-
// Set the listeningAddressIpv6 for the matter commissioning server
|
|
401
303
|
if (hasParameter('ipv6address')) {
|
|
402
304
|
this.ipv6address = getParameter('ipv6address');
|
|
403
305
|
}
|
|
@@ -406,17 +308,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
406
308
|
if (this.ipv6address === '')
|
|
407
309
|
this.ipv6address = undefined;
|
|
408
310
|
}
|
|
409
|
-
// Initialize PluginManager
|
|
410
311
|
this.plugins = new PluginManager(this);
|
|
411
312
|
await this.plugins.loadFromStorage();
|
|
412
|
-
// Initialize DeviceManager
|
|
413
313
|
this.devices = new DeviceManager(this, this.nodeContext);
|
|
414
|
-
// Get the plugins from node storage and create the plugins node storage contexts
|
|
415
314
|
for (const plugin of this.plugins) {
|
|
416
315
|
const packageJson = await this.plugins.parse(plugin);
|
|
417
316
|
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
317
|
this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
|
|
421
318
|
try {
|
|
422
319
|
await this.spawnCommand('npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose']);
|
|
@@ -438,7 +335,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
438
335
|
await plugin.nodeContext.set('description', plugin.description);
|
|
439
336
|
await plugin.nodeContext.set('author', plugin.author);
|
|
440
337
|
}
|
|
441
|
-
// Log system info and create .matterbridge directory
|
|
442
338
|
await this.logNodeAndSystemInfo();
|
|
443
339
|
this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
|
|
444
340
|
`${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
|
|
@@ -446,7 +342,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
446
342
|
`${hasParameter('controller') ? 'mode controller ' : ''}` +
|
|
447
343
|
`${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
|
|
448
344
|
`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
345
|
const minNodeVersion = 18;
|
|
451
346
|
const nodeVersion = process.versions.node;
|
|
452
347
|
const versionMajor = parseInt(nodeVersion.split('.')[0]);
|
|
@@ -454,15 +349,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
454
349
|
this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
455
350
|
throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
456
351
|
}
|
|
457
|
-
// Parse command line
|
|
458
352
|
await this.parseCommandLine();
|
|
459
353
|
this.initialized = true;
|
|
460
354
|
}
|
|
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
355
|
async parseCommandLine() {
|
|
467
356
|
if (hasParameter('help')) {
|
|
468
357
|
this.log.info(`\nUsage: matterbridge [options]\n
|
|
@@ -572,7 +461,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
572
461
|
await this.shutdownProcessAndFactoryReset();
|
|
573
462
|
return;
|
|
574
463
|
}
|
|
575
|
-
// Start the matter storage and create the matterbridge context
|
|
576
464
|
try {
|
|
577
465
|
await this.startMatterStorage();
|
|
578
466
|
}
|
|
@@ -580,12 +468,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
580
468
|
this.log.fatal(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
581
469
|
throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
582
470
|
}
|
|
583
|
-
// Clear the matterbridge context if the reset parameter is set
|
|
584
471
|
if (hasParameter('reset') && getParameter('reset') === undefined) {
|
|
585
472
|
await this.shutdownProcessAndReset();
|
|
586
473
|
return;
|
|
587
474
|
}
|
|
588
|
-
// Clear matterbridge plugin context if the reset parameter is set
|
|
589
475
|
if (hasParameter('reset') && getParameter('reset') !== undefined) {
|
|
590
476
|
this.log.debug(`Reset plugin ${getParameter('reset')}`);
|
|
591
477
|
const plugin = this.plugins.get(getParameter('reset'));
|
|
@@ -607,10 +493,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
607
493
|
this.emit('shutdown');
|
|
608
494
|
return;
|
|
609
495
|
}
|
|
610
|
-
// Initialize frontend
|
|
611
496
|
if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
|
|
612
497
|
await this.frontend.start(getIntParameter('frontend'));
|
|
613
|
-
// Check each 60 minutes the latest versions
|
|
614
498
|
this.checkUpdateInterval = setInterval(() => {
|
|
615
499
|
this.getMatterbridgeLatestVersion();
|
|
616
500
|
for (const plugin of this.plugins) {
|
|
@@ -618,24 +502,20 @@ export class Matterbridge extends EventEmitter {
|
|
|
618
502
|
}
|
|
619
503
|
this.frontend.wssSendRefreshRequired();
|
|
620
504
|
}, 60 * 60 * 1000);
|
|
621
|
-
// Start the matterbridge in mode test
|
|
622
505
|
if (hasParameter('test')) {
|
|
623
506
|
this.bridgeMode = 'bridge';
|
|
624
507
|
MatterbridgeEndpoint.bridgeMode = 'bridge';
|
|
625
508
|
return;
|
|
626
509
|
}
|
|
627
|
-
// Start the matterbridge in mode controller
|
|
628
510
|
if (hasParameter('controller')) {
|
|
629
511
|
this.bridgeMode = 'controller';
|
|
630
512
|
await this.startController();
|
|
631
513
|
return;
|
|
632
514
|
}
|
|
633
|
-
// Check if the bridge mode is set and start matterbridge in bridge mode if not set
|
|
634
515
|
if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
|
|
635
516
|
this.log.info('Setting default matterbridge start mode to bridge');
|
|
636
517
|
await this.nodeContext?.set('bridgeMode', 'bridge');
|
|
637
518
|
}
|
|
638
|
-
// Start matterbridge in bridge mode
|
|
639
519
|
if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
|
|
640
520
|
this.bridgeMode = 'bridge';
|
|
641
521
|
MatterbridgeEndpoint.bridgeMode = 'bridge';
|
|
@@ -643,7 +523,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
643
523
|
await this.startBridge();
|
|
644
524
|
return;
|
|
645
525
|
}
|
|
646
|
-
// Start matterbridge in childbridge mode
|
|
647
526
|
if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
|
|
648
527
|
this.bridgeMode = 'childbridge';
|
|
649
528
|
MatterbridgeEndpoint.bridgeMode = 'childbridge';
|
|
@@ -652,28 +531,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
652
531
|
return;
|
|
653
532
|
}
|
|
654
533
|
}
|
|
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
534
|
async startPlugins() {
|
|
664
|
-
// Check, load and start the plugins
|
|
665
535
|
for (const plugin of this.plugins) {
|
|
666
536
|
plugin.configJson = await this.plugins.loadConfig(plugin);
|
|
667
537
|
plugin.schemaJson = await this.plugins.loadSchema(plugin);
|
|
668
|
-
// Check if the plugin is available
|
|
669
538
|
if (!(await this.plugins.resolve(plugin.path))) {
|
|
670
539
|
this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
|
|
671
540
|
plugin.enabled = false;
|
|
672
541
|
plugin.error = true;
|
|
673
542
|
continue;
|
|
674
543
|
}
|
|
675
|
-
// Check if the plugin has a new version
|
|
676
|
-
// this.getPluginLatestVersion(plugin); // No await do it asyncronously
|
|
677
544
|
if (!plugin.enabled) {
|
|
678
545
|
this.log.info(`Plugin ${plg}${plugin.name}${nf} not enabled`);
|
|
679
546
|
continue;
|
|
@@ -687,26 +554,20 @@ export class Matterbridge extends EventEmitter {
|
|
|
687
554
|
plugin.addedDevices = undefined;
|
|
688
555
|
plugin.qrPairingCode = undefined;
|
|
689
556
|
plugin.manualPairingCode = undefined;
|
|
690
|
-
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
557
|
+
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
691
558
|
}
|
|
692
559
|
this.frontend.wssSendRefreshRequired();
|
|
693
560
|
}
|
|
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
561
|
registerProcessHandlers() {
|
|
699
562
|
this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
|
|
700
563
|
process.removeAllListeners('uncaughtException');
|
|
701
564
|
process.removeAllListeners('unhandledRejection');
|
|
702
565
|
this.exceptionHandler = async (error) => {
|
|
703
566
|
this.log.fatal('Unhandled Exception detected at:', error.stack || error, rs);
|
|
704
|
-
// await this.cleanup('Unhandled Exception detected, cleaning up...');
|
|
705
567
|
};
|
|
706
568
|
process.on('uncaughtException', this.exceptionHandler);
|
|
707
569
|
this.rejectionHandler = async (reason, promise) => {
|
|
708
570
|
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
571
|
};
|
|
711
572
|
process.on('unhandledRejection', this.rejectionHandler);
|
|
712
573
|
this.log.debug(`Registering SIGINT and SIGTERM signal handlers...`);
|
|
@@ -719,9 +580,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
719
580
|
};
|
|
720
581
|
process.on('SIGTERM', this.sigtermHandler);
|
|
721
582
|
}
|
|
722
|
-
/**
|
|
723
|
-
* Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
|
|
724
|
-
*/
|
|
725
583
|
deregisterProcesslHandlers() {
|
|
726
584
|
this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
|
|
727
585
|
if (this.exceptionHandler)
|
|
@@ -738,17 +596,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
738
596
|
process.off('SIGTERM', this.sigtermHandler);
|
|
739
597
|
this.sigtermHandler = undefined;
|
|
740
598
|
}
|
|
741
|
-
/**
|
|
742
|
-
* Logs the node and system information.
|
|
743
|
-
*/
|
|
744
599
|
async logNodeAndSystemInfo() {
|
|
745
|
-
// IP address information
|
|
746
600
|
const networkInterfaces = os.networkInterfaces();
|
|
747
601
|
this.systemInformation.interfaceName = '';
|
|
748
602
|
this.systemInformation.ipv4Address = '';
|
|
749
603
|
this.systemInformation.ipv6Address = '';
|
|
750
604
|
for (const [interfaceName, interfaceDetails] of Object.entries(networkInterfaces)) {
|
|
751
|
-
// this.log.debug(`Checking interface: '${interfaceName}' for '${this.mdnsInterface}'`);
|
|
752
605
|
if (this.mdnsInterface && interfaceName !== this.mdnsInterface)
|
|
753
606
|
continue;
|
|
754
607
|
if (!interfaceDetails) {
|
|
@@ -774,22 +627,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
774
627
|
break;
|
|
775
628
|
}
|
|
776
629
|
}
|
|
777
|
-
// Node information
|
|
778
630
|
this.systemInformation.nodeVersion = process.versions.node;
|
|
779
631
|
const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
|
|
780
632
|
const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
|
|
781
633
|
const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
|
|
782
|
-
// Host system information
|
|
783
634
|
this.systemInformation.hostname = os.hostname();
|
|
784
635
|
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
|
|
636
|
+
this.systemInformation.osType = os.type();
|
|
637
|
+
this.systemInformation.osRelease = os.release();
|
|
638
|
+
this.systemInformation.osPlatform = os.platform();
|
|
639
|
+
this.systemInformation.osArch = os.arch();
|
|
640
|
+
this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
641
|
+
this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
642
|
+
this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
|
|
793
643
|
this.log.debug('Host System Information:');
|
|
794
644
|
this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
|
|
795
645
|
this.log.debug(`- User: ${this.systemInformation.user}`);
|
|
@@ -805,19 +655,15 @@ export class Matterbridge extends EventEmitter {
|
|
|
805
655
|
this.log.debug(`- Total Memory: ${this.systemInformation.totalMemory}`);
|
|
806
656
|
this.log.debug(`- Free Memory: ${this.systemInformation.freeMemory}`);
|
|
807
657
|
this.log.debug(`- System Uptime: ${this.systemInformation.systemUptime}`);
|
|
808
|
-
// Home directory
|
|
809
658
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
810
659
|
this.matterbridgeInformation.homeDirectory = this.homeDirectory;
|
|
811
660
|
this.log.debug(`Home Directory: ${this.homeDirectory}`);
|
|
812
|
-
// Package root directory
|
|
813
661
|
const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
814
662
|
this.rootDirectory = path.resolve(currentFileDirectory, '../');
|
|
815
663
|
this.matterbridgeInformation.rootDirectory = this.rootDirectory;
|
|
816
664
|
this.log.debug(`Root Directory: ${this.rootDirectory}`);
|
|
817
|
-
// Global node_modules directory
|
|
818
665
|
if (this.nodeContext)
|
|
819
666
|
this.globalModulesDirectory = this.matterbridgeInformation.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
|
|
820
|
-
// First run of Matterbridge so the node storage is empty
|
|
821
667
|
if (this.globalModulesDirectory === '') {
|
|
822
668
|
try {
|
|
823
669
|
this.globalModulesDirectory = await this.getGlobalNodeModules();
|
|
@@ -831,20 +677,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
831
677
|
}
|
|
832
678
|
else
|
|
833
679
|
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
680
|
this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
|
|
849
681
|
this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
|
|
850
682
|
try {
|
|
@@ -868,7 +700,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
868
700
|
}
|
|
869
701
|
}
|
|
870
702
|
this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
|
|
871
|
-
// Create the plugin directory Matterbridge in the home directory
|
|
872
703
|
this.matterbridgePluginDirectory = path.join(this.homeDirectory, 'Matterbridge');
|
|
873
704
|
this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
|
|
874
705
|
try {
|
|
@@ -892,28 +723,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
892
723
|
}
|
|
893
724
|
}
|
|
894
725
|
this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
|
|
895
|
-
// Matterbridge version
|
|
896
726
|
const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
|
|
897
727
|
this.matterbridgeVersion = this.matterbridgeLatestVersion = packageJson.version;
|
|
898
728
|
this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeInformation.matterbridgeLatestVersion = this.matterbridgeVersion;
|
|
899
729
|
this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
|
|
900
|
-
// Matterbridge latest version
|
|
901
730
|
if (this.nodeContext)
|
|
902
731
|
this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
|
|
903
732
|
this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
|
|
904
|
-
// this.getMatterbridgeLatestVersion();
|
|
905
|
-
// Current working directory
|
|
906
733
|
const currentDir = process.cwd();
|
|
907
734
|
this.log.debug(`Current Working Directory: ${currentDir}`);
|
|
908
|
-
// Command line arguments (excluding 'node' and the script name)
|
|
909
735
|
const cmdArgs = process.argv.slice(2).join(' ');
|
|
910
736
|
this.log.debug(`Command Line Arguments: ${cmdArgs}`);
|
|
911
737
|
}
|
|
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
738
|
async getLatestVersion(packageName) {
|
|
918
739
|
return new Promise((resolve, reject) => {
|
|
919
740
|
this.execRunningCount++;
|
|
@@ -928,16 +749,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
928
749
|
});
|
|
929
750
|
});
|
|
930
751
|
}
|
|
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
752
|
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
753
|
return new Promise((resolve, reject) => {
|
|
942
754
|
this.execRunningCount++;
|
|
943
755
|
exec('npm root -g', (error, stdout) => {
|
|
@@ -951,11 +763,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
951
763
|
});
|
|
952
764
|
});
|
|
953
765
|
}
|
|
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
766
|
async getMatterbridgeLatestVersion() {
|
|
960
767
|
this.getLatestVersion('matterbridge')
|
|
961
768
|
.then(async (matterbridgeLatestVersion) => {
|
|
@@ -972,19 +779,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
972
779
|
})
|
|
973
780
|
.catch((error) => {
|
|
974
781
|
this.log.error(`Error getting Matterbridge latest version: ${error.message}`);
|
|
975
|
-
// error.stack && this.log.debug(error.stack);
|
|
976
782
|
});
|
|
977
783
|
}
|
|
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
784
|
async getPluginLatestVersion(plugin) {
|
|
989
785
|
this.getLatestVersion(plugin.name)
|
|
990
786
|
.then(async (latestVersion) => {
|
|
@@ -996,54 +792,40 @@ export class Matterbridge extends EventEmitter {
|
|
|
996
792
|
})
|
|
997
793
|
.catch((error) => {
|
|
998
794
|
this.log.error(`Error getting ${plg}${plugin.name}${er} latest version: ${error.message}`);
|
|
999
|
-
// error.stack && this.log.debug(error.stack);
|
|
1000
795
|
});
|
|
1001
796
|
}
|
|
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
797
|
createMatterLogger() {
|
|
1008
|
-
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4
|
|
798
|
+
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
|
|
1009
799
|
return (_level, formattedLog) => {
|
|
1010
800
|
const logger = formattedLog.slice(44, 44 + 20).trim();
|
|
1011
801
|
const message = formattedLog.slice(65);
|
|
1012
802
|
matterLogger.logName = logger;
|
|
1013
803
|
switch (_level) {
|
|
1014
804
|
case MatterLogLevel.DEBUG:
|
|
1015
|
-
matterLogger.log("debug"
|
|
805
|
+
matterLogger.log("debug", message);
|
|
1016
806
|
break;
|
|
1017
807
|
case MatterLogLevel.INFO:
|
|
1018
|
-
matterLogger.log("info"
|
|
808
|
+
matterLogger.log("info", message);
|
|
1019
809
|
break;
|
|
1020
810
|
case MatterLogLevel.NOTICE:
|
|
1021
|
-
matterLogger.log("notice"
|
|
811
|
+
matterLogger.log("notice", message);
|
|
1022
812
|
break;
|
|
1023
813
|
case MatterLogLevel.WARN:
|
|
1024
|
-
matterLogger.log("warn"
|
|
814
|
+
matterLogger.log("warn", message);
|
|
1025
815
|
break;
|
|
1026
816
|
case MatterLogLevel.ERROR:
|
|
1027
|
-
matterLogger.log("error"
|
|
817
|
+
matterLogger.log("error", message);
|
|
1028
818
|
break;
|
|
1029
819
|
case MatterLogLevel.FATAL:
|
|
1030
|
-
matterLogger.log("fatal"
|
|
820
|
+
matterLogger.log("fatal", message);
|
|
1031
821
|
break;
|
|
1032
822
|
default:
|
|
1033
|
-
matterLogger.log("debug"
|
|
823
|
+
matterLogger.log("debug", message);
|
|
1034
824
|
break;
|
|
1035
825
|
}
|
|
1036
826
|
};
|
|
1037
827
|
}
|
|
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
828
|
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
829
|
let fileSize = 0;
|
|
1048
830
|
if (unlink) {
|
|
1049
831
|
try {
|
|
@@ -1092,21 +874,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1092
874
|
}
|
|
1093
875
|
};
|
|
1094
876
|
}
|
|
1095
|
-
/**
|
|
1096
|
-
* Restarts the process by exiting the current instance and loading a new instance.
|
|
1097
|
-
*/
|
|
1098
877
|
async restartProcess() {
|
|
1099
878
|
await this.cleanup('restarting...', true);
|
|
1100
879
|
}
|
|
1101
|
-
/**
|
|
1102
|
-
* Shut down the process by exiting the current process.
|
|
1103
|
-
*/
|
|
1104
880
|
async shutdownProcess() {
|
|
1105
881
|
await this.cleanup('shutting down...', false);
|
|
1106
882
|
}
|
|
1107
|
-
/**
|
|
1108
|
-
* Update matterbridge and and shut down the process.
|
|
1109
|
-
*/
|
|
1110
883
|
async updateProcess() {
|
|
1111
884
|
this.log.info('Updating matterbridge...');
|
|
1112
885
|
try {
|
|
@@ -1119,9 +892,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1119
892
|
this.frontend.wssSendRestartRequired();
|
|
1120
893
|
await this.cleanup('updating...', false);
|
|
1121
894
|
}
|
|
1122
|
-
/**
|
|
1123
|
-
* Unregister all devices and shut down the process.
|
|
1124
|
-
*/
|
|
1125
895
|
async unregisterAndShutdownProcess() {
|
|
1126
896
|
this.log.info('Unregistering all devices and shutting down...');
|
|
1127
897
|
for (const plugin of this.plugins) {
|
|
@@ -1129,9 +899,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1129
899
|
}
|
|
1130
900
|
await this.cleanup('unregistered all devices and shutting down...', false);
|
|
1131
901
|
}
|
|
1132
|
-
/**
|
|
1133
|
-
* Reset commissioning and shut down the process.
|
|
1134
|
-
*/
|
|
1135
902
|
async shutdownProcessAndReset() {
|
|
1136
903
|
this.log.info('Resetting Matterbridge commissioning information...');
|
|
1137
904
|
await this.matterStorageManager?.createContext('events')?.clearAll();
|
|
@@ -1143,12 +910,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1143
910
|
this.log.info('Matter storage reset done! Remove the bridge from the controller.');
|
|
1144
911
|
await this.cleanup('shutting down with reset...', false);
|
|
1145
912
|
}
|
|
1146
|
-
/**
|
|
1147
|
-
* Factory reset and shut down the process.
|
|
1148
|
-
*/
|
|
1149
913
|
async shutdownProcessAndFactoryReset() {
|
|
1150
914
|
try {
|
|
1151
|
-
// Delete old matter storage file and backup
|
|
1152
915
|
const file = path.join(this.matterbridgeDirectory, 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.json');
|
|
1153
916
|
this.log.info(`Unlinking old matter storage file: ${file}`);
|
|
1154
917
|
await fs.unlink(file);
|
|
@@ -1162,7 +925,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1162
925
|
}
|
|
1163
926
|
}
|
|
1164
927
|
try {
|
|
1165
|
-
// Delete matter node storage directory with its subdirectories and backup
|
|
1166
928
|
const dir = path.join(this.matterbridgeDirectory, 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
|
|
1167
929
|
this.log.info(`Removing matter node storage directory: ${dir}`);
|
|
1168
930
|
await fs.rm(dir, { recursive: true });
|
|
@@ -1176,7 +938,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1176
938
|
}
|
|
1177
939
|
}
|
|
1178
940
|
try {
|
|
1179
|
-
// Delete node storage directory with its subdirectories and backup
|
|
1180
941
|
const dir = path.join(this.matterbridgeDirectory, 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
|
|
1181
942
|
this.log.info(`Removing storage directory: ${dir}`);
|
|
1182
943
|
await fs.rm(dir, { recursive: true });
|
|
@@ -1196,41 +957,30 @@ export class Matterbridge extends EventEmitter {
|
|
|
1196
957
|
this.devices.clear();
|
|
1197
958
|
await this.cleanup('shutting down with factory reset...', false);
|
|
1198
959
|
}
|
|
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
960
|
async cleanup(message, restart = false) {
|
|
1206
961
|
if (this.initialized && !this.hasCleanupStarted) {
|
|
1207
962
|
this.hasCleanupStarted = true;
|
|
1208
963
|
this.log.info(message);
|
|
1209
|
-
// Clear the start matter interval
|
|
1210
964
|
if (this.startMatterInterval) {
|
|
1211
965
|
clearInterval(this.startMatterInterval);
|
|
1212
966
|
this.startMatterInterval = undefined;
|
|
1213
967
|
this.log.debug('Start matter interval cleared');
|
|
1214
968
|
}
|
|
1215
|
-
// Clear the check update interval
|
|
1216
969
|
if (this.checkUpdateInterval) {
|
|
1217
970
|
clearInterval(this.checkUpdateInterval);
|
|
1218
971
|
this.checkUpdateInterval = undefined;
|
|
1219
972
|
this.log.debug('Check update interval cleared');
|
|
1220
973
|
}
|
|
1221
|
-
// Clear the configure timeout
|
|
1222
974
|
if (this.configureTimeout) {
|
|
1223
975
|
clearTimeout(this.configureTimeout);
|
|
1224
976
|
this.configureTimeout = undefined;
|
|
1225
977
|
this.log.debug('Matterbridge configure timeout cleared');
|
|
1226
978
|
}
|
|
1227
|
-
// Clear the reachability timeout
|
|
1228
979
|
if (this.reachabilityTimeout) {
|
|
1229
980
|
clearTimeout(this.reachabilityTimeout);
|
|
1230
981
|
this.reachabilityTimeout = undefined;
|
|
1231
982
|
this.log.debug('Matterbridge reachability timeout cleared');
|
|
1232
983
|
}
|
|
1233
|
-
// Calling the shutdown method of each plugin and clear the plugins reachability timeout
|
|
1234
984
|
for (const plugin of this.plugins) {
|
|
1235
985
|
if (!plugin.enabled || plugin.error)
|
|
1236
986
|
continue;
|
|
@@ -1241,13 +991,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1241
991
|
this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
|
|
1242
992
|
}
|
|
1243
993
|
}
|
|
1244
|
-
// Stop the frontend
|
|
1245
994
|
this.frontend.stop();
|
|
1246
|
-
// Stopping matter server nodes
|
|
1247
995
|
this.log.info(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
|
|
1248
996
|
if (this.bridgeMode === 'bridge') {
|
|
1249
997
|
if (this.serverNode) {
|
|
1250
998
|
await this.stopServerNode(this.serverNode);
|
|
999
|
+
this.serverNode = undefined;
|
|
1251
1000
|
this.log.info(`Stopped matter server node for Matterbridge`);
|
|
1252
1001
|
}
|
|
1253
1002
|
}
|
|
@@ -1255,41 +1004,22 @@ export class Matterbridge extends EventEmitter {
|
|
|
1255
1004
|
for (const plugin of this.plugins.array()) {
|
|
1256
1005
|
if (plugin.serverNode) {
|
|
1257
1006
|
await this.stopServerNode(plugin.serverNode);
|
|
1007
|
+
plugin.serverNode = undefined;
|
|
1258
1008
|
this.log.info(`Stopped matter server node for ${plugin.name}`);
|
|
1259
1009
|
}
|
|
1260
1010
|
}
|
|
1261
1011
|
}
|
|
1262
1012
|
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
1013
|
await this.stopMatterStorage();
|
|
1268
|
-
// Remove the matterfilelogger
|
|
1269
1014
|
try {
|
|
1270
1015
|
Logger.removeLogger('matterfilelogger');
|
|
1271
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1272
1016
|
}
|
|
1273
1017
|
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
1018
|
}
|
|
1276
|
-
// Serialize registeredDevices
|
|
1277
1019
|
if (this.nodeStorage && this.nodeContext) {
|
|
1278
|
-
this.log.info('Saving registered devices...');
|
|
1279
|
-
const serializedRegisteredDevices = [];
|
|
1280
|
-
this.devices.forEach(async (device) => {
|
|
1281
|
-
const serializedMatterbridgeDevice = device.serialize();
|
|
1282
|
-
// this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
|
|
1283
|
-
if (serializedMatterbridgeDevice)
|
|
1284
|
-
serializedRegisteredDevices.push(serializedMatterbridgeDevice);
|
|
1285
|
-
});
|
|
1286
|
-
await this.nodeContext.set('devices', serializedRegisteredDevices);
|
|
1287
|
-
this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
|
|
1288
|
-
// Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
|
|
1289
1020
|
this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
|
|
1290
1021
|
await this.nodeContext.close();
|
|
1291
1022
|
this.nodeContext = undefined;
|
|
1292
|
-
// Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
|
|
1293
1023
|
for (const plugin of this.plugins) {
|
|
1294
1024
|
if (plugin.nodeContext) {
|
|
1295
1025
|
this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
|
|
@@ -1306,13 +1036,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1306
1036
|
}
|
|
1307
1037
|
this.plugins.clear();
|
|
1308
1038
|
this.devices.clear();
|
|
1309
|
-
// Deregisters the process handlers
|
|
1310
1039
|
this.deregisterProcesslHandlers();
|
|
1311
1040
|
if (restart) {
|
|
1312
1041
|
if (message === 'updating...') {
|
|
1313
1042
|
this.log.info('Cleanup completed. Updating...');
|
|
1314
1043
|
Matterbridge.instance = undefined;
|
|
1315
|
-
this.emit('update');
|
|
1044
|
+
this.emit('update');
|
|
1316
1045
|
}
|
|
1317
1046
|
else if (message === 'restarting...') {
|
|
1318
1047
|
this.log.info('Cleanup completed. Restarting...');
|
|
@@ -1329,14 +1058,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1329
1058
|
this.initialized = false;
|
|
1330
1059
|
}
|
|
1331
1060
|
}
|
|
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
1061
|
async createAccessoryPlugin(plugin, device, start = false) {
|
|
1341
1062
|
if (!plugin.locked && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
|
|
1342
1063
|
plugin.locked = true;
|
|
@@ -1348,13 +1069,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1348
1069
|
await this.startServerNode(plugin.serverNode);
|
|
1349
1070
|
}
|
|
1350
1071
|
}
|
|
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
1072
|
async createDynamicPlugin(plugin, start = false) {
|
|
1359
1073
|
if (!plugin.locked) {
|
|
1360
1074
|
plugin.locked = true;
|
|
@@ -1366,13 +1080,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1366
1080
|
await this.startServerNode(plugin.serverNode);
|
|
1367
1081
|
}
|
|
1368
1082
|
}
|
|
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
1083
|
async startBridge() {
|
|
1375
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1376
1084
|
if (!this.matterStorageManager)
|
|
1377
1085
|
throw new Error('No storage manager initialized');
|
|
1378
1086
|
if (!this.matterbridgeContext)
|
|
@@ -1409,15 +1117,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1409
1117
|
clearInterval(this.startMatterInterval);
|
|
1410
1118
|
this.startMatterInterval = undefined;
|
|
1411
1119
|
this.log.debug('Cleared startMatterInterval interval for Matterbridge');
|
|
1412
|
-
// Start the Matter server node
|
|
1413
1120
|
this.startServerNode(this.serverNode);
|
|
1414
|
-
// Configure the plugins
|
|
1415
1121
|
this.configureTimeout = setTimeout(async () => {
|
|
1416
1122
|
for (const plugin of this.plugins) {
|
|
1417
1123
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
1418
1124
|
continue;
|
|
1419
1125
|
try {
|
|
1420
|
-
await this.plugins.configure(plugin);
|
|
1126
|
+
await this.plugins.configure(plugin);
|
|
1421
1127
|
}
|
|
1422
1128
|
catch (error) {
|
|
1423
1129
|
plugin.error = true;
|
|
@@ -1426,7 +1132,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1426
1132
|
}
|
|
1427
1133
|
this.frontend.wssSendRefreshRequired();
|
|
1428
1134
|
}, 30 * 1000);
|
|
1429
|
-
// Setting reachability to true
|
|
1430
1135
|
this.reachabilityTimeout = setTimeout(() => {
|
|
1431
1136
|
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1432
1137
|
if (this.serverNode)
|
|
@@ -1437,14 +1142,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1437
1142
|
}, 60 * 1000);
|
|
1438
1143
|
}, 1000);
|
|
1439
1144
|
}
|
|
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
1145
|
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
1146
|
if (!this.matterStorageManager)
|
|
1449
1147
|
throw new Error('No storage manager initialized');
|
|
1450
1148
|
for (const plugin of this.plugins) {
|
|
@@ -1491,13 +1189,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1491
1189
|
clearInterval(this.startMatterInterval);
|
|
1492
1190
|
this.startMatterInterval = undefined;
|
|
1493
1191
|
this.log.debug('Cleared startMatterInterval interval in childbridge mode');
|
|
1494
|
-
// Configure the plugins
|
|
1495
1192
|
this.configureTimeout = setTimeout(async () => {
|
|
1496
1193
|
for (const plugin of this.plugins) {
|
|
1497
1194
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
1498
1195
|
continue;
|
|
1499
1196
|
try {
|
|
1500
|
-
await this.plugins.configure(plugin);
|
|
1197
|
+
await this.plugins.configure(plugin);
|
|
1501
1198
|
}
|
|
1502
1199
|
catch (error) {
|
|
1503
1200
|
plugin.error = true;
|
|
@@ -1525,9 +1222,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1525
1222
|
this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
|
|
1526
1223
|
continue;
|
|
1527
1224
|
}
|
|
1528
|
-
// Start the Matter server node
|
|
1529
1225
|
this.startServerNode(plugin.serverNode);
|
|
1530
|
-
// Setting reachability to true
|
|
1531
1226
|
plugin.reachabilityTimeout = setTimeout(() => {
|
|
1532
1227
|
this.log.info(`Setting reachability to true for ${plg}${plugin.name}${db}`);
|
|
1533
1228
|
if (plugin.serverNode)
|
|
@@ -1541,219 +1236,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
1541
1236
|
}
|
|
1542
1237
|
}, 1000);
|
|
1543
1238
|
}
|
|
1544
|
-
/**
|
|
1545
|
-
* Starts the Matterbridge controller.
|
|
1546
|
-
* @private
|
|
1547
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1548
|
-
*/
|
|
1549
1239
|
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
1240
|
}
|
|
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
1241
|
async startMatterStorage() {
|
|
1756
|
-
// Setup Matter storage
|
|
1757
1242
|
this.log.info(`Starting matter node storage...`);
|
|
1758
1243
|
this.matterStorageService = this.environment.get(StorageService);
|
|
1759
1244
|
this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
|
|
@@ -1761,24 +1246,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1761
1246
|
this.log.info('Matter node storage manager "Matterbridge" created');
|
|
1762
1247
|
this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', bridge.code, this.aggregatorVendorId, 'Matterbridge', this.aggregatorProductId, 'Matterbridge aggregator');
|
|
1763
1248
|
this.log.info('Matter node storage started');
|
|
1764
|
-
// Backup matter storage since it is created/opened correctly
|
|
1765
1249
|
await this.backupMatterStorage(path.join(this.matterbridgeDirectory, this.matterStorageName), path.join(this.matterbridgeDirectory, this.matterStorageName + '.backup'));
|
|
1766
1250
|
}
|
|
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
1251
|
async backupMatterStorage(storageName, backupName) {
|
|
1774
1252
|
this.log.info('Creating matter node storage backup...');
|
|
1775
1253
|
await copyDirectory(storageName, backupName);
|
|
1776
1254
|
this.log.info('Created matter node storage backup');
|
|
1777
1255
|
}
|
|
1778
|
-
/**
|
|
1779
|
-
* Stops the matter storage.
|
|
1780
|
-
* @returns {Promise<void>} A promise that resolves when the storage is stopped.
|
|
1781
|
-
*/
|
|
1782
1256
|
async stopMatterStorage() {
|
|
1783
1257
|
this.log.info('Closing matter node storage...');
|
|
1784
1258
|
this.matterStorageManager?.close();
|
|
@@ -1787,24 +1261,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1787
1261
|
this.matterbridgeContext = undefined;
|
|
1788
1262
|
this.log.info('Matter node storage closed');
|
|
1789
1263
|
}
|
|
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
1264
|
async createServerNodeContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber) {
|
|
1809
1265
|
if (!this.matterStorageService)
|
|
1810
1266
|
throw new Error('No storage service initialized');
|
|
@@ -1846,33 +1302,21 @@ export class Matterbridge extends EventEmitter {
|
|
|
1846
1302
|
this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
|
|
1847
1303
|
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
1848
1304
|
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
1305
|
const serverNode = await ServerNode.create({
|
|
1853
|
-
// Required: Give the Node a unique ID which is used to store the state of this node
|
|
1854
1306
|
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
1307
|
network: {
|
|
1858
1308
|
listeningAddressIpv4: this.ipv4address,
|
|
1859
1309
|
listeningAddressIpv6: this.ipv6address,
|
|
1860
1310
|
port,
|
|
1861
1311
|
},
|
|
1862
|
-
// Provide Commissioning relevant settings
|
|
1863
|
-
// Optional for development/testing purposes
|
|
1864
1312
|
commissioning: {
|
|
1865
1313
|
passcode,
|
|
1866
1314
|
discriminator,
|
|
1867
1315
|
},
|
|
1868
|
-
// Provide Node announcement settings
|
|
1869
|
-
// Optional: If Ommitted some development defaults are used
|
|
1870
1316
|
productDescription: {
|
|
1871
1317
|
name: await storageContext.get('deviceName'),
|
|
1872
1318
|
deviceType: DeviceTypeId(await storageContext.get('deviceType')),
|
|
1873
1319
|
},
|
|
1874
|
-
// Provide defaults for the BasicInformation cluster on the Root endpoint
|
|
1875
|
-
// Optional: If Omitted some development defaults are used
|
|
1876
1320
|
basicInformation: {
|
|
1877
1321
|
vendorId: VendorId(await storageContext.get('vendorId')),
|
|
1878
1322
|
vendorName: await storageContext.get('vendorName'),
|
|
@@ -1889,13 +1333,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1889
1333
|
},
|
|
1890
1334
|
});
|
|
1891
1335
|
const sanitizeFabrics = (fabrics, resetSessions = false) => {
|
|
1892
|
-
// New type of fabric information: Record<FabricIndex, ExposedFabricInformation>
|
|
1893
1336
|
const sanitizedFabrics = this.sanitizeFabricInformations(Array.from(Object.values(fabrics)));
|
|
1894
1337
|
this.log.info(`Fabrics: ${debugStringify(sanitizedFabrics)}`);
|
|
1895
1338
|
if (this.bridgeMode === 'bridge') {
|
|
1896
1339
|
this.matterbridgeFabricInformations = sanitizedFabrics;
|
|
1897
1340
|
if (resetSessions)
|
|
1898
|
-
this.matterbridgeSessionInformations =
|
|
1341
|
+
this.matterbridgeSessionInformations = undefined;
|
|
1899
1342
|
this.matterbridgePaired = true;
|
|
1900
1343
|
}
|
|
1901
1344
|
if (this.bridgeMode === 'childbridge') {
|
|
@@ -1903,20 +1346,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
1903
1346
|
if (plugin) {
|
|
1904
1347
|
plugin.fabricInformations = sanitizedFabrics;
|
|
1905
1348
|
if (resetSessions)
|
|
1906
|
-
plugin.sessionInformations =
|
|
1349
|
+
plugin.sessionInformations = undefined;
|
|
1907
1350
|
plugin.paired = true;
|
|
1908
1351
|
}
|
|
1909
1352
|
}
|
|
1910
1353
|
};
|
|
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
1354
|
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
1355
|
serverNode.lifecycle.decommissioned.on(() => this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`));
|
|
1918
|
-
|
|
1919
|
-
serverNode.lifecycle.online.on(() => {
|
|
1356
|
+
serverNode.lifecycle.online.on(async () => {
|
|
1920
1357
|
this.log.notice(`Server node for ${storeId} is online`);
|
|
1921
1358
|
if (!serverNode.lifecycle.isCommissioned) {
|
|
1922
1359
|
this.log.notice(`Server node for ${storeId} is not commissioned. Pair to commission ...`);
|
|
@@ -1924,22 +1361,34 @@ export class Matterbridge extends EventEmitter {
|
|
|
1924
1361
|
if (this.bridgeMode === 'bridge') {
|
|
1925
1362
|
this.matterbridgeQrPairingCode = qrPairingCode;
|
|
1926
1363
|
this.matterbridgeManualPairingCode = manualPairingCode;
|
|
1927
|
-
this.matterbridgeFabricInformations =
|
|
1928
|
-
this.matterbridgeSessionInformations =
|
|
1364
|
+
this.matterbridgeFabricInformations = undefined;
|
|
1365
|
+
this.matterbridgeSessionInformations = undefined;
|
|
1929
1366
|
this.matterbridgePaired = false;
|
|
1930
1367
|
this.log.notice(`QR Code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=${qrPairingCode}`);
|
|
1931
1368
|
this.log.notice(`Manual pairing code: ${manualPairingCode}`);
|
|
1369
|
+
if (this.matterStorageService) {
|
|
1370
|
+
const storageManager = await this.matterStorageService.open(storeId);
|
|
1371
|
+
const storageContext = storageManager.createContext('persist');
|
|
1372
|
+
await storageContext.set('qrPairingCode', qrPairingCode);
|
|
1373
|
+
await storageContext.set('manualPairingCode', manualPairingCode);
|
|
1374
|
+
}
|
|
1932
1375
|
}
|
|
1933
1376
|
if (this.bridgeMode === 'childbridge') {
|
|
1934
1377
|
const plugin = this.plugins.get(storeId);
|
|
1935
1378
|
if (plugin) {
|
|
1936
1379
|
plugin.qrPairingCode = qrPairingCode;
|
|
1937
1380
|
plugin.manualPairingCode = manualPairingCode;
|
|
1938
|
-
plugin.fabricInformations =
|
|
1939
|
-
plugin.sessionInformations =
|
|
1381
|
+
plugin.fabricInformations = undefined;
|
|
1382
|
+
plugin.sessionInformations = undefined;
|
|
1940
1383
|
plugin.paired = false;
|
|
1941
1384
|
this.log.notice(`QR Code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=${qrPairingCode}`);
|
|
1942
1385
|
this.log.notice(`Manual pairing code: ${manualPairingCode}`);
|
|
1386
|
+
if (this.matterStorageService) {
|
|
1387
|
+
const storageManager = await this.matterStorageService.open(storeId);
|
|
1388
|
+
const storageContext = storageManager.createContext('persist');
|
|
1389
|
+
await storageContext.set('qrPairingCode', qrPairingCode);
|
|
1390
|
+
await storageContext.set('manualPairingCode', manualPairingCode);
|
|
1391
|
+
}
|
|
1943
1392
|
}
|
|
1944
1393
|
}
|
|
1945
1394
|
}
|
|
@@ -1949,32 +1398,27 @@ export class Matterbridge extends EventEmitter {
|
|
|
1949
1398
|
}
|
|
1950
1399
|
this.frontend.wssSendRefreshRequired();
|
|
1951
1400
|
});
|
|
1952
|
-
/** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
|
|
1953
1401
|
serverNode.lifecycle.offline.on(() => {
|
|
1954
1402
|
this.log.notice(`Server node for ${storeId} is offline`);
|
|
1955
1403
|
if (this.bridgeMode === 'bridge') {
|
|
1956
1404
|
this.matterbridgeQrPairingCode = undefined;
|
|
1957
1405
|
this.matterbridgeManualPairingCode = undefined;
|
|
1958
|
-
this.matterbridgeFabricInformations =
|
|
1959
|
-
this.matterbridgeSessionInformations =
|
|
1960
|
-
this.matterbridgePaired =
|
|
1406
|
+
this.matterbridgeFabricInformations = undefined;
|
|
1407
|
+
this.matterbridgeSessionInformations = undefined;
|
|
1408
|
+
this.matterbridgePaired = undefined;
|
|
1961
1409
|
}
|
|
1962
1410
|
if (this.bridgeMode === 'childbridge') {
|
|
1963
1411
|
const plugin = this.plugins.get(storeId);
|
|
1964
1412
|
if (plugin) {
|
|
1965
1413
|
plugin.qrPairingCode = undefined;
|
|
1966
1414
|
plugin.manualPairingCode = undefined;
|
|
1967
|
-
plugin.fabricInformations =
|
|
1968
|
-
plugin.sessionInformations =
|
|
1969
|
-
plugin.paired =
|
|
1415
|
+
plugin.fabricInformations = undefined;
|
|
1416
|
+
plugin.sessionInformations = undefined;
|
|
1417
|
+
plugin.paired = undefined;
|
|
1970
1418
|
}
|
|
1971
1419
|
}
|
|
1972
1420
|
this.frontend.wssSendRefreshRequired();
|
|
1973
1421
|
});
|
|
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
1422
|
serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
|
|
1979
1423
|
let action = '';
|
|
1980
1424
|
switch (fabricAction) {
|
|
@@ -2008,24 +1452,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
2008
1452
|
}
|
|
2009
1453
|
}
|
|
2010
1454
|
};
|
|
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
1455
|
serverNode.events.sessions.opened.on((session) => {
|
|
2016
1456
|
this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2017
1457
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
2018
1458
|
this.frontend.wssSendRefreshRequired();
|
|
2019
1459
|
});
|
|
2020
|
-
/**
|
|
2021
|
-
* This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
|
|
2022
|
-
*/
|
|
2023
1460
|
serverNode.events.sessions.closed.on((session) => {
|
|
2024
1461
|
this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2025
1462
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
2026
1463
|
this.frontend.wssSendRefreshRequired();
|
|
2027
1464
|
});
|
|
2028
|
-
/** This event is triggered when a subscription gets added or removed on an operative session. */
|
|
2029
1465
|
serverNode.events.sessions.subscriptionsChanged.on((session) => {
|
|
2030
1466
|
this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2031
1467
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
@@ -2045,6 +1481,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
2045
1481
|
return;
|
|
2046
1482
|
this.log.notice(`Closing ${matterServerNode.id} server node`);
|
|
2047
1483
|
await matterServerNode.close();
|
|
1484
|
+
await matterServerNode.env.get(MdnsService)[Symbol.asyncDispose]();
|
|
1485
|
+
}
|
|
1486
|
+
async advertiseServerNode(matterServerNode) {
|
|
1487
|
+
if (matterServerNode && matterServerNode.lifecycle.isCommissioned) {
|
|
1488
|
+
await matterServerNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
|
|
1489
|
+
const { qrPairingCode, manualPairingCode } = matterServerNode.state.commissioning.pairingCodes;
|
|
1490
|
+
this.log.notice(`Advertising for ${matterServerNode.id} is now started with the following pairing codes: qrPairingCode ${qrPairingCode}, manualPairingCode ${manualPairingCode}`);
|
|
1491
|
+
return { qrPairingCode, manualPairingCode };
|
|
1492
|
+
}
|
|
1493
|
+
return undefined;
|
|
2048
1494
|
}
|
|
2049
1495
|
async createAggregatorNode(storageContext) {
|
|
2050
1496
|
this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator `);
|
|
@@ -2052,13 +1498,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
2052
1498
|
return aggregatorNode;
|
|
2053
1499
|
}
|
|
2054
1500
|
async addBridgedEndpoint(pluginName, device) {
|
|
2055
|
-
// Check if the plugin is registered
|
|
2056
1501
|
const plugin = this.plugins.get(pluginName);
|
|
2057
1502
|
if (!plugin) {
|
|
2058
1503
|
this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
|
|
2059
1504
|
return;
|
|
2060
1505
|
}
|
|
2061
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2062
1506
|
if (this.bridgeMode === 'bridge') {
|
|
2063
1507
|
this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
|
|
2064
1508
|
if (!this.aggregatorNode)
|
|
@@ -2081,19 +1525,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
2081
1525
|
plugin.registeredDevices++;
|
|
2082
1526
|
if (plugin.addedDevices !== undefined)
|
|
2083
1527
|
plugin.addedDevices++;
|
|
2084
|
-
// Add the device to the DeviceManager
|
|
2085
1528
|
this.devices.set(device);
|
|
2086
1529
|
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
1530
|
}
|
|
2088
1531
|
async removeBridgedEndpoint(pluginName, device) {
|
|
2089
1532
|
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
1533
|
const plugin = this.plugins.get(pluginName);
|
|
2092
1534
|
if (!plugin) {
|
|
2093
1535
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
2094
1536
|
return;
|
|
2095
1537
|
}
|
|
2096
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2097
1538
|
if (this.bridgeMode === 'bridge') {
|
|
2098
1539
|
if (!this.aggregatorNode) {
|
|
2099
1540
|
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 +1549,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2108
1549
|
}
|
|
2109
1550
|
else if (this.bridgeMode === 'childbridge') {
|
|
2110
1551
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2111
|
-
// Nothing to do here since the server node has no aggregator node but only the device itself
|
|
2112
1552
|
}
|
|
2113
1553
|
else if (plugin.type === 'DynamicPlatform') {
|
|
2114
1554
|
if (!plugin.aggregatorNode) {
|
|
@@ -2122,7 +1562,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2122
1562
|
plugin.registeredDevices--;
|
|
2123
1563
|
if (plugin.addedDevices !== undefined)
|
|
2124
1564
|
plugin.addedDevices--;
|
|
2125
|
-
// Close the server node TODO check if this is correct
|
|
2126
1565
|
if (plugin.registeredDevices === 0 && plugin.addedDevices === 0) {
|
|
2127
1566
|
if (plugin.serverNode) {
|
|
2128
1567
|
await this.stopServerNode(plugin.serverNode);
|
|
@@ -2133,7 +1572,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2133
1572
|
}
|
|
2134
1573
|
}
|
|
2135
1574
|
}
|
|
2136
|
-
// Remove the device from the DeviceManager
|
|
2137
1575
|
this.devices.remove(device);
|
|
2138
1576
|
}
|
|
2139
1577
|
async removeAllBridgedEndpoints(pluginName) {
|
|
@@ -2142,12 +1580,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2142
1580
|
await this.removeBridgedEndpoint(pluginName, device);
|
|
2143
1581
|
}
|
|
2144
1582
|
}
|
|
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
1583
|
sanitizeFabricInformations(fabricInfo) {
|
|
2152
1584
|
return fabricInfo.map((info) => {
|
|
2153
1585
|
return {
|
|
@@ -2161,12 +1593,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2161
1593
|
};
|
|
2162
1594
|
});
|
|
2163
1595
|
}
|
|
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
1596
|
sanitizeSessionInformation(sessionInfo) {
|
|
2171
1597
|
return sessionInfo
|
|
2172
1598
|
.filter((session) => session.isPeerActive)
|
|
@@ -2194,51 +1620,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
2194
1620
|
};
|
|
2195
1621
|
});
|
|
2196
1622
|
}
|
|
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
1623
|
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
1624
|
}
|
|
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
1625
|
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
1626
|
}
|
|
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
1627
|
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
1628
|
}
|
|
2243
1629
|
getVendorIdName = (vendorId) => {
|
|
2244
1630
|
if (!vendorId)
|
|
@@ -2281,36 +1667,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2281
1667
|
}
|
|
2282
1668
|
return vendorName;
|
|
2283
1669
|
};
|
|
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
1670
|
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
1671
|
const cmdLine = command + ' ' + args.join(' ');
|
|
2305
1672
|
if (process.platform === 'win32' && command === 'npm') {
|
|
2306
|
-
// Must be spawn('cmd.exe', ['/c', 'npm -g install <package>']);
|
|
2307
1673
|
const argstring = 'npm ' + args.join(' ');
|
|
2308
1674
|
args.splice(0, args.length, '/c', argstring);
|
|
2309
1675
|
command = 'cmd.exe';
|
|
2310
1676
|
}
|
|
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
1677
|
if (hasParameter('sudo') || (process.platform === 'linux' && command === 'npm' && !hasParameter('docker') && !hasParameter('nosudo'))) {
|
|
2315
1678
|
args.unshift(command);
|
|
2316
1679
|
command = 'sudo';
|
|
@@ -2369,4 +1732,3 @@ export class Matterbridge extends EventEmitter {
|
|
|
2369
1732
|
});
|
|
2370
1733
|
}
|
|
2371
1734
|
}
|
|
2372
|
-
//# sourceMappingURL=matterbridge.js.map
|