matterbridge 2.1.3 → 2.1.4-dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -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 +57 -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 +6 -690
- package/dist/matterbridgeEndpointHelpers.js +16 -96
- package/dist/matterbridgePlatform.js +19 -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...');
|
|
@@ -1329,14 +1079,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1329
1079
|
this.initialized = false;
|
|
1330
1080
|
}
|
|
1331
1081
|
}
|
|
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
1082
|
async createAccessoryPlugin(plugin, device, start = false) {
|
|
1341
1083
|
if (!plugin.locked && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
|
|
1342
1084
|
plugin.locked = true;
|
|
@@ -1348,13 +1090,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1348
1090
|
await this.startServerNode(plugin.serverNode);
|
|
1349
1091
|
}
|
|
1350
1092
|
}
|
|
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
1093
|
async createDynamicPlugin(plugin, start = false) {
|
|
1359
1094
|
if (!plugin.locked) {
|
|
1360
1095
|
plugin.locked = true;
|
|
@@ -1366,13 +1101,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1366
1101
|
await this.startServerNode(plugin.serverNode);
|
|
1367
1102
|
}
|
|
1368
1103
|
}
|
|
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
1104
|
async startBridge() {
|
|
1375
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1376
1105
|
if (!this.matterStorageManager)
|
|
1377
1106
|
throw new Error('No storage manager initialized');
|
|
1378
1107
|
if (!this.matterbridgeContext)
|
|
@@ -1409,9 +1138,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1409
1138
|
clearInterval(this.startMatterInterval);
|
|
1410
1139
|
this.startMatterInterval = undefined;
|
|
1411
1140
|
this.log.debug('Cleared startMatterInterval interval for Matterbridge');
|
|
1412
|
-
// Start the Matter server node
|
|
1413
1141
|
this.startServerNode(this.serverNode);
|
|
1414
|
-
// Configure the plugins
|
|
1415
1142
|
this.configureTimeout = setTimeout(async () => {
|
|
1416
1143
|
for (const plugin of this.plugins) {
|
|
1417
1144
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1426,7 +1153,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1426
1153
|
}
|
|
1427
1154
|
this.frontend.wssSendRefreshRequired();
|
|
1428
1155
|
}, 30 * 1000);
|
|
1429
|
-
// Setting reachability to true
|
|
1430
1156
|
this.reachabilityTimeout = setTimeout(() => {
|
|
1431
1157
|
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1432
1158
|
if (this.serverNode)
|
|
@@ -1437,14 +1163,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1437
1163
|
}, 60 * 1000);
|
|
1438
1164
|
}, 1000);
|
|
1439
1165
|
}
|
|
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
1166
|
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
1167
|
if (!this.matterStorageManager)
|
|
1449
1168
|
throw new Error('No storage manager initialized');
|
|
1450
1169
|
for (const plugin of this.plugins) {
|
|
@@ -1491,13 +1210,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1491
1210
|
clearInterval(this.startMatterInterval);
|
|
1492
1211
|
this.startMatterInterval = undefined;
|
|
1493
1212
|
this.log.debug('Cleared startMatterInterval interval in childbridge mode');
|
|
1494
|
-
// Configure the plugins
|
|
1495
1213
|
this.configureTimeout = setTimeout(async () => {
|
|
1496
1214
|
for (const plugin of this.plugins) {
|
|
1497
1215
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
1498
1216
|
continue;
|
|
1499
1217
|
try {
|
|
1500
|
-
await this.plugins.configure(plugin);
|
|
1218
|
+
await this.plugins.configure(plugin);
|
|
1501
1219
|
}
|
|
1502
1220
|
catch (error) {
|
|
1503
1221
|
plugin.error = true;
|
|
@@ -1525,9 +1243,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1525
1243
|
this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
|
|
1526
1244
|
continue;
|
|
1527
1245
|
}
|
|
1528
|
-
// Start the Matter server node
|
|
1529
1246
|
this.startServerNode(plugin.serverNode);
|
|
1530
|
-
// Setting reachability to true
|
|
1531
1247
|
plugin.reachabilityTimeout = setTimeout(() => {
|
|
1532
1248
|
this.log.info(`Setting reachability to true for ${plg}${plugin.name}${db}`);
|
|
1533
1249
|
if (plugin.serverNode)
|
|
@@ -1541,219 +1257,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
1541
1257
|
}
|
|
1542
1258
|
}, 1000);
|
|
1543
1259
|
}
|
|
1544
|
-
/**
|
|
1545
|
-
* Starts the Matterbridge controller.
|
|
1546
|
-
* @private
|
|
1547
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1548
|
-
*/
|
|
1549
1260
|
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
1261
|
}
|
|
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
1262
|
async startMatterStorage() {
|
|
1756
|
-
// Setup Matter storage
|
|
1757
1263
|
this.log.info(`Starting matter node storage...`);
|
|
1758
1264
|
this.matterStorageService = this.environment.get(StorageService);
|
|
1759
1265
|
this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
|
|
@@ -1761,25 +1267,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1761
1267
|
this.log.info('Matter node storage manager "Matterbridge" created');
|
|
1762
1268
|
this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', bridge.code, this.aggregatorVendorId, 'Matterbridge', this.aggregatorProductId, 'Matterbridge aggregator');
|
|
1763
1269
|
this.log.info('Matter node storage started');
|
|
1764
|
-
// Backup matter storage since it is created/opened correctly
|
|
1765
1270
|
await this.backupMatterStorage(path.join(this.matterbridgeDirectory, this.matterStorageName), path.join(this.matterbridgeDirectory, this.matterStorageName + '.backup'));
|
|
1766
1271
|
}
|
|
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
1272
|
async backupMatterStorage(storageName, backupName) {
|
|
1775
1273
|
this.log.info('Creating matter node storage backup...');
|
|
1776
1274
|
await copyDirectory(storageName, backupName);
|
|
1777
1275
|
this.log.info('Created matter node storage backup');
|
|
1778
1276
|
}
|
|
1779
|
-
/**
|
|
1780
|
-
* Stops the matter storage.
|
|
1781
|
-
* @returns {Promise<void>} A promise that resolves when the storage is stopped.
|
|
1782
|
-
*/
|
|
1783
1277
|
async stopMatterStorage() {
|
|
1784
1278
|
this.log.info('Closing matter node storage...');
|
|
1785
1279
|
this.matterStorageManager?.close();
|
|
@@ -1788,19 +1282,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1788
1282
|
this.matterbridgeContext = undefined;
|
|
1789
1283
|
this.log.info('Matter node storage closed');
|
|
1790
1284
|
}
|
|
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
1285
|
async createServerNodeContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber) {
|
|
1805
1286
|
if (!this.matterStorageService)
|
|
1806
1287
|
throw new Error('No storage service initialized');
|
|
@@ -1833,15 +1314,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1833
1314
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1834
1315
|
return storageContext;
|
|
1835
1316
|
}
|
|
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
1317
|
async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
|
|
1846
1318
|
const storeId = await storageContext.get('storeId');
|
|
1847
1319
|
this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
|
|
@@ -1851,33 +1323,21 @@ export class Matterbridge extends EventEmitter {
|
|
|
1851
1323
|
this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
|
|
1852
1324
|
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
1853
1325
|
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
1326
|
const serverNode = await ServerNode.create({
|
|
1858
|
-
// Required: Give the Node a unique ID which is used to store the state of this node
|
|
1859
1327
|
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
1328
|
network: {
|
|
1863
1329
|
listeningAddressIpv4: this.ipv4address,
|
|
1864
1330
|
listeningAddressIpv6: this.ipv6address,
|
|
1865
1331
|
port,
|
|
1866
1332
|
},
|
|
1867
|
-
// Provide Commissioning relevant settings
|
|
1868
|
-
// Optional for development/testing purposes
|
|
1869
1333
|
commissioning: {
|
|
1870
1334
|
passcode,
|
|
1871
1335
|
discriminator,
|
|
1872
1336
|
},
|
|
1873
|
-
// Provide Node announcement settings
|
|
1874
|
-
// Optional: If Ommitted some development defaults are used
|
|
1875
1337
|
productDescription: {
|
|
1876
1338
|
name: await storageContext.get('deviceName'),
|
|
1877
1339
|
deviceType: DeviceTypeId(await storageContext.get('deviceType')),
|
|
1878
1340
|
},
|
|
1879
|
-
// Provide defaults for the BasicInformation cluster on the Root endpoint
|
|
1880
|
-
// Optional: If Omitted some development defaults are used
|
|
1881
1341
|
basicInformation: {
|
|
1882
1342
|
vendorId: VendorId(await storageContext.get('vendorId')),
|
|
1883
1343
|
vendorName: await storageContext.get('vendorName'),
|
|
@@ -1894,13 +1354,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1894
1354
|
},
|
|
1895
1355
|
});
|
|
1896
1356
|
const sanitizeFabrics = (fabrics, resetSessions = false) => {
|
|
1897
|
-
// New type of fabric information: Record<FabricIndex, ExposedFabricInformation>
|
|
1898
1357
|
const sanitizedFabrics = this.sanitizeFabricInformations(Array.from(Object.values(fabrics)));
|
|
1899
1358
|
this.log.info(`Fabrics: ${debugStringify(sanitizedFabrics)}`);
|
|
1900
1359
|
if (this.bridgeMode === 'bridge') {
|
|
1901
1360
|
this.matterbridgeFabricInformations = sanitizedFabrics;
|
|
1902
1361
|
if (resetSessions)
|
|
1903
|
-
this.matterbridgeSessionInformations = undefined;
|
|
1362
|
+
this.matterbridgeSessionInformations = undefined;
|
|
1904
1363
|
this.matterbridgePaired = true;
|
|
1905
1364
|
}
|
|
1906
1365
|
if (this.bridgeMode === 'childbridge') {
|
|
@@ -1908,19 +1367,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1908
1367
|
if (plugin) {
|
|
1909
1368
|
plugin.fabricInformations = sanitizedFabrics;
|
|
1910
1369
|
if (resetSessions)
|
|
1911
|
-
plugin.sessionInformations = undefined;
|
|
1370
|
+
plugin.sessionInformations = undefined;
|
|
1912
1371
|
plugin.paired = true;
|
|
1913
1372
|
}
|
|
1914
1373
|
}
|
|
1915
1374
|
};
|
|
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
1375
|
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
1376
|
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
1377
|
serverNode.lifecycle.online.on(async () => {
|
|
1925
1378
|
this.log.notice(`Server node for ${storeId} is online`);
|
|
1926
1379
|
if (!serverNode.lifecycle.isCommissioned) {
|
|
@@ -1966,7 +1419,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1966
1419
|
}
|
|
1967
1420
|
this.frontend.wssSendRefreshRequired();
|
|
1968
1421
|
});
|
|
1969
|
-
/** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
|
|
1970
1422
|
serverNode.lifecycle.offline.on(() => {
|
|
1971
1423
|
this.log.notice(`Server node for ${storeId} is offline`);
|
|
1972
1424
|
if (this.bridgeMode === 'bridge') {
|
|
@@ -1988,10 +1440,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1988
1440
|
}
|
|
1989
1441
|
this.frontend.wssSendRefreshRequired();
|
|
1990
1442
|
});
|
|
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
1443
|
serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
|
|
1996
1444
|
let action = '';
|
|
1997
1445
|
switch (fabricAction) {
|
|
@@ -2025,24 +1473,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
2025
1473
|
}
|
|
2026
1474
|
}
|
|
2027
1475
|
};
|
|
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
1476
|
serverNode.events.sessions.opened.on((session) => {
|
|
2033
1477
|
this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2034
1478
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
2035
1479
|
this.frontend.wssSendRefreshRequired();
|
|
2036
1480
|
});
|
|
2037
|
-
/**
|
|
2038
|
-
* This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
|
|
2039
|
-
*/
|
|
2040
1481
|
serverNode.events.sessions.closed.on((session) => {
|
|
2041
1482
|
this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2042
1483
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
2043
1484
|
this.frontend.wssSendRefreshRequired();
|
|
2044
1485
|
});
|
|
2045
|
-
/** This event is triggered when a subscription gets added or removed on an operative session. */
|
|
2046
1486
|
serverNode.events.sessions.subscriptionsChanged.on((session) => {
|
|
2047
1487
|
this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2048
1488
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
@@ -2051,61 +1491,38 @@ export class Matterbridge extends EventEmitter {
|
|
|
2051
1491
|
this.log.info(`Created server node for ${storeId}`);
|
|
2052
1492
|
return serverNode;
|
|
2053
1493
|
}
|
|
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
1494
|
async startServerNode(matterServerNode) {
|
|
2061
1495
|
if (!matterServerNode)
|
|
2062
1496
|
return;
|
|
2063
1497
|
this.log.notice(`Starting ${matterServerNode.id} server node`);
|
|
2064
1498
|
await matterServerNode.start();
|
|
2065
1499
|
}
|
|
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
1500
|
async stopServerNode(matterServerNode) {
|
|
2073
1501
|
if (!matterServerNode)
|
|
2074
1502
|
return;
|
|
2075
1503
|
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
1504
|
const withTimeout = (promise, ms) => {
|
|
2082
1505
|
return new Promise((resolve, reject) => {
|
|
2083
1506
|
const timer = setTimeout(() => reject(new Error('Operation timed out')), ms);
|
|
2084
1507
|
promise
|
|
2085
1508
|
.then((result) => {
|
|
2086
|
-
clearTimeout(timer);
|
|
1509
|
+
clearTimeout(timer);
|
|
2087
1510
|
resolve(result);
|
|
2088
1511
|
})
|
|
2089
1512
|
.catch((error) => {
|
|
2090
|
-
clearTimeout(timer);
|
|
1513
|
+
clearTimeout(timer);
|
|
2091
1514
|
reject(error);
|
|
2092
1515
|
});
|
|
2093
1516
|
});
|
|
2094
1517
|
};
|
|
2095
1518
|
try {
|
|
2096
|
-
await withTimeout(matterServerNode.close(), 5000);
|
|
1519
|
+
await withTimeout(matterServerNode.close(), 5000);
|
|
2097
1520
|
this.log.info(`Closed ${matterServerNode.id} server node`);
|
|
2098
1521
|
}
|
|
2099
1522
|
catch (error) {
|
|
2100
1523
|
this.log.error(`Failed to close ${matterServerNode.id} server node: ${error instanceof Error ? error.message : error}`);
|
|
2101
1524
|
}
|
|
2102
1525
|
}
|
|
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
1526
|
async advertiseServerNode(matterServerNode) {
|
|
2110
1527
|
if (matterServerNode && matterServerNode.lifecycle.isCommissioned) {
|
|
2111
1528
|
await matterServerNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
|
|
@@ -2115,32 +1532,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
2115
1532
|
}
|
|
2116
1533
|
return undefined;
|
|
2117
1534
|
}
|
|
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
1535
|
async createAggregatorNode(storageContext) {
|
|
2125
1536
|
this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator `);
|
|
2126
1537
|
const aggregatorNode = new EndpointNode(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
|
|
2127
1538
|
return aggregatorNode;
|
|
2128
1539
|
}
|
|
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
1540
|
async addBridgedEndpoint(pluginName, device) {
|
|
2137
|
-
// Check if the plugin is registered
|
|
2138
1541
|
const plugin = this.plugins.get(pluginName);
|
|
2139
1542
|
if (!plugin) {
|
|
2140
1543
|
this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
|
|
2141
1544
|
return;
|
|
2142
1545
|
}
|
|
2143
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2144
1546
|
if (this.bridgeMode === 'bridge') {
|
|
2145
1547
|
this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
|
|
2146
1548
|
if (!this.aggregatorNode)
|
|
@@ -2163,26 +1565,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
2163
1565
|
plugin.registeredDevices++;
|
|
2164
1566
|
if (plugin.addedDevices !== undefined)
|
|
2165
1567
|
plugin.addedDevices++;
|
|
2166
|
-
// Add the device to the DeviceManager
|
|
2167
1568
|
this.devices.set(device);
|
|
2168
1569
|
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
1570
|
}
|
|
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
1571
|
async removeBridgedEndpoint(pluginName, device) {
|
|
2178
1572
|
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
1573
|
const plugin = this.plugins.get(pluginName);
|
|
2181
1574
|
if (!plugin) {
|
|
2182
1575
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
2183
1576
|
return;
|
|
2184
1577
|
}
|
|
2185
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2186
1578
|
if (this.bridgeMode === 'bridge') {
|
|
2187
1579
|
if (!this.aggregatorNode) {
|
|
2188
1580
|
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 +1589,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2197
1589
|
}
|
|
2198
1590
|
else if (this.bridgeMode === 'childbridge') {
|
|
2199
1591
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2200
|
-
// Nothing to do here since the server node has no aggregator node but only the device itself
|
|
2201
1592
|
}
|
|
2202
1593
|
else if (plugin.type === 'DynamicPlatform') {
|
|
2203
1594
|
if (!plugin.aggregatorNode) {
|
|
@@ -2211,7 +1602,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2211
1602
|
plugin.registeredDevices--;
|
|
2212
1603
|
if (plugin.addedDevices !== undefined)
|
|
2213
1604
|
plugin.addedDevices--;
|
|
2214
|
-
// Close the server node TODO check if this is correct
|
|
2215
1605
|
if (plugin.registeredDevices === 0 && plugin.addedDevices === 0) {
|
|
2216
1606
|
if (plugin.serverNode) {
|
|
2217
1607
|
await this.stopServerNode(plugin.serverNode);
|
|
@@ -2222,27 +1612,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
2222
1612
|
}
|
|
2223
1613
|
}
|
|
2224
1614
|
}
|
|
2225
|
-
// Remove the device from the DeviceManager
|
|
2226
1615
|
this.devices.remove(device);
|
|
2227
1616
|
}
|
|
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
1617
|
async removeAllBridgedEndpoints(pluginName) {
|
|
2235
1618
|
this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}`);
|
|
2236
1619
|
for (const device of this.devices.array().filter((device) => device.plugin === pluginName)) {
|
|
2237
1620
|
await this.removeBridgedEndpoint(pluginName, device);
|
|
2238
1621
|
}
|
|
2239
1622
|
}
|
|
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
1623
|
sanitizeFabricInformations(fabricInfo) {
|
|
2247
1624
|
return fabricInfo.map((info) => {
|
|
2248
1625
|
return {
|
|
@@ -2256,12 +1633,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2256
1633
|
};
|
|
2257
1634
|
});
|
|
2258
1635
|
}
|
|
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
1636
|
sanitizeSessionInformation(sessionInfo) {
|
|
2266
1637
|
return sessionInfo
|
|
2267
1638
|
.filter((session) => session.isPeerActive)
|
|
@@ -2289,51 +1660,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
2289
1660
|
};
|
|
2290
1661
|
});
|
|
2291
1662
|
}
|
|
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
1663
|
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
1664
|
}
|
|
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
1665
|
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
1666
|
}
|
|
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
1667
|
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
1668
|
}
|
|
2338
1669
|
getVendorIdName = (vendorId) => {
|
|
2339
1670
|
if (!vendorId)
|
|
@@ -2376,36 +1707,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2376
1707
|
}
|
|
2377
1708
|
return vendorName;
|
|
2378
1709
|
};
|
|
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
1710
|
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
1711
|
const cmdLine = command + ' ' + args.join(' ');
|
|
2400
1712
|
if (process.platform === 'win32' && command === 'npm') {
|
|
2401
|
-
// Must be spawn('cmd.exe', ['/c', 'npm -g install <package>']);
|
|
2402
1713
|
const argstring = 'npm ' + args.join(' ');
|
|
2403
1714
|
args.splice(0, args.length, '/c', argstring);
|
|
2404
1715
|
command = 'cmd.exe';
|
|
2405
1716
|
}
|
|
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
1717
|
if (hasParameter('sudo') || (process.platform === 'linux' && command === 'npm' && !hasParameter('docker') && !hasParameter('nosudo'))) {
|
|
2410
1718
|
args.unshift(command);
|
|
2411
1719
|
command = 'sudo';
|
|
@@ -2464,4 +1772,3 @@ export class Matterbridge extends EventEmitter {
|
|
|
2464
1772
|
});
|
|
2465
1773
|
}
|
|
2466
1774
|
}
|
|
2467
|
-
//# sourceMappingURL=matterbridge.js.map
|