matterbridge 1.6.5 → 1.6.6-dev.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/dist/cli.js +0 -26
- package/dist/cluster/export.js +0 -2
- package/dist/defaultConfigSchema.js +0 -23
- package/dist/deviceManager.js +1 -26
- package/dist/index.js +0 -30
- package/dist/logger/export.js +0 -1
- package/dist/matter/export.js +0 -1
- package/dist/matterbridge.js +62 -702
- package/dist/matterbridgeAccessoryPlatform.js +0 -33
- package/dist/matterbridgeBehaviors.js +1 -29
- package/dist/matterbridgeDevice.js +39 -933
- package/dist/matterbridgeDeviceTypes.js +21 -41
- package/dist/matterbridgeDynamicPlatform.js +0 -33
- package/dist/matterbridgeEdge.js +0 -525
- package/dist/matterbridgeEndpoint.js +61 -1027
- package/dist/matterbridgePlatform.js +3 -74
- package/dist/matterbridgeTypes.js +0 -24
- package/dist/matterbridgeWebsocket.js +0 -45
- package/dist/pluginManager.js +3 -237
- package/dist/storage/export.js +0 -1
- package/dist/utils/colorUtils.js +2 -78
- package/dist/utils/export.js +0 -1
- package/dist/utils/utils.js +7 -252
- package/npm-shrinkwrap.json +12 -15
- package/package.json +2 -2
- package/dist/cli.d.ts +0 -25
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cluster/export.d.ts +0 -2
- package/dist/cluster/export.d.ts.map +0 -1
- package/dist/cluster/export.js.map +0 -1
- package/dist/defaultConfigSchema.d.ts +0 -27
- package/dist/defaultConfigSchema.d.ts.map +0 -1
- package/dist/defaultConfigSchema.js.map +0 -1
- package/dist/deviceManager.d.ts +0 -46
- package/dist/deviceManager.d.ts.map +0 -1
- package/dist/deviceManager.js.map +0 -1
- package/dist/index.d.ts +0 -40
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/logger/export.d.ts +0 -2
- package/dist/logger/export.d.ts.map +0 -1
- package/dist/logger/export.js.map +0 -1
- package/dist/matter/export.d.ts +0 -5
- package/dist/matter/export.d.ts.map +0 -1
- package/dist/matter/export.js.map +0 -1
- package/dist/matterbridge.d.ts +0 -466
- package/dist/matterbridge.d.ts.map +0 -1
- package/dist/matterbridge.js.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.d.ts +0 -39
- package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
- package/dist/matterbridgeBehaviors.d.ts +0 -934
- package/dist/matterbridgeBehaviors.d.ts.map +0 -1
- package/dist/matterbridgeBehaviors.js.map +0 -1
- package/dist/matterbridgeDevice.d.ts +0 -6504
- package/dist/matterbridgeDevice.d.ts.map +0 -1
- package/dist/matterbridgeDevice.js.map +0 -1
- package/dist/matterbridgeDeviceTypes.d.ts +0 -65
- package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
- package/dist/matterbridgeDeviceTypes.js.map +0 -1
- package/dist/matterbridgeDynamicPlatform.d.ts +0 -39
- package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
- package/dist/matterbridgeDynamicPlatform.js.map +0 -1
- package/dist/matterbridgeEdge.d.ts +0 -89
- package/dist/matterbridgeEdge.d.ts.map +0 -1
- package/dist/matterbridgeEdge.js.map +0 -1
- package/dist/matterbridgeEndpoint.d.ts +0 -8529
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts +0 -96
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts +0 -147
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/matterbridgeWebsocket.d.ts +0 -49
- package/dist/matterbridgeWebsocket.d.ts.map +0 -1
- package/dist/matterbridgeWebsocket.js.map +0 -1
- package/dist/pluginManager.d.ts +0 -238
- package/dist/pluginManager.d.ts.map +0 -1
- package/dist/pluginManager.js.map +0 -1
- package/dist/storage/export.d.ts +0 -2
- package/dist/storage/export.d.ts.map +0 -1
- package/dist/storage/export.js.map +0 -1
- package/dist/utils/colorUtils.d.ts +0 -61
- package/dist/utils/colorUtils.d.ts.map +0 -1
- package/dist/utils/colorUtils.js.map +0 -1
- package/dist/utils/export.d.ts +0 -3
- package/dist/utils/export.d.ts.map +0 -1
- package/dist/utils/export.js.map +0 -1
- package/dist/utils/utils.d.ts +0 -221
- package/dist/utils/utils.d.ts.map +0 -1
- package/dist/utils/utils.js.map +0 -1
package/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';
|
|
@@ -29,35 +6,26 @@ import EventEmitter from 'events';
|
|
|
29
6
|
import os from 'os';
|
|
30
7
|
import path from 'path';
|
|
31
8
|
import { randomBytes } from 'crypto';
|
|
32
|
-
// Package modules
|
|
33
9
|
import https from 'https';
|
|
34
10
|
import express from 'express';
|
|
35
11
|
import WebSocket, { WebSocketServer } from 'ws';
|
|
36
|
-
// NodeStorage and AnsiLogger modules
|
|
37
12
|
import { NodeStorageManager } from 'node-persist-manager';
|
|
38
13
|
import { AnsiLogger, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, stringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN, nt, idn, or, hk, BLUE } from 'node-ansi-logger';
|
|
39
|
-
// Matterbridge
|
|
40
14
|
import { MatterbridgeDevice } from './matterbridgeDevice.js';
|
|
41
15
|
import { WS_ID_LOG, WS_ID_REFRESH_NEEDED, WS_ID_RESTART_NEEDED, wsMessageHandler } from './matterbridgeWebsocket.js';
|
|
42
16
|
import { logInterfaces, wait, waiter, createZip, copyDirectory, getParameter, getIntParameter, hasParameter } from './utils/utils.js';
|
|
43
17
|
import { PluginManager } from './pluginManager.js';
|
|
44
18
|
import { DeviceManager } from './deviceManager.js';
|
|
45
19
|
import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
|
|
46
|
-
// @matter
|
|
47
20
|
import { DeviceTypeId, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, VendorId, StorageManager, EndpointServer } from '@matter/main';
|
|
48
21
|
import { BasicInformationCluster, BridgedDeviceBasicInformation, BridgedDeviceBasicInformationCluster, FixedLabelCluster, GeneralCommissioning, PowerSourceCluster, SwitchCluster, ThreadNetworkDiagnosticsCluster, UserLabelCluster, } from '@matter/main/clusters';
|
|
49
22
|
import { getClusterNameById, ManualPairingCodeCodec, QrCodeSchema } from '@matter/main/types';
|
|
50
23
|
import { StorageBackendDisk, StorageBackendJsonFile } from '@matter/nodejs';
|
|
51
|
-
// @project-chip
|
|
52
24
|
import { CommissioningController, CommissioningServer, MatterServer } from '@project-chip/matter.js';
|
|
53
25
|
import { Aggregator, DeviceTypes, NodeStateInformation } from '@project-chip/matter.js/device';
|
|
54
|
-
// Default colors
|
|
55
26
|
const plg = '\u001B[38;5;33m';
|
|
56
27
|
const dev = '\u001B[38;5;79m';
|
|
57
28
|
const typ = '\u001B[38;5;207m';
|
|
58
|
-
/**
|
|
59
|
-
* Represents the Matterbridge application.
|
|
60
|
-
*/
|
|
61
29
|
export class Matterbridge extends EventEmitter {
|
|
62
30
|
systemInformation = {
|
|
63
31
|
interfaceName: '',
|
|
@@ -93,7 +61,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
93
61
|
restartMode: '',
|
|
94
62
|
edge: hasParameter('edge'),
|
|
95
63
|
profile: getParameter('profile'),
|
|
96
|
-
loggerLevel: "info"
|
|
64
|
+
loggerLevel: "info",
|
|
97
65
|
fileLogger: false,
|
|
98
66
|
matterLoggerLevel: MatterLogLevel.INFO,
|
|
99
67
|
matterFileLogger: false,
|
|
@@ -129,7 +97,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
129
97
|
nodeContext;
|
|
130
98
|
matterStorageName = 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.json';
|
|
131
99
|
nodeStorageName = 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
|
|
132
|
-
// Cleanup
|
|
133
100
|
hasCleanupStarted = false;
|
|
134
101
|
initialized = false;
|
|
135
102
|
execRunningCount = 0;
|
|
@@ -141,18 +108,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
141
108
|
sigtermHandler;
|
|
142
109
|
exceptionHandler;
|
|
143
110
|
rejectionHandler;
|
|
144
|
-
// Frontend
|
|
145
111
|
expressApp;
|
|
146
112
|
httpServer;
|
|
147
113
|
httpsServer;
|
|
148
114
|
webSocketServer;
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
discriminator; // first commissioning server discriminator
|
|
115
|
+
mdnsInterface;
|
|
116
|
+
ipv4address;
|
|
117
|
+
ipv6address;
|
|
118
|
+
port = 5540;
|
|
119
|
+
passcode;
|
|
120
|
+
discriminator;
|
|
156
121
|
storageManager;
|
|
157
122
|
matterbridgeContext;
|
|
158
123
|
mattercontrollerContext;
|
|
@@ -163,26 +128,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
163
128
|
aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
|
|
164
129
|
aggregatorProductId = getIntParameter('productId') ?? 0x8000;
|
|
165
130
|
static instance;
|
|
166
|
-
// We load asyncronously so is private
|
|
167
131
|
constructor() {
|
|
168
132
|
super();
|
|
169
|
-
// Bind the handler to the instance
|
|
170
133
|
this.matterbridgeMessageHandler = wsMessageHandler.bind(this);
|
|
171
134
|
}
|
|
172
135
|
matterbridgeMessageHandler;
|
|
173
|
-
/** ***********************************************************************************************************************************/
|
|
174
|
-
/** loadInstance() and cleanup() methods */
|
|
175
|
-
/** ***********************************************************************************************************************************/
|
|
176
|
-
/**
|
|
177
|
-
* Loads an instance of the Matterbridge class.
|
|
178
|
-
* If an instance already exists, return that instance.
|
|
179
|
-
*
|
|
180
|
-
* @param initialize - Whether to initialize the Matterbridge instance after loading.
|
|
181
|
-
* @returns The loaded Matterbridge instance.
|
|
182
|
-
*/
|
|
183
136
|
static async loadInstance(initialize = false) {
|
|
184
137
|
if (!Matterbridge.instance) {
|
|
185
|
-
// eslint-disable-next-line no-console
|
|
186
138
|
if (hasParameter('debug'))
|
|
187
139
|
console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
|
|
188
140
|
Matterbridge.instance = new Matterbridge();
|
|
@@ -191,11 +143,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
191
143
|
}
|
|
192
144
|
return Matterbridge.instance;
|
|
193
145
|
}
|
|
194
|
-
/**
|
|
195
|
-
* Call cleanup().
|
|
196
|
-
* @deprecated This method is deprecated and is only used for jest tests.
|
|
197
|
-
*
|
|
198
|
-
*/
|
|
199
146
|
async destroyInstance() {
|
|
200
147
|
await this.cleanup('destroying instance...', false);
|
|
201
148
|
await waiter('destroying instance...', () => {
|
|
@@ -203,66 +150,42 @@ export class Matterbridge extends EventEmitter {
|
|
|
203
150
|
}, false, 60000, 100, false);
|
|
204
151
|
await wait(1000, 'Wait for the global node_modules and matterbridge version', false);
|
|
205
152
|
}
|
|
206
|
-
/**
|
|
207
|
-
* Initializes the Matterbridge application.
|
|
208
|
-
*
|
|
209
|
-
* @remarks
|
|
210
|
-
* This method performs the necessary setup and initialization steps for the Matterbridge application.
|
|
211
|
-
* It displays the help information if the 'help' parameter is provided, sets up the logger, checks the
|
|
212
|
-
* node version, registers signal handlers, initializes storage, and parses the command line.
|
|
213
|
-
*
|
|
214
|
-
* @returns A Promise that resolves when the initialization is complete.
|
|
215
|
-
*/
|
|
216
153
|
async initialize() {
|
|
217
|
-
// Set the first port to use for the commissioning server (will be incremented in childbridge mode)
|
|
218
154
|
this.port = getIntParameter('port') ?? 5540;
|
|
219
|
-
// Set the first passcode to use for the commissioning server (will be incremented in childbridge mode)
|
|
220
155
|
this.passcode = getIntParameter('passcode');
|
|
221
|
-
// Set the first discriminator to use for the commissioning server (will be incremented in childbridge mode)
|
|
222
156
|
this.discriminator = getIntParameter('discriminator');
|
|
223
|
-
// Set the restart mode
|
|
224
157
|
if (hasParameter('service'))
|
|
225
158
|
this.restartMode = 'service';
|
|
226
159
|
if (hasParameter('docker'))
|
|
227
160
|
this.restartMode = 'docker';
|
|
228
|
-
// Set the matterbridge directory
|
|
229
161
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
230
162
|
this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
|
|
231
|
-
|
|
232
|
-
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
|
|
233
|
-
// Initialize nodeStorage and nodeContext
|
|
163
|
+
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
234
164
|
try {
|
|
235
165
|
this.log.debug(`Creating node storage manager: ${CYAN}${this.nodeStorageName}${db}`);
|
|
236
166
|
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, this.nodeStorageName), writeQueue: false, expiredInterval: undefined, logging: false });
|
|
237
167
|
this.log.debug('Creating node storage context for matterbridge');
|
|
238
168
|
this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
|
|
239
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
240
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
241
169
|
const keys = (await this.nodeStorage?.storage.keys());
|
|
242
170
|
for (const key of keys) {
|
|
243
171
|
this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
|
|
244
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
245
172
|
await this.nodeStorage?.storage.get(key);
|
|
246
173
|
}
|
|
247
174
|
const storages = await this.nodeStorage.getStorageNames();
|
|
248
175
|
for (const storage of storages) {
|
|
249
176
|
this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
|
|
250
177
|
const nodeContext = await this.nodeStorage?.createStorage(storage);
|
|
251
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
252
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
253
178
|
const keys = (await nodeContext?.storage.keys());
|
|
254
179
|
keys.forEach(async (key) => {
|
|
255
180
|
this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
|
|
256
181
|
await nodeContext?.get(key);
|
|
257
182
|
});
|
|
258
183
|
}
|
|
259
|
-
// Creating a backup of the node storage since it is not corrupted
|
|
260
184
|
this.log.debug('Creating node storage backup...');
|
|
261
185
|
await copyDirectory(path.join(this.matterbridgeDirectory, this.nodeStorageName), path.join(this.matterbridgeDirectory, this.nodeStorageName + '.backup'));
|
|
262
186
|
this.log.debug('Created node storage backup');
|
|
263
187
|
}
|
|
264
188
|
catch (error) {
|
|
265
|
-
// Restoring the backup of the node storage since it is corrupted
|
|
266
189
|
this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
|
|
267
190
|
if (hasParameter('norestore')) {
|
|
268
191
|
this.log.fatal(`The matterbridge node storage is corrupted. Parameter -norestore found: exiting...`);
|
|
@@ -277,44 +200,41 @@ export class Matterbridge extends EventEmitter {
|
|
|
277
200
|
this.log.fatal('Fatal error creating node storage manager and context for matterbridge');
|
|
278
201
|
throw new Error('Fatal error creating node storage manager and context for matterbridge');
|
|
279
202
|
}
|
|
280
|
-
// Set matterbridge logger level (context: matterbridgeLogLevel)
|
|
281
203
|
if (hasParameter('logger')) {
|
|
282
204
|
const level = getParameter('logger');
|
|
283
205
|
if (level === 'debug') {
|
|
284
|
-
this.log.logLevel = "debug"
|
|
206
|
+
this.log.logLevel = "debug";
|
|
285
207
|
}
|
|
286
208
|
else if (level === 'info') {
|
|
287
|
-
this.log.logLevel = "info"
|
|
209
|
+
this.log.logLevel = "info";
|
|
288
210
|
}
|
|
289
211
|
else if (level === 'notice') {
|
|
290
|
-
this.log.logLevel = "notice"
|
|
212
|
+
this.log.logLevel = "notice";
|
|
291
213
|
}
|
|
292
214
|
else if (level === 'warn') {
|
|
293
|
-
this.log.logLevel = "warn"
|
|
215
|
+
this.log.logLevel = "warn";
|
|
294
216
|
}
|
|
295
217
|
else if (level === 'error') {
|
|
296
|
-
this.log.logLevel = "error"
|
|
218
|
+
this.log.logLevel = "error";
|
|
297
219
|
}
|
|
298
220
|
else if (level === 'fatal') {
|
|
299
|
-
this.log.logLevel = "fatal"
|
|
221
|
+
this.log.logLevel = "fatal";
|
|
300
222
|
}
|
|
301
223
|
else {
|
|
302
224
|
this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
|
|
303
|
-
this.log.logLevel = "info"
|
|
225
|
+
this.log.logLevel = "info";
|
|
304
226
|
}
|
|
305
227
|
}
|
|
306
228
|
else {
|
|
307
|
-
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', "info"
|
|
229
|
+
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', "info");
|
|
308
230
|
}
|
|
309
231
|
MatterbridgeDevice.logLevel = this.log.logLevel;
|
|
310
|
-
// Create the file logger for matterbridge (context: matterbridgeFileLog)
|
|
311
232
|
if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
|
|
312
233
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), this.log.logLevel, true);
|
|
313
234
|
this.matterbridgeInformation.fileLogger = true;
|
|
314
235
|
}
|
|
315
236
|
this.log.notice('Matterbridge is starting...');
|
|
316
237
|
this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.matterbridgeInformation.fileLogger}.`);
|
|
317
|
-
// Set matter.js logger level, format and logger (context: matterLogLevel)
|
|
318
238
|
if (hasParameter('matterlogger')) {
|
|
319
239
|
const level = getParameter('matterlogger');
|
|
320
240
|
if (level === 'debug') {
|
|
@@ -345,7 +265,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
345
265
|
}
|
|
346
266
|
Logger.format = MatterLogFormat.ANSI;
|
|
347
267
|
Logger.setLogger('default', this.createMatterLogger());
|
|
348
|
-
// Create the file logger for matter.js (context: matterFileLog)
|
|
349
268
|
if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
|
|
350
269
|
this.matterbridgeInformation.matterFileLogger = true;
|
|
351
270
|
Logger.addLogger('matterfilelogger', await this.createMatterFileLogger(path.join(this.matterbridgeDirectory, this.matterLoggerFile), true), {
|
|
@@ -354,7 +273,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
354
273
|
});
|
|
355
274
|
}
|
|
356
275
|
this.log.debug(`Matter logLevel: ${Logger.defaultLogLevel} fileLoger: ${this.matterbridgeInformation.matterFileLogger}.`);
|
|
357
|
-
// Set the interface to use for the matter server mdnsInterface
|
|
358
276
|
if (hasParameter('mdnsinterface')) {
|
|
359
277
|
this.mdnsInterface = getParameter('mdnsinterface');
|
|
360
278
|
}
|
|
@@ -363,7 +281,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
363
281
|
if (this.mdnsInterface === '')
|
|
364
282
|
this.mdnsInterface = undefined;
|
|
365
283
|
}
|
|
366
|
-
// Set the listeningAddressIpv4 for the matter commissioning server
|
|
367
284
|
if (hasParameter('ipv4address')) {
|
|
368
285
|
this.ipv4address = getParameter('ipv4address');
|
|
369
286
|
}
|
|
@@ -372,7 +289,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
372
289
|
if (this.ipv4address === '')
|
|
373
290
|
this.ipv4address = undefined;
|
|
374
291
|
}
|
|
375
|
-
// Set the listeningAddressIpv6 for the matter commissioning server
|
|
376
292
|
if (hasParameter('ipv6address')) {
|
|
377
293
|
this.ipv6address = getParameter('ipv6address');
|
|
378
294
|
}
|
|
@@ -381,23 +297,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
381
297
|
if (this.ipv6address === '')
|
|
382
298
|
this.ipv6address = undefined;
|
|
383
299
|
}
|
|
384
|
-
// Initialize PluginManager
|
|
385
300
|
this.plugins = new PluginManager(this);
|
|
386
301
|
await this.plugins.loadFromStorage();
|
|
387
|
-
// Initialize DeviceManager
|
|
388
302
|
this.devices = new DeviceManager(this, this.nodeContext);
|
|
389
|
-
// Get the plugins from node storage and create the plugins node storage contexts
|
|
390
303
|
for (const plugin of this.plugins) {
|
|
391
304
|
const packageJson = await this.plugins.parse(plugin);
|
|
392
305
|
if (packageJson === null && !hasParameter('add')) {
|
|
393
|
-
// Try to reinstall the plugin from npm (for Docker pull and external plugins)
|
|
394
|
-
// We don't do this when the add parameter is set because we shut down the process after adding the plugin
|
|
395
306
|
this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
|
|
396
307
|
try {
|
|
397
308
|
await this.spawnCommand('npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose']);
|
|
398
309
|
this.log.info(`Plugin ${plg}${plugin.name}${nf} reinstalled.`);
|
|
399
310
|
plugin.error = false;
|
|
400
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
401
311
|
}
|
|
402
312
|
catch (error) {
|
|
403
313
|
plugin.error = true;
|
|
@@ -414,7 +324,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
414
324
|
await plugin.nodeContext.set('description', plugin.description);
|
|
415
325
|
await plugin.nodeContext.set('author', plugin.author);
|
|
416
326
|
}
|
|
417
|
-
// Log system info and create .matterbridge directory
|
|
418
327
|
await this.logNodeAndSystemInfo();
|
|
419
328
|
this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
|
|
420
329
|
`${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
|
|
@@ -422,7 +331,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
422
331
|
`${hasParameter('controller') ? 'mode controller ' : ''}` +
|
|
423
332
|
`${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
|
|
424
333
|
`running on ${this.systemInformation.osType} (v.${this.systemInformation.osRelease}) platform ${this.systemInformation.osPlatform} arch ${this.systemInformation.osArch}`);
|
|
425
|
-
// Check node version and throw error
|
|
426
334
|
const minNodeVersion = 18;
|
|
427
335
|
const nodeVersion = process.versions.node;
|
|
428
336
|
const versionMajor = parseInt(nodeVersion.split('.')[0]);
|
|
@@ -430,17 +338,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
430
338
|
this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
431
339
|
throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
432
340
|
}
|
|
433
|
-
// Register process handlers
|
|
434
341
|
this.registerProcessHandlers();
|
|
435
|
-
// Parse command line
|
|
436
342
|
await this.parseCommandLine();
|
|
437
343
|
this.initialized = true;
|
|
438
344
|
}
|
|
439
|
-
/**
|
|
440
|
-
* Parses the command line arguments and performs the corresponding actions.
|
|
441
|
-
* @private
|
|
442
|
-
* @returns {Promise<void>} A promise that resolves when the command line arguments have been processed, or the process exits.
|
|
443
|
-
*/
|
|
444
345
|
async parseCommandLine() {
|
|
445
346
|
if (hasParameter('help')) {
|
|
446
347
|
this.log.info(`\nUsage: matterbridge [options]\n
|
|
@@ -548,14 +449,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
548
449
|
}
|
|
549
450
|
if (hasParameter('factoryreset')) {
|
|
550
451
|
try {
|
|
551
|
-
// Delete matter storage file
|
|
552
452
|
await fs.unlink(path.join(this.matterbridgeDirectory, this.matterStorageName));
|
|
553
453
|
}
|
|
554
454
|
catch (err) {
|
|
555
455
|
this.log.error(`Error deleting storage: ${err}`);
|
|
556
456
|
}
|
|
557
457
|
try {
|
|
558
|
-
// Delete node storage directory with its subdirectories
|
|
559
458
|
await fs.rm(path.join(this.matterbridgeDirectory, this.nodeStorageName), { recursive: true });
|
|
560
459
|
}
|
|
561
460
|
catch (err) {
|
|
@@ -569,7 +468,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
569
468
|
this.emit('shutdown');
|
|
570
469
|
return;
|
|
571
470
|
}
|
|
572
|
-
// Start the matter storage and create the matterbridge context
|
|
573
471
|
try {
|
|
574
472
|
await this.startMatterStorage('json', path.join(this.matterbridgeDirectory, this.matterStorageName));
|
|
575
473
|
}
|
|
@@ -604,34 +502,28 @@ export class Matterbridge extends EventEmitter {
|
|
|
604
502
|
this.emit('shutdown');
|
|
605
503
|
return;
|
|
606
504
|
}
|
|
607
|
-
// Initialize frontend
|
|
608
505
|
if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
|
|
609
506
|
await this.initializeFrontend(getIntParameter('frontend'));
|
|
610
|
-
// Check each 60 minutes the latest versions
|
|
611
507
|
this.checkUpdateInterval = setInterval(() => {
|
|
612
508
|
this.getMatterbridgeLatestVersion();
|
|
613
509
|
for (const plugin of this.plugins) {
|
|
614
510
|
this.getPluginLatestVersion(plugin);
|
|
615
511
|
}
|
|
616
512
|
}, 60 * 60 * 1000);
|
|
617
|
-
// Start the matterbridge in mode test
|
|
618
513
|
if (hasParameter('test')) {
|
|
619
514
|
this.bridgeMode = 'bridge';
|
|
620
515
|
MatterbridgeDevice.bridgeMode = 'bridge';
|
|
621
516
|
return;
|
|
622
517
|
}
|
|
623
|
-
// Start the matterbridge in mode controller
|
|
624
518
|
if (hasParameter('controller')) {
|
|
625
519
|
this.bridgeMode = 'controller';
|
|
626
520
|
await this.startController();
|
|
627
521
|
return;
|
|
628
522
|
}
|
|
629
|
-
// Check if the bridge mode is set and start matterbridge in bridge mode if not set
|
|
630
523
|
if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
|
|
631
524
|
this.log.info('Setting default matterbridge start mode to bridge');
|
|
632
525
|
await this.nodeContext?.set('bridgeMode', 'bridge');
|
|
633
526
|
}
|
|
634
|
-
// Start matterbridge in bridge mode
|
|
635
527
|
if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
|
|
636
528
|
this.bridgeMode = 'bridge';
|
|
637
529
|
MatterbridgeDevice.bridgeMode = 'bridge';
|
|
@@ -640,7 +532,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
640
532
|
await this.startBridge();
|
|
641
533
|
return;
|
|
642
534
|
}
|
|
643
|
-
// Start matterbridge in childbridge mode
|
|
644
535
|
if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
|
|
645
536
|
this.bridgeMode = 'childbridge';
|
|
646
537
|
MatterbridgeDevice.bridgeMode = 'childbridge';
|
|
@@ -650,28 +541,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
650
541
|
return;
|
|
651
542
|
}
|
|
652
543
|
}
|
|
653
|
-
/**
|
|
654
|
-
* Asynchronously loads and starts the registered plugins.
|
|
655
|
-
*
|
|
656
|
-
* This method is responsible for initializing and staarting all enabled plugins.
|
|
657
|
-
* It ensures that each plugin is properly loaded and started before the ridge starts.
|
|
658
|
-
*
|
|
659
|
-
* @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
|
|
660
|
-
*/
|
|
661
544
|
async startPlugins() {
|
|
662
|
-
// Check, load and start the plugins
|
|
663
545
|
for (const plugin of this.plugins) {
|
|
664
546
|
plugin.configJson = await this.plugins.loadConfig(plugin);
|
|
665
547
|
plugin.schemaJson = await this.plugins.loadSchema(plugin);
|
|
666
|
-
// Check if the plugin is available
|
|
667
548
|
if (!(await this.plugins.resolve(plugin.path))) {
|
|
668
549
|
this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
|
|
669
550
|
plugin.enabled = false;
|
|
670
551
|
plugin.error = true;
|
|
671
552
|
continue;
|
|
672
553
|
}
|
|
673
|
-
|
|
674
|
-
this.getPluginLatestVersion(plugin); // No await do it asyncronously
|
|
554
|
+
this.getPluginLatestVersion(plugin);
|
|
675
555
|
if (!plugin.enabled) {
|
|
676
556
|
this.log.info(`Plugin ${plg}${plugin.name}${nf} not enabled`);
|
|
677
557
|
continue;
|
|
@@ -686,26 +566,20 @@ export class Matterbridge extends EventEmitter {
|
|
|
686
566
|
plugin.addedDevices = undefined;
|
|
687
567
|
plugin.qrPairingCode = undefined;
|
|
688
568
|
plugin.manualPairingCode = undefined;
|
|
689
|
-
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
569
|
+
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
690
570
|
}
|
|
691
571
|
this.wssSendRefreshRequired();
|
|
692
572
|
}
|
|
693
|
-
/**
|
|
694
|
-
* Registers the process handlers for uncaughtException, unhandledRejection, SIGINT and SIGTERM.
|
|
695
|
-
* When either of these signals are received, the cleanup method is called with an appropriate message.
|
|
696
|
-
*/
|
|
697
573
|
registerProcessHandlers() {
|
|
698
574
|
this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
|
|
699
575
|
process.removeAllListeners('uncaughtException');
|
|
700
576
|
process.removeAllListeners('unhandledRejection');
|
|
701
577
|
this.exceptionHandler = async (error) => {
|
|
702
578
|
this.log.fatal('Unhandled Exception detected at:', error.stack || error, rs);
|
|
703
|
-
// await this.cleanup('Unhandled Exception detected, cleaning up...');
|
|
704
579
|
};
|
|
705
580
|
process.on('uncaughtException', this.exceptionHandler);
|
|
706
581
|
this.rejectionHandler = async (reason, promise) => {
|
|
707
582
|
this.log.fatal('Unhandled Rejection detected at:', promise, 'reason:', reason instanceof Error ? reason.stack : reason, rs);
|
|
708
|
-
// await this.cleanup('Unhandled Rejection detected, cleaning up...');
|
|
709
583
|
};
|
|
710
584
|
process.on('unhandledRejection', this.rejectionHandler);
|
|
711
585
|
this.log.debug(`Registering SIGINT and SIGTERM signal handlers...`);
|
|
@@ -718,9 +592,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
718
592
|
};
|
|
719
593
|
process.on('SIGTERM', this.sigtermHandler);
|
|
720
594
|
}
|
|
721
|
-
/**
|
|
722
|
-
* Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
|
|
723
|
-
*/
|
|
724
595
|
deregisterProcesslHandlers() {
|
|
725
596
|
this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
|
|
726
597
|
if (this.exceptionHandler)
|
|
@@ -737,11 +608,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
737
608
|
process.off('SIGTERM', this.sigtermHandler);
|
|
738
609
|
this.sigtermHandler = undefined;
|
|
739
610
|
}
|
|
740
|
-
/**
|
|
741
|
-
* Logs the node and system information.
|
|
742
|
-
*/
|
|
743
611
|
async logNodeAndSystemInfo() {
|
|
744
|
-
// IP address information
|
|
745
612
|
const networkInterfaces = os.networkInterfaces();
|
|
746
613
|
this.systemInformation.ipv4Address = '';
|
|
747
614
|
this.systemInformation.ipv6Address = '';
|
|
@@ -761,7 +628,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
761
628
|
this.systemInformation.macAddress = detail.mac;
|
|
762
629
|
}
|
|
763
630
|
}
|
|
764
|
-
if (this.systemInformation.ipv4Address !== ''
|
|
631
|
+
if (this.systemInformation.ipv4Address !== '') {
|
|
765
632
|
this.log.debug(`Using interface: '${this.systemInformation.interfaceName}'`);
|
|
766
633
|
this.log.debug(`- with MAC address: '${this.systemInformation.macAddress}'`);
|
|
767
634
|
this.log.debug(`- with IPv4 address: '${this.systemInformation.ipv4Address}'`);
|
|
@@ -769,22 +636,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
769
636
|
break;
|
|
770
637
|
}
|
|
771
638
|
}
|
|
772
|
-
// Node information
|
|
773
639
|
this.systemInformation.nodeVersion = process.versions.node;
|
|
774
640
|
const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
|
|
775
641
|
const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
|
|
776
642
|
const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
|
|
777
|
-
// Host system information
|
|
778
643
|
this.systemInformation.hostname = os.hostname();
|
|
779
644
|
this.systemInformation.user = os.userInfo().username;
|
|
780
|
-
this.systemInformation.osType = os.type();
|
|
781
|
-
this.systemInformation.osRelease = os.release();
|
|
782
|
-
this.systemInformation.osPlatform = os.platform();
|
|
783
|
-
this.systemInformation.osArch = os.arch();
|
|
784
|
-
this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
785
|
-
this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
786
|
-
this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
|
|
787
|
-
// Log the system information
|
|
645
|
+
this.systemInformation.osType = os.type();
|
|
646
|
+
this.systemInformation.osRelease = os.release();
|
|
647
|
+
this.systemInformation.osPlatform = os.platform();
|
|
648
|
+
this.systemInformation.osArch = os.arch();
|
|
649
|
+
this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
650
|
+
this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
651
|
+
this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
|
|
788
652
|
this.log.debug('Host System Information:');
|
|
789
653
|
this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
|
|
790
654
|
this.log.debug(`- User: ${this.systemInformation.user}`);
|
|
@@ -800,19 +664,15 @@ export class Matterbridge extends EventEmitter {
|
|
|
800
664
|
this.log.debug(`- Total Memory: ${this.systemInformation.totalMemory}`);
|
|
801
665
|
this.log.debug(`- Free Memory: ${this.systemInformation.freeMemory}`);
|
|
802
666
|
this.log.debug(`- System Uptime: ${this.systemInformation.systemUptime}`);
|
|
803
|
-
// Home directory
|
|
804
667
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
805
668
|
this.matterbridgeInformation.homeDirectory = this.homeDirectory;
|
|
806
669
|
this.log.debug(`Home Directory: ${this.homeDirectory}`);
|
|
807
|
-
// Package root directory
|
|
808
670
|
const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
809
671
|
this.rootDirectory = path.resolve(currentFileDirectory, '../');
|
|
810
672
|
this.matterbridgeInformation.rootDirectory = this.rootDirectory;
|
|
811
673
|
this.log.debug(`Root Directory: ${this.rootDirectory}`);
|
|
812
|
-
// Global node_modules directory
|
|
813
674
|
if (this.nodeContext)
|
|
814
675
|
this.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
|
|
815
|
-
// First run of Matterbridge so the node storage is empty
|
|
816
676
|
if (this.globalModulesDirectory === '') {
|
|
817
677
|
try {
|
|
818
678
|
this.globalModulesDirectory = await this.getGlobalNodeModules();
|
|
@@ -836,7 +696,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
836
696
|
this.log.error(`Error getting global node_modules directory: ${error}`);
|
|
837
697
|
});
|
|
838
698
|
}
|
|
839
|
-
// Create the data directory .matterbridge in the home directory
|
|
840
699
|
this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
|
|
841
700
|
this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
|
|
842
701
|
try {
|
|
@@ -860,7 +719,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
860
719
|
}
|
|
861
720
|
}
|
|
862
721
|
this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
|
|
863
|
-
// Create the plugin directory Matterbridge in the home directory
|
|
864
722
|
this.matterbridgePluginDirectory = path.join(this.homeDirectory, 'Matterbridge');
|
|
865
723
|
this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
|
|
866
724
|
try {
|
|
@@ -884,28 +742,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
884
742
|
}
|
|
885
743
|
}
|
|
886
744
|
this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
|
|
887
|
-
// Matterbridge version
|
|
888
745
|
const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
|
|
889
746
|
this.matterbridgeVersion = packageJson.version;
|
|
890
747
|
this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeVersion;
|
|
891
748
|
this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
|
|
892
|
-
// Matterbridge latest version
|
|
893
749
|
if (this.nodeContext)
|
|
894
750
|
this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', '');
|
|
895
751
|
this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
|
|
896
752
|
this.getMatterbridgeLatestVersion();
|
|
897
|
-
// Current working directory
|
|
898
753
|
const currentDir = process.cwd();
|
|
899
754
|
this.log.debug(`Current Working Directory: ${currentDir}`);
|
|
900
|
-
// Command line arguments (excluding 'node' and the script name)
|
|
901
755
|
const cmdArgs = process.argv.slice(2).join(' ');
|
|
902
756
|
this.log.debug(`Command Line Arguments: ${cmdArgs}`);
|
|
903
757
|
}
|
|
904
|
-
/**
|
|
905
|
-
* Retrieves the latest version of a package from the npm registry.
|
|
906
|
-
* @param packageName - The name of the package.
|
|
907
|
-
* @returns A Promise that resolves to the latest version of the package.
|
|
908
|
-
*/
|
|
909
758
|
async getLatestVersion(packageName) {
|
|
910
759
|
return new Promise((resolve, reject) => {
|
|
911
760
|
this.execRunningCount++;
|
|
@@ -920,10 +769,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
920
769
|
});
|
|
921
770
|
});
|
|
922
771
|
}
|
|
923
|
-
/**
|
|
924
|
-
* Retrieves the path to the global Node.js modules directory.
|
|
925
|
-
* @returns A promise that resolves to the path of the global Node.js modules directory.
|
|
926
|
-
*/
|
|
927
772
|
async getGlobalNodeModules() {
|
|
928
773
|
return new Promise((resolve, reject) => {
|
|
929
774
|
this.execRunningCount++;
|
|
@@ -938,11 +783,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
938
783
|
});
|
|
939
784
|
});
|
|
940
785
|
}
|
|
941
|
-
/**
|
|
942
|
-
* Retrieves the latest version of Matterbridge and performs necessary actions based on the version comparison.
|
|
943
|
-
* @private
|
|
944
|
-
* @returns {Promise<void>} A promise that resolves when the latest version is retrieved and actions are performed.
|
|
945
|
-
*/
|
|
946
786
|
async getMatterbridgeLatestVersion() {
|
|
947
787
|
this.getLatestVersion('matterbridge')
|
|
948
788
|
.then(async (matterbridgeLatestVersion) => {
|
|
@@ -959,19 +799,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
959
799
|
})
|
|
960
800
|
.catch((error) => {
|
|
961
801
|
this.log.error(`Error getting Matterbridge latest version: ${error.message}`);
|
|
962
|
-
// error.stack && this.log.debug(error.stack);
|
|
963
802
|
});
|
|
964
803
|
}
|
|
965
|
-
/**
|
|
966
|
-
* Retrieves the latest version of a plugin and updates the plugin's latestVersion property.
|
|
967
|
-
* If the plugin's version is different from the latest version, logs a warning message.
|
|
968
|
-
* If the plugin's version is the same as the latest version, logs an info message.
|
|
969
|
-
* If there is an error retrieving the latest version, logs an error message.
|
|
970
|
-
*
|
|
971
|
-
* @private
|
|
972
|
-
* @param {RegisteredPlugin} plugin - The plugin for which to retrieve the latest version.
|
|
973
|
-
* @returns {Promise<void>} A promise that resolves when the latest version is retrieved and actions are performed.
|
|
974
|
-
*/
|
|
975
804
|
async getPluginLatestVersion(plugin) {
|
|
976
805
|
this.getLatestVersion(plugin.name)
|
|
977
806
|
.then(async (latestVersion) => {
|
|
@@ -983,54 +812,40 @@ export class Matterbridge extends EventEmitter {
|
|
|
983
812
|
})
|
|
984
813
|
.catch((error) => {
|
|
985
814
|
this.log.error(`Error getting ${plg}${plugin.name}${er} latest version: ${error.message}`);
|
|
986
|
-
// error.stack && this.log.debug(error.stack);
|
|
987
815
|
});
|
|
988
816
|
}
|
|
989
|
-
/**
|
|
990
|
-
* Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (for the frontend).
|
|
991
|
-
*
|
|
992
|
-
* @returns {Function} The MatterLogger function.
|
|
993
|
-
*/
|
|
994
817
|
createMatterLogger() {
|
|
995
|
-
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4
|
|
818
|
+
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
|
|
996
819
|
return (_level, formattedLog) => {
|
|
997
820
|
const logger = formattedLog.slice(44, 44 + 20).trim();
|
|
998
821
|
const message = formattedLog.slice(65);
|
|
999
822
|
matterLogger.logName = logger;
|
|
1000
823
|
switch (_level) {
|
|
1001
824
|
case MatterLogLevel.DEBUG:
|
|
1002
|
-
matterLogger.log("debug"
|
|
825
|
+
matterLogger.log("debug", message);
|
|
1003
826
|
break;
|
|
1004
827
|
case MatterLogLevel.INFO:
|
|
1005
|
-
matterLogger.log("info"
|
|
828
|
+
matterLogger.log("info", message);
|
|
1006
829
|
break;
|
|
1007
830
|
case MatterLogLevel.NOTICE:
|
|
1008
|
-
matterLogger.log("notice"
|
|
831
|
+
matterLogger.log("notice", message);
|
|
1009
832
|
break;
|
|
1010
833
|
case MatterLogLevel.WARN:
|
|
1011
|
-
matterLogger.log("warn"
|
|
834
|
+
matterLogger.log("warn", message);
|
|
1012
835
|
break;
|
|
1013
836
|
case MatterLogLevel.ERROR:
|
|
1014
|
-
matterLogger.log("error"
|
|
837
|
+
matterLogger.log("error", message);
|
|
1015
838
|
break;
|
|
1016
839
|
case MatterLogLevel.FATAL:
|
|
1017
|
-
matterLogger.log("fatal"
|
|
840
|
+
matterLogger.log("fatal", message);
|
|
1018
841
|
break;
|
|
1019
842
|
default:
|
|
1020
|
-
matterLogger.log("debug"
|
|
843
|
+
matterLogger.log("debug", message);
|
|
1021
844
|
break;
|
|
1022
845
|
}
|
|
1023
846
|
};
|
|
1024
847
|
}
|
|
1025
|
-
/**
|
|
1026
|
-
* Creates a Matter File Logger.
|
|
1027
|
-
*
|
|
1028
|
-
* @param {string} filePath - The path to the log file.
|
|
1029
|
-
* @param {boolean} [unlink=false] - Whether to unlink the log file before creating a new one.
|
|
1030
|
-
* @returns {Function} - A function that logs formatted messages to the log file.
|
|
1031
|
-
*/
|
|
1032
848
|
async createMatterFileLogger(filePath, unlink = false) {
|
|
1033
|
-
// 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
|
|
1034
849
|
let fileSize = 0;
|
|
1035
850
|
if (unlink) {
|
|
1036
851
|
try {
|
|
@@ -1079,83 +894,53 @@ export class Matterbridge extends EventEmitter {
|
|
|
1079
894
|
}
|
|
1080
895
|
};
|
|
1081
896
|
}
|
|
1082
|
-
/**
|
|
1083
|
-
* Update matterbridge and cleanup.
|
|
1084
|
-
*/
|
|
1085
897
|
async updateProcess() {
|
|
1086
898
|
await this.cleanup('updating...', false);
|
|
1087
899
|
}
|
|
1088
|
-
/**
|
|
1089
|
-
* Restarts the process by spawning a new process and exiting the current process.
|
|
1090
|
-
*/
|
|
1091
900
|
async restartProcess() {
|
|
1092
901
|
await this.cleanup('restarting...', true);
|
|
1093
902
|
}
|
|
1094
|
-
/**
|
|
1095
|
-
* Shut down the process by exiting the current process.
|
|
1096
|
-
*/
|
|
1097
903
|
async shutdownProcess() {
|
|
1098
904
|
await this.cleanup('shutting down...', false);
|
|
1099
905
|
}
|
|
1100
|
-
/**
|
|
1101
|
-
* Shut down the process and reset.
|
|
1102
|
-
*/
|
|
1103
906
|
async unregisterAndShutdownProcess() {
|
|
1104
907
|
this.log.info('Unregistering all devices and shutting down...');
|
|
1105
|
-
for (const plugin of this.plugins
|
|
908
|
+
for (const plugin of this.plugins) {
|
|
1106
909
|
await this.removeAllBridgedDevices(plugin.name);
|
|
1107
910
|
}
|
|
1108
911
|
await this.cleanup('unregistered all devices and shutting down...', false);
|
|
1109
912
|
}
|
|
1110
|
-
/**
|
|
1111
|
-
* Shut down the process and reset.
|
|
1112
|
-
*/
|
|
1113
913
|
async shutdownProcessAndReset() {
|
|
1114
914
|
await this.cleanup('shutting down with reset...', false);
|
|
1115
915
|
}
|
|
1116
|
-
/**
|
|
1117
|
-
* Shut down the process and factory reset.
|
|
1118
|
-
*/
|
|
1119
916
|
async shutdownProcessAndFactoryReset() {
|
|
1120
917
|
await this.cleanup('shutting down with factory reset...', false);
|
|
1121
918
|
}
|
|
1122
|
-
/**
|
|
1123
|
-
* Cleans up the Matterbridge instance.
|
|
1124
|
-
* @param message - The cleanup message.
|
|
1125
|
-
* @param restart - Indicates whether to restart the instance after cleanup. Default is `false`.
|
|
1126
|
-
* @returns A promise that resolves when the cleanup is completed.
|
|
1127
|
-
*/
|
|
1128
919
|
async cleanup(message, restart = false) {
|
|
1129
920
|
if (this.initialized && !this.hasCleanupStarted) {
|
|
1130
921
|
this.hasCleanupStarted = true;
|
|
1131
922
|
this.log.info(message);
|
|
1132
|
-
// Deregisters the process handlers
|
|
1133
923
|
this.deregisterProcesslHandlers();
|
|
1134
|
-
// Clear the start matter interval
|
|
1135
924
|
if (this.startMatterInterval) {
|
|
1136
925
|
clearInterval(this.startMatterInterval);
|
|
1137
926
|
this.startMatterInterval = undefined;
|
|
1138
927
|
this.log.debug('Start matter interval cleared');
|
|
1139
928
|
}
|
|
1140
|
-
// Clear the check update interval
|
|
1141
929
|
if (this.checkUpdateInterval) {
|
|
1142
930
|
clearInterval(this.checkUpdateInterval);
|
|
1143
931
|
this.checkUpdateInterval = undefined;
|
|
1144
932
|
this.log.debug('Check update interval cleared');
|
|
1145
933
|
}
|
|
1146
|
-
// Clear the configure timeout
|
|
1147
934
|
if (this.configureTimeout) {
|
|
1148
935
|
clearTimeout(this.configureTimeout);
|
|
1149
936
|
this.configureTimeout = undefined;
|
|
1150
937
|
this.log.debug('Matterbridge configure timeout cleared');
|
|
1151
938
|
}
|
|
1152
|
-
// Clear the reachability timeout
|
|
1153
939
|
if (this.reachabilityTimeout) {
|
|
1154
940
|
clearTimeout(this.reachabilityTimeout);
|
|
1155
941
|
this.reachabilityTimeout = undefined;
|
|
1156
942
|
this.log.debug('Matterbridge reachability timeout cleared');
|
|
1157
943
|
}
|
|
1158
|
-
// Calling the shutdown method of each plugin and clear the reachability timeout
|
|
1159
944
|
for (const plugin of this.plugins) {
|
|
1160
945
|
if (!plugin.enabled || plugin.error)
|
|
1161
946
|
continue;
|
|
@@ -1166,29 +951,24 @@ export class Matterbridge extends EventEmitter {
|
|
|
1166
951
|
this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
|
|
1167
952
|
}
|
|
1168
953
|
}
|
|
1169
|
-
// Close the http server
|
|
1170
954
|
if (this.httpServer) {
|
|
1171
955
|
this.httpServer.close();
|
|
1172
956
|
this.httpServer.removeAllListeners();
|
|
1173
957
|
this.httpServer = undefined;
|
|
1174
958
|
this.log.debug('Frontend http server closed successfully');
|
|
1175
959
|
}
|
|
1176
|
-
// Close the https server
|
|
1177
960
|
if (this.httpsServer) {
|
|
1178
961
|
this.httpsServer.close();
|
|
1179
962
|
this.httpsServer.removeAllListeners();
|
|
1180
963
|
this.httpsServer = undefined;
|
|
1181
964
|
this.log.debug('Frontend https server closed successfully');
|
|
1182
965
|
}
|
|
1183
|
-
// Remove listeners from the express app
|
|
1184
966
|
if (this.expressApp) {
|
|
1185
967
|
this.expressApp.removeAllListeners();
|
|
1186
968
|
this.expressApp = undefined;
|
|
1187
969
|
this.log.debug('Frontend app closed successfully');
|
|
1188
970
|
}
|
|
1189
|
-
// Close the WebSocket server
|
|
1190
971
|
if (this.webSocketServer) {
|
|
1191
|
-
// Close all active connections
|
|
1192
972
|
this.webSocketServer.clients.forEach((client) => {
|
|
1193
973
|
if (client.readyState === WebSocket.OPEN) {
|
|
1194
974
|
client.close();
|
|
@@ -1204,35 +984,26 @@ export class Matterbridge extends EventEmitter {
|
|
|
1204
984
|
});
|
|
1205
985
|
this.webSocketServer = undefined;
|
|
1206
986
|
}
|
|
1207
|
-
// Closing matter
|
|
1208
987
|
await this.stopMatterServer();
|
|
1209
|
-
// Closing matter storage
|
|
1210
988
|
await this.stopMatterStorage();
|
|
1211
|
-
// Remove the matterfilelogger
|
|
1212
989
|
try {
|
|
1213
990
|
Logger.removeLogger('matterfilelogger');
|
|
1214
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1215
991
|
}
|
|
1216
992
|
catch (error) {
|
|
1217
|
-
// this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridgeDirectory, this.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
|
|
1218
993
|
}
|
|
1219
|
-
// Serialize registeredDevices
|
|
1220
994
|
if (this.nodeStorage && this.nodeContext) {
|
|
1221
995
|
this.log.info('Saving registered devices...');
|
|
1222
996
|
const serializedRegisteredDevices = [];
|
|
1223
997
|
this.devices.forEach(async (device) => {
|
|
1224
998
|
const serializedMatterbridgeDevice = device.serialize();
|
|
1225
|
-
// this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
|
|
1226
999
|
if (serializedMatterbridgeDevice)
|
|
1227
1000
|
serializedRegisteredDevices.push(serializedMatterbridgeDevice);
|
|
1228
1001
|
});
|
|
1229
1002
|
await this.nodeContext.set('devices', serializedRegisteredDevices);
|
|
1230
1003
|
this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
|
|
1231
|
-
// Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
|
|
1232
1004
|
this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
|
|
1233
1005
|
await this.nodeContext.close();
|
|
1234
1006
|
this.nodeContext = undefined;
|
|
1235
|
-
// Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
|
|
1236
1007
|
for (const plugin of this.plugins) {
|
|
1237
1008
|
if (plugin.nodeContext) {
|
|
1238
1009
|
this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
|
|
@@ -1263,16 +1034,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1263
1034
|
}
|
|
1264
1035
|
else {
|
|
1265
1036
|
if (message === 'shutting down with reset...') {
|
|
1266
|
-
// Delete matter storage file
|
|
1267
1037
|
this.log.info('Resetting Matterbridge commissioning information...');
|
|
1268
1038
|
await fs.unlink(path.join(this.matterbridgeDirectory, this.matterStorageName));
|
|
1269
1039
|
this.log.info('Reset done! Remove all paired devices from the controllers.');
|
|
1270
1040
|
}
|
|
1271
1041
|
if (message === 'shutting down with factory reset...') {
|
|
1272
|
-
// Delete matter storage file
|
|
1273
1042
|
this.log.info('Resetting Matterbridge commissioning information...');
|
|
1274
1043
|
await fs.unlink(path.join(this.matterbridgeDirectory, this.matterStorageName));
|
|
1275
|
-
// Delete node storage directory with its subdirectories
|
|
1276
1044
|
this.log.info('Resetting Matterbridge storage...');
|
|
1277
1045
|
await fs.rm(path.join(this.matterbridgeDirectory, this.nodeStorageName), { recursive: true });
|
|
1278
1046
|
this.log.info('Factory reset done! Remove all paired devices from the controllers.');
|
|
@@ -1285,33 +1053,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
1285
1053
|
this.initialized = false;
|
|
1286
1054
|
}
|
|
1287
1055
|
}
|
|
1288
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1289
1056
|
async addBridgedEndpoint(pluginName, device) {
|
|
1290
|
-
// Nothing to do here
|
|
1291
1057
|
}
|
|
1292
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1293
1058
|
async removeBridgedEndpoint(pluginName, device) {
|
|
1294
|
-
// Nothing to do here
|
|
1295
1059
|
}
|
|
1296
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1297
1060
|
async removeAllBridgedEndpoints(pluginName) {
|
|
1298
|
-
// Nothing to do here
|
|
1299
1061
|
}
|
|
1300
|
-
/**
|
|
1301
|
-
* Adds a bridged device to the Matterbridge.
|
|
1302
|
-
* @param pluginName - The name of the plugin.
|
|
1303
|
-
* @param device - The bridged device to add.
|
|
1304
|
-
* @returns {Promise<void>} - A promise that resolves when the device is added.
|
|
1305
|
-
*/
|
|
1306
1062
|
async addBridgedDevice(pluginName, device) {
|
|
1307
1063
|
this.log.debug(`Adding bridged device ${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
1308
|
-
// Check if the plugin is registered
|
|
1309
1064
|
const plugin = this.plugins.get(pluginName);
|
|
1310
1065
|
if (!plugin) {
|
|
1311
1066
|
this.log.error(`Error adding bridged device ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) plugin ${plg}${pluginName}${er} not found`);
|
|
1312
1067
|
return;
|
|
1313
1068
|
}
|
|
1314
|
-
// Register and add the device to matterbridge aggregator in bridge mode
|
|
1315
1069
|
if (this.bridgeMode === 'bridge') {
|
|
1316
1070
|
if (!this.matterAggregator) {
|
|
1317
1071
|
this.log.error(`Adding bridged device ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er} error: matterAggregator not found`);
|
|
@@ -1319,11 +1073,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1319
1073
|
}
|
|
1320
1074
|
this.matterAggregator.addBridgedDevice(device);
|
|
1321
1075
|
}
|
|
1322
|
-
// The first time create the commissioning server and the aggregator for DynamicPlatform
|
|
1323
|
-
// Register and add the device in childbridge mode
|
|
1324
1076
|
if (this.bridgeMode === 'childbridge') {
|
|
1325
1077
|
if (plugin.type === 'AccessoryPlatform') {
|
|
1326
|
-
// Check if the plugin is locked with the commissioning server
|
|
1327
1078
|
if (!plugin.locked) {
|
|
1328
1079
|
plugin.locked = true;
|
|
1329
1080
|
plugin.storageContext = await this.importCommissioningServerContext(plugin.name, device);
|
|
@@ -1337,7 +1088,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1337
1088
|
}
|
|
1338
1089
|
}
|
|
1339
1090
|
if (plugin.type === 'DynamicPlatform') {
|
|
1340
|
-
// Check if the plugin is locked with the commissioning server and the aggregator
|
|
1341
1091
|
if (!plugin.locked) {
|
|
1342
1092
|
plugin.locked = true;
|
|
1343
1093
|
this.log.debug(`Creating commissioning server context for ${plg}${plugin.name}${db}`);
|
|
@@ -1345,7 +1095,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1345
1095
|
this.log.debug(`Creating commissioning server for ${plg}${plugin.name}${db}`);
|
|
1346
1096
|
plugin.commissioningServer = await this.createCommisioningServer(plugin.storageContext, plugin.name);
|
|
1347
1097
|
this.log.debug(`Creating aggregator for plugin ${plg}${plugin.name}${db}`);
|
|
1348
|
-
plugin.aggregator = await this.createMatterAggregator(plugin.storageContext, plugin.name);
|
|
1098
|
+
plugin.aggregator = await this.createMatterAggregator(plugin.storageContext, plugin.name);
|
|
1349
1099
|
this.log.debug(`Adding matter aggregator to commissioning server for plugin ${plg}${plugin.name}${db}`);
|
|
1350
1100
|
plugin.commissioningServer.addDevice(plugin.aggregator);
|
|
1351
1101
|
this.log.debug(`Adding commissioning server to matter server for plugin ${plg}${plugin.name}${db}`);
|
|
@@ -1358,25 +1108,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
1358
1108
|
plugin.registeredDevices++;
|
|
1359
1109
|
if (plugin.addedDevices !== undefined)
|
|
1360
1110
|
plugin.addedDevices++;
|
|
1361
|
-
// Add the device to the DeviceManager
|
|
1362
1111
|
this.devices.set(device);
|
|
1363
1112
|
this.log.info(`Added and registered bridged device (${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
1364
1113
|
}
|
|
1365
|
-
/**
|
|
1366
|
-
* Removes a bridged device from the Matterbridge.
|
|
1367
|
-
* @param pluginName - The name of the plugin.
|
|
1368
|
-
* @param device - The device to be removed.
|
|
1369
|
-
* @returns A Promise that resolves when the device is successfully removed.
|
|
1370
|
-
*/
|
|
1371
1114
|
async removeBridgedDevice(pluginName, device) {
|
|
1372
1115
|
this.log.debug(`Removing bridged device ${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
1373
|
-
// Check if the plugin is registered
|
|
1374
1116
|
const plugin = this.plugins.get(pluginName);
|
|
1375
1117
|
if (!plugin) {
|
|
1376
1118
|
this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
1377
1119
|
return;
|
|
1378
1120
|
}
|
|
1379
|
-
// Remove the device from matterbridge aggregator in bridge mode
|
|
1380
1121
|
if (this.bridgeMode === 'bridge') {
|
|
1381
1122
|
if (!this.matterAggregator) {
|
|
1382
1123
|
this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: matterAggregator not found`);
|
|
@@ -1386,8 +1127,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1386
1127
|
device.setBridgedDeviceReachability(false);
|
|
1387
1128
|
device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
|
|
1388
1129
|
}
|
|
1389
|
-
// device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerShutDownEvent({});
|
|
1390
|
-
// device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerLeaveEvent({});
|
|
1391
1130
|
this.matterAggregator?.removeBridgedDevice(device);
|
|
1392
1131
|
this.log.info(`Removed bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${zb}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
1393
1132
|
if (plugin.registeredDevices !== undefined)
|
|
@@ -1395,7 +1134,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1395
1134
|
if (plugin.addedDevices !== undefined)
|
|
1396
1135
|
plugin.addedDevices--;
|
|
1397
1136
|
}
|
|
1398
|
-
// Remove the device in childbridge mode
|
|
1399
1137
|
if (this.bridgeMode === 'childbridge') {
|
|
1400
1138
|
if (plugin.type === 'AccessoryPlatform') {
|
|
1401
1139
|
if (!plugin.commissioningServer) {
|
|
@@ -1419,22 +1157,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
1419
1157
|
plugin.registeredDevices--;
|
|
1420
1158
|
if (plugin.addedDevices !== undefined)
|
|
1421
1159
|
plugin.addedDevices--;
|
|
1422
|
-
// Remove the commissioning server
|
|
1423
1160
|
if (plugin.registeredDevices === 0 && plugin.addedDevices === 0 && plugin.commissioningServer) {
|
|
1424
1161
|
this.matterServer?.removeCommissioningServer(plugin.commissioningServer);
|
|
1425
1162
|
plugin.commissioningServer = undefined;
|
|
1426
1163
|
this.log.info(`Removed commissioning server for plugin ${plg}${pluginName}${nf}`);
|
|
1427
1164
|
}
|
|
1428
1165
|
}
|
|
1429
|
-
// Remove the device from the DeviceManager
|
|
1430
1166
|
this.devices.remove(device);
|
|
1431
1167
|
}
|
|
1432
|
-
/**
|
|
1433
|
-
* Removes all bridged devices associated with a specific plugin.
|
|
1434
|
-
*
|
|
1435
|
-
* @param pluginName - The name of the plugin.
|
|
1436
|
-
* @returns A promise that resolves when all devices have been removed.
|
|
1437
|
-
*/
|
|
1438
1168
|
async removeAllBridgedDevices(pluginName) {
|
|
1439
1169
|
this.log.debug(`Removing all bridged devices for plugin ${plg}${pluginName}${db}`);
|
|
1440
1170
|
this.devices.forEach(async (device) => {
|
|
@@ -1443,13 +1173,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1443
1173
|
}
|
|
1444
1174
|
});
|
|
1445
1175
|
}
|
|
1446
|
-
/**
|
|
1447
|
-
* Starts the Matterbridge in bridge mode.
|
|
1448
|
-
* @private
|
|
1449
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1450
|
-
*/
|
|
1451
1176
|
async startBridge() {
|
|
1452
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1453
1177
|
if (!this.storageManager)
|
|
1454
1178
|
throw new Error('No storage manager initialized');
|
|
1455
1179
|
if (!this.matterbridgeContext)
|
|
@@ -1468,7 +1192,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1468
1192
|
let failCount = 0;
|
|
1469
1193
|
this.startMatterInterval = setInterval(async () => {
|
|
1470
1194
|
for (const plugin of this.plugins) {
|
|
1471
|
-
// new code to not start the bridge if one plugin is in error cause the controllers will delete the devices loosing all the configuration
|
|
1472
1195
|
if (!plugin.enabled)
|
|
1473
1196
|
continue;
|
|
1474
1197
|
if (plugin.error) {
|
|
@@ -1493,18 +1216,15 @@ export class Matterbridge extends EventEmitter {
|
|
|
1493
1216
|
clearInterval(this.startMatterInterval);
|
|
1494
1217
|
this.startMatterInterval = undefined;
|
|
1495
1218
|
this.log.debug('Cleared startMatterInterval interval for Matterbridge');
|
|
1496
|
-
// Start the Matter server
|
|
1497
1219
|
await this.startMatterServer();
|
|
1498
1220
|
this.log.notice('Matter server started');
|
|
1499
|
-
// Show the QR code for commissioning or log the already commissioned message
|
|
1500
1221
|
await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
|
|
1501
|
-
// Configure the plugins
|
|
1502
1222
|
this.configureTimeout = setTimeout(async () => {
|
|
1503
1223
|
for (const plugin of this.plugins) {
|
|
1504
1224
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
1505
1225
|
continue;
|
|
1506
1226
|
try {
|
|
1507
|
-
await this.plugins.configure(plugin);
|
|
1227
|
+
await this.plugins.configure(plugin);
|
|
1508
1228
|
}
|
|
1509
1229
|
catch (error) {
|
|
1510
1230
|
plugin.error = true;
|
|
@@ -1513,7 +1233,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1513
1233
|
}
|
|
1514
1234
|
this.wssSendRefreshRequired();
|
|
1515
1235
|
}, 30 * 1000);
|
|
1516
|
-
// Setting reachability to true
|
|
1517
1236
|
this.reachabilityTimeout = setTimeout(() => {
|
|
1518
1237
|
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1519
1238
|
if (this.commissioningServer)
|
|
@@ -1523,14 +1242,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1523
1242
|
}, 60 * 1000);
|
|
1524
1243
|
}, 1000);
|
|
1525
1244
|
}
|
|
1526
|
-
/**
|
|
1527
|
-
* Starts the Matterbridge in childbridge mode.
|
|
1528
|
-
* @private
|
|
1529
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1530
|
-
*/
|
|
1531
1245
|
async startChildbridge() {
|
|
1532
|
-
// Matterbridge.addBridgedDevice creates the commissionig servers and add the devices to the the commissioning server or to the aggregator
|
|
1533
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1534
1246
|
if (!this.storageManager)
|
|
1535
1247
|
throw new Error('No storage manager initialized');
|
|
1536
1248
|
this.matterServer = this.createMatterServer(this.storageManager);
|
|
@@ -1540,7 +1252,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1540
1252
|
this.startMatterInterval = setInterval(async () => {
|
|
1541
1253
|
let allStarted = true;
|
|
1542
1254
|
for (const plugin of this.plugins) {
|
|
1543
|
-
// new code to not start the bridge if one plugin is in error cause the controllers will delete the devices loosing all the configuration
|
|
1544
1255
|
if (!plugin.enabled)
|
|
1545
1256
|
continue;
|
|
1546
1257
|
if (plugin.error) {
|
|
@@ -1568,16 +1279,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
1568
1279
|
clearInterval(this.startMatterInterval);
|
|
1569
1280
|
this.startMatterInterval = undefined;
|
|
1570
1281
|
this.log.debug('Cleared startMatterInterval interval in childbridge mode');
|
|
1571
|
-
// Start the Matter server
|
|
1572
1282
|
await this.startMatterServer();
|
|
1573
1283
|
this.log.notice('Matter server started');
|
|
1574
|
-
// Configure the plugins
|
|
1575
1284
|
this.configureTimeout = setTimeout(async () => {
|
|
1576
1285
|
for (const plugin of this.plugins) {
|
|
1577
1286
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
1578
1287
|
continue;
|
|
1579
1288
|
try {
|
|
1580
|
-
await this.plugins.configure(plugin);
|
|
1289
|
+
await this.plugins.configure(plugin);
|
|
1581
1290
|
}
|
|
1582
1291
|
catch (error) {
|
|
1583
1292
|
plugin.error = true;
|
|
@@ -1606,7 +1315,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1606
1315
|
continue;
|
|
1607
1316
|
}
|
|
1608
1317
|
await this.showCommissioningQRCode(plugin.commissioningServer, plugin.storageContext, plugin.nodeContext, plugin.name);
|
|
1609
|
-
// Setting reachability to true
|
|
1610
1318
|
plugin.reachabilityTimeout = setTimeout(() => {
|
|
1611
1319
|
this.log.info(`Setting reachability to true for ${plg}${plugin.name}${db}`);
|
|
1612
1320
|
if (plugin.commissioningServer)
|
|
@@ -1619,11 +1327,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1619
1327
|
}
|
|
1620
1328
|
}, 1000);
|
|
1621
1329
|
}
|
|
1622
|
-
/**
|
|
1623
|
-
* Starts the Matterbridge controller.
|
|
1624
|
-
* @private
|
|
1625
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1626
|
-
*/
|
|
1627
1330
|
async startController() {
|
|
1628
1331
|
if (!this.storageManager) {
|
|
1629
1332
|
this.log.error('No storage manager initialized');
|
|
@@ -1686,7 +1389,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1686
1389
|
const nodeId = await this.commissioningController.commissionNode(options);
|
|
1687
1390
|
this.log.info(`Commissioning successfully done with nodeId: ${nodeId}`);
|
|
1688
1391
|
this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
|
|
1689
|
-
}
|
|
1392
|
+
}
|
|
1690
1393
|
if (hasParameter('unpairall')) {
|
|
1691
1394
|
this.log.info('***Commissioning controller unpairing all nodes...');
|
|
1692
1395
|
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
@@ -1697,8 +1400,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1697
1400
|
return;
|
|
1698
1401
|
}
|
|
1699
1402
|
if (hasParameter('discover')) {
|
|
1700
|
-
// const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
|
|
1701
|
-
// console.log(discover);
|
|
1702
1403
|
}
|
|
1703
1404
|
if (!this.commissioningController.isCommissioned()) {
|
|
1704
1405
|
this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
|
|
@@ -1739,12 +1440,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
1739
1440
|
},
|
|
1740
1441
|
});
|
|
1741
1442
|
node.logStructure();
|
|
1742
|
-
// Get the interaction client
|
|
1743
1443
|
this.log.info('Getting the interaction client');
|
|
1744
1444
|
const interactionClient = await node.getInteractionClient();
|
|
1745
1445
|
let cluster;
|
|
1746
1446
|
let attributes;
|
|
1747
|
-
// Log BasicInformationCluster
|
|
1748
1447
|
cluster = BasicInformationCluster;
|
|
1749
1448
|
attributes = await interactionClient.getMultipleAttributes({
|
|
1750
1449
|
attributes: [{ clusterId: cluster.id }],
|
|
@@ -1754,7 +1453,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1754
1453
|
attributes.forEach((attribute) => {
|
|
1755
1454
|
this.log.info(`- 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}`);
|
|
1756
1455
|
});
|
|
1757
|
-
// Log PowerSourceCluster
|
|
1758
1456
|
cluster = PowerSourceCluster;
|
|
1759
1457
|
attributes = await interactionClient.getMultipleAttributes({
|
|
1760
1458
|
attributes: [{ clusterId: cluster.id }],
|
|
@@ -1764,7 +1462,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1764
1462
|
attributes.forEach((attribute) => {
|
|
1765
1463
|
this.log.info(`- 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}`);
|
|
1766
1464
|
});
|
|
1767
|
-
// Log ThreadNetworkDiagnostics
|
|
1768
1465
|
cluster = ThreadNetworkDiagnosticsCluster;
|
|
1769
1466
|
attributes = await interactionClient.getMultipleAttributes({
|
|
1770
1467
|
attributes: [{ clusterId: cluster.id }],
|
|
@@ -1774,7 +1471,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1774
1471
|
attributes.forEach((attribute) => {
|
|
1775
1472
|
this.log.info(`- 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}`);
|
|
1776
1473
|
});
|
|
1777
|
-
// Log SwitchCluster
|
|
1778
1474
|
cluster = SwitchCluster;
|
|
1779
1475
|
attributes = await interactionClient.getMultipleAttributes({
|
|
1780
1476
|
attributes: [{ clusterId: cluster.id }],
|
|
@@ -1795,15 +1491,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1795
1491
|
this.log.info('Subscribed to all attributes and events');
|
|
1796
1492
|
}
|
|
1797
1493
|
}
|
|
1798
|
-
/** ***********************************************************************************************************************************/
|
|
1799
|
-
/** Matter.js methods */
|
|
1800
|
-
/** ***********************************************************************************************************************************/
|
|
1801
|
-
/**
|
|
1802
|
-
* Starts the matter storage process based on the specified storage type and name.
|
|
1803
|
-
* @param {string} storageType - The type of storage to start (e.g., 'disk', 'json').
|
|
1804
|
-
* @param {string} storageName - The name of the storage file.
|
|
1805
|
-
* @returns {Promise<void>} - A promise that resolves when the storage process is started.
|
|
1806
|
-
*/
|
|
1807
1494
|
async startMatterStorage(storageType, storageName) {
|
|
1808
1495
|
this.log.debug(`Starting matter ${storageType} storage ${CYAN}${storageName}${db}`);
|
|
1809
1496
|
if (storageType === 'disk') {
|
|
@@ -1849,12 +1536,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1849
1536
|
this.log.debug(`Creating commissioning server context for ${plg}Matterbridge${db}`);
|
|
1850
1537
|
this.matterbridgeContext = await this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge aggregator');
|
|
1851
1538
|
}
|
|
1852
|
-
/**
|
|
1853
|
-
* Makes a backup copy of the specified matter JSON storage file.
|
|
1854
|
-
*
|
|
1855
|
-
* @param storageName - The name of the JSON storage file to be backed up.
|
|
1856
|
-
* @param backupName - The name of the backup file to be created.
|
|
1857
|
-
*/
|
|
1858
1539
|
async backupMatterStorage(storageName, backupName) {
|
|
1859
1540
|
try {
|
|
1860
1541
|
this.log.debug(`Making backup copy of ${storageName}`);
|
|
@@ -1875,12 +1556,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1875
1556
|
}
|
|
1876
1557
|
}
|
|
1877
1558
|
}
|
|
1878
|
-
/**
|
|
1879
|
-
* Restore the specified matter JSON storage file.
|
|
1880
|
-
*
|
|
1881
|
-
* @param backupName - The name of the backup file to restore from.
|
|
1882
|
-
* @param storageName - The name of the JSON storage file to restored.
|
|
1883
|
-
*/
|
|
1884
1559
|
async restoreMatterStorage(backupName, storageName) {
|
|
1885
1560
|
try {
|
|
1886
1561
|
this.log.notice(`Restoring the backup copy of ${storageName}`);
|
|
@@ -1901,10 +1576,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1901
1576
|
}
|
|
1902
1577
|
}
|
|
1903
1578
|
}
|
|
1904
|
-
/**
|
|
1905
|
-
* Stops the matter storage.
|
|
1906
|
-
* @returns {Promise<void>} A promise that resolves when the storage is stopped.
|
|
1907
|
-
*/
|
|
1908
1579
|
async stopMatterStorage() {
|
|
1909
1580
|
this.log.debug('Stopping storage');
|
|
1910
1581
|
await this.storageManager?.close();
|
|
@@ -1913,14 +1584,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1913
1584
|
this.matterbridgeContext = undefined;
|
|
1914
1585
|
this.mattercontrollerContext = undefined;
|
|
1915
1586
|
}
|
|
1916
|
-
/**
|
|
1917
|
-
* Creates a Matter server using the provided storage manager and the provided mdnsInterface.
|
|
1918
|
-
* @param storageManager The storage manager to be used by the Matter server.
|
|
1919
|
-
*
|
|
1920
|
-
*/
|
|
1921
1587
|
createMatterServer(storageManager) {
|
|
1922
1588
|
this.log.debug('Creating matter server');
|
|
1923
|
-
// Validate mdnsInterface
|
|
1924
1589
|
if (this.mdnsInterface) {
|
|
1925
1590
|
const networkInterfaces = os.networkInterfaces();
|
|
1926
1591
|
const availableInterfaces = Object.keys(networkInterfaces);
|
|
@@ -1936,10 +1601,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1936
1601
|
this.log.debug(`Created matter server with mdnsInterface: ${this.mdnsInterface ?? 'all available interfaces'}`);
|
|
1937
1602
|
return matterServer;
|
|
1938
1603
|
}
|
|
1939
|
-
/**
|
|
1940
|
-
* Starts the Matter server.
|
|
1941
|
-
* If the Matter server is not initialized, it logs an error and performs cleanup.
|
|
1942
|
-
*/
|
|
1943
1604
|
async startMatterServer() {
|
|
1944
1605
|
if (!this.matterServer) {
|
|
1945
1606
|
this.log.error('No matter server initialized');
|
|
@@ -1949,11 +1610,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1949
1610
|
this.log.debug('Starting matter server...');
|
|
1950
1611
|
await this.matterServer.start();
|
|
1951
1612
|
this.log.debug('Started matter server');
|
|
1952
|
-
// this.commissioningServer?.getRootEndpoint() && logEndpoint(this.commissioningServer?.getRootEndpoint());
|
|
1953
1613
|
}
|
|
1954
|
-
/**
|
|
1955
|
-
* Stops the Matter server, commissioningServer and commissioningController.
|
|
1956
|
-
*/
|
|
1957
1614
|
async stopMatterServer() {
|
|
1958
1615
|
this.log.debug('Stopping matter commissioningServer');
|
|
1959
1616
|
await this.commissioningServer?.close();
|
|
@@ -1967,78 +1624,22 @@ export class Matterbridge extends EventEmitter {
|
|
|
1967
1624
|
this.matterAggregator = undefined;
|
|
1968
1625
|
this.matterServer = undefined;
|
|
1969
1626
|
}
|
|
1970
|
-
/**
|
|
1971
|
-
* Creates a Matter Aggregator.
|
|
1972
|
-
* @param {StorageContext} context - The storage context.
|
|
1973
|
-
* @returns {Aggregator} - The created Matter Aggregator.
|
|
1974
|
-
*/
|
|
1975
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1976
1627
|
async createMatterAggregator(context, pluginName) {
|
|
1977
|
-
/*
|
|
1978
|
-
const random = randomBytes(8).toString('hex');
|
|
1979
|
-
await context.set('aggregatorSerialNumber', await context.get('aggregatorSerialNumber', random));
|
|
1980
|
-
await context.set('aggregatorUniqueId', await context.get('aggregatorUniqueId', random));
|
|
1981
|
-
|
|
1982
|
-
this.log.debug(`Creating matter aggregator for plugin ${plg}${pluginName}${db} with uniqueId ${await context.get<string>('aggregatorUniqueId')} serialNumber ${await context.get<string>('aggregatorSerialNumber')}`);
|
|
1983
|
-
this.log.debug(`Creating matter aggregator for plugin ${plg}${pluginName}${db} with softwareVersion ${await context.get<number>('softwareVersion', 1)} softwareVersionString ${await context.get<string>('softwareVersionString', '1.0.0')}`);
|
|
1984
|
-
this.log.debug(`Creating matter aggregator for plugin ${plg}${pluginName}${db} with hardwareVersion ${await context.get<number>('hardwareVersion', 1)} hardwareVersionString ${await context.get<string>('hardwareVersionString', '1.0.0')}`);
|
|
1985
|
-
*/
|
|
1986
1628
|
const matterAggregator = new Aggregator();
|
|
1987
|
-
/*
|
|
1988
|
-
matterAggregator.addClusterServer(
|
|
1989
|
-
ClusterServer(
|
|
1990
|
-
BasicInformationCluster,
|
|
1991
|
-
{
|
|
1992
|
-
dataModelRevision: 1,
|
|
1993
|
-
location: 'FR',
|
|
1994
|
-
vendorId: VendorId(0xfff1),
|
|
1995
|
-
vendorName: 'Matterbridge',
|
|
1996
|
-
productId: 0x8000,
|
|
1997
|
-
productName: 'Matterbridge aggregator',
|
|
1998
|
-
productLabel: 'Matterbridge aggregator',
|
|
1999
|
-
nodeLabel: 'Matterbridge aggregator',
|
|
2000
|
-
serialNumber: await context.get<string>('aggregatorSerialNumber'),
|
|
2001
|
-
uniqueId: await context.get<string>('aggregatorUniqueId'),
|
|
2002
|
-
softwareVersion: await context.get<number>('softwareVersion', 1),
|
|
2003
|
-
softwareVersionString: await context.get<string>('softwareVersionString', '1.0.0'),
|
|
2004
|
-
hardwareVersion: await context.get<number>('hardwareVersion', 1),
|
|
2005
|
-
hardwareVersionString: await context.get<string>('hardwareVersionString', '1.0.0'),
|
|
2006
|
-
reachable: true,
|
|
2007
|
-
capabilityMinima: { caseSessionsPerFabric: 3, subscriptionsPerFabric: 3 },
|
|
2008
|
-
specificationVersion: Specification.SPECIFICATION_VERSION,
|
|
2009
|
-
maxPathsPerInvoke: 1,
|
|
2010
|
-
},
|
|
2011
|
-
{},
|
|
2012
|
-
{
|
|
2013
|
-
startUp: true,
|
|
2014
|
-
shutDown: true,
|
|
2015
|
-
leave: true,
|
|
2016
|
-
reachableChanged: true,
|
|
2017
|
-
},
|
|
2018
|
-
),
|
|
2019
|
-
);
|
|
2020
|
-
*/
|
|
2021
1629
|
return matterAggregator;
|
|
2022
1630
|
}
|
|
2023
|
-
/**
|
|
2024
|
-
* Creates a matter commissioning server.
|
|
2025
|
-
*
|
|
2026
|
-
* @param {StorageContext} context - The storage context.
|
|
2027
|
-
* @param {string} pluginName - The name of the commissioning server.
|
|
2028
|
-
* @returns {CommissioningServer} The created commissioning server.
|
|
2029
|
-
*/
|
|
2030
1631
|
async createCommisioningServer(context, pluginName) {
|
|
2031
1632
|
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db}`);
|
|
2032
1633
|
const deviceName = await context.get('deviceName');
|
|
2033
1634
|
const deviceType = await context.get('deviceType');
|
|
2034
1635
|
const vendorId = await context.get('vendorId');
|
|
2035
|
-
const vendorName = await context.get('vendorName');
|
|
1636
|
+
const vendorName = await context.get('vendorName');
|
|
2036
1637
|
const productId = await context.get('productId');
|
|
2037
|
-
const productName = await context.get('productName');
|
|
1638
|
+
const productName = await context.get('productName');
|
|
2038
1639
|
const serialNumber = await context.get('serialNumber');
|
|
2039
1640
|
const uniqueId = await context.get('uniqueId');
|
|
2040
1641
|
const softwareVersion = await context.get('softwareVersion', 1);
|
|
2041
|
-
const softwareVersionString = await context.get('softwareVersionString', '1.0.0');
|
|
1642
|
+
const softwareVersionString = await context.get('softwareVersionString', '1.0.0');
|
|
2042
1643
|
const hardwareVersion = await context.get('hardwareVersion', 1);
|
|
2043
1644
|
const hardwareVersionString = await context.get('hardwareVersionString', '1.0.0');
|
|
2044
1645
|
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with deviceName '${deviceName}' deviceType ${deviceType}(0x${deviceType.toString(16).padStart(4, '0')})`);
|
|
@@ -2046,7 +1647,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2046
1647
|
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with softwareVersion ${softwareVersion} softwareVersionString ${softwareVersionString}`);
|
|
2047
1648
|
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with hardwareVersion ${hardwareVersion} hardwareVersionString ${hardwareVersionString}`);
|
|
2048
1649
|
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with nodeLabel '${productName}' port ${this.port} passcode ${this.passcode} discriminator ${this.discriminator}`);
|
|
2049
|
-
// Validate ipv4address
|
|
2050
1650
|
if (this.ipv4address) {
|
|
2051
1651
|
const networkInterfaces = os.networkInterfaces();
|
|
2052
1652
|
const availableAddresses = Object.values(networkInterfaces)
|
|
@@ -2061,7 +1661,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2061
1661
|
this.log.info(`Using ipv4address '${this.ipv4address}' for the Matter commissioning server.`);
|
|
2062
1662
|
}
|
|
2063
1663
|
}
|
|
2064
|
-
// Validate ipv6address
|
|
2065
1664
|
if (this.ipv6address) {
|
|
2066
1665
|
const networkInterfaces = os.networkInterfaces();
|
|
2067
1666
|
const availableAddresses = Object.values(networkInterfaces)
|
|
@@ -2092,7 +1691,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2092
1691
|
nodeLabel: productName,
|
|
2093
1692
|
productLabel: productName,
|
|
2094
1693
|
softwareVersion,
|
|
2095
|
-
softwareVersionString,
|
|
1694
|
+
softwareVersionString,
|
|
2096
1695
|
hardwareVersion,
|
|
2097
1696
|
hardwareVersionString,
|
|
2098
1697
|
uniqueId,
|
|
@@ -2188,24 +1787,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2188
1787
|
commissioningServer.addCommandHandler('testEventTrigger', async ({ request: { enableKey, eventTrigger } }) => this.log.info(`testEventTrigger called on GeneralDiagnostic cluster: ${enableKey} ${eventTrigger}`));
|
|
2189
1788
|
return commissioningServer;
|
|
2190
1789
|
}
|
|
2191
|
-
/**
|
|
2192
|
-
* Creates a commissioning server storage context.
|
|
2193
|
-
*
|
|
2194
|
-
* @param pluginName - The name of the plugin.
|
|
2195
|
-
* @param deviceName - The name of the device.
|
|
2196
|
-
* @param deviceType - The type of the device.
|
|
2197
|
-
* @param vendorId - The vendor ID.
|
|
2198
|
-
* @param vendorName - The vendor name.
|
|
2199
|
-
* @param productId - The product ID.
|
|
2200
|
-
* @param productName - The product name.
|
|
2201
|
-
* @param serialNumber - The serial number of the device (optional).
|
|
2202
|
-
* @param uniqueId - The unique ID of the device (optional).
|
|
2203
|
-
* @param softwareVersion - The software version of the device (optional).
|
|
2204
|
-
* @param softwareVersionString - The software version string of the device (optional).
|
|
2205
|
-
* @param hardwareVersion - The hardware version of the device (optional).
|
|
2206
|
-
* @param hardwareVersionString - The hardware version string of the device (optional).
|
|
2207
|
-
* @returns The storage context for the commissioning server.
|
|
2208
|
-
*/
|
|
2209
1790
|
async createCommissioningServerContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName) {
|
|
2210
1791
|
if (!this.storageManager)
|
|
2211
1792
|
throw new Error('No storage manager initialized');
|
|
@@ -2233,13 +1814,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2233
1814
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
2234
1815
|
return storageContext;
|
|
2235
1816
|
}
|
|
2236
|
-
/**
|
|
2237
|
-
* Imports the commissioning server context for a specific plugin and device.
|
|
2238
|
-
* @param pluginName - The name of the plugin.
|
|
2239
|
-
* @param device - The MatterbridgeDevice object representing the device.
|
|
2240
|
-
* @returns The commissioning server context.
|
|
2241
|
-
* @throws Error if the BasicInformationCluster is not found.
|
|
2242
|
-
*/
|
|
2243
1817
|
async importCommissioningServerContext(pluginName, device) {
|
|
2244
1818
|
this.log.debug(`Importing matter commissioning server storage context from device for ${plg}${pluginName}${db}`);
|
|
2245
1819
|
const basic = device.getClusterServer(BasicInformationCluster);
|
|
@@ -2274,14 +1848,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2274
1848
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
2275
1849
|
return storageContext;
|
|
2276
1850
|
}
|
|
2277
|
-
/**
|
|
2278
|
-
* Shows the commissioning server QR code for a given plugin.
|
|
2279
|
-
* @param {CommissioningServer} commissioningServer - The commissioning server instance.
|
|
2280
|
-
* @param {StorageContext} storageContext - The storage context instance.
|
|
2281
|
-
* @param {NodeStorage} nodeContext - The node storage instance.
|
|
2282
|
-
* @param {string} pluginName - The name of the plugin of Matterbridge in bridge mode.
|
|
2283
|
-
* @returns {Promise<void>} - A promise that resolves when the QR code is shown.
|
|
2284
|
-
*/
|
|
2285
1851
|
async showCommissioningQRCode(commissioningServer, storageContext, nodeContext, pluginName) {
|
|
2286
1852
|
if (!commissioningServer || !storageContext || !nodeContext || !pluginName) {
|
|
2287
1853
|
this.log.error(`showCommissioningQRCode error: commissioningServer: ${!commissioningServer} storageContext: ${!storageContext} nodeContext: ${!nodeContext} pluginName: ${pluginName}`);
|
|
@@ -2292,8 +1858,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2292
1858
|
const { qrPairingCode, manualPairingCode } = commissioningServer.getPairingCode();
|
|
2293
1859
|
const QrCode = new QrCodeSchema();
|
|
2294
1860
|
this.log.info(`*The commissioning server on port ${commissioningServer.getPort()} for ${plg}${pluginName}${nf} is not commissioned. Pair it scanning the QR code:\n\n`);
|
|
2295
|
-
|
|
2296
|
-
if (this.log.logLevel === "debug" /* LogLevel.DEBUG */ || this.log.logLevel === "info" /* LogLevel.INFO */)
|
|
1861
|
+
if (this.log.logLevel === "debug" || this.log.logLevel === "info")
|
|
2297
1862
|
console.log(`${QrCode.encode(qrPairingCode)}\n`);
|
|
2298
1863
|
this.log.info(`${plg}${pluginName}${nf} \n\nqrPairingCode: ${qrPairingCode} \n\nManual pairing code: ${manualPairingCode}\n`);
|
|
2299
1864
|
if (pluginName === 'Matterbridge') {
|
|
@@ -2340,12 +1905,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2340
1905
|
}
|
|
2341
1906
|
this.wssSendRefreshRequired();
|
|
2342
1907
|
}
|
|
2343
|
-
/**
|
|
2344
|
-
* Sanitizes the fabric information by converting bigint properties to string cause res.json doesn't know bigint.
|
|
2345
|
-
*
|
|
2346
|
-
* @param fabricInfo - The array of exposed fabric information objects.
|
|
2347
|
-
* @returns An array of sanitized exposed fabric information objects.
|
|
2348
|
-
*/
|
|
2349
1908
|
sanitizeFabricInformations(fabricInfo) {
|
|
2350
1909
|
return fabricInfo.map((info) => {
|
|
2351
1910
|
return {
|
|
@@ -2359,12 +1918,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2359
1918
|
};
|
|
2360
1919
|
});
|
|
2361
1920
|
}
|
|
2362
|
-
/**
|
|
2363
|
-
* Sanitizes the session information by converting bigint properties to string.
|
|
2364
|
-
*
|
|
2365
|
-
* @param sessionInfo - The array of session information objects.
|
|
2366
|
-
* @returns An array of sanitized session information objects.
|
|
2367
|
-
*/
|
|
2368
1921
|
sanitizeSessionInformation(sessionInfo) {
|
|
2369
1922
|
return sessionInfo
|
|
2370
1923
|
.filter((session) => session.isPeerActive)
|
|
@@ -2392,12 +1945,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2392
1945
|
};
|
|
2393
1946
|
});
|
|
2394
1947
|
}
|
|
2395
|
-
/**
|
|
2396
|
-
* Sets the reachability of a commissioning server and trigger.
|
|
2397
|
-
*
|
|
2398
|
-
* @param {CommissioningServer} commissioningServer - The commissioning server to set the reachability for.
|
|
2399
|
-
* @param {boolean} reachable - The new reachability status.
|
|
2400
|
-
*/
|
|
2401
1948
|
setCommissioningServerReachability(commissioningServer, reachable) {
|
|
2402
1949
|
const basicInformationCluster = commissioningServer?.getRootClusterServer(BasicInformationCluster);
|
|
2403
1950
|
if (basicInformationCluster && basicInformationCluster.attributes.reachable !== undefined)
|
|
@@ -2405,11 +1952,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2405
1952
|
if (basicInformationCluster && basicInformationCluster.triggerReachableChangedEvent)
|
|
2406
1953
|
basicInformationCluster.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
2407
1954
|
}
|
|
2408
|
-
/**
|
|
2409
|
-
* Sets the reachability of the specified matter aggregator and its bridged devices and trigger.
|
|
2410
|
-
* @param {Aggregator} matterAggregator - The matter aggregator to set the reachability for.
|
|
2411
|
-
* @param {boolean} reachable - A boolean indicating the reachability status to set.
|
|
2412
|
-
*/
|
|
2413
1955
|
setAggregatorReachability(matterAggregator, reachable) {
|
|
2414
1956
|
const basicInformationCluster = matterAggregator.getClusterServer(BasicInformationCluster);
|
|
2415
1957
|
if (basicInformationCluster && basicInformationCluster.attributes.reachable !== undefined)
|
|
@@ -2422,12 +1964,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2422
1964
|
device.getClusterServer(BridgedDeviceBasicInformationCluster)?.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
2423
1965
|
});
|
|
2424
1966
|
}
|
|
2425
|
-
/**
|
|
2426
|
-
* Sets the reachability of a device and trigger.
|
|
2427
|
-
*
|
|
2428
|
-
* @param {MatterbridgeDevice} device - The device to set the reachability for.
|
|
2429
|
-
* @param {boolean} reachable - The new reachability status of the device.
|
|
2430
|
-
*/
|
|
2431
1967
|
setDeviceReachability(device, reachable) {
|
|
2432
1968
|
const basicInformationCluster = device.getClusterServer(BasicInformationCluster);
|
|
2433
1969
|
if (basicInformationCluster && basicInformationCluster.attributes.reachable !== undefined)
|
|
@@ -2476,10 +2012,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2476
2012
|
}
|
|
2477
2013
|
return vendorName;
|
|
2478
2014
|
};
|
|
2479
|
-
/**
|
|
2480
|
-
* Retrieves the base registered plugins sanitized for res.json().
|
|
2481
|
-
* @returns {BaseRegisteredPlugin[]} A promise that resolves to an array of BaseRegisteredPlugin objects.
|
|
2482
|
-
*/
|
|
2483
2015
|
async getBaseRegisteredPlugins() {
|
|
2484
2016
|
const baseRegisteredPlugins = [];
|
|
2485
2017
|
for (const plugin of this.plugins) {
|
|
@@ -2511,36 +2043,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2511
2043
|
}
|
|
2512
2044
|
return baseRegisteredPlugins;
|
|
2513
2045
|
}
|
|
2514
|
-
/**
|
|
2515
|
-
* Spawns a child process with the given command and arguments.
|
|
2516
|
-
* @param {string} command - The command to execute.
|
|
2517
|
-
* @param {string[]} args - The arguments to pass to the command (default: []).
|
|
2518
|
-
* @returns {Promise<boolean>} A promise that resolves when the child process exits successfully, or rejects if there is an error.
|
|
2519
|
-
*/
|
|
2520
2046
|
async spawnCommand(command, args = []) {
|
|
2521
|
-
/*
|
|
2522
|
-
npm > npm.cmd on windows
|
|
2523
|
-
cmd.exe ['dir'] on windows
|
|
2524
|
-
await this.spawnCommand('npm', ['install', '-g', 'matterbridge']);
|
|
2525
|
-
process.on('unhandledRejection', (reason, promise) => {
|
|
2526
|
-
this.log.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
2527
|
-
});
|
|
2528
|
-
|
|
2529
|
-
spawn - [14:27:21.125] [Matterbridge:spawn]: changed 38 packages in 4s
|
|
2530
|
-
spawn - [14:27:21.125] [Matterbridge:spawn]: 10 packages are looking for funding run `npm fund` for details
|
|
2531
|
-
debug - [14:27:21.131] [Matterbridge]: Child process exited with code 0 and signal null
|
|
2532
|
-
debug - [14:27:21.131] [Matterbridge]: Child process stdio streams have closed with code 0
|
|
2533
|
-
*/
|
|
2534
2047
|
const cmdLine = command + ' ' + args.join(' ');
|
|
2535
2048
|
if (process.platform === 'win32' && command === 'npm') {
|
|
2536
|
-
// Must be spawn('cmd.exe', ['/c', 'npm -g install <package>']);
|
|
2537
2049
|
const argstring = 'npm ' + args.join(' ');
|
|
2538
2050
|
args.splice(0, args.length, '/c', argstring);
|
|
2539
2051
|
command = 'cmd.exe';
|
|
2540
2052
|
}
|
|
2541
|
-
// Decide when using sudo on linux
|
|
2542
|
-
// When you need sudo: Spawn stderr: npm error Error: EACCES: permission denied
|
|
2543
|
-
// When you don't need sudo: Failed to start child process "npm install -g matterbridge-eve-door": spawn sudo ENOENT
|
|
2544
2053
|
if (hasParameter('sudo') || (process.platform === 'linux' && command === 'npm' && !hasParameter('docker') && !hasParameter('nosudo'))) {
|
|
2545
2054
|
args.unshift(command);
|
|
2546
2055
|
command = 'sudo';
|
|
@@ -2598,102 +2107,55 @@ export class Matterbridge extends EventEmitter {
|
|
|
2598
2107
|
}
|
|
2599
2108
|
});
|
|
2600
2109
|
}
|
|
2601
|
-
/**
|
|
2602
|
-
* Sends a WebSocket message to all connected clients.
|
|
2603
|
-
*
|
|
2604
|
-
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
2605
|
-
* @param {string} time - The time string of the message
|
|
2606
|
-
* @param {string} name - The logger name of the message
|
|
2607
|
-
* @param {string} message - The content of the message.
|
|
2608
|
-
*/
|
|
2609
2110
|
wssSendMessage(level, time, name, message) {
|
|
2610
2111
|
if (!level || !time || !name || !message)
|
|
2611
2112
|
return;
|
|
2612
|
-
// Remove ANSI escape codes from the message
|
|
2613
|
-
// eslint-disable-next-line no-control-regex
|
|
2614
2113
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
2615
|
-
// Remove leading asterisks from the message
|
|
2616
2114
|
message = message.replace(/^\*+/, '');
|
|
2617
|
-
// Replace all occurrences of \t and \n
|
|
2618
2115
|
message = message.replace(/[\t\n]/g, '');
|
|
2619
|
-
// Remove non-printable characters
|
|
2620
|
-
// eslint-disable-next-line no-control-regex
|
|
2621
2116
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
2622
|
-
// Replace all occurrences of \" with "
|
|
2623
2117
|
message = message.replace(/\\"/g, '"');
|
|
2624
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
2625
2118
|
const maxContinuousLength = 100;
|
|
2626
2119
|
const keepStartLength = 20;
|
|
2627
2120
|
const keepEndLength = 20;
|
|
2628
|
-
// Split the message into words
|
|
2629
2121
|
message = message
|
|
2630
2122
|
.split(' ')
|
|
2631
2123
|
.map((word) => {
|
|
2632
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
2633
2124
|
if (word.length > maxContinuousLength) {
|
|
2634
2125
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
2635
2126
|
}
|
|
2636
2127
|
return word;
|
|
2637
2128
|
})
|
|
2638
2129
|
.join(' ');
|
|
2639
|
-
// Send the message to all connected clients
|
|
2640
2130
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2641
2131
|
if (client.readyState === WebSocket.OPEN) {
|
|
2642
2132
|
client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
|
|
2643
2133
|
}
|
|
2644
2134
|
});
|
|
2645
2135
|
}
|
|
2646
|
-
/**
|
|
2647
|
-
* Sends a need to refresh WebSocket message to all connected clients.
|
|
2648
|
-
*
|
|
2649
|
-
*/
|
|
2650
2136
|
wssSendRefreshRequired() {
|
|
2651
2137
|
this.matterbridgeInformation.refreshRequired = true;
|
|
2652
|
-
// Send the message to all connected clients
|
|
2653
2138
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2654
2139
|
if (client.readyState === WebSocket.OPEN) {
|
|
2655
2140
|
client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Matterbridge', method: 'refresh_required', params: {} }));
|
|
2656
2141
|
}
|
|
2657
2142
|
});
|
|
2658
2143
|
}
|
|
2659
|
-
/**
|
|
2660
|
-
* Sends a need to restart WebSocket message to all connected clients.
|
|
2661
|
-
*
|
|
2662
|
-
*/
|
|
2663
2144
|
wssSendRestartRequired() {
|
|
2664
2145
|
this.matterbridgeInformation.restartRequired = true;
|
|
2665
|
-
// Send the message to all connected clients
|
|
2666
2146
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2667
2147
|
if (client.readyState === WebSocket.OPEN) {
|
|
2668
2148
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Matterbridge', method: 'restart_required', params: {} }));
|
|
2669
2149
|
}
|
|
2670
2150
|
});
|
|
2671
2151
|
}
|
|
2672
|
-
/**
|
|
2673
|
-
* Initializes the frontend of Matterbridge.
|
|
2674
|
-
*
|
|
2675
|
-
* @param port The port number to run the frontend server on. Default is 8283.
|
|
2676
|
-
*/
|
|
2677
2152
|
async initializeFrontend(port = 8283) {
|
|
2678
2153
|
let initializeError = false;
|
|
2679
2154
|
this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${port}${db}`);
|
|
2680
|
-
// Create the express app that serves the frontend
|
|
2681
2155
|
this.expressApp = express();
|
|
2682
|
-
// Log all requests to the server for debugging
|
|
2683
|
-
/*
|
|
2684
|
-
if (hasParameter('homedir')) {
|
|
2685
|
-
this.expressApp.use((req, res, next) => {
|
|
2686
|
-
this.log.debug(`Received request on expressApp: ${req.method} ${req.url}`);
|
|
2687
|
-
next();
|
|
2688
|
-
});
|
|
2689
|
-
}
|
|
2690
|
-
*/
|
|
2691
|
-
// Serve static files from '/static' endpoint
|
|
2692
2156
|
this.expressApp.use(express.static(path.join(this.rootDirectory, 'frontend/build')));
|
|
2693
2157
|
if (!hasParameter('ssl')) {
|
|
2694
|
-
// Create an HTTP server and attach the express app
|
|
2695
2158
|
this.httpServer = createServer(this.expressApp);
|
|
2696
|
-
// Listen on the specified port
|
|
2697
2159
|
if (hasParameter('ingress')) {
|
|
2698
2160
|
this.httpServer.listen(port, '0.0.0.0', () => {
|
|
2699
2161
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${port}${UNDERLINEOFF}${rs}`);
|
|
@@ -2707,7 +2169,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2707
2169
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.systemInformation.ipv6Address}]:${port}${UNDERLINEOFF}${rs}`);
|
|
2708
2170
|
});
|
|
2709
2171
|
}
|
|
2710
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2711
2172
|
this.httpServer.on('error', (error) => {
|
|
2712
2173
|
this.log.error(`Frontend http server error listening on ${port}`);
|
|
2713
2174
|
switch (error.code) {
|
|
@@ -2723,7 +2184,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2723
2184
|
});
|
|
2724
2185
|
}
|
|
2725
2186
|
else {
|
|
2726
|
-
// Load the SSL certificate, the private key and optionally the CA certificate
|
|
2727
2187
|
let cert;
|
|
2728
2188
|
try {
|
|
2729
2189
|
cert = await fs.readFile(path.join(this.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
|
|
@@ -2751,9 +2211,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2751
2211
|
this.log.info(`CA certificate file ${path.join(this.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
|
|
2752
2212
|
}
|
|
2753
2213
|
const serverOptions = { cert, key, ca };
|
|
2754
|
-
// Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
|
|
2755
2214
|
this.httpsServer = https.createServer(serverOptions, this.expressApp);
|
|
2756
|
-
// Listen on the specified port
|
|
2757
2215
|
if (hasParameter('ingress')) {
|
|
2758
2216
|
this.httpsServer.listen(port, '0.0.0.0', () => {
|
|
2759
2217
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${port}${UNDERLINEOFF}${rs}`);
|
|
@@ -2767,7 +2225,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2767
2225
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.systemInformation.ipv6Address}]:${port}${UNDERLINEOFF}${rs}`);
|
|
2768
2226
|
});
|
|
2769
2227
|
}
|
|
2770
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2771
2228
|
this.httpsServer.on('error', (error) => {
|
|
2772
2229
|
this.log.error(`Frontend https server error listening on ${port}`);
|
|
2773
2230
|
switch (error.code) {
|
|
@@ -2784,13 +2241,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2784
2241
|
}
|
|
2785
2242
|
if (initializeError)
|
|
2786
2243
|
return;
|
|
2787
|
-
// Createe a WebSocket server and attach it to the http or https server
|
|
2788
2244
|
const wssPort = port;
|
|
2789
2245
|
const wssHost = hasParameter('ssl') ? `wss://${this.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.systemInformation.ipv4Address}:${wssPort}`;
|
|
2790
2246
|
this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
|
|
2791
2247
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
2792
2248
|
const clientIp = request.socket.remoteAddress;
|
|
2793
|
-
AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), "debug"
|
|
2249
|
+
AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), "debug");
|
|
2794
2250
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
2795
2251
|
ws.on('message', (message) => {
|
|
2796
2252
|
this.log.debug(`WebSocket client message: ${message}`);
|
|
@@ -2823,7 +2279,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2823
2279
|
this.webSocketServer.on('error', (ws, error) => {
|
|
2824
2280
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
2825
2281
|
});
|
|
2826
|
-
// Endpoint to validate login code
|
|
2827
2282
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
2828
2283
|
const { password } = req.body;
|
|
2829
2284
|
this.log.debug('The frontend sent /api/login', password);
|
|
@@ -2842,14 +2297,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2842
2297
|
this.log.warn('/api/login error wrong password');
|
|
2843
2298
|
res.json({ valid: false });
|
|
2844
2299
|
}
|
|
2845
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2846
2300
|
}
|
|
2847
2301
|
catch (error) {
|
|
2848
2302
|
this.log.error('/api/login error getting password');
|
|
2849
2303
|
res.json({ valid: false });
|
|
2850
2304
|
}
|
|
2851
2305
|
});
|
|
2852
|
-
// Endpoint to provide settings
|
|
2853
2306
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
2854
2307
|
this.log.debug('The frontend sent /api/settings');
|
|
2855
2308
|
this.matterbridgeInformation.bridgeMode = this.bridgeMode;
|
|
@@ -2867,17 +2320,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2867
2320
|
this.matterbridgeInformation.matterbridgeSessionInformations = Array.from(this.matterbridgeSessionInformations.values());
|
|
2868
2321
|
this.matterbridgeInformation.profile = this.profile;
|
|
2869
2322
|
const response = { wssHost, ssl: hasParameter('ssl'), systemInformation: this.systemInformation, matterbridgeInformation: this.matterbridgeInformation };
|
|
2870
|
-
// this.log.debug('Response:', debugStringify(response));
|
|
2871
2323
|
res.json(response);
|
|
2872
2324
|
});
|
|
2873
|
-
// Endpoint to provide plugins
|
|
2874
2325
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
2875
2326
|
this.log.debug('The frontend sent /api/plugins');
|
|
2876
2327
|
const response = await this.getBaseRegisteredPlugins();
|
|
2877
|
-
// this.log.debug('Response:', debugStringify(response));
|
|
2878
2328
|
res.json(response);
|
|
2879
2329
|
});
|
|
2880
|
-
// Endpoint to provide devices
|
|
2881
2330
|
this.expressApp.get('/api/devices', (req, res) => {
|
|
2882
2331
|
this.log.debug('The frontend sent /api/devices');
|
|
2883
2332
|
const data = [];
|
|
@@ -2905,10 +2354,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2905
2354
|
cluster: cluster,
|
|
2906
2355
|
});
|
|
2907
2356
|
});
|
|
2908
|
-
// this.log.debug('Response:', debugStringify(data));
|
|
2909
2357
|
res.json(data);
|
|
2910
2358
|
});
|
|
2911
|
-
// Endpoint to provide the cluster servers of the devices
|
|
2912
2359
|
this.expressApp.get('/api/devices_clusters/:selectedPluginName/:selectedDeviceEndpoint', (req, res) => {
|
|
2913
2360
|
const selectedPluginName = req.params.selectedPluginName;
|
|
2914
2361
|
const selectedDeviceEndpoint = parseInt(req.params.selectedDeviceEndpoint, 10);
|
|
@@ -2928,7 +2375,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2928
2375
|
Object.entries(clusterServer.attributes).forEach(([key, value]) => {
|
|
2929
2376
|
if (clusterServer.name === 'EveHistory')
|
|
2930
2377
|
return;
|
|
2931
|
-
// this.log.debug(`***--clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute:${key}(${value.id}) ${value.isFixed} ${value.isWritable} ${value.isWritable}`);
|
|
2932
2378
|
let attributeValue;
|
|
2933
2379
|
try {
|
|
2934
2380
|
if (typeof value.getLocal() === 'object')
|
|
@@ -2939,7 +2385,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2939
2385
|
catch (error) {
|
|
2940
2386
|
attributeValue = 'Fabric-Scoped';
|
|
2941
2387
|
this.log.debug(`GetLocal value ${error} in clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute: ${key}(${value.id})`);
|
|
2942
|
-
// console.log(error);
|
|
2943
2388
|
}
|
|
2944
2389
|
data.push({
|
|
2945
2390
|
endpoint: device.number ? device.number.toString() : '...',
|
|
@@ -2952,14 +2397,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2952
2397
|
});
|
|
2953
2398
|
});
|
|
2954
2399
|
device.getChildEndpoints().forEach((childEndpoint) => {
|
|
2955
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2956
2400
|
const name = this.edge ? childEndpoint.endpoint?.id : childEndpoint.uniqueStorageKey;
|
|
2957
2401
|
const clusterServers = childEndpoint.getAllClusterServers();
|
|
2958
2402
|
clusterServers.forEach((clusterServer) => {
|
|
2959
2403
|
Object.entries(clusterServer.attributes).forEach(([key, value]) => {
|
|
2960
2404
|
if (clusterServer.name === 'EveHistory')
|
|
2961
2405
|
return;
|
|
2962
|
-
// this.log.debug(`***--clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute:${key}(${value.id}) ${value.isFixed} ${value.isWritable} ${value.isWritable}`);
|
|
2963
2406
|
let attributeValue;
|
|
2964
2407
|
try {
|
|
2965
2408
|
if (typeof value.getLocal() === 'object')
|
|
@@ -2970,7 +2413,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2970
2413
|
catch (error) {
|
|
2971
2414
|
attributeValue = 'Unavailable';
|
|
2972
2415
|
this.log.debug(`GetLocal error ${error} in clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute: ${key}(${value.id})`);
|
|
2973
|
-
// console.log(error);
|
|
2974
2416
|
}
|
|
2975
2417
|
data.push({
|
|
2976
2418
|
endpoint: (childEndpoint.number ? childEndpoint.number.toString() : '...') + (name ? ' (' + name + ')' : ''),
|
|
@@ -2987,7 +2429,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2987
2429
|
});
|
|
2988
2430
|
res.json(data);
|
|
2989
2431
|
});
|
|
2990
|
-
// Endpoint to view the log
|
|
2991
2432
|
this.expressApp.get('/api/view-log', async (req, res) => {
|
|
2992
2433
|
this.log.debug('The frontend sent /api/log');
|
|
2993
2434
|
try {
|
|
@@ -3000,12 +2441,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
3000
2441
|
res.status(500).send('Error reading log file');
|
|
3001
2442
|
}
|
|
3002
2443
|
});
|
|
3003
|
-
// Endpoint to download the matterbridge log
|
|
3004
2444
|
this.expressApp.get('/api/download-mblog', async (req, res) => {
|
|
3005
2445
|
this.log.debug('The frontend sent /api/download-mblog');
|
|
3006
2446
|
try {
|
|
3007
2447
|
await fs.access(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), fs.constants.F_OK);
|
|
3008
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
3009
2448
|
}
|
|
3010
2449
|
catch (error) {
|
|
3011
2450
|
fs.appendFile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), 'Enable the log on file in the settings to enable the file logger');
|
|
@@ -3017,12 +2456,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
3017
2456
|
}
|
|
3018
2457
|
});
|
|
3019
2458
|
});
|
|
3020
|
-
// Endpoint to download the matter log
|
|
3021
2459
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
3022
2460
|
this.log.debug('The frontend sent /api/download-mjlog');
|
|
3023
2461
|
try {
|
|
3024
2462
|
await fs.access(path.join(this.matterbridgeDirectory, this.matterLoggerFile), fs.constants.F_OK);
|
|
3025
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
3026
2463
|
}
|
|
3027
2464
|
catch (error) {
|
|
3028
2465
|
fs.appendFile(path.join(this.matterbridgeDirectory, this.matterLoggerFile), 'Enable the log on file in the settings to enable the file logger');
|
|
@@ -3034,7 +2471,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3034
2471
|
}
|
|
3035
2472
|
});
|
|
3036
2473
|
});
|
|
3037
|
-
// Endpoint to download the matter storage file
|
|
3038
2474
|
this.expressApp.get('/api/download-mjstorage', (req, res) => {
|
|
3039
2475
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
3040
2476
|
res.download(path.join(this.matterbridgeDirectory, this.matterStorageName), 'matterbridge.json', (error) => {
|
|
@@ -3044,7 +2480,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3044
2480
|
}
|
|
3045
2481
|
});
|
|
3046
2482
|
});
|
|
3047
|
-
// Endpoint to download the matterbridge storage directory
|
|
3048
2483
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
3049
2484
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
3050
2485
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.nodeStorageName}.zip`), path.join(this.matterbridgeDirectory, this.nodeStorageName));
|
|
@@ -3055,7 +2490,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3055
2490
|
}
|
|
3056
2491
|
});
|
|
3057
2492
|
});
|
|
3058
|
-
// Endpoint to download the matterbridge plugin directory
|
|
3059
2493
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
3060
2494
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
3061
2495
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridgePluginDirectory);
|
|
@@ -3066,11 +2500,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
3066
2500
|
}
|
|
3067
2501
|
});
|
|
3068
2502
|
});
|
|
3069
|
-
// Endpoint to download the matterbridge plugin config files
|
|
3070
2503
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
3071
2504
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
3072
2505
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridgeDirectory, '*.config.json')));
|
|
3073
|
-
// await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridgeDirectory, 'certs', '*.*')), path.relative(process.cwd(), path.join(this.matterbridgeDirectory, '*.config.json')));
|
|
3074
2506
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
|
|
3075
2507
|
if (error) {
|
|
3076
2508
|
this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
|
|
@@ -3078,7 +2510,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3078
2510
|
}
|
|
3079
2511
|
});
|
|
3080
2512
|
});
|
|
3081
|
-
// Endpoint to download the matterbridge plugin config files
|
|
3082
2513
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
3083
2514
|
this.log.debug('The frontend sent /api/download-backup');
|
|
3084
2515
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
@@ -3088,7 +2519,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3088
2519
|
}
|
|
3089
2520
|
});
|
|
3090
2521
|
});
|
|
3091
|
-
// Endpoint to receive commands
|
|
3092
2522
|
this.expressApp.post('/api/command/:command/:param', express.json(), async (req, res) => {
|
|
3093
2523
|
const command = req.params.command;
|
|
3094
2524
|
let param = req.params.param;
|
|
@@ -3098,15 +2528,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
3098
2528
|
return;
|
|
3099
2529
|
}
|
|
3100
2530
|
this.log.debug(`Received frontend command: ${command}:${param}`);
|
|
3101
|
-
// Handle the command setpassword from Settings
|
|
3102
2531
|
if (command === 'setpassword') {
|
|
3103
|
-
const password = param.slice(1, -1);
|
|
2532
|
+
const password = param.slice(1, -1);
|
|
3104
2533
|
this.log.debug('setpassword', param, password);
|
|
3105
2534
|
await this.nodeContext?.set('password', password);
|
|
3106
2535
|
res.json({ message: 'Command received' });
|
|
3107
2536
|
return;
|
|
3108
2537
|
}
|
|
3109
|
-
// Handle the command setbridgemode from Settings
|
|
3110
2538
|
if (command === 'setbridgemode') {
|
|
3111
2539
|
this.log.debug(`setbridgemode: ${param}`);
|
|
3112
2540
|
this.wssSendRestartRequired();
|
|
@@ -3114,7 +2542,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3114
2542
|
res.json({ message: 'Command received' });
|
|
3115
2543
|
return;
|
|
3116
2544
|
}
|
|
3117
|
-
// Handle the command backup from Settings
|
|
3118
2545
|
if (command === 'backup') {
|
|
3119
2546
|
this.log.notice(`Prepairing the backup...`);
|
|
3120
2547
|
await createZip(path.join(os.tmpdir(), `matterbridge.backup.zip`), path.join(this.matterbridgeDirectory), path.join(this.matterbridgePluginDirectory));
|
|
@@ -3122,26 +2549,25 @@ export class Matterbridge extends EventEmitter {
|
|
|
3122
2549
|
res.json({ message: 'Command received' });
|
|
3123
2550
|
return;
|
|
3124
2551
|
}
|
|
3125
|
-
// Handle the command setmbloglevel from Settings
|
|
3126
2552
|
if (command === 'setmbloglevel') {
|
|
3127
2553
|
this.log.debug('Matterbridge log level:', param);
|
|
3128
2554
|
if (param === 'Debug') {
|
|
3129
|
-
this.log.logLevel = "debug"
|
|
2555
|
+
this.log.logLevel = "debug";
|
|
3130
2556
|
}
|
|
3131
2557
|
else if (param === 'Info') {
|
|
3132
|
-
this.log.logLevel = "info"
|
|
2558
|
+
this.log.logLevel = "info";
|
|
3133
2559
|
}
|
|
3134
2560
|
else if (param === 'Notice') {
|
|
3135
|
-
this.log.logLevel = "notice"
|
|
2561
|
+
this.log.logLevel = "notice";
|
|
3136
2562
|
}
|
|
3137
2563
|
else if (param === 'Warn') {
|
|
3138
|
-
this.log.logLevel = "warn"
|
|
2564
|
+
this.log.logLevel = "warn";
|
|
3139
2565
|
}
|
|
3140
2566
|
else if (param === 'Error') {
|
|
3141
|
-
this.log.logLevel = "error"
|
|
2567
|
+
this.log.logLevel = "error";
|
|
3142
2568
|
}
|
|
3143
2569
|
else if (param === 'Fatal') {
|
|
3144
|
-
this.log.logLevel = "fatal"
|
|
2570
|
+
this.log.logLevel = "fatal";
|
|
3145
2571
|
}
|
|
3146
2572
|
await this.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
3147
2573
|
MatterbridgeDevice.logLevel = this.log.logLevel;
|
|
@@ -3149,13 +2575,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
3149
2575
|
for (const plugin of this.plugins) {
|
|
3150
2576
|
if (!plugin.platform || !plugin.platform.config)
|
|
3151
2577
|
continue;
|
|
3152
|
-
plugin.platform.log.logLevel = plugin.platform.config.debug ? "debug"
|
|
3153
|
-
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug ? "debug"
|
|
2578
|
+
plugin.platform.log.logLevel = plugin.platform.config.debug ? "debug" : this.log.logLevel;
|
|
2579
|
+
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug ? "debug" : this.log.logLevel);
|
|
3154
2580
|
}
|
|
3155
2581
|
res.json({ message: 'Command received' });
|
|
3156
2582
|
return;
|
|
3157
2583
|
}
|
|
3158
|
-
// Handle the command setmbloglevel from Settings
|
|
3159
2584
|
if (command === 'setmjloglevel') {
|
|
3160
2585
|
this.log.debug('Matter.js log level:', param);
|
|
3161
2586
|
if (param === 'Debug') {
|
|
@@ -3180,47 +2605,41 @@ export class Matterbridge extends EventEmitter {
|
|
|
3180
2605
|
res.json({ message: 'Command received' });
|
|
3181
2606
|
return;
|
|
3182
2607
|
}
|
|
3183
|
-
// Handle the command setmdnsinterface from Settings
|
|
3184
2608
|
if (command === 'setmdnsinterface') {
|
|
3185
|
-
param = param.slice(1, -1);
|
|
2609
|
+
param = param.slice(1, -1);
|
|
3186
2610
|
this.matterbridgeInformation.mattermdnsinterface = param;
|
|
3187
2611
|
this.log.debug('Matter.js mdns interface:', param === '' ? 'All interfaces' : param);
|
|
3188
2612
|
await this.nodeContext?.set('mattermdnsinterface', param);
|
|
3189
2613
|
res.json({ message: 'Command received' });
|
|
3190
2614
|
return;
|
|
3191
2615
|
}
|
|
3192
|
-
// Handle the command setipv4address from Settings
|
|
3193
2616
|
if (command === 'setipv4address') {
|
|
3194
|
-
param = param.slice(1, -1);
|
|
2617
|
+
param = param.slice(1, -1);
|
|
3195
2618
|
this.matterbridgeInformation.matteripv4address = param;
|
|
3196
2619
|
this.log.debug('Matter.js ipv4 address:', param === '' ? 'All ipv4 addresses' : param);
|
|
3197
2620
|
await this.nodeContext?.set('matteripv4address', param);
|
|
3198
2621
|
res.json({ message: 'Command received' });
|
|
3199
2622
|
return;
|
|
3200
2623
|
}
|
|
3201
|
-
// Handle the command setipv6address from Settings
|
|
3202
2624
|
if (command === 'setipv6address') {
|
|
3203
|
-
param = param.slice(1, -1);
|
|
2625
|
+
param = param.slice(1, -1);
|
|
3204
2626
|
this.matterbridgeInformation.matteripv6address = param;
|
|
3205
2627
|
this.log.debug('Matter.js ipv6 address:', param === '' ? 'All ipv6 addresses' : param);
|
|
3206
2628
|
await this.nodeContext?.set('matteripv6address', param);
|
|
3207
2629
|
res.json({ message: 'Command received' });
|
|
3208
2630
|
return;
|
|
3209
2631
|
}
|
|
3210
|
-
// Handle the command setmbloglevel from Settings
|
|
3211
2632
|
if (command === 'setmblogfile') {
|
|
3212
2633
|
this.log.debug('Matterbridge file log:', param);
|
|
3213
2634
|
this.matterbridgeInformation.fileLogger = param === 'true';
|
|
3214
2635
|
await this.nodeContext?.set('matterbridgeFileLog', param === 'true');
|
|
3215
|
-
// Create the file logger for matterbridge
|
|
3216
2636
|
if (param === 'true')
|
|
3217
|
-
AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), "debug"
|
|
2637
|
+
AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), "debug", true);
|
|
3218
2638
|
else
|
|
3219
2639
|
AnsiLogger.setGlobalLogfile(undefined);
|
|
3220
2640
|
res.json({ message: 'Command received' });
|
|
3221
2641
|
return;
|
|
3222
2642
|
}
|
|
3223
|
-
// Handle the command setmbloglevel from Settings
|
|
3224
2643
|
if (command === 'setmjlogfile') {
|
|
3225
2644
|
this.log.debug('Matter file log:', param);
|
|
3226
2645
|
this.matterbridgeInformation.matterFileLogger = param === 'true';
|
|
@@ -3247,43 +2666,36 @@ export class Matterbridge extends EventEmitter {
|
|
|
3247
2666
|
res.json({ message: 'Command received' });
|
|
3248
2667
|
return;
|
|
3249
2668
|
}
|
|
3250
|
-
// Handle the command unregister from Settings
|
|
3251
2669
|
if (command === 'unregister') {
|
|
3252
2670
|
await this.unregisterAndShutdownProcess();
|
|
3253
2671
|
res.json({ message: 'Command received' });
|
|
3254
2672
|
return;
|
|
3255
2673
|
}
|
|
3256
|
-
// Handle the command reset from Settings
|
|
3257
2674
|
if (command === 'reset') {
|
|
3258
2675
|
await this.shutdownProcessAndReset();
|
|
3259
2676
|
res.json({ message: 'Command received' });
|
|
3260
2677
|
return;
|
|
3261
2678
|
}
|
|
3262
|
-
// Handle the command factoryreset from Settings
|
|
3263
2679
|
if (command === 'factoryreset') {
|
|
3264
2680
|
await this.shutdownProcessAndFactoryReset();
|
|
3265
2681
|
res.json({ message: 'Command received' });
|
|
3266
2682
|
return;
|
|
3267
2683
|
}
|
|
3268
|
-
// Handle the command shutdown from Header
|
|
3269
2684
|
if (command === 'shutdown') {
|
|
3270
2685
|
await this.shutdownProcess();
|
|
3271
2686
|
res.json({ message: 'Command received' });
|
|
3272
2687
|
return;
|
|
3273
2688
|
}
|
|
3274
|
-
// Handle the command restart from Header
|
|
3275
2689
|
if (command === 'restart') {
|
|
3276
2690
|
await this.restartProcess();
|
|
3277
2691
|
res.json({ message: 'Command received' });
|
|
3278
2692
|
return;
|
|
3279
2693
|
}
|
|
3280
|
-
// Handle the command update from Header
|
|
3281
2694
|
if (command === 'update') {
|
|
3282
2695
|
this.log.info('Updating matterbridge...');
|
|
3283
2696
|
try {
|
|
3284
2697
|
await this.spawnCommand('npm', ['install', '-g', 'matterbridge', '--omit=dev', '--verbose']);
|
|
3285
2698
|
this.log.info('Matterbridge has been updated. Full restart required.');
|
|
3286
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
3287
2699
|
}
|
|
3288
2700
|
catch (error) {
|
|
3289
2701
|
this.log.error('Error updating matterbridge');
|
|
@@ -3293,11 +2705,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
3293
2705
|
res.json({ message: 'Command received' });
|
|
3294
2706
|
return;
|
|
3295
2707
|
}
|
|
3296
|
-
// Handle the command saveconfig from Home
|
|
3297
2708
|
if (command === 'saveconfig') {
|
|
3298
2709
|
param = param.replace(/\*/g, '\\');
|
|
3299
2710
|
this.log.info(`Saving config for plugin ${plg}${param}${nf}...`);
|
|
3300
|
-
// console.log('Req.body:', JSON.stringify(req.body, null, 2));
|
|
3301
2711
|
if (!this.plugins.has(param)) {
|
|
3302
2712
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
3303
2713
|
}
|
|
@@ -3311,35 +2721,28 @@ export class Matterbridge extends EventEmitter {
|
|
|
3311
2721
|
res.json({ message: 'Command received' });
|
|
3312
2722
|
return;
|
|
3313
2723
|
}
|
|
3314
|
-
// Handle the command installplugin from Home
|
|
3315
2724
|
if (command === 'installplugin') {
|
|
3316
2725
|
param = param.replace(/\*/g, '\\');
|
|
3317
2726
|
this.log.info(`Installing plugin ${plg}${param}${nf}...`);
|
|
3318
2727
|
try {
|
|
3319
2728
|
await this.spawnCommand('npm', ['install', '-g', param, '--omit=dev', '--verbose']);
|
|
3320
2729
|
this.log.info(`Plugin ${plg}${param}${nf} installed. Full restart required.`);
|
|
3321
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
3322
2730
|
}
|
|
3323
2731
|
catch (error) {
|
|
3324
2732
|
this.log.error(`Error installing plugin ${plg}${param}${er}`);
|
|
3325
2733
|
}
|
|
3326
2734
|
this.wssSendRestartRequired();
|
|
3327
|
-
// Also add the plugin to matterbridge so no return!
|
|
3328
|
-
// res.json({ message: 'Command received' });
|
|
3329
|
-
// return;
|
|
3330
2735
|
}
|
|
3331
|
-
// Handle the command addplugin from Home
|
|
3332
2736
|
if (command === 'addplugin' || command === 'installplugin') {
|
|
3333
2737
|
param = param.replace(/\*/g, '\\');
|
|
3334
2738
|
const plugin = await this.plugins.add(param);
|
|
3335
2739
|
if (plugin) {
|
|
3336
|
-
this.plugins.load(plugin, true, 'The plugin has been added', true);
|
|
2740
|
+
this.plugins.load(plugin, true, 'The plugin has been added', true);
|
|
3337
2741
|
}
|
|
3338
2742
|
res.json({ message: 'Command received' });
|
|
3339
2743
|
this.wssSendRefreshRequired();
|
|
3340
2744
|
return;
|
|
3341
2745
|
}
|
|
3342
|
-
// Handle the command removeplugin from Home
|
|
3343
2746
|
if (command === 'removeplugin') {
|
|
3344
2747
|
if (!this.plugins.has(param)) {
|
|
3345
2748
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
@@ -3353,7 +2756,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3353
2756
|
this.wssSendRefreshRequired();
|
|
3354
2757
|
return;
|
|
3355
2758
|
}
|
|
3356
|
-
// Handle the command enableplugin from Home
|
|
3357
2759
|
if (command === 'enableplugin') {
|
|
3358
2760
|
if (!this.plugins.has(param)) {
|
|
3359
2761
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
@@ -3371,14 +2773,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
3371
2773
|
plugin.registeredDevices = undefined;
|
|
3372
2774
|
plugin.addedDevices = undefined;
|
|
3373
2775
|
await this.plugins.enable(param);
|
|
3374
|
-
this.plugins.load(plugin, true, 'The plugin has been enabled', true);
|
|
2776
|
+
this.plugins.load(plugin, true, 'The plugin has been enabled', true);
|
|
3375
2777
|
}
|
|
3376
2778
|
}
|
|
3377
2779
|
res.json({ message: 'Command received' });
|
|
3378
2780
|
this.wssSendRefreshRequired();
|
|
3379
2781
|
return;
|
|
3380
2782
|
}
|
|
3381
|
-
// Handle the command disableplugin from Home
|
|
3382
2783
|
if (command === 'disableplugin') {
|
|
3383
2784
|
if (!this.plugins.has(param)) {
|
|
3384
2785
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
@@ -3395,7 +2796,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3395
2796
|
return;
|
|
3396
2797
|
}
|
|
3397
2798
|
});
|
|
3398
|
-
// Fallback for routing (must be the last route)
|
|
3399
2799
|
this.expressApp.get('*', (req, res) => {
|
|
3400
2800
|
this.log.debug('The frontend sent:', req.url);
|
|
3401
2801
|
this.log.debug('Response send file:', path.join(this.rootDirectory, 'frontend/build/index.html'));
|
|
@@ -3403,11 +2803,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3403
2803
|
});
|
|
3404
2804
|
this.log.debug(`Frontend initialized on port ${YELLOW}${port}${db} static ${UNDERLINE}${path.join(this.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
|
|
3405
2805
|
}
|
|
3406
|
-
/**
|
|
3407
|
-
* Retrieves the cluster text description from a given device.
|
|
3408
|
-
* @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
|
|
3409
|
-
* @returns {string} The attributes description of the cluster servers in the device.
|
|
3410
|
-
*/
|
|
3411
2806
|
getClusterTextFromDevice(device) {
|
|
3412
2807
|
const stringifyUserLabel = (endpoint) => {
|
|
3413
2808
|
const labelList = endpoint.getClusterServer(UserLabelCluster)?.attributes.labelList.getLocal();
|
|
@@ -3430,11 +2825,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
3430
2825
|
return '';
|
|
3431
2826
|
};
|
|
3432
2827
|
let attributes = '';
|
|
3433
|
-
// this.log.debug(`***getClusterTextFromDevice: ${device.deviceName} (${device.name})`);
|
|
3434
2828
|
const clusterServers = device.getAllClusterServers();
|
|
3435
2829
|
clusterServers.forEach((clusterServer) => {
|
|
3436
2830
|
try {
|
|
3437
|
-
// this.log.debug(`**--clusterServer: ${clusterServer.id} (${clusterServer.name})`);
|
|
3438
2831
|
if (clusterServer.name === 'OnOff')
|
|
3439
2832
|
attributes += `OnOff: ${clusterServer.attributes.onOff.getLocal()} `;
|
|
3440
2833
|
if (clusterServer.name === 'Switch')
|
|
@@ -3485,30 +2878,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
3485
2878
|
attributes += `${stringifyFixedLabel(device)} `;
|
|
3486
2879
|
if (clusterServer.name === 'UserLabel')
|
|
3487
2880
|
attributes += `${stringifyUserLabel(device)} `;
|
|
3488
|
-
// this.log.debug(`*--clusterServer: ${clusterServer.id} (${clusterServer.name})`);
|
|
3489
2881
|
}
|
|
3490
2882
|
catch (error) {
|
|
3491
2883
|
this.log.error(`getClusterTextFromDevice with ${clusterServer.name} error: ${error}`);
|
|
3492
2884
|
}
|
|
3493
2885
|
});
|
|
3494
|
-
// this.log.debug(`*getClusterTextFromDevice: ${device.deviceName} (${device.name})`);
|
|
3495
2886
|
return attributes;
|
|
3496
2887
|
}
|
|
3497
|
-
/**
|
|
3498
|
-
* Initializes the Matterbridge instance as extension for zigbee2mqtt.
|
|
3499
|
-
* @deprecated This method is deprecated and will be removed in a future version.
|
|
3500
|
-
*
|
|
3501
|
-
* @returns A Promise that resolves when the initialization is complete.
|
|
3502
|
-
*/
|
|
3503
2888
|
async startExtension(dataPath, extensionVersion, port = 5540) {
|
|
3504
|
-
// Set the bridge mode
|
|
3505
2889
|
this.bridgeMode = 'bridge';
|
|
3506
|
-
// Set the first port to use
|
|
3507
2890
|
this.port = port;
|
|
3508
|
-
|
|
3509
|
-
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: "info" /* LogLevel.INFO */ });
|
|
2891
|
+
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: "info" });
|
|
3510
2892
|
this.log.debug('Matterbridge extension is starting...');
|
|
3511
|
-
// Initialize NodeStorage
|
|
3512
2893
|
this.matterbridgeDirectory = dataPath;
|
|
3513
2894
|
this.log.debug('Creating node storage manager dir: ' + path.join(this.matterbridgeDirectory, 'node_storage'));
|
|
3514
2895
|
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, 'node_storage'), logging: false });
|
|
@@ -3527,13 +2908,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
3527
2908
|
};
|
|
3528
2909
|
this.plugins.set(plugin);
|
|
3529
2910
|
this.plugins.saveToStorage();
|
|
3530
|
-
// Log system info and create .matterbridge directory
|
|
3531
2911
|
await this.logNodeAndSystemInfo();
|
|
3532
2912
|
this.matterbridgeDirectory = dataPath;
|
|
3533
|
-
// Set matter.js logger level and format
|
|
3534
2913
|
Logger.defaultLogLevel = MatterLogLevel.INFO;
|
|
3535
2914
|
Logger.format = MatterLogFormat.ANSI;
|
|
3536
|
-
// Start the storage and create matterbridgeContext
|
|
3537
2915
|
await this.startMatterStorage('json', path.join(this.matterbridgeDirectory, this.matterStorageName));
|
|
3538
2916
|
if (!this.storageManager)
|
|
3539
2917
|
return false;
|
|
@@ -3543,7 +2921,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
3543
2921
|
await this.matterbridgeContext.set('softwareVersion', 1);
|
|
3544
2922
|
await this.matterbridgeContext.set('softwareVersionString', this.matterbridgeVersion);
|
|
3545
2923
|
await this.matterbridgeContext.set('hardwareVersion', 1);
|
|
3546
|
-
await this.matterbridgeContext.set('hardwareVersionString', extensionVersion);
|
|
2924
|
+
await this.matterbridgeContext.set('hardwareVersionString', extensionVersion);
|
|
3547
2925
|
this.matterServer = this.createMatterServer(this.storageManager);
|
|
3548
2926
|
this.log.debug(`Creating commissioning server for ${plg}Matterbridge${db}`);
|
|
3549
2927
|
this.commissioningServer = await this.createCommisioningServer(this.matterbridgeContext, 'Matterbridge');
|
|
@@ -3556,7 +2934,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3556
2934
|
await this.startMatterServer();
|
|
3557
2935
|
this.log.info('Matter server started');
|
|
3558
2936
|
await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
|
|
3559
|
-
// Set reachability to true and trigger event after 60 seconds
|
|
3560
2937
|
setTimeout(() => {
|
|
3561
2938
|
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
3562
2939
|
if (this.commissioningServer)
|
|
@@ -3566,31 +2943,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
3566
2943
|
}, 60 * 1000);
|
|
3567
2944
|
return this.commissioningServer.isCommissioned();
|
|
3568
2945
|
}
|
|
3569
|
-
/**
|
|
3570
|
-
* Close the Matterbridge instance as extension for zigbee2mqtt.
|
|
3571
|
-
* @deprecated This method is deprecated and will be removed in a future version.
|
|
3572
|
-
*
|
|
3573
|
-
* @returns A Promise that resolves when the initialization is complete.
|
|
3574
|
-
*/
|
|
3575
2946
|
async stopExtension() {
|
|
3576
|
-
// Closing matter
|
|
3577
2947
|
await this.stopMatterServer();
|
|
3578
|
-
// Clearing the session manager
|
|
3579
|
-
// this.matterbridgeContext?.createContext('SessionManager').clear();
|
|
3580
|
-
// Closing storage
|
|
3581
2948
|
await this.stopMatterStorage();
|
|
3582
2949
|
this.log.info('Matter server stopped');
|
|
3583
2950
|
}
|
|
3584
|
-
/**
|
|
3585
|
-
* Checks if the extension is commissioned.
|
|
3586
|
-
* @deprecated This method is deprecated and will be removed in a future version.
|
|
3587
|
-
*
|
|
3588
|
-
* @returns {boolean} Returns true if the extension is commissioned, false otherwise.
|
|
3589
|
-
*/
|
|
3590
2951
|
isExtensionCommissioned() {
|
|
3591
2952
|
if (!this.commissioningServer)
|
|
3592
2953
|
return false;
|
|
3593
2954
|
return this.commissioningServer.isCommissioned();
|
|
3594
2955
|
}
|
|
3595
2956
|
}
|
|
3596
|
-
//# sourceMappingURL=matterbridge.js.map
|