matterbridge 1.5.9 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -3
- package/README-DEV.md +10 -6
- package/README-PODMAN.md +2 -0
- package/README-SERVICE.md +44 -6
- package/README.md +53 -7
- package/dist/cluster/export.js +1 -1
- package/dist/cluster/export.js.map +1 -1
- package/dist/defaultConfigSchema.d.ts.map +1 -1
- package/dist/defaultConfigSchema.js +9 -1
- package/dist/defaultConfigSchema.js.map +1 -1
- package/dist/matterbridge.d.ts +72 -48
- package/dist/matterbridge.d.ts.map +1 -1
- package/dist/matterbridge.js +174 -173
- package/dist/matterbridge.js.map +1 -1
- package/dist/matterbridgeDevice.d.ts +140 -1042
- package/dist/matterbridgeDevice.d.ts.map +1 -1
- package/dist/matterbridgeDevice.js +57 -363
- package/dist/matterbridgeDevice.js.map +1 -1
- package/dist/matterbridgeEdge.d.ts +90 -0
- package/dist/matterbridgeEdge.d.ts.map +1 -0
- package/dist/matterbridgeEdge.js +555 -0
- package/dist/matterbridgeEdge.js.map +1 -0
- package/dist/matterbridgeEndpoint.d.ts +5195 -0
- package/dist/matterbridgeEndpoint.d.ts.map +1 -0
- package/dist/matterbridgeEndpoint.js +2196 -0
- package/dist/matterbridgeEndpoint.js.map +1 -0
- package/dist/matterbridgeTypes.d.ts +6 -5
- package/dist/matterbridgeTypes.d.ts.map +1 -1
- package/dist/matterbridgeTypes.js +1 -1
- package/dist/matterbridgeTypes.js.map +1 -1
- package/dist/matterbridgeWebsocket.d.ts +49 -0
- package/dist/matterbridgeWebsocket.d.ts.map +1 -0
- package/dist/matterbridgeWebsocket.js +145 -0
- package/dist/matterbridgeWebsocket.js.map +1 -0
- package/dist/pluginManager.d.ts +134 -1
- package/dist/pluginManager.d.ts.map +1 -1
- package/dist/pluginManager.js +167 -14
- package/dist/pluginManager.js.map +1 -1
- package/frontend/build/asset-manifest.json +3 -3
- package/frontend/build/index.html +1 -1
- package/frontend/build/static/js/{main.96d6324b.js → main.045d08f7.js} +3 -3
- package/frontend/build/static/js/main.045d08f7.js.map +1 -0
- package/npm-shrinkwrap.json +838 -6222
- package/package.json +3 -78
- package/CODEOWNERS +0 -1
- package/dist/history/export.d.ts +0 -2
- package/dist/history/export.d.ts.map +0 -1
- package/dist/history/export.js +0 -2
- package/dist/history/export.js.map +0 -1
- package/frontend/build/static/js/main.96d6324b.js.map +0 -1
- /package/frontend/build/static/js/{main.96d6324b.js.LICENSE.txt → main.045d08f7.js.LICENSE.txt} +0 -0
package/dist/matterbridge.js
CHANGED
|
@@ -50,6 +50,7 @@ import { StorageBackendDisk, StorageBackendJsonFile, StorageManager } from '@pro
|
|
|
50
50
|
import { getParameter, getIntParameter, hasParameter } from '@project-chip/matter-node.js/util';
|
|
51
51
|
import { CryptoNode } from '@project-chip/matter-node.js/crypto';
|
|
52
52
|
import { Specification } from '@project-chip/matter-node.js/model';
|
|
53
|
+
import { WS_ID_LOG, WS_ID_REFRESH_NEEDED, WS_ID_RESTART_NEEDED, wsMessageHandler } from './matterbridgeWebsocket.js';
|
|
53
54
|
// Default colors
|
|
54
55
|
const plg = '\u001B[38;5;33m';
|
|
55
56
|
const dev = '\u001B[38;5;79m';
|
|
@@ -121,7 +122,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
121
122
|
matterLoggerFile = 'matter' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.log';
|
|
122
123
|
plugins;
|
|
123
124
|
devices;
|
|
124
|
-
registeredDevices = [];
|
|
125
125
|
nodeStorage;
|
|
126
126
|
nodeContext;
|
|
127
127
|
matterStorageName = 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.json';
|
|
@@ -131,8 +131,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
131
131
|
initialized = false;
|
|
132
132
|
execRunningCount = 0;
|
|
133
133
|
startMatterInterval;
|
|
134
|
-
cleanupTimeout1;
|
|
135
|
-
cleanupTimeout2;
|
|
136
134
|
checkUpdateInterval;
|
|
137
135
|
configureTimeout;
|
|
138
136
|
reachabilityTimeout;
|
|
@@ -161,7 +159,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
161
159
|
// We load asyncronously so is private
|
|
162
160
|
constructor() {
|
|
163
161
|
super();
|
|
162
|
+
// Bind the handler to the instance
|
|
163
|
+
this.matterbridgeMessageHandler = wsMessageHandler.bind(this);
|
|
164
164
|
}
|
|
165
|
+
matterbridgeMessageHandler;
|
|
165
166
|
/** ***********************************************************************************************************************************/
|
|
166
167
|
/** loadInstance() and cleanup() methods */
|
|
167
168
|
/** ***********************************************************************************************************************************/
|
|
@@ -225,6 +226,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
225
226
|
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, this.nodeStorageName), writeQueue: false, expiredInterval: undefined, logging: false });
|
|
226
227
|
// this.log.debug('Creating node storage context for matterbridge');
|
|
227
228
|
this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
|
|
229
|
+
// Check if the storage is corrupted and remove it
|
|
230
|
+
// TODO: Check if the storage is corrupted and remove it
|
|
228
231
|
// Create matterbridge logger
|
|
229
232
|
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: "info" /* LogLevel.INFO */ });
|
|
230
233
|
// Create the file logger for matterbridge (context: matterbridgeFileLog)
|
|
@@ -344,7 +347,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
344
347
|
// We don't do this when the add parameter is set because we shut down the process after adding the plugin
|
|
345
348
|
this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
|
|
346
349
|
try {
|
|
347
|
-
await this.spawnCommand('npm', ['install', '-g', plugin.name]);
|
|
350
|
+
await this.spawnCommand('npm', ['install', '-g', '--omit=dev', plugin.name]);
|
|
348
351
|
this.log.info(`Plugin ${plg}${plugin.name}${nf} reinstalled.`);
|
|
349
352
|
plugin.error = false;
|
|
350
353
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
@@ -473,28 +476,24 @@ export class Matterbridge extends EventEmitter {
|
|
|
473
476
|
}
|
|
474
477
|
if (getParameter('add')) {
|
|
475
478
|
this.log.debug(`Adding plugin ${getParameter('add')}`);
|
|
476
|
-
// await this.executeCommandLine(getParameter('add') as string, 'add');
|
|
477
479
|
await this.plugins.add(getParameter('add'));
|
|
478
480
|
this.emit('shutdown');
|
|
479
481
|
return;
|
|
480
482
|
}
|
|
481
483
|
if (getParameter('remove')) {
|
|
482
484
|
this.log.debug(`Removing plugin ${getParameter('remove')}`);
|
|
483
|
-
// await this.executeCommandLine(getParameter('remove') as string, 'remove');
|
|
484
485
|
await this.plugins.remove(getParameter('remove'));
|
|
485
486
|
this.emit('shutdown');
|
|
486
487
|
return;
|
|
487
488
|
}
|
|
488
489
|
if (getParameter('enable')) {
|
|
489
490
|
this.log.debug(`Enabling plugin ${getParameter('enable')}`);
|
|
490
|
-
// await this.executeCommandLine(getParameter('enable') as string, 'enable');
|
|
491
491
|
await this.plugins.enable(getParameter('enable'));
|
|
492
492
|
this.emit('shutdown');
|
|
493
493
|
return;
|
|
494
494
|
}
|
|
495
495
|
if (getParameter('disable')) {
|
|
496
496
|
this.log.debug(`Disabling plugin ${getParameter('disable')}`);
|
|
497
|
-
// await this.executeCommandLine(getParameter('disable') as string, 'disable');
|
|
498
497
|
await this.plugins.disable(getParameter('disable'));
|
|
499
498
|
this.emit('shutdown');
|
|
500
499
|
return;
|
|
@@ -518,14 +517,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
518
517
|
this.nodeContext = undefined;
|
|
519
518
|
this.nodeStorage = undefined;
|
|
520
519
|
this.plugins.clear();
|
|
521
|
-
this.
|
|
520
|
+
this.devices.clear();
|
|
522
521
|
this.emit('shutdown');
|
|
523
522
|
return;
|
|
524
523
|
}
|
|
525
524
|
// Start the matter storage and create the matterbridge context
|
|
526
525
|
await this.startMatterStorage('json', path.join(this.matterbridgeDirectory, this.matterStorageName));
|
|
527
|
-
this.log.debug(`Creating commissioning server context for ${plg}Matterbridge${db}`);
|
|
528
|
-
this.matterbridgeContext = await this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge aggregator');
|
|
529
526
|
if (hasParameter('reset') && getParameter('reset') === undefined) {
|
|
530
527
|
this.log.info('Resetting Matterbridge commissioning information...');
|
|
531
528
|
await this.matterbridgeContext?.clearAll();
|
|
@@ -563,12 +560,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
563
560
|
this.getPluginLatestVersion(plugin);
|
|
564
561
|
}
|
|
565
562
|
}, 60 * 60 * 1000);
|
|
563
|
+
// Start the matterbridge in mode test
|
|
566
564
|
if (hasParameter('test')) {
|
|
567
565
|
this.bridgeMode = 'bridge';
|
|
568
566
|
MatterbridgeDevice.bridgeMode = 'bridge';
|
|
569
|
-
await this.startTest();
|
|
570
567
|
return;
|
|
571
568
|
}
|
|
569
|
+
// Start the matterbridge in mode controller
|
|
572
570
|
if (hasParameter('controller')) {
|
|
573
571
|
this.bridgeMode = 'controller';
|
|
574
572
|
await this.startController();
|
|
@@ -579,91 +577,63 @@ export class Matterbridge extends EventEmitter {
|
|
|
579
577
|
this.log.info('Setting default matterbridge start mode to bridge');
|
|
580
578
|
await this.nodeContext?.set('bridgeMode', 'bridge');
|
|
581
579
|
}
|
|
580
|
+
// Start matterbridge in bridge mode
|
|
582
581
|
if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
|
|
583
582
|
this.bridgeMode = 'bridge';
|
|
584
583
|
MatterbridgeDevice.bridgeMode = 'bridge';
|
|
585
|
-
|
|
586
|
-
throw new Error('No storage manager initialized');
|
|
587
|
-
this.log.debug('Starting matterbridge in mode', this.bridgeMode);
|
|
588
|
-
this.matterServer = this.createMatterServer(this.storageManager);
|
|
589
|
-
this.log.debug(`Creating commissioning server for ${plg}Matterbridge${db}`);
|
|
590
|
-
this.commissioningServer = await this.createCommisioningServer(this.matterbridgeContext, 'Matterbridge');
|
|
591
|
-
this.log.debug(`Creating matter aggregator for ${plg}Matterbridge${db}`);
|
|
592
|
-
this.matterAggregator = await this.createMatterAggregator(this.matterbridgeContext, 'Matterbridge');
|
|
593
|
-
this.log.debug('Adding matterbridge aggregator to commissioning server');
|
|
594
|
-
this.commissioningServer.addDevice(this.matterAggregator);
|
|
595
|
-
this.log.debug('Adding matterbridge commissioning server to matter server');
|
|
596
|
-
await this.matterServer.addCommissioningServer(this.commissioningServer, { uniqueStorageKey: 'Matterbridge' });
|
|
597
|
-
for (const plugin of this.plugins) {
|
|
598
|
-
plugin.configJson = await this.plugins.loadConfig(plugin);
|
|
599
|
-
plugin.schemaJson = await this.plugins.loadSchema(plugin);
|
|
600
|
-
// Check if the plugin is available
|
|
601
|
-
if (!(await this.plugins.resolve(plugin.path))) {
|
|
602
|
-
this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
|
|
603
|
-
plugin.enabled = false;
|
|
604
|
-
plugin.error = true;
|
|
605
|
-
continue;
|
|
606
|
-
}
|
|
607
|
-
// Check if the plugin has a new version
|
|
608
|
-
this.getPluginLatestVersion(plugin); // No await do it asyncronously
|
|
609
|
-
if (!plugin.enabled) {
|
|
610
|
-
this.log.info(`Plugin ${plg}${plugin.name}${nf} not enabled`);
|
|
611
|
-
continue;
|
|
612
|
-
}
|
|
613
|
-
plugin.error = false;
|
|
614
|
-
plugin.locked = false;
|
|
615
|
-
plugin.loaded = false;
|
|
616
|
-
plugin.started = false;
|
|
617
|
-
plugin.configured = false;
|
|
618
|
-
plugin.connected = undefined;
|
|
619
|
-
plugin.registeredDevices = undefined;
|
|
620
|
-
plugin.addedDevices = undefined;
|
|
621
|
-
plugin.qrPairingCode = undefined;
|
|
622
|
-
plugin.manualPairingCode = undefined;
|
|
623
|
-
this.plugins.load(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
|
|
624
|
-
}
|
|
584
|
+
this.log.debug(`Starting matterbridge in mode ${this.bridgeMode}`);
|
|
625
585
|
await this.startBridge();
|
|
626
586
|
return;
|
|
627
587
|
}
|
|
588
|
+
// Start matterbridge in childbridge mode
|
|
628
589
|
if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
|
|
629
590
|
this.bridgeMode = 'childbridge';
|
|
630
591
|
MatterbridgeDevice.bridgeMode = 'childbridge';
|
|
631
|
-
|
|
632
|
-
throw new Error('No storage manager initialized');
|
|
633
|
-
this.log.debug('Starting matterbridge in mode', this.bridgeMode);
|
|
634
|
-
this.matterServer = this.createMatterServer(this.storageManager);
|
|
635
|
-
for (const plugin of this.plugins) {
|
|
636
|
-
plugin.configJson = await this.plugins.loadConfig(plugin);
|
|
637
|
-
plugin.schemaJson = await this.plugins.loadSchema(plugin);
|
|
638
|
-
// Check if the plugin is available
|
|
639
|
-
if (!(await this.plugins.resolve(plugin.path))) {
|
|
640
|
-
this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
|
|
641
|
-
plugin.enabled = false;
|
|
642
|
-
plugin.error = true;
|
|
643
|
-
continue;
|
|
644
|
-
}
|
|
645
|
-
// Check if the plugin has a new version
|
|
646
|
-
this.getPluginLatestVersion(plugin); // No await do it asyncronously
|
|
647
|
-
if (!plugin.enabled) {
|
|
648
|
-
this.log.info(`Plugin ${plg}${plugin.name}${nf} not enabled`);
|
|
649
|
-
continue;
|
|
650
|
-
}
|
|
651
|
-
plugin.error = false;
|
|
652
|
-
plugin.locked = false;
|
|
653
|
-
plugin.loaded = false;
|
|
654
|
-
plugin.started = false;
|
|
655
|
-
plugin.configured = false;
|
|
656
|
-
plugin.connected = false;
|
|
657
|
-
plugin.registeredDevices = undefined;
|
|
658
|
-
plugin.addedDevices = undefined;
|
|
659
|
-
plugin.qrPairingCode = undefined;
|
|
660
|
-
plugin.manualPairingCode = undefined;
|
|
661
|
-
this.plugins.load(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
|
|
662
|
-
}
|
|
592
|
+
this.log.debug(`Starting matterbridge in mode ${this.bridgeMode}`);
|
|
663
593
|
await this.startChildbridge();
|
|
664
594
|
return;
|
|
665
595
|
}
|
|
666
596
|
}
|
|
597
|
+
/**
|
|
598
|
+
* Asynchronously loads and starts the registered plugins.
|
|
599
|
+
*
|
|
600
|
+
* This method is responsible for initializing and staarting all enabled plugins.
|
|
601
|
+
* It ensures that each plugin is properly loaded and started before the ridge starts.
|
|
602
|
+
*
|
|
603
|
+
* @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
|
|
604
|
+
*/
|
|
605
|
+
async startPlugins() {
|
|
606
|
+
// Check, load and start the plugins
|
|
607
|
+
for (const plugin of this.plugins) {
|
|
608
|
+
plugin.configJson = await this.plugins.loadConfig(plugin);
|
|
609
|
+
plugin.schemaJson = await this.plugins.loadSchema(plugin);
|
|
610
|
+
// Check if the plugin is available
|
|
611
|
+
if (!(await this.plugins.resolve(plugin.path))) {
|
|
612
|
+
this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
|
|
613
|
+
plugin.enabled = false;
|
|
614
|
+
plugin.error = true;
|
|
615
|
+
continue;
|
|
616
|
+
}
|
|
617
|
+
// Check if the plugin has a new version
|
|
618
|
+
this.getPluginLatestVersion(plugin); // No await do it asyncronously
|
|
619
|
+
if (!plugin.enabled) {
|
|
620
|
+
this.log.info(`Plugin ${plg}${plugin.name}${nf} not enabled`);
|
|
621
|
+
continue;
|
|
622
|
+
}
|
|
623
|
+
plugin.error = false;
|
|
624
|
+
plugin.locked = false;
|
|
625
|
+
plugin.loaded = false;
|
|
626
|
+
plugin.started = false;
|
|
627
|
+
plugin.configured = false;
|
|
628
|
+
plugin.connected = undefined;
|
|
629
|
+
plugin.registeredDevices = undefined;
|
|
630
|
+
plugin.addedDevices = undefined;
|
|
631
|
+
plugin.qrPairingCode = undefined;
|
|
632
|
+
plugin.manualPairingCode = undefined;
|
|
633
|
+
this.plugins.load(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
|
|
634
|
+
}
|
|
635
|
+
this.wssSendRefreshRequired();
|
|
636
|
+
}
|
|
667
637
|
/**
|
|
668
638
|
* Registers the signal handlers for SIGINT and SIGTERM.
|
|
669
639
|
* When either of these signals are received, the cleanup method is called with an appropriate message.
|
|
@@ -1158,7 +1128,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1158
1128
|
});
|
|
1159
1129
|
this.webSocketServer = undefined;
|
|
1160
1130
|
}
|
|
1161
|
-
// this.cleanupTimeout1 = setTimeout(async () => {
|
|
1162
1131
|
// Closing matter
|
|
1163
1132
|
await this.stopMatterServer();
|
|
1164
1133
|
// Closing matter storage
|
|
@@ -1175,8 +1144,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1175
1144
|
if (this.nodeStorage && this.nodeContext) {
|
|
1176
1145
|
this.log.info('Saving registered devices...');
|
|
1177
1146
|
const serializedRegisteredDevices = [];
|
|
1178
|
-
this.
|
|
1179
|
-
const serializedMatterbridgeDevice =
|
|
1147
|
+
this.devices.forEach(async (device) => {
|
|
1148
|
+
const serializedMatterbridgeDevice = device.serialize();
|
|
1180
1149
|
// this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
|
|
1181
1150
|
if (serializedMatterbridgeDevice)
|
|
1182
1151
|
serializedRegisteredDevices.push(serializedMatterbridgeDevice);
|
|
@@ -1203,9 +1172,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1203
1172
|
this.log.error('Error saving registered devices: nodeContext not found!');
|
|
1204
1173
|
}
|
|
1205
1174
|
this.plugins.clear();
|
|
1206
|
-
this.
|
|
1207
|
-
// this.log.info('Waiting for matter to deliver last messages...');
|
|
1208
|
-
// this.cleanupTimeout2 = setTimeout(async () => {
|
|
1175
|
+
this.devices.clear();
|
|
1209
1176
|
if (restart) {
|
|
1210
1177
|
if (message === 'updating...') {
|
|
1211
1178
|
this.log.info('Cleanup completed. Updating...');
|
|
@@ -1240,8 +1207,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1240
1207
|
}
|
|
1241
1208
|
this.hasCleanupStarted = false;
|
|
1242
1209
|
this.initialized = false;
|
|
1243
|
-
// }, 2 * 1000);
|
|
1244
|
-
// }, 3 * 1000);
|
|
1245
1210
|
}
|
|
1246
1211
|
}
|
|
1247
1212
|
/**
|
|
@@ -1251,17 +1216,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
1251
1216
|
* @returns {Promise<void>} - A promise that resolves when the device is added.
|
|
1252
1217
|
*/
|
|
1253
1218
|
async addBridgedDevice(pluginName, device) {
|
|
1254
|
-
this.log.debug(`Adding bridged device ${dev}${device.deviceName}${db} (${
|
|
1219
|
+
this.log.debug(`Adding bridged device ${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
1255
1220
|
// Check if the plugin is registered
|
|
1256
1221
|
const plugin = this.plugins.get(pluginName);
|
|
1257
1222
|
if (!plugin) {
|
|
1258
|
-
this.log.error(`Error adding bridged device ${dev}${device.deviceName}${er} (${
|
|
1223
|
+
this.log.error(`Error adding bridged device ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) plugin ${plg}${pluginName}${er} not found`);
|
|
1259
1224
|
return;
|
|
1260
1225
|
}
|
|
1261
1226
|
// Register and add the device to matterbridge aggregator in bridge mode
|
|
1262
1227
|
if (this.bridgeMode === 'bridge') {
|
|
1263
1228
|
if (!this.matterAggregator) {
|
|
1264
|
-
this.log.error(`Adding bridged device ${dev}${device.deviceName}${er} (${
|
|
1229
|
+
this.log.error(`Adding bridged device ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er} error: matterAggregator not found`);
|
|
1265
1230
|
return;
|
|
1266
1231
|
}
|
|
1267
1232
|
this.matterAggregator.addBridgedDevice(device);
|
|
@@ -1301,7 +1266,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1301
1266
|
plugin.aggregator?.addBridgedDevice(device);
|
|
1302
1267
|
}
|
|
1303
1268
|
}
|
|
1304
|
-
this.registeredDevices.push({ plugin: pluginName, device });
|
|
1305
1269
|
if (plugin.registeredDevices !== undefined)
|
|
1306
1270
|
plugin.registeredDevices++;
|
|
1307
1271
|
if (plugin.addedDevices !== undefined)
|
|
@@ -1317,17 +1281,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
1317
1281
|
* @returns A Promise that resolves when the device is successfully removed.
|
|
1318
1282
|
*/
|
|
1319
1283
|
async removeBridgedDevice(pluginName, device) {
|
|
1320
|
-
this.log.debug(`Removing bridged device ${dev}${device.deviceName}${db} (${
|
|
1284
|
+
this.log.debug(`Removing bridged device ${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
1321
1285
|
// Check if the plugin is registered
|
|
1322
1286
|
const plugin = this.plugins.get(pluginName);
|
|
1323
1287
|
if (!plugin) {
|
|
1324
|
-
this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${
|
|
1288
|
+
this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
1325
1289
|
return;
|
|
1326
1290
|
}
|
|
1327
1291
|
// Remove the device from matterbridge aggregator in bridge mode
|
|
1328
1292
|
if (this.bridgeMode === 'bridge') {
|
|
1329
1293
|
if (!this.matterAggregator) {
|
|
1330
|
-
this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${
|
|
1294
|
+
this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: matterAggregator not found`);
|
|
1331
1295
|
return;
|
|
1332
1296
|
}
|
|
1333
1297
|
if (device.number !== undefined) {
|
|
@@ -1337,13 +1301,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1337
1301
|
// device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerShutDownEvent({});
|
|
1338
1302
|
// device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerLeaveEvent({});
|
|
1339
1303
|
this.matterAggregator?.removeBridgedDevice(device);
|
|
1340
|
-
this.
|
|
1341
|
-
if (registeredDevice.device === device) {
|
|
1342
|
-
this.registeredDevices.splice(index, 1);
|
|
1343
|
-
return;
|
|
1344
|
-
}
|
|
1345
|
-
});
|
|
1346
|
-
this.log.info(`Removed bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
1304
|
+
this.log.info(`Removed bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${zb}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
1347
1305
|
if (plugin.registeredDevices !== undefined)
|
|
1348
1306
|
plugin.registeredDevices--;
|
|
1349
1307
|
if (plugin.addedDevices !== undefined)
|
|
@@ -1353,34 +1311,22 @@ export class Matterbridge extends EventEmitter {
|
|
|
1353
1311
|
if (this.bridgeMode === 'childbridge') {
|
|
1354
1312
|
if (plugin.type === 'AccessoryPlatform') {
|
|
1355
1313
|
if (!plugin.commissioningServer) {
|
|
1356
|
-
this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${
|
|
1314
|
+
this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: commissioning server not found`);
|
|
1357
1315
|
return;
|
|
1358
1316
|
}
|
|
1359
|
-
this.registeredDevices.forEach((registeredDevice, index) => {
|
|
1360
|
-
if (registeredDevice.device === device) {
|
|
1361
|
-
this.registeredDevices.splice(index, 1);
|
|
1362
|
-
return;
|
|
1363
|
-
}
|
|
1364
|
-
});
|
|
1365
1317
|
}
|
|
1366
1318
|
else if (plugin.type === 'DynamicPlatform') {
|
|
1367
1319
|
if (!plugin.aggregator) {
|
|
1368
|
-
this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${
|
|
1320
|
+
this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator not found`);
|
|
1369
1321
|
return;
|
|
1370
1322
|
}
|
|
1371
|
-
this.registeredDevices.forEach((registeredDevice, index) => {
|
|
1372
|
-
if (registeredDevice.device === device) {
|
|
1373
|
-
this.registeredDevices.splice(index, 1);
|
|
1374
|
-
return;
|
|
1375
|
-
}
|
|
1376
|
-
});
|
|
1377
1323
|
if (device.number !== undefined) {
|
|
1378
1324
|
device.setBridgedDeviceReachability(false);
|
|
1379
1325
|
device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
|
|
1380
1326
|
}
|
|
1381
1327
|
plugin.aggregator.removeBridgedDevice(device);
|
|
1382
1328
|
}
|
|
1383
|
-
this.log.info(`Removed bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${
|
|
1329
|
+
this.log.info(`Removed bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${zb}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
1384
1330
|
if (plugin.registeredDevices !== undefined)
|
|
1385
1331
|
plugin.registeredDevices--;
|
|
1386
1332
|
if (plugin.addedDevices !== undefined)
|
|
@@ -1403,18 +1349,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
1403
1349
|
*/
|
|
1404
1350
|
async removeAllBridgedDevices(pluginName) {
|
|
1405
1351
|
this.log.debug(`Removing all bridged devices for plugin ${plg}${pluginName}${db}`);
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
devicesToRemove.push(registeredDevice);
|
|
1352
|
+
this.devices.forEach(async (device) => {
|
|
1353
|
+
if (device.plugin === pluginName) {
|
|
1354
|
+
await this.removeBridgedDevice(pluginName, device);
|
|
1410
1355
|
}
|
|
1411
|
-
}
|
|
1412
|
-
for (const registeredDevice of devicesToRemove) {
|
|
1413
|
-
this.removeBridgedDevice(pluginName, registeredDevice.device);
|
|
1414
|
-
}
|
|
1415
|
-
}
|
|
1416
|
-
async startTest() {
|
|
1417
|
-
// Start the Matterbridge
|
|
1356
|
+
});
|
|
1418
1357
|
}
|
|
1419
1358
|
/**
|
|
1420
1359
|
* Starts the Matterbridge in bridge mode.
|
|
@@ -1422,9 +1361,22 @@ export class Matterbridge extends EventEmitter {
|
|
|
1422
1361
|
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1423
1362
|
*/
|
|
1424
1363
|
async startBridge() {
|
|
1425
|
-
// Plugins are loaded and started by loadPlugin on startup and plugin.loaded and plugin.started are set to true
|
|
1426
1364
|
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1427
|
-
this.
|
|
1365
|
+
if (!this.storageManager)
|
|
1366
|
+
throw new Error('No storage manager initialized');
|
|
1367
|
+
if (!this.matterbridgeContext)
|
|
1368
|
+
throw new Error('No storage context initialized');
|
|
1369
|
+
this.matterServer = this.createMatterServer(this.storageManager);
|
|
1370
|
+
this.log.debug(`Creating commissioning server for ${plg}Matterbridge${db}`);
|
|
1371
|
+
this.commissioningServer = await this.createCommisioningServer(this.matterbridgeContext, 'Matterbridge');
|
|
1372
|
+
this.log.debug(`Creating matter aggregator for ${plg}Matterbridge${db}`);
|
|
1373
|
+
this.matterAggregator = await this.createMatterAggregator(this.matterbridgeContext, 'Matterbridge');
|
|
1374
|
+
this.log.debug('Adding matterbridge aggregator to commissioning server');
|
|
1375
|
+
this.commissioningServer.addDevice(this.matterAggregator);
|
|
1376
|
+
this.log.debug('Adding matterbridge commissioning server to matter server');
|
|
1377
|
+
await this.matterServer.addCommissioningServer(this.commissioningServer, { uniqueStorageKey: 'Matterbridge' });
|
|
1378
|
+
await this.startPlugins();
|
|
1379
|
+
this.log.debug('Starting start matter interval in bridge mode');
|
|
1428
1380
|
let failCount = 0;
|
|
1429
1381
|
this.startMatterInterval = setInterval(async () => {
|
|
1430
1382
|
for (const plugin of this.plugins) {
|
|
@@ -1453,8 +1405,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
1453
1405
|
clearInterval(this.startMatterInterval);
|
|
1454
1406
|
this.startMatterInterval = undefined;
|
|
1455
1407
|
this.log.debug('Cleared startMatterInterval interval for Matterbridge');
|
|
1408
|
+
// Start the Matter server
|
|
1456
1409
|
await this.startMatterServer();
|
|
1457
1410
|
this.log.notice('Matter server started');
|
|
1411
|
+
// Show the QR code for commissioning or log the already commissioned message
|
|
1412
|
+
await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
|
|
1458
1413
|
// Configure the plugins
|
|
1459
1414
|
this.configureTimeout = setTimeout(async () => {
|
|
1460
1415
|
for (const plugin of this.plugins) {
|
|
@@ -1468,9 +1423,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1468
1423
|
this.log.error(`Error configuring plugin ${plg}${plugin.name}${er}`, error);
|
|
1469
1424
|
}
|
|
1470
1425
|
}
|
|
1426
|
+
this.wssSendRefreshRequired();
|
|
1471
1427
|
}, 30 * 1000);
|
|
1472
|
-
// Show the QR code for commissioning or log the already commissioned message
|
|
1473
|
-
await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
|
|
1474
1428
|
// Setting reachability to true
|
|
1475
1429
|
this.reachabilityTimeout = setTimeout(() => {
|
|
1476
1430
|
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
@@ -1487,9 +1441,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1487
1441
|
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1488
1442
|
*/
|
|
1489
1443
|
async startChildbridge() {
|
|
1490
|
-
//
|
|
1491
|
-
// addDevice and addBridgedDeevice create the commissionig servers and add the devices to the the commissioning server or to the aggregator
|
|
1444
|
+
// Matterbridge.addBridgedDevice creates the commissionig servers and add the devices to the the commissioning server or to the aggregator
|
|
1492
1445
|
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1446
|
+
if (!this.storageManager)
|
|
1447
|
+
throw new Error('No storage manager initialized');
|
|
1448
|
+
this.matterServer = this.createMatterServer(this.storageManager);
|
|
1449
|
+
await this.startPlugins();
|
|
1493
1450
|
this.log.debug('Starting start matter interval in childbridge mode...');
|
|
1494
1451
|
let failCount = 0;
|
|
1495
1452
|
this.startMatterInterval = setInterval(async () => {
|
|
@@ -1501,7 +1458,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1501
1458
|
if (plugin.error) {
|
|
1502
1459
|
clearInterval(this.startMatterInterval);
|
|
1503
1460
|
this.startMatterInterval = undefined;
|
|
1504
|
-
this.log.debug('Cleared startMatterInterval interval for
|
|
1461
|
+
this.log.debug('Cleared startMatterInterval interval for a plugin in error state');
|
|
1505
1462
|
this.log.error(`The plugin ${plg}${plugin.name}${er} is in error state.`);
|
|
1506
1463
|
this.log.error('The bridge will not start until the problem is solved to prevent the controllers from deleting all registered devices.');
|
|
1507
1464
|
this.log.error('If you want to start the bridge disable the plugin in error state and restart.');
|
|
@@ -1523,6 +1480,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1523
1480
|
clearInterval(this.startMatterInterval);
|
|
1524
1481
|
this.startMatterInterval = undefined;
|
|
1525
1482
|
this.log.debug('Cleared startMatterInterval interval in childbridge mode');
|
|
1483
|
+
// Start the Matter server
|
|
1526
1484
|
await this.startMatterServer();
|
|
1527
1485
|
this.log.notice('Matter server started');
|
|
1528
1486
|
// Configure the plugins
|
|
@@ -1538,6 +1496,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1538
1496
|
this.log.error(`Error configuring plugin ${plg}${plugin.name}${er}`, error);
|
|
1539
1497
|
}
|
|
1540
1498
|
}
|
|
1499
|
+
this.wssSendRefreshRequired();
|
|
1541
1500
|
}, 30 * 1000);
|
|
1542
1501
|
for (const plugin of this.plugins) {
|
|
1543
1502
|
if (!plugin.enabled || plugin.error)
|
|
@@ -1778,7 +1737,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1778
1737
|
await this.storageManager.initialize();
|
|
1779
1738
|
this.log.debug('Storage initialized');
|
|
1780
1739
|
if (storageType === 'json') {
|
|
1781
|
-
await this.
|
|
1740
|
+
await this.backupMatterStorage(storageName, storageName.replace('.json', '') + '.backup.json');
|
|
1782
1741
|
}
|
|
1783
1742
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1784
1743
|
}
|
|
@@ -1787,6 +1746,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1787
1746
|
this.log.error(`Please delete it and rename ${storageName.replace('.json', '.backup.json')} to ${storageName} and try to restart Matterbridge.`);
|
|
1788
1747
|
await this.cleanup('Storage initialize() error!');
|
|
1789
1748
|
}
|
|
1749
|
+
this.log.debug(`Creating commissioning server context for ${plg}Matterbridge${db}`);
|
|
1750
|
+
this.matterbridgeContext = await this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge aggregator');
|
|
1790
1751
|
}
|
|
1791
1752
|
/**
|
|
1792
1753
|
* Makes a backup copy of the specified matter JSON storage file.
|
|
@@ -1794,7 +1755,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1794
1755
|
* @param storageName - The name of the JSON storage file to be backed up.
|
|
1795
1756
|
* @param backupName - The name of the backup file to be created.
|
|
1796
1757
|
*/
|
|
1797
|
-
async
|
|
1758
|
+
async backupMatterStorage(storageName, backupName) {
|
|
1798
1759
|
try {
|
|
1799
1760
|
this.log.debug(`Making backup copy of ${storageName}`);
|
|
1800
1761
|
await fs.copyFile(storageName, backupName);
|
|
@@ -2035,6 +1996,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2035
1996
|
}
|
|
2036
1997
|
}
|
|
2037
1998
|
}
|
|
1999
|
+
this.wssSendRefreshRequired();
|
|
2038
2000
|
},
|
|
2039
2001
|
commissioningChangedCallback: async (fabricIndex) => {
|
|
2040
2002
|
const fabricInfo = commissioningServer.getCommissionedFabricInformation(fabricIndex);
|
|
@@ -2077,6 +2039,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2077
2039
|
}
|
|
2078
2040
|
}
|
|
2079
2041
|
}
|
|
2042
|
+
this.wssSendRefreshRequired();
|
|
2080
2043
|
},
|
|
2081
2044
|
});
|
|
2082
2045
|
if (this.passcode !== undefined)
|
|
@@ -2236,6 +2199,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2236
2199
|
}
|
|
2237
2200
|
}
|
|
2238
2201
|
}
|
|
2202
|
+
this.wssSendRefreshRequired();
|
|
2239
2203
|
}
|
|
2240
2204
|
/**
|
|
2241
2205
|
* Sanitizes the fabric information by converting bigint properties to string cause res.json doesn't know bigint.
|
|
@@ -2358,6 +2322,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
2358
2322
|
case 4701:
|
|
2359
2323
|
vendorName = '(Tuya)';
|
|
2360
2324
|
break;
|
|
2325
|
+
case 4718:
|
|
2326
|
+
vendorName = '(Xiaomi)';
|
|
2327
|
+
break;
|
|
2361
2328
|
case 4742:
|
|
2362
2329
|
vendorName = '(eWeLink)';
|
|
2363
2330
|
break;
|
|
@@ -2452,7 +2419,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2452
2419
|
this.wssSendMessage('spawn', this.log.now(), 'Matterbridge:spawn', `child process closed with code ${code} and signal ${signal}`);
|
|
2453
2420
|
if (code === 0) {
|
|
2454
2421
|
if (cmdLine.startsWith('npm install -g'))
|
|
2455
|
-
this.log.notice(`Package ${cmdLine.replace('npm install -g ', '').replace('--verbose', '')} installed correctly`);
|
|
2422
|
+
this.log.notice(`Package ${cmdLine.replace('npm install -g ', '').replace('--verbose', '').replace('--omit=dev', '')} installed correctly`);
|
|
2456
2423
|
this.log.debug(`Child process "${cmdLine}" closed with code ${code} and signal ${signal}`);
|
|
2457
2424
|
resolve();
|
|
2458
2425
|
}
|
|
@@ -2533,7 +2500,33 @@ export class Matterbridge extends EventEmitter {
|
|
|
2533
2500
|
// Send the message to all connected clients
|
|
2534
2501
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2535
2502
|
if (client.readyState === WebSocket.OPEN) {
|
|
2536
|
-
client.send(JSON.stringify({ level, time, name, message }));
|
|
2503
|
+
client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
|
|
2504
|
+
}
|
|
2505
|
+
});
|
|
2506
|
+
}
|
|
2507
|
+
/**
|
|
2508
|
+
* Sends a need to refresh WebSocket message to all connected clients.
|
|
2509
|
+
*
|
|
2510
|
+
*/
|
|
2511
|
+
wssSendRefreshRequired() {
|
|
2512
|
+
this.matterbridgeInformation.refreshRequired = true;
|
|
2513
|
+
// Send the message to all connected clients
|
|
2514
|
+
this.webSocketServer?.clients.forEach((client) => {
|
|
2515
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
2516
|
+
client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Matterbridge', method: 'refresh_required', params: {} }));
|
|
2517
|
+
}
|
|
2518
|
+
});
|
|
2519
|
+
}
|
|
2520
|
+
/**
|
|
2521
|
+
* Sends a need to restart WebSocket message to all connected clients.
|
|
2522
|
+
*
|
|
2523
|
+
*/
|
|
2524
|
+
wssSendRestartRequired() {
|
|
2525
|
+
this.matterbridgeInformation.restartRequired = true;
|
|
2526
|
+
// Send the message to all connected clients
|
|
2527
|
+
this.webSocketServer?.clients.forEach((client) => {
|
|
2528
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
2529
|
+
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Matterbridge', method: 'restart_required', params: {} }));
|
|
2537
2530
|
}
|
|
2538
2531
|
});
|
|
2539
2532
|
}
|
|
@@ -2652,7 +2645,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2652
2645
|
}
|
|
2653
2646
|
if (initializeError)
|
|
2654
2647
|
return;
|
|
2655
|
-
// Createe a WebSocket server and attach it to the http server
|
|
2648
|
+
// Createe a WebSocket server and attach it to the http or https server
|
|
2656
2649
|
const wssPort = port;
|
|
2657
2650
|
const wssHost = hasParameter('ssl') ? `wss://${this.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.systemInformation.ipv4Address}:${wssPort}`;
|
|
2658
2651
|
this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
|
|
@@ -2662,6 +2655,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
2662
2655
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
2663
2656
|
ws.on('message', (message) => {
|
|
2664
2657
|
this.log.debug(`WebSocket client message: ${message}`);
|
|
2658
|
+
this.matterbridgeMessageHandler(ws, message);
|
|
2659
|
+
});
|
|
2660
|
+
ws.on('ping', () => {
|
|
2661
|
+
this.log.debug('WebSocket client ping');
|
|
2662
|
+
ws.pong();
|
|
2663
|
+
});
|
|
2664
|
+
ws.on('pong', () => {
|
|
2665
|
+
this.log.debug('WebSocket client pong');
|
|
2665
2666
|
});
|
|
2666
2667
|
ws.on('close', () => {
|
|
2667
2668
|
this.log.info('WebSocket client disconnected');
|
|
@@ -2724,12 +2725,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2724
2725
|
this.matterbridgeInformation.matterbridgeQrPairingCode = this.matterbridgeQrPairingCode;
|
|
2725
2726
|
this.matterbridgeInformation.matterbridgeManualPairingCode = this.matterbridgeManualPairingCode;
|
|
2726
2727
|
this.matterbridgeInformation.matterbridgeFabricInformations = this.matterbridgeFabricInformations;
|
|
2727
|
-
// this.matterbridgeInformation.matterbridgeSessionInformations = this.matterbridgeSessionInformations;
|
|
2728
2728
|
this.matterbridgeInformation.matterbridgeSessionInformations = Array.from(this.matterbridgeSessionInformations.values());
|
|
2729
|
-
|
|
2730
|
-
if (this.profile)
|
|
2731
|
-
this.matterbridgeInformation.profile = this.profile;
|
|
2732
|
-
// const response = { wssHost, ssl: hasParameter('ssl'), qrPairingCode, manualPairingCode, systemInformation: this.systemInformation, matterbridgeInformation: this.matterbridgeInformation };
|
|
2729
|
+
this.matterbridgeInformation.profile = this.profile;
|
|
2733
2730
|
const response = { wssHost, ssl: hasParameter('ssl'), systemInformation: this.systemInformation, matterbridgeInformation: this.matterbridgeInformation };
|
|
2734
2731
|
// this.log.debug('Response:', debugStringify(response));
|
|
2735
2732
|
res.json(response);
|
|
@@ -2745,21 +2742,21 @@ export class Matterbridge extends EventEmitter {
|
|
|
2745
2742
|
this.expressApp.get('/api/devices', (req, res) => {
|
|
2746
2743
|
this.log.debug('The frontend sent /api/devices');
|
|
2747
2744
|
const data = [];
|
|
2748
|
-
this.
|
|
2749
|
-
let name =
|
|
2745
|
+
this.devices.forEach(async (device) => {
|
|
2746
|
+
let name = device.getClusterServer(BasicInformationCluster)?.attributes.nodeLabel?.getLocal();
|
|
2750
2747
|
if (!name)
|
|
2751
|
-
name =
|
|
2752
|
-
let serial =
|
|
2748
|
+
name = device.getClusterServer(BridgedDeviceBasicInformationCluster)?.attributes.nodeLabel?.getLocal() ?? 'Unknown';
|
|
2749
|
+
let serial = device.getClusterServer(BasicInformationCluster)?.attributes.serialNumber?.getLocal();
|
|
2753
2750
|
if (!serial)
|
|
2754
|
-
serial =
|
|
2755
|
-
let uniqueId =
|
|
2751
|
+
serial = device.getClusterServer(BridgedDeviceBasicInformationCluster)?.attributes.serialNumber?.getLocal() ?? 'Unknown';
|
|
2752
|
+
let uniqueId = device.getClusterServer(BasicInformationCluster)?.attributes.uniqueId?.getLocal();
|
|
2756
2753
|
if (!uniqueId)
|
|
2757
|
-
uniqueId =
|
|
2758
|
-
const cluster = this.getClusterTextFromDevice(
|
|
2754
|
+
uniqueId = device.getClusterServer(BridgedDeviceBasicInformationCluster)?.attributes.uniqueId?.getLocal() ?? 'Unknown';
|
|
2755
|
+
const cluster = this.getClusterTextFromDevice(device);
|
|
2759
2756
|
data.push({
|
|
2760
|
-
pluginName:
|
|
2761
|
-
type:
|
|
2762
|
-
endpoint:
|
|
2757
|
+
pluginName: device.plugin ?? 'Unknown',
|
|
2758
|
+
type: device.name + ' (0x' + device.deviceType.toString(16).padStart(4, '0') + ')',
|
|
2759
|
+
endpoint: device.number,
|
|
2763
2760
|
name,
|
|
2764
2761
|
serial,
|
|
2765
2762
|
uniqueId,
|
|
@@ -2779,9 +2776,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
2779
2776
|
return;
|
|
2780
2777
|
}
|
|
2781
2778
|
const data = [];
|
|
2782
|
-
this.
|
|
2783
|
-
if (
|
|
2784
|
-
const clusterServers =
|
|
2779
|
+
this.devices.forEach(async (device) => {
|
|
2780
|
+
if (device.plugin === selectedPluginName && device.number === selectedDeviceEndpoint) {
|
|
2781
|
+
const clusterServers = device.getAllClusterServers();
|
|
2785
2782
|
clusterServers.forEach((clusterServer) => {
|
|
2786
2783
|
Object.entries(clusterServer.attributes).forEach(([key, value]) => {
|
|
2787
2784
|
if (clusterServer.name === 'EveHistory')
|
|
@@ -2800,7 +2797,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2800
2797
|
// console.log(error);
|
|
2801
2798
|
}
|
|
2802
2799
|
data.push({
|
|
2803
|
-
endpoint:
|
|
2800
|
+
endpoint: device.number ? device.number.toString() : '...',
|
|
2804
2801
|
clusterName: clusterServer.name,
|
|
2805
2802
|
clusterId: '0x' + clusterServer.id.toString(16).padStart(2, '0'),
|
|
2806
2803
|
attributeName: key,
|
|
@@ -2809,8 +2806,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2809
2806
|
});
|
|
2810
2807
|
});
|
|
2811
2808
|
});
|
|
2812
|
-
|
|
2813
|
-
const name =
|
|
2809
|
+
device.getChildEndpoints().forEach((childEndpoint) => {
|
|
2810
|
+
const name = childEndpoint.uniqueStorageKey;
|
|
2814
2811
|
const clusterServers = childEndpoint.getAllClusterServers();
|
|
2815
2812
|
clusterServers.forEach((clusterServer) => {
|
|
2816
2813
|
Object.entries(clusterServer.attributes).forEach(([key, value]) => {
|
|
@@ -2844,7 +2841,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2844
2841
|
});
|
|
2845
2842
|
res.json(data);
|
|
2846
2843
|
});
|
|
2847
|
-
// Endpoint to
|
|
2844
|
+
// Endpoint to view the log
|
|
2848
2845
|
this.expressApp.get('/api/view-log', async (req, res) => {
|
|
2849
2846
|
this.log.debug('The frontend sent /api/log');
|
|
2850
2847
|
try {
|
|
@@ -2966,7 +2963,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2966
2963
|
// Handle the command setbridgemode from Settings
|
|
2967
2964
|
if (command === 'setbridgemode') {
|
|
2968
2965
|
this.log.debug(`setbridgemode: ${param}`);
|
|
2969
|
-
this.
|
|
2966
|
+
this.wssSendRestartRequired();
|
|
2970
2967
|
await this.nodeContext?.set('bridgeMode', param);
|
|
2971
2968
|
res.json({ message: 'Command received' });
|
|
2972
2969
|
return;
|
|
@@ -3138,7 +3135,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
3138
3135
|
if (command === 'update') {
|
|
3139
3136
|
this.log.info('Updating matterbridge...');
|
|
3140
3137
|
try {
|
|
3141
|
-
await this.spawnCommand('npm', ['install', '-g', 'matterbridge', '--verbose']);
|
|
3138
|
+
await this.spawnCommand('npm', ['install', '-g', 'matterbridge', '--omit=dev', '--verbose']);
|
|
3142
3139
|
this.log.info('Matterbridge has been updated. Full restart required.');
|
|
3143
3140
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
3144
3141
|
}
|
|
@@ -3146,7 +3143,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
3146
3143
|
this.log.error('Error updating matterbridge');
|
|
3147
3144
|
}
|
|
3148
3145
|
await this.updateProcess();
|
|
3149
|
-
this.
|
|
3146
|
+
this.wssSendRestartRequired();
|
|
3150
3147
|
res.json({ message: 'Command received' });
|
|
3151
3148
|
return;
|
|
3152
3149
|
}
|
|
@@ -3164,7 +3161,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
3164
3161
|
return;
|
|
3165
3162
|
this.plugins.saveConfigFromJson(plugin, req.body);
|
|
3166
3163
|
}
|
|
3167
|
-
this.
|
|
3164
|
+
this.wssSendRestartRequired();
|
|
3168
3165
|
res.json({ message: 'Command received' });
|
|
3169
3166
|
return;
|
|
3170
3167
|
}
|
|
@@ -3173,14 +3170,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
3173
3170
|
param = param.replace(/\*/g, '\\');
|
|
3174
3171
|
this.log.info(`Installing plugin ${plg}${param}${nf}...`);
|
|
3175
3172
|
try {
|
|
3176
|
-
await this.spawnCommand('npm', ['install', '-g', param, '--verbose']);
|
|
3173
|
+
await this.spawnCommand('npm', ['install', '-g', param, '--omit=dev', '--verbose']);
|
|
3177
3174
|
this.log.info(`Plugin ${plg}${param}${nf} installed. Full restart required.`);
|
|
3178
3175
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
3179
3176
|
}
|
|
3180
3177
|
catch (error) {
|
|
3181
3178
|
this.log.error(`Error installing plugin ${plg}${param}${er}`);
|
|
3182
3179
|
}
|
|
3183
|
-
this.
|
|
3180
|
+
this.wssSendRestartRequired();
|
|
3184
3181
|
// Also add the plugin to matterbridge so no return!
|
|
3185
3182
|
// res.json({ message: 'Command received' });
|
|
3186
3183
|
// return;
|
|
@@ -3193,6 +3190,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
3193
3190
|
this.plugins.load(plugin, true, 'The plugin has been added', true); // No await do it in the background
|
|
3194
3191
|
}
|
|
3195
3192
|
res.json({ message: 'Command received' });
|
|
3193
|
+
this.wssSendRefreshRequired();
|
|
3196
3194
|
return;
|
|
3197
3195
|
}
|
|
3198
3196
|
// Handle the command removeplugin from Home
|
|
@@ -3206,6 +3204,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
3206
3204
|
await this.plugins.remove(param);
|
|
3207
3205
|
}
|
|
3208
3206
|
res.json({ message: 'Command received' });
|
|
3207
|
+
this.wssSendRefreshRequired();
|
|
3209
3208
|
return;
|
|
3210
3209
|
}
|
|
3211
3210
|
// Handle the command enableplugin from Home
|
|
@@ -3230,6 +3229,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
3230
3229
|
}
|
|
3231
3230
|
}
|
|
3232
3231
|
res.json({ message: 'Command received' });
|
|
3232
|
+
this.wssSendRefreshRequired();
|
|
3233
3233
|
return;
|
|
3234
3234
|
}
|
|
3235
3235
|
// Handle the command disableplugin from Home
|
|
@@ -3245,6 +3245,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
3245
3245
|
}
|
|
3246
3246
|
}
|
|
3247
3247
|
res.json({ message: 'Command received' });
|
|
3248
|
+
this.wssSendRefreshRequired();
|
|
3248
3249
|
return;
|
|
3249
3250
|
}
|
|
3250
3251
|
});
|