matterbridge 1.3.3 → 1.3.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 +598 -542
- package/README.md +641 -594
- package/dist/cluster/CarbonDioxideConcentrationMeasurementCluster.d.ts +1 -1
- package/dist/cluster/CarbonDioxideConcentrationMeasurementCluster.d.ts.map +1 -1
- package/dist/cluster/CarbonDioxideConcentrationMeasurementCluster.js +1 -1
- package/dist/cluster/CarbonDioxideConcentrationMeasurementCluster.js.map +1 -1
- package/dist/cluster/CarbonMonoxideConcentrationMeasurementCluster.d.ts +1 -1
- package/dist/cluster/CarbonMonoxideConcentrationMeasurementCluster.d.ts.map +1 -1
- package/dist/cluster/CarbonMonoxideConcentrationMeasurementCluster.js +1 -1
- package/dist/cluster/CarbonMonoxideConcentrationMeasurementCluster.js.map +1 -1
- package/dist/cluster/FormaldehydeConcentrationMeasurementCluster.d.ts +1 -1
- package/dist/cluster/FormaldehydeConcentrationMeasurementCluster.d.ts.map +1 -1
- package/dist/cluster/FormaldehydeConcentrationMeasurementCluster.js +1 -1
- package/dist/cluster/FormaldehydeConcentrationMeasurementCluster.js.map +1 -1
- package/dist/cluster/NitrogenDioxideConcentrationMeasurementCluster.d.ts +1 -1
- package/dist/cluster/NitrogenDioxideConcentrationMeasurementCluster.d.ts.map +1 -1
- package/dist/cluster/NitrogenDioxideConcentrationMeasurementCluster.js +1 -1
- package/dist/cluster/NitrogenDioxideConcentrationMeasurementCluster.js.map +1 -1
- package/dist/cluster/OzoneConcentrationMeasurementCluster.d.ts +1 -1
- package/dist/cluster/OzoneConcentrationMeasurementCluster.d.ts.map +1 -1
- package/dist/cluster/OzoneConcentrationMeasurementCluster.js +1 -1
- package/dist/cluster/OzoneConcentrationMeasurementCluster.js.map +1 -1
- package/dist/cluster/Pm10ConcentrationMeasurementCluster.d.ts +1 -1
- package/dist/cluster/Pm10ConcentrationMeasurementCluster.d.ts.map +1 -1
- package/dist/cluster/Pm10ConcentrationMeasurementCluster.js +1 -1
- package/dist/cluster/Pm10ConcentrationMeasurementCluster.js.map +1 -1
- package/dist/cluster/Pm1ConcentrationMeasurementCluster.d.ts +1 -1
- package/dist/cluster/Pm1ConcentrationMeasurementCluster.d.ts.map +1 -1
- package/dist/cluster/Pm1ConcentrationMeasurementCluster.js +1 -1
- package/dist/cluster/Pm1ConcentrationMeasurementCluster.js.map +1 -1
- package/dist/cluster/Pm25ConcentrationMeasurementCluster.d.ts +1 -1
- package/dist/cluster/Pm25ConcentrationMeasurementCluster.d.ts.map +1 -1
- package/dist/cluster/Pm25ConcentrationMeasurementCluster.js +2 -2
- package/dist/cluster/Pm25ConcentrationMeasurementCluster.js.map +1 -1
- package/dist/cluster/RadonConcentrationMeasurementCluster.d.ts +1 -1
- package/dist/cluster/RadonConcentrationMeasurementCluster.d.ts.map +1 -1
- package/dist/cluster/RadonConcentrationMeasurementCluster.js +1 -1
- package/dist/cluster/RadonConcentrationMeasurementCluster.js.map +1 -1
- package/dist/cluster/export.d.ts +25 -0
- package/dist/cluster/export.d.ts.map +1 -0
- package/dist/cluster/export.js +25 -0
- package/dist/cluster/export.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -2
- package/dist/index.js.map +1 -1
- package/dist/log/export.d.ts +2 -0
- package/dist/log/export.d.ts.map +1 -0
- package/dist/log/export.js +2 -0
- package/dist/log/export.js.map +1 -0
- package/dist/matterbridge.d.ts +6 -0
- package/dist/matterbridge.d.ts.map +1 -1
- package/dist/matterbridge.js +172 -306
- package/dist/matterbridge.js.map +1 -1
- package/dist/matterbridgeController.d.ts.map +1 -1
- package/dist/matterbridgeController.js +0 -3
- package/dist/matterbridgeController.js.map +1 -1
- package/dist/matterbridgeDevice.d.ts +213 -4
- package/dist/matterbridgeDevice.d.ts.map +1 -1
- package/dist/matterbridgeDevice.js +129 -42
- package/dist/matterbridgeDevice.js.map +1 -1
- package/dist/storage/export.d.ts +2 -0
- package/dist/storage/export.d.ts.map +1 -0
- package/dist/storage/export.js +2 -0
- package/dist/storage/export.js.map +1 -0
- package/dist/utils/colorUtils.d.ts +61 -0
- package/dist/utils/colorUtils.d.ts.map +1 -0
- package/dist/utils/colorUtils.js +313 -0
- package/dist/utils/colorUtils.js.map +1 -0
- package/dist/utils/export.d.ts +3 -0
- package/dist/utils/export.d.ts.map +1 -0
- package/dist/utils/export.js +3 -0
- package/dist/utils/export.js.map +1 -0
- package/dist/utils/utils.d.ts +73 -0
- package/dist/utils/utils.d.ts.map +1 -0
- package/dist/utils/utils.js +266 -0
- package/dist/utils/utils.js.map +1 -0
- package/dist/utils.d.ts +1 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +4 -0
- package/dist/utils.js.map +1 -1
- package/frontend/build/asset-manifest.json +3 -3
- package/frontend/build/index.html +1 -1
- package/frontend/build/static/js/{main.cbfc6c9b.js → main.c3b5dfce.js} +3 -3
- package/frontend/build/static/js/{main.cbfc6c9b.js.map → main.c3b5dfce.js.map} +1 -1
- package/package.json +34 -20
- package/migrationV8.txt +0 -61
- /package/frontend/build/static/js/{main.cbfc6c9b.js.LICENSE.txt → main.c3b5dfce.js.LICENSE.txt} +0 -0
package/dist/matterbridge.js
CHANGED
|
@@ -35,6 +35,7 @@ import WebSocket, { WebSocketServer } from 'ws';
|
|
|
35
35
|
import { MatterbridgeDevice } from './matterbridgeDevice.js';
|
|
36
36
|
import { shelly_config, somfytahoma_config, zigbee2mqtt_config } from './defaultConfigSchema.js';
|
|
37
37
|
import { BridgedDeviceBasicInformation, BridgedDeviceBasicInformationCluster } from './cluster/BridgedDeviceBasicInformationCluster.js';
|
|
38
|
+
import { logInterfaces } from './utils/utils.js';
|
|
38
39
|
// @project-chip/matter-node.js
|
|
39
40
|
import { CommissioningController, CommissioningServer, MatterServer } from '@project-chip/matter-node.js';
|
|
40
41
|
import { BasicInformationCluster, ClusterServer, FixedLabelCluster, GeneralCommissioning, PowerSourceCluster, ThreadNetworkDiagnosticsCluster, getClusterNameById } from '@project-chip/matter-node.js/cluster';
|
|
@@ -54,6 +55,7 @@ const typ = '\u001B[38;5;207m';
|
|
|
54
55
|
*/
|
|
55
56
|
export class Matterbridge extends EventEmitter {
|
|
56
57
|
systemInformation = {
|
|
58
|
+
interfaceName: '',
|
|
57
59
|
macAddress: '',
|
|
58
60
|
ipv4Address: '',
|
|
59
61
|
ipv6Address: '',
|
|
@@ -100,6 +102,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
100
102
|
port = 5540;
|
|
101
103
|
log;
|
|
102
104
|
hasCleanupStarted = false;
|
|
105
|
+
plugins = new Map();
|
|
106
|
+
devices = new Map();
|
|
103
107
|
registeredPlugins = [];
|
|
104
108
|
registeredDevices = [];
|
|
105
109
|
nodeStorage;
|
|
@@ -256,7 +260,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
256
260
|
- bridge: start Matterbridge in bridge mode
|
|
257
261
|
- childbridge: start Matterbridge in childbridge mode
|
|
258
262
|
- frontend [port]: start the frontend on the given port (default 8283)
|
|
259
|
-
- debug: enable debug mode (default false)
|
|
263
|
+
- debug: enable the Matterbridge debug mode (default false)
|
|
264
|
+
- matterlogger: set the matter.js logger level: debug | info | notice | warn | error | fatal (default info)
|
|
260
265
|
- reset: remove the commissioning for Matterbridge (bridge mode). Shutdown Matterbridge before using it!
|
|
261
266
|
- factoryreset: remove all commissioning information and reset all internal storages. Shutdown Matterbridge before using it!
|
|
262
267
|
- list: list the registered plugins
|
|
@@ -305,9 +310,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
305
310
|
}
|
|
306
311
|
// Log system info and create .matterbridge directory
|
|
307
312
|
await this.logNodeAndSystemInfo();
|
|
308
|
-
this.log.info(
|
|
309
|
-
// eslint-disable-next-line max-len
|
|
310
|
-
`Matterbridge version ${this.matterbridgeVersion} mode ${hasParameter('bridge') ? 'bridge' : ''}${hasParameter('childbridge') ? 'childbridge' : ''}${hasParameter('controller') ? 'controller' : ''} ` +
|
|
313
|
+
this.log.info(`Matterbridge version ${this.matterbridgeVersion} mode ${hasParameter('bridge') ? 'bridge' : ''}${hasParameter('childbridge') ? 'childbridge' : ''}${hasParameter('controller') ? 'controller' : ''} ` +
|
|
311
314
|
`${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}running on ${this.systemInformation.osType} ${this.systemInformation.osRelease} ${this.systemInformation.osPlatform} ${this.systemInformation.osArch}`);
|
|
312
315
|
// Check node version and throw error
|
|
313
316
|
requireMinNodeVersion(18);
|
|
@@ -380,7 +383,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
380
383
|
process.exit(0);
|
|
381
384
|
}
|
|
382
385
|
if (hasParameter('logstorage')) {
|
|
383
|
-
this.log.info(`${plg}
|
|
386
|
+
this.log.info(`${plg}Matterbridge${nf} storage log`);
|
|
384
387
|
await this.nodeContext?.logStorage();
|
|
385
388
|
for (const plugin of this.registeredPlugins) {
|
|
386
389
|
this.log.info(`${plg}${plugin.name}${nf} storage log`);
|
|
@@ -389,6 +392,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
389
392
|
this.emit('shutdown');
|
|
390
393
|
process.exit(0);
|
|
391
394
|
}
|
|
395
|
+
if (hasParameter('loginterfaces')) {
|
|
396
|
+
this.log.info(`${plg}Matterbridge${nf} network interfaces log`);
|
|
397
|
+
logInterfaces();
|
|
398
|
+
this.emit('shutdown');
|
|
399
|
+
process.exit(0);
|
|
400
|
+
}
|
|
392
401
|
if (getParameter('add')) {
|
|
393
402
|
this.log.debug(`Registering plugin ${getParameter('add')}`);
|
|
394
403
|
await this.executeCommandLine(getParameter('add'), 'add');
|
|
@@ -507,10 +516,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
507
516
|
continue;
|
|
508
517
|
}
|
|
509
518
|
plugin.error = false;
|
|
519
|
+
plugin.locked = false;
|
|
510
520
|
plugin.loaded = false;
|
|
511
521
|
plugin.started = false;
|
|
512
522
|
plugin.configured = false;
|
|
513
523
|
plugin.connected = undefined;
|
|
524
|
+
plugin.registeredDevices = undefined;
|
|
525
|
+
plugin.addedDevices = undefined;
|
|
514
526
|
plugin.qrPairingCode = undefined;
|
|
515
527
|
plugin.manualPairingCode = undefined;
|
|
516
528
|
this.loadPlugin(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
|
|
@@ -537,10 +549,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
537
549
|
continue;
|
|
538
550
|
}
|
|
539
551
|
plugin.error = false;
|
|
552
|
+
plugin.locked = false;
|
|
540
553
|
plugin.loaded = false;
|
|
541
554
|
plugin.started = false;
|
|
542
555
|
plugin.configured = false;
|
|
543
556
|
plugin.connected = false;
|
|
557
|
+
plugin.registeredDevices = undefined;
|
|
558
|
+
plugin.addedDevices = undefined;
|
|
544
559
|
plugin.qrPairingCode = (await plugin.nodeContext?.get('qrPairingCode', undefined)) ?? undefined;
|
|
545
560
|
plugin.manualPairingCode = (await plugin.nodeContext?.get('manualPairingCode', undefined)) ?? undefined;
|
|
546
561
|
this.loadPlugin(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
|
|
@@ -549,6 +564,28 @@ export class Matterbridge extends EventEmitter {
|
|
|
549
564
|
return;
|
|
550
565
|
}
|
|
551
566
|
}
|
|
567
|
+
async savePluginsToStorage() {
|
|
568
|
+
if (!this.nodeContext) {
|
|
569
|
+
this.log.error('loadPluginsFromStorage() error: the node context is not initialized');
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
// Convert the map to an array
|
|
573
|
+
// const pluginArray = Array.from(this.plugins.values());
|
|
574
|
+
// await this.nodeContext.set('plugins', pluginArray);
|
|
575
|
+
// TODO remove after migration done
|
|
576
|
+
await this.nodeContext.set('plugins', await this.getBaseRegisteredPlugins());
|
|
577
|
+
}
|
|
578
|
+
async loadPluginsFromStorage() {
|
|
579
|
+
if (!this.nodeContext) {
|
|
580
|
+
this.log.error('loadPluginsFromStorage() error: the node context is not initialized');
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
// Load the array from storage and convert it back to a map
|
|
584
|
+
// const pluginArray = await this.nodeContext.get<RegisteredPlugin[]>('plugins', []);
|
|
585
|
+
// for (const plugin of pluginArray) this.plugins.set(plugin.name, plugin);
|
|
586
|
+
// TODO remove after migration done
|
|
587
|
+
this.registeredPlugins = await this.nodeContext.get('plugins', []);
|
|
588
|
+
}
|
|
552
589
|
/**
|
|
553
590
|
* Resolves the name of a plugin by loading and parsing its package.json file.
|
|
554
591
|
* @param pluginPath - The path to the plugin or the path to the plugin's package.json file.
|
|
@@ -573,7 +610,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
573
610
|
this.log.debug(`Package.json not found at ${packageJsonPath}`);
|
|
574
611
|
this.log.debug(`Trying at ${this.globalModulesDirectory}`);
|
|
575
612
|
packageJsonPath = path.join(this.globalModulesDirectory, pluginPath);
|
|
576
|
-
// this.log.debug(`Got ${packageJsonPath}`);
|
|
577
613
|
}
|
|
578
614
|
try {
|
|
579
615
|
// Load the package.json of the plugin
|
|
@@ -1236,8 +1272,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1236
1272
|
await fs.access(configFile);
|
|
1237
1273
|
const data = await fs.readFile(configFile, 'utf8');
|
|
1238
1274
|
const config = JSON.parse(data);
|
|
1239
|
-
// this.log.debug(`Config file found: ${configFile}.\nConfig:${rs}\n`, config);
|
|
1240
1275
|
this.log.debug(`Config file found: ${configFile}.`);
|
|
1276
|
+
// this.log.debug(`Config file found: ${configFile}.\nConfig:${rs}\n`, config);
|
|
1241
1277
|
/* The first time a plugin is added to the system, the config file is created with the plugin name and type "".*/
|
|
1242
1278
|
config.name = plugin.name;
|
|
1243
1279
|
config.type = plugin.type;
|
|
@@ -1547,7 +1583,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1547
1583
|
this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
|
|
1548
1584
|
for (const nodeId of nodeIds) {
|
|
1549
1585
|
this.log.info(`***Connecting to commissioned node: ${nodeId}`);
|
|
1550
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1551
1586
|
const node = await this.commissioningController.connectNode(nodeId, {
|
|
1552
1587
|
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)}`),
|
|
1553
1588
|
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)}`),
|
|
@@ -1590,9 +1625,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1590
1625
|
});
|
|
1591
1626
|
this.log.warn(`Cluster: ${cluster.name} attributes:`);
|
|
1592
1627
|
attributes.forEach((attribute) => {
|
|
1593
|
-
this.log.info(
|
|
1594
|
-
// eslint-disable-next-line max-len
|
|
1595
|
-
`- 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}`);
|
|
1628
|
+
this.log.info(`- 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}`);
|
|
1596
1629
|
});
|
|
1597
1630
|
// Log PowerSourceCluster
|
|
1598
1631
|
cluster = PowerSourceCluster;
|
|
@@ -1601,9 +1634,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1601
1634
|
});
|
|
1602
1635
|
this.log.warn(`Cluster: ${cluster.name} attributes:`);
|
|
1603
1636
|
attributes.forEach((attribute) => {
|
|
1604
|
-
this.log.info(
|
|
1605
|
-
// eslint-disable-next-line max-len
|
|
1606
|
-
`- 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}`);
|
|
1637
|
+
this.log.info(`- 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}`);
|
|
1607
1638
|
});
|
|
1608
1639
|
// Log ThreadNetworkDiagnostics
|
|
1609
1640
|
cluster = ThreadNetworkDiagnosticsCluster;
|
|
@@ -1612,9 +1643,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1612
1643
|
});
|
|
1613
1644
|
this.log.warn(`Cluster: ${cluster.name} attributes:`);
|
|
1614
1645
|
attributes.forEach((attribute) => {
|
|
1615
|
-
this.log.info(
|
|
1616
|
-
// eslint-disable-next-line max-len
|
|
1617
|
-
`- 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}`);
|
|
1646
|
+
this.log.info(`- 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}`);
|
|
1618
1647
|
});
|
|
1619
1648
|
}
|
|
1620
1649
|
}
|
|
@@ -1635,7 +1664,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1635
1664
|
let failCount = 0;
|
|
1636
1665
|
const startMatterInterval = setInterval(async () => {
|
|
1637
1666
|
for (const plugin of this.registeredPlugins) {
|
|
1638
|
-
// if (!plugin.enabled || plugin.error) continue;
|
|
1639
1667
|
// new code to not start the bridge if one plugin is in error cause the controllers will delete the devices loosing all the configuration
|
|
1640
1668
|
if (!plugin.enabled)
|
|
1641
1669
|
continue;
|
|
@@ -1661,53 +1689,20 @@ export class Matterbridge extends EventEmitter {
|
|
|
1661
1689
|
this.log.debug('***Cleared startMatterInterval interval for Matterbridge');
|
|
1662
1690
|
await this.startMatterServer();
|
|
1663
1691
|
this.log.info('Matter server started');
|
|
1664
|
-
|
|
1692
|
+
// Configure the plugins
|
|
1665
1693
|
/*
|
|
1666
|
-
const
|
|
1667
|
-
|
|
1668
|
-
console.log('GeneralDiagnosticsCluster', gdcCluster);
|
|
1669
|
-
const net = gdcCluster.getNetworkInterfacesAttribute();
|
|
1670
|
-
console.log('NetworkInterfaces', net);
|
|
1671
|
-
|
|
1672
|
-
// We have like "30:f6:ef:69:2b:c5" in this.systemInformation.macAddress
|
|
1673
|
-
const macArray = this.systemInformation.macAddress.split(':').map((hex) => parseInt(hex, 16));
|
|
1674
|
-
let hardwareAddress = new Uint8Array(macArray);
|
|
1675
|
-
if (hardwareAddress.length === 6) hardwareAddress = Uint8Array.from([0, 0, ...hardwareAddress]);
|
|
1676
|
-
// We have like "192.168.1.189" in this.systemInformation.ipv4Address
|
|
1677
|
-
const ipv4Array = this.systemInformation.ipv4Address.split('.').map((num) => parseInt(num));
|
|
1678
|
-
const iPv4Address = new Uint8Array(ipv4Array);
|
|
1679
|
-
// We have like "fd78:cbf8:4939:746:d555:85a9:74f6:9c6" in this.systemInformation.ipv6Address
|
|
1680
|
-
const ipv6Groups = this.systemInformation.ipv6Address.split(':');
|
|
1681
|
-
const ipv6Array = [];
|
|
1682
|
-
for (const group of ipv6Groups) {
|
|
1683
|
-
const decimal = parseInt(group, 16);
|
|
1684
|
-
ipv6Array.push(decimal >> 8); // High byte
|
|
1685
|
-
ipv6Array.push(decimal & 0xff); // Low byte
|
|
1686
|
-
}
|
|
1687
|
-
const iPv6Address = new Uint8Array(ipv6Array);
|
|
1688
|
-
this.log.warn(`GeneralDiagnosticsCluster for hardwareAddress ${this.systemInformation.macAddress} => ${debugStringify(hardwareAddress)}`);
|
|
1689
|
-
this.log.warn(`GeneralDiagnosticsCluster for iPv4Address ${this.systemInformation.ipv4Address} => ${debugStringify(iPv4Address)}`);
|
|
1690
|
-
this.log.warn(`GeneralDiagnosticsCluster for iPv6Address ${this.systemInformation.ipv6Address} => ${debugStringify(iPv6Address)}`);
|
|
1694
|
+
for (const plugin of this.registeredPlugins) {
|
|
1695
|
+
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error) continue;
|
|
1691
1696
|
try {
|
|
1692
|
-
|
|
1693
|
-
{
|
|
1694
|
-
name: 'eth0',
|
|
1695
|
-
isOperational: true,
|
|
1696
|
-
offPremiseServicesReachableIPv4: null,
|
|
1697
|
-
offPremiseServicesReachableIPv6: null,
|
|
1698
|
-
hardwareAddress,
|
|
1699
|
-
iPv4Addresses: [iPv4Address],
|
|
1700
|
-
iPv6Addresses: [iPv6Address],
|
|
1701
|
-
type: GeneralDiagnostics.InterfaceType.Ethernet,
|
|
1702
|
-
},
|
|
1703
|
-
]);
|
|
1704
|
-
const net = gdcCluster.getNetworkInterfacesAttribute();
|
|
1705
|
-
console.log('NetworkInterfaces', net);
|
|
1697
|
+
this.configurePlugin(plugin); // No await do it asyncronously
|
|
1706
1698
|
} catch (error) {
|
|
1707
|
-
|
|
1699
|
+
plugin.error = true;
|
|
1700
|
+
this.log.error(`Error configuring plugin ${plg}${plugin.name}${er}`, error);
|
|
1708
1701
|
}
|
|
1709
1702
|
}
|
|
1710
1703
|
*/
|
|
1704
|
+
// Show the QR code for commissioning or log the already commissioned message
|
|
1705
|
+
await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
|
|
1711
1706
|
// Setting reachability to true
|
|
1712
1707
|
setTimeout(() => {
|
|
1713
1708
|
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
@@ -1723,14 +1718,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
1723
1718
|
// addDevice and addBridgedDeevice create the commissionig servers and add the devices to the the commissioning server or to the aggregator
|
|
1724
1719
|
// Plugins are configured by callback when the plugin is commissioned
|
|
1725
1720
|
// Start the interval to check if all plugins are loaded and started and so start the matter server
|
|
1726
|
-
// TODO set a counter or a timeout
|
|
1727
1721
|
this.log.debug('***Starting start matter interval in childbridge mode...');
|
|
1728
1722
|
let failCount = 0;
|
|
1729
1723
|
const startMatterInterval = setInterval(async () => {
|
|
1730
1724
|
let allStarted = true;
|
|
1731
|
-
// this.registeredPlugins.forEach((plugin) => {
|
|
1732
1725
|
for (const plugin of this.registeredPlugins) {
|
|
1733
|
-
// if (!plugin.enabled || plugin.error) return;
|
|
1734
1726
|
// new code to not start the bridge if one plugin is in error cause the controllers will delete the devices loosing all the configuration
|
|
1735
1727
|
if (!plugin.enabled)
|
|
1736
1728
|
continue;
|
|
@@ -2093,9 +2085,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2093
2085
|
const info = commissioningServer.getActiveSessionInformation(fabricIndex);
|
|
2094
2086
|
let connected = false;
|
|
2095
2087
|
info.forEach((session) => {
|
|
2096
|
-
this.log.info(
|
|
2097
|
-
// eslint-disable-next-line max-len
|
|
2098
|
-
`*Active session changed on fabric ${zb}${fabricIndex}${nf} id ${zb}${session.fabric?.fabricId}${nf} vendor ${zb}${session.fabric?.rootVendorId}${nf} ${this.getVendorIdName(session.fabric?.rootVendorId)} ${session.fabric?.label} for ${plg}${pluginName}${nf}`, debugStringify(session));
|
|
2088
|
+
this.log.info(`*Active session changed on fabric ${zb}${fabricIndex}${nf} id ${zb}${session.fabric?.fabricId}${nf} vendor ${zb}${session.fabric?.rootVendorId}${nf} ${this.getVendorIdName(session.fabric?.rootVendorId)} ${session.fabric?.label} for ${plg}${pluginName}${nf}`, debugStringify(session));
|
|
2099
2089
|
if (session.isPeerActive === true && session.secure === true && session.numberOfActiveSubscriptions >= 1) {
|
|
2100
2090
|
this.log.info(`*Controller ${zb}${session.fabric?.rootVendorId}${nf} ${this.getVendorIdName(session.fabric?.rootVendorId)} ${session.fabric?.label} connected to ${plg}${pluginName}${nf} on session ${session.name}`);
|
|
2101
2091
|
connected = true;
|
|
@@ -2172,48 +2162,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2172
2162
|
}
|
|
2173
2163
|
},
|
|
2174
2164
|
});
|
|
2175
|
-
/*
|
|
2176
|
-
const gdcCluster = commissioningServer.getRootClusterServer(GeneralDiagnosticsCluster);
|
|
2177
|
-
if (gdcCluster) {
|
|
2178
|
-
// console.log('GeneralDiagnosticsCluster found for', plg, pluginName, db);
|
|
2179
|
-
// console.log('GeneralDiagnosticsCluster', gdcCluster);
|
|
2180
|
-
// We have like "30:f6:ef:69:2b:c5" in this.systemInformation.macAddress
|
|
2181
|
-
const macArray = this.systemInformation.macAddress.split(':').map((hex) => parseInt(hex, 16));
|
|
2182
|
-
let hardwareAddress = new Uint8Array(macArray);
|
|
2183
|
-
if (hardwareAddress.length === 6) hardwareAddress = Uint8Array.from([0, 0, ...hardwareAddress]);
|
|
2184
|
-
// We have like "192.168.1.189" in this.systemInformation.ipv4Address
|
|
2185
|
-
const ipv4Array = this.systemInformation.ipv4Address.split('.').map((num) => parseInt(num));
|
|
2186
|
-
const iPv4Address = new Uint8Array(ipv4Array);
|
|
2187
|
-
// We have like "fd78:cbf8:4939:746:d555:85a9:74f6:9c6" in this.systemInformation.ipv6Address
|
|
2188
|
-
const ipv6Groups = this.systemInformation.ipv6Address.split(':');
|
|
2189
|
-
const ipv6Array = [];
|
|
2190
|
-
for (const group of ipv6Groups) {
|
|
2191
|
-
const decimal = parseInt(group, 16);
|
|
2192
|
-
ipv6Array.push(decimal >> 8); // High byte
|
|
2193
|
-
ipv6Array.push(decimal & 0xff); // Low byte
|
|
2194
|
-
}
|
|
2195
|
-
const iPv6Address = new Uint8Array(ipv6Array);
|
|
2196
|
-
this.log.debug(`GeneralDiagnosticsCluster for ${plg}${pluginName}${db} hardwareAddress ${this.systemInformation.macAddress} => ${debugStringify(hardwareAddress)}`);
|
|
2197
|
-
this.log.debug(`GeneralDiagnosticsCluster for ${plg}${pluginName}${db} iPv4Address ${this.systemInformation.ipv4Address} => ${debugStringify(iPv4Address)}`);
|
|
2198
|
-
this.log.debug(`GeneralDiagnosticsCluster for ${plg}${pluginName}${db} iPv6Address ${this.systemInformation.ipv6Address} => ${debugStringify(iPv6Address)}`);
|
|
2199
|
-
try {
|
|
2200
|
-
gdcCluster.setNetworkInterfacesAttribute([
|
|
2201
|
-
{
|
|
2202
|
-
name: 'eth0',
|
|
2203
|
-
isOperational: true,
|
|
2204
|
-
offPremiseServicesReachableIPv4: null,
|
|
2205
|
-
offPremiseServicesReachableIPv6: null,
|
|
2206
|
-
hardwareAddress,
|
|
2207
|
-
iPv4Addresses: [iPv4Address],
|
|
2208
|
-
iPv6Addresses: [iPv6Address],
|
|
2209
|
-
type: GeneralDiagnostics.InterfaceType.Ethernet,
|
|
2210
|
-
},
|
|
2211
|
-
]);
|
|
2212
|
-
} catch (error) {
|
|
2213
|
-
this.log.error(`GeneralDiagnosticsCluster.setNetworkInterfacesAttribute for ${plg}${pluginName}${er} error:`, error);
|
|
2214
|
-
}
|
|
2215
|
-
} else this.log.warn(`*GeneralDiagnosticsCluster not found for ${plg}${pluginName}${wr}`);
|
|
2216
|
-
*/
|
|
2217
2165
|
commissioningServer.addCommandHandler('testEventTrigger', async ({ request: { enableKey, eventTrigger } }) => this.log.info(`testEventTrigger called on GeneralDiagnostic cluster: ${enableKey} ${eventTrigger}`));
|
|
2218
2166
|
return commissioningServer;
|
|
2219
2167
|
}
|
|
@@ -2323,16 +2271,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
2323
2271
|
const networkInterfaces = os.networkInterfaces();
|
|
2324
2272
|
this.systemInformation.ipv4Address = 'Not found';
|
|
2325
2273
|
this.systemInformation.ipv6Address = 'Not found';
|
|
2326
|
-
for (const interfaceDetails of Object.
|
|
2274
|
+
for (const [interfaceName, interfaceDetails] of Object.entries(networkInterfaces)) {
|
|
2327
2275
|
if (!interfaceDetails) {
|
|
2328
2276
|
break;
|
|
2329
2277
|
}
|
|
2330
2278
|
for (const detail of interfaceDetails) {
|
|
2331
2279
|
if (detail.family === 'IPv4' && !detail.internal && this.systemInformation.ipv4Address === 'Not found') {
|
|
2280
|
+
this.systemInformation.interfaceName = interfaceName;
|
|
2332
2281
|
this.systemInformation.ipv4Address = detail.address;
|
|
2333
2282
|
this.systemInformation.macAddress = detail.mac;
|
|
2334
2283
|
}
|
|
2335
2284
|
else if (detail.family === 'IPv6' && !detail.internal && this.systemInformation.ipv6Address === 'Not found') {
|
|
2285
|
+
this.systemInformation.interfaceName = interfaceName;
|
|
2336
2286
|
this.systemInformation.ipv6Address = detail.address;
|
|
2337
2287
|
this.systemInformation.macAddress = detail.mac;
|
|
2338
2288
|
}
|
|
@@ -2361,6 +2311,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2361
2311
|
this.log.debug('Host System Information:');
|
|
2362
2312
|
this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
|
|
2363
2313
|
this.log.debug(`- User: ${this.systemInformation.user}`);
|
|
2314
|
+
this.log.debug(`- Interface: ${this.systemInformation.interfaceName}`);
|
|
2364
2315
|
this.log.debug(`- MAC Address: ${this.systemInformation.macAddress}`);
|
|
2365
2316
|
this.log.debug(`- IPv4 Address: ${this.systemInformation.ipv4Address}`);
|
|
2366
2317
|
this.log.debug(`- IPv6 Address: ${this.systemInformation.ipv6Address}`);
|
|
@@ -2532,9 +2483,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
2532
2483
|
type: plugin.type,
|
|
2533
2484
|
name: plugin.name,
|
|
2534
2485
|
version: plugin.version,
|
|
2535
|
-
latestVersion: plugin.latestVersion,
|
|
2536
2486
|
description: plugin.description,
|
|
2537
2487
|
author: plugin.author,
|
|
2488
|
+
latestVersion: plugin.latestVersion,
|
|
2489
|
+
locked: plugin.locked,
|
|
2538
2490
|
error: plugin.error,
|
|
2539
2491
|
enabled: plugin.enabled,
|
|
2540
2492
|
loaded: plugin.loaded,
|
|
@@ -2542,11 +2494,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
2542
2494
|
configured: plugin.configured,
|
|
2543
2495
|
paired: plugin.paired,
|
|
2544
2496
|
connected: plugin.connected,
|
|
2497
|
+
fabricInfo: plugin.fabricInfo,
|
|
2545
2498
|
registeredDevices: plugin.registeredDevices,
|
|
2499
|
+
addedDevices: plugin.addedDevices,
|
|
2546
2500
|
qrPairingCode: plugin.qrPairingCode,
|
|
2547
2501
|
manualPairingCode: plugin.manualPairingCode,
|
|
2548
|
-
// configJson: includeConfigSchema ? await this.loadPluginConfig(plugin) : {},
|
|
2549
|
-
// schemaJson: includeConfigSchema ? await this.loadPluginSchema(plugin) : {},
|
|
2550
2502
|
configJson: includeConfigSchema ? plugin.configJson : {},
|
|
2551
2503
|
schemaJson: includeConfigSchema ? plugin.schemaJson : {},
|
|
2552
2504
|
});
|
|
@@ -2662,6 +2614,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2662
2614
|
const cleanMessage = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
2663
2615
|
// Remove leading asterisks from the message
|
|
2664
2616
|
const finalMessage = cleanMessage.replace(/^\*+/, '');
|
|
2617
|
+
// Send the message to all connected clients
|
|
2665
2618
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2666
2619
|
if (client.readyState === WebSocket.OPEN) {
|
|
2667
2620
|
client.send(JSON.stringify({ type, subType, message: finalMessage }));
|
|
@@ -2674,52 +2627,33 @@ export class Matterbridge extends EventEmitter {
|
|
|
2674
2627
|
* @param port The port number to run the frontend server on. Default is 3000.
|
|
2675
2628
|
*/
|
|
2676
2629
|
async initializeFrontend(port = 8283) {
|
|
2630
|
+
if (hasParameter('test_https')) {
|
|
2631
|
+
await this.initializeHttpsFrontend(8443);
|
|
2632
|
+
return;
|
|
2633
|
+
}
|
|
2677
2634
|
this.log.debug(`Initializing the frontend on port ${YELLOW}${port}${db} static ${UNDERLINE}${path.join(this.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
|
|
2635
|
+
// Create a WebSocket server
|
|
2678
2636
|
const wssPort = 8284;
|
|
2679
|
-
const
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
if (!useHttps) {
|
|
2683
|
-
// Create a WebSocket server no certificate required
|
|
2684
|
-
this.webSocketServer = new WebSocketServer({ port: wssPort });
|
|
2685
|
-
// this.log.info(`WebSocket server listening on ${UNDERLINE}${wssHost}${UNDERLINEOFF}${rs}`);
|
|
2686
|
-
}
|
|
2687
|
-
else {
|
|
2688
|
-
// Define the options for HTTPS server
|
|
2689
|
-
// openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout mykey.key -out mycert.pem -config openssl.cnf
|
|
2690
|
-
// For wss connect the browser to https://laptop5_luca:8284/ https://192.168.1.189/log and accept the certificate
|
|
2691
|
-
const serverOptions = {
|
|
2692
|
-
cert: await fs.readFile(path.join(this.rootDirectory, 'frontend/certificates/mycert.pem')), // Ensure the path is correct
|
|
2693
|
-
key: await fs.readFile(path.join(this.rootDirectory, 'frontend/certificates/mykey.key')), // Ensure the path is correct
|
|
2694
|
-
// cert: await fs.readFile(path.join(this.rootDirectory, 'frontend/certificates/laptop5_luca.pem')), // Ensure the path is correct
|
|
2695
|
-
// key: await fs.readFile(path.join(this.rootDirectory, 'frontend/certificates/laptop5_luca.key')), // Ensure the path is correct
|
|
2696
|
-
};
|
|
2697
|
-
// Create an HTTPS server
|
|
2698
|
-
const httpsServer = https.createServer(serverOptions);
|
|
2699
|
-
// Attach WebSocket server to HTTPS server
|
|
2700
|
-
this.webSocketServer = new WebSocketServer({ server: httpsServer });
|
|
2701
|
-
// Listen on a specific port
|
|
2702
|
-
httpsServer.listen(wssPort, () => {
|
|
2703
|
-
this.log.info(`WebSocket server listening on ${UNDERLINE}${wssHost}${UNDERLINEOFF}${rs}`);
|
|
2704
|
-
});
|
|
2705
|
-
}
|
|
2637
|
+
const wssHost = `ws://${this.systemInformation.ipv4Address}:${wssPort}`;
|
|
2638
|
+
this.webSocketServer = new WebSocketServer({ port: wssPort, host: this.systemInformation.ipv4Address });
|
|
2639
|
+
this.log.debug(`WebSocket server created on ${UNDERLINE}${wssHost}${UNDERLINEOFF}${rs}`);
|
|
2706
2640
|
this.webSocketServer.on('connection', (ws) => {
|
|
2707
2641
|
this.log.info('WebSocketServer client connected');
|
|
2708
2642
|
this.log.setGlobalCallback(this.wssSendMessage.bind(this));
|
|
2709
|
-
this.log.debug('WebSocketServer
|
|
2643
|
+
this.log.debug('WebSocketServer logger callback added');
|
|
2710
2644
|
this.wssSendMessage('Matterbridge', 'info', 'WebSocketServer client connected to Matterbridge');
|
|
2711
2645
|
ws.on('message', (message) => {
|
|
2712
|
-
this.log.info(`WebSocket
|
|
2646
|
+
this.log.info(`WebSocket client sent a message => ${message}`);
|
|
2713
2647
|
});
|
|
2714
2648
|
ws.on('close', () => {
|
|
2715
2649
|
this.log.info('WebSocket client disconnected');
|
|
2716
2650
|
if (this.webSocketServer?.clients.size === 0) {
|
|
2717
2651
|
this.log.setGlobalCallback(undefined);
|
|
2718
|
-
this.log.debug('WebSocket
|
|
2652
|
+
this.log.debug('All WebSocket client disconnected. WebSocketServer logger callback removed');
|
|
2719
2653
|
}
|
|
2720
2654
|
});
|
|
2721
2655
|
ws.on('error', (error) => {
|
|
2722
|
-
this.log.error(`WebSocket error: ${error}`);
|
|
2656
|
+
this.log.error(`WebSocket client error: ${error}`);
|
|
2723
2657
|
});
|
|
2724
2658
|
});
|
|
2725
2659
|
this.webSocketServer.on('error', (ws, error) => {
|
|
@@ -2733,6 +2667,24 @@ export class Matterbridge extends EventEmitter {
|
|
|
2733
2667
|
// Serve React build directory
|
|
2734
2668
|
this.expressApp = express();
|
|
2735
2669
|
this.expressApp.use(express.static(path.join(this.rootDirectory, 'frontend/build')));
|
|
2670
|
+
// Listen on HTTP
|
|
2671
|
+
this.expressServer = this.expressApp.listen(port, () => {
|
|
2672
|
+
this.log.info(`The frontend is listening on ${UNDERLINE}http://${this.systemInformation.ipv4Address}:${port}${UNDERLINEOFF}${rs}`);
|
|
2673
|
+
this.log.debug(`The frontend is listening on ${UNDERLINE}http://[${this.systemInformation.ipv6Address}]:${port}${UNDERLINEOFF}${rs}`);
|
|
2674
|
+
});
|
|
2675
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2676
|
+
this.expressServer.on('error', (error) => {
|
|
2677
|
+
this.log.error(`Frontend error listening on ${UNDERLINE}http://${this.systemInformation.ipv4Address}:${port}${UNDERLINEOFF}${rs}`);
|
|
2678
|
+
switch (error.code) {
|
|
2679
|
+
case 'EACCES':
|
|
2680
|
+
this.log.error(`Port ${port} requires elevated privileges`);
|
|
2681
|
+
break;
|
|
2682
|
+
case 'EADDRINUSE':
|
|
2683
|
+
this.log.error(`Port ${port} is already in use`);
|
|
2684
|
+
break;
|
|
2685
|
+
}
|
|
2686
|
+
process.exit(1);
|
|
2687
|
+
});
|
|
2736
2688
|
// Endpoint to validate login code
|
|
2737
2689
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
2738
2690
|
const { password } = req.body;
|
|
@@ -2783,13 +2735,15 @@ export class Matterbridge extends EventEmitter {
|
|
|
2783
2735
|
this.matterbridgeInformation.matterbridgeConnected = this.matterbridgeConnected;
|
|
2784
2736
|
// this.matterbridgeInformation.matterbridgeFabricInfo = this.matterbridgeFabricInfo;
|
|
2785
2737
|
const response = { wssHost, qrPairingCode, manualPairingCode, systemInformation: this.systemInformation, matterbridgeInformation: this.matterbridgeInformation };
|
|
2786
|
-
this.log.debug('Response:', debugStringify(response));
|
|
2738
|
+
// this.log.debug('Response:', debugStringify(response));
|
|
2787
2739
|
res.json(response);
|
|
2788
2740
|
});
|
|
2789
2741
|
// Endpoint to provide plugins
|
|
2790
2742
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
2791
2743
|
this.log.debug('The frontend sent /api/plugins');
|
|
2792
|
-
|
|
2744
|
+
const response = await this.getBaseRegisteredPlugins(true);
|
|
2745
|
+
// this.log.debug('Response:', debugStringify(response));
|
|
2746
|
+
res.json(response);
|
|
2793
2747
|
});
|
|
2794
2748
|
// Endpoint to provide devices
|
|
2795
2749
|
this.expressApp.get('/api/devices', (req, res) => {
|
|
@@ -2816,6 +2770,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2816
2770
|
cluster: cluster,
|
|
2817
2771
|
});
|
|
2818
2772
|
});
|
|
2773
|
+
// this.log.debug('Response:', debugStringify(data));
|
|
2819
2774
|
res.json(data);
|
|
2820
2775
|
});
|
|
2821
2776
|
// Endpoint to provide the cluster servers of the devices
|
|
@@ -2946,7 +2901,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2946
2901
|
plugin.platform?.log.setLogDebug(this.debugEnabled);
|
|
2947
2902
|
});
|
|
2948
2903
|
}
|
|
2949
|
-
// Handle the command
|
|
2904
|
+
// Handle the command unregister from Settings
|
|
2950
2905
|
if (command === 'unregister') {
|
|
2951
2906
|
await this.unregisterAndShutdownProcess();
|
|
2952
2907
|
}
|
|
@@ -2958,7 +2913,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2958
2913
|
if (command === 'factoryreset') {
|
|
2959
2914
|
this.shutdownProcessAndFactoryReset(); // No await do it asyncronously
|
|
2960
2915
|
}
|
|
2961
|
-
// Handle the command
|
|
2916
|
+
// Handle the command shutdown from Header
|
|
2962
2917
|
if (command === 'shutdown') {
|
|
2963
2918
|
this.shutdownProcess(); // No await do it asyncronously
|
|
2964
2919
|
}
|
|
@@ -2980,7 +2935,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2980
2935
|
}
|
|
2981
2936
|
this.updateProcess();
|
|
2982
2937
|
}
|
|
2983
|
-
// Handle the command
|
|
2938
|
+
// Handle the command saveconfig from Home
|
|
2984
2939
|
if (command === 'saveconfig') {
|
|
2985
2940
|
param = param.replace(/\*/g, '\\');
|
|
2986
2941
|
this.log.info(`Saving config for plugin ${plg}${param}${nf}...`);
|
|
@@ -3030,6 +2985,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
3030
2985
|
this.registeredPlugins.push(plugin);
|
|
3031
2986
|
await this.nodeContext?.set('plugins', await this.getBaseRegisteredPlugins());
|
|
3032
2987
|
this.log.info(`Plugin ${plg}${packageJsonPath}${nf} type ${plugin.type} added to matterbridge. Restart required.`);
|
|
2988
|
+
this.startPlugin(plugin, 'The plugin has been added to Matterbridge', true);
|
|
3033
2989
|
}
|
|
3034
2990
|
else {
|
|
3035
2991
|
this.log.error(`Error adding plugin ${plg}${packageJsonPath}${er}`);
|
|
@@ -3047,8 +3003,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
3047
3003
|
if (index !== -1) {
|
|
3048
3004
|
if (this.registeredPlugins[index].platform) {
|
|
3049
3005
|
await this.registeredPlugins[index].platform?.onShutdown('The plugin has been removed.');
|
|
3050
|
-
// await this.savePluginConfig(this.registeredPlugins[index]);
|
|
3051
3006
|
}
|
|
3007
|
+
// Remove all devices from the plugin
|
|
3008
|
+
await this.removeAllBridgedDevices(this.registeredPlugins[index].name);
|
|
3009
|
+
// Remove the plugin from the registered plugins
|
|
3052
3010
|
this.registeredPlugins.splice(index, 1);
|
|
3053
3011
|
await this.nodeContext?.set('plugins', await this.getBaseRegisteredPlugins());
|
|
3054
3012
|
this.log.info(`Plugin ${plg}${param}${nf} removed from matterbridge`);
|
|
@@ -3065,6 +3023,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
3065
3023
|
const plugin = plugins.find((plugin) => plugin.name === param);
|
|
3066
3024
|
if (plugin) {
|
|
3067
3025
|
plugin.enabled = true;
|
|
3026
|
+
plugin.locked = undefined;
|
|
3068
3027
|
plugin.error = undefined;
|
|
3069
3028
|
plugin.loaded = undefined;
|
|
3070
3029
|
plugin.started = undefined;
|
|
@@ -3072,6 +3031,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
3072
3031
|
plugin.connected = undefined;
|
|
3073
3032
|
plugin.platform = undefined;
|
|
3074
3033
|
plugin.registeredDevices = undefined;
|
|
3034
|
+
plugin.addedDevices = undefined;
|
|
3075
3035
|
await this.nodeContext?.set('plugins', plugins);
|
|
3076
3036
|
this.log.info(`Enabled plugin ${plg}${param}${nf}`);
|
|
3077
3037
|
}
|
|
@@ -3096,10 +3056,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
3096
3056
|
const pluginToDisable = this.findPlugin(param);
|
|
3097
3057
|
if (pluginToDisable) {
|
|
3098
3058
|
if (pluginToDisable.platform) {
|
|
3099
|
-
await pluginToDisable.platform.onShutdown('The plugin has been
|
|
3100
|
-
// await this.savePluginConfig(pluginToDisable);
|
|
3059
|
+
await pluginToDisable.platform.onShutdown('The plugin has been disabled.');
|
|
3101
3060
|
}
|
|
3061
|
+
// Remove all devices from the plugin
|
|
3062
|
+
this.log.info(`Unregistering devices for plugin ${plg}${pluginToDisable.name}${nf}...`);
|
|
3063
|
+
await this.removeAllBridgedDevices(pluginToDisable.name);
|
|
3102
3064
|
pluginToDisable.enabled = false;
|
|
3065
|
+
pluginToDisable.locked = undefined;
|
|
3103
3066
|
pluginToDisable.error = undefined;
|
|
3104
3067
|
pluginToDisable.loaded = undefined;
|
|
3105
3068
|
pluginToDisable.started = undefined;
|
|
@@ -3107,12 +3070,15 @@ export class Matterbridge extends EventEmitter {
|
|
|
3107
3070
|
pluginToDisable.connected = undefined;
|
|
3108
3071
|
pluginToDisable.platform = undefined;
|
|
3109
3072
|
pluginToDisable.registeredDevices = undefined;
|
|
3073
|
+
pluginToDisable.addedDevices = undefined;
|
|
3074
|
+
// Save the plugins state in node storage
|
|
3110
3075
|
const plugins = await this.nodeContext?.get('plugins');
|
|
3111
3076
|
if (!plugins)
|
|
3112
3077
|
return;
|
|
3113
3078
|
const plugin = plugins.find((plugin) => plugin.name === param);
|
|
3114
3079
|
if (plugin) {
|
|
3115
3080
|
plugin.enabled = false;
|
|
3081
|
+
plugin.locked = undefined;
|
|
3116
3082
|
plugin.error = undefined;
|
|
3117
3083
|
plugin.loaded = undefined;
|
|
3118
3084
|
plugin.started = undefined;
|
|
@@ -3120,6 +3086,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
3120
3086
|
plugin.connected = undefined;
|
|
3121
3087
|
plugin.platform = undefined;
|
|
3122
3088
|
plugin.registeredDevices = undefined;
|
|
3089
|
+
plugin.addedDevices = undefined;
|
|
3123
3090
|
await this.nodeContext?.set('plugins', plugins);
|
|
3124
3091
|
this.log.info(`Disabled plugin ${plg}${param}${nf}`);
|
|
3125
3092
|
}
|
|
@@ -3127,47 +3094,67 @@ export class Matterbridge extends EventEmitter {
|
|
|
3127
3094
|
}
|
|
3128
3095
|
res.json({ message: 'Command received' });
|
|
3129
3096
|
});
|
|
3130
|
-
// Fallback for routing
|
|
3097
|
+
// Fallback for routing (must be the last route)
|
|
3131
3098
|
this.expressApp.get('*', (req, res) => {
|
|
3132
|
-
this.log.debug('The frontend sent
|
|
3099
|
+
this.log.debug('The frontend sent:', req.url);
|
|
3100
|
+
this.log.debug('Response send file:', path.join(this.rootDirectory, 'frontend/build/index.html'));
|
|
3133
3101
|
res.sendFile(path.join(this.rootDirectory, 'frontend/build/index.html'));
|
|
3134
3102
|
});
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3103
|
+
this.log.debug(`Frontend initialized on port ${YELLOW}${port}${db} static ${UNDERLINE}${path.join(this.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
|
|
3104
|
+
}
|
|
3105
|
+
// Just for testing purposes. Use with matterbridge -test_https so no bridge is loaded
|
|
3106
|
+
async initializeHttpsFrontend(port = 8443) {
|
|
3107
|
+
this.log.debug(`Initializing the https frontend on port ${YELLOW}${port}${db} static ${UNDERLINE}${path.join(this.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
|
|
3108
|
+
// Create the express app that serves the frontend
|
|
3109
|
+
this.expressApp = express();
|
|
3110
|
+
// Load the SSL certificate and private key
|
|
3111
|
+
// If we need also the CA certificate, we can add it to the serverOptions object
|
|
3112
|
+
// I created dev certificates with openssl using the following commands:
|
|
3113
|
+
// openssl genrsa -out key.pem 2048
|
|
3114
|
+
// openssl req -new -key key.pem -out csr.pem
|
|
3115
|
+
// openssl x509 -req -days 365 -in csr.pem -signkey key.pem -out cert.pem
|
|
3116
|
+
const serverOptions = {
|
|
3117
|
+
cert: await fs.readFile(path.join(this.matterbridgeDirectory, 'certs/cert.pem'), 'utf8'),
|
|
3118
|
+
key: await fs.readFile(path.join(this.matterbridgeDirectory, 'certs/key.pem'), 'utf8'),
|
|
3119
|
+
// ca: await fs.readFile(path.join(this.matterbridgeDirectory, 'certs/ca.pem'), 'utf8'),
|
|
3120
|
+
};
|
|
3121
|
+
// Create an HTTPS server with the SSL certificate and private key and attach the express app
|
|
3122
|
+
const httpsServer = https.createServer(serverOptions, this.expressApp);
|
|
3123
|
+
// Fallback for routing
|
|
3124
|
+
this.expressApp.get('/', (req, res) => {
|
|
3125
|
+
this.log.debug('The frontend sent:', req.url);
|
|
3126
|
+
res.send('Hello, HTTPS!');
|
|
3127
|
+
});
|
|
3128
|
+
// Listen on a specific port
|
|
3129
|
+
// Connect to the frontend with 'https://localhost:8443'
|
|
3130
|
+
httpsServer.listen(port, () => {
|
|
3131
|
+
this.log.info(`HTTPS server listening on ${UNDERLINE}'https://localhost:${port}${UNDERLINEOFF}${rs}`);
|
|
3132
|
+
});
|
|
3133
|
+
// Attach WebSocket server to HTTPS server
|
|
3134
|
+
this.webSocketServer = new WebSocketServer({ server: httpsServer });
|
|
3135
|
+
this.log.info(`WebSocketServer runnig on ${UNDERLINE}wss://localhost:${port}${UNDERLINEOFF}${rs}`);
|
|
3136
|
+
// Handle connections to the WebSocketServer
|
|
3137
|
+
// Connect to the WebSocket server using wss from the react frontend this way: const ws = new WebSocket('wss://localhost:8443');
|
|
3138
|
+
this.webSocketServer.on('connection', (ws) => {
|
|
3139
|
+
this.log.info('WebSocketServer: WebSocket client connected');
|
|
3140
|
+
ws.on('message', (message) => {
|
|
3141
|
+
this.log.info(`WebSocket client sent message => ${message}`);
|
|
3139
3142
|
});
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
this.
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
this.log.error(`Port ${port} requires elevated privileges`);
|
|
3146
|
-
break;
|
|
3147
|
-
case 'EADDRINUSE':
|
|
3148
|
-
this.log.error(`Port ${port} is already in use`);
|
|
3149
|
-
break;
|
|
3150
|
-
default:
|
|
3151
|
-
this.log.error(`Port ${port} requires elevated privileges`);
|
|
3152
|
-
}
|
|
3153
|
-
process.exit(1);
|
|
3143
|
+
ws.on('close', () => {
|
|
3144
|
+
this.log.info('WebSocket client disconnected');
|
|
3145
|
+
if (this.webSocketServer?.clients.size === 0) {
|
|
3146
|
+
this.log.info('All WebSocket clients disconnected');
|
|
3147
|
+
}
|
|
3154
3148
|
});
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
// SSL certificate and private key paths
|
|
3158
|
-
const options = {
|
|
3159
|
-
cert: await fs.readFile(path.join(this.rootDirectory, 'frontend/certificates/laptop5_luca.pem')), // Ensure the path is correct
|
|
3160
|
-
key: await fs.readFile(path.join(this.rootDirectory, 'frontend/certificates/laptop5_luca.key')), // Ensure the path is correct
|
|
3161
|
-
};
|
|
3162
|
-
// Create HTTPS server
|
|
3163
|
-
const httpsServer = https.createServer(options, this.expressApp);
|
|
3164
|
-
// Specify the port to listen on, for example 443 for default HTTPS
|
|
3165
|
-
const PORT = 443;
|
|
3166
|
-
httpsServer.listen(PORT, () => {
|
|
3167
|
-
this.log.info(`The frontend is listening on ${UNDERLINE}https://${this.systemInformation.ipv4Address}:${PORT}${UNDERLINEOFF}${rs}`);
|
|
3149
|
+
ws.on('error', (error) => {
|
|
3150
|
+
this.log.error(`WebSocket client error: ${error}`);
|
|
3168
3151
|
});
|
|
3169
|
-
}
|
|
3170
|
-
|
|
3152
|
+
});
|
|
3153
|
+
// Handle error from the WebSocketServer
|
|
3154
|
+
this.webSocketServer.on('error', (ws, error) => {
|
|
3155
|
+
this.log.error(`WebSocketServer error: ${error}`);
|
|
3156
|
+
return;
|
|
3157
|
+
});
|
|
3171
3158
|
}
|
|
3172
3159
|
/**
|
|
3173
3160
|
* Retrieves the cluster text from a given device.
|
|
@@ -3228,125 +3215,4 @@ export class Matterbridge extends EventEmitter {
|
|
|
3228
3215
|
return attributes;
|
|
3229
3216
|
}
|
|
3230
3217
|
}
|
|
3231
|
-
/*
|
|
3232
|
-
TO IMPLEMENT
|
|
3233
|
-
|
|
3234
|
-
import { spawn } from 'child_process';
|
|
3235
|
-
|
|
3236
|
-
function restartProcess() {
|
|
3237
|
-
// Spawn a new process
|
|
3238
|
-
const newProcess = spawn(process.argv[0], process.argv.slice(1), {
|
|
3239
|
-
detached: true,
|
|
3240
|
-
stdio: 'inherit',
|
|
3241
|
-
});
|
|
3242
|
-
|
|
3243
|
-
// Handle errors
|
|
3244
|
-
newProcess.on('error', (err) => {
|
|
3245
|
-
console.error('Failed to start new process:', err);
|
|
3246
|
-
});
|
|
3247
|
-
|
|
3248
|
-
// Unreference the new process so that the current process can exit
|
|
3249
|
-
newProcess.unref();
|
|
3250
|
-
|
|
3251
|
-
// Exit the current process
|
|
3252
|
-
cleanup();
|
|
3253
|
-
process.exit();
|
|
3254
|
-
}
|
|
3255
|
-
|
|
3256
|
-
import React from 'react';
|
|
3257
|
-
import Form from "@rjsf/core";
|
|
3258
|
-
|
|
3259
|
-
const schema = {
|
|
3260
|
-
title: "Todo",
|
|
3261
|
-
type: "object",
|
|
3262
|
-
required: ["title"],
|
|
3263
|
-
properties: {
|
|
3264
|
-
title: {type: "string", title: "Title", default: "A new task"},
|
|
3265
|
-
done: {type: "boolean", title: "Done?", default: false}
|
|
3266
|
-
}
|
|
3267
|
-
};
|
|
3268
|
-
|
|
3269
|
-
const log = (type) => console.log.bind(console, type);
|
|
3270
|
-
|
|
3271
|
-
function Todo() {
|
|
3272
|
-
return (
|
|
3273
|
-
<Form schema={schema}
|
|
3274
|
-
onChange={log("changed")}
|
|
3275
|
-
onSubmit={log("submitted")}
|
|
3276
|
-
onError={log("errors")} />
|
|
3277
|
-
);
|
|
3278
|
-
}
|
|
3279
|
-
|
|
3280
|
-
export default Todo;
|
|
3281
|
-
|
|
3282
|
-
/*
|
|
3283
|
-
How frontend was created
|
|
3284
|
-
npx create-react-app matterbridge-frontend
|
|
3285
|
-
cd matterbridge-frontend
|
|
3286
|
-
npm install react-router-dom
|
|
3287
|
-
|
|
3288
|
-
Success! Created frontend at C:\Users\lligu\OneDrive\GitHub\matterbridge\frontend
|
|
3289
|
-
Inside that directory, you can run several commands:
|
|
3290
|
-
|
|
3291
|
-
npm start
|
|
3292
|
-
Starts the development server.
|
|
3293
|
-
|
|
3294
|
-
npm run build
|
|
3295
|
-
Bundles the app into static files for production.
|
|
3296
|
-
|
|
3297
|
-
npm test
|
|
3298
|
-
Starts the test runner.
|
|
3299
|
-
|
|
3300
|
-
npm run eject
|
|
3301
|
-
Removes this tool and copies build dependencies, configuration files
|
|
3302
|
-
and scripts into the app directory. If you do this, you can’t go back!
|
|
3303
|
-
|
|
3304
|
-
We suggest that you begin by typing:
|
|
3305
|
-
|
|
3306
|
-
cd frontend
|
|
3307
|
-
npm start
|
|
3308
|
-
|
|
3309
|
-
Happy hacking!
|
|
3310
|
-
PS C:\Users\lligu\OneDrive\GitHub\matterbridge> cd frontend
|
|
3311
|
-
PS C:\Users\lligu\OneDrive\GitHub\matterbridge\frontend> npm run build
|
|
3312
|
-
|
|
3313
|
-
> frontend@0.1.0 build
|
|
3314
|
-
> react-scripts build
|
|
3315
|
-
|
|
3316
|
-
Creating an optimized production build...
|
|
3317
|
-
One of your dependencies, babel-preset-react-app, is importing the
|
|
3318
|
-
"@babel/plugin-proposal-private-property-in-object" package without
|
|
3319
|
-
declaring it in its dependencies. This is currently working because
|
|
3320
|
-
"@babel/plugin-proposal-private-property-in-object" is already in your
|
|
3321
|
-
node_modules folder for unrelated reasons, but it may break at any time.
|
|
3322
|
-
|
|
3323
|
-
babel-preset-react-app is part of the create-react-app project, which
|
|
3324
|
-
is not maintianed anymore. It is thus unlikely that this bug will
|
|
3325
|
-
ever be fixed. Add "@babel/plugin-proposal-private-property-in-object" to
|
|
3326
|
-
your devDependencies to work around this error. This will make this message
|
|
3327
|
-
go away.
|
|
3328
|
-
|
|
3329
|
-
Compiled successfully.
|
|
3330
|
-
|
|
3331
|
-
File sizes after gzip:
|
|
3332
|
-
|
|
3333
|
-
46.65 kB build\static\js\main.9b7ec296.js
|
|
3334
|
-
1.77 kB build\static\js\453.8ab44547.chunk.js
|
|
3335
|
-
513 B build\static\css\main.f855e6bc.css
|
|
3336
|
-
|
|
3337
|
-
The project was built assuming it is hosted at /.
|
|
3338
|
-
You can control this with the homepage field in your package.json.
|
|
3339
|
-
|
|
3340
|
-
The build folder is ready to be deployed.
|
|
3341
|
-
You may serve it with a static server:
|
|
3342
|
-
|
|
3343
|
-
npm install -g serve
|
|
3344
|
-
serve -s build
|
|
3345
|
-
|
|
3346
|
-
Find out more about deployment here:
|
|
3347
|
-
|
|
3348
|
-
https://cra.link/deployment
|
|
3349
|
-
|
|
3350
|
-
PS C:\Users\lligu\OneDrive\GitHub\matterbridge\frontend>
|
|
3351
|
-
*/
|
|
3352
3218
|
//# sourceMappingURL=matterbridge.js.map
|