matterbridge 1.4.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -1
- package/README.md +7 -4
- package/dist/deviceManager.d.ts +46 -0
- package/dist/deviceManager.d.ts.map +1 -0
- package/dist/deviceManager.js +92 -0
- package/dist/deviceManager.js.map +1 -0
- package/dist/matterbridge.d.ts +18 -76
- package/dist/matterbridge.d.ts.map +1 -1
- package/dist/matterbridge.js +218 -440
- package/dist/matterbridge.js.map +1 -1
- package/dist/matterbridgeDevice.d.ts +18 -5
- package/dist/matterbridgeDevice.d.ts.map +1 -1
- package/dist/matterbridgeDevice.js +9 -5
- package/dist/matterbridgeDevice.js.map +1 -1
- package/dist/matterbridgePlatform.d.ts +6 -1
- package/dist/matterbridgePlatform.d.ts.map +1 -1
- package/dist/matterbridgePlatform.js +8 -0
- package/dist/matterbridgePlatform.js.map +1 -1
- package/dist/matterbridgeTypes.d.ts +28 -1
- package/dist/matterbridgeTypes.d.ts.map +1 -1
- package/dist/matterbridgeTypes.js +26 -1
- package/dist/matterbridgeTypes.js.map +1 -1
- package/dist/{plugins.d.ts → pluginManager.d.ts} +6 -3
- package/dist/pluginManager.d.ts.map +1 -0
- package/dist/{plugins.js → pluginManager.js} +26 -16
- package/dist/pluginManager.js.map +1 -0
- package/frontend/build/asset-manifest.json +6 -6
- package/frontend/build/index.html +1 -1
- package/frontend/build/static/css/{main.df840158.css → main.5174e68c.css} +2 -2
- package/frontend/build/static/css/{main.df840158.css.map → main.5174e68c.css.map} +1 -1
- package/frontend/build/static/js/{main.2a46688a.js → main.fd6f85a1.js} +3 -3
- package/frontend/build/static/js/main.fd6f85a1.js.map +1 -0
- package/package.json +12 -11
- package/dist/plugins.d.ts.map +0 -1
- package/dist/plugins.js.map +0 -1
- package/frontend/build/static/js/main.2a46688a.js.map +0 -1
- /package/frontend/build/static/js/{main.2a46688a.js.LICENSE.txt → main.fd6f85a1.js.LICENSE.txt} +0 -0
package/dist/matterbridge.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @file matterbridge.ts
|
|
5
5
|
* @author Luca Liguori
|
|
6
6
|
* @date 2023-12-29
|
|
7
|
-
* @version 1.
|
|
7
|
+
* @version 1.4.0
|
|
8
8
|
*
|
|
9
9
|
* Copyright 2023, 2024 Luca Liguori.
|
|
10
10
|
*
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
* limitations under the License. *
|
|
22
22
|
*/
|
|
23
23
|
import { NodeStorageManager } from 'node-persist-manager';
|
|
24
|
-
import { AnsiLogger,
|
|
25
|
-
import { fileURLToPath
|
|
24
|
+
import { AnsiLogger, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, stringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, hk, or, idn, BLUE, CYAN, nt } from 'node-ansi-logger';
|
|
25
|
+
import { fileURLToPath } from 'url';
|
|
26
26
|
import { promises as fs } from 'fs';
|
|
27
27
|
import { exec, spawn } from 'child_process';
|
|
28
28
|
import { createServer } from 'http';
|
|
@@ -34,7 +34,6 @@ import path from 'path';
|
|
|
34
34
|
import WebSocket, { WebSocketServer } from 'ws';
|
|
35
35
|
// Matterbridge
|
|
36
36
|
import { MatterbridgeDevice } from './matterbridgeDevice.js';
|
|
37
|
-
import { shelly_config, somfytahoma_config, zigbee2mqtt_config } from './defaultConfigSchema.js';
|
|
38
37
|
import { BridgedDeviceBasicInformation, BridgedDeviceBasicInformationCluster } from './cluster/BridgedDeviceBasicInformationCluster.js';
|
|
39
38
|
import { logInterfaces, wait, waiter } from './utils/utils.js';
|
|
40
39
|
// @project-chip/matter-node.js
|
|
@@ -47,7 +46,8 @@ import { ManualPairingCodeCodec, QrCodeSchema } from '@project-chip/matter-node.
|
|
|
47
46
|
import { StorageBackendDisk, StorageBackendJsonFile, StorageManager } from '@project-chip/matter-node.js/storage';
|
|
48
47
|
import { requireMinNodeVersion, getParameter, getIntParameter, hasParameter } from '@project-chip/matter-node.js/util';
|
|
49
48
|
import { CryptoNode } from '@project-chip/matter-node.js/crypto';
|
|
50
|
-
import {
|
|
49
|
+
import { PluginManager } from './pluginManager.js';
|
|
50
|
+
import { DeviceManager } from './deviceManager.js';
|
|
51
51
|
// Default colors
|
|
52
52
|
const plg = '\u001B[38;5;33m';
|
|
53
53
|
const dev = '\u001B[38;5;79m';
|
|
@@ -86,7 +86,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
86
86
|
matterbridgeConnected: false,
|
|
87
87
|
bridgeMode: '',
|
|
88
88
|
restartMode: '',
|
|
89
|
-
|
|
89
|
+
loggerLevel: "info" /* LogLevel.INFO */,
|
|
90
90
|
matterLoggerLevel: Level.INFO,
|
|
91
91
|
};
|
|
92
92
|
homeDirectory = '';
|
|
@@ -102,10 +102,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
102
102
|
matterbridgeConnected = false;
|
|
103
103
|
bridgeMode = '';
|
|
104
104
|
restartMode = '';
|
|
105
|
-
debugEnabled = false;
|
|
105
|
+
// public debugEnabled = false;
|
|
106
|
+
// public loggerLevel: LogLevel = LogLevel.INFO;
|
|
106
107
|
profile = getParameter('profile');
|
|
107
108
|
log;
|
|
108
109
|
plugins;
|
|
110
|
+
devices;
|
|
109
111
|
registeredDevices = [];
|
|
110
112
|
nodeStorage;
|
|
111
113
|
nodeContext;
|
|
@@ -202,9 +204,37 @@ export class Matterbridge extends EventEmitter {
|
|
|
202
204
|
if (hasParameter('docker'))
|
|
203
205
|
this.restartMode = 'docker';
|
|
204
206
|
// Set Matterbridge logger
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
207
|
+
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: "info" /* LogLevel.INFO */ });
|
|
208
|
+
// Set matter.js logger level and format
|
|
209
|
+
if (hasParameter('logger')) {
|
|
210
|
+
const level = getParameter('logger');
|
|
211
|
+
if (level === 'debug') {
|
|
212
|
+
this.log.logLevel = "debug" /* LogLevel.DEBUG */;
|
|
213
|
+
}
|
|
214
|
+
else if (level === 'info') {
|
|
215
|
+
this.log.logLevel = "info" /* LogLevel.INFO */;
|
|
216
|
+
}
|
|
217
|
+
else if (level === 'notice') {
|
|
218
|
+
this.log.logLevel = "notice" /* LogLevel.NOTICE */;
|
|
219
|
+
}
|
|
220
|
+
else if (level === 'warn') {
|
|
221
|
+
this.log.logLevel = "warn" /* LogLevel.WARN */;
|
|
222
|
+
}
|
|
223
|
+
else if (level === 'error') {
|
|
224
|
+
this.log.logLevel = "error" /* LogLevel.ERROR */;
|
|
225
|
+
}
|
|
226
|
+
else if (level === 'fatal') {
|
|
227
|
+
this.log.logLevel = "fatal" /* LogLevel.FATAL */;
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
|
|
231
|
+
this.log.logLevel = "info" /* LogLevel.INFO */;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
this.log.logLevel = "info" /* LogLevel.INFO */;
|
|
236
|
+
}
|
|
237
|
+
MatterbridgeDevice.logLevel = this.log.logLevel;
|
|
208
238
|
this.log.debug('Matterbridge is starting...');
|
|
209
239
|
// Set matter.js logger level and format
|
|
210
240
|
if (hasParameter('matterlogger')) {
|
|
@@ -228,7 +258,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
228
258
|
Logger.defaultLogLevel = Level.FATAL;
|
|
229
259
|
}
|
|
230
260
|
else {
|
|
231
|
-
this.log.warn(`Invalid
|
|
261
|
+
this.log.warn(`Invalid matter.js logger level: ${level}. Using default level "info".`);
|
|
232
262
|
Logger.defaultLogLevel = Level.INFO;
|
|
233
263
|
}
|
|
234
264
|
}
|
|
@@ -243,9 +273,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
243
273
|
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, this.nodeStorageName), writeQueue: false, expiredInterval: undefined, logging: false });
|
|
244
274
|
this.log.debug('Creating node storage context for matterbridge');
|
|
245
275
|
this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
|
|
246
|
-
// Initialize
|
|
247
|
-
this.plugins = new
|
|
276
|
+
// Initialize PluginManager
|
|
277
|
+
this.plugins = new PluginManager(this);
|
|
248
278
|
await this.plugins.loadFromStorage();
|
|
279
|
+
// Initialize DeviceManager
|
|
280
|
+
this.devices = new DeviceManager(this, this.nodeContext);
|
|
249
281
|
// Get the plugins from node storage and create the plugins node storage contexts
|
|
250
282
|
for (const plugin of this.plugins) {
|
|
251
283
|
const packageJson = await this.plugins.parse(plugin);
|
|
@@ -274,7 +306,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
274
306
|
}
|
|
275
307
|
// Log system info and create .matterbridge directory
|
|
276
308
|
await this.logNodeAndSystemInfo();
|
|
277
|
-
this.log.
|
|
309
|
+
this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ${hasParameter('bridge') ? 'mode bridge' : ''}${hasParameter('childbridge') ? 'mode childbridge' : ''}${hasParameter('controller') ? 'mode controller' : ''} ` +
|
|
278
310
|
`${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}running on ${this.systemInformation.osType} ${this.systemInformation.osRelease} ${this.systemInformation.osPlatform} ${this.systemInformation.osArch} `);
|
|
279
311
|
// Check node version and throw error
|
|
280
312
|
requireMinNodeVersion(18);
|
|
@@ -299,7 +331,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
299
331
|
- port [port]: start the commissioning server on the given port (default 5540)
|
|
300
332
|
- mdnsinterface [name]: set the interface to use for the matter server mdnsInterface (default all interfaces)
|
|
301
333
|
- frontend [port]: start the frontend on the given port (default 8283)
|
|
302
|
-
-
|
|
334
|
+
- logger: set the matterbridge logger level: debug | info | notice | warn | error | fatal (default info)
|
|
303
335
|
- matterlogger: set the matter.js logger level: debug | info | notice | warn | error | fatal (default info)
|
|
304
336
|
- reset: remove the commissioning for Matterbridge (bridge mode). Shutdown Matterbridge before using it!
|
|
305
337
|
- factoryreset: remove all commissioning information and reset all internal storages. Shutdown Matterbridge before using it!
|
|
@@ -417,13 +449,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
417
449
|
return;
|
|
418
450
|
}
|
|
419
451
|
// Start the matter storage and create the matterbridge context
|
|
420
|
-
await this.
|
|
452
|
+
await this.startMatterStorage('json', path.join(this.matterbridgeDirectory, this.matterStorageName));
|
|
421
453
|
this.log.debug(`Creating commissioning server context for ${plg}Matterbridge${db}`);
|
|
422
454
|
this.matterbridgeContext = await this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge aggregator');
|
|
423
455
|
if (hasParameter('reset') && getParameter('reset') === undefined) {
|
|
424
456
|
this.log.info('Resetting Matterbridge commissioning information...');
|
|
425
457
|
await this.matterbridgeContext?.clearAll();
|
|
426
|
-
await this.
|
|
458
|
+
await this.stopMatterStorage();
|
|
427
459
|
this.log.info('Reset done! Remove the device from the controller.');
|
|
428
460
|
this.emit('shutdown');
|
|
429
461
|
return;
|
|
@@ -443,7 +475,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
443
475
|
else {
|
|
444
476
|
this.log.warn(`Plugin ${plg}${getParameter('reset')}${wr} not registerd in matterbridge`);
|
|
445
477
|
}
|
|
446
|
-
await this.
|
|
478
|
+
await this.stopMatterStorage();
|
|
447
479
|
this.emit('shutdown');
|
|
448
480
|
return;
|
|
449
481
|
}
|
|
@@ -484,8 +516,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
484
516
|
this.log.debug('Adding matterbridge commissioning server to matter server');
|
|
485
517
|
await this.matterServer.addCommissioningServer(this.commissioningServer, { uniqueStorageKey: 'Matterbridge' });
|
|
486
518
|
for (const plugin of this.plugins) {
|
|
487
|
-
plugin.configJson = await this.
|
|
488
|
-
plugin.schemaJson = await this.
|
|
519
|
+
plugin.configJson = await this.plugins.loadConfig(plugin);
|
|
520
|
+
plugin.schemaJson = await this.plugins.loadSchema(plugin);
|
|
489
521
|
// Check if the plugin is available
|
|
490
522
|
if (!(await this.plugins.resolve(plugin.path))) {
|
|
491
523
|
this.log.error(`Plugin ${plg}${plugin.name}${er} not found. Disabling it.`);
|
|
@@ -509,7 +541,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
509
541
|
plugin.addedDevices = undefined;
|
|
510
542
|
plugin.qrPairingCode = undefined;
|
|
511
543
|
plugin.manualPairingCode = undefined;
|
|
512
|
-
this.loadPlugin(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
|
|
544
|
+
this.plugins.load(plugin, true, 'Matterbridge is starting'); // this.loadPlugin(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
|
|
513
545
|
}
|
|
514
546
|
await this.startBridge();
|
|
515
547
|
return;
|
|
@@ -522,8 +554,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
522
554
|
this.log.debug('Starting matterbridge in mode', this.bridgeMode);
|
|
523
555
|
this.matterServer = this.createMatterServer(this.storageManager);
|
|
524
556
|
for (const plugin of this.plugins) {
|
|
525
|
-
plugin.configJson = await this.
|
|
526
|
-
plugin.schemaJson = await this.
|
|
557
|
+
plugin.configJson = await this.plugins.loadConfig(plugin);
|
|
558
|
+
plugin.schemaJson = await this.plugins.loadSchema(plugin);
|
|
527
559
|
// Check if the plugin is available
|
|
528
560
|
if (!(await this.plugins.resolve(plugin.path))) {
|
|
529
561
|
this.log.error(`Plugin ${plg}${plugin.name}${er} not found. Disabling it.`);
|
|
@@ -547,7 +579,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
547
579
|
plugin.addedDevices = undefined;
|
|
548
580
|
plugin.qrPairingCode = (await plugin.nodeContext?.get('qrPairingCode', undefined)) ?? undefined;
|
|
549
581
|
plugin.manualPairingCode = (await plugin.nodeContext?.get('manualPairingCode', undefined)) ?? undefined;
|
|
550
|
-
this.loadPlugin(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
|
|
582
|
+
this.plugins.load(plugin, true, 'Matterbridge is starting'); // this.loadPlugin(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
|
|
551
583
|
}
|
|
552
584
|
await this.startChildbridge();
|
|
553
585
|
return;
|
|
@@ -945,8 +977,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
945
977
|
// this.cleanupTimeout1 = setTimeout(async () => {
|
|
946
978
|
// Closing matter
|
|
947
979
|
await this.stopMatterServer();
|
|
948
|
-
// Closing storage
|
|
949
|
-
await this.
|
|
980
|
+
// Closing matter storage
|
|
981
|
+
await this.stopMatterStorage();
|
|
950
982
|
// Serialize registeredDevices
|
|
951
983
|
if (this.nodeStorage && this.nodeContext) {
|
|
952
984
|
this.log.info('Saving registered devices...');
|
|
@@ -1010,7 +1042,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1010
1042
|
await fs.rm(path.join(this.matterbridgeDirectory, this.nodeStorageName), { recursive: true });
|
|
1011
1043
|
this.log.info('Factory reset done! Remove all paired devices from the controllers.');
|
|
1012
1044
|
}
|
|
1013
|
-
this.log.
|
|
1045
|
+
this.log.notice('Cleanup completed. Shutting down...');
|
|
1014
1046
|
Matterbridge.instance = undefined;
|
|
1015
1047
|
this.emit('shutdown');
|
|
1016
1048
|
}
|
|
@@ -1027,10 +1059,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1027
1059
|
* @returns {Promise<void>} - A promise that resolves when the device is added.
|
|
1028
1060
|
*/
|
|
1029
1061
|
async addBridgedDevice(pluginName, device) {
|
|
1030
|
-
if (this.bridgeMode === 'bridge' && !this.matterAggregator) {
|
|
1031
|
-
this.log.error(`Adding bridged device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) for plugin ${plg}${pluginName}${er} error: matterAggregator not found`);
|
|
1032
|
-
return;
|
|
1033
|
-
}
|
|
1034
1062
|
this.log.debug(`Adding bridged device ${dev}${device.deviceName}${db} (${dev}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
1035
1063
|
// Check if the plugin is registered
|
|
1036
1064
|
const plugin = this.plugins.get(pluginName);
|
|
@@ -1040,12 +1068,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
1040
1068
|
}
|
|
1041
1069
|
// Register and add the device to matterbridge aggregator in bridge mode
|
|
1042
1070
|
if (this.bridgeMode === 'bridge') {
|
|
1043
|
-
this.matterAggregator
|
|
1071
|
+
if (!this.matterAggregator) {
|
|
1072
|
+
this.log.error(`Adding bridged device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) for plugin ${plg}${pluginName}${er} error: matterAggregator not found`);
|
|
1073
|
+
return;
|
|
1074
|
+
}
|
|
1075
|
+
this.matterAggregator.addBridgedDevice(device);
|
|
1044
1076
|
}
|
|
1045
1077
|
// The first time create the commissioning server and the aggregator for DynamicPlatform
|
|
1046
1078
|
// Register and add the device in childbridge mode
|
|
1047
1079
|
if (this.bridgeMode === 'childbridge') {
|
|
1048
1080
|
if (plugin.type === 'AccessoryPlatform') {
|
|
1081
|
+
// Check if the plugin is locked with the commissioning server
|
|
1049
1082
|
if (!plugin.locked) {
|
|
1050
1083
|
plugin.locked = true;
|
|
1051
1084
|
plugin.storageContext = await this.importCommissioningServerContext(plugin.name, device);
|
|
@@ -1059,10 +1092,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
1059
1092
|
}
|
|
1060
1093
|
}
|
|
1061
1094
|
if (plugin.type === 'DynamicPlatform') {
|
|
1095
|
+
// Check if the plugin is locked with the commissioning server and the aggregator
|
|
1062
1096
|
if (!plugin.locked) {
|
|
1063
1097
|
plugin.locked = true;
|
|
1064
1098
|
this.log.debug(`Creating commissioning server context for ${plg}${plugin.name}${db}`);
|
|
1065
|
-
// plugin.storageContext = await this.createCommissioningServerContext(plugin.name, 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Dynamic Platform');
|
|
1066
1099
|
plugin.storageContext = await this.createCommissioningServerContext(plugin.name, 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, plugin.description);
|
|
1067
1100
|
this.log.debug(`Creating commissioning server for ${plg}${plugin.name}${db}`);
|
|
1068
1101
|
plugin.commissioningServer = await this.createCommisioningServer(plugin.storageContext, plugin.name);
|
|
@@ -1081,6 +1114,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1081
1114
|
plugin.registeredDevices++;
|
|
1082
1115
|
if (plugin.addedDevices !== undefined)
|
|
1083
1116
|
plugin.addedDevices++;
|
|
1117
|
+
// Add the device to the DeviceManager
|
|
1118
|
+
this.devices.set(device);
|
|
1084
1119
|
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}`);
|
|
1085
1120
|
}
|
|
1086
1121
|
/**
|
|
@@ -1090,10 +1125,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1090
1125
|
* @returns A Promise that resolves when the device is successfully removed.
|
|
1091
1126
|
*/
|
|
1092
1127
|
async removeBridgedDevice(pluginName, device) {
|
|
1093
|
-
if (this.bridgeMode === 'bridge' && !this.matterAggregator) {
|
|
1094
|
-
this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) for plugin ${plg}${pluginName}${er}: matterAggregator not found`);
|
|
1095
|
-
return;
|
|
1096
|
-
}
|
|
1097
1128
|
this.log.debug(`Removing bridged device ${dev}${device.deviceName}${db} (${dev}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
1098
1129
|
// Check if the plugin is registered
|
|
1099
1130
|
const plugin = this.plugins.get(pluginName);
|
|
@@ -1101,18 +1132,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
1101
1132
|
this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
1102
1133
|
return;
|
|
1103
1134
|
}
|
|
1104
|
-
if (this.bridgeMode === 'childbridge' && plugin.type === 'AccessoryPlatfoem' && !plugin.aggregator) {
|
|
1105
|
-
this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator not found`);
|
|
1106
|
-
return;
|
|
1107
|
-
}
|
|
1108
|
-
if (this.bridgeMode === 'childbridge' && plugin.type === 'DynamicPlatfoem' && !plugin.commissioningServer) {
|
|
1109
|
-
this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) for plugin ${plg}${pluginName}${er}: commissioning server not found`);
|
|
1110
|
-
return;
|
|
1111
|
-
}
|
|
1112
1135
|
// Remove the device from matterbridge aggregator in bridge mode
|
|
1113
1136
|
if (this.bridgeMode === 'bridge') {
|
|
1137
|
+
if (!this.matterAggregator) {
|
|
1138
|
+
this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) for plugin ${plg}${pluginName}${er}: matterAggregator not found`);
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1114
1141
|
device.setBridgedDeviceReachability(false);
|
|
1115
1142
|
device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
|
|
1143
|
+
// device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerShutDownEvent({});
|
|
1144
|
+
// device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerLeaveEvent({});
|
|
1116
1145
|
this.matterAggregator?.removeBridgedDevice(device);
|
|
1117
1146
|
this.registeredDevices.forEach((registeredDevice, index) => {
|
|
1118
1147
|
if (registeredDevice.device === device) {
|
|
@@ -1129,6 +1158,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
1129
1158
|
// Remove the device in childbridge mode
|
|
1130
1159
|
if (this.bridgeMode === 'childbridge') {
|
|
1131
1160
|
if (plugin.type === 'AccessoryPlatform') {
|
|
1161
|
+
if (!plugin.commissioningServer) {
|
|
1162
|
+
this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) for plugin ${plg}${pluginName}${er}: commissioning server not found`);
|
|
1163
|
+
return;
|
|
1164
|
+
}
|
|
1132
1165
|
this.registeredDevices.forEach((registeredDevice, index) => {
|
|
1133
1166
|
if (registeredDevice.device === device) {
|
|
1134
1167
|
this.registeredDevices.splice(index, 1);
|
|
@@ -1137,6 +1170,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
1137
1170
|
});
|
|
1138
1171
|
}
|
|
1139
1172
|
else if (plugin.type === 'DynamicPlatform') {
|
|
1173
|
+
if (!plugin.aggregator) {
|
|
1174
|
+
this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator not found`);
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1140
1177
|
this.registeredDevices.forEach((registeredDevice, index) => {
|
|
1141
1178
|
if (registeredDevice.device === device) {
|
|
1142
1179
|
this.registeredDevices.splice(index, 1);
|
|
@@ -1145,19 +1182,22 @@ export class Matterbridge extends EventEmitter {
|
|
|
1145
1182
|
});
|
|
1146
1183
|
device.setBridgedDeviceReachability(false);
|
|
1147
1184
|
device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
|
|
1148
|
-
plugin.aggregator
|
|
1185
|
+
plugin.aggregator.removeBridgedDevice(device);
|
|
1149
1186
|
}
|
|
1150
1187
|
this.log.info(`Removed bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
1151
1188
|
if (plugin.registeredDevices !== undefined)
|
|
1152
1189
|
plugin.registeredDevices--;
|
|
1153
1190
|
if (plugin.addedDevices !== undefined)
|
|
1154
1191
|
plugin.addedDevices--;
|
|
1192
|
+
// Remove the commissioning server
|
|
1155
1193
|
if (plugin.registeredDevices === 0 && plugin.addedDevices === 0 && plugin.commissioningServer) {
|
|
1156
1194
|
this.matterServer?.removeCommissioningServer(plugin.commissioningServer);
|
|
1157
1195
|
plugin.commissioningServer = undefined;
|
|
1158
1196
|
this.log.info(`Removed commissioning server for plugin ${plg}${pluginName}${nf}`);
|
|
1159
1197
|
}
|
|
1160
1198
|
}
|
|
1199
|
+
// Remove the device from the DeviceManager
|
|
1200
|
+
this.devices.remove(device);
|
|
1161
1201
|
}
|
|
1162
1202
|
/**
|
|
1163
1203
|
* Removes all bridged devices associated with a specific plugin.
|
|
@@ -1177,304 +1217,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1177
1217
|
this.removeBridgedDevice(pluginName, registeredDevice.device);
|
|
1178
1218
|
}
|
|
1179
1219
|
}
|
|
1180
|
-
/**
|
|
1181
|
-
* Loads the schema for a plugin.
|
|
1182
|
-
* If the schema file exists in the plugin directory, it reads the file and returns the parsed JSON data and delete the schema form .matterbridge.
|
|
1183
|
-
* If the schema file does not exist, it creates a new schema with the default configuration and returns it.
|
|
1184
|
-
*
|
|
1185
|
-
* @param plugin - The plugin for which to load the schema.
|
|
1186
|
-
* @returns A promise that resolves to the loaded or created schema.
|
|
1187
|
-
*/
|
|
1188
|
-
async loadPluginSchema(plugin) {
|
|
1189
|
-
let schemaFile = plugin.path.replace('package.json', `${plugin.name}.schema.json`);
|
|
1190
|
-
try {
|
|
1191
|
-
await fs.access(schemaFile);
|
|
1192
|
-
const data = await fs.readFile(schemaFile, 'utf8');
|
|
1193
|
-
const schema = JSON.parse(data);
|
|
1194
|
-
schema.title = plugin.description;
|
|
1195
|
-
schema.description = plugin.name + ' v. ' + plugin.version + ' by ' + plugin.author;
|
|
1196
|
-
this.log.debug(`Schema file found: ${schemaFile}.`);
|
|
1197
|
-
// this.log.debug(`Schema file found: ${schemaFile}.\nSchema:${rs}\n`, schema);
|
|
1198
|
-
schemaFile = path.join(this.matterbridgeDirectory, `${plugin.name}.schema.json`);
|
|
1199
|
-
try {
|
|
1200
|
-
await fs.unlink(schemaFile);
|
|
1201
|
-
this.log.debug(`Schema file ${schemaFile} deleted.`);
|
|
1202
|
-
}
|
|
1203
|
-
catch (err) {
|
|
1204
|
-
this.log.debug(`Schema file ${schemaFile} to delete not found.`);
|
|
1205
|
-
}
|
|
1206
|
-
return schema;
|
|
1207
|
-
}
|
|
1208
|
-
catch (err) {
|
|
1209
|
-
this.log.debug(`Schema file ${schemaFile} not found. Loading default schema.`);
|
|
1210
|
-
const schema = {
|
|
1211
|
-
title: plugin.description,
|
|
1212
|
-
description: plugin.name + ' v. ' + plugin.version + ' by ' + plugin.author,
|
|
1213
|
-
type: 'object',
|
|
1214
|
-
properties: {
|
|
1215
|
-
name: {
|
|
1216
|
-
description: 'Plugin name',
|
|
1217
|
-
type: 'string',
|
|
1218
|
-
readOnly: true,
|
|
1219
|
-
},
|
|
1220
|
-
type: {
|
|
1221
|
-
description: 'Plugin type',
|
|
1222
|
-
type: 'string',
|
|
1223
|
-
readOnly: true,
|
|
1224
|
-
},
|
|
1225
|
-
debug: {
|
|
1226
|
-
description: 'Enable the debug for the plugin (development only)',
|
|
1227
|
-
type: 'boolean',
|
|
1228
|
-
default: false,
|
|
1229
|
-
},
|
|
1230
|
-
unregisterOnShutdown: {
|
|
1231
|
-
description: 'Unregister all devices on shutdown (development only)',
|
|
1232
|
-
type: 'boolean',
|
|
1233
|
-
default: false,
|
|
1234
|
-
},
|
|
1235
|
-
},
|
|
1236
|
-
};
|
|
1237
|
-
return schema;
|
|
1238
|
-
}
|
|
1239
|
-
}
|
|
1240
|
-
/**
|
|
1241
|
-
* Loads the configuration for a plugin.
|
|
1242
|
-
* If the configuration file exists, it reads the file and returns the parsed JSON data.
|
|
1243
|
-
* If the configuration file does not exist, it creates a new file with default configuration and returns it.
|
|
1244
|
-
* If any error occurs during file access or creation, it logs an error and return un empty config.
|
|
1245
|
-
*
|
|
1246
|
-
* @param plugin - The plugin for which to load the configuration.
|
|
1247
|
-
* @returns A promise that resolves to the loaded or created configuration.
|
|
1248
|
-
*/
|
|
1249
|
-
async loadPluginConfig(plugin) {
|
|
1250
|
-
const configFile = path.join(this.matterbridgeDirectory, `${plugin.name}.config.json`);
|
|
1251
|
-
try {
|
|
1252
|
-
await fs.access(configFile);
|
|
1253
|
-
const data = await fs.readFile(configFile, 'utf8');
|
|
1254
|
-
const config = JSON.parse(data);
|
|
1255
|
-
this.log.debug(`Config file found: ${configFile}.`);
|
|
1256
|
-
// this.log.debug(`Config file found: ${configFile}.\nConfig:${rs}\n`, config);
|
|
1257
|
-
/* The first time a plugin is added to the system, the config file is created with the plugin name and type "".*/
|
|
1258
|
-
config.name = plugin.name;
|
|
1259
|
-
config.type = plugin.type;
|
|
1260
|
-
if (config.debug === undefined)
|
|
1261
|
-
config.debug = false;
|
|
1262
|
-
if (config.unregisterOnShutdown === undefined)
|
|
1263
|
-
config.unregisterOnShutdown = false;
|
|
1264
|
-
return config;
|
|
1265
|
-
}
|
|
1266
|
-
catch (err) {
|
|
1267
|
-
if (err instanceof Error) {
|
|
1268
|
-
const nodeErr = err;
|
|
1269
|
-
if (nodeErr.code === 'ENOENT') {
|
|
1270
|
-
let config;
|
|
1271
|
-
if (plugin.name === 'matterbridge-zigbee2mqtt')
|
|
1272
|
-
config = zigbee2mqtt_config;
|
|
1273
|
-
else if (plugin.name === 'matterbridge-somfy-tahoma')
|
|
1274
|
-
config = somfytahoma_config;
|
|
1275
|
-
else if (plugin.name === 'matterbridge-shelly')
|
|
1276
|
-
config = shelly_config;
|
|
1277
|
-
else
|
|
1278
|
-
config = { name: plugin.name, type: plugin.type, debug: false, unregisterOnShutdown: false };
|
|
1279
|
-
try {
|
|
1280
|
-
await this.writeFile(configFile, JSON.stringify(config, null, 2));
|
|
1281
|
-
this.log.debug(`Created config file: ${configFile}.`);
|
|
1282
|
-
// this.log.debug(`Created config file: ${configFile}.\nConfig:${rs}\n`, config);
|
|
1283
|
-
return config;
|
|
1284
|
-
}
|
|
1285
|
-
catch (err) {
|
|
1286
|
-
this.log.error(`Error creating config file ${configFile}: ${err}`);
|
|
1287
|
-
return config;
|
|
1288
|
-
}
|
|
1289
|
-
}
|
|
1290
|
-
else {
|
|
1291
|
-
this.log.error(`Error accessing config file ${configFile}: ${err}`);
|
|
1292
|
-
return { name: plugin.name, type: plugin.type, debug: false, unregisterOnShutdown: false };
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
this.log.error(`Error loading config file ${configFile}: ${err}`);
|
|
1296
|
-
return { name: plugin.name, type: plugin.type, debug: false, unregisterOnShutdown: false };
|
|
1297
|
-
}
|
|
1298
|
-
}
|
|
1299
|
-
/**
|
|
1300
|
-
* Saves the configuration of a registered plugin.
|
|
1301
|
-
* @param {RegisteredPlugin} plugin - The plugin whose configuration needs to be saved.
|
|
1302
|
-
* @returns {Promise<void>} - A promise that resolves when the configuration is successfully saved.
|
|
1303
|
-
* @throws {Error} - If the plugin's configuration is not found or if there is an error while saving the configuration.
|
|
1304
|
-
*/
|
|
1305
|
-
async savePluginConfig(plugin) {
|
|
1306
|
-
if (!plugin.platform?.config) {
|
|
1307
|
-
this.log.error(`Error saving plugin ${plg}${plugin.name}${er} config: config not found`);
|
|
1308
|
-
return Promise.reject(new Error(`Error saving plugin ${plg}${plugin.name}${er} config: config not found`));
|
|
1309
|
-
}
|
|
1310
|
-
const configFile = path.join(this.matterbridgeDirectory, `${plugin.name}.config.json`);
|
|
1311
|
-
try {
|
|
1312
|
-
await this.writeFile(configFile, JSON.stringify(plugin.platform.config, null, 2));
|
|
1313
|
-
this.log.debug(`Saved config file: ${configFile}.`);
|
|
1314
|
-
// this.log.debug(`Saved config file: ${configFile}.\nConfig:${rs}\n`, plugin.platform.config);
|
|
1315
|
-
}
|
|
1316
|
-
catch (err) {
|
|
1317
|
-
this.log.error(`Error saving plugin ${plg}${plugin.name}${er} config: ${err}`);
|
|
1318
|
-
return Promise.reject(err);
|
|
1319
|
-
}
|
|
1320
|
-
}
|
|
1321
|
-
/**
|
|
1322
|
-
* Writes data to a file.
|
|
1323
|
-
*
|
|
1324
|
-
* @param {string} filePath - The path of the file to write to.
|
|
1325
|
-
* @param {string} data - The data to write to the file.
|
|
1326
|
-
* @returns {Promise<void>} - A promise that resolves when the data is successfully written to the file.
|
|
1327
|
-
*/
|
|
1328
|
-
async writeFile(filePath, data) {
|
|
1329
|
-
try {
|
|
1330
|
-
await fs.writeFile(`${filePath}`, data, 'utf8');
|
|
1331
|
-
this.log.debug(`Successfully wrote to ${filePath}`);
|
|
1332
|
-
}
|
|
1333
|
-
catch (error) {
|
|
1334
|
-
this.log.error(`Error writing to ${filePath}:`, error);
|
|
1335
|
-
}
|
|
1336
|
-
}
|
|
1337
|
-
/**
|
|
1338
|
-
* Loads a plugin and returns the corresponding MatterbridgePlatform instance.
|
|
1339
|
-
* @param plugin - The plugin to load.
|
|
1340
|
-
* @param start - Optional flag indicating whether to start the plugin after loading. Default is false.
|
|
1341
|
-
* @param message - Optional message to pass to the plugin when starting.
|
|
1342
|
-
* @returns A Promise that resolves to the loaded MatterbridgePlatform instance.
|
|
1343
|
-
* @throws An error if the plugin is not enabled, already loaded, or fails to load.
|
|
1344
|
-
*/
|
|
1345
|
-
async loadPlugin(plugin, start = false, message = '') {
|
|
1346
|
-
if (!plugin.enabled) {
|
|
1347
|
-
this.log.error(`Plugin ${plg}${plugin.name}${er} not enabled`);
|
|
1348
|
-
return Promise.resolve(undefined);
|
|
1349
|
-
}
|
|
1350
|
-
if (plugin.platform) {
|
|
1351
|
-
this.log.error(`Plugin ${plg}${plugin.name}${er} already loaded`);
|
|
1352
|
-
return Promise.resolve(plugin.platform);
|
|
1353
|
-
}
|
|
1354
|
-
this.log.info(`Loading plugin ${plg}${plugin.name}${nf} type ${typ}${plugin.type}${nf}`);
|
|
1355
|
-
try {
|
|
1356
|
-
// Load the package.json of the plugin
|
|
1357
|
-
const packageJson = JSON.parse(await fs.readFile(plugin.path, 'utf8'));
|
|
1358
|
-
// Resolve the main module path relative to package.json
|
|
1359
|
-
const pluginEntry = path.resolve(path.dirname(plugin.path), packageJson.main);
|
|
1360
|
-
// Dynamically import the plugin
|
|
1361
|
-
const pluginUrl = pathToFileURL(pluginEntry);
|
|
1362
|
-
this.log.debug(`Importing plugin ${plg}${plugin.name}${db} from ${pluginUrl.href}`);
|
|
1363
|
-
const pluginInstance = await import(pluginUrl.href);
|
|
1364
|
-
this.log.debug(`Imported plugin ${plg}${plugin.name}${db} from ${pluginUrl.href}`);
|
|
1365
|
-
// Call the default export function of the plugin, passing this MatterBridge instance, the log and the config
|
|
1366
|
-
if (pluginInstance.default) {
|
|
1367
|
-
const config = await this.loadPluginConfig(plugin);
|
|
1368
|
-
const log = new AnsiLogger({ logName: plugin.description ?? 'No description', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logDebug: config.debug ?? false });
|
|
1369
|
-
const platform = pluginInstance.default(this, log, config);
|
|
1370
|
-
platform.name = packageJson.name;
|
|
1371
|
-
platform.config = config;
|
|
1372
|
-
platform.version = packageJson.version;
|
|
1373
|
-
plugin.name = packageJson.name;
|
|
1374
|
-
plugin.description = packageJson.description ?? 'No description';
|
|
1375
|
-
plugin.version = packageJson.version;
|
|
1376
|
-
plugin.author = packageJson.author ?? 'Unknown';
|
|
1377
|
-
plugin.type = platform.type;
|
|
1378
|
-
plugin.platform = platform;
|
|
1379
|
-
plugin.loaded = true;
|
|
1380
|
-
plugin.registeredDevices = 0;
|
|
1381
|
-
plugin.addedDevices = 0;
|
|
1382
|
-
plugin.configJson = config;
|
|
1383
|
-
plugin.schemaJson = await this.loadPluginSchema(plugin);
|
|
1384
|
-
this.log.info(`Loaded plugin ${plg}${plugin.name}${nf} type ${typ}${platform.type} ${db}(entrypoint ${UNDERLINE}${pluginEntry}${UNDERLINEOFF})`);
|
|
1385
|
-
if (start)
|
|
1386
|
-
this.startPlugin(plugin, message); // No await do it asyncronously
|
|
1387
|
-
return Promise.resolve(platform);
|
|
1388
|
-
}
|
|
1389
|
-
else {
|
|
1390
|
-
this.log.error(`Plugin ${plg}${plugin.name}${er} does not provide a default export`);
|
|
1391
|
-
plugin.error = true;
|
|
1392
|
-
return Promise.resolve(undefined);
|
|
1393
|
-
}
|
|
1394
|
-
}
|
|
1395
|
-
catch (err) {
|
|
1396
|
-
this.log.error(`Failed to load plugin ${plg}${plugin.name}${er}: ${err}`);
|
|
1397
|
-
plugin.error = true;
|
|
1398
|
-
return Promise.resolve(undefined);
|
|
1399
|
-
}
|
|
1400
|
-
}
|
|
1401
|
-
/**
|
|
1402
|
-
* Starts a plugin.
|
|
1403
|
-
*
|
|
1404
|
-
* @param {RegisteredPlugin} plugin - The plugin to start.
|
|
1405
|
-
* @param {string} [message] - Optional message to pass to the plugin's onStart method.
|
|
1406
|
-
* @param {boolean} [configure] - Indicates whether to configure the plugin after starting (default false).
|
|
1407
|
-
* @returns {Promise<void>} A promise that resolves when the plugin is started successfully, or rejects with an error if starting the plugin fails.
|
|
1408
|
-
*/
|
|
1409
|
-
async startPlugin(plugin, message, configure = false) {
|
|
1410
|
-
if (!plugin.loaded || !plugin.platform) {
|
|
1411
|
-
this.log.error(`Plugin ${plg}${plugin.name}${er} not loaded or no platform`);
|
|
1412
|
-
return Promise.resolve();
|
|
1413
|
-
}
|
|
1414
|
-
if (plugin.started) {
|
|
1415
|
-
this.log.debug(`Plugin ${plg}${plugin.name}${db} already started`);
|
|
1416
|
-
return Promise.resolve();
|
|
1417
|
-
}
|
|
1418
|
-
this.log.info(`Starting plugin ${plg}${plugin.name}${db} type ${typ}${plugin.type}${db}`);
|
|
1419
|
-
try {
|
|
1420
|
-
plugin.platform
|
|
1421
|
-
.onStart(message)
|
|
1422
|
-
.then(() => {
|
|
1423
|
-
plugin.started = true;
|
|
1424
|
-
this.log.info(`Started plugin ${plg}${plugin.name}${db} type ${typ}${plugin.type}${db}`);
|
|
1425
|
-
if (configure)
|
|
1426
|
-
this.configurePlugin(plugin); // No await do it asyncronously
|
|
1427
|
-
return Promise.resolve();
|
|
1428
|
-
})
|
|
1429
|
-
.catch((err) => {
|
|
1430
|
-
plugin.error = true;
|
|
1431
|
-
this.log.error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`);
|
|
1432
|
-
return Promise.resolve();
|
|
1433
|
-
});
|
|
1434
|
-
}
|
|
1435
|
-
catch (err) {
|
|
1436
|
-
plugin.error = true;
|
|
1437
|
-
this.log.error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`);
|
|
1438
|
-
return Promise.resolve();
|
|
1439
|
-
}
|
|
1440
|
-
}
|
|
1441
|
-
/**
|
|
1442
|
-
* Configures a plugin.
|
|
1443
|
-
*
|
|
1444
|
-
* @param {RegisteredPlugin} plugin - The plugin to configure.
|
|
1445
|
-
* @returns {Promise<void>} A promise that resolves when the plugin is configured successfully, or rejects with an error if configuration fails.
|
|
1446
|
-
*/
|
|
1447
|
-
async configurePlugin(plugin) {
|
|
1448
|
-
if (!plugin.loaded || !plugin.started || !plugin.platform) {
|
|
1449
|
-
this.log.error(`Plugin ${plg}${plugin.name}${er} not loaded (${plugin.loaded}) or not started (${plugin.started}) or not platform (${plugin.platform?.name})`);
|
|
1450
|
-
return Promise.resolve();
|
|
1451
|
-
}
|
|
1452
|
-
if (plugin.configured) {
|
|
1453
|
-
this.log.info(`Plugin ${plg}${plugin.name}${nf} already configured`);
|
|
1454
|
-
return Promise.resolve();
|
|
1455
|
-
}
|
|
1456
|
-
this.log.info(`Configuring plugin ${plg}${plugin.name}${nf} type ${typ}${plugin.type}${nf}`);
|
|
1457
|
-
try {
|
|
1458
|
-
plugin.platform
|
|
1459
|
-
.onConfigure()
|
|
1460
|
-
.then(() => {
|
|
1461
|
-
plugin.configured = true;
|
|
1462
|
-
this.log.info(`Configured plugin ${plg}${plugin.name}${nf} type ${typ}${plugin.type}${nf}`);
|
|
1463
|
-
this.savePluginConfig(plugin);
|
|
1464
|
-
return Promise.resolve();
|
|
1465
|
-
})
|
|
1466
|
-
.catch((err) => {
|
|
1467
|
-
plugin.error = true;
|
|
1468
|
-
this.log.error(`Failed to configure plugin ${plg}${plugin.name}${er}: ${err}`);
|
|
1469
|
-
return Promise.resolve();
|
|
1470
|
-
});
|
|
1471
|
-
}
|
|
1472
|
-
catch (err) {
|
|
1473
|
-
plugin.error = true;
|
|
1474
|
-
this.log.error(`Failed to configure plugin ${plg}${plugin.name}${er}: ${err}`);
|
|
1475
|
-
return Promise.resolve();
|
|
1476
|
-
}
|
|
1477
|
-
}
|
|
1478
1220
|
async startTest() {
|
|
1479
1221
|
// Start the Matterbridge
|
|
1480
1222
|
}
|
|
@@ -1514,14 +1256,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
1514
1256
|
clearInterval(startMatterInterval);
|
|
1515
1257
|
this.log.debug('***Cleared startMatterInterval interval for Matterbridge');
|
|
1516
1258
|
await this.startMatterServer();
|
|
1517
|
-
this.log.
|
|
1259
|
+
this.log.notice('Matter server started');
|
|
1518
1260
|
// Configure the plugins
|
|
1519
1261
|
this.configureTimeout = setTimeout(async () => {
|
|
1520
1262
|
for (const plugin of this.plugins) {
|
|
1521
1263
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
1522
1264
|
continue;
|
|
1523
1265
|
try {
|
|
1524
|
-
await this.plugins.configure(plugin); // No await do it
|
|
1266
|
+
await this.plugins.configure(plugin); // TODO No await do it in parallel
|
|
1525
1267
|
}
|
|
1526
1268
|
catch (error) {
|
|
1527
1269
|
plugin.error = true;
|
|
@@ -1582,14 +1324,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
1582
1324
|
clearInterval(startMatterInterval);
|
|
1583
1325
|
this.log.debug('***Cleared startMatterInterval interval in childbridge mode');
|
|
1584
1326
|
await this.startMatterServer();
|
|
1585
|
-
this.log.
|
|
1327
|
+
this.log.notice('Matter server started');
|
|
1586
1328
|
// Configure the plugins
|
|
1587
1329
|
this.configureTimeout = setTimeout(async () => {
|
|
1588
1330
|
for (const plugin of this.plugins) {
|
|
1589
1331
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
1590
1332
|
continue;
|
|
1591
1333
|
try {
|
|
1592
|
-
await this.plugins.configure(plugin); // No await do it
|
|
1334
|
+
await this.plugins.configure(plugin); // TODO No await do it in parallel
|
|
1593
1335
|
}
|
|
1594
1336
|
catch (error) {
|
|
1595
1337
|
plugin.error = true;
|
|
@@ -1815,7 +1557,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1815
1557
|
* @param {string} storageName - The name of the storage file.
|
|
1816
1558
|
* @returns {Promise<void>} - A promise that resolves when the storage process is started.
|
|
1817
1559
|
*/
|
|
1818
|
-
async
|
|
1560
|
+
async startMatterStorage(storageType, storageName) {
|
|
1819
1561
|
this.log.debug(`Starting ${storageType} storage ${CYAN}${storageName}${db}`);
|
|
1820
1562
|
if (storageType === 'disk') {
|
|
1821
1563
|
const storageDisk = new StorageBackendDisk(storageName);
|
|
@@ -1836,7 +1578,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1836
1578
|
await this.storageManager.initialize();
|
|
1837
1579
|
this.log.debug('Storage initialized');
|
|
1838
1580
|
if (storageType === 'json') {
|
|
1839
|
-
await this.
|
|
1581
|
+
await this.backupJsonMatterStorage(storageName, storageName.replace('.json', '') + '.backup.json');
|
|
1840
1582
|
}
|
|
1841
1583
|
}
|
|
1842
1584
|
catch (error) {
|
|
@@ -1851,7 +1593,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1851
1593
|
* @param storageName - The name of the JSON storage file to be backed up.
|
|
1852
1594
|
* @param backupName - The name of the backup file to be created.
|
|
1853
1595
|
*/
|
|
1854
|
-
async
|
|
1596
|
+
async backupJsonMatterStorage(storageName, backupName) {
|
|
1855
1597
|
try {
|
|
1856
1598
|
this.log.debug(`Making backup copy of ${storageName}`);
|
|
1857
1599
|
await fs.copyFile(storageName, backupName);
|
|
@@ -1875,7 +1617,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1875
1617
|
* Stops the matter storage.
|
|
1876
1618
|
* @returns {Promise<void>} A promise that resolves when the storage is stopped.
|
|
1877
1619
|
*/
|
|
1878
|
-
async
|
|
1620
|
+
async stopMatterStorage() {
|
|
1879
1621
|
this.log.debug('Stopping storage');
|
|
1880
1622
|
await this.storageManager?.close();
|
|
1881
1623
|
this.log.debug('Storage closed');
|
|
@@ -2028,9 +1770,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
2028
1770
|
const sessionInformations = commissioningServer.getActiveSessionInformation(fabricIndex);
|
|
2029
1771
|
let connected = false;
|
|
2030
1772
|
sessionInformations.forEach((session) => {
|
|
2031
|
-
this.log.info(
|
|
1773
|
+
this.log.info(`Active session changed on fabric ${zb}${fabricIndex}${nf} id ${zb}${session.fabric?.fabricId}${nf} vendor ${zb}${session.fabric?.rootVendorId}${nf} ${this.getVendorIdName(session.fabric?.rootVendorId)} ${session.fabric?.label} for ${plg}${pluginName}${nf}`, debugStringify(session));
|
|
2032
1774
|
if (session.isPeerActive === true && session.secure === true && session.numberOfActiveSubscriptions >= 1) {
|
|
2033
|
-
this.log.
|
|
1775
|
+
this.log.notice(`Controller ${zb}${session.fabric?.rootVendorId}${nt} ${this.getVendorIdName(session.fabric?.rootVendorId)} ${session.fabric?.label} connected to ${plg}${pluginName}${nt} on session ${session.name}`);
|
|
2034
1776
|
connected = true;
|
|
2035
1777
|
}
|
|
2036
1778
|
});
|
|
@@ -2081,9 +1823,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
2081
1823
|
},
|
|
2082
1824
|
commissioningChangedCallback: async (fabricIndex) => {
|
|
2083
1825
|
const fabricInfo = commissioningServer.getCommissionedFabricInformation(fabricIndex);
|
|
2084
|
-
this.log.debug(
|
|
1826
|
+
this.log.debug(`Commissioning changed on fabric ${zb}${fabricIndex}${db} for ${plg}${pluginName}${db}`, debugStringify(fabricInfo));
|
|
2085
1827
|
if (commissioningServer.getCommissionedFabricInformation().length === 0) {
|
|
2086
|
-
this.log.warn(
|
|
1828
|
+
this.log.warn(`Commissioning removed from fabric ${zb}${fabricIndex}${wr} for ${plg}${pluginName}${wr}. Resetting the commissioning server ...`);
|
|
2087
1829
|
await commissioningServer.factoryReset();
|
|
2088
1830
|
if (pluginName === 'Matterbridge') {
|
|
2089
1831
|
await this.matterbridgeContext?.clearAll();
|
|
@@ -2104,7 +1846,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2104
1846
|
}
|
|
2105
1847
|
}
|
|
2106
1848
|
}
|
|
2107
|
-
this.log.warn(
|
|
1849
|
+
this.log.warn(`Restart to activate the pairing for ${plg}${pluginName}${wr}.`);
|
|
2108
1850
|
}
|
|
2109
1851
|
else {
|
|
2110
1852
|
const fabricInfo = commissioningServer.getCommissionedFabricInformation();
|
|
@@ -2282,7 +2024,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2282
2024
|
}
|
|
2283
2025
|
}
|
|
2284
2026
|
/**
|
|
2285
|
-
* Sanitizes the fabric information by converting bigint properties to string cause res
|
|
2027
|
+
* Sanitizes the fabric information by converting bigint properties to string cause res.json doesn't know bigint.
|
|
2286
2028
|
*
|
|
2287
2029
|
* @param fabricInfo - The array of exposed fabric information objects.
|
|
2288
2030
|
* @returns An array of sanitized exposed fabric information objects.
|
|
@@ -2414,10 +2156,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
2414
2156
|
};
|
|
2415
2157
|
/**
|
|
2416
2158
|
* Retrieves the base registered plugins sanitized for res.json().
|
|
2417
|
-
* @param {boolean} includeAll - Whether to include all information for each plugin.
|
|
2418
2159
|
* @returns {BaseRegisteredPlugin[]} A promise that resolves to an array of BaseRegisteredPlugin objects.
|
|
2419
2160
|
*/
|
|
2420
|
-
async getBaseRegisteredPlugins(
|
|
2161
|
+
async getBaseRegisteredPlugins() {
|
|
2421
2162
|
const baseRegisteredPlugins = [];
|
|
2422
2163
|
for (const plugin of this.plugins) {
|
|
2423
2164
|
baseRegisteredPlugins.push({
|
|
@@ -2436,23 +2177,23 @@ export class Matterbridge extends EventEmitter {
|
|
|
2436
2177
|
configured: plugin.configured,
|
|
2437
2178
|
paired: plugin.paired,
|
|
2438
2179
|
connected: plugin.connected,
|
|
2439
|
-
fabricInformations:
|
|
2440
|
-
sessionInformations:
|
|
2180
|
+
fabricInformations: plugin.fabricInformations,
|
|
2181
|
+
sessionInformations: plugin.sessionInformations,
|
|
2441
2182
|
registeredDevices: plugin.registeredDevices,
|
|
2442
2183
|
addedDevices: plugin.addedDevices,
|
|
2443
2184
|
qrPairingCode: plugin.qrPairingCode,
|
|
2444
2185
|
manualPairingCode: plugin.manualPairingCode,
|
|
2445
|
-
configJson:
|
|
2446
|
-
schemaJson:
|
|
2186
|
+
configJson: plugin.configJson,
|
|
2187
|
+
schemaJson: plugin.schemaJson,
|
|
2447
2188
|
});
|
|
2448
2189
|
}
|
|
2449
2190
|
return baseRegisteredPlugins;
|
|
2450
2191
|
}
|
|
2451
2192
|
/**
|
|
2452
2193
|
* Spawns a child process with the given command and arguments.
|
|
2453
|
-
* @param command - The command to execute.
|
|
2454
|
-
* @param args - The arguments to pass to the command (default: []).
|
|
2455
|
-
* @returns A promise that resolves when the child process exits successfully, or rejects if there is an error.
|
|
2194
|
+
* @param {string} command - The command to execute.
|
|
2195
|
+
* @param {string[]} args - The arguments to pass to the command (default: []).
|
|
2196
|
+
* @returns {Promise<void>} A promise that resolves when the child process exits successfully, or rejects if there is an error.
|
|
2456
2197
|
*/
|
|
2457
2198
|
async spawnCommand(command, args = []) {
|
|
2458
2199
|
/*
|
|
@@ -2510,14 +2251,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
2510
2251
|
childProcess.stdout.on('data', (data) => {
|
|
2511
2252
|
const message = data.toString().trim();
|
|
2512
2253
|
// this.log.info('\n' + message);
|
|
2513
|
-
this.wssSendMessage('
|
|
2254
|
+
this.wssSendMessage('spawn', this.log.now(), 'Matterbridge:spawn', message);
|
|
2514
2255
|
});
|
|
2515
2256
|
}
|
|
2516
2257
|
if (childProcess.stderr) {
|
|
2517
2258
|
childProcess.stderr.on('data', (data) => {
|
|
2518
2259
|
const message = data.toString().trim();
|
|
2519
2260
|
// this.log.debug('\n' + message);
|
|
2520
|
-
this.wssSendMessage('
|
|
2261
|
+
this.wssSendMessage('spawn', this.log.now(), 'Matterbridge:spawn', message);
|
|
2521
2262
|
});
|
|
2522
2263
|
}
|
|
2523
2264
|
});
|
|
@@ -2525,20 +2266,35 @@ export class Matterbridge extends EventEmitter {
|
|
|
2525
2266
|
/**
|
|
2526
2267
|
* Sends a WebSocket message to all connected clients.
|
|
2527
2268
|
*
|
|
2528
|
-
* @param {string}
|
|
2529
|
-
* @param {string}
|
|
2269
|
+
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
2270
|
+
* @param {string} time - The time string of the message
|
|
2271
|
+
* @param {string} name - The logger name of the message
|
|
2530
2272
|
* @param {string} message - The content of the message.
|
|
2531
2273
|
*/
|
|
2532
|
-
wssSendMessage(
|
|
2274
|
+
wssSendMessage(level, time, name, message) {
|
|
2275
|
+
if (!level || !time || !name || !message)
|
|
2276
|
+
return;
|
|
2533
2277
|
// Remove ANSI escape codes from the message
|
|
2534
2278
|
// eslint-disable-next-line no-control-regex
|
|
2535
|
-
|
|
2279
|
+
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
2536
2280
|
// Remove leading asterisks from the message
|
|
2537
|
-
|
|
2281
|
+
message = message.replace(/^\*+/, '');
|
|
2282
|
+
// Replace all occurrences of \t and \n
|
|
2283
|
+
message = message.replace(/[\t\n]/g, '');
|
|
2284
|
+
// Remove non-printable characters
|
|
2285
|
+
// eslint-disable-next-line no-control-regex
|
|
2286
|
+
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
2287
|
+
// Replace all occurrences of \" with "
|
|
2288
|
+
message = message.replace(/\\"/g, '"');
|
|
2289
|
+
/*
|
|
2290
|
+
if (message.length > 500) {
|
|
2291
|
+
console.error(`${er}Message long: ${level} ${time} ${name} ${message} `);
|
|
2292
|
+
}
|
|
2293
|
+
*/
|
|
2538
2294
|
// Send the message to all connected clients
|
|
2539
2295
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2540
2296
|
if (client.readyState === WebSocket.OPEN) {
|
|
2541
|
-
client.send(JSON.stringify({
|
|
2297
|
+
client.send(JSON.stringify({ level, time, name, message }));
|
|
2542
2298
|
}
|
|
2543
2299
|
});
|
|
2544
2300
|
}
|
|
@@ -2639,10 +2395,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
2639
2395
|
this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
|
|
2640
2396
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
2641
2397
|
const clientIp = request.socket.remoteAddress;
|
|
2642
|
-
this.log.info(`WebSocketServer client ${clientIp} connected`);
|
|
2643
2398
|
this.log.setGlobalCallback(this.wssSendMessage.bind(this));
|
|
2644
2399
|
this.log.debug('WebSocketServer logger global callback added');
|
|
2645
|
-
this.
|
|
2400
|
+
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
2401
|
+
// this.wssSendMessage('info', this.log.now(), 'Matterbridge', `WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
2646
2402
|
ws.on('message', (message) => {
|
|
2647
2403
|
this.log.debug(`WebSocket client message: ${message}`);
|
|
2648
2404
|
});
|
|
@@ -2711,13 +2467,15 @@ export class Matterbridge extends EventEmitter {
|
|
|
2711
2467
|
}
|
|
2712
2468
|
this.matterbridgeInformation.bridgeMode = this.bridgeMode;
|
|
2713
2469
|
this.matterbridgeInformation.restartMode = this.restartMode;
|
|
2714
|
-
this.matterbridgeInformation.
|
|
2470
|
+
this.matterbridgeInformation.loggerLevel = this.log.logLevel;
|
|
2715
2471
|
this.matterbridgeInformation.matterLoggerLevel = Logger.defaultLogLevel;
|
|
2716
2472
|
this.matterbridgeInformation.matterbridgePaired = this.matterbridgePaired;
|
|
2717
2473
|
this.matterbridgeInformation.matterbridgeConnected = this.matterbridgeConnected;
|
|
2718
2474
|
this.matterbridgeInformation.matterbridgeFabricInformations = this.matterbridgeFabricInformations;
|
|
2719
2475
|
this.matterbridgeInformation.matterbridgeSessionInformations = this.matterbridgeSessionInformations;
|
|
2720
|
-
|
|
2476
|
+
if (this.profile)
|
|
2477
|
+
this.matterbridgeInformation.profile = this.profile;
|
|
2478
|
+
const response = { wssHost, ssl: hasParameter('ssl'), qrPairingCode, manualPairingCode, systemInformation: this.systemInformation, matterbridgeInformation: this.matterbridgeInformation };
|
|
2721
2479
|
// this.log.debug('Response:', debugStringify(response));
|
|
2722
2480
|
this.log.debug(`WebSocketServer logger local callback: ${this.log.getCallback() ? 'active' : 'inactive'}`);
|
|
2723
2481
|
this.log.debug(`WebSocketServer logger global callback: ${this.log.getGlobalCallback() ? 'active' : 'inactive'}`);
|
|
@@ -2730,7 +2488,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2730
2488
|
// Endpoint to provide plugins
|
|
2731
2489
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
2732
2490
|
this.log.debug('The frontend sent /api/plugins');
|
|
2733
|
-
const response = await this.getBaseRegisteredPlugins(
|
|
2491
|
+
const response = await this.getBaseRegisteredPlugins();
|
|
2734
2492
|
// this.log.debug('Response:', debugStringify(response));
|
|
2735
2493
|
res.json(response);
|
|
2736
2494
|
});
|
|
@@ -2859,12 +2617,31 @@ export class Matterbridge extends EventEmitter {
|
|
|
2859
2617
|
if (command === 'setmbloglevel') {
|
|
2860
2618
|
this.log.debug('Matterbridge log level:', param);
|
|
2861
2619
|
if (param === 'Debug') {
|
|
2862
|
-
this.log.
|
|
2863
|
-
this.debugEnabled = true;
|
|
2620
|
+
this.log.logLevel = "debug" /* LogLevel.DEBUG */;
|
|
2864
2621
|
}
|
|
2865
2622
|
else if (param === 'Info') {
|
|
2866
|
-
this.log.
|
|
2867
|
-
|
|
2623
|
+
this.log.logLevel = "info" /* LogLevel.INFO */;
|
|
2624
|
+
}
|
|
2625
|
+
else if (param === 'Notice') {
|
|
2626
|
+
this.log.logLevel = "notice" /* LogLevel.NOTICE */;
|
|
2627
|
+
}
|
|
2628
|
+
else if (param === 'Warn') {
|
|
2629
|
+
this.log.logLevel = "warn" /* LogLevel.WARN */;
|
|
2630
|
+
}
|
|
2631
|
+
else if (param === 'Error') {
|
|
2632
|
+
this.log.logLevel = "error" /* LogLevel.ERROR */;
|
|
2633
|
+
}
|
|
2634
|
+
else if (param === 'Fatal') {
|
|
2635
|
+
this.log.logLevel = "fatal" /* LogLevel.FATAL */;
|
|
2636
|
+
}
|
|
2637
|
+
await this.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
2638
|
+
MatterbridgeDevice.logLevel = this.log.logLevel;
|
|
2639
|
+
this.plugins.logLevel = this.log.logLevel;
|
|
2640
|
+
for (const plugin of this.plugins) {
|
|
2641
|
+
if (!plugin.platform || !plugin.platform.config)
|
|
2642
|
+
continue;
|
|
2643
|
+
plugin.platform.log.logLevel = plugin.platform.config.debug ? "debug" /* LogLevel.DEBUG */ : this.log.logLevel;
|
|
2644
|
+
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug ? "debug" /* LogLevel.DEBUG */ : this.log.logLevel);
|
|
2868
2645
|
}
|
|
2869
2646
|
res.json({ message: 'Command received' });
|
|
2870
2647
|
return;
|
|
@@ -2890,6 +2667,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2890
2667
|
else if (param === 'Fatal') {
|
|
2891
2668
|
Logger.defaultLogLevel = Level.FATAL;
|
|
2892
2669
|
}
|
|
2670
|
+
await this.nodeContext?.set('matterLogLevel', Logger.defaultLogLevel);
|
|
2893
2671
|
res.json({ message: 'Command received' });
|
|
2894
2672
|
return;
|
|
2895
2673
|
}
|
|
@@ -2975,12 +2753,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2975
2753
|
const plugin = await this.plugins.add(param);
|
|
2976
2754
|
if (plugin) {
|
|
2977
2755
|
this.plugins.load(plugin, true, 'The plugin has been added', true); // No await do it in the background
|
|
2978
|
-
/*
|
|
2979
|
-
plugin.platform = await this.plugins.load(plugin, false, 'The plugin has been added');
|
|
2980
|
-
if (plugin.platform) {
|
|
2981
|
-
await this.plugins.start(plugin, 'The plugin has been added', true);
|
|
2982
|
-
}
|
|
2983
|
-
*/
|
|
2984
2756
|
}
|
|
2985
2757
|
res.json({ message: 'Command received' });
|
|
2986
2758
|
return;
|
|
@@ -3017,12 +2789,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3017
2789
|
plugin.addedDevices = undefined;
|
|
3018
2790
|
await this.plugins.enable(param);
|
|
3019
2791
|
this.plugins.load(plugin, true, 'The plugin has been enabled', true); // No await do it in the background
|
|
3020
|
-
/*
|
|
3021
|
-
plugin.platform = await this.plugins.load(plugin, false, 'The plugin has been enabled');
|
|
3022
|
-
if (plugin.platform) {
|
|
3023
|
-
await this.plugins.start(plugin, 'The plugin has been enabled', true);
|
|
3024
|
-
}
|
|
3025
|
-
*/
|
|
3026
2792
|
}
|
|
3027
2793
|
}
|
|
3028
2794
|
res.json({ message: 'Command received' });
|
|
@@ -3053,9 +2819,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
3053
2819
|
this.log.debug(`Frontend initialized on port ${YELLOW}${port}${db} static ${UNDERLINE}${path.join(this.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
|
|
3054
2820
|
}
|
|
3055
2821
|
/**
|
|
3056
|
-
* Retrieves the cluster text from a given device.
|
|
3057
|
-
* @param device - The MatterbridgeDevice object.
|
|
3058
|
-
* @returns The attributes of the cluster servers in the device.
|
|
2822
|
+
* Retrieves the cluster text description from a given device.
|
|
2823
|
+
* @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
|
|
2824
|
+
* @returns {string} The attributes description of the cluster servers in the device.
|
|
3059
2825
|
*/
|
|
3060
2826
|
getClusterTextFromDevice(device) {
|
|
3061
2827
|
const stringifyFixedLabel = (endpoint) => {
|
|
@@ -3072,41 +2838,54 @@ export class Matterbridge extends EventEmitter {
|
|
|
3072
2838
|
// this.log.debug(`getClusterTextFromDevice: ${device.name}`);
|
|
3073
2839
|
const clusterServers = device.getAllClusterServers();
|
|
3074
2840
|
clusterServers.forEach((clusterServer) => {
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
2841
|
+
try {
|
|
2842
|
+
// this.log.debug(`***--clusterServer: ${clusterServer.id} (${clusterServer.name})`);
|
|
2843
|
+
if (clusterServer.name === 'OnOff')
|
|
2844
|
+
attributes += `OnOff: ${clusterServer.getOnOffAttribute()} `;
|
|
2845
|
+
if (clusterServer.name === 'Switch')
|
|
2846
|
+
attributes += `Position: ${clusterServer.getCurrentPositionAttribute()} `;
|
|
2847
|
+
if (clusterServer.name === 'WindowCovering')
|
|
2848
|
+
attributes += `Cover position: ${clusterServer.attributes.currentPositionLiftPercent100ths.getLocal() / 100}% `;
|
|
2849
|
+
if (clusterServer.name === 'DoorLock')
|
|
2850
|
+
attributes += `State: ${clusterServer.attributes.lockState.getLocal() === 1 ? 'Locked' : 'Not locked'} `;
|
|
2851
|
+
if (clusterServer.name === 'Thermostat')
|
|
2852
|
+
attributes += `Temperature: ${clusterServer.attributes.localTemperature.getLocal() / 100}°C `;
|
|
2853
|
+
if (clusterServer.name === 'LevelControl')
|
|
2854
|
+
attributes += `Level: ${clusterServer.getCurrentLevelAttribute()}% `;
|
|
2855
|
+
if (clusterServer.name === 'ColorControl' && clusterServer.isAttributeSupportedByName('currentHue'))
|
|
2856
|
+
attributes += `Hue: ${Math.round(clusterServer.getCurrentHueAttribute())} Saturation: ${Math.round(clusterServer.getCurrentSaturationAttribute())}% `;
|
|
2857
|
+
if (clusterServer.name === 'ColorControl' && clusterServer.isAttributeSupportedByName('colorTemperatureMireds'))
|
|
2858
|
+
attributes += `ColorTemp: ${Math.round(clusterServer.getColorTemperatureMiredsAttribute())} `;
|
|
2859
|
+
if (clusterServer.name === 'BooleanState')
|
|
2860
|
+
attributes += `Contact: ${clusterServer.getStateValueAttribute()} `;
|
|
2861
|
+
if (clusterServer.name === 'BooleanStateConfiguration' && clusterServer.isAttributeSupportedByName('alarmsActive'))
|
|
2862
|
+
attributes += `Active alarms: ${stringify(clusterServer.getAlarmsActiveAttribute())} `;
|
|
2863
|
+
if (clusterServer.name === 'FanControl')
|
|
2864
|
+
attributes += `Mode: ${clusterServer.getFanModeAttribute()} Speed: ${clusterServer.getPercentCurrentAttribute()} `;
|
|
2865
|
+
if (clusterServer.name === 'FanControl' && clusterServer.isAttributeSupportedByName('speedCurrent'))
|
|
2866
|
+
attributes += `MultiSpeed: ${clusterServer.getSpeedCurrentAttribute()} `;
|
|
2867
|
+
if (clusterServer.name === 'OccupancySensing')
|
|
2868
|
+
attributes += `Occupancy: ${clusterServer.getOccupancyAttribute().occupied} `;
|
|
2869
|
+
if (clusterServer.name === 'IlluminanceMeasurement')
|
|
2870
|
+
attributes += `Illuminance: ${clusterServer.getMeasuredValueAttribute()} `;
|
|
2871
|
+
if (clusterServer.name === 'AirQuality')
|
|
2872
|
+
attributes += `Air quality: ${clusterServer.getAirQualityAttribute()} `;
|
|
2873
|
+
if (clusterServer.name === 'TvocMeasurement')
|
|
2874
|
+
attributes += `Voc: ${clusterServer.getMeasuredValueAttribute()} `;
|
|
2875
|
+
if (clusterServer.name === 'TemperatureMeasurement')
|
|
2876
|
+
attributes += `Temperature: ${clusterServer.getMeasuredValueAttribute() / 100}°C `;
|
|
2877
|
+
if (clusterServer.name === 'RelativeHumidityMeasurement')
|
|
2878
|
+
attributes += `Humidity: ${clusterServer.getMeasuredValueAttribute() / 100}% `;
|
|
2879
|
+
if (clusterServer.name === 'PressureMeasurement')
|
|
2880
|
+
attributes += `Pressure: ${clusterServer.getMeasuredValueAttribute()} `;
|
|
2881
|
+
if (clusterServer.name === 'FlowMeasurement')
|
|
2882
|
+
attributes += `Flow: ${clusterServer.getMeasuredValueAttribute()} `;
|
|
2883
|
+
if (clusterServer.name === 'FixedLabel')
|
|
2884
|
+
attributes += `${stringifyFixedLabel(device)} `;
|
|
2885
|
+
}
|
|
2886
|
+
catch (error) {
|
|
2887
|
+
this.log.error(`getClusterTextFromDevice with ${clusterServer.name} error: ${error}`);
|
|
2888
|
+
}
|
|
3110
2889
|
});
|
|
3111
2890
|
return attributes;
|
|
3112
2891
|
}
|
|
@@ -3116,14 +2895,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
3116
2895
|
*
|
|
3117
2896
|
* @returns A Promise that resolves when the initialization is complete.
|
|
3118
2897
|
*/
|
|
3119
|
-
async startExtension(dataPath,
|
|
2898
|
+
async startExtension(dataPath, extensionVersion, port = 5540) {
|
|
3120
2899
|
// Set the bridge mode
|
|
3121
2900
|
this.bridgeMode = 'bridge';
|
|
3122
2901
|
// Set the first port to use
|
|
3123
2902
|
this.port = port;
|
|
3124
2903
|
// Set Matterbridge logger
|
|
3125
|
-
this.
|
|
3126
|
-
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logDebug: this.debugEnabled });
|
|
2904
|
+
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: "info" /* LogLevel.INFO */ });
|
|
3127
2905
|
this.log.debug('Matterbridge extension is starting...');
|
|
3128
2906
|
// Initialize NodeStorage
|
|
3129
2907
|
this.matterbridgeDirectory = dataPath;
|
|
@@ -3148,10 +2926,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
3148
2926
|
await this.logNodeAndSystemInfo();
|
|
3149
2927
|
this.matterbridgeDirectory = dataPath;
|
|
3150
2928
|
// Set matter.js logger level and format
|
|
3151
|
-
Logger.defaultLogLevel =
|
|
2929
|
+
Logger.defaultLogLevel = Level.INFO;
|
|
3152
2930
|
Logger.format = Format.ANSI;
|
|
3153
2931
|
// Start the storage and create matterbridgeContext
|
|
3154
|
-
await this.
|
|
2932
|
+
await this.startMatterStorage('json', path.join(this.matterbridgeDirectory, this.matterStorageName));
|
|
3155
2933
|
if (!this.storageManager)
|
|
3156
2934
|
return false;
|
|
3157
2935
|
this.matterbridgeContext = await this.createCommissioningServerContext('Matterbridge', 'Matterbridge zigbee2MQTT', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'zigbee2MQTT Matter extension');
|
|
@@ -3195,7 +2973,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
3195
2973
|
// Clearing the session manager
|
|
3196
2974
|
// this.matterbridgeContext?.createContext('SessionManager').clear();
|
|
3197
2975
|
// Closing storage
|
|
3198
|
-
await this.
|
|
2976
|
+
await this.stopMatterStorage();
|
|
3199
2977
|
this.log.info('Matter server stopped');
|
|
3200
2978
|
}
|
|
3201
2979
|
/**
|