matterbridge 1.2.10 → 1.2.12
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 +37 -1
- package/README.md +97 -4
- package/dist/cli.js +10 -5
- package/dist/cli.js.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/matterbridge.d.ts +14 -0
- package/dist/matterbridge.d.ts.map +1 -1
- package/dist/matterbridge.js +215 -47
- package/dist/matterbridge.js.map +1 -1
- package/dist/matterbridgeDevice.d.ts +43 -16
- package/dist/matterbridgeDevice.d.ts.map +1 -1
- package/dist/matterbridgeDevice.js +127 -50
- package/dist/matterbridgeDevice.js.map +1 -1
- package/frontend/build/asset-manifest.json +6 -6
- package/frontend/build/index.html +1 -1
- package/frontend/build/static/css/main.1880392b.css +2 -0
- package/frontend/build/static/css/main.1880392b.css.map +1 -0
- package/frontend/build/static/js/main.0c70c26b.js +3 -0
- package/frontend/build/static/js/{main.cf64bc64.js.LICENSE.txt → main.0c70c26b.js.LICENSE.txt} +2 -0
- package/frontend/build/static/js/main.0c70c26b.js.map +1 -0
- package/package.json +5 -1
- package/frontend/build/static/css/main.4c325919.css +0 -2
- package/frontend/build/static/css/main.4c325919.css.map +0 -1
- package/frontend/build/static/js/main.cf64bc64.js +0 -3
- package/frontend/build/static/js/main.cf64bc64.js.map +0 -1
package/dist/matterbridge.js
CHANGED
|
@@ -33,9 +33,7 @@ import os from 'os';
|
|
|
33
33
|
import path from 'path';
|
|
34
34
|
import WebSocket, { WebSocketServer } from 'ws';
|
|
35
35
|
import { CommissioningController, CommissioningServer, MatterServer } from '@project-chip/matter-node.js';
|
|
36
|
-
import { BasicInformationCluster, BooleanStateCluster,
|
|
37
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
38
|
-
BridgedDeviceBasicInformation, BridgedDeviceBasicInformationCluster, ClusterServer, GeneralCommissioning, PowerSourceCluster, ThreadNetworkDiagnosticsCluster, getClusterNameById, } from '@project-chip/matter-node.js/cluster';
|
|
36
|
+
import { BasicInformationCluster, BooleanStateCluster, BridgedDeviceBasicInformation, BridgedDeviceBasicInformationCluster, ClusterServer, FixedLabelCluster, GeneralCommissioning, PowerSourceCluster, ThreadNetworkDiagnosticsCluster, getClusterNameById, } from '@project-chip/matter-node.js/cluster';
|
|
39
37
|
import { DeviceTypeId, VendorId } from '@project-chip/matter-node.js/datatype';
|
|
40
38
|
import { Aggregator, DeviceTypes, NodeStateInformation } from '@project-chip/matter-node.js/device';
|
|
41
39
|
import { Format, Level, Logger } from '@project-chip/matter-node.js/log';
|
|
@@ -55,6 +53,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
55
53
|
ipv6Address: '',
|
|
56
54
|
nodeVersion: '',
|
|
57
55
|
hostname: '',
|
|
56
|
+
user: '',
|
|
58
57
|
osType: '',
|
|
59
58
|
osRelease: '',
|
|
60
59
|
osPlatform: '',
|
|
@@ -72,6 +71,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
72
71
|
matterbridgeVersion: '',
|
|
73
72
|
matterbridgeLatestVersion: '',
|
|
74
73
|
bridgeMode: '',
|
|
74
|
+
restartMode: '',
|
|
75
75
|
debugEnabled: false,
|
|
76
76
|
};
|
|
77
77
|
homeDirectory = '';
|
|
@@ -188,7 +188,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
188
188
|
await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
|
|
189
189
|
// Set reachability to true and trigger event after 60 seconds
|
|
190
190
|
setTimeout(() => {
|
|
191
|
-
this.log.info(
|
|
191
|
+
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
192
192
|
if (this.commissioningServer)
|
|
193
193
|
this.setCommissioningServerReachability(this.commissioningServer, true);
|
|
194
194
|
if (this.matterAggregator)
|
|
@@ -365,7 +365,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
365
365
|
// Delete matter storage file
|
|
366
366
|
await fs.unlink(path.join(this.matterbridgeDirectory, 'matterbridge.json'));
|
|
367
367
|
// Delete node storage directory with its subdirectories
|
|
368
|
-
await fs.
|
|
368
|
+
await fs.rm(path.join(this.matterbridgeDirectory, 'storage'), { recursive: true });
|
|
369
369
|
this.log.info('Factory reset done! Remove all paired devices from the controllers.');
|
|
370
370
|
this.emit('shutdown');
|
|
371
371
|
process.exit(0);
|
|
@@ -609,6 +609,31 @@ export class Matterbridge extends EventEmitter {
|
|
|
609
609
|
await this.cleanup('shutting down...', false);
|
|
610
610
|
this.hasCleanupStarted = false;
|
|
611
611
|
}
|
|
612
|
+
/**
|
|
613
|
+
* Shut down the process and reset.
|
|
614
|
+
*/
|
|
615
|
+
async unregisterAndShutdownProcess() {
|
|
616
|
+
this.log.info('Unregistering all devices and shutting down...');
|
|
617
|
+
for (const plugin of this.registeredPlugins.filter((plugin) => plugin.enabled && !plugin.error)) {
|
|
618
|
+
await this.removeAllBridgedDevices(plugin.name);
|
|
619
|
+
}
|
|
620
|
+
await this.cleanup('unregistered all devices and shutting down...', false);
|
|
621
|
+
this.hasCleanupStarted = false;
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Shut down the process and reset.
|
|
625
|
+
*/
|
|
626
|
+
async shutdownProcessAndReset() {
|
|
627
|
+
await this.cleanup('shutting down with reset...', false);
|
|
628
|
+
this.hasCleanupStarted = false;
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* Shut down the process and factory reset.
|
|
632
|
+
*/
|
|
633
|
+
async shutdownProcessAndFactoryReset() {
|
|
634
|
+
await this.cleanup('shutting down with factory reset...', false);
|
|
635
|
+
this.hasCleanupStarted = false;
|
|
636
|
+
}
|
|
612
637
|
/**
|
|
613
638
|
* Cleans up the Matterbridge instance.
|
|
614
639
|
* @param message - The cleanup message.
|
|
@@ -624,12 +649,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
624
649
|
this.log.debug('All listeners removed');
|
|
625
650
|
// Calling the shutdown functions with a reason
|
|
626
651
|
for (const plugin of this.registeredPlugins) {
|
|
627
|
-
if (!plugin.enabled)
|
|
652
|
+
if (!plugin.enabled || plugin.error)
|
|
628
653
|
continue;
|
|
629
|
-
this.log.info(
|
|
654
|
+
this.log.info(`Shutting down plugin ${plg}${plugin.name}${nf}`);
|
|
630
655
|
if (plugin.platform) {
|
|
631
|
-
|
|
632
|
-
|
|
656
|
+
try {
|
|
657
|
+
await plugin.platform.onShutdown('Matterbridge is closing: ' + message);
|
|
658
|
+
await this.savePluginConfig(plugin);
|
|
659
|
+
}
|
|
660
|
+
catch (error) {
|
|
661
|
+
this.log.error(`Plugin ${plg}${plugin.name}${er} shutting down error: ${error}`);
|
|
662
|
+
}
|
|
633
663
|
}
|
|
634
664
|
else {
|
|
635
665
|
this.log.warn(`Plugin ${plg}${plugin.name}${wr} platform not found`);
|
|
@@ -703,7 +733,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
703
733
|
});
|
|
704
734
|
this.webSocketServer = undefined;
|
|
705
735
|
}
|
|
706
|
-
|
|
736
|
+
setTimeout(async () => {
|
|
707
737
|
// Closing matter
|
|
708
738
|
await this.stopMatter();
|
|
709
739
|
// Closing storage
|
|
@@ -733,7 +763,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
733
763
|
this.registeredPlugins = [];
|
|
734
764
|
this.registeredDevices = [];
|
|
735
765
|
this.log.info('Waiting for matter to deliver last messages...');
|
|
736
|
-
|
|
766
|
+
setTimeout(async () => {
|
|
737
767
|
if (restart) {
|
|
738
768
|
if (message === 'updating...') {
|
|
739
769
|
this.log.info('Cleanup completed. Updating...');
|
|
@@ -747,14 +777,27 @@ export class Matterbridge extends EventEmitter {
|
|
|
747
777
|
}
|
|
748
778
|
}
|
|
749
779
|
else {
|
|
780
|
+
if (message === 'shutting down with reset...') {
|
|
781
|
+
// Delete matter storage file
|
|
782
|
+
this.log.info('Resetting Matterbridge commissioning information...');
|
|
783
|
+
await fs.unlink(path.join(this.matterbridgeDirectory, 'matterbridge.json'));
|
|
784
|
+
this.log.info('Reset done! Remove all paired devices from the controllers.');
|
|
785
|
+
}
|
|
786
|
+
if (message === 'shutting down with factory reset...') {
|
|
787
|
+
// Delete matter storage file
|
|
788
|
+
this.log.info('Resetting Matterbridge commissioning information...');
|
|
789
|
+
await fs.unlink(path.join(this.matterbridgeDirectory, 'matterbridge.json'));
|
|
790
|
+
// Delete node storage directory with its subdirectories
|
|
791
|
+
this.log.info('Resetting Matterbridge storage...');
|
|
792
|
+
await fs.rm(path.join(this.matterbridgeDirectory, 'storage'), { recursive: true });
|
|
793
|
+
this.log.info('Factory reset done! Remove all paired devices from the controllers.');
|
|
794
|
+
}
|
|
750
795
|
this.log.info('Cleanup completed. Shutting down...');
|
|
751
796
|
Matterbridge.instance = undefined;
|
|
752
797
|
this.emit('shutdown');
|
|
753
798
|
}
|
|
754
799
|
}, 2 * 1000);
|
|
755
|
-
//cleanupTimeout2.unref();
|
|
756
800
|
}, 3 * 1000);
|
|
757
|
-
//cleanupTimeout1.unref();
|
|
758
801
|
}
|
|
759
802
|
}
|
|
760
803
|
/**
|
|
@@ -904,7 +947,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
904
947
|
async removeAllBridgedDevices(pluginName) {
|
|
905
948
|
const plugin = this.findPlugin(pluginName);
|
|
906
949
|
if (this.bridgeMode === 'childbridge' && plugin?.type === 'AccessoryPlatform') {
|
|
907
|
-
this.log.info(`Removing devices for plugin ${plg}${pluginName}${nf}
|
|
950
|
+
this.log.info(`Removing devices for plugin ${plg}${pluginName}${nf} type AccessoryPlatform is not supported in childbridge mode`);
|
|
908
951
|
return;
|
|
909
952
|
}
|
|
910
953
|
const devicesToRemove = [];
|
|
@@ -1190,13 +1233,15 @@ export class Matterbridge extends EventEmitter {
|
|
|
1190
1233
|
return Promise.resolve();
|
|
1191
1234
|
})
|
|
1192
1235
|
.catch((err) => {
|
|
1236
|
+
plugin.error = true;
|
|
1193
1237
|
this.log.error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`);
|
|
1194
|
-
return Promise.reject(new Error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`));
|
|
1238
|
+
// return Promise.reject(new Error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`));
|
|
1195
1239
|
});
|
|
1196
1240
|
}
|
|
1197
1241
|
catch (err) {
|
|
1242
|
+
plugin.error = true;
|
|
1198
1243
|
this.log.error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`);
|
|
1199
|
-
return Promise.reject(new Error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`));
|
|
1244
|
+
// return Promise.reject(new Error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`));
|
|
1200
1245
|
}
|
|
1201
1246
|
}
|
|
1202
1247
|
/**
|
|
@@ -1224,13 +1269,15 @@ export class Matterbridge extends EventEmitter {
|
|
|
1224
1269
|
return Promise.resolve();
|
|
1225
1270
|
})
|
|
1226
1271
|
.catch((err) => {
|
|
1272
|
+
plugin.error = true;
|
|
1227
1273
|
this.log.error(`Failed to configure plugin ${plg}${plugin.name}${er}: ${err}`);
|
|
1228
|
-
return Promise.reject(new Error(`Failed to configure plugin ${plg}${plugin.name}${er}: ${err}`));
|
|
1274
|
+
// return Promise.reject(new Error(`Failed to configure plugin ${plg}${plugin.name}${er}: ${err}`));
|
|
1229
1275
|
});
|
|
1230
1276
|
}
|
|
1231
1277
|
catch (err) {
|
|
1278
|
+
plugin.error = true;
|
|
1232
1279
|
this.log.error(`Failed to configure plugin ${plg}${plugin.name}${er}: ${err}`);
|
|
1233
|
-
return Promise.reject(new Error(`Failed to configure plugin ${plg}${plugin.name}${er}: ${err}`));
|
|
1280
|
+
// return Promise.reject(new Error(`Failed to configure plugin ${plg}${plugin.name}${er}: ${err}`));
|
|
1234
1281
|
}
|
|
1235
1282
|
}
|
|
1236
1283
|
/**
|
|
@@ -1286,12 +1333,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
1286
1333
|
}
|
|
1287
1334
|
else {
|
|
1288
1335
|
this.log.error(`Plugin ${plg}${plugin.name}${er} does not provide a default export`);
|
|
1336
|
+
plugin.error = true;
|
|
1289
1337
|
return;
|
|
1290
1338
|
//return Promise.reject(new Error(`Plugin ${plg}${plugin.name}${er} does not provide a default export`));
|
|
1291
1339
|
}
|
|
1292
1340
|
}
|
|
1293
1341
|
catch (err) {
|
|
1294
1342
|
this.log.error(`Failed to load plugin ${plg}${plugin.name}${er}: ${err}`);
|
|
1343
|
+
plugin.error = true;
|
|
1295
1344
|
return;
|
|
1296
1345
|
//return Promise.reject(new Error(`Failed to load plugin ${plg}${plugin.name}${er}: ${err}`));
|
|
1297
1346
|
}
|
|
@@ -1480,10 +1529,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
1480
1529
|
let failCount = 0;
|
|
1481
1530
|
const startInterval = setInterval(async () => {
|
|
1482
1531
|
for (const plugin of this.registeredPlugins) {
|
|
1483
|
-
if (!plugin.enabled)
|
|
1532
|
+
if (!plugin.enabled || plugin.error)
|
|
1484
1533
|
continue;
|
|
1485
1534
|
if (!plugin.loaded) {
|
|
1486
|
-
this.log.
|
|
1535
|
+
this.log.debug(`***Waiting (failSafeCount=${failCount}/30) in startMatterbridge interval for plugin ${plg}${plugin.name}${db} loaded: ${plugin.loaded}...`);
|
|
1487
1536
|
failCount++;
|
|
1488
1537
|
if (failCount > 30) {
|
|
1489
1538
|
this.log.error(`***Failed to load plugin ${plg}${plugin.name}${er}`);
|
|
@@ -1521,7 +1570,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1521
1570
|
await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
|
|
1522
1571
|
//if (hasParameter('advertise')) await this.commissioningServer.advertise();
|
|
1523
1572
|
setTimeout(() => {
|
|
1524
|
-
this.log.info(
|
|
1573
|
+
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1525
1574
|
if (this.commissioningServer)
|
|
1526
1575
|
this.setCommissioningServerReachability(this.commissioningServer, true);
|
|
1527
1576
|
if (this.matterAggregator)
|
|
@@ -1542,7 +1591,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1542
1591
|
let failCount = 0;
|
|
1543
1592
|
const startInterval = setInterval(async () => {
|
|
1544
1593
|
if (!plugin.loaded || !plugin.started /* || !plugin.configured*/) {
|
|
1545
|
-
this.log.
|
|
1594
|
+
this.log.debug(`***Waiting (failSafeCount=${failCount}/30) in startMatterbridge interval for plugin ${plg}${plugin.name}${db} loaded: ${plugin.loaded} started: ${plugin.started}...`);
|
|
1546
1595
|
failCount++;
|
|
1547
1596
|
if (failCount > 30) {
|
|
1548
1597
|
this.log.error(`***Failed to load plugin ${plg}${plugin.name}${er}`);
|
|
@@ -1624,7 +1673,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1624
1673
|
}
|
|
1625
1674
|
await this.showCommissioningQRCode(plugin.commissioningServer, plugin.storageContext, plugin.nodeContext, plugin.name);
|
|
1626
1675
|
// Setting reachability to true
|
|
1627
|
-
this.log.info(
|
|
1676
|
+
this.log.info(`Setting reachability to true for ${plg}${plugin.name}${db}`);
|
|
1628
1677
|
if (plugin.commissioningServer)
|
|
1629
1678
|
this.setCommissioningServerReachability(plugin.commissioningServer, true);
|
|
1630
1679
|
if (plugin.type === 'AccessoryPlatform' && plugin.device)
|
|
@@ -1743,7 +1792,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1743
1792
|
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
1744
1793
|
}
|
|
1745
1794
|
else {
|
|
1746
|
-
this.log.info(
|
|
1795
|
+
this.log.info(`*The commissioning server on port ${commissioningServer.getPort()} for ${plg}${pluginName}${nf} is already commissioned . Waiting for controllers to connect ...`);
|
|
1747
1796
|
if (pluginName !== 'Matterbridge') {
|
|
1748
1797
|
const plugin = this.findPlugin(pluginName);
|
|
1749
1798
|
if (plugin) {
|
|
@@ -1792,7 +1841,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1792
1841
|
if (basicInformationCluster && basicInformationCluster.triggerReachableChangedEvent)
|
|
1793
1842
|
basicInformationCluster.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
1794
1843
|
matterAggregator.getBridgedDevices().forEach((device) => {
|
|
1795
|
-
this.log.debug(
|
|
1844
|
+
this.log.debug(`Setting reachability to true for bridged device: ${dev}${device.name}${nf}`);
|
|
1796
1845
|
device.getClusterServer(BridgedDeviceBasicInformationCluster)?.setReachableAttribute(reachable);
|
|
1797
1846
|
device.getClusterServer(BridgedDeviceBasicInformationCluster)?.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
1798
1847
|
});
|
|
@@ -1818,6 +1867,35 @@ export class Matterbridge extends EventEmitter {
|
|
|
1818
1867
|
* @returns {CommissioningServer} The created commissioning server.
|
|
1819
1868
|
*/
|
|
1820
1869
|
async createCommisioningServer(context, pluginName) {
|
|
1870
|
+
const getVendorIdName = (vendorId) => {
|
|
1871
|
+
if (!vendorId)
|
|
1872
|
+
return '';
|
|
1873
|
+
let vendorName = '';
|
|
1874
|
+
switch (vendorId) {
|
|
1875
|
+
case 4937:
|
|
1876
|
+
vendorName = '(AppleHome)';
|
|
1877
|
+
break;
|
|
1878
|
+
case 4362:
|
|
1879
|
+
vendorName = '(SmartThings)';
|
|
1880
|
+
break;
|
|
1881
|
+
case 4939:
|
|
1882
|
+
vendorName = '(HomeAssistant)';
|
|
1883
|
+
break;
|
|
1884
|
+
case 24582:
|
|
1885
|
+
vendorName = '(GoogleHome)';
|
|
1886
|
+
break;
|
|
1887
|
+
case 4701:
|
|
1888
|
+
vendorName = '(Tuya)';
|
|
1889
|
+
break;
|
|
1890
|
+
case 4742:
|
|
1891
|
+
vendorName = '(eWeLink)';
|
|
1892
|
+
break;
|
|
1893
|
+
default:
|
|
1894
|
+
vendorName = '(unknown)';
|
|
1895
|
+
break;
|
|
1896
|
+
}
|
|
1897
|
+
return vendorName;
|
|
1898
|
+
};
|
|
1821
1899
|
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db}`);
|
|
1822
1900
|
const deviceName = await context.get('deviceName');
|
|
1823
1901
|
const deviceType = await context.get('deviceType');
|
|
@@ -1860,16 +1938,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
1860
1938
|
const info = commissioningServer.getActiveSessionInformation(fabricIndex);
|
|
1861
1939
|
let connected = false;
|
|
1862
1940
|
info.forEach((session) => {
|
|
1863
|
-
this.log.info(
|
|
1941
|
+
this.log.info(`*Active session changed on fabric ${fabricIndex} ${session.fabric?.rootVendorId}${getVendorIdName(session.fabric?.rootVendorId)}/${session.fabric?.label} for ${plg}${pluginName}${nf}`, debugStringify(session));
|
|
1864
1942
|
if (session.isPeerActive === true && session.secure === true && session.numberOfActiveSubscriptions >= 1) {
|
|
1865
|
-
|
|
1866
|
-
if (session.fabric?.rootVendorId === 4937)
|
|
1867
|
-
controllerName = 'AppleHome';
|
|
1868
|
-
if (session.fabric?.rootVendorId === 4362)
|
|
1869
|
-
controllerName = 'SmartThings';
|
|
1870
|
-
if (session.fabric?.rootVendorId === 4939)
|
|
1871
|
-
controllerName = 'HomeAssistant';
|
|
1872
|
-
this.log.info(`***Controller ${session.fabric?.rootVendorId}${controllerName !== '' ? '(' + controllerName + ')' : ''}/${session.fabric?.label} connected to ${plg}${pluginName}${nf} on session ${session.name}`);
|
|
1943
|
+
this.log.info(`*Controller ${session.fabric?.rootVendorId}${getVendorIdName(session.fabric?.rootVendorId)}/${session.fabric?.label} connected to ${plg}${pluginName}${nf} on session ${session.name}`);
|
|
1873
1944
|
connected = true;
|
|
1874
1945
|
}
|
|
1875
1946
|
});
|
|
@@ -1885,9 +1956,15 @@ export class Matterbridge extends EventEmitter {
|
|
|
1885
1956
|
if (this.bridgeMode === 'bridge') {
|
|
1886
1957
|
//Logger.defaultLogLevel = Level.INFO;
|
|
1887
1958
|
for (const plugin of this.registeredPlugins) {
|
|
1888
|
-
if (!plugin.enabled)
|
|
1959
|
+
if (!plugin.enabled || !plugin.loaded || plugin.error)
|
|
1889
1960
|
continue;
|
|
1890
|
-
|
|
1961
|
+
try {
|
|
1962
|
+
this.startPlugin(plugin, 'Matterbridge is commissioned and controllers are connected', true); // No await do it asyncronously with also configurePlugin
|
|
1963
|
+
}
|
|
1964
|
+
catch (error) {
|
|
1965
|
+
plugin.error = true;
|
|
1966
|
+
this.log.error(`Error starting plugin ${plg}${plugin.name}${er}`, error);
|
|
1967
|
+
}
|
|
1891
1968
|
}
|
|
1892
1969
|
Logger.defaultLogLevel = this.debugEnabled ? Level.DEBUG : Level.INFO;
|
|
1893
1970
|
}
|
|
@@ -1925,9 +2002,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
1925
2002
|
},
|
|
1926
2003
|
commissioningChangedCallback: async (fabricIndex) => {
|
|
1927
2004
|
const info = commissioningServer.getCommissionedFabricInformation(fabricIndex);
|
|
1928
|
-
this.log.debug(
|
|
2005
|
+
this.log.debug(`*Commissioning changed on fabric ${fabricIndex} for ${plg}${pluginName}${nf}`, debugStringify(info));
|
|
1929
2006
|
if (info.length === 0) {
|
|
1930
|
-
this.log.warn(
|
|
2007
|
+
this.log.warn(`*Commissioning removed from fabric ${fabricIndex} for ${plg}${pluginName}${wr}. Resetting the commissioning server ...`);
|
|
1931
2008
|
await commissioningServer.factoryReset();
|
|
1932
2009
|
if (pluginName === 'Matterbridge') {
|
|
1933
2010
|
await this.matterbridgeContext?.clearAll();
|
|
@@ -1942,7 +2019,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1942
2019
|
}
|
|
1943
2020
|
}
|
|
1944
2021
|
}
|
|
1945
|
-
this.log.warn(
|
|
2022
|
+
this.log.warn(`*Restart to activate the pairing for ${plg}${pluginName}${wr}`);
|
|
1946
2023
|
}
|
|
1947
2024
|
},
|
|
1948
2025
|
});
|
|
@@ -2076,6 +2153,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2076
2153
|
const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
|
|
2077
2154
|
// Host system information
|
|
2078
2155
|
this.systemInformation.hostname = os.hostname();
|
|
2156
|
+
this.systemInformation.user = os.userInfo().username;
|
|
2079
2157
|
this.systemInformation.osType = os.type(); // "Windows_NT", "Darwin", etc.
|
|
2080
2158
|
this.systemInformation.osRelease = os.release(); // Kernel version
|
|
2081
2159
|
this.systemInformation.osPlatform = os.platform(); // "win32", "linux", "darwin", etc.
|
|
@@ -2086,6 +2164,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2086
2164
|
// Log the system information
|
|
2087
2165
|
this.log.debug('Host System Information:');
|
|
2088
2166
|
this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
|
|
2167
|
+
this.log.debug(`- User: ${this.systemInformation.user}`);
|
|
2089
2168
|
this.log.debug(`- IPv4 Address: ${this.systemInformation.ipv4Address}`);
|
|
2090
2169
|
this.log.debug(`- IPv6 Address: ${this.systemInformation.ipv6Address}`);
|
|
2091
2170
|
this.log.debug(`- Node.js: ${versionMajor}.${versionMinor}.${versionPatch}`);
|
|
@@ -2267,7 +2346,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2267
2346
|
args.unshift(command);
|
|
2268
2347
|
command = 'sudo';
|
|
2269
2348
|
}
|
|
2270
|
-
this.log.debug(`
|
|
2349
|
+
this.log.debug(`Spawn command ${command} with ${debugStringify(args)}`);
|
|
2271
2350
|
return new Promise((resolve, reject) => {
|
|
2272
2351
|
const childProcess = spawn(command, args, {
|
|
2273
2352
|
stdio: ['inherit', 'pipe', 'pipe'],
|
|
@@ -2431,6 +2510,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2431
2510
|
res.json({});
|
|
2432
2511
|
}
|
|
2433
2512
|
this.matterbridgeInformation.bridgeMode = this.bridgeMode;
|
|
2513
|
+
this.matterbridgeInformation.restartMode = this.restartMode;
|
|
2434
2514
|
this.matterbridgeInformation.debugEnabled = this.debugEnabled;
|
|
2435
2515
|
const response = { wssHost, qrPairingCode, manualPairingCode, systemInformation: this.systemInformation, matterbridgeInformation: this.matterbridgeInformation };
|
|
2436
2516
|
this.log.debug('The frontend sent /api/settings');
|
|
@@ -2500,6 +2580,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2500
2580
|
//console.log(error);
|
|
2501
2581
|
}
|
|
2502
2582
|
data.push({
|
|
2583
|
+
endpoint: registeredDevice.device.number ? registeredDevice.device.number.toString() : '...',
|
|
2503
2584
|
clusterName: clusterServer.name,
|
|
2504
2585
|
clusterId: '0x' + clusterServer.id.toString(16).padStart(2, '0'),
|
|
2505
2586
|
attributeName: key,
|
|
@@ -2508,6 +2589,37 @@ export class Matterbridge extends EventEmitter {
|
|
|
2508
2589
|
});
|
|
2509
2590
|
});
|
|
2510
2591
|
});
|
|
2592
|
+
registeredDevice.device.getChildEndpoints().forEach((childEndpoint) => {
|
|
2593
|
+
const name = registeredDevice.device.getChildEndpointName(childEndpoint);
|
|
2594
|
+
const clusterServers = childEndpoint.getAllClusterServers();
|
|
2595
|
+
clusterServers.forEach((clusterServer) => {
|
|
2596
|
+
Object.entries(clusterServer.attributes).forEach(([key, value]) => {
|
|
2597
|
+
if (clusterServer.name === 'EveHistory')
|
|
2598
|
+
return;
|
|
2599
|
+
//this.log.debug(`***--clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute:${key}(${value.id}) ${value.isFixed} ${value.isWritable} ${value.isWritable}`);
|
|
2600
|
+
let attributeValue;
|
|
2601
|
+
try {
|
|
2602
|
+
if (typeof value.getLocal() === 'object')
|
|
2603
|
+
attributeValue = stringify(value.getLocal());
|
|
2604
|
+
else
|
|
2605
|
+
attributeValue = value.getLocal().toString();
|
|
2606
|
+
}
|
|
2607
|
+
catch (error) {
|
|
2608
|
+
attributeValue = 'Unavailable';
|
|
2609
|
+
this.log.debug(`GetLocal error ${error} in clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute: ${key}(${value.id})`);
|
|
2610
|
+
//console.log(error);
|
|
2611
|
+
}
|
|
2612
|
+
data.push({
|
|
2613
|
+
endpoint: (childEndpoint.number ? childEndpoint.number.toString() : '...') + (name ? ' (' + name + ')' : ''),
|
|
2614
|
+
clusterName: clusterServer.name,
|
|
2615
|
+
clusterId: '0x' + clusterServer.id.toString(16).padStart(2, '0'),
|
|
2616
|
+
attributeName: key,
|
|
2617
|
+
attributeId: '0x' + value.id.toString(16).padStart(2, '0'),
|
|
2618
|
+
attributeValue,
|
|
2619
|
+
});
|
|
2620
|
+
});
|
|
2621
|
+
});
|
|
2622
|
+
});
|
|
2511
2623
|
}
|
|
2512
2624
|
});
|
|
2513
2625
|
res.json(data);
|
|
@@ -2549,13 +2661,25 @@ export class Matterbridge extends EventEmitter {
|
|
|
2549
2661
|
plugin.platform?.log.setLogDebug(this.debugEnabled);
|
|
2550
2662
|
});
|
|
2551
2663
|
}
|
|
2552
|
-
// Handle the command
|
|
2664
|
+
// Handle the command reset from Settings
|
|
2665
|
+
if (command === 'unregister') {
|
|
2666
|
+
await this.unregisterAndShutdownProcess();
|
|
2667
|
+
}
|
|
2668
|
+
// Handle the command reset from Settings
|
|
2669
|
+
if (command === 'reset') {
|
|
2670
|
+
this.shutdownProcessAndReset(); // No await do it asyncronously
|
|
2671
|
+
}
|
|
2672
|
+
// Handle the command factoryreset from Settings
|
|
2673
|
+
if (command === 'factoryreset') {
|
|
2674
|
+
this.shutdownProcessAndFactoryReset(); // No await do it asyncronously
|
|
2675
|
+
}
|
|
2676
|
+
// Handle the command restart from Header
|
|
2553
2677
|
if (command === 'shutdown') {
|
|
2554
|
-
this.shutdownProcess();
|
|
2678
|
+
this.shutdownProcess(); // No await do it asyncronously
|
|
2555
2679
|
}
|
|
2556
2680
|
// Handle the command restart from Header
|
|
2557
2681
|
if (command === 'restart') {
|
|
2558
|
-
this.restartProcess();
|
|
2682
|
+
this.restartProcess(); // No await do it asyncronously
|
|
2559
2683
|
}
|
|
2560
2684
|
// Handle the command update from Header
|
|
2561
2685
|
if (command === 'update') {
|
|
@@ -2643,10 +2767,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2643
2767
|
const plugin = plugins.find((plugin) => plugin.name === param);
|
|
2644
2768
|
if (plugin) {
|
|
2645
2769
|
plugin.enabled = true;
|
|
2770
|
+
plugin.error = undefined;
|
|
2646
2771
|
plugin.loaded = undefined;
|
|
2647
2772
|
plugin.started = undefined;
|
|
2648
2773
|
plugin.configured = undefined;
|
|
2649
2774
|
plugin.connected = undefined;
|
|
2775
|
+
plugin.platform = undefined;
|
|
2776
|
+
plugin.registeredDevices = undefined;
|
|
2650
2777
|
await this.nodeContext?.set('plugins', plugins);
|
|
2651
2778
|
this.log.info(`Enabled plugin ${plg}${param}${nf}`);
|
|
2652
2779
|
}
|
|
@@ -2657,7 +2784,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2657
2784
|
pluginToEnable.platform = await this.loadPlugin(pluginToEnable);
|
|
2658
2785
|
if (pluginToEnable.platform) {
|
|
2659
2786
|
await this.startPlugin(pluginToEnable, 'The plugin has been enabled', true);
|
|
2660
|
-
pluginToEnable.enabled = false;
|
|
2661
2787
|
}
|
|
2662
2788
|
else {
|
|
2663
2789
|
pluginToEnable.enabled = false;
|
|
@@ -2676,21 +2802,26 @@ export class Matterbridge extends EventEmitter {
|
|
|
2676
2802
|
await this.savePluginConfig(pluginToDisable);
|
|
2677
2803
|
}
|
|
2678
2804
|
pluginToDisable.enabled = false;
|
|
2805
|
+
pluginToDisable.error = undefined;
|
|
2679
2806
|
pluginToDisable.loaded = undefined;
|
|
2680
2807
|
pluginToDisable.started = undefined;
|
|
2681
2808
|
pluginToDisable.configured = undefined;
|
|
2682
2809
|
pluginToDisable.connected = undefined;
|
|
2683
2810
|
pluginToDisable.platform = undefined;
|
|
2811
|
+
pluginToDisable.registeredDevices = undefined;
|
|
2684
2812
|
const plugins = await this.nodeContext?.get('plugins');
|
|
2685
2813
|
if (!plugins)
|
|
2686
2814
|
return;
|
|
2687
2815
|
const plugin = plugins.find((plugin) => plugin.name === param);
|
|
2688
2816
|
if (plugin) {
|
|
2689
2817
|
plugin.enabled = false;
|
|
2818
|
+
plugin.error = undefined;
|
|
2690
2819
|
plugin.loaded = undefined;
|
|
2691
2820
|
plugin.started = undefined;
|
|
2692
2821
|
plugin.configured = undefined;
|
|
2693
2822
|
plugin.connected = undefined;
|
|
2823
|
+
plugin.platform = undefined;
|
|
2824
|
+
plugin.registeredDevices = undefined;
|
|
2694
2825
|
await this.nodeContext?.set('plugins', plugins);
|
|
2695
2826
|
this.log.info(`Disabled plugin ${plg}${param}${nf}`);
|
|
2696
2827
|
}
|
|
@@ -2706,7 +2837,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2706
2837
|
if (!useHttps) {
|
|
2707
2838
|
// Listen on HTTP
|
|
2708
2839
|
this.expressServer = this.expressApp.listen(port, () => {
|
|
2709
|
-
this.log.info(`The frontend is
|
|
2840
|
+
this.log.info(`The frontend is listening on ${UNDERLINE}http://${this.systemInformation.ipv4Address}:${port}${UNDERLINEOFF}${rs}`);
|
|
2710
2841
|
});
|
|
2711
2842
|
}
|
|
2712
2843
|
else {
|
|
@@ -2720,7 +2851,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2720
2851
|
// Specify the port to listen on, for example 443 for default HTTPS
|
|
2721
2852
|
const PORT = 443;
|
|
2722
2853
|
httpsServer.listen(PORT, () => {
|
|
2723
|
-
this.log.info(`The frontend is
|
|
2854
|
+
this.log.info(`The frontend is listening on ${UNDERLINE}https://${this.systemInformation.ipv4Address}:${PORT}${UNDERLINEOFF}${rs}`);
|
|
2724
2855
|
});
|
|
2725
2856
|
}
|
|
2726
2857
|
this.log.debug(`Frontend initialized on port ${YELLOW}${port}${db} static ${UNDERLINE}${path.join(this.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
|
|
@@ -2731,6 +2862,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
2731
2862
|
* @returns The attributes of the cluster servers in the device.
|
|
2732
2863
|
*/
|
|
2733
2864
|
getClusterTextFromDevice(device) {
|
|
2865
|
+
const stringifyFixedLabel = (endpoint) => {
|
|
2866
|
+
const labelList = endpoint.getClusterServer(FixedLabelCluster)?.getLabelListAttribute();
|
|
2867
|
+
if (!labelList)
|
|
2868
|
+
return;
|
|
2869
|
+
const composed = labelList.find((entry) => entry.label === 'composed');
|
|
2870
|
+
if (composed)
|
|
2871
|
+
return 'Composed: ' + composed.value;
|
|
2872
|
+
else
|
|
2873
|
+
return ''; //'FixedLabel: ' + labelList.map((entry) => entry.label + ': ' + entry.value).join(' ');
|
|
2874
|
+
};
|
|
2734
2875
|
let attributes = '';
|
|
2735
2876
|
//this.log.debug(`getClusterTextFromDevice: ${device.name}`);
|
|
2736
2877
|
const clusterServers = device.getAllClusterServers();
|
|
@@ -2767,7 +2908,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
2767
2908
|
if (clusterServer.name === 'PressureMeasurement')
|
|
2768
2909
|
attributes += `Pressure: ${clusterServer.getMeasuredValueAttribute()} `;
|
|
2769
2910
|
if (clusterServer.name === 'FlowMeasurement')
|
|
2770
|
-
attributes += `
|
|
2911
|
+
attributes += `Flow: ${clusterServer.getMeasuredValueAttribute()} `;
|
|
2912
|
+
if (clusterServer.name === 'FixedLabel')
|
|
2913
|
+
attributes += `${stringifyFixedLabel(device)} `;
|
|
2771
2914
|
});
|
|
2772
2915
|
return attributes;
|
|
2773
2916
|
}
|
|
@@ -2797,6 +2940,31 @@ function restartProcess() {
|
|
|
2797
2940
|
process.exit();
|
|
2798
2941
|
}
|
|
2799
2942
|
|
|
2943
|
+
import React from 'react';
|
|
2944
|
+
import Form from "@rjsf/core";
|
|
2945
|
+
|
|
2946
|
+
const schema = {
|
|
2947
|
+
title: "Todo",
|
|
2948
|
+
type: "object",
|
|
2949
|
+
required: ["title"],
|
|
2950
|
+
properties: {
|
|
2951
|
+
title: {type: "string", title: "Title", default: "A new task"},
|
|
2952
|
+
done: {type: "boolean", title: "Done?", default: false}
|
|
2953
|
+
}
|
|
2954
|
+
};
|
|
2955
|
+
|
|
2956
|
+
const log = (type) => console.log.bind(console, type);
|
|
2957
|
+
|
|
2958
|
+
function Todo() {
|
|
2959
|
+
return (
|
|
2960
|
+
<Form schema={schema}
|
|
2961
|
+
onChange={log("changed")}
|
|
2962
|
+
onSubmit={log("submitted")}
|
|
2963
|
+
onError={log("errors")} />
|
|
2964
|
+
);
|
|
2965
|
+
}
|
|
2966
|
+
|
|
2967
|
+
export default Todo;
|
|
2800
2968
|
|
|
2801
2969
|
/*
|
|
2802
2970
|
How frontend was created
|