matterbridge 1.2.3 → 1.2.5
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 +189 -171
- package/README.md +379 -297
- package/TODO.md +7 -0
- package/dist/AirQualityCluster.d.ts +134 -104
- package/dist/AirQualityCluster.d.ts.map +1 -1
- package/dist/AirQualityCluster.js +43 -26
- package/dist/AirQualityCluster.js.map +1 -1
- package/dist/EveHistoryCluster.d.ts +446 -0
- package/dist/EveHistoryCluster.d.ts.map +1 -0
- package/dist/EveHistoryCluster.js +170 -0
- package/dist/EveHistoryCluster.js.map +1 -0
- package/dist/TvocCluster.d.ts +364 -148
- package/dist/TvocCluster.d.ts.map +1 -1
- package/dist/TvocCluster.js +115 -32
- 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 +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/matterbridge.d.ts +9 -1
- package/dist/matterbridge.d.ts.map +1 -1
- package/dist/matterbridge.js +338 -150
- 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 +1254 -9
- package/dist/matterbridgeDevice.d.ts.map +1 -1
- package/dist/matterbridgeDevice.js +372 -91
- package/dist/matterbridgeDevice.js.map +1 -1
- package/frontend/build/asset-manifest.json +3 -3
- 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.map +1 -1
- package/frontend/build/static/js/main.6b861489.js +3 -0
- package/frontend/build/static/js/main.6b861489.js.map +1 -0
- package/matterbridge.service +18 -0
- package/package.json +88 -87
- 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/frontend/build/static/js/{main.e3553a4d.js.LICENSE.txt → main.6b861489.js.LICENSE.txt} +0 -0
package/dist/matterbridge.js
CHANGED
|
@@ -31,7 +31,7 @@ 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, BridgedDeviceBasicInformationCluster, ClusterServer, GeneralCommissioning, PowerSourceCluster, ThreadNetworkDiagnosticsCluster
|
|
34
|
+
import { BasicInformationCluster, BooleanStateCluster, BridgedDeviceBasicInformationCluster, ClusterServer, GeneralCommissioning, PowerSourceCluster, ThreadNetworkDiagnosticsCluster } from '@project-chip/matter-node.js/cluster';
|
|
35
35
|
import { DeviceTypeId, VendorId } from '@project-chip/matter-node.js/datatype';
|
|
36
36
|
import { Aggregator, DeviceTypes, NodeStateInformation } from '@project-chip/matter-node.js/device';
|
|
37
37
|
import { Format, Level, Logger } from '@project-chip/matter-node.js/log';
|
|
@@ -222,6 +222,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
222
222
|
/*
|
|
223
223
|
npm > npm.cmd on windows
|
|
224
224
|
*/
|
|
225
|
+
if (process.platform === 'win32' && command === 'npm') {
|
|
226
|
+
command = command + '.cmd';
|
|
227
|
+
}
|
|
225
228
|
return new Promise((resolve, reject) => {
|
|
226
229
|
const childProcess = spawn(command, args, {
|
|
227
230
|
stdio: 'inherit',
|
|
@@ -297,7 +300,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
297
300
|
// Start the storage (we need it now for frontend and later for matterbridge)
|
|
298
301
|
await this.startStorage('json', path.join(this.matterbridgeDirectory, 'matterbridge.json'));
|
|
299
302
|
this.log.debug(`Creating commissioning server context for ${plg}Matterbridge${db}`);
|
|
300
|
-
this.matterbridgeContext = this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge aggregator');
|
|
303
|
+
this.matterbridgeContext = await this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge aggregator');
|
|
301
304
|
// Initialize frontend
|
|
302
305
|
await this.initializeFrontend(getIntParameter('frontend'));
|
|
303
306
|
if (hasParameter('test')) {
|
|
@@ -310,7 +313,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
310
313
|
this.bridgeMode = 'controller';
|
|
311
314
|
this.log.info('Creating mattercontrollerContext: mattercontrollerContext');
|
|
312
315
|
this.mattercontrollerContext = this.storageManager?.createContext('mattercontrollerContext');
|
|
313
|
-
await this.
|
|
316
|
+
await this.startMatterbridge();
|
|
314
317
|
return;
|
|
315
318
|
}
|
|
316
319
|
if (hasParameter('bridge')) {
|
|
@@ -329,7 +332,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
329
332
|
plugin.manualPairingCode = undefined;
|
|
330
333
|
this.loadPlugin(plugin); // No await do it asyncronously
|
|
331
334
|
}
|
|
332
|
-
await this.
|
|
335
|
+
await this.startMatterbridge();
|
|
333
336
|
return;
|
|
334
337
|
}
|
|
335
338
|
if (hasParameter('childbridge')) {
|
|
@@ -348,7 +351,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
348
351
|
plugin.manualPairingCode = (await plugin.nodeContext?.get('manualPairingCode', undefined)) ?? undefined;
|
|
349
352
|
this.loadPlugin(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
|
|
350
353
|
}
|
|
351
|
-
await this.
|
|
354
|
+
await this.startMatterbridge();
|
|
352
355
|
return;
|
|
353
356
|
}
|
|
354
357
|
}
|
|
@@ -478,6 +481,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
478
481
|
await this.cleanup('SIGTERM received, cleaning up...');
|
|
479
482
|
});
|
|
480
483
|
}
|
|
484
|
+
/**
|
|
485
|
+
* Update matterbridge.
|
|
486
|
+
*/
|
|
487
|
+
async updateProcess() {
|
|
488
|
+
await this.cleanup('updating...', false);
|
|
489
|
+
this.hasCleanupStarted = false;
|
|
490
|
+
}
|
|
481
491
|
/**
|
|
482
492
|
* Restarts the process by spawning a new process and exiting the current process.
|
|
483
493
|
*/
|
|
@@ -485,6 +495,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
485
495
|
await this.cleanup('restarting...', true);
|
|
486
496
|
this.hasCleanupStarted = false;
|
|
487
497
|
}
|
|
498
|
+
/**
|
|
499
|
+
* Shut down the process by exiting the current process.
|
|
500
|
+
*/
|
|
501
|
+
async shutdownProcess() {
|
|
502
|
+
await this.cleanup('shutting down...', false);
|
|
503
|
+
this.hasCleanupStarted = false;
|
|
504
|
+
}
|
|
488
505
|
/**
|
|
489
506
|
* Cleans up the Matterbridge instance.
|
|
490
507
|
* @param message - The cleanup message.
|
|
@@ -536,7 +553,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
536
553
|
this.expressApp.removeAllListeners();
|
|
537
554
|
this.expressApp = undefined;
|
|
538
555
|
}
|
|
539
|
-
const cleanupTimeout1
|
|
556
|
+
/*const cleanupTimeout1 =*/ setTimeout(async () => {
|
|
540
557
|
// Closing matter
|
|
541
558
|
await this.stopMatter();
|
|
542
559
|
// Closing storage
|
|
@@ -564,21 +581,28 @@ export class Matterbridge extends EventEmitter {
|
|
|
564
581
|
this.registeredPlugins = [];
|
|
565
582
|
this.registeredDevices = [];
|
|
566
583
|
this.log.info('Waiting for matter to deliver last messages...');
|
|
567
|
-
const cleanupTimeout2
|
|
584
|
+
/*const cleanupTimeout2 =*/ setTimeout(async () => {
|
|
568
585
|
if (restart) {
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
586
|
+
if (message === 'updating...') {
|
|
587
|
+
this.log.info('Cleanup completed. Updating...');
|
|
588
|
+
Matterbridge.instance = undefined;
|
|
589
|
+
this.emit('update');
|
|
590
|
+
}
|
|
591
|
+
else if (message === 'restarting...') {
|
|
592
|
+
this.log.info('Cleanup completed. Restarting...');
|
|
593
|
+
Matterbridge.instance = undefined;
|
|
594
|
+
this.emit('restart');
|
|
595
|
+
}
|
|
572
596
|
}
|
|
573
597
|
else {
|
|
574
598
|
this.log.info('Cleanup completed. Shutting down...');
|
|
575
599
|
Matterbridge.instance = undefined;
|
|
576
600
|
this.emit('shutdown');
|
|
577
601
|
}
|
|
578
|
-
},
|
|
579
|
-
cleanupTimeout2.unref();
|
|
602
|
+
}, 1 * 1000);
|
|
603
|
+
//cleanupTimeout2.unref();
|
|
580
604
|
}, 3 * 1000);
|
|
581
|
-
cleanupTimeout1.unref();
|
|
605
|
+
//cleanupTimeout1.unref();
|
|
582
606
|
}
|
|
583
607
|
}
|
|
584
608
|
/**
|
|
@@ -825,50 +849,98 @@ export class Matterbridge extends EventEmitter {
|
|
|
825
849
|
this.mattercontrollerContext = undefined;
|
|
826
850
|
}
|
|
827
851
|
async testStartMatterBridge() {
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
this.
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
plugin.started = false;
|
|
835
|
-
plugin.configured = false;
|
|
836
|
-
plugin.connected = undefined;
|
|
837
|
-
this.log.error(`****Starting registeredPlugin ${plugin.name}`);
|
|
838
|
-
this.loadPlugin(plugin, true, 'Matterbridge is starting');
|
|
839
|
-
});
|
|
840
|
-
this.log.error('****Stop forEach registeredPlugin');
|
|
841
|
-
*/
|
|
842
|
-
/*
|
|
843
|
-
for (const plugin of this.registeredPlugins) {
|
|
844
|
-
if (!plugin.enabled) continue;
|
|
845
|
-
// No await do it asyncronously
|
|
846
|
-
this.loadPlugin(plugin)
|
|
847
|
-
.then(() => {
|
|
848
|
-
// No await do it asyncronously
|
|
849
|
-
this.startPlugin(plugin)
|
|
850
|
-
.then(() => {})
|
|
851
|
-
.catch((err) => {
|
|
852
|
-
this.log.error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`);
|
|
853
|
-
});
|
|
854
|
-
})
|
|
855
|
-
.catch((err) => {
|
|
856
|
-
this.log.error(`Failed to load plugin ${plg}${plugin.name}${er}: ${err}`);
|
|
857
|
-
});
|
|
852
|
+
// Plugins are loaded by loadPlugin on startup and plugin.loaded is set to true
|
|
853
|
+
// Plugins are started and configured by callback when Matterbridge is commissioned
|
|
854
|
+
if (!this.storageManager) {
|
|
855
|
+
this.log.error('No storage manager initialized');
|
|
856
|
+
await this.cleanup('No storage manager initialized');
|
|
857
|
+
return;
|
|
858
858
|
}
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
this.log.info(`Waiting ${times} secs for plugin ${plg}${plugin.name}${db} to load (${plugin.loaded}) and start (${plugin.started}) and send devices ...`);
|
|
866
|
-
if (!plugin.loaded || !plugin.started) return;
|
|
867
|
-
this.log.info(`Plugin ${plg}${plugin.name}${db} sent ${plugin.registeredDevices} devices`);
|
|
868
|
-
clearInterval(interval);
|
|
869
|
-
}, 1000);
|
|
859
|
+
this.log.debug('Starting matterbridge in mode', this.bridgeMode);
|
|
860
|
+
this.createMatterServer(this.storageManager);
|
|
861
|
+
if (!this.matterServer) {
|
|
862
|
+
this.log.error('No matter server initialized');
|
|
863
|
+
await this.cleanup('No matter server initialized');
|
|
864
|
+
return;
|
|
870
865
|
}
|
|
871
|
-
|
|
866
|
+
this.log.debug('***Starting startMatterbridge interval for Matterbridge');
|
|
867
|
+
let failCount = 0;
|
|
868
|
+
const startInterval = setInterval(async () => {
|
|
869
|
+
for (const plugin of this.registeredPlugins) {
|
|
870
|
+
if (!plugin.enabled)
|
|
871
|
+
continue;
|
|
872
|
+
if (!plugin.loaded) {
|
|
873
|
+
this.log.info(`***Waiting (failSafeCount=${failCount}/30) in startMatterbridge interval for plugin ${plg}${plugin.name}${db} loaded: ${plugin.loaded}...`);
|
|
874
|
+
failCount++;
|
|
875
|
+
if (failCount > 30) {
|
|
876
|
+
this.log.error(`***Failed to load plugin ${plg}${plugin.name}${er}`);
|
|
877
|
+
plugin.error = true;
|
|
878
|
+
}
|
|
879
|
+
else {
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
clearInterval(startInterval);
|
|
885
|
+
this.log.debug('***Cleared startMatterbridge interval for Matterbridge');
|
|
886
|
+
this.log.debug(`Creating commissioning server context for ${plg}Matterbridge${db}`);
|
|
887
|
+
this.matterbridgeContext = await this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Aggregator');
|
|
888
|
+
if (!this.matterbridgeContext) {
|
|
889
|
+
this.log.error(`Error creating storage context for ${plg}Matterbridge${er}`);
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
if (!this.nodeContext) {
|
|
893
|
+
this.log.error(`Node storage context undefined for ${plg}Matterbridge${er}`);
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
await this.matterbridgeContext.set('softwareVersion', 1);
|
|
897
|
+
await this.matterbridgeContext.set('softwareVersionString', this.matterbridgeVersion);
|
|
898
|
+
this.log.debug(`Creating commissioning server for ${plg}Matterbridge${db}`);
|
|
899
|
+
this.commissioningServer = await this.createCommisioningServer(this.matterbridgeContext, 'Matterbridge');
|
|
900
|
+
this.log.debug(`Creating matter aggregator for ${plg}Matterbridge${db}`);
|
|
901
|
+
this.matterAggregator = await this.createMatterAggregator(this.matterbridgeContext);
|
|
902
|
+
this.log.debug('Adding matterbridge aggregator to commissioning server');
|
|
903
|
+
this.commissioningServer.addDevice(this.matterAggregator);
|
|
904
|
+
const device = new MatterbridgeDevice(DeviceTypes.CONTACT_SENSOR);
|
|
905
|
+
device.createDefaultIdentifyClusterServer();
|
|
906
|
+
//device.createDefaultBasicInformationClusterServer('Boolean test', '0x89930475', 0x8000, 'Matterbridge', 77, 'Boolean');
|
|
907
|
+
device.createDefaultBridgedDeviceBasicInformationClusterServer('Boolean test', '0x89930475', 0x8000, 'Matterbridge', 'Boolean');
|
|
908
|
+
device.createDefaultBooleanStateClusterServer(true);
|
|
909
|
+
device.createDefaultPowerSourceReplaceableBatteryClusterServer(75);
|
|
910
|
+
device.createDefaultPowerSourceConfigurationClusterServer(1);
|
|
911
|
+
//this.commissioningServer.addDevice(device);
|
|
912
|
+
//this.matterAggregator.addBridgedDevice(device);
|
|
913
|
+
this.log.debug('Adding matterbridge commissioning server to matter server');
|
|
914
|
+
await this.matterServer?.addCommissioningServer(this.commissioningServer, { uniqueStorageKey: 'Matterbridge' });
|
|
915
|
+
this.log.debug(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
916
|
+
this.commissioningServer.setReachability(true);
|
|
917
|
+
this.log.debug('Starting matter server...');
|
|
918
|
+
await this.startMatterServer();
|
|
919
|
+
this.log.info('Matter server started');
|
|
920
|
+
await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
|
|
921
|
+
//2024-03-31 21:54:12.065 DEBUG InteractionServer Received subscribe request from udp://fe80::14b6:bb16:5d3a:1293%9:51251 on session secure/48170 (keepSubscriptions=true, isFabricFiltered=false)
|
|
922
|
+
//2024-03-31 21:54:12.066 DEBUG InteractionServer Subscribe to attributes:*/*/*, events:!*/*/*
|
|
923
|
+
/*
|
|
924
|
+
2024-03-31 21:52:22.669 DEBUG SubscriptionHandler Sending subscription update message for ID 2529153003 with 1 attributes and 1 events
|
|
925
|
+
2024-03-31 21:52:22.669 DEBUG MessageExchange New exchange protocol: 1 id: 30278 session: secure/24489 peerSessionId: 2131 active threshold ms: 4000 active interval ms: 300 idle interval ms: 300 retries: 5
|
|
926
|
+
2024-03-31 21:52:22.669 DEBUG SubscriptionHandler Sending subscription changes for ID 2529153003: MA-contactsensor(0x3b)/BooleanState(0x45)/stateValue(0x0)=true (1805472651)
|
|
927
|
+
2024-03-31 21:52:22.670 DEBUG InteractionMessenger Sending DataReport chunk with 1 attributes and 1 events: 85 bytes
|
|
928
|
+
*/
|
|
929
|
+
setTimeout(() => {
|
|
930
|
+
this.matterAggregator?.addBridgedDevice(device);
|
|
931
|
+
this.log.info('Added device to aggregator');
|
|
932
|
+
}, 30 * 1000);
|
|
933
|
+
setInterval(() => {
|
|
934
|
+
const cluster = device.getClusterServer(BooleanStateCluster);
|
|
935
|
+
if (!cluster)
|
|
936
|
+
return;
|
|
937
|
+
const contact = cluster.getStateValueAttribute();
|
|
938
|
+
cluster.setStateValueAttribute(!contact);
|
|
939
|
+
if (cluster.isEventSupportedByName('stateChange'))
|
|
940
|
+
cluster.triggerStateChangeEvent({ stateValue: !contact });
|
|
941
|
+
this.log.info('Set attribute and event for BooleanStateCluster to', !contact);
|
|
942
|
+
}, 60 * 1000);
|
|
943
|
+
}, 1000);
|
|
872
944
|
}
|
|
873
945
|
/**
|
|
874
946
|
* Loads the configuration for a plugin.
|
|
@@ -884,8 +956,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
884
956
|
try {
|
|
885
957
|
await fs.access(configFile);
|
|
886
958
|
const data = await fs.readFile(configFile, 'utf8');
|
|
887
|
-
|
|
888
|
-
|
|
959
|
+
const config = JSON.parse(data);
|
|
960
|
+
this.log.debug(`Config file found: ${configFile}.\nConfig:${rs}\n`, config);
|
|
961
|
+
/* The first time a plugin is added to the system, the config file is created with the plugin name and type "".*/
|
|
962
|
+
config.name = plugin.name;
|
|
963
|
+
config.type = plugin.type;
|
|
964
|
+
return config;
|
|
889
965
|
}
|
|
890
966
|
catch (err) {
|
|
891
967
|
if (err instanceof Error) {
|
|
@@ -1071,12 +1147,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
1071
1147
|
}
|
|
1072
1148
|
else {
|
|
1073
1149
|
this.log.error(`Plugin ${plg}${plugin.name}${er} does not provide a default export`);
|
|
1074
|
-
return
|
|
1150
|
+
return;
|
|
1151
|
+
//return Promise.reject(new Error(`Plugin ${plg}${plugin.name}${er} does not provide a default export`));
|
|
1075
1152
|
}
|
|
1076
1153
|
}
|
|
1077
1154
|
catch (err) {
|
|
1078
1155
|
this.log.error(`Failed to load plugin ${plg}${plugin.name}${er}: ${err}`);
|
|
1079
|
-
return
|
|
1156
|
+
return;
|
|
1157
|
+
//return Promise.reject(new Error(`Failed to load plugin ${plg}${plugin.name}${er}: ${err}`));
|
|
1080
1158
|
}
|
|
1081
1159
|
}
|
|
1082
1160
|
/**
|
|
@@ -1088,7 +1166,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1088
1166
|
* @private
|
|
1089
1167
|
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1090
1168
|
*/
|
|
1091
|
-
async
|
|
1169
|
+
async startMatterbridge() {
|
|
1092
1170
|
if (!this.storageManager) {
|
|
1093
1171
|
this.log.error('No storage manager initialized');
|
|
1094
1172
|
await this.cleanup('No storage manager initialized');
|
|
@@ -1129,7 +1207,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1129
1207
|
this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
|
|
1130
1208
|
}
|
|
1131
1209
|
else {
|
|
1132
|
-
longDiscriminator = this.mattercontrollerContext.get('longDiscriminator', 3840);
|
|
1210
|
+
longDiscriminator = await this.mattercontrollerContext.get('longDiscriminator', 3840);
|
|
1133
1211
|
if (longDiscriminator > 4095)
|
|
1134
1212
|
throw new Error('Discriminator value must be less than 4096');
|
|
1135
1213
|
setupPin = this.mattercontrollerContext.get('pin', 20202021);
|
|
@@ -1238,30 +1316,62 @@ export class Matterbridge extends EventEmitter {
|
|
|
1238
1316
|
if (this.bridgeMode === 'bridge') {
|
|
1239
1317
|
// Plugins are loaded by loadPlugin on startup and plugin.loaded is set to true
|
|
1240
1318
|
// Plugins are started and configured by callback when Matterbridge is commissioned
|
|
1241
|
-
this.log.debug(
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1319
|
+
this.log.debug('***Starting startMatterbridge interval for Matterbridge');
|
|
1320
|
+
let failCount = 0;
|
|
1321
|
+
const startInterval = setInterval(async () => {
|
|
1322
|
+
for (const plugin of this.registeredPlugins) {
|
|
1323
|
+
if (!plugin.enabled)
|
|
1324
|
+
continue;
|
|
1325
|
+
if (!plugin.loaded) {
|
|
1326
|
+
this.log.info(`***Waiting (failSafeCount=${failCount}/30) in startMatterbridge interval for plugin ${plg}${plugin.name}${db} loaded: ${plugin.loaded}...`);
|
|
1327
|
+
failCount++;
|
|
1328
|
+
if (failCount > 30) {
|
|
1329
|
+
this.log.error(`***Failed to load plugin ${plg}${plugin.name}${er}`);
|
|
1330
|
+
plugin.error = true;
|
|
1331
|
+
}
|
|
1332
|
+
else {
|
|
1333
|
+
return;
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
clearInterval(startInterval);
|
|
1338
|
+
this.log.debug('***Cleared startMatterbridge interval for Matterbridge');
|
|
1339
|
+
this.log.debug(`Creating commissioning server context for ${plg}Matterbridge${db}`);
|
|
1340
|
+
this.matterbridgeContext = await this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Aggregator');
|
|
1341
|
+
if (!this.matterbridgeContext) {
|
|
1342
|
+
this.log.error(`Error creating storage context for ${plg}Matterbridge${er}`);
|
|
1343
|
+
return;
|
|
1344
|
+
}
|
|
1345
|
+
if (!this.nodeContext) {
|
|
1346
|
+
this.log.error(`Node storage context undefined for ${plg}Matterbridge${er}`);
|
|
1347
|
+
return;
|
|
1348
|
+
}
|
|
1349
|
+
await this.matterbridgeContext.set('softwareVersion', 1);
|
|
1350
|
+
await this.matterbridgeContext.set('softwareVersionString', this.matterbridgeVersion);
|
|
1351
|
+
this.log.debug(`Creating commissioning server for ${plg}Matterbridge${db}`);
|
|
1352
|
+
this.commissioningServer = await this.createCommisioningServer(this.matterbridgeContext, 'Matterbridge');
|
|
1353
|
+
this.log.debug(`Creating matter aggregator for ${plg}Matterbridge${db}`);
|
|
1354
|
+
this.matterAggregator = await this.createMatterAggregator(this.matterbridgeContext);
|
|
1355
|
+
this.log.debug('Adding matterbridge aggregator to commissioning server');
|
|
1356
|
+
this.commissioningServer.addDevice(this.matterAggregator);
|
|
1357
|
+
this.log.debug('Adding matterbridge commissioning server to matter server');
|
|
1358
|
+
await this.matterServer?.addCommissioningServer(this.commissioningServer, { uniqueStorageKey: 'Matterbridge' });
|
|
1359
|
+
this.log.debug(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1360
|
+
this.commissioningServer.setReachability(true);
|
|
1361
|
+
this.log.debug('Starting matter server...');
|
|
1362
|
+
await this.startMatterServer();
|
|
1363
|
+
this.log.info('Matter server started');
|
|
1364
|
+
await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
|
|
1365
|
+
/*
|
|
1366
|
+
setInterval(() => {
|
|
1367
|
+
this.matterAggregator?.getBridgedDevices().forEach((device) => {
|
|
1368
|
+
this.log.info(`Bridged device: ${dev}${device.name}${nf}`);
|
|
1369
|
+
device.getClusterServer(BridgedDeviceBasicInformationCluster)?.setReachableAttribute(true);
|
|
1370
|
+
device.getClusterServer(BridgedDeviceBasicInformationCluster)?.triggerReachableChangedEvent({ reachableNewValue: true });
|
|
1371
|
+
});
|
|
1372
|
+
}, 30 * 1000);
|
|
1373
|
+
*/
|
|
1374
|
+
}, 1000);
|
|
1265
1375
|
}
|
|
1266
1376
|
if (this.bridgeMode === 'childbridge') {
|
|
1267
1377
|
// Plugins are loaded and started by loadPlugin on startup
|
|
@@ -1272,45 +1382,50 @@ export class Matterbridge extends EventEmitter {
|
|
|
1272
1382
|
if (!plugin.enabled)
|
|
1273
1383
|
return;
|
|
1274
1384
|
// Start the interval to check if the plugins is started
|
|
1275
|
-
|
|
1276
|
-
|
|
1385
|
+
this.log.debug(`*Starting startMatterbridge interval for plugin ${plg}${plugin.name}${db} loaded: ${plugin.loaded} started: ${plugin.started}...`);
|
|
1386
|
+
let failCount = 0;
|
|
1277
1387
|
const startInterval = setInterval(async () => {
|
|
1278
|
-
if (!
|
|
1279
|
-
this.log.
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1388
|
+
if (!plugin.loaded || !plugin.started /* || !plugin.configured*/) {
|
|
1389
|
+
this.log.info(`***Waiting (failSafeCount=${failCount}/30) in startMatterbridge interval for plugin ${plg}${plugin.name}${db} loaded: ${plugin.loaded} started: ${plugin.started}...`);
|
|
1390
|
+
failCount++;
|
|
1391
|
+
if (failCount > 30) {
|
|
1392
|
+
this.log.error(`***Failed to load plugin ${plg}${plugin.name}${er}`);
|
|
1393
|
+
plugin.error = true;
|
|
1394
|
+
clearInterval(startInterval);
|
|
1395
|
+
}
|
|
1285
1396
|
return;
|
|
1286
1397
|
}
|
|
1287
1398
|
if (plugin.type === 'AccessoryPlatform') {
|
|
1288
|
-
this.registeredDevices
|
|
1289
|
-
|
|
1290
|
-
|
|
1399
|
+
for (const registeredDevice of this.registeredDevices) {
|
|
1400
|
+
if (registeredDevice.plugin !== plugin.name)
|
|
1401
|
+
continue;
|
|
1291
1402
|
if (!plugin.storageContext)
|
|
1292
|
-
plugin.storageContext = this.importCommissioningServerContext(plugin.name, registeredDevice.device);
|
|
1403
|
+
plugin.storageContext = await this.importCommissioningServerContext(plugin.name, registeredDevice.device);
|
|
1404
|
+
await plugin.storageContext.set('softwareVersion', 1);
|
|
1405
|
+
await plugin.storageContext.set('softwareVersionString', this.matterbridgeVersion);
|
|
1293
1406
|
if (!plugin.commissioningServer)
|
|
1294
|
-
plugin.commissioningServer = this.createCommisioningServer(plugin.storageContext, plugin.name);
|
|
1407
|
+
plugin.commissioningServer = await this.createCommisioningServer(plugin.storageContext, plugin.name);
|
|
1295
1408
|
this.log.debug(`Adding device ${dev}${registeredDevice.device.name}${db} to commissioning server for plugin ${plg}${plugin.name}${db}`);
|
|
1296
1409
|
plugin.commissioningServer.addDevice(registeredDevice.device);
|
|
1297
|
-
}
|
|
1410
|
+
}
|
|
1298
1411
|
this.log.debug(`Adding commissioning server to matter server for plugin ${plg}${plugin.name}${db}`);
|
|
1299
|
-
await this.matterServer
|
|
1412
|
+
await this.matterServer?.addCommissioningServer(plugin.commissioningServer, { uniqueStorageKey: plugin.name });
|
|
1300
1413
|
}
|
|
1301
1414
|
if (plugin.type === 'DynamicPlatform') {
|
|
1302
|
-
|
|
1303
|
-
plugin.storageContext.
|
|
1304
|
-
plugin.storageContext.set('
|
|
1305
|
-
plugin.
|
|
1415
|
+
// eslint-disable-next-line max-len
|
|
1416
|
+
plugin.storageContext = await this.createCommissioningServerContext(plugin.name, 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Dynamic Platform');
|
|
1417
|
+
await plugin.storageContext.set('softwareVersion', 1);
|
|
1418
|
+
await plugin.storageContext.set('softwareVersionString', this.matterbridgeVersion);
|
|
1419
|
+
plugin.commissioningServer = await this.createCommisioningServer(plugin.storageContext, plugin.name);
|
|
1306
1420
|
this.log.debug(`Creating aggregator for plugin ${plg}${plugin.name}${db}`);
|
|
1307
|
-
plugin.aggregator = this.createMatterAggregator(plugin.storageContext); // Generate serialNumber and uniqueId
|
|
1421
|
+
plugin.aggregator = await this.createMatterAggregator(plugin.storageContext); // Generate serialNumber and uniqueId
|
|
1308
1422
|
this.log.debug(`Adding matter aggregator to commissioning server for plugin ${plg}${plugin.name}${db}`);
|
|
1309
1423
|
plugin.commissioningServer.addDevice(plugin.aggregator);
|
|
1310
1424
|
this.log.debug(`Adding commissioning server to matter server for plugin ${plg}${plugin.name}${db}`);
|
|
1311
|
-
await this.matterServer
|
|
1425
|
+
await this.matterServer?.addCommissioningServer(plugin.commissioningServer, { uniqueStorageKey: plugin.name });
|
|
1312
1426
|
}
|
|
1313
1427
|
clearInterval(startInterval);
|
|
1428
|
+
this.log.debug(`*Cleared startMatterbridge interval for plugin ${plg}${plugin.name}${db} loaded: ${plugin.loaded} started: ${plugin.started}...`);
|
|
1314
1429
|
}, 1000);
|
|
1315
1430
|
});
|
|
1316
1431
|
// Start the interval to check if all plugins are loaded and started and so start the matter server
|
|
@@ -1321,10 +1436,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1321
1436
|
this.registeredPlugins.forEach((plugin) => {
|
|
1322
1437
|
if (!plugin.enabled)
|
|
1323
1438
|
return;
|
|
1439
|
+
if (plugin.error)
|
|
1440
|
+
return;
|
|
1324
1441
|
if (plugin.enabled && (!plugin.loaded || !plugin.started))
|
|
1325
1442
|
allStarted = false;
|
|
1326
1443
|
if (!allStarted)
|
|
1327
|
-
this.log.info(
|
|
1444
|
+
this.log.info(`***Waiting in start matter server interval for plugin ${plg}${plugin.name}${db} to load (${plugin.loaded}) and start (${plugin.started}) ...`);
|
|
1328
1445
|
});
|
|
1329
1446
|
if (!allStarted)
|
|
1330
1447
|
return;
|
|
@@ -1389,14 +1506,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
1389
1506
|
* @returns The commissioning server context.
|
|
1390
1507
|
* @throws Error if the BasicInformationCluster is not found.
|
|
1391
1508
|
*/
|
|
1392
|
-
importCommissioningServerContext(pluginName, device) {
|
|
1509
|
+
async importCommissioningServerContext(pluginName, device) {
|
|
1393
1510
|
this.log.debug(`Importing matter commissioning server storage context from device for ${plg}${pluginName}${db}`);
|
|
1394
1511
|
const basic = device.getClusterServer(BasicInformationCluster);
|
|
1395
1512
|
if (!basic) {
|
|
1396
1513
|
throw new Error('importCommissioningServerContext error: cannot find the BasicInformationCluster');
|
|
1397
1514
|
}
|
|
1398
1515
|
//const random = 'CS' + CryptoNode.getRandomData(8).toHex();
|
|
1399
|
-
return this.createCommissioningServerContext(pluginName, basic.getNodeLabelAttribute(), DeviceTypeId(device.deviceType), basic.getVendorIdAttribute(), basic.getVendorNameAttribute(), basic.getProductIdAttribute(), basic.getProductNameAttribute(), basic.attributes.serialNumber?.getLocal(), basic.attributes.uniqueId?.getLocal(), basic.
|
|
1516
|
+
return await this.createCommissioningServerContext(pluginName, basic.getNodeLabelAttribute(), DeviceTypeId(device.deviceType), basic.getVendorIdAttribute(), basic.getVendorNameAttribute(), basic.getProductIdAttribute(), basic.getProductNameAttribute(), basic.attributes.serialNumber?.getLocal(), basic.attributes.uniqueId?.getLocal(), basic.getSoftwareVersionAttribute(), basic.getSoftwareVersionStringAttribute(), basic.getHardwareVersionAttribute(), basic.getHardwareVersionStringAttribute());
|
|
1400
1517
|
}
|
|
1401
1518
|
/**
|
|
1402
1519
|
* Creates a commissioning server storage context.
|
|
@@ -1416,7 +1533,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1416
1533
|
* @param hardwareVersionString - The hardware version string of the device (optional).
|
|
1417
1534
|
* @returns The storage context for the commissioning server.
|
|
1418
1535
|
*/
|
|
1419
|
-
createCommissioningServerContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber, uniqueId, softwareVersion, softwareVersionString, hardwareVersion, hardwareVersionString) {
|
|
1536
|
+
async createCommissioningServerContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber, uniqueId, softwareVersion, softwareVersionString, hardwareVersion, hardwareVersionString) {
|
|
1420
1537
|
if (!this.storageManager) {
|
|
1421
1538
|
this.log.error('No storage manager initialized');
|
|
1422
1539
|
process.exit(1);
|
|
@@ -1424,20 +1541,23 @@ export class Matterbridge extends EventEmitter {
|
|
|
1424
1541
|
this.log.debug(`Creating commissioning server storage context for ${plg}${pluginName}${db}`);
|
|
1425
1542
|
const random = 'CS' + CryptoNode.getRandomData(8).toHex();
|
|
1426
1543
|
const storageContext = this.storageManager.createContext(pluginName);
|
|
1427
|
-
storageContext.set('deviceName', deviceName);
|
|
1428
|
-
storageContext.set('deviceType', deviceType);
|
|
1429
|
-
storageContext.set('vendorId', vendorId);
|
|
1430
|
-
storageContext.set('vendorName', vendorName.slice(0, 32));
|
|
1431
|
-
storageContext.set('productId', productId);
|
|
1432
|
-
storageContext.set('productName', productName.slice(0, 32));
|
|
1433
|
-
storageContext.set('nodeLabel', productName.slice(0, 32));
|
|
1434
|
-
storageContext.set('productLabel', productName.slice(0, 32));
|
|
1435
|
-
storageContext.set('serialNumber', storageContext.get('serialNumber', random));
|
|
1436
|
-
storageContext.set('uniqueId', storageContext.get('uniqueId', random));
|
|
1437
|
-
storageContext.set('softwareVersion', softwareVersion ?? 1);
|
|
1438
|
-
storageContext.set('softwareVersionString', softwareVersionString ?? '1.0');
|
|
1439
|
-
storageContext.set('hardwareVersion', hardwareVersion ?? 1);
|
|
1440
|
-
storageContext.set('hardwareVersionString', hardwareVersionString ?? '1.0');
|
|
1544
|
+
await storageContext.set('deviceName', deviceName);
|
|
1545
|
+
await storageContext.set('deviceType', deviceType);
|
|
1546
|
+
await storageContext.set('vendorId', vendorId);
|
|
1547
|
+
await storageContext.set('vendorName', vendorName.slice(0, 32));
|
|
1548
|
+
await storageContext.set('productId', productId);
|
|
1549
|
+
await storageContext.set('productName', productName.slice(0, 32));
|
|
1550
|
+
await storageContext.set('nodeLabel', productName.slice(0, 32));
|
|
1551
|
+
await storageContext.set('productLabel', productName.slice(0, 32));
|
|
1552
|
+
await storageContext.set('serialNumber', await storageContext.get('serialNumber', random));
|
|
1553
|
+
await storageContext.set('uniqueId', await storageContext.get('uniqueId', random));
|
|
1554
|
+
await storageContext.set('softwareVersion', softwareVersion ?? 1);
|
|
1555
|
+
await storageContext.set('softwareVersionString', softwareVersionString ?? '1.0.0');
|
|
1556
|
+
await storageContext.set('hardwareVersion', hardwareVersion ?? 1);
|
|
1557
|
+
await storageContext.set('hardwareVersionString', hardwareVersionString ?? '1.0.0');
|
|
1558
|
+
this.log.debug(`**Created commissioning server storage context for ${plg}${pluginName}${db}`);
|
|
1559
|
+
this.log.debug(`**- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
1560
|
+
this.log.debug(`**- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1441
1561
|
return storageContext;
|
|
1442
1562
|
}
|
|
1443
1563
|
/**
|
|
@@ -1502,20 +1622,20 @@ export class Matterbridge extends EventEmitter {
|
|
|
1502
1622
|
* @param {string} pluginName - The name of the commissioning server.
|
|
1503
1623
|
* @returns {CommissioningServer} The created commissioning server.
|
|
1504
1624
|
*/
|
|
1505
|
-
createCommisioningServer(context, pluginName) {
|
|
1625
|
+
async createCommisioningServer(context, pluginName) {
|
|
1506
1626
|
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db}`);
|
|
1507
|
-
const deviceName = context.get('deviceName');
|
|
1508
|
-
const deviceType = context.get('deviceType');
|
|
1509
|
-
const vendorId = context.get('vendorId');
|
|
1510
|
-
const vendorName = context.get('vendorName'); // Home app = Manufacturer
|
|
1511
|
-
const productId = context.get('productId');
|
|
1512
|
-
const productName = context.get('productName'); // Home app = Model
|
|
1513
|
-
const serialNumber = context.get('serialNumber');
|
|
1514
|
-
const uniqueId = context.get('uniqueId');
|
|
1515
|
-
const softwareVersion = context.get('softwareVersion', 1);
|
|
1516
|
-
const softwareVersionString = context.get('softwareVersionString', '1.0.0'); // Home app = Firmware Revision
|
|
1517
|
-
const hardwareVersion = context.get('hardwareVersion', 1);
|
|
1518
|
-
const hardwareVersionString = context.get('hardwareVersionString', '1.0.0');
|
|
1627
|
+
const deviceName = await context.get('deviceName');
|
|
1628
|
+
const deviceType = await context.get('deviceType');
|
|
1629
|
+
const vendorId = await context.get('vendorId');
|
|
1630
|
+
const vendorName = await context.get('vendorName'); // Home app = Manufacturer
|
|
1631
|
+
const productId = await context.get('productId');
|
|
1632
|
+
const productName = await context.get('productName'); // Home app = Model
|
|
1633
|
+
const serialNumber = await context.get('serialNumber');
|
|
1634
|
+
const uniqueId = await context.get('uniqueId');
|
|
1635
|
+
const softwareVersion = await context.get('softwareVersion', 1);
|
|
1636
|
+
const softwareVersionString = await context.get('softwareVersionString', '1.0.0'); // Home app = Firmware Revision
|
|
1637
|
+
const hardwareVersion = await context.get('hardwareVersion', 1);
|
|
1638
|
+
const hardwareVersionString = await context.get('hardwareVersionString', '1.0.0');
|
|
1519
1639
|
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with deviceName ${deviceName} deviceType ${deviceType}(0x${deviceType.toString(16).padStart(4, '0')})`);
|
|
1520
1640
|
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with uniqueId ${uniqueId} serialNumber ${serialNumber}`);
|
|
1521
1641
|
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with softwareVersion ${softwareVersion} softwareVersionString ${softwareVersionString}`);
|
|
@@ -1645,10 +1765,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
1645
1765
|
* @param {StorageContext} context - The storage context.
|
|
1646
1766
|
* @returns {Aggregator} - The created Matter Aggregator.
|
|
1647
1767
|
*/
|
|
1648
|
-
createMatterAggregator(context) {
|
|
1768
|
+
async createMatterAggregator(context) {
|
|
1649
1769
|
const random = 'AG' + CryptoNode.getRandomData(8).toHex();
|
|
1650
|
-
context.set('aggregatorSerialNumber', context.get('aggregatorSerialNumber', random));
|
|
1651
|
-
context.set('aggregatorUniqueId', context.get('aggregatorUniqueId', random));
|
|
1770
|
+
await context.set('aggregatorSerialNumber', await context.get('aggregatorSerialNumber', random));
|
|
1771
|
+
await context.set('aggregatorUniqueId', await context.get('aggregatorUniqueId', random));
|
|
1652
1772
|
const matterAggregator = new Aggregator();
|
|
1653
1773
|
matterAggregator.addClusterServer(ClusterServer(BasicInformationCluster, {
|
|
1654
1774
|
dataModelRevision: 1,
|
|
@@ -1659,8 +1779,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1659
1779
|
productName: 'Matterbridge aggregator',
|
|
1660
1780
|
productLabel: 'Matterbridge aggregator',
|
|
1661
1781
|
nodeLabel: 'Matterbridge aggregator',
|
|
1662
|
-
serialNumber: context.get('aggregatorSerialNumber'),
|
|
1663
|
-
uniqueId: context.get('aggregatorUniqueId'),
|
|
1782
|
+
serialNumber: await context.get('aggregatorSerialNumber'),
|
|
1783
|
+
uniqueId: await context.get('aggregatorUniqueId'),
|
|
1664
1784
|
softwareVersion: 1,
|
|
1665
1785
|
softwareVersionString: 'v.1.0',
|
|
1666
1786
|
hardwareVersion: 1,
|
|
@@ -1942,6 +2062,26 @@ export class Matterbridge extends EventEmitter {
|
|
|
1942
2062
|
this.expressApp = express();
|
|
1943
2063
|
// Serve React build directory
|
|
1944
2064
|
this.expressApp.use(express.static(path.join(this.rootDirectory, 'frontend/build')));
|
|
2065
|
+
// Endpoint to provide login code
|
|
2066
|
+
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
2067
|
+
const { password } = req.body;
|
|
2068
|
+
this.log.debug('The frontend sent /api/login', password);
|
|
2069
|
+
if (!this.nodeContext) {
|
|
2070
|
+
this.log.error('/api/login nodeContext not found');
|
|
2071
|
+
res.json({ valid: false });
|
|
2072
|
+
return;
|
|
2073
|
+
}
|
|
2074
|
+
try {
|
|
2075
|
+
const storedPassword = await this.nodeContext.get('password', '');
|
|
2076
|
+
if (storedPassword === '' || password === storedPassword)
|
|
2077
|
+
res.json({ valid: true });
|
|
2078
|
+
else
|
|
2079
|
+
res.json({ valid: false });
|
|
2080
|
+
}
|
|
2081
|
+
catch (error) {
|
|
2082
|
+
res.json({ valid: false });
|
|
2083
|
+
}
|
|
2084
|
+
});
|
|
1945
2085
|
// Endpoint to provide QR pairing code
|
|
1946
2086
|
this.expressApp.get('/api/qr-code', (req, res) => {
|
|
1947
2087
|
this.log.debug('The frontend sent /api/qr-code');
|
|
@@ -1995,7 +2135,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1995
2135
|
data.push({
|
|
1996
2136
|
pluginName: registeredDevice.plugin,
|
|
1997
2137
|
type: registeredDevice.device.name + ' (0x' + registeredDevice.device.deviceType.toString(16).padStart(4, '0') + ')',
|
|
1998
|
-
endpoint: registeredDevice.device.
|
|
2138
|
+
endpoint: registeredDevice.device.number,
|
|
1999
2139
|
name,
|
|
2000
2140
|
serial,
|
|
2001
2141
|
uniqueId,
|
|
@@ -2015,7 +2155,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2015
2155
|
}
|
|
2016
2156
|
const data = [];
|
|
2017
2157
|
this.registeredDevices.forEach((registeredDevice) => {
|
|
2018
|
-
if (registeredDevice.plugin === selectedPluginName && registeredDevice.device.
|
|
2158
|
+
if (registeredDevice.plugin === selectedPluginName && registeredDevice.device.number === selectedDeviceEndpoint) {
|
|
2019
2159
|
const clusterServers = registeredDevice.device.getAllClusterServers();
|
|
2020
2160
|
clusterServers.forEach((clusterServer) => {
|
|
2021
2161
|
Object.entries(clusterServer.attributes).forEach(([key, value]) => {
|
|
@@ -2056,7 +2196,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2056
2196
|
res.status(400).json({ error: 'No command provided' });
|
|
2057
2197
|
return;
|
|
2058
2198
|
}
|
|
2059
|
-
this.log.
|
|
2199
|
+
this.log.debug(`*Received frontend command: ${command}:${param}`);
|
|
2200
|
+
// Handle the command setpassword from Settings
|
|
2201
|
+
if (command === 'setpassword') {
|
|
2202
|
+
const password = param.slice(1, -1); // Remove the first and last characters
|
|
2203
|
+
this.log.info('setpassword', param, password);
|
|
2204
|
+
await this.nodeContext?.set('password', password);
|
|
2205
|
+
}
|
|
2060
2206
|
// Handle the command debugLevel from Settings
|
|
2061
2207
|
if (command === 'setloglevel') {
|
|
2062
2208
|
if (param === 'Debug') {
|
|
@@ -2078,15 +2224,47 @@ export class Matterbridge extends EventEmitter {
|
|
|
2078
2224
|
plugin.platform?.log.setLogDebug(this.debugEnabled);
|
|
2079
2225
|
});
|
|
2080
2226
|
}
|
|
2081
|
-
// Handle the command
|
|
2227
|
+
// Handle the command shutdown from Header
|
|
2228
|
+
if (command === 'shutdown') {
|
|
2229
|
+
this.shutdownProcess();
|
|
2230
|
+
}
|
|
2231
|
+
// Handle the command restart from Header
|
|
2082
2232
|
if (command === 'restart') {
|
|
2083
2233
|
this.restartProcess();
|
|
2084
2234
|
}
|
|
2085
2235
|
// Handle the command update from Header
|
|
2236
|
+
if (command === 'update') {
|
|
2237
|
+
this.log.warn(`***Updating matterbridge ${plg}${param}${db}`);
|
|
2238
|
+
try {
|
|
2239
|
+
await this.spawnCommand('npm', ['install', '-g', 'matterbridge']);
|
|
2240
|
+
this.log.info('Matterbridge has been updated. Full restart required.');
|
|
2241
|
+
}
|
|
2242
|
+
catch (error) {
|
|
2243
|
+
this.log.error('Error updating matterbridge');
|
|
2244
|
+
res.json({ message: 'Command received' });
|
|
2245
|
+
return;
|
|
2246
|
+
}
|
|
2247
|
+
this.updateProcess();
|
|
2248
|
+
}
|
|
2249
|
+
// Handle the command update from Header
|
|
2086
2250
|
if (command === 'update') {
|
|
2087
2251
|
this.log.warn(`The /api/command/${command} is not yet implemented`);
|
|
2088
2252
|
}
|
|
2089
|
-
// Handle the command
|
|
2253
|
+
// Handle the command installplugin from Home
|
|
2254
|
+
if (command === 'installplugin') {
|
|
2255
|
+
param = param.replace(/\*/g, '\\');
|
|
2256
|
+
this.log.warn(`***Installing plugin ${plg}${param}${db}`);
|
|
2257
|
+
try {
|
|
2258
|
+
await this.spawnCommand('npm', ['install', '-g', param]);
|
|
2259
|
+
this.log.info(`Plugin ${plg}${param}${nf} installed. Restart required.`);
|
|
2260
|
+
}
|
|
2261
|
+
catch (error) {
|
|
2262
|
+
this.log.error(`Error installing plugin ${plg}${param}${er}`);
|
|
2263
|
+
res.json({ message: 'Command received' });
|
|
2264
|
+
return;
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
// Handle the command addplugin from Home
|
|
2090
2268
|
if (command === 'addplugin') {
|
|
2091
2269
|
param = param.replace(/\*/g, '\\');
|
|
2092
2270
|
if (this.registeredPlugins.find((plugin) => plugin.name === param)) {
|
|
@@ -2156,7 +2334,15 @@ export class Matterbridge extends EventEmitter {
|
|
|
2156
2334
|
if (pluginToEnable) {
|
|
2157
2335
|
pluginToEnable.enabled = true;
|
|
2158
2336
|
pluginToEnable.platform = await this.loadPlugin(pluginToEnable);
|
|
2159
|
-
|
|
2337
|
+
if (pluginToEnable.platform) {
|
|
2338
|
+
await this.startPlugin(pluginToEnable, 'The plugin has been enabled', true);
|
|
2339
|
+
pluginToEnable.enabled = false;
|
|
2340
|
+
}
|
|
2341
|
+
else {
|
|
2342
|
+
pluginToEnable.enabled = false;
|
|
2343
|
+
pluginToEnable.error = true;
|
|
2344
|
+
this.log.error(`Error enabling plugin ${plg}${param}${er}`);
|
|
2345
|
+
}
|
|
2160
2346
|
}
|
|
2161
2347
|
}
|
|
2162
2348
|
}
|
|
@@ -2193,7 +2379,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2193
2379
|
});
|
|
2194
2380
|
// Fallback for routing
|
|
2195
2381
|
this.expressApp.get('*', (req, res) => {
|
|
2196
|
-
this.log.
|
|
2382
|
+
this.log.debug('The frontend sent *', req.url);
|
|
2197
2383
|
res.sendFile(path.join(this.rootDirectory, 'frontend/build/index.html'));
|
|
2198
2384
|
});
|
|
2199
2385
|
this.expressServer = this.expressApp.listen(port, () => {
|
|
@@ -2220,6 +2406,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2220
2406
|
attributes += `Cover position: ${clusterServer.attributes.currentPositionLiftPercent100ths.getLocal() / 100}% `;
|
|
2221
2407
|
if (clusterServer.name === 'DoorLock')
|
|
2222
2408
|
attributes += `State: ${clusterServer.attributes.lockState.getLocal() === 1 ? 'Locked' : 'Not locked'} `;
|
|
2409
|
+
if (clusterServer.name === 'Thermostat')
|
|
2410
|
+
attributes += `Temperature: ${clusterServer.attributes.localTemperature.getLocal() / 100}°C `;
|
|
2223
2411
|
if (clusterServer.name === 'LevelControl')
|
|
2224
2412
|
attributes += `Level: ${clusterServer.getCurrentLevelAttribute()}% `;
|
|
2225
2413
|
if (clusterServer.name === 'ColorControl')
|