matterbridge 1.2.4 → 1.2.6
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 +29 -0
- package/README.md +106 -8
- package/TODO.md +7 -0
- package/dist/AirQualityCluster.d.ts +2 -13
- package/dist/AirQualityCluster.d.ts.map +1 -1
- package/dist/AirQualityCluster.js +6 -6
- package/dist/AirQualityCluster.js.map +1 -1
- package/dist/EveHistoryCluster.d.ts +6 -26
- package/dist/EveHistoryCluster.d.ts.map +1 -1
- package/dist/EveHistoryCluster.js +6 -14
- package/dist/EveHistoryCluster.js.map +1 -1
- package/dist/TvocCluster.d.ts +2 -15
- package/dist/TvocCluster.d.ts.map +1 -1
- package/dist/TvocCluster.js +6 -6
- package/dist/TvocCluster.js.map +1 -1
- package/dist/cli.js +6 -1
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/matterbridge.d.ts +15 -0
- package/dist/matterbridge.d.ts.map +1 -1
- package/dist/matterbridge.js +367 -174
- package/dist/matterbridge.js.map +1 -1
- package/dist/matterbridgeController.d.ts.map +1 -1
- package/dist/matterbridgeController.js +9 -0
- package/dist/matterbridgeController.js.map +1 -1
- package/dist/matterbridgeDevice.d.ts +1241 -9
- package/dist/matterbridgeDevice.d.ts.map +1 -1
- package/dist/matterbridgeDevice.js +315 -92
- package/dist/matterbridgeDevice.js.map +1 -1
- package/frontend/build/asset-manifest.json +14 -14
- package/frontend/build/index.html +1 -1
- package/frontend/build/matterbridge 32x32.png +0 -0
- package/frontend/build/matterbridge 64x64.png +0 -0
- package/frontend/build/static/css/main.61f6cf42.css +1 -1
- package/frontend/build/static/css/main.61f6cf42.css.map +1 -1
- package/frontend/build/static/js/main.6b861489.js +3 -0
- package/frontend/build/static/js/{main.e3553a4d.js.LICENSE.txt → main.6b861489.js.LICENSE.txt} +99 -99
- package/frontend/build/static/js/main.6b861489.js.map +1 -0
- package/matterbridge.service +17 -0
- package/package.json +6 -5
- package/dist/matterbridgeComposed.d.ts +0 -43
- package/dist/matterbridgeComposed.d.ts.map +0 -1
- package/dist/matterbridgeComposed.js +0 -58
- package/dist/matterbridgeComposed.js.map +0 -1
- package/frontend/build/Matterbridge.jpg +0 -0
- package/frontend/build/static/js/main.e3553a4d.js +0 -3
- package/frontend/build/static/js/main.e3553a4d.js.map +0 -1
package/dist/matterbridge.js
CHANGED
|
@@ -31,7 +31,9 @@ import express from 'express';
|
|
|
31
31
|
import os from 'os';
|
|
32
32
|
import path from 'path';
|
|
33
33
|
import { CommissioningController, CommissioningServer, MatterServer } from '@project-chip/matter-node.js';
|
|
34
|
-
import { BasicInformationCluster, BooleanStateCluster,
|
|
34
|
+
import { BasicInformationCluster, BooleanStateCluster,
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
36
|
+
BridgedDeviceBasicInformation, BridgedDeviceBasicInformationCluster, ClusterServer, GeneralCommissioning, PowerSourceCluster, ThreadNetworkDiagnosticsCluster, getClusterNameById, } from '@project-chip/matter-node.js/cluster';
|
|
35
37
|
import { DeviceTypeId, VendorId } from '@project-chip/matter-node.js/datatype';
|
|
36
38
|
import { Aggregator, DeviceTypes, NodeStateInformation } from '@project-chip/matter-node.js/device';
|
|
37
39
|
import { Format, Level, Logger } from '@project-chip/matter-node.js/log';
|
|
@@ -79,6 +81,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
79
81
|
matterbridgeLatestVersion = '';
|
|
80
82
|
bridgeMode = '';
|
|
81
83
|
debugEnabled = false;
|
|
84
|
+
port = 5540;
|
|
82
85
|
log;
|
|
83
86
|
hasCleanupStarted = false;
|
|
84
87
|
registeredPlugins = [];
|
|
@@ -137,6 +140,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
137
140
|
- childbridge: start Matterbridge in childbridge mode
|
|
138
141
|
- frontend [port]: start the frontend on the given port (default 3000)
|
|
139
142
|
- debug: enable debug mode (default false)
|
|
143
|
+
- reset: remove the commissioning for Matterbridge (bridge mode). Shutdown Matterbridge before using it!
|
|
144
|
+
- factoryreset: remove all commissioning information and reset all internal storages. Shutdown Matterbridge before using it!
|
|
140
145
|
- list: list the registered plugins
|
|
141
146
|
- add [plugin path]: register the plugin from the given absolute or relative path
|
|
142
147
|
- add [plugin name]: register the globally installed plugin with the given name
|
|
@@ -145,9 +150,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
145
150
|
- enable [plugin path]: enable the plugin from the given absolute or relative path
|
|
146
151
|
- enable [plugin name]: enable the globally installed plugin with the given name
|
|
147
152
|
- disable [plugin path]: disable the plugin from the given absolute or relative path
|
|
148
|
-
- disable [plugin name]: disable the globally installed plugin with the given name
|
|
153
|
+
- disable [plugin name]: disable the globally installed plugin with the given name
|
|
154
|
+
- reset [plugin path]: remove the commissioning for the plugin from the given absolute or relative path (childbridge mode). Shutdown Matterbridge before using it!
|
|
155
|
+
- reset [plugin name]: remove the commissioning for the globally installed plugin (childbridge mode). Shutdown Matterbridge before using it!\n`);
|
|
149
156
|
process.exit(0);
|
|
150
157
|
}
|
|
158
|
+
// Set the first port to use
|
|
159
|
+
this.port = getIntParameter('port') ?? 5540;
|
|
151
160
|
// Set Matterbridge logger
|
|
152
161
|
if (hasParameter('debug'))
|
|
153
162
|
this.debugEnabled = true;
|
|
@@ -222,6 +231,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
222
231
|
/*
|
|
223
232
|
npm > npm.cmd on windows
|
|
224
233
|
*/
|
|
234
|
+
if (process.platform === 'win32' && command === 'npm') {
|
|
235
|
+
command = command + '.cmd';
|
|
236
|
+
}
|
|
237
|
+
if (process.platform === 'linux' && command === 'npm') {
|
|
238
|
+
args.unshift(command);
|
|
239
|
+
command = 'sudo';
|
|
240
|
+
}
|
|
225
241
|
return new Promise((resolve, reject) => {
|
|
226
242
|
const childProcess = spawn(command, args, {
|
|
227
243
|
stdio: 'inherit',
|
|
@@ -294,10 +310,33 @@ export class Matterbridge extends EventEmitter {
|
|
|
294
310
|
this.emit('shutdown');
|
|
295
311
|
process.exit(0);
|
|
296
312
|
}
|
|
297
|
-
|
|
313
|
+
if (hasParameter('factoryreset')) {
|
|
314
|
+
// Delete matter storage file
|
|
315
|
+
await fs.unlink(path.join(this.matterbridgeDirectory, 'matterbridge.json'));
|
|
316
|
+
// Delete node storage directory with its subdirectories
|
|
317
|
+
await fs.rmdir(path.join(this.matterbridgeDirectory, 'storage'), { recursive: true });
|
|
318
|
+
this.log.info('Factory reset done! Remove all paired devices from the controllers.');
|
|
319
|
+
this.emit('shutdown');
|
|
320
|
+
process.exit(0);
|
|
321
|
+
}
|
|
322
|
+
// Start the storage and create matterbridgeContext (we need it now for frontend and later for matterbridge)
|
|
298
323
|
await this.startStorage('json', path.join(this.matterbridgeDirectory, 'matterbridge.json'));
|
|
299
|
-
this.log.debug(`Creating commissioning server context for ${plg}Matterbridge${db}`);
|
|
300
324
|
this.matterbridgeContext = await this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge aggregator');
|
|
325
|
+
if (hasParameter('reset') && getParameter('reset') === undefined) {
|
|
326
|
+
this.log.info('Resetting Matterbridge commissioning information...');
|
|
327
|
+
await this.matterbridgeContext?.clearAll();
|
|
328
|
+
await this.stopStorage();
|
|
329
|
+
this.log.info('Reset done! Remove the device from the controller.');
|
|
330
|
+
this.emit('shutdown');
|
|
331
|
+
process.exit(0);
|
|
332
|
+
}
|
|
333
|
+
if (getParameter('reset') && getParameter('reset') !== undefined) {
|
|
334
|
+
this.log.debug(`Reset plugin ${getParameter('reset')}`);
|
|
335
|
+
await this.executeCommandLine(getParameter('reset'), 'reset');
|
|
336
|
+
await this.stopStorage();
|
|
337
|
+
this.emit('shutdown');
|
|
338
|
+
process.exit(0);
|
|
339
|
+
}
|
|
301
340
|
// Initialize frontend
|
|
302
341
|
await this.initializeFrontend(getIntParameter('frontend'));
|
|
303
342
|
if (hasParameter('test')) {
|
|
@@ -308,9 +347,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
308
347
|
}
|
|
309
348
|
if (hasParameter('controller')) {
|
|
310
349
|
this.bridgeMode = 'controller';
|
|
311
|
-
this.
|
|
312
|
-
this.mattercontrollerContext = this.storageManager?.createContext('mattercontrollerContext');
|
|
313
|
-
await this.startMatterbridge();
|
|
350
|
+
await this.startMattercontroller();
|
|
314
351
|
return;
|
|
315
352
|
}
|
|
316
353
|
if (hasParameter('bridge')) {
|
|
@@ -461,6 +498,28 @@ export class Matterbridge extends EventEmitter {
|
|
|
461
498
|
this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} not registerd in matterbridge`);
|
|
462
499
|
}
|
|
463
500
|
}
|
|
501
|
+
else if (mode === 'reset') {
|
|
502
|
+
const plugin = this.registeredPlugins.find((registeredPlugin) => registeredPlugin.name === packageJson.name);
|
|
503
|
+
if (plugin) {
|
|
504
|
+
plugin.loaded = undefined;
|
|
505
|
+
plugin.started = undefined;
|
|
506
|
+
plugin.configured = undefined;
|
|
507
|
+
plugin.connected = undefined;
|
|
508
|
+
plugin.paired = undefined;
|
|
509
|
+
plugin.connected = undefined;
|
|
510
|
+
if (!this.storageManager)
|
|
511
|
+
this.log.error(`Plugin ${plg}${plugin.name}${er} storageManager not found`);
|
|
512
|
+
const context = this.storageManager?.createContext(plugin.name);
|
|
513
|
+
if (!context)
|
|
514
|
+
this.log.error(`Plugin ${plg}${plugin.name}${er} context not found`);
|
|
515
|
+
await context?.clearAll();
|
|
516
|
+
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
517
|
+
this.log.info(`Reset commissionig for plugin ${plg}${plugin.name}${nf} done! Remove the device from the controller.`);
|
|
518
|
+
}
|
|
519
|
+
else {
|
|
520
|
+
this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} not registerd in matterbridge`);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
464
523
|
}
|
|
465
524
|
catch (err) {
|
|
466
525
|
this.log.error(`Failed to load plugin from ${plg}${packageJsonPath}${er}: ${err}`);
|
|
@@ -478,6 +537,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
478
537
|
await this.cleanup('SIGTERM received, cleaning up...');
|
|
479
538
|
});
|
|
480
539
|
}
|
|
540
|
+
/**
|
|
541
|
+
* Update matterbridge.
|
|
542
|
+
*/
|
|
543
|
+
async updateProcess() {
|
|
544
|
+
await this.cleanup('updating...', false);
|
|
545
|
+
this.hasCleanupStarted = false;
|
|
546
|
+
}
|
|
481
547
|
/**
|
|
482
548
|
* Restarts the process by spawning a new process and exiting the current process.
|
|
483
549
|
*/
|
|
@@ -485,6 +551,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
485
551
|
await this.cleanup('restarting...', true);
|
|
486
552
|
this.hasCleanupStarted = false;
|
|
487
553
|
}
|
|
554
|
+
/**
|
|
555
|
+
* Shut down the process by exiting the current process.
|
|
556
|
+
*/
|
|
557
|
+
async shutdownProcess() {
|
|
558
|
+
await this.cleanup('shutting down...', false);
|
|
559
|
+
this.hasCleanupStarted = false;
|
|
560
|
+
}
|
|
488
561
|
/**
|
|
489
562
|
* Cleans up the Matterbridge instance.
|
|
490
563
|
* @param message - The cleanup message.
|
|
@@ -507,7 +580,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
507
580
|
await this.savePluginConfig(plugin);
|
|
508
581
|
}
|
|
509
582
|
else {
|
|
510
|
-
this.log.warn(`Plugin ${plg}${plugin.name}${
|
|
583
|
+
this.log.warn(`Plugin ${plg}${plugin.name}${wr} platform not found`);
|
|
511
584
|
}
|
|
512
585
|
}
|
|
513
586
|
// Set reachability to false
|
|
@@ -519,12 +592,34 @@ export class Matterbridge extends EventEmitter {
|
|
|
519
592
|
this.log.error(`Plugin ${plg}${registeredDevice.plugin}${er} not found`);
|
|
520
593
|
return;
|
|
521
594
|
}
|
|
522
|
-
this.
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
595
|
+
if (this.bridgeMode === 'bridge' && registeredDevice.device.number) {
|
|
596
|
+
this.log.debug(`*-- device: ${dev}${registeredDevice.device.name}${db} plugin ${plg}${registeredDevice.plugin}${db} type ${plugin.type}${db}`);
|
|
597
|
+
registeredDevice.device.setBridgedDeviceReachability(false);
|
|
598
|
+
registeredDevice.device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
|
|
599
|
+
}
|
|
600
|
+
if (this.bridgeMode === 'childbridge') {
|
|
601
|
+
if (plugin.type === 'DynamicPlatform' && registeredDevice.device.number) {
|
|
602
|
+
this.log.debug(`*-- device: ${dev}${registeredDevice.device.name}${db} plugin ${plg}${registeredDevice.plugin}${db} type ${plugin.type}${db}`);
|
|
603
|
+
registeredDevice.device.setBridgedDeviceReachability(false);
|
|
604
|
+
registeredDevice.device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
|
|
605
|
+
}
|
|
606
|
+
}
|
|
527
607
|
});
|
|
608
|
+
if (this.bridgeMode === 'bridge') {
|
|
609
|
+
this.log.debug('*Changing reachability to false for Matterbridge');
|
|
610
|
+
this.matterAggregator?.getClusterServerById(BasicInformation.Cluster.id)?.setReachableAttribute(false);
|
|
611
|
+
this.matterAggregator?.getClusterServerById(BasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
|
|
612
|
+
this.commissioningServer?.setReachability(false);
|
|
613
|
+
}
|
|
614
|
+
if (this.bridgeMode === 'childbridge') {
|
|
615
|
+
for (const plugin of this.registeredPlugins) {
|
|
616
|
+
if (!plugin.enabled || plugin.error) continue;
|
|
617
|
+
this.log.debug(`*Changing reachability to false for plugin ${plg}${plugin.name}${db} type ${plugin.type}`);
|
|
618
|
+
plugin.aggregator?.getClusterServerById(BasicInformation.Cluster.id)?.setReachableAttribute(false);
|
|
619
|
+
plugin.aggregator?.getClusterServerById(BasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
|
|
620
|
+
plugin.commissioningServer?.setReachability(false);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
528
623
|
*/
|
|
529
624
|
// Close the express server
|
|
530
625
|
if (this.expressServer) {
|
|
@@ -536,7 +631,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
536
631
|
this.expressApp.removeAllListeners();
|
|
537
632
|
this.expressApp = undefined;
|
|
538
633
|
}
|
|
539
|
-
const cleanupTimeout1
|
|
634
|
+
/*const cleanupTimeout1 =*/ setTimeout(async () => {
|
|
540
635
|
// Closing matter
|
|
541
636
|
await this.stopMatter();
|
|
542
637
|
// Closing storage
|
|
@@ -564,21 +659,28 @@ export class Matterbridge extends EventEmitter {
|
|
|
564
659
|
this.registeredPlugins = [];
|
|
565
660
|
this.registeredDevices = [];
|
|
566
661
|
this.log.info('Waiting for matter to deliver last messages...');
|
|
567
|
-
const cleanupTimeout2
|
|
662
|
+
/*const cleanupTimeout2 =*/ setTimeout(async () => {
|
|
568
663
|
if (restart) {
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
664
|
+
if (message === 'updating...') {
|
|
665
|
+
this.log.info('Cleanup completed. Updating...');
|
|
666
|
+
Matterbridge.instance = undefined;
|
|
667
|
+
this.emit('update');
|
|
668
|
+
}
|
|
669
|
+
else if (message === 'restarting...') {
|
|
670
|
+
this.log.info('Cleanup completed. Restarting...');
|
|
671
|
+
Matterbridge.instance = undefined;
|
|
672
|
+
this.emit('restart');
|
|
673
|
+
}
|
|
572
674
|
}
|
|
573
675
|
else {
|
|
574
676
|
this.log.info('Cleanup completed. Shutting down...');
|
|
575
677
|
Matterbridge.instance = undefined;
|
|
576
678
|
this.emit('shutdown');
|
|
577
679
|
}
|
|
578
|
-
},
|
|
579
|
-
cleanupTimeout2.unref();
|
|
680
|
+
}, 1 * 1000);
|
|
681
|
+
//cleanupTimeout2.unref();
|
|
580
682
|
}, 3 * 1000);
|
|
581
|
-
cleanupTimeout1.unref();
|
|
683
|
+
//cleanupTimeout1.unref();
|
|
582
684
|
}
|
|
583
685
|
}
|
|
584
686
|
/**
|
|
@@ -693,8 +795,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
693
795
|
this.log.warn(`Removing bridged device ${dev}${device.deviceName}${wr} (${dev}${device.name}${wr}) plugin ${plg}${pluginName}${wr} not connected`);
|
|
694
796
|
return;
|
|
695
797
|
}
|
|
696
|
-
//
|
|
798
|
+
// Remove the device from matterbridge aggregator in bridge mode
|
|
697
799
|
if (this.bridgeMode === 'bridge') {
|
|
800
|
+
device.setBridgedDeviceReachability(false);
|
|
801
|
+
device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
|
|
698
802
|
this.matterAggregator.removeBridgedDevice(device);
|
|
699
803
|
this.registeredDevices.forEach((registeredDevice, index) => {
|
|
700
804
|
if (registeredDevice.device === device) {
|
|
@@ -711,7 +815,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
711
815
|
// Only register the device in childbridge mode
|
|
712
816
|
if (this.bridgeMode === 'childbridge') {
|
|
713
817
|
if (plugin.type === 'AccessoryPlatform') {
|
|
714
|
-
this.log.warn(`Removing bridged device ${dev}${device.deviceName}${wr} (${dev}${device.name}${wr}) for plugin ${plg}${pluginName}${wr}
|
|
818
|
+
this.log.warn(`Removing bridged device ${dev}${device.deviceName}${wr} (${dev}${device.name}${wr}) for plugin ${plg}${pluginName}${wr}: AccessoryPlatform not supported in childbridge mode`);
|
|
715
819
|
}
|
|
716
820
|
else if (plugin.type === 'DynamicPlatform') {
|
|
717
821
|
this.registeredDevices.forEach((registeredDevice, index) => {
|
|
@@ -720,6 +824,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
720
824
|
return;
|
|
721
825
|
}
|
|
722
826
|
});
|
|
827
|
+
device.setBridgedDeviceReachability(false);
|
|
828
|
+
device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
|
|
723
829
|
plugin.aggregator.removeBridgedDevice(device);
|
|
724
830
|
}
|
|
725
831
|
this.log.info(`Removed bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
@@ -1134,20 +1240,23 @@ export class Matterbridge extends EventEmitter {
|
|
|
1134
1240
|
}
|
|
1135
1241
|
}
|
|
1136
1242
|
/**
|
|
1137
|
-
* Starts the Matterbridge
|
|
1138
|
-
* If the bridge mode is 'bridge', it creates a commissioning server, matter aggregator,
|
|
1139
|
-
* and starts the matter server.
|
|
1140
|
-
* If the bridge mode is 'childbridge', it starts the plugins, creates commissioning servers,
|
|
1141
|
-
* and starts the matter server when all plugins are loaded and started.
|
|
1243
|
+
* Starts the Matterbridge controller.
|
|
1142
1244
|
* @private
|
|
1143
1245
|
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1144
1246
|
*/
|
|
1145
|
-
async
|
|
1247
|
+
async startMattercontroller() {
|
|
1146
1248
|
if (!this.storageManager) {
|
|
1147
1249
|
this.log.error('No storage manager initialized');
|
|
1148
1250
|
await this.cleanup('No storage manager initialized');
|
|
1149
1251
|
return;
|
|
1150
1252
|
}
|
|
1253
|
+
this.log.info('Creating context: mattercontrollerContext');
|
|
1254
|
+
this.mattercontrollerContext = this.storageManager.createContext('mattercontrollerContext');
|
|
1255
|
+
if (!this.mattercontrollerContext) {
|
|
1256
|
+
this.log.error('No storage context mattercontrollerContext initialized');
|
|
1257
|
+
await this.cleanup('No storage context mattercontrollerContext initialized');
|
|
1258
|
+
return;
|
|
1259
|
+
}
|
|
1151
1260
|
this.log.debug('Starting matterbridge in mode', this.bridgeMode);
|
|
1152
1261
|
this.createMatterServer(this.storageManager);
|
|
1153
1262
|
if (!this.matterServer) {
|
|
@@ -1155,139 +1264,167 @@ export class Matterbridge extends EventEmitter {
|
|
|
1155
1264
|
await this.cleanup('No matter server initialized');
|
|
1156
1265
|
return;
|
|
1157
1266
|
}
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1267
|
+
this.log.info('Creating matter commissioning controller');
|
|
1268
|
+
this.commissioningController = new CommissioningController({
|
|
1269
|
+
autoConnect: false,
|
|
1270
|
+
});
|
|
1271
|
+
this.log.info('Adding matter commissioning controller to matter server');
|
|
1272
|
+
await this.matterServer.addCommissioningController(this.commissioningController);
|
|
1273
|
+
this.log.info('Starting matter server');
|
|
1274
|
+
await this.matterServer.start();
|
|
1275
|
+
this.log.info('Matter server started');
|
|
1276
|
+
if (hasParameter('pairingcode')) {
|
|
1277
|
+
this.log.info('Pairing device with pairingcode:', getParameter('pairingcode'));
|
|
1278
|
+
const pairingCode = getParameter('pairingcode');
|
|
1279
|
+
const ip = this.mattercontrollerContext.has('ip') ? this.mattercontrollerContext.get('ip') : undefined;
|
|
1280
|
+
const port = this.mattercontrollerContext.has('port') ? this.mattercontrollerContext.get('port') : undefined;
|
|
1281
|
+
let longDiscriminator, setupPin, shortDiscriminator;
|
|
1282
|
+
if (pairingCode !== undefined) {
|
|
1283
|
+
const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
|
|
1284
|
+
shortDiscriminator = pairingCodeCodec.shortDiscriminator;
|
|
1285
|
+
longDiscriminator = undefined;
|
|
1286
|
+
setupPin = pairingCodeCodec.passcode;
|
|
1287
|
+
this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
|
|
1163
1288
|
}
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
this.log.info('Matter server started');
|
|
1173
|
-
if (hasParameter('pairingcode')) {
|
|
1174
|
-
const pairingCode = getParameter('pairingcode');
|
|
1175
|
-
const ip = this.mattercontrollerContext.has('ip') ? this.mattercontrollerContext.get('ip') : undefined;
|
|
1176
|
-
const port = this.mattercontrollerContext.has('port') ? this.mattercontrollerContext.get('port') : undefined;
|
|
1177
|
-
let longDiscriminator, setupPin, shortDiscriminator;
|
|
1178
|
-
if (pairingCode !== undefined) {
|
|
1179
|
-
const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
|
|
1180
|
-
shortDiscriminator = pairingCodeCodec.shortDiscriminator;
|
|
1181
|
-
longDiscriminator = undefined;
|
|
1182
|
-
setupPin = pairingCodeCodec.passcode;
|
|
1183
|
-
this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
|
|
1184
|
-
}
|
|
1185
|
-
else {
|
|
1186
|
-
longDiscriminator = await this.mattercontrollerContext.get('longDiscriminator', 3840);
|
|
1187
|
-
if (longDiscriminator > 4095)
|
|
1188
|
-
throw new Error('Discriminator value must be less than 4096');
|
|
1189
|
-
setupPin = this.mattercontrollerContext.get('pin', 20202021);
|
|
1190
|
-
}
|
|
1191
|
-
if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
|
|
1192
|
-
throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
|
|
1193
|
-
}
|
|
1194
|
-
const commissioningOptions = {
|
|
1195
|
-
regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
|
|
1196
|
-
regulatoryCountryCode: 'XX',
|
|
1197
|
-
};
|
|
1198
|
-
const options = {
|
|
1199
|
-
commissioning: commissioningOptions,
|
|
1200
|
-
discovery: {
|
|
1201
|
-
knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
|
|
1202
|
-
identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
|
|
1203
|
-
},
|
|
1204
|
-
passcode: setupPin,
|
|
1205
|
-
};
|
|
1206
|
-
//this.log.info(`Commissioning ... ${JSON.stringify(options)}`);
|
|
1207
|
-
this.log.info('Commissioning ...', options);
|
|
1208
|
-
const nodeId = await this.commissioningController.commissionNode(options);
|
|
1209
|
-
this.mattercontrollerContext.set('nodeId', nodeId.nodeId);
|
|
1210
|
-
this.log.info(`Commissioning successfully done with nodeId: ${nodeId.nodeId}`);
|
|
1211
|
-
// eslint-disable-next-line no-console
|
|
1212
|
-
this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
|
|
1213
|
-
} // (hasParameter('pairingcode'))
|
|
1214
|
-
if (hasParameter('discover')) {
|
|
1215
|
-
//const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
|
|
1216
|
-
//console.log(discover);
|
|
1289
|
+
else {
|
|
1290
|
+
longDiscriminator = await this.mattercontrollerContext.get('longDiscriminator', 3840);
|
|
1291
|
+
if (longDiscriminator > 4095)
|
|
1292
|
+
throw new Error('Discriminator value must be less than 4096');
|
|
1293
|
+
setupPin = this.mattercontrollerContext.get('pin', 20202021);
|
|
1294
|
+
}
|
|
1295
|
+
if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
|
|
1296
|
+
throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
|
|
1217
1297
|
}
|
|
1298
|
+
const commissioningOptions = {
|
|
1299
|
+
regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
|
|
1300
|
+
regulatoryCountryCode: 'XX',
|
|
1301
|
+
};
|
|
1302
|
+
const options = {
|
|
1303
|
+
commissioning: commissioningOptions,
|
|
1304
|
+
discovery: {
|
|
1305
|
+
knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
|
|
1306
|
+
identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
|
|
1307
|
+
},
|
|
1308
|
+
passcode: setupPin,
|
|
1309
|
+
};
|
|
1310
|
+
this.log.info('Commissioning with options:', options);
|
|
1311
|
+
const nodeId = await this.commissioningController.commissionNode(options);
|
|
1312
|
+
this.log.info(`Commissioning successfully done with nodeId: ${nodeId.nodeId}`);
|
|
1313
|
+
this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
|
|
1314
|
+
} // (hasParameter('pairingcode'))
|
|
1315
|
+
if (hasParameter('unpairall')) {
|
|
1316
|
+
this.log.info('***Commissioning controller unpairing all nodes...');
|
|
1218
1317
|
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1219
|
-
this.log.info(`***Commissioning controller has ${nodeIds.length} nodes commisioned: ${this.commissioningController.isCommissioned()}`);
|
|
1220
1318
|
for (const nodeId of nodeIds) {
|
|
1221
|
-
this.log.info(
|
|
1222
|
-
|
|
1223
|
-
const node = await this.commissioningController.connectNode(nodeId, {
|
|
1224
|
-
attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) => this.log.info(`***mattributeChangedCallback ${peerNodeId}: Attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
|
|
1225
|
-
eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) => this.log.info(`***eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
|
|
1226
|
-
stateInformationCallback: (peerNodeId, info) => {
|
|
1227
|
-
switch (info) {
|
|
1228
|
-
case NodeStateInformation.Connected:
|
|
1229
|
-
this.log.info(`***stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
|
|
1230
|
-
break;
|
|
1231
|
-
case NodeStateInformation.Disconnected:
|
|
1232
|
-
this.log.info(`***stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
|
|
1233
|
-
break;
|
|
1234
|
-
case NodeStateInformation.Reconnecting:
|
|
1235
|
-
this.log.info(`***stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
|
|
1236
|
-
break;
|
|
1237
|
-
case NodeStateInformation.WaitingForDeviceDiscovery:
|
|
1238
|
-
this.log.info(`***stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
|
|
1239
|
-
break;
|
|
1240
|
-
case NodeStateInformation.StructureChanged:
|
|
1241
|
-
this.log.info(`***stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
|
|
1242
|
-
break;
|
|
1243
|
-
case NodeStateInformation.Decommissioned:
|
|
1244
|
-
this.log.info(`***stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
|
|
1245
|
-
break;
|
|
1246
|
-
default:
|
|
1247
|
-
this.log.info(`***stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
|
|
1248
|
-
break;
|
|
1249
|
-
}
|
|
1250
|
-
},
|
|
1251
|
-
});
|
|
1252
|
-
node.logStructure();
|
|
1253
|
-
// Get the interaction client
|
|
1254
|
-
const interactionClient = await node.getInteractionClient();
|
|
1255
|
-
let cluster;
|
|
1256
|
-
let attributes;
|
|
1257
|
-
// Log BasicInformationCluster
|
|
1258
|
-
cluster = BasicInformationCluster;
|
|
1259
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1260
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1261
|
-
});
|
|
1262
|
-
this.log.warn(`Cluster: ${cluster.name} attributes:`);
|
|
1263
|
-
attributes.forEach((attribute) => {
|
|
1264
|
-
this.log.info(
|
|
1265
|
-
// eslint-disable-next-line max-len
|
|
1266
|
-
`- endpoint: ${attribute.path.endpointId} clusterId: ${attribute.path.clusterId} id: ${attribute.path.attributeId} name: ${attribute.path.attributeName}: ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`);
|
|
1267
|
-
});
|
|
1268
|
-
// Log PowerSourceCluster
|
|
1269
|
-
cluster = PowerSourceCluster;
|
|
1270
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1271
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1272
|
-
});
|
|
1273
|
-
this.log.warn(`Cluster: ${cluster.name} attributes:`);
|
|
1274
|
-
attributes.forEach((attribute) => {
|
|
1275
|
-
this.log.info(
|
|
1276
|
-
// eslint-disable-next-line max-len
|
|
1277
|
-
`- endpoint: ${attribute.path.endpointId} clusterId: ${attribute.path.clusterId} id: ${attribute.path.attributeId} name: ${attribute.path.attributeName}: ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`);
|
|
1278
|
-
});
|
|
1279
|
-
// Log ThreadNetworkDiagnostics
|
|
1280
|
-
cluster = ThreadNetworkDiagnosticsCluster;
|
|
1281
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1282
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1283
|
-
});
|
|
1284
|
-
this.log.warn(`Cluster: ${cluster.name} attributes:`);
|
|
1285
|
-
attributes.forEach((attribute) => {
|
|
1286
|
-
this.log.info(
|
|
1287
|
-
// eslint-disable-next-line max-len
|
|
1288
|
-
`- endpoint: ${attribute.path.endpointId} clusterId: ${attribute.path.clusterId} id: ${attribute.path.attributeId} name: ${attribute.path.attributeName}: ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`);
|
|
1289
|
-
});
|
|
1319
|
+
this.log.info('***Commissioning controller unpairing node:', nodeId);
|
|
1320
|
+
await this.commissioningController.removeNode(nodeId);
|
|
1290
1321
|
}
|
|
1322
|
+
return;
|
|
1323
|
+
}
|
|
1324
|
+
if (hasParameter('discover')) {
|
|
1325
|
+
//const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
|
|
1326
|
+
//console.log(discover);
|
|
1327
|
+
}
|
|
1328
|
+
if (!this.commissioningController.isCommissioned()) {
|
|
1329
|
+
this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
|
|
1330
|
+
return;
|
|
1331
|
+
}
|
|
1332
|
+
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1333
|
+
this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
|
|
1334
|
+
for (const nodeId of nodeIds) {
|
|
1335
|
+
this.log.info(`***Connecting to commissioned node: ${nodeId}`);
|
|
1336
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1337
|
+
const node = await this.commissioningController.connectNode(nodeId, {
|
|
1338
|
+
attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) => this.log.info(`***Commissioning controller attributeChangedCallback ${peerNodeId}: attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
|
|
1339
|
+
eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) => this.log.info(`***Commissioning controller eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
|
|
1340
|
+
stateInformationCallback: (peerNodeId, info) => {
|
|
1341
|
+
switch (info) {
|
|
1342
|
+
case NodeStateInformation.Connected:
|
|
1343
|
+
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
|
|
1344
|
+
break;
|
|
1345
|
+
case NodeStateInformation.Disconnected:
|
|
1346
|
+
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
|
|
1347
|
+
break;
|
|
1348
|
+
case NodeStateInformation.Reconnecting:
|
|
1349
|
+
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
|
|
1350
|
+
break;
|
|
1351
|
+
case NodeStateInformation.WaitingForDeviceDiscovery:
|
|
1352
|
+
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
|
|
1353
|
+
break;
|
|
1354
|
+
case NodeStateInformation.StructureChanged:
|
|
1355
|
+
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
|
|
1356
|
+
break;
|
|
1357
|
+
case NodeStateInformation.Decommissioned:
|
|
1358
|
+
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
|
|
1359
|
+
break;
|
|
1360
|
+
default:
|
|
1361
|
+
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
|
|
1362
|
+
break;
|
|
1363
|
+
}
|
|
1364
|
+
},
|
|
1365
|
+
});
|
|
1366
|
+
//node.logStructure();
|
|
1367
|
+
// Get the interaction client
|
|
1368
|
+
this.log.info('Getting the interaction client');
|
|
1369
|
+
const interactionClient = await node.getInteractionClient();
|
|
1370
|
+
let cluster;
|
|
1371
|
+
let attributes;
|
|
1372
|
+
// Log BasicInformationCluster
|
|
1373
|
+
cluster = BasicInformationCluster;
|
|
1374
|
+
attributes = await interactionClient.getMultipleAttributes({
|
|
1375
|
+
attributes: [{ clusterId: cluster.id }],
|
|
1376
|
+
});
|
|
1377
|
+
this.log.warn(`Cluster: ${cluster.name} attributes:`);
|
|
1378
|
+
attributes.forEach((attribute) => {
|
|
1379
|
+
this.log.info(
|
|
1380
|
+
// eslint-disable-next-line max-len
|
|
1381
|
+
`- endpoint ${attribute.path.endpointId} cluster ${getClusterNameById(attribute.path.clusterId)} (${attribute.path.clusterId}) attribute ${attribute.path.attributeName} (${attribute.path.attributeId}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`);
|
|
1382
|
+
});
|
|
1383
|
+
// Log PowerSourceCluster
|
|
1384
|
+
cluster = PowerSourceCluster;
|
|
1385
|
+
attributes = await interactionClient.getMultipleAttributes({
|
|
1386
|
+
attributes: [{ clusterId: cluster.id }],
|
|
1387
|
+
});
|
|
1388
|
+
this.log.warn(`Cluster: ${cluster.name} attributes:`);
|
|
1389
|
+
attributes.forEach((attribute) => {
|
|
1390
|
+
this.log.info(
|
|
1391
|
+
// eslint-disable-next-line max-len
|
|
1392
|
+
`- endpoint ${attribute.path.endpointId} cluster ${getClusterNameById(attribute.path.clusterId)} (${attribute.path.clusterId}) attribute ${attribute.path.attributeName} (${attribute.path.attributeId}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`);
|
|
1393
|
+
});
|
|
1394
|
+
// Log ThreadNetworkDiagnostics
|
|
1395
|
+
cluster = ThreadNetworkDiagnosticsCluster;
|
|
1396
|
+
attributes = await interactionClient.getMultipleAttributes({
|
|
1397
|
+
attributes: [{ clusterId: cluster.id }],
|
|
1398
|
+
});
|
|
1399
|
+
this.log.warn(`Cluster: ${cluster.name} attributes:`);
|
|
1400
|
+
attributes.forEach((attribute) => {
|
|
1401
|
+
this.log.info(
|
|
1402
|
+
// eslint-disable-next-line max-len
|
|
1403
|
+
`- endpoint ${attribute.path.endpointId} cluster ${getClusterNameById(attribute.path.clusterId)} (${attribute.path.clusterId}) attribute ${attribute.path.attributeName} (${attribute.path.attributeId}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`);
|
|
1404
|
+
});
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
/**
|
|
1408
|
+
* Starts the Matterbridge based on the bridge mode.
|
|
1409
|
+
* If the bridge mode is 'bridge', it creates a commissioning server, matter aggregator,
|
|
1410
|
+
* and starts the matter server.
|
|
1411
|
+
* If the bridge mode is 'childbridge', it starts the plugins, creates commissioning servers,
|
|
1412
|
+
* and starts the matter server when all plugins are loaded and started.
|
|
1413
|
+
* @private
|
|
1414
|
+
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1415
|
+
*/
|
|
1416
|
+
async startMatterbridge() {
|
|
1417
|
+
if (!this.storageManager) {
|
|
1418
|
+
this.log.error('No storage manager initialized');
|
|
1419
|
+
await this.cleanup('No storage manager initialized');
|
|
1420
|
+
return;
|
|
1421
|
+
}
|
|
1422
|
+
this.log.debug('Starting matterbridge in mode', this.bridgeMode);
|
|
1423
|
+
this.createMatterServer(this.storageManager);
|
|
1424
|
+
if (!this.matterServer) {
|
|
1425
|
+
this.log.error('No matter server initialized');
|
|
1426
|
+
await this.cleanup('No matter server initialized');
|
|
1427
|
+
return;
|
|
1291
1428
|
}
|
|
1292
1429
|
if (this.bridgeMode === 'bridge') {
|
|
1293
1430
|
// Plugins are loaded by loadPlugin on startup and plugin.loaded is set to true
|
|
@@ -1338,6 +1475,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1338
1475
|
await this.startMatterServer();
|
|
1339
1476
|
this.log.info('Matter server started');
|
|
1340
1477
|
await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
|
|
1478
|
+
//if (hasParameter('advertise')) await this.commissioningServer.advertise();
|
|
1341
1479
|
/*
|
|
1342
1480
|
setInterval(() => {
|
|
1343
1481
|
this.matterAggregator?.getBridgedDevices().forEach((device) => {
|
|
@@ -1421,6 +1559,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1421
1559
|
});
|
|
1422
1560
|
if (!allStarted)
|
|
1423
1561
|
return;
|
|
1562
|
+
clearInterval(startMatterInterval);
|
|
1424
1563
|
this.log.info('Starting matter server...');
|
|
1425
1564
|
// Setting reachability to true
|
|
1426
1565
|
this.registeredPlugins.forEach((plugin) => {
|
|
@@ -1457,7 +1596,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1457
1596
|
await this.showCommissioningQRCode(plugin.commissioningServer, plugin.storageContext, plugin.nodeContext, plugin.name);
|
|
1458
1597
|
}
|
|
1459
1598
|
Logger.defaultLogLevel = this.debugEnabled ? Level.DEBUG : Level.INFO;
|
|
1460
|
-
clearInterval(startMatterInterval);
|
|
1599
|
+
//clearInterval(startMatterInterval);
|
|
1461
1600
|
}, 1000);
|
|
1462
1601
|
}
|
|
1463
1602
|
}
|
|
@@ -1531,9 +1670,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
1531
1670
|
await storageContext.set('softwareVersionString', softwareVersionString ?? '1.0.0');
|
|
1532
1671
|
await storageContext.set('hardwareVersion', hardwareVersion ?? 1);
|
|
1533
1672
|
await storageContext.set('hardwareVersionString', hardwareVersionString ?? '1.0.0');
|
|
1534
|
-
this.log.debug(
|
|
1535
|
-
this.log.debug(
|
|
1536
|
-
this.log.debug(
|
|
1673
|
+
this.log.debug(`Created commissioning server storage context for ${plg}${pluginName}${db}`);
|
|
1674
|
+
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
1675
|
+
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1537
1676
|
return storageContext;
|
|
1538
1677
|
}
|
|
1539
1678
|
/**
|
|
@@ -1548,7 +1687,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1548
1687
|
if (!commissioningServer || !storageContext || !pluginName)
|
|
1549
1688
|
return;
|
|
1550
1689
|
if (!commissioningServer.isCommissioned()) {
|
|
1551
|
-
this.log.info(`***The commissioning server for ${plg}${pluginName}${nf} is not commissioned. Pair it scanning the QR code ...`);
|
|
1690
|
+
this.log.info(`***The commissioning server on port ${commissioningServer.getPort()} for ${plg}${pluginName}${nf} is not commissioned. Pair it scanning the QR code ...`);
|
|
1552
1691
|
const { qrPairingCode, manualPairingCode } = commissioningServer.getPairingCode();
|
|
1553
1692
|
storageContext.set('qrPairingCode', qrPairingCode);
|
|
1554
1693
|
storageContext.set('manualPairingCode', manualPairingCode);
|
|
@@ -1567,7 +1706,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1567
1706
|
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
1568
1707
|
}
|
|
1569
1708
|
else {
|
|
1570
|
-
this.log.info(`***The commissioning server for ${plg}${pluginName}${nf} is already commissioned. Waiting for controllers to connect ...`);
|
|
1709
|
+
this.log.info(`***The commissioning server on port ${commissioningServer.getPort()} for ${plg}${pluginName}${nf} is already commissioned . Waiting for controllers to connect ...`);
|
|
1571
1710
|
if (pluginName !== 'Matterbridge') {
|
|
1572
1711
|
const plugin = this.findPlugin(pluginName);
|
|
1573
1712
|
if (plugin) {
|
|
@@ -1617,7 +1756,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1617
1756
|
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with softwareVersion ${softwareVersion} softwareVersionString ${softwareVersionString}`);
|
|
1618
1757
|
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with hardwareVersion ${hardwareVersion} hardwareVersionString ${hardwareVersionString}`);
|
|
1619
1758
|
const commissioningServer = new CommissioningServer({
|
|
1620
|
-
port:
|
|
1759
|
+
port: this.port++,
|
|
1621
1760
|
passcode: undefined,
|
|
1622
1761
|
discriminator: undefined,
|
|
1623
1762
|
deviceName,
|
|
@@ -1704,9 +1843,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1704
1843
|
this.log.warn(`***Commissioning removed from fabric ${fabricIndex} for ${plg}${pluginName}${nf}. Resetting the commissioning server ...`);
|
|
1705
1844
|
await commissioningServer.factoryReset();
|
|
1706
1845
|
if (pluginName === 'Matterbridge') {
|
|
1707
|
-
this.matterbridgeContext?.
|
|
1708
|
-
this.matterbridgeContext?.delete(`${pluginName}.EventHandler`);
|
|
1709
|
-
this.matterbridgeContext?.delete(`${pluginName}.SessionManager`);
|
|
1846
|
+
await this.matterbridgeContext?.clearAll();
|
|
1710
1847
|
}
|
|
1711
1848
|
else {
|
|
1712
1849
|
for (const plugin of this.registeredPlugins) {
|
|
@@ -1714,9 +1851,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1714
1851
|
await plugin.platform?.onShutdown('Commissioning removed by the controller');
|
|
1715
1852
|
plugin.paired = false;
|
|
1716
1853
|
plugin.connected = false;
|
|
1717
|
-
plugin.storageContext?.
|
|
1718
|
-
plugin.storageContext?.delete(`${pluginName}.EventHandler`);
|
|
1719
|
-
plugin.storageContext?.delete(`${pluginName}.SessionManager`);
|
|
1854
|
+
await plugin.storageContext?.clearAll();
|
|
1720
1855
|
}
|
|
1721
1856
|
}
|
|
1722
1857
|
}
|
|
@@ -2038,6 +2173,26 @@ export class Matterbridge extends EventEmitter {
|
|
|
2038
2173
|
this.expressApp = express();
|
|
2039
2174
|
// Serve React build directory
|
|
2040
2175
|
this.expressApp.use(express.static(path.join(this.rootDirectory, 'frontend/build')));
|
|
2176
|
+
// Endpoint to provide login code
|
|
2177
|
+
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
2178
|
+
const { password } = req.body;
|
|
2179
|
+
this.log.debug('The frontend sent /api/login', password);
|
|
2180
|
+
if (!this.nodeContext) {
|
|
2181
|
+
this.log.error('/api/login nodeContext not found');
|
|
2182
|
+
res.json({ valid: false });
|
|
2183
|
+
return;
|
|
2184
|
+
}
|
|
2185
|
+
try {
|
|
2186
|
+
const storedPassword = await this.nodeContext.get('password', '');
|
|
2187
|
+
if (storedPassword === '' || password === storedPassword)
|
|
2188
|
+
res.json({ valid: true });
|
|
2189
|
+
else
|
|
2190
|
+
res.json({ valid: false });
|
|
2191
|
+
}
|
|
2192
|
+
catch (error) {
|
|
2193
|
+
res.json({ valid: false });
|
|
2194
|
+
}
|
|
2195
|
+
});
|
|
2041
2196
|
// Endpoint to provide QR pairing code
|
|
2042
2197
|
this.expressApp.get('/api/qr-code', (req, res) => {
|
|
2043
2198
|
this.log.debug('The frontend sent /api/qr-code');
|
|
@@ -2152,7 +2307,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2152
2307
|
res.status(400).json({ error: 'No command provided' });
|
|
2153
2308
|
return;
|
|
2154
2309
|
}
|
|
2155
|
-
this.log.
|
|
2310
|
+
this.log.debug(`*Received frontend command: ${command}:${param}`);
|
|
2311
|
+
// Handle the command setpassword from Settings
|
|
2312
|
+
if (command === 'setpassword') {
|
|
2313
|
+
const password = param.slice(1, -1); // Remove the first and last characters
|
|
2314
|
+
this.log.info('setpassword', param, password);
|
|
2315
|
+
await this.nodeContext?.set('password', password);
|
|
2316
|
+
}
|
|
2156
2317
|
// Handle the command debugLevel from Settings
|
|
2157
2318
|
if (command === 'setloglevel') {
|
|
2158
2319
|
if (param === 'Debug') {
|
|
@@ -2174,15 +2335,47 @@ export class Matterbridge extends EventEmitter {
|
|
|
2174
2335
|
plugin.platform?.log.setLogDebug(this.debugEnabled);
|
|
2175
2336
|
});
|
|
2176
2337
|
}
|
|
2177
|
-
// Handle the command
|
|
2338
|
+
// Handle the command shutdown from Header
|
|
2339
|
+
if (command === 'shutdown') {
|
|
2340
|
+
this.shutdownProcess();
|
|
2341
|
+
}
|
|
2342
|
+
// Handle the command restart from Header
|
|
2178
2343
|
if (command === 'restart') {
|
|
2179
2344
|
this.restartProcess();
|
|
2180
2345
|
}
|
|
2181
2346
|
// Handle the command update from Header
|
|
2347
|
+
if (command === 'update') {
|
|
2348
|
+
this.log.warn(`***Updating matterbridge ${plg}${param}${db}`);
|
|
2349
|
+
try {
|
|
2350
|
+
await this.spawnCommand('npm', ['install', '-g', 'matterbridge']);
|
|
2351
|
+
this.log.info('Matterbridge has been updated. Full restart required.');
|
|
2352
|
+
}
|
|
2353
|
+
catch (error) {
|
|
2354
|
+
this.log.error('Error updating matterbridge');
|
|
2355
|
+
res.json({ message: 'Command received' });
|
|
2356
|
+
return;
|
|
2357
|
+
}
|
|
2358
|
+
this.updateProcess();
|
|
2359
|
+
}
|
|
2360
|
+
// Handle the command update from Header
|
|
2182
2361
|
if (command === 'update') {
|
|
2183
2362
|
this.log.warn(`The /api/command/${command} is not yet implemented`);
|
|
2184
2363
|
}
|
|
2185
|
-
// Handle the command
|
|
2364
|
+
// Handle the command installplugin from Home
|
|
2365
|
+
if (command === 'installplugin') {
|
|
2366
|
+
param = param.replace(/\*/g, '\\');
|
|
2367
|
+
this.log.warn(`***Installing plugin ${plg}${param}${db}`);
|
|
2368
|
+
try {
|
|
2369
|
+
await this.spawnCommand('npm', ['install', '-g', param]);
|
|
2370
|
+
this.log.info(`Plugin ${plg}${param}${nf} installed. Restart required.`);
|
|
2371
|
+
}
|
|
2372
|
+
catch (error) {
|
|
2373
|
+
this.log.error(`Error installing plugin ${plg}${param}${er}`);
|
|
2374
|
+
res.json({ message: 'Command received' });
|
|
2375
|
+
return;
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
// Handle the command addplugin from Home
|
|
2186
2379
|
if (command === 'addplugin') {
|
|
2187
2380
|
param = param.replace(/\*/g, '\\');
|
|
2188
2381
|
if (this.registeredPlugins.find((plugin) => plugin.name === param)) {
|
|
@@ -2297,7 +2490,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2297
2490
|
});
|
|
2298
2491
|
// Fallback for routing
|
|
2299
2492
|
this.expressApp.get('*', (req, res) => {
|
|
2300
|
-
this.log.
|
|
2493
|
+
this.log.debug('The frontend sent *', req.url);
|
|
2301
2494
|
res.sendFile(path.join(this.rootDirectory, 'frontend/build/index.html'));
|
|
2302
2495
|
});
|
|
2303
2496
|
this.expressServer = this.expressApp.listen(port, () => {
|