matterbridge 2.1.3 → 2.1.4-dev.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -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 -94
- package/dist/frontend.js +34 -233
- package/dist/index.js +0 -28
- package/dist/logger/export.js +0 -1
- package/dist/matter/behaviors.js +0 -2
- package/dist/matter/clusters.js +0 -2
- package/dist/matter/devices.js +0 -2
- package/dist/matter/endpoints.js +0 -2
- package/dist/matter/export.js +0 -2
- package/dist/matter/types.js +0 -2
- package/dist/matterbridge.js +60 -750
- package/dist/matterbridgeAccessoryPlatform.js +0 -33
- package/dist/matterbridgeBehaviors.js +1 -32
- package/dist/matterbridgeDeviceTypes.js +11 -112
- package/dist/matterbridgeDynamicPlatform.js +0 -33
- package/dist/matterbridgeEndpoint.js +10 -691
- package/dist/matterbridgeEndpointHelpers.js +16 -96
- package/dist/matterbridgePlatform.js +30 -112
- package/dist/matterbridgeTypes.js +0 -24
- package/dist/pluginManager.js +3 -230
- package/dist/storage/export.js +0 -1
- package/dist/utils/colorUtils.js +2 -205
- package/dist/utils/export.js +0 -1
- package/dist/utils/utils.js +7 -251
- package/npm-shrinkwrap.json +47 -47
- package/package.json +2 -3
- package/dist/cli.d.ts +0 -25
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cluster/export.d.ts +0 -2
- package/dist/cluster/export.d.ts.map +0 -1
- package/dist/cluster/export.js.map +0 -1
- package/dist/defaultConfigSchema.d.ts +0 -27
- package/dist/defaultConfigSchema.d.ts.map +0 -1
- package/dist/defaultConfigSchema.js.map +0 -1
- package/dist/deviceManager.d.ts +0 -114
- package/dist/deviceManager.d.ts.map +0 -1
- package/dist/deviceManager.js.map +0 -1
- package/dist/frontend.d.ts +0 -110
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/index.d.ts +0 -35
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/logger/export.d.ts +0 -2
- package/dist/logger/export.d.ts.map +0 -1
- package/dist/logger/export.js.map +0 -1
- package/dist/matter/behaviors.d.ts +0 -2
- package/dist/matter/behaviors.d.ts.map +0 -1
- package/dist/matter/behaviors.js.map +0 -1
- package/dist/matter/clusters.d.ts +0 -2
- package/dist/matter/clusters.d.ts.map +0 -1
- package/dist/matter/clusters.js.map +0 -1
- package/dist/matter/devices.d.ts +0 -2
- package/dist/matter/devices.d.ts.map +0 -1
- package/dist/matter/devices.js.map +0 -1
- package/dist/matter/endpoints.d.ts +0 -2
- package/dist/matter/endpoints.d.ts.map +0 -1
- package/dist/matter/endpoints.js.map +0 -1
- package/dist/matter/export.d.ts +0 -5
- package/dist/matter/export.d.ts.map +0 -1
- package/dist/matter/export.js.map +0 -1
- package/dist/matter/types.d.ts +0 -3
- package/dist/matter/types.d.ts.map +0 -1
- package/dist/matter/types.js.map +0 -1
- package/dist/matterbridge.d.ts +0 -409
- package/dist/matterbridge.d.ts.map +0 -1
- package/dist/matterbridge.js.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.d.ts +0 -39
- package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
- package/dist/matterbridgeBehaviors.d.ts +0 -1056
- package/dist/matterbridgeBehaviors.d.ts.map +0 -1
- package/dist/matterbridgeBehaviors.js.map +0 -1
- package/dist/matterbridgeDeviceTypes.d.ts +0 -177
- package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
- package/dist/matterbridgeDeviceTypes.js.map +0 -1
- package/dist/matterbridgeDynamicPlatform.d.ts +0 -39
- package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
- package/dist/matterbridgeDynamicPlatform.js.map +0 -1
- package/dist/matterbridgeEndpoint.d.ts +0 -834
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -2262
- package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
- package/dist/matterbridgeEndpointHelpers.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts +0 -152
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts +0 -167
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/pluginManager.d.ts +0 -236
- package/dist/pluginManager.d.ts.map +0 -1
- package/dist/pluginManager.js.map +0 -1
- package/dist/storage/export.d.ts +0 -2
- package/dist/storage/export.d.ts.map +0 -1
- package/dist/storage/export.js.map +0 -1
- package/dist/utils/colorUtils.d.ts +0 -61
- package/dist/utils/colorUtils.d.ts.map +0 -1
- package/dist/utils/colorUtils.js.map +0 -1
- package/dist/utils/export.d.ts +0 -3
- package/dist/utils/export.d.ts.map +0 -1
- package/dist/utils/export.js.map +0 -1
- package/dist/utils/utils.d.ts +0 -221
- package/dist/utils/utils.d.ts.map +0 -1
- package/dist/utils/utils.js.map +0 -1
package/dist/matterbridge.js
CHANGED
|
@@ -1,26 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This file contains the class Matterbridge.
|
|
3
|
-
*
|
|
4
|
-
* @file matterbridge.ts
|
|
5
|
-
* @author Luca Liguori
|
|
6
|
-
* @date 2023-12-29
|
|
7
|
-
* @version 1.5.2
|
|
8
|
-
*
|
|
9
|
-
* Copyright 2023, 2024, 2025 Luca Liguori.
|
|
10
|
-
*
|
|
11
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
12
|
-
* you may not use this file except in compliance with the License.
|
|
13
|
-
* You may obtain a copy of the License at
|
|
14
|
-
*
|
|
15
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
16
|
-
*
|
|
17
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
18
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
19
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
20
|
-
* See the License for the specific language governing permissions and
|
|
21
|
-
* limitations under the License. *
|
|
22
|
-
*/
|
|
23
|
-
// Node.js modules
|
|
24
1
|
import { fileURLToPath } from 'url';
|
|
25
2
|
import { promises as fs } from 'fs';
|
|
26
3
|
import { exec, spawn } from 'child_process';
|
|
@@ -28,27 +5,20 @@ import EventEmitter from 'events';
|
|
|
28
5
|
import os from 'os';
|
|
29
6
|
import path from 'path';
|
|
30
7
|
import { randomBytes } from 'crypto';
|
|
31
|
-
// NodeStorage and AnsiLogger modules
|
|
32
8
|
import { NodeStorageManager } from './storage/export.js';
|
|
33
9
|
import { AnsiLogger, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN, nt } from './logger/export.js';
|
|
34
|
-
// Matterbridge
|
|
35
10
|
import { logInterfaces, copyDirectory, getParameter, getIntParameter, hasParameter } from './utils/utils.js';
|
|
36
11
|
import { PluginManager } from './pluginManager.js';
|
|
37
12
|
import { DeviceManager } from './deviceManager.js';
|
|
38
13
|
import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
|
|
39
14
|
import { bridge } from './matterbridgeDeviceTypes.js';
|
|
40
15
|
import { Frontend } from './frontend.js';
|
|
41
|
-
// @matter
|
|
42
16
|
import { DeviceTypeId, Endpoint as EndpointNode, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, VendorId, StorageService, Environment, ServerNode } from '@matter/main';
|
|
43
|
-
import { DeviceCommissioner, 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: '',
|
|
@@ -85,7 +55,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
85
55
|
restartMode: '',
|
|
86
56
|
readOnly: hasParameter('readonly'),
|
|
87
57
|
profile: getParameter('profile'),
|
|
88
|
-
loggerLevel: "info"
|
|
58
|
+
loggerLevel: "info",
|
|
89
59
|
fileLogger: false,
|
|
90
60
|
matterLoggerLevel: MatterLogLevel.INFO,
|
|
91
61
|
matterFileLogger: false,
|
|
@@ -120,11 +90,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
120
90
|
plugins;
|
|
121
91
|
devices;
|
|
122
92
|
frontend = new Frontend(this);
|
|
123
|
-
// Matterbridge storage
|
|
124
93
|
nodeStorage;
|
|
125
94
|
nodeContext;
|
|
126
95
|
nodeStorageName = 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
|
|
127
|
-
// Cleanup
|
|
128
96
|
hasCleanupStarted = false;
|
|
129
97
|
initialized = false;
|
|
130
98
|
execRunningCount = 0;
|
|
@@ -136,57 +104,34 @@ export class Matterbridge extends EventEmitter {
|
|
|
136
104
|
sigtermHandler;
|
|
137
105
|
exceptionHandler;
|
|
138
106
|
rejectionHandler;
|
|
139
|
-
// Matter environment
|
|
140
107
|
environment = Environment.default;
|
|
141
|
-
// Matter storage
|
|
142
108
|
matterStorageName = 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
|
|
143
109
|
matterStorageService;
|
|
144
110
|
matterStorageManager;
|
|
145
111
|
matterbridgeContext;
|
|
146
112
|
mattercontrollerContext;
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
discriminator; // first server node discriminator
|
|
113
|
+
mdnsInterface;
|
|
114
|
+
ipv4address;
|
|
115
|
+
ipv6address;
|
|
116
|
+
port;
|
|
117
|
+
passcode;
|
|
118
|
+
discriminator;
|
|
154
119
|
serverNode;
|
|
155
120
|
aggregatorNode;
|
|
156
121
|
aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
|
|
157
122
|
aggregatorProductId = getIntParameter('productId') ?? 0x8000;
|
|
158
123
|
static instance;
|
|
159
|
-
// We load asyncronously so is private
|
|
160
124
|
constructor() {
|
|
161
125
|
super();
|
|
162
126
|
}
|
|
163
|
-
/**
|
|
164
|
-
* Retrieves the list of Matterbridge devices.
|
|
165
|
-
* @returns {MatterbridgeEndpoint[]} An array of MatterbridgeDevice objects.
|
|
166
|
-
*/
|
|
167
127
|
getDevices() {
|
|
168
128
|
return this.devices.array();
|
|
169
129
|
}
|
|
170
|
-
/**
|
|
171
|
-
* Retrieves the list of registered plugins.
|
|
172
|
-
* @returns {RegisteredPlugin[]} An array of RegisteredPlugin objects.
|
|
173
|
-
*/
|
|
174
130
|
getPlugins() {
|
|
175
131
|
return this.plugins.array();
|
|
176
132
|
}
|
|
177
|
-
/** ***********************************************************************************************************************************/
|
|
178
|
-
/** loadInstance() and cleanup() methods */
|
|
179
|
-
/** ***********************************************************************************************************************************/
|
|
180
|
-
/**
|
|
181
|
-
* Loads an instance of the Matterbridge class.
|
|
182
|
-
* If an instance already exists, return that instance.
|
|
183
|
-
*
|
|
184
|
-
* @param initialize - Whether to initialize the Matterbridge instance after loading.
|
|
185
|
-
* @returns The loaded Matterbridge instance.
|
|
186
|
-
*/
|
|
187
133
|
static async loadInstance(initialize = false) {
|
|
188
134
|
if (!Matterbridge.instance) {
|
|
189
|
-
// eslint-disable-next-line no-console
|
|
190
135
|
if (hasParameter('debug'))
|
|
191
136
|
console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
|
|
192
137
|
Matterbridge.instance = new Matterbridge();
|
|
@@ -195,81 +140,66 @@ export class Matterbridge extends EventEmitter {
|
|
|
195
140
|
}
|
|
196
141
|
return Matterbridge.instance;
|
|
197
142
|
}
|
|
198
|
-
/**
|
|
199
|
-
* Call cleanup().
|
|
200
|
-
* @deprecated This method is deprecated and is only used for jest tests.
|
|
201
|
-
*
|
|
202
|
-
*/
|
|
203
143
|
async destroyInstance() {
|
|
144
|
+
const servers = [];
|
|
145
|
+
if (this.bridgeMode === 'bridge') {
|
|
146
|
+
if (this.serverNode)
|
|
147
|
+
servers.push(this.serverNode);
|
|
148
|
+
}
|
|
149
|
+
if (this.bridgeMode === 'childbridge') {
|
|
150
|
+
for (const plugin of this.plugins.array()) {
|
|
151
|
+
if (plugin.serverNode)
|
|
152
|
+
servers.push(plugin.serverNode);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
204
155
|
await this.cleanup('destroying instance...', false);
|
|
205
|
-
|
|
206
|
-
|
|
156
|
+
for (const server of servers) {
|
|
157
|
+
await server.env.get(MdnsService)[Symbol.asyncDispose]();
|
|
158
|
+
this.log.info(`Closed ${server.id} MdnsService`);
|
|
159
|
+
}
|
|
207
160
|
await new Promise((resolve) => {
|
|
208
161
|
setTimeout(resolve, 1000);
|
|
209
162
|
});
|
|
210
163
|
}
|
|
211
|
-
/**
|
|
212
|
-
* Initializes the Matterbridge application.
|
|
213
|
-
*
|
|
214
|
-
* @remarks
|
|
215
|
-
* This method performs the necessary setup and initialization steps for the Matterbridge application.
|
|
216
|
-
* It displays the help information if the 'help' parameter is provided, sets up the logger, checks the
|
|
217
|
-
* node version, registers signal handlers, initializes storage, and parses the command line.
|
|
218
|
-
*
|
|
219
|
-
* @returns A Promise that resolves when the initialization is complete.
|
|
220
|
-
*/
|
|
221
164
|
async initialize() {
|
|
222
|
-
// Set the restart mode
|
|
223
165
|
if (hasParameter('service'))
|
|
224
166
|
this.restartMode = 'service';
|
|
225
167
|
if (hasParameter('docker'))
|
|
226
168
|
this.restartMode = 'docker';
|
|
227
|
-
// Set the matterbridge directory
|
|
228
169
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
229
170
|
this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
|
|
230
|
-
// Setup the matter environment
|
|
231
171
|
this.environment.vars.set('log.level', MatterLogLevel.INFO);
|
|
232
172
|
this.environment.vars.set('log.format', MatterLogFormat.ANSI);
|
|
233
173
|
this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, this.matterStorageName));
|
|
234
174
|
this.environment.vars.set('runtime.signals', false);
|
|
235
175
|
this.environment.vars.set('runtime.exitcode', false);
|
|
236
|
-
|
|
237
|
-
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
|
|
238
|
-
// Register process handlers
|
|
176
|
+
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
239
177
|
this.registerProcessHandlers();
|
|
240
|
-
// Initialize nodeStorage and nodeContext
|
|
241
178
|
try {
|
|
242
179
|
this.log.debug(`Creating node storage manager: ${CYAN}${this.nodeStorageName}${db}`);
|
|
243
180
|
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, this.nodeStorageName), writeQueue: false, expiredInterval: undefined, logging: false });
|
|
244
181
|
this.log.debug('Creating node storage context for matterbridge');
|
|
245
182
|
this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
|
|
246
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
247
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
248
183
|
const keys = (await this.nodeStorage?.storage.keys());
|
|
249
184
|
for (const key of keys) {
|
|
250
185
|
this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
|
|
251
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
252
186
|
await this.nodeStorage?.storage.get(key);
|
|
253
187
|
}
|
|
254
188
|
const storages = await this.nodeStorage.getStorageNames();
|
|
255
189
|
for (const storage of storages) {
|
|
256
190
|
this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
|
|
257
191
|
const nodeContext = await this.nodeStorage?.createStorage(storage);
|
|
258
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
259
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
260
192
|
const keys = (await nodeContext?.storage.keys());
|
|
261
193
|
keys.forEach(async (key) => {
|
|
262
194
|
this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
|
|
263
195
|
await nodeContext?.get(key);
|
|
264
196
|
});
|
|
265
197
|
}
|
|
266
|
-
// Creating a backup of the node storage since it is not corrupted
|
|
267
198
|
this.log.debug('Creating node storage backup...');
|
|
268
199
|
await copyDirectory(path.join(this.matterbridgeDirectory, this.nodeStorageName), path.join(this.matterbridgeDirectory, this.nodeStorageName + '.backup'));
|
|
269
200
|
this.log.debug('Created node storage backup');
|
|
270
201
|
}
|
|
271
202
|
catch (error) {
|
|
272
|
-
// Restoring the backup of the node storage since it is corrupted
|
|
273
203
|
this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
|
|
274
204
|
if (hasParameter('norestore')) {
|
|
275
205
|
this.log.fatal(`The matterbridge node storage is corrupted. Parameter -norestore found: exiting...`);
|
|
@@ -284,51 +214,45 @@ export class Matterbridge extends EventEmitter {
|
|
|
284
214
|
this.log.fatal('Fatal error creating node storage manager and context for matterbridge');
|
|
285
215
|
throw new Error('Fatal error creating node storage manager and context for matterbridge');
|
|
286
216
|
}
|
|
287
|
-
// Set the first port to use for the commissioning server (will be incremented in childbridge mode)
|
|
288
217
|
this.port = getIntParameter('port') ?? (await this.nodeContext.get('matterport', 5540)) ?? 5540;
|
|
289
|
-
// Set the first passcode to use for the commissioning server (will be incremented in childbridge mode)
|
|
290
218
|
this.passcode = getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode')) ?? PaseClient.generateRandomPasscode();
|
|
291
|
-
// Set the first discriminator to use for the commissioning server (will be incremented in childbridge mode)
|
|
292
219
|
this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator();
|
|
293
220
|
this.log.debug(`Initializing commissioning server for Matterbridge... on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
|
|
294
|
-
// Set matterbridge logger level (context: matterbridgeLogLevel)
|
|
295
221
|
if (hasParameter('logger')) {
|
|
296
222
|
const level = getParameter('logger');
|
|
297
223
|
if (level === 'debug') {
|
|
298
|
-
this.log.logLevel = "debug"
|
|
224
|
+
this.log.logLevel = "debug";
|
|
299
225
|
}
|
|
300
226
|
else if (level === 'info') {
|
|
301
|
-
this.log.logLevel = "info"
|
|
227
|
+
this.log.logLevel = "info";
|
|
302
228
|
}
|
|
303
229
|
else if (level === 'notice') {
|
|
304
|
-
this.log.logLevel = "notice"
|
|
230
|
+
this.log.logLevel = "notice";
|
|
305
231
|
}
|
|
306
232
|
else if (level === 'warn') {
|
|
307
|
-
this.log.logLevel = "warn"
|
|
233
|
+
this.log.logLevel = "warn";
|
|
308
234
|
}
|
|
309
235
|
else if (level === 'error') {
|
|
310
|
-
this.log.logLevel = "error"
|
|
236
|
+
this.log.logLevel = "error";
|
|
311
237
|
}
|
|
312
238
|
else if (level === 'fatal') {
|
|
313
|
-
this.log.logLevel = "fatal"
|
|
239
|
+
this.log.logLevel = "fatal";
|
|
314
240
|
}
|
|
315
241
|
else {
|
|
316
242
|
this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
|
|
317
|
-
this.log.logLevel = "info"
|
|
243
|
+
this.log.logLevel = "info";
|
|
318
244
|
}
|
|
319
245
|
}
|
|
320
246
|
else {
|
|
321
|
-
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', "info"
|
|
247
|
+
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', "info");
|
|
322
248
|
}
|
|
323
249
|
MatterbridgeEndpoint.logLevel = this.log.logLevel;
|
|
324
|
-
// Create the file logger for matterbridge (context: matterbridgeFileLog)
|
|
325
250
|
if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
|
|
326
251
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), this.log.logLevel, true);
|
|
327
252
|
this.matterbridgeInformation.fileLogger = true;
|
|
328
253
|
}
|
|
329
254
|
this.log.notice('Matterbridge is starting...');
|
|
330
255
|
this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.matterbridgeInformation.fileLogger}.`);
|
|
331
|
-
// Set matter.js logger level, format and logger (context: matterLogLevel)
|
|
332
256
|
if (hasParameter('matterlogger')) {
|
|
333
257
|
const level = getParameter('matterlogger');
|
|
334
258
|
if (level === 'debug') {
|
|
@@ -359,7 +283,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
359
283
|
}
|
|
360
284
|
Logger.format = MatterLogFormat.ANSI;
|
|
361
285
|
Logger.setLogger('default', this.createMatterLogger());
|
|
362
|
-
// Create the file logger for matter.js (context: matterFileLog)
|
|
363
286
|
if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
|
|
364
287
|
this.matterbridgeInformation.matterFileLogger = true;
|
|
365
288
|
Logger.addLogger('matterfilelogger', await this.createMatterFileLogger(path.join(this.matterbridgeDirectory, this.matterLoggerFile), true), {
|
|
@@ -368,7 +291,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
368
291
|
});
|
|
369
292
|
}
|
|
370
293
|
this.log.debug(`Matter logLevel: ${Logger.defaultLogLevel} fileLoger: ${this.matterbridgeInformation.matterFileLogger}.`);
|
|
371
|
-
// Set the interface to use for matter server node mdnsInterface
|
|
372
294
|
if (hasParameter('mdnsinterface')) {
|
|
373
295
|
this.mdnsInterface = getParameter('mdnsinterface');
|
|
374
296
|
}
|
|
@@ -377,7 +299,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
377
299
|
if (this.mdnsInterface === '')
|
|
378
300
|
this.mdnsInterface = undefined;
|
|
379
301
|
}
|
|
380
|
-
// Validate mdnsInterface
|
|
381
302
|
if (this.mdnsInterface) {
|
|
382
303
|
const networkInterfaces = os.networkInterfaces();
|
|
383
304
|
const availableInterfaces = Object.keys(networkInterfaces);
|
|
@@ -391,7 +312,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
391
312
|
}
|
|
392
313
|
if (this.mdnsInterface)
|
|
393
314
|
this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
|
|
394
|
-
// Set the listeningAddressIpv4 for the matter commissioning server
|
|
395
315
|
if (hasParameter('ipv4address')) {
|
|
396
316
|
this.ipv4address = getParameter('ipv4address');
|
|
397
317
|
}
|
|
@@ -400,7 +320,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
400
320
|
if (this.ipv4address === '')
|
|
401
321
|
this.ipv4address = undefined;
|
|
402
322
|
}
|
|
403
|
-
// Set the listeningAddressIpv6 for the matter commissioning server
|
|
404
323
|
if (hasParameter('ipv6address')) {
|
|
405
324
|
this.ipv6address = getParameter('ipv6address');
|
|
406
325
|
}
|
|
@@ -409,19 +328,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
409
328
|
if (this.ipv6address === '')
|
|
410
329
|
this.ipv6address = undefined;
|
|
411
330
|
}
|
|
412
|
-
// Initialize PluginManager
|
|
413
331
|
this.plugins = new PluginManager(this);
|
|
414
332
|
await this.plugins.loadFromStorage();
|
|
415
333
|
this.plugins.logLevel = this.log.logLevel;
|
|
416
|
-
// Initialize DeviceManager
|
|
417
334
|
this.devices = new DeviceManager(this, this.nodeContext);
|
|
418
335
|
this.devices.logLevel = this.log.logLevel;
|
|
419
|
-
// Get the plugins from node storage and create the plugins node storage contexts
|
|
420
336
|
for (const plugin of this.plugins) {
|
|
421
337
|
const packageJson = await this.plugins.parse(plugin);
|
|
422
338
|
if (packageJson === null && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
|
|
423
|
-
// Try to reinstall the plugin from npm (for Docker pull and external plugins)
|
|
424
|
-
// We don't do this when the add and other parameters are set because we shut down the process after adding the plugin
|
|
425
339
|
this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
|
|
426
340
|
try {
|
|
427
341
|
await this.spawnCommand('npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose']);
|
|
@@ -443,7 +357,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
443
357
|
await plugin.nodeContext.set('description', plugin.description);
|
|
444
358
|
await plugin.nodeContext.set('author', plugin.author);
|
|
445
359
|
}
|
|
446
|
-
// Log system info and create .matterbridge directory
|
|
447
360
|
await this.logNodeAndSystemInfo();
|
|
448
361
|
this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
|
|
449
362
|
`${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
|
|
@@ -451,7 +364,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
451
364
|
`${hasParameter('controller') ? 'mode controller ' : ''}` +
|
|
452
365
|
`${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
|
|
453
366
|
`running on ${this.systemInformation.osType} (v.${this.systemInformation.osRelease}) platform ${this.systemInformation.osPlatform} arch ${this.systemInformation.osArch}`);
|
|
454
|
-
// Check node version and throw error
|
|
455
367
|
const minNodeVersion = 18;
|
|
456
368
|
const nodeVersion = process.versions.node;
|
|
457
369
|
const versionMajor = parseInt(nodeVersion.split('.')[0]);
|
|
@@ -459,15 +371,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
459
371
|
this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
460
372
|
throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
461
373
|
}
|
|
462
|
-
// Parse command line
|
|
463
374
|
await this.parseCommandLine();
|
|
464
375
|
this.initialized = true;
|
|
465
376
|
}
|
|
466
|
-
/**
|
|
467
|
-
* Parses the command line arguments and performs the corresponding actions.
|
|
468
|
-
* @private
|
|
469
|
-
* @returns {Promise<void>} A promise that resolves when the command line arguments have been processed, or the process exits.
|
|
470
|
-
*/
|
|
471
377
|
async parseCommandLine() {
|
|
472
378
|
if (hasParameter('help')) {
|
|
473
379
|
this.log.info(`\nUsage: matterbridge [options]\n
|
|
@@ -577,7 +483,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
577
483
|
await this.shutdownProcessAndFactoryReset();
|
|
578
484
|
return;
|
|
579
485
|
}
|
|
580
|
-
// Start the matter storage and create the matterbridge context
|
|
581
486
|
try {
|
|
582
487
|
await this.startMatterStorage();
|
|
583
488
|
}
|
|
@@ -585,12 +490,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
585
490
|
this.log.fatal(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
586
491
|
throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
587
492
|
}
|
|
588
|
-
// Clear the matterbridge context if the reset parameter is set
|
|
589
493
|
if (hasParameter('reset') && getParameter('reset') === undefined) {
|
|
590
494
|
await this.shutdownProcessAndReset();
|
|
591
495
|
return;
|
|
592
496
|
}
|
|
593
|
-
// Clear matterbridge plugin context if the reset parameter is set
|
|
594
497
|
if (hasParameter('reset') && getParameter('reset') !== undefined) {
|
|
595
498
|
this.log.debug(`Reset plugin ${getParameter('reset')}`);
|
|
596
499
|
const plugin = this.plugins.get(getParameter('reset'));
|
|
@@ -612,11 +515,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
612
515
|
this.emit('shutdown');
|
|
613
516
|
return;
|
|
614
517
|
}
|
|
615
|
-
// Initialize frontend
|
|
616
518
|
if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
|
|
617
519
|
await this.frontend.start(getIntParameter('frontend'));
|
|
618
520
|
this.frontend.logLevel = this.log.logLevel;
|
|
619
|
-
// Check each 60 minutes the latest versions
|
|
620
521
|
this.checkUpdateInterval = setInterval(() => {
|
|
621
522
|
this.getMatterbridgeLatestVersion();
|
|
622
523
|
for (const plugin of this.plugins) {
|
|
@@ -624,24 +525,20 @@ export class Matterbridge extends EventEmitter {
|
|
|
624
525
|
}
|
|
625
526
|
this.frontend.wssSendRefreshRequired();
|
|
626
527
|
}, 60 * 60 * 1000);
|
|
627
|
-
// Start the matterbridge in mode test
|
|
628
528
|
if (hasParameter('test')) {
|
|
629
529
|
this.bridgeMode = 'bridge';
|
|
630
530
|
MatterbridgeEndpoint.bridgeMode = 'bridge';
|
|
631
531
|
return;
|
|
632
532
|
}
|
|
633
|
-
// Start the matterbridge in mode controller
|
|
634
533
|
if (hasParameter('controller')) {
|
|
635
534
|
this.bridgeMode = 'controller';
|
|
636
535
|
await this.startController();
|
|
637
536
|
return;
|
|
638
537
|
}
|
|
639
|
-
// Check if the bridge mode is set and start matterbridge in bridge mode if not set
|
|
640
538
|
if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
|
|
641
539
|
this.log.info('Setting default matterbridge start mode to bridge');
|
|
642
540
|
await this.nodeContext?.set('bridgeMode', 'bridge');
|
|
643
541
|
}
|
|
644
|
-
// Start matterbridge in bridge mode
|
|
645
542
|
if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
|
|
646
543
|
this.bridgeMode = 'bridge';
|
|
647
544
|
MatterbridgeEndpoint.bridgeMode = 'bridge';
|
|
@@ -649,7 +546,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
649
546
|
await this.startBridge();
|
|
650
547
|
return;
|
|
651
548
|
}
|
|
652
|
-
// Start matterbridge in childbridge mode
|
|
653
549
|
if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
|
|
654
550
|
this.bridgeMode = 'childbridge';
|
|
655
551
|
MatterbridgeEndpoint.bridgeMode = 'childbridge';
|
|
@@ -658,28 +554,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
658
554
|
return;
|
|
659
555
|
}
|
|
660
556
|
}
|
|
661
|
-
/**
|
|
662
|
-
* Asynchronously loads and starts the registered plugins.
|
|
663
|
-
*
|
|
664
|
-
* This method is responsible for initializing and staarting all enabled plugins.
|
|
665
|
-
* It ensures that each plugin is properly loaded and started before the bridge starts.
|
|
666
|
-
*
|
|
667
|
-
* @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
|
|
668
|
-
*/
|
|
669
557
|
async startPlugins() {
|
|
670
|
-
// Check, load and start the plugins
|
|
671
558
|
for (const plugin of this.plugins) {
|
|
672
559
|
plugin.configJson = await this.plugins.loadConfig(plugin);
|
|
673
560
|
plugin.schemaJson = await this.plugins.loadSchema(plugin);
|
|
674
|
-
// Check if the plugin is available
|
|
675
561
|
if (!(await this.plugins.resolve(plugin.path))) {
|
|
676
562
|
this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
|
|
677
563
|
plugin.enabled = false;
|
|
678
564
|
plugin.error = true;
|
|
679
565
|
continue;
|
|
680
566
|
}
|
|
681
|
-
// Check if the plugin has a new version
|
|
682
|
-
// this.getPluginLatestVersion(plugin); // No await do it asyncronously
|
|
683
567
|
if (!plugin.enabled) {
|
|
684
568
|
this.log.info(`Plugin ${plg}${plugin.name}${nf} not enabled`);
|
|
685
569
|
continue;
|
|
@@ -693,26 +577,20 @@ export class Matterbridge extends EventEmitter {
|
|
|
693
577
|
plugin.addedDevices = undefined;
|
|
694
578
|
plugin.qrPairingCode = undefined;
|
|
695
579
|
plugin.manualPairingCode = undefined;
|
|
696
|
-
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
580
|
+
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
697
581
|
}
|
|
698
582
|
this.frontend.wssSendRefreshRequired();
|
|
699
583
|
}
|
|
700
|
-
/**
|
|
701
|
-
* Registers the process handlers for uncaughtException, unhandledRejection, SIGINT and SIGTERM.
|
|
702
|
-
* When either of these signals are received, the cleanup method is called with an appropriate message.
|
|
703
|
-
*/
|
|
704
584
|
registerProcessHandlers() {
|
|
705
585
|
this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
|
|
706
586
|
process.removeAllListeners('uncaughtException');
|
|
707
587
|
process.removeAllListeners('unhandledRejection');
|
|
708
588
|
this.exceptionHandler = async (error) => {
|
|
709
|
-
this.log.
|
|
710
|
-
// await this.cleanup('Unhandled Exception detected, cleaning up...');
|
|
589
|
+
this.log.error('Unhandled Exception detected at:', error.stack || error, rs);
|
|
711
590
|
};
|
|
712
591
|
process.on('uncaughtException', this.exceptionHandler);
|
|
713
592
|
this.rejectionHandler = async (reason, promise) => {
|
|
714
|
-
this.log.
|
|
715
|
-
// await this.cleanup('Unhandled Rejection detected, cleaning up...');
|
|
593
|
+
this.log.error('Unhandled Rejection detected at:', promise, 'reason:', reason instanceof Error ? reason.stack : reason, rs);
|
|
716
594
|
};
|
|
717
595
|
process.on('unhandledRejection', this.rejectionHandler);
|
|
718
596
|
this.log.debug(`Registering SIGINT and SIGTERM signal handlers...`);
|
|
@@ -725,9 +603,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
725
603
|
};
|
|
726
604
|
process.on('SIGTERM', this.sigtermHandler);
|
|
727
605
|
}
|
|
728
|
-
/**
|
|
729
|
-
* Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
|
|
730
|
-
*/
|
|
731
606
|
deregisterProcesslHandlers() {
|
|
732
607
|
this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
|
|
733
608
|
if (this.exceptionHandler)
|
|
@@ -744,17 +619,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
744
619
|
process.off('SIGTERM', this.sigtermHandler);
|
|
745
620
|
this.sigtermHandler = undefined;
|
|
746
621
|
}
|
|
747
|
-
/**
|
|
748
|
-
* Logs the node and system information.
|
|
749
|
-
*/
|
|
750
622
|
async logNodeAndSystemInfo() {
|
|
751
|
-
// IP address information
|
|
752
623
|
const networkInterfaces = os.networkInterfaces();
|
|
753
624
|
this.systemInformation.interfaceName = '';
|
|
754
625
|
this.systemInformation.ipv4Address = '';
|
|
755
626
|
this.systemInformation.ipv6Address = '';
|
|
756
627
|
for (const [interfaceName, interfaceDetails] of Object.entries(networkInterfaces)) {
|
|
757
|
-
// this.log.debug(`Checking interface: '${interfaceName}' for '${this.mdnsInterface}'`);
|
|
758
628
|
if (this.mdnsInterface && interfaceName !== this.mdnsInterface)
|
|
759
629
|
continue;
|
|
760
630
|
if (!interfaceDetails) {
|
|
@@ -780,22 +650,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
780
650
|
break;
|
|
781
651
|
}
|
|
782
652
|
}
|
|
783
|
-
// Node information
|
|
784
653
|
this.systemInformation.nodeVersion = process.versions.node;
|
|
785
654
|
const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
|
|
786
655
|
const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
|
|
787
656
|
const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
|
|
788
|
-
// Host system information
|
|
789
657
|
this.systemInformation.hostname = os.hostname();
|
|
790
658
|
this.systemInformation.user = os.userInfo().username;
|
|
791
|
-
this.systemInformation.osType = os.type();
|
|
792
|
-
this.systemInformation.osRelease = os.release();
|
|
793
|
-
this.systemInformation.osPlatform = os.platform();
|
|
794
|
-
this.systemInformation.osArch = os.arch();
|
|
795
|
-
this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
796
|
-
this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
797
|
-
this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
|
|
798
|
-
// Log the system information
|
|
659
|
+
this.systemInformation.osType = os.type();
|
|
660
|
+
this.systemInformation.osRelease = os.release();
|
|
661
|
+
this.systemInformation.osPlatform = os.platform();
|
|
662
|
+
this.systemInformation.osArch = os.arch();
|
|
663
|
+
this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
664
|
+
this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
665
|
+
this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
|
|
799
666
|
this.log.debug('Host System Information:');
|
|
800
667
|
this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
|
|
801
668
|
this.log.debug(`- User: ${this.systemInformation.user}`);
|
|
@@ -811,19 +678,15 @@ export class Matterbridge extends EventEmitter {
|
|
|
811
678
|
this.log.debug(`- Total Memory: ${this.systemInformation.totalMemory}`);
|
|
812
679
|
this.log.debug(`- Free Memory: ${this.systemInformation.freeMemory}`);
|
|
813
680
|
this.log.debug(`- System Uptime: ${this.systemInformation.systemUptime}`);
|
|
814
|
-
// Home directory
|
|
815
681
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
816
682
|
this.matterbridgeInformation.homeDirectory = this.homeDirectory;
|
|
817
683
|
this.log.debug(`Home Directory: ${this.homeDirectory}`);
|
|
818
|
-
// Package root directory
|
|
819
684
|
const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
820
685
|
this.rootDirectory = path.resolve(currentFileDirectory, '../');
|
|
821
686
|
this.matterbridgeInformation.rootDirectory = this.rootDirectory;
|
|
822
687
|
this.log.debug(`Root Directory: ${this.rootDirectory}`);
|
|
823
|
-
// Global node_modules directory
|
|
824
688
|
if (this.nodeContext)
|
|
825
689
|
this.globalModulesDirectory = this.matterbridgeInformation.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
|
|
826
|
-
// First run of Matterbridge so the node storage is empty
|
|
827
690
|
if (this.globalModulesDirectory === '') {
|
|
828
691
|
try {
|
|
829
692
|
this.globalModulesDirectory = await this.getGlobalNodeModules();
|
|
@@ -837,20 +700,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
837
700
|
}
|
|
838
701
|
else
|
|
839
702
|
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
840
|
-
/* removed cause is too expensive for the shelly board and not really needed. Why should it change the globalModulesDirectory?
|
|
841
|
-
else {
|
|
842
|
-
this.getGlobalNodeModules()
|
|
843
|
-
.then(async (globalModulesDirectory) => {
|
|
844
|
-
this.globalModulesDirectory = globalModulesDirectory;
|
|
845
|
-
this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory;
|
|
846
|
-
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
847
|
-
await this.nodeContext?.set<string>('globalModulesDirectory', this.globalModulesDirectory);
|
|
848
|
-
})
|
|
849
|
-
.catch((error) => {
|
|
850
|
-
this.log.error(`Error getting global node_modules directory: ${error}`);
|
|
851
|
-
});
|
|
852
|
-
}*/
|
|
853
|
-
// Create the data directory .matterbridge in the home directory
|
|
854
703
|
this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
|
|
855
704
|
this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
|
|
856
705
|
try {
|
|
@@ -874,7 +723,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
874
723
|
}
|
|
875
724
|
}
|
|
876
725
|
this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
|
|
877
|
-
// Create the plugin directory Matterbridge in the home directory
|
|
878
726
|
this.matterbridgePluginDirectory = path.join(this.homeDirectory, 'Matterbridge');
|
|
879
727
|
this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
|
|
880
728
|
try {
|
|
@@ -898,28 +746,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
898
746
|
}
|
|
899
747
|
}
|
|
900
748
|
this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
|
|
901
|
-
// Matterbridge version
|
|
902
749
|
const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
|
|
903
750
|
this.matterbridgeVersion = this.matterbridgeLatestVersion = packageJson.version;
|
|
904
751
|
this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeInformation.matterbridgeLatestVersion = this.matterbridgeVersion;
|
|
905
752
|
this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
|
|
906
|
-
// Matterbridge latest version
|
|
907
753
|
if (this.nodeContext)
|
|
908
754
|
this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
|
|
909
755
|
this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
|
|
910
|
-
// this.getMatterbridgeLatestVersion();
|
|
911
|
-
// Current working directory
|
|
912
756
|
const currentDir = process.cwd();
|
|
913
757
|
this.log.debug(`Current Working Directory: ${currentDir}`);
|
|
914
|
-
// Command line arguments (excluding 'node' and the script name)
|
|
915
758
|
const cmdArgs = process.argv.slice(2).join(' ');
|
|
916
759
|
this.log.debug(`Command Line Arguments: ${cmdArgs}`);
|
|
917
760
|
}
|
|
918
|
-
/**
|
|
919
|
-
* Retrieves the latest version of a package from the npm registry.
|
|
920
|
-
* @param packageName - The name of the package.
|
|
921
|
-
* @returns A Promise that resolves to the latest version of the package.
|
|
922
|
-
*/
|
|
923
761
|
async getLatestVersion(packageName) {
|
|
924
762
|
return new Promise((resolve, reject) => {
|
|
925
763
|
this.execRunningCount++;
|
|
@@ -934,10 +772,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
934
772
|
});
|
|
935
773
|
});
|
|
936
774
|
}
|
|
937
|
-
/**
|
|
938
|
-
* Retrieves the path to the global Node.js modules directory.
|
|
939
|
-
* @returns A promise that resolves to the path of the global Node.js modules directory.
|
|
940
|
-
*/
|
|
941
775
|
async getGlobalNodeModules() {
|
|
942
776
|
return new Promise((resolve, reject) => {
|
|
943
777
|
this.execRunningCount++;
|
|
@@ -952,11 +786,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
952
786
|
});
|
|
953
787
|
});
|
|
954
788
|
}
|
|
955
|
-
/**
|
|
956
|
-
* Retrieves the latest version of Matterbridge and performs necessary actions based on the version comparison.
|
|
957
|
-
* @private
|
|
958
|
-
* @returns {Promise<void>} A promise that resolves when the latest version is retrieved and actions are performed.
|
|
959
|
-
*/
|
|
960
789
|
async getMatterbridgeLatestVersion() {
|
|
961
790
|
this.getLatestVersion('matterbridge')
|
|
962
791
|
.then(async (matterbridgeLatestVersion) => {
|
|
@@ -973,19 +802,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
973
802
|
})
|
|
974
803
|
.catch((error) => {
|
|
975
804
|
this.log.error(`Error getting Matterbridge latest version: ${error.message}`);
|
|
976
|
-
// error.stack && this.log.debug(error.stack);
|
|
977
805
|
});
|
|
978
806
|
}
|
|
979
|
-
/**
|
|
980
|
-
* Retrieves the latest version of a plugin and updates the plugin's latestVersion property.
|
|
981
|
-
* If the plugin's version is different from the latest version, logs a warning message.
|
|
982
|
-
* If the plugin's version is the same as the latest version, logs an info message.
|
|
983
|
-
* If there is an error retrieving the latest version, logs an error message.
|
|
984
|
-
*
|
|
985
|
-
* @private
|
|
986
|
-
* @param {RegisteredPlugin} plugin - The plugin for which to retrieve the latest version.
|
|
987
|
-
* @returns {Promise<void>} A promise that resolves when the latest version is retrieved and actions are performed.
|
|
988
|
-
*/
|
|
989
807
|
async getPluginLatestVersion(plugin) {
|
|
990
808
|
this.getLatestVersion(plugin.name)
|
|
991
809
|
.then(async (latestVersion) => {
|
|
@@ -997,54 +815,40 @@ export class Matterbridge extends EventEmitter {
|
|
|
997
815
|
})
|
|
998
816
|
.catch((error) => {
|
|
999
817
|
this.log.error(`Error getting ${plg}${plugin.name}${er} latest version: ${error.message}`);
|
|
1000
|
-
// error.stack && this.log.debug(error.stack);
|
|
1001
818
|
});
|
|
1002
819
|
}
|
|
1003
|
-
/**
|
|
1004
|
-
* Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (for the frontend).
|
|
1005
|
-
*
|
|
1006
|
-
* @returns {Function} The MatterLogger function.
|
|
1007
|
-
*/
|
|
1008
820
|
createMatterLogger() {
|
|
1009
|
-
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4
|
|
821
|
+
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
|
|
1010
822
|
return (_level, formattedLog) => {
|
|
1011
823
|
const logger = formattedLog.slice(44, 44 + 20).trim();
|
|
1012
824
|
const message = formattedLog.slice(65);
|
|
1013
825
|
matterLogger.logName = logger;
|
|
1014
826
|
switch (_level) {
|
|
1015
827
|
case MatterLogLevel.DEBUG:
|
|
1016
|
-
matterLogger.log("debug"
|
|
828
|
+
matterLogger.log("debug", message);
|
|
1017
829
|
break;
|
|
1018
830
|
case MatterLogLevel.INFO:
|
|
1019
|
-
matterLogger.log("info"
|
|
831
|
+
matterLogger.log("info", message);
|
|
1020
832
|
break;
|
|
1021
833
|
case MatterLogLevel.NOTICE:
|
|
1022
|
-
matterLogger.log("notice"
|
|
834
|
+
matterLogger.log("notice", message);
|
|
1023
835
|
break;
|
|
1024
836
|
case MatterLogLevel.WARN:
|
|
1025
|
-
matterLogger.log("warn"
|
|
837
|
+
matterLogger.log("warn", message);
|
|
1026
838
|
break;
|
|
1027
839
|
case MatterLogLevel.ERROR:
|
|
1028
|
-
matterLogger.log("error"
|
|
840
|
+
matterLogger.log("error", message);
|
|
1029
841
|
break;
|
|
1030
842
|
case MatterLogLevel.FATAL:
|
|
1031
|
-
matterLogger.log("fatal"
|
|
843
|
+
matterLogger.log("fatal", message);
|
|
1032
844
|
break;
|
|
1033
845
|
default:
|
|
1034
|
-
matterLogger.log("debug"
|
|
846
|
+
matterLogger.log("debug", message);
|
|
1035
847
|
break;
|
|
1036
848
|
}
|
|
1037
849
|
};
|
|
1038
850
|
}
|
|
1039
|
-
/**
|
|
1040
|
-
* Creates a Matter File Logger.
|
|
1041
|
-
*
|
|
1042
|
-
* @param {string} filePath - The path to the log file.
|
|
1043
|
-
* @param {boolean} [unlink=false] - Whether to unlink the log file before creating a new one.
|
|
1044
|
-
* @returns {Function} - A function that logs formatted messages to the log file.
|
|
1045
|
-
*/
|
|
1046
851
|
async createMatterFileLogger(filePath, unlink = false) {
|
|
1047
|
-
// 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
|
|
1048
852
|
let fileSize = 0;
|
|
1049
853
|
if (unlink) {
|
|
1050
854
|
try {
|
|
@@ -1093,21 +897,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1093
897
|
}
|
|
1094
898
|
};
|
|
1095
899
|
}
|
|
1096
|
-
/**
|
|
1097
|
-
* Restarts the process by exiting the current instance and loading a new instance.
|
|
1098
|
-
*/
|
|
1099
900
|
async restartProcess() {
|
|
1100
901
|
await this.cleanup('restarting...', true);
|
|
1101
902
|
}
|
|
1102
|
-
/**
|
|
1103
|
-
* Shut down the process by exiting the current process.
|
|
1104
|
-
*/
|
|
1105
903
|
async shutdownProcess() {
|
|
1106
904
|
await this.cleanup('shutting down...', false);
|
|
1107
905
|
}
|
|
1108
|
-
/**
|
|
1109
|
-
* Update matterbridge and and shut down the process.
|
|
1110
|
-
*/
|
|
1111
906
|
async updateProcess() {
|
|
1112
907
|
this.log.info('Updating matterbridge...');
|
|
1113
908
|
try {
|
|
@@ -1120,9 +915,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1120
915
|
this.frontend.wssSendRestartRequired();
|
|
1121
916
|
await this.cleanup('updating...', false);
|
|
1122
917
|
}
|
|
1123
|
-
/**
|
|
1124
|
-
* Unregister all devices and shut down the process.
|
|
1125
|
-
*/
|
|
1126
918
|
async unregisterAndShutdownProcess() {
|
|
1127
919
|
this.log.info('Unregistering all devices and shutting down...');
|
|
1128
920
|
for (const plugin of this.plugins) {
|
|
@@ -1130,9 +922,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1130
922
|
}
|
|
1131
923
|
await this.cleanup('unregistered all devices and shutting down...', false);
|
|
1132
924
|
}
|
|
1133
|
-
/**
|
|
1134
|
-
* Reset commissioning and shut down the process.
|
|
1135
|
-
*/
|
|
1136
925
|
async shutdownProcessAndReset() {
|
|
1137
926
|
this.log.info('Resetting Matterbridge commissioning information...');
|
|
1138
927
|
await this.matterStorageManager?.createContext('events')?.clearAll();
|
|
@@ -1144,12 +933,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1144
933
|
this.log.info('Matter storage reset done! Remove the bridge from the controller.');
|
|
1145
934
|
await this.cleanup('shutting down with reset...', false);
|
|
1146
935
|
}
|
|
1147
|
-
/**
|
|
1148
|
-
* Factory reset and shut down the process.
|
|
1149
|
-
*/
|
|
1150
936
|
async shutdownProcessAndFactoryReset() {
|
|
1151
937
|
try {
|
|
1152
|
-
// Delete old matter storage file and backup
|
|
1153
938
|
const file = path.join(this.matterbridgeDirectory, 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.json');
|
|
1154
939
|
this.log.info(`Unlinking old matter storage file: ${file}`);
|
|
1155
940
|
await fs.unlink(file);
|
|
@@ -1163,7 +948,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1163
948
|
}
|
|
1164
949
|
}
|
|
1165
950
|
try {
|
|
1166
|
-
// Delete matter node storage directory with its subdirectories and backup
|
|
1167
951
|
const dir = path.join(this.matterbridgeDirectory, 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
|
|
1168
952
|
this.log.info(`Removing matter node storage directory: ${dir}`);
|
|
1169
953
|
await fs.rm(dir, { recursive: true });
|
|
@@ -1177,7 +961,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1177
961
|
}
|
|
1178
962
|
}
|
|
1179
963
|
try {
|
|
1180
|
-
// Delete node storage directory with its subdirectories and backup
|
|
1181
964
|
const dir = path.join(this.matterbridgeDirectory, 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
|
|
1182
965
|
this.log.info(`Removing storage directory: ${dir}`);
|
|
1183
966
|
await fs.rm(dir, { recursive: true });
|
|
@@ -1197,41 +980,30 @@ export class Matterbridge extends EventEmitter {
|
|
|
1197
980
|
this.devices.clear();
|
|
1198
981
|
await this.cleanup('shutting down with factory reset...', false);
|
|
1199
982
|
}
|
|
1200
|
-
/**
|
|
1201
|
-
* Cleans up the Matterbridge instance.
|
|
1202
|
-
* @param message - The cleanup message.
|
|
1203
|
-
* @param restart - Indicates whether to restart the instance after cleanup. Default is `false`.
|
|
1204
|
-
* @returns A promise that resolves when the cleanup is completed.
|
|
1205
|
-
*/
|
|
1206
983
|
async cleanup(message, restart = false) {
|
|
1207
984
|
if (this.initialized && !this.hasCleanupStarted) {
|
|
1208
985
|
this.hasCleanupStarted = true;
|
|
1209
986
|
this.log.info(message);
|
|
1210
|
-
// Clear the start matter interval
|
|
1211
987
|
if (this.startMatterInterval) {
|
|
1212
988
|
clearInterval(this.startMatterInterval);
|
|
1213
989
|
this.startMatterInterval = undefined;
|
|
1214
990
|
this.log.debug('Start matter interval cleared');
|
|
1215
991
|
}
|
|
1216
|
-
// Clear the check update interval
|
|
1217
992
|
if (this.checkUpdateInterval) {
|
|
1218
993
|
clearInterval(this.checkUpdateInterval);
|
|
1219
994
|
this.checkUpdateInterval = undefined;
|
|
1220
995
|
this.log.debug('Check update interval cleared');
|
|
1221
996
|
}
|
|
1222
|
-
// Clear the configure timeout
|
|
1223
997
|
if (this.configureTimeout) {
|
|
1224
998
|
clearTimeout(this.configureTimeout);
|
|
1225
999
|
this.configureTimeout = undefined;
|
|
1226
1000
|
this.log.debug('Matterbridge configure timeout cleared');
|
|
1227
1001
|
}
|
|
1228
|
-
// Clear the reachability timeout
|
|
1229
1002
|
if (this.reachabilityTimeout) {
|
|
1230
1003
|
clearTimeout(this.reachabilityTimeout);
|
|
1231
1004
|
this.reachabilityTimeout = undefined;
|
|
1232
1005
|
this.log.debug('Matterbridge reachability timeout cleared');
|
|
1233
1006
|
}
|
|
1234
|
-
// Calling the shutdown method of each plugin and clear the plugins reachability timeout
|
|
1235
1007
|
for (const plugin of this.plugins) {
|
|
1236
1008
|
if (!plugin.enabled || plugin.error)
|
|
1237
1009
|
continue;
|
|
@@ -1242,7 +1014,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1242
1014
|
this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
|
|
1243
1015
|
}
|
|
1244
1016
|
}
|
|
1245
|
-
// Stopping matter server nodes
|
|
1246
1017
|
this.log.notice(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
|
|
1247
1018
|
if (this.bridgeMode === 'bridge') {
|
|
1248
1019
|
if (this.serverNode) {
|
|
@@ -1259,37 +1030,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
1259
1030
|
}
|
|
1260
1031
|
}
|
|
1261
1032
|
this.log.notice('Stopped matter server nodes');
|
|
1262
|
-
// Stop matter storage
|
|
1263
1033
|
await this.stopMatterStorage();
|
|
1264
|
-
// Stop the frontend
|
|
1265
1034
|
await this.frontend.stop();
|
|
1266
|
-
// Remove the matterfilelogger
|
|
1267
1035
|
try {
|
|
1268
1036
|
Logger.removeLogger('matterfilelogger');
|
|
1269
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1270
1037
|
}
|
|
1271
1038
|
catch (error) {
|
|
1272
|
-
// this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridgeDirectory, this.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
|
|
1273
1039
|
}
|
|
1274
|
-
// Serialize registeredDevices
|
|
1275
1040
|
if (this.nodeStorage && this.nodeContext) {
|
|
1276
|
-
/*
|
|
1277
|
-
TODO: Implement serialization of registered devices in edge mode
|
|
1278
|
-
this.log.info('Saving registered devices...');
|
|
1279
|
-
const serializedRegisteredDevices: SerializedMatterbridgeEndpoint[] = [];
|
|
1280
|
-
this.devices.forEach(async (device) => {
|
|
1281
|
-
const serializedMatterbridgeDevice = MatterbridgeEndpoint.serialize(device);
|
|
1282
|
-
// this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
|
|
1283
|
-
if (serializedMatterbridgeDevice) serializedRegisteredDevices.push(serializedMatterbridgeDevice);
|
|
1284
|
-
});
|
|
1285
|
-
await this.nodeContext.set<SerializedMatterbridgeEndpoint[]>('devices', serializedRegisteredDevices);
|
|
1286
|
-
this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
|
|
1287
|
-
*/
|
|
1288
|
-
// Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
|
|
1289
1041
|
this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
|
|
1290
1042
|
await this.nodeContext.close();
|
|
1291
1043
|
this.nodeContext = undefined;
|
|
1292
|
-
// Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
|
|
1293
1044
|
for (const plugin of this.plugins) {
|
|
1294
1045
|
if (plugin.nodeContext) {
|
|
1295
1046
|
this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
|
|
@@ -1306,13 +1057,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1306
1057
|
}
|
|
1307
1058
|
this.plugins.clear();
|
|
1308
1059
|
this.devices.clear();
|
|
1309
|
-
// Deregisters the process handlers
|
|
1310
1060
|
this.deregisterProcesslHandlers();
|
|
1311
1061
|
if (restart) {
|
|
1312
1062
|
if (message === 'updating...') {
|
|
1313
1063
|
this.log.info('Cleanup completed. Updating...');
|
|
1314
1064
|
Matterbridge.instance = undefined;
|
|
1315
|
-
this.emit('update');
|
|
1065
|
+
this.emit('update');
|
|
1316
1066
|
}
|
|
1317
1067
|
else if (message === 'restarting...') {
|
|
1318
1068
|
this.log.info('Cleanup completed. Restarting...');
|
|
@@ -1328,15 +1078,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
1328
1078
|
this.hasCleanupStarted = false;
|
|
1329
1079
|
this.initialized = false;
|
|
1330
1080
|
}
|
|
1081
|
+
else {
|
|
1082
|
+
this.log.debug('Cleanup already started...');
|
|
1083
|
+
}
|
|
1331
1084
|
}
|
|
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
1085
|
async createAccessoryPlugin(plugin, device, start = false) {
|
|
1341
1086
|
if (!plugin.locked && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
|
|
1342
1087
|
plugin.locked = true;
|
|
@@ -1348,13 +1093,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1348
1093
|
await this.startServerNode(plugin.serverNode);
|
|
1349
1094
|
}
|
|
1350
1095
|
}
|
|
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
1096
|
async createDynamicPlugin(plugin, start = false) {
|
|
1359
1097
|
if (!plugin.locked) {
|
|
1360
1098
|
plugin.locked = true;
|
|
@@ -1366,13 +1104,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1366
1104
|
await this.startServerNode(plugin.serverNode);
|
|
1367
1105
|
}
|
|
1368
1106
|
}
|
|
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
1107
|
async startBridge() {
|
|
1375
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1376
1108
|
if (!this.matterStorageManager)
|
|
1377
1109
|
throw new Error('No storage manager initialized');
|
|
1378
1110
|
if (!this.matterbridgeContext)
|
|
@@ -1409,9 +1141,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1409
1141
|
clearInterval(this.startMatterInterval);
|
|
1410
1142
|
this.startMatterInterval = undefined;
|
|
1411
1143
|
this.log.debug('Cleared startMatterInterval interval for Matterbridge');
|
|
1412
|
-
// Start the Matter server node
|
|
1413
1144
|
this.startServerNode(this.serverNode);
|
|
1414
|
-
// Configure the plugins
|
|
1415
1145
|
this.configureTimeout = setTimeout(async () => {
|
|
1416
1146
|
for (const plugin of this.plugins) {
|
|
1417
1147
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1426,7 +1156,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1426
1156
|
}
|
|
1427
1157
|
this.frontend.wssSendRefreshRequired();
|
|
1428
1158
|
}, 30 * 1000);
|
|
1429
|
-
// Setting reachability to true
|
|
1430
1159
|
this.reachabilityTimeout = setTimeout(() => {
|
|
1431
1160
|
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1432
1161
|
if (this.serverNode)
|
|
@@ -1437,14 +1166,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1437
1166
|
}, 60 * 1000);
|
|
1438
1167
|
}, 1000);
|
|
1439
1168
|
}
|
|
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
1169
|
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
1170
|
if (!this.matterStorageManager)
|
|
1449
1171
|
throw new Error('No storage manager initialized');
|
|
1450
1172
|
for (const plugin of this.plugins) {
|
|
@@ -1491,13 +1213,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1491
1213
|
clearInterval(this.startMatterInterval);
|
|
1492
1214
|
this.startMatterInterval = undefined;
|
|
1493
1215
|
this.log.debug('Cleared startMatterInterval interval in childbridge mode');
|
|
1494
|
-
// Configure the plugins
|
|
1495
1216
|
this.configureTimeout = setTimeout(async () => {
|
|
1496
1217
|
for (const plugin of this.plugins) {
|
|
1497
1218
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
1498
1219
|
continue;
|
|
1499
1220
|
try {
|
|
1500
|
-
await this.plugins.configure(plugin);
|
|
1221
|
+
await this.plugins.configure(plugin);
|
|
1501
1222
|
}
|
|
1502
1223
|
catch (error) {
|
|
1503
1224
|
plugin.error = true;
|
|
@@ -1525,9 +1246,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1525
1246
|
this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
|
|
1526
1247
|
continue;
|
|
1527
1248
|
}
|
|
1528
|
-
// Start the Matter server node
|
|
1529
1249
|
this.startServerNode(plugin.serverNode);
|
|
1530
|
-
// Setting reachability to true
|
|
1531
1250
|
plugin.reachabilityTimeout = setTimeout(() => {
|
|
1532
1251
|
this.log.info(`Setting reachability to true for ${plg}${plugin.name}${db}`);
|
|
1533
1252
|
if (plugin.serverNode)
|
|
@@ -1541,219 +1260,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
1541
1260
|
}
|
|
1542
1261
|
}, 1000);
|
|
1543
1262
|
}
|
|
1544
|
-
/**
|
|
1545
|
-
* Starts the Matterbridge controller.
|
|
1546
|
-
* @private
|
|
1547
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1548
|
-
*/
|
|
1549
1263
|
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
1264
|
}
|
|
1748
|
-
/** ***********************************************************************************************************************************/
|
|
1749
|
-
/** Matter.js methods */
|
|
1750
|
-
/** ***********************************************************************************************************************************/
|
|
1751
|
-
/**
|
|
1752
|
-
* Starts the matter storage process with name Matterbridge.
|
|
1753
|
-
* @returns {Promise<void>} - A promise that resolves when the storage process is started.
|
|
1754
|
-
*/
|
|
1755
1265
|
async startMatterStorage() {
|
|
1756
|
-
// Setup Matter storage
|
|
1757
1266
|
this.log.info(`Starting matter node storage...`);
|
|
1758
1267
|
this.matterStorageService = this.environment.get(StorageService);
|
|
1759
1268
|
this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
|
|
@@ -1761,25 +1270,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1761
1270
|
this.log.info('Matter node storage manager "Matterbridge" created');
|
|
1762
1271
|
this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', bridge.code, this.aggregatorVendorId, 'Matterbridge', this.aggregatorProductId, 'Matterbridge aggregator');
|
|
1763
1272
|
this.log.info('Matter node storage started');
|
|
1764
|
-
// Backup matter storage since it is created/opened correctly
|
|
1765
1273
|
await this.backupMatterStorage(path.join(this.matterbridgeDirectory, this.matterStorageName), path.join(this.matterbridgeDirectory, this.matterStorageName + '.backup'));
|
|
1766
1274
|
}
|
|
1767
|
-
/**
|
|
1768
|
-
* Makes a backup copy of the specified matter storage directory.
|
|
1769
|
-
*
|
|
1770
|
-
* @param storageName - The name of the storage directory to be backed up.
|
|
1771
|
-
* @param backupName - The name of the backup directory to be created.
|
|
1772
|
-
* @returns {Promise<void>} A promise that resolves when the has been done.
|
|
1773
|
-
*/
|
|
1774
1275
|
async backupMatterStorage(storageName, backupName) {
|
|
1775
1276
|
this.log.info('Creating matter node storage backup...');
|
|
1776
1277
|
await copyDirectory(storageName, backupName);
|
|
1777
1278
|
this.log.info('Created matter node storage backup');
|
|
1778
1279
|
}
|
|
1779
|
-
/**
|
|
1780
|
-
* Stops the matter storage.
|
|
1781
|
-
* @returns {Promise<void>} A promise that resolves when the storage is stopped.
|
|
1782
|
-
*/
|
|
1783
1280
|
async stopMatterStorage() {
|
|
1784
1281
|
this.log.info('Closing matter node storage...');
|
|
1785
1282
|
this.matterStorageManager?.close();
|
|
@@ -1788,19 +1285,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1788
1285
|
this.matterbridgeContext = undefined;
|
|
1789
1286
|
this.log.info('Matter node storage closed');
|
|
1790
1287
|
}
|
|
1791
|
-
/**
|
|
1792
|
-
* Creates a server node storage context.
|
|
1793
|
-
*
|
|
1794
|
-
* @param {string} pluginName - The name of the plugin.
|
|
1795
|
-
* @param {string} deviceName - The name of the device.
|
|
1796
|
-
* @param {DeviceTypeId} deviceType - The device type of the device.
|
|
1797
|
-
* @param {number} vendorId - The vendor ID.
|
|
1798
|
-
* @param {string} vendorName - The vendor name.
|
|
1799
|
-
* @param {number} productId - The product ID.
|
|
1800
|
-
* @param {string} productName - The product name.
|
|
1801
|
-
* @param {string} [serialNumber] - The serial number of the device (optional).
|
|
1802
|
-
* @returns {Promise<StorageContext>} The storage context for the commissioning server.
|
|
1803
|
-
*/
|
|
1804
1288
|
async createServerNodeContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber) {
|
|
1805
1289
|
if (!this.matterStorageService)
|
|
1806
1290
|
throw new Error('No storage service initialized');
|
|
@@ -1833,15 +1317,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1833
1317
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1834
1318
|
return storageContext;
|
|
1835
1319
|
}
|
|
1836
|
-
/**
|
|
1837
|
-
* Creates a server node.
|
|
1838
|
-
*
|
|
1839
|
-
* @param {StorageContext} storageContext - The storage context for the server node.
|
|
1840
|
-
* @param {number} [port=5540] - The port number for the server node. Defaults to 5540.
|
|
1841
|
-
* @param {number} [passcode=20242025] - The passcode for the server node. Defaults to 20242025.
|
|
1842
|
-
* @param {number} [discriminator=3850] - The discriminator for the server node. Defaults to 3850.
|
|
1843
|
-
* @returns {Promise<ServerNode<ServerNode.RootEndpoint>>} A promise that resolves to the created server node.
|
|
1844
|
-
*/
|
|
1845
1320
|
async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
|
|
1846
1321
|
const storeId = await storageContext.get('storeId');
|
|
1847
1322
|
this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
|
|
@@ -1851,33 +1326,21 @@ export class Matterbridge extends EventEmitter {
|
|
|
1851
1326
|
this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
|
|
1852
1327
|
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
1853
1328
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1854
|
-
/**
|
|
1855
|
-
* Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration
|
|
1856
|
-
*/
|
|
1857
1329
|
const serverNode = await ServerNode.create({
|
|
1858
|
-
// Required: Give the Node a unique ID which is used to store the state of this node
|
|
1859
1330
|
id: storeId,
|
|
1860
|
-
// Provide Network relevant configuration like the port
|
|
1861
|
-
// Optional when operating only one device on a host, Default port is 5540
|
|
1862
1331
|
network: {
|
|
1863
1332
|
listeningAddressIpv4: this.ipv4address,
|
|
1864
1333
|
listeningAddressIpv6: this.ipv6address,
|
|
1865
1334
|
port,
|
|
1866
1335
|
},
|
|
1867
|
-
// Provide Commissioning relevant settings
|
|
1868
|
-
// Optional for development/testing purposes
|
|
1869
1336
|
commissioning: {
|
|
1870
1337
|
passcode,
|
|
1871
1338
|
discriminator,
|
|
1872
1339
|
},
|
|
1873
|
-
// Provide Node announcement settings
|
|
1874
|
-
// Optional: If Ommitted some development defaults are used
|
|
1875
1340
|
productDescription: {
|
|
1876
1341
|
name: await storageContext.get('deviceName'),
|
|
1877
1342
|
deviceType: DeviceTypeId(await storageContext.get('deviceType')),
|
|
1878
1343
|
},
|
|
1879
|
-
// Provide defaults for the BasicInformation cluster on the Root endpoint
|
|
1880
|
-
// Optional: If Omitted some development defaults are used
|
|
1881
1344
|
basicInformation: {
|
|
1882
1345
|
vendorId: VendorId(await storageContext.get('vendorId')),
|
|
1883
1346
|
vendorName: await storageContext.get('vendorName'),
|
|
@@ -1894,13 +1357,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1894
1357
|
},
|
|
1895
1358
|
});
|
|
1896
1359
|
const sanitizeFabrics = (fabrics, resetSessions = false) => {
|
|
1897
|
-
// New type of fabric information: Record<FabricIndex, ExposedFabricInformation>
|
|
1898
1360
|
const sanitizedFabrics = this.sanitizeFabricInformations(Array.from(Object.values(fabrics)));
|
|
1899
1361
|
this.log.info(`Fabrics: ${debugStringify(sanitizedFabrics)}`);
|
|
1900
1362
|
if (this.bridgeMode === 'bridge') {
|
|
1901
1363
|
this.matterbridgeFabricInformations = sanitizedFabrics;
|
|
1902
1364
|
if (resetSessions)
|
|
1903
|
-
this.matterbridgeSessionInformations = undefined;
|
|
1365
|
+
this.matterbridgeSessionInformations = undefined;
|
|
1904
1366
|
this.matterbridgePaired = true;
|
|
1905
1367
|
}
|
|
1906
1368
|
if (this.bridgeMode === 'childbridge') {
|
|
@@ -1908,19 +1370,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1908
1370
|
if (plugin) {
|
|
1909
1371
|
plugin.fabricInformations = sanitizedFabrics;
|
|
1910
1372
|
if (resetSessions)
|
|
1911
|
-
plugin.sessionInformations = undefined;
|
|
1373
|
+
plugin.sessionInformations = undefined;
|
|
1912
1374
|
plugin.paired = true;
|
|
1913
1375
|
}
|
|
1914
1376
|
}
|
|
1915
1377
|
};
|
|
1916
|
-
/**
|
|
1917
|
-
* This event is triggered when the device is initially commissioned successfully.
|
|
1918
|
-
* This means: It is added to the first fabric.
|
|
1919
|
-
*/
|
|
1920
1378
|
serverNode.lifecycle.commissioned.on(() => this.log.notice(`Server node for ${storeId} was initially commissioned successfully!`));
|
|
1921
|
-
/** This event is triggered when all fabrics are removed from the device, usually it also does a factory reset then. */
|
|
1922
1379
|
serverNode.lifecycle.decommissioned.on(() => this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`));
|
|
1923
|
-
/** This event is triggered when the device went online. This means that it is discoverable in the network. */
|
|
1924
1380
|
serverNode.lifecycle.online.on(async () => {
|
|
1925
1381
|
this.log.notice(`Server node for ${storeId} is online`);
|
|
1926
1382
|
if (!serverNode.lifecycle.isCommissioned) {
|
|
@@ -1966,7 +1422,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1966
1422
|
}
|
|
1967
1423
|
this.frontend.wssSendRefreshRequired();
|
|
1968
1424
|
});
|
|
1969
|
-
/** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
|
|
1970
1425
|
serverNode.lifecycle.offline.on(() => {
|
|
1971
1426
|
this.log.notice(`Server node for ${storeId} is offline`);
|
|
1972
1427
|
if (this.bridgeMode === 'bridge') {
|
|
@@ -1988,10 +1443,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1988
1443
|
}
|
|
1989
1444
|
this.frontend.wssSendRefreshRequired();
|
|
1990
1445
|
});
|
|
1991
|
-
/**
|
|
1992
|
-
* This event is triggered when a fabric is added, removed or updated on the device. Use this if more granular
|
|
1993
|
-
* information is needed.
|
|
1994
|
-
*/
|
|
1995
1446
|
serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
|
|
1996
1447
|
let action = '';
|
|
1997
1448
|
switch (fabricAction) {
|
|
@@ -2025,24 +1476,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
2025
1476
|
}
|
|
2026
1477
|
}
|
|
2027
1478
|
};
|
|
2028
|
-
/**
|
|
2029
|
-
* This event is triggered when an operative new session was opened by a Controller.
|
|
2030
|
-
* It is not triggered for the initial commissioning process, just afterwards for real connections.
|
|
2031
|
-
*/
|
|
2032
1479
|
serverNode.events.sessions.opened.on((session) => {
|
|
2033
1480
|
this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2034
1481
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
2035
1482
|
this.frontend.wssSendRefreshRequired();
|
|
2036
1483
|
});
|
|
2037
|
-
/**
|
|
2038
|
-
* This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
|
|
2039
|
-
*/
|
|
2040
1484
|
serverNode.events.sessions.closed.on((session) => {
|
|
2041
1485
|
this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2042
1486
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
2043
1487
|
this.frontend.wssSendRefreshRequired();
|
|
2044
1488
|
});
|
|
2045
|
-
/** This event is triggered when a subscription gets added or removed on an operative session. */
|
|
2046
1489
|
serverNode.events.sessions.subscriptionsChanged.on((session) => {
|
|
2047
1490
|
this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2048
1491
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
@@ -2051,61 +1494,38 @@ export class Matterbridge extends EventEmitter {
|
|
|
2051
1494
|
this.log.info(`Created server node for ${storeId}`);
|
|
2052
1495
|
return serverNode;
|
|
2053
1496
|
}
|
|
2054
|
-
/**
|
|
2055
|
-
* Starts the specified server node.
|
|
2056
|
-
*
|
|
2057
|
-
* @param {ServerNode} [matterServerNode] - The server node to start.
|
|
2058
|
-
* @returns {Promise<void>} A promise that resolves when the server node has started.
|
|
2059
|
-
*/
|
|
2060
1497
|
async startServerNode(matterServerNode) {
|
|
2061
1498
|
if (!matterServerNode)
|
|
2062
1499
|
return;
|
|
2063
1500
|
this.log.notice(`Starting ${matterServerNode.id} server node`);
|
|
2064
1501
|
await matterServerNode.start();
|
|
2065
1502
|
}
|
|
2066
|
-
/**
|
|
2067
|
-
* Stops the specified server node.
|
|
2068
|
-
*
|
|
2069
|
-
* @param {ServerNode} matterServerNode - The server node to stop.
|
|
2070
|
-
* @returns {Promise<void>} A promise that resolves when the server node has stopped.
|
|
2071
|
-
*/
|
|
2072
1503
|
async stopServerNode(matterServerNode) {
|
|
2073
1504
|
if (!matterServerNode)
|
|
2074
1505
|
return;
|
|
2075
1506
|
this.log.notice(`Closing ${matterServerNode.id} server node`);
|
|
2076
|
-
/*
|
|
2077
|
-
await matterServerNode.close();
|
|
2078
|
-
this.log.info(`Closed ${matterServerNode.id} server node`);
|
|
2079
|
-
*/
|
|
2080
|
-
// Helper function to add a timeout to a promise
|
|
2081
1507
|
const withTimeout = (promise, ms) => {
|
|
2082
1508
|
return new Promise((resolve, reject) => {
|
|
2083
1509
|
const timer = setTimeout(() => reject(new Error('Operation timed out')), ms);
|
|
2084
1510
|
promise
|
|
2085
1511
|
.then((result) => {
|
|
2086
|
-
clearTimeout(timer);
|
|
1512
|
+
clearTimeout(timer);
|
|
2087
1513
|
resolve(result);
|
|
2088
1514
|
})
|
|
2089
1515
|
.catch((error) => {
|
|
2090
|
-
clearTimeout(timer);
|
|
1516
|
+
clearTimeout(timer);
|
|
2091
1517
|
reject(error);
|
|
2092
1518
|
});
|
|
2093
1519
|
});
|
|
2094
1520
|
};
|
|
2095
1521
|
try {
|
|
2096
|
-
await withTimeout(matterServerNode.close(), 5000);
|
|
1522
|
+
await withTimeout(matterServerNode.close(), 5000);
|
|
2097
1523
|
this.log.info(`Closed ${matterServerNode.id} server node`);
|
|
2098
1524
|
}
|
|
2099
1525
|
catch (error) {
|
|
2100
1526
|
this.log.error(`Failed to close ${matterServerNode.id} server node: ${error instanceof Error ? error.message : error}`);
|
|
2101
1527
|
}
|
|
2102
1528
|
}
|
|
2103
|
-
/**
|
|
2104
|
-
* Advertises the specified server node if it is commissioned.
|
|
2105
|
-
*
|
|
2106
|
-
* @param {ServerNode} [matterServerNode] - The server node to advertise.
|
|
2107
|
-
* @returns {Promise<{ qrPairingCode: string, manualPairingCode: string } | undefined>} A promise that resolves to the pairing codes if the server node is advertised, or undefined if not.
|
|
2108
|
-
*/
|
|
2109
1529
|
async advertiseServerNode(matterServerNode) {
|
|
2110
1530
|
if (matterServerNode && matterServerNode.lifecycle.isCommissioned) {
|
|
2111
1531
|
await matterServerNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
|
|
@@ -2115,32 +1535,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
2115
1535
|
}
|
|
2116
1536
|
return undefined;
|
|
2117
1537
|
}
|
|
2118
|
-
/**
|
|
2119
|
-
* Creates an aggregator node with the specified storage context.
|
|
2120
|
-
*
|
|
2121
|
-
* @param {StorageContext} storageContext - The storage context for the aggregator node.
|
|
2122
|
-
* @returns {Promise<EndpointNode<AggregatorEndpoint>>} A promise that resolves to the created aggregator node.
|
|
2123
|
-
*/
|
|
2124
1538
|
async createAggregatorNode(storageContext) {
|
|
2125
1539
|
this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator `);
|
|
2126
1540
|
const aggregatorNode = new EndpointNode(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
|
|
2127
1541
|
return aggregatorNode;
|
|
2128
1542
|
}
|
|
2129
|
-
/**
|
|
2130
|
-
* Adds a MatterbridgeEndpoint to the specified plugin.
|
|
2131
|
-
*
|
|
2132
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2133
|
-
* @param {MatterbridgeEndpoint} device - The device to add as a bridged endpoint.
|
|
2134
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been added.
|
|
2135
|
-
*/
|
|
2136
1543
|
async addBridgedEndpoint(pluginName, device) {
|
|
2137
|
-
// Check if the plugin is registered
|
|
2138
1544
|
const plugin = this.plugins.get(pluginName);
|
|
2139
1545
|
if (!plugin) {
|
|
2140
1546
|
this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
|
|
2141
1547
|
return;
|
|
2142
1548
|
}
|
|
2143
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2144
1549
|
if (this.bridgeMode === 'bridge') {
|
|
2145
1550
|
this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
|
|
2146
1551
|
if (!this.aggregatorNode)
|
|
@@ -2163,26 +1568,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
2163
1568
|
plugin.registeredDevices++;
|
|
2164
1569
|
if (plugin.addedDevices !== undefined)
|
|
2165
1570
|
plugin.addedDevices++;
|
|
2166
|
-
// Add the device to the DeviceManager
|
|
2167
1571
|
this.devices.set(device);
|
|
2168
1572
|
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}`);
|
|
2169
1573
|
}
|
|
2170
|
-
/**
|
|
2171
|
-
* Removes a MatterbridgeEndpoint from the specified plugin.
|
|
2172
|
-
*
|
|
2173
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2174
|
-
* @param {MatterbridgeEndpoint} device - The device to remove as a bridged endpoint.
|
|
2175
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been removed.
|
|
2176
|
-
*/
|
|
2177
1574
|
async removeBridgedEndpoint(pluginName, device) {
|
|
2178
1575
|
this.log.debug(`Removing bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
2179
|
-
// Check if the plugin is registered
|
|
2180
1576
|
const plugin = this.plugins.get(pluginName);
|
|
2181
1577
|
if (!plugin) {
|
|
2182
1578
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
2183
1579
|
return;
|
|
2184
1580
|
}
|
|
2185
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2186
1581
|
if (this.bridgeMode === 'bridge') {
|
|
2187
1582
|
if (!this.aggregatorNode) {
|
|
2188
1583
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
|
|
@@ -2197,7 +1592,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2197
1592
|
}
|
|
2198
1593
|
else if (this.bridgeMode === 'childbridge') {
|
|
2199
1594
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2200
|
-
// Nothing to do here since the server node has no aggregator node but only the device itself
|
|
2201
1595
|
}
|
|
2202
1596
|
else if (plugin.type === 'DynamicPlatform') {
|
|
2203
1597
|
if (!plugin.aggregatorNode) {
|
|
@@ -2211,7 +1605,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2211
1605
|
plugin.registeredDevices--;
|
|
2212
1606
|
if (plugin.addedDevices !== undefined)
|
|
2213
1607
|
plugin.addedDevices--;
|
|
2214
|
-
// Close the server node TODO check if this is correct
|
|
2215
1608
|
if (plugin.registeredDevices === 0 && plugin.addedDevices === 0) {
|
|
2216
1609
|
if (plugin.serverNode) {
|
|
2217
1610
|
await this.stopServerNode(plugin.serverNode);
|
|
@@ -2222,27 +1615,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
2222
1615
|
}
|
|
2223
1616
|
}
|
|
2224
1617
|
}
|
|
2225
|
-
// Remove the device from the DeviceManager
|
|
2226
1618
|
this.devices.remove(device);
|
|
2227
1619
|
}
|
|
2228
|
-
/**
|
|
2229
|
-
* Removes all bridged endpoints from the specified plugin.
|
|
2230
|
-
*
|
|
2231
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2232
|
-
* @returns {Promise<void>} A promise that resolves when all bridged endpoints have been removed.
|
|
2233
|
-
*/
|
|
2234
1620
|
async removeAllBridgedEndpoints(pluginName) {
|
|
2235
1621
|
this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}`);
|
|
2236
1622
|
for (const device of this.devices.array().filter((device) => device.plugin === pluginName)) {
|
|
2237
1623
|
await this.removeBridgedEndpoint(pluginName, device);
|
|
2238
1624
|
}
|
|
2239
1625
|
}
|
|
2240
|
-
/**
|
|
2241
|
-
* Sanitizes the fabric information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2242
|
-
*
|
|
2243
|
-
* @param {ExposedFabricInformation[]} fabricInfo - The array of exposed fabric information objects.
|
|
2244
|
-
* @returns {SanitizedExposedFabricInformation[]} An array of sanitized exposed fabric information objects.
|
|
2245
|
-
*/
|
|
2246
1626
|
sanitizeFabricInformations(fabricInfo) {
|
|
2247
1627
|
return fabricInfo.map((info) => {
|
|
2248
1628
|
return {
|
|
@@ -2256,12 +1636,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2256
1636
|
};
|
|
2257
1637
|
});
|
|
2258
1638
|
}
|
|
2259
|
-
/**
|
|
2260
|
-
* Sanitizes the session information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2261
|
-
*
|
|
2262
|
-
* @param {SessionInformation[]} sessionInfo - The array of session information objects.
|
|
2263
|
-
* @returns {SanitizedSessionInformation[]} An array of sanitized session information objects.
|
|
2264
|
-
*/
|
|
2265
1639
|
sanitizeSessionInformation(sessionInfo) {
|
|
2266
1640
|
return sessionInfo
|
|
2267
1641
|
.filter((session) => session.isPeerActive)
|
|
@@ -2289,51 +1663,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
2289
1663
|
};
|
|
2290
1664
|
});
|
|
2291
1665
|
}
|
|
2292
|
-
/**
|
|
2293
|
-
* Sets the reachability of a matter server node and trigger ReachableChanged event.
|
|
2294
|
-
*
|
|
2295
|
-
* @param {ServerNode<ServerNode.RootEndpoint>} serverNode - The commissioning server to set the reachability for.
|
|
2296
|
-
* @param {boolean} reachable - The new reachability status.
|
|
2297
|
-
*/
|
|
2298
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2299
1666
|
setServerNodeReachability(serverNode, reachable) {
|
|
2300
|
-
/*
|
|
2301
|
-
const basicInformationCluster = commissioningServer?.getRootClusterServer(BasicInformationCluster);
|
|
2302
|
-
if (basicInformationCluster && basicInformationCluster.attributes.reachable !== undefined) basicInformationCluster.setReachableAttribute(reachable);
|
|
2303
|
-
if (basicInformationCluster && basicInformationCluster.triggerReachableChangedEvent) basicInformationCluster.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
2304
|
-
*/
|
|
2305
1667
|
}
|
|
2306
|
-
/**
|
|
2307
|
-
* Sets the reachability of the specified matter aggregator and its bridged devices and trigger.
|
|
2308
|
-
* @param {EndpointNode<AggregatorEndpoint>} aggregatorNode - The matter aggregator to set the reachability for.
|
|
2309
|
-
* @param {boolean} reachable - A boolean indicating the reachability status to set.
|
|
2310
|
-
*/
|
|
2311
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2312
1668
|
setAggregatorReachability(aggregatorNode, reachable) {
|
|
2313
|
-
/*
|
|
2314
|
-
const basicInformationCluster = matterAggregator.getClusterServer(BasicInformationCluster);
|
|
2315
|
-
if (basicInformationCluster && basicInformationCluster.attributes.reachable !== undefined) basicInformationCluster.setReachableAttribute(reachable);
|
|
2316
|
-
if (basicInformationCluster && basicInformationCluster.triggerReachableChangedEvent) basicInformationCluster.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
2317
|
-
matterAggregator.getBridgedDevices().forEach((device) => {
|
|
2318
|
-
this.log.debug(`Setting reachability to true for bridged device: ${dev}${device.name}${nf}`);
|
|
2319
|
-
device.getClusterServer(BridgedDeviceBasicInformationCluster)?.setReachableAttribute(reachable);
|
|
2320
|
-
device.getClusterServer(BridgedDeviceBasicInformationCluster)?.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
2321
|
-
});
|
|
2322
|
-
*/
|
|
2323
1669
|
}
|
|
2324
|
-
/**
|
|
2325
|
-
* Sets the reachability of a device and trigger.
|
|
2326
|
-
*
|
|
2327
|
-
* @param {MatterbridgeEndpoint} device - The device to set the reachability for.
|
|
2328
|
-
* @param {boolean} reachable - The new reachability status of the device.
|
|
2329
|
-
*/
|
|
2330
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2331
1670
|
setDeviceReachability(device, reachable) {
|
|
2332
|
-
/*
|
|
2333
|
-
const basicInformationCluster = device.getClusterServer(BasicInformationCluster);
|
|
2334
|
-
if (basicInformationCluster && basicInformationCluster.attributes.reachable !== undefined) basicInformationCluster.setReachableAttribute(reachable);
|
|
2335
|
-
if (basicInformationCluster && basicInformationCluster.triggerReachableChangedEvent) basicInformationCluster.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
2336
|
-
*/
|
|
2337
1671
|
}
|
|
2338
1672
|
getVendorIdName = (vendorId) => {
|
|
2339
1673
|
if (!vendorId)
|
|
@@ -2376,36 +1710,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2376
1710
|
}
|
|
2377
1711
|
return vendorName;
|
|
2378
1712
|
};
|
|
2379
|
-
/**
|
|
2380
|
-
* Spawns a child process with the given command and arguments.
|
|
2381
|
-
* @param {string} command - The command to execute.
|
|
2382
|
-
* @param {string[]} args - The arguments to pass to the command (default: []).
|
|
2383
|
-
* @returns {Promise<boolean>} A promise that resolves when the child process exits successfully, or rejects if there is an error.
|
|
2384
|
-
*/
|
|
2385
1713
|
async spawnCommand(command, args = []) {
|
|
2386
|
-
/*
|
|
2387
|
-
npm > npm.cmd on windows
|
|
2388
|
-
cmd.exe ['dir'] on windows
|
|
2389
|
-
await this.spawnCommand('npm', ['install', '-g', 'matterbridge']);
|
|
2390
|
-
process.on('unhandledRejection', (reason, promise) => {
|
|
2391
|
-
this.log.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
2392
|
-
});
|
|
2393
|
-
|
|
2394
|
-
spawn - [14:27:21.125] [Matterbridge:spawn]: changed 38 packages in 4s
|
|
2395
|
-
spawn - [14:27:21.125] [Matterbridge:spawn]: 10 packages are looking for funding run `npm fund` for details
|
|
2396
|
-
debug - [14:27:21.131] [Matterbridge]: Child process exited with code 0 and signal null
|
|
2397
|
-
debug - [14:27:21.131] [Matterbridge]: Child process stdio streams have closed with code 0
|
|
2398
|
-
*/
|
|
2399
1714
|
const cmdLine = command + ' ' + args.join(' ');
|
|
2400
1715
|
if (process.platform === 'win32' && command === 'npm') {
|
|
2401
|
-
// Must be spawn('cmd.exe', ['/c', 'npm -g install <package>']);
|
|
2402
1716
|
const argstring = 'npm ' + args.join(' ');
|
|
2403
1717
|
args.splice(0, args.length, '/c', argstring);
|
|
2404
1718
|
command = 'cmd.exe';
|
|
2405
1719
|
}
|
|
2406
|
-
// Decide when using sudo on linux
|
|
2407
|
-
// When you need sudo: Spawn stderr: npm error Error: EACCES: permission denied
|
|
2408
|
-
// When you don't need sudo: Failed to start child process "npm install -g matterbridge-eve-door": spawn sudo ENOENT
|
|
2409
1720
|
if (hasParameter('sudo') || (process.platform === 'linux' && command === 'npm' && !hasParameter('docker') && !hasParameter('nosudo'))) {
|
|
2410
1721
|
args.unshift(command);
|
|
2411
1722
|
command = 'sudo';
|
|
@@ -2464,4 +1775,3 @@ export class Matterbridge extends EventEmitter {
|
|
|
2464
1775
|
});
|
|
2465
1776
|
}
|
|
2466
1777
|
}
|
|
2467
|
-
//# sourceMappingURL=matterbridge.js.map
|