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