matterbridge 1.2.7 → 1.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/README.md +3 -0
- package/dist/AirQualityCluster.d.ts +1 -1
- package/dist/AirQualityCluster.d.ts.map +1 -1
- package/dist/TvocCluster.d.ts +1 -1
- package/dist/TvocCluster.d.ts.map +1 -1
- package/dist/matterbridge.d.ts +60 -25
- package/dist/matterbridge.d.ts.map +1 -1
- package/dist/matterbridge.js +363 -170
- package/dist/matterbridge.js.map +1 -1
- package/dist/matterbridgeDevice.d.ts +50 -3
- package/dist/matterbridgeDevice.d.ts.map +1 -1
- package/dist/matterbridgeDevice.js +12 -3
- 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.61f6cf42.css → main.4c325919.css} +2 -2
- package/frontend/build/static/css/main.4c325919.css.map +1 -0
- package/frontend/build/static/js/{main.491fc08f.js → main.cf64bc64.js} +3 -3
- package/frontend/build/static/js/main.cf64bc64.js.map +1 -0
- package/package.json +16 -14
- package/TODO.md +0 -3
- package/dist/EveHistoryCluster.d.ts +0 -426
- package/dist/EveHistoryCluster.d.ts.map +0 -1
- package/dist/EveHistoryCluster.js +0 -162
- package/dist/EveHistoryCluster.js.map +0 -1
- package/frontend/build/static/css/main.61f6cf42.css.map +0 -1
- package/frontend/build/static/js/main.491fc08f.js.map +0 -1
- package/matterbridge.service +0 -17
- /package/frontend/build/static/js/{main.491fc08f.js.LICENSE.txt → main.cf64bc64.js.LICENSE.txt} +0 -0
package/dist/matterbridge.js
CHANGED
|
@@ -26,10 +26,12 @@ import { AnsiLogger, BRIGHT, RESET, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugSt
|
|
|
26
26
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
27
27
|
import { promises as fs } from 'fs';
|
|
28
28
|
import { exec, spawn } from 'child_process';
|
|
29
|
+
import https from 'https';
|
|
29
30
|
import EventEmitter from 'events';
|
|
30
31
|
import express from 'express';
|
|
31
32
|
import os from 'os';
|
|
32
33
|
import path from 'path';
|
|
34
|
+
import WebSocket, { WebSocketServer } from 'ws';
|
|
33
35
|
import { CommissioningController, CommissioningServer, MatterServer } from '@project-chip/matter-node.js';
|
|
34
36
|
import { BasicInformationCluster, BooleanStateCluster,
|
|
35
37
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
@@ -90,6 +92,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
90
92
|
nodeContext;
|
|
91
93
|
expressApp;
|
|
92
94
|
expressServer;
|
|
95
|
+
webSocketServer;
|
|
93
96
|
storageManager;
|
|
94
97
|
matterbridgeContext;
|
|
95
98
|
mattercontrollerContext;
|
|
@@ -112,13 +115,96 @@ export class Matterbridge extends EventEmitter {
|
|
|
112
115
|
static async loadInstance(initialize = false) {
|
|
113
116
|
if (!Matterbridge.instance) {
|
|
114
117
|
// eslint-disable-next-line no-console
|
|
115
|
-
|
|
118
|
+
if (hasParameter('debug'))
|
|
119
|
+
console.log(wr + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
|
|
116
120
|
Matterbridge.instance = new Matterbridge();
|
|
117
121
|
if (initialize)
|
|
118
122
|
await Matterbridge.instance.initialize();
|
|
119
123
|
}
|
|
120
124
|
return Matterbridge.instance;
|
|
121
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* Initializes the Matterbridge instance as extension for zigbee2mqtt.
|
|
128
|
+
*
|
|
129
|
+
* @returns A Promise that resolves when the initialization is complete.
|
|
130
|
+
*/
|
|
131
|
+
async initializeAsExtension(dataPath, debugEnabled) {
|
|
132
|
+
// Set the bridge mode
|
|
133
|
+
this.bridgeMode = 'bridge';
|
|
134
|
+
// Set the first port to use
|
|
135
|
+
this.port = 5560;
|
|
136
|
+
// Set Matterbridge logger
|
|
137
|
+
this.debugEnabled = debugEnabled;
|
|
138
|
+
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logDebug: this.debugEnabled });
|
|
139
|
+
this.log.debug('Matterbridge extension is starting...');
|
|
140
|
+
// Initialize NodeStorage
|
|
141
|
+
this.matterbridgeDirectory = dataPath;
|
|
142
|
+
this.log.debug('Creating node storage manager dir: ' + path.join(this.matterbridgeDirectory, 'node_storage'));
|
|
143
|
+
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, 'node_storage'), logging: false });
|
|
144
|
+
this.log.debug('Creating node storage context for matterbridge: matterbridge');
|
|
145
|
+
this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
|
|
146
|
+
const plugin = {
|
|
147
|
+
path: '',
|
|
148
|
+
type: 'DynamicPlatform',
|
|
149
|
+
name: 'MatterbridgeExtension',
|
|
150
|
+
version: '1.0.0',
|
|
151
|
+
description: 'Matterbridge extension',
|
|
152
|
+
author: 'https://github.com/Luligu',
|
|
153
|
+
enabled: false,
|
|
154
|
+
registeredDevices: 0,
|
|
155
|
+
addedDevices: 0,
|
|
156
|
+
};
|
|
157
|
+
this.registeredPlugins.push(plugin);
|
|
158
|
+
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
159
|
+
// Log system info and create .matterbridge directory
|
|
160
|
+
await this.logNodeAndSystemInfo();
|
|
161
|
+
this.matterbridgeDirectory = dataPath;
|
|
162
|
+
// Set matter.js logger level and format
|
|
163
|
+
Logger.defaultLogLevel = this.debugEnabled ? Level.DEBUG : Level.INFO;
|
|
164
|
+
Logger.format = Format.ANSI;
|
|
165
|
+
// Start the storage and create matterbridgeContext
|
|
166
|
+
await this.startStorage('json', path.join(this.matterbridgeDirectory, 'matterbridge.json'));
|
|
167
|
+
this.matterbridgeContext = await this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge aggregator');
|
|
168
|
+
if (!this.storageManager || !this.matterbridgeContext)
|
|
169
|
+
return;
|
|
170
|
+
await this.matterbridgeContext.set('softwareVersion', 1);
|
|
171
|
+
await this.matterbridgeContext.set('softwareVersionString', this.matterbridgeVersion);
|
|
172
|
+
await this.matterbridgeContext.set('hardwareVersion', 0);
|
|
173
|
+
await this.matterbridgeContext.set('hardwareVersionString', '1.0.0'); // Update with the extension version
|
|
174
|
+
this.createMatterServer(this.storageManager);
|
|
175
|
+
this.log.debug(`Creating commissioning server for ${plg}Matterbridge${db}`);
|
|
176
|
+
this.commissioningServer = await this.createCommisioningServer(this.matterbridgeContext, 'Matterbridge');
|
|
177
|
+
this.log.debug(`Creating matter aggregator for ${plg}Matterbridge${db}`);
|
|
178
|
+
this.matterAggregator = await this.createMatterAggregator(this.matterbridgeContext);
|
|
179
|
+
this.log.debug('Adding matterbridge aggregator to commissioning server');
|
|
180
|
+
this.commissioningServer.addDevice(this.matterAggregator);
|
|
181
|
+
this.log.debug('Adding matterbridge commissioning server to matter server');
|
|
182
|
+
await this.matterServer?.addCommissioningServer(this.commissioningServer, { uniqueStorageKey: 'Matterbridge' });
|
|
183
|
+
this.log.debug(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
184
|
+
this.commissioningServer.setReachability(true);
|
|
185
|
+
await this.startMatterServer();
|
|
186
|
+
this.log.info('Matter server started');
|
|
187
|
+
await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
|
|
188
|
+
// Set reachability to true and trigger event after 60 seconds
|
|
189
|
+
setTimeout(() => {
|
|
190
|
+
this.log.info(`*Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
191
|
+
if (this.commissioningServer)
|
|
192
|
+
this.setCommissioningServerReachability(this.commissioningServer, true);
|
|
193
|
+
if (this.matterAggregator)
|
|
194
|
+
this.setAggregatorReachability(this.matterAggregator, true);
|
|
195
|
+
}, 60 * 1000);
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Close the Matterbridge instance as extension for zigbee2mqtt.
|
|
199
|
+
*
|
|
200
|
+
* @returns A Promise that resolves when the initialization is complete.
|
|
201
|
+
*/
|
|
202
|
+
async closeAsExtension() {
|
|
203
|
+
// Closing matter
|
|
204
|
+
await this.stopMatter();
|
|
205
|
+
// Closing storage
|
|
206
|
+
await this.stopStorage();
|
|
207
|
+
}
|
|
122
208
|
/**
|
|
123
209
|
* Initializes the Matterbridge application.
|
|
124
210
|
*
|
|
@@ -202,66 +288,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
202
288
|
// Parse command line
|
|
203
289
|
this.parseCommandLine();
|
|
204
290
|
}
|
|
205
|
-
/**
|
|
206
|
-
* Spawns a child process with the given command and arguments.
|
|
207
|
-
* @param command - The command to execute.
|
|
208
|
-
* @param args - The arguments to pass to the command (default: []).
|
|
209
|
-
* @returns A promise that resolves when the child process exits successfully, or rejects if there is an error.
|
|
210
|
-
*/
|
|
211
|
-
async spawnCommand(command, args = []) {
|
|
212
|
-
/*
|
|
213
|
-
npm > npm.cmd on windows
|
|
214
|
-
*/
|
|
215
|
-
if (process.platform === 'win32' && command === 'npm') {
|
|
216
|
-
command = command + '.cmd';
|
|
217
|
-
}
|
|
218
|
-
if (process.platform === 'linux' && command === 'npm' && !hasParameter('docker')) {
|
|
219
|
-
args.unshift(command);
|
|
220
|
-
command = 'sudo';
|
|
221
|
-
}
|
|
222
|
-
return new Promise((resolve, reject) => {
|
|
223
|
-
const childProcess = spawn(command, args, {
|
|
224
|
-
stdio: ['inherit', 'pipe', 'pipe'],
|
|
225
|
-
});
|
|
226
|
-
childProcess.on('error', (err) => {
|
|
227
|
-
this.log.error(`Failed to start child process: ${err.message}`);
|
|
228
|
-
reject(err); // Reject the promise on error
|
|
229
|
-
});
|
|
230
|
-
childProcess.on('close', (code) => {
|
|
231
|
-
if (code === 0) {
|
|
232
|
-
this.log.info(`Child process stdio streams have closed with code ${code}`);
|
|
233
|
-
resolve();
|
|
234
|
-
}
|
|
235
|
-
else {
|
|
236
|
-
this.log.error(`Child process stdio streams have closed with code ${code}`);
|
|
237
|
-
reject(new Error(`Process exited with code ${code}`));
|
|
238
|
-
}
|
|
239
|
-
});
|
|
240
|
-
// The 'exit' event might be redundant here since 'close' is also being handled
|
|
241
|
-
childProcess.on('exit', (code, signal) => {
|
|
242
|
-
this.log.info(`Child process exited with code ${code} and signal ${signal}`);
|
|
243
|
-
});
|
|
244
|
-
childProcess.on('disconnect', () => {
|
|
245
|
-
this.log.info('Child process has been disconnected from the parent');
|
|
246
|
-
});
|
|
247
|
-
if (childProcess.stdout) {
|
|
248
|
-
childProcess.stdout.on('data', (data) => {
|
|
249
|
-
// Convert the Buffer data to a string.
|
|
250
|
-
const message = data.toString();
|
|
251
|
-
this.log.info(message);
|
|
252
|
-
// TODO: Send this message to the frontend.
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
if (childProcess.stderr) {
|
|
256
|
-
childProcess.stderr.on('data', (data) => {
|
|
257
|
-
// Convert the Buffer data to a string.
|
|
258
|
-
const message = data.toString();
|
|
259
|
-
this.log.error(message);
|
|
260
|
-
// TODO: Handle the error message.
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
291
|
/**
|
|
266
292
|
* Parses the command line arguments and performs the corresponding actions.
|
|
267
293
|
* @private
|
|
@@ -579,6 +605,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
579
605
|
this.log.info(message);
|
|
580
606
|
process.removeAllListeners('SIGINT');
|
|
581
607
|
process.removeAllListeners('SIGTERM');
|
|
608
|
+
this.log.debug('All listeners removed');
|
|
582
609
|
// Calling the shutdown functions with a reason
|
|
583
610
|
for (const plugin of this.registeredPlugins) {
|
|
584
611
|
if (!plugin.enabled)
|
|
@@ -634,11 +661,31 @@ export class Matterbridge extends EventEmitter {
|
|
|
634
661
|
if (this.expressServer) {
|
|
635
662
|
this.expressServer.close();
|
|
636
663
|
this.expressServer = undefined;
|
|
664
|
+
this.log.debug('Express server closed successfully');
|
|
637
665
|
}
|
|
638
666
|
// Remove listeners
|
|
639
667
|
if (this.expressApp) {
|
|
640
668
|
this.expressApp.removeAllListeners();
|
|
641
669
|
this.expressApp = undefined;
|
|
670
|
+
this.log.debug('Frontend closed successfully');
|
|
671
|
+
}
|
|
672
|
+
// Close the WebSocket server
|
|
673
|
+
if (this.webSocketServer) {
|
|
674
|
+
// Close all active connections
|
|
675
|
+
this.webSocketServer.clients.forEach((client) => {
|
|
676
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
677
|
+
client.close();
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
this.webSocketServer.close((error) => {
|
|
681
|
+
if (error) {
|
|
682
|
+
this.log.error(`Error closing WebSocket server: ${error}`);
|
|
683
|
+
}
|
|
684
|
+
else {
|
|
685
|
+
this.log.debug('WebSocket server closed successfully');
|
|
686
|
+
}
|
|
687
|
+
});
|
|
688
|
+
this.webSocketServer = undefined;
|
|
642
689
|
}
|
|
643
690
|
/*const cleanupTimeout1 =*/ setTimeout(async () => {
|
|
644
691
|
// Closing matter
|
|
@@ -688,26 +735,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
688
735
|
Matterbridge.instance = undefined;
|
|
689
736
|
this.emit('shutdown');
|
|
690
737
|
}
|
|
691
|
-
},
|
|
738
|
+
}, 2 * 1000);
|
|
692
739
|
//cleanupTimeout2.unref();
|
|
693
740
|
}, 3 * 1000);
|
|
694
741
|
//cleanupTimeout1.unref();
|
|
695
742
|
}
|
|
696
743
|
}
|
|
697
|
-
/**
|
|
698
|
-
* Sets the reachable attribute of a device.
|
|
699
|
-
*
|
|
700
|
-
* @param device - The device for which to set the reachable attribute.
|
|
701
|
-
* @param reachable - The value to set for the reachable attribute.
|
|
702
|
-
*/
|
|
703
|
-
setReachableAttribute(device, reachable) {
|
|
704
|
-
const basicInformationCluster = device.getClusterServer(BasicInformationCluster);
|
|
705
|
-
if (!basicInformationCluster) {
|
|
706
|
-
this.log.error('setReachableAttribute BasicInformationCluster needs to be set!');
|
|
707
|
-
return;
|
|
708
|
-
}
|
|
709
|
-
basicInformationCluster.setReachableAttribute(reachable);
|
|
710
|
-
}
|
|
711
744
|
/**
|
|
712
745
|
* Adds a device to the Matterbridge.
|
|
713
746
|
* @param pluginName - The name of the plugin.
|
|
@@ -803,7 +836,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
803
836
|
return;
|
|
804
837
|
}
|
|
805
838
|
if (this.bridgeMode === 'childbridge' && !plugin.connected) {
|
|
806
|
-
this.log.
|
|
839
|
+
this.log.info(`Removing bridged device ${dev}${device.deviceName}${wr} (${dev}${device.name}${wr}) plugin ${plg}${pluginName}${wr} not connected`);
|
|
807
840
|
return;
|
|
808
841
|
}
|
|
809
842
|
// Remove the device from matterbridge aggregator in bridge mode
|
|
@@ -1220,7 +1253,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
1220
1253
|
// Call the default export function of the plugin, passing this MatterBridge instance
|
|
1221
1254
|
if (pluginInstance.default) {
|
|
1222
1255
|
const config = await this.loadPluginConfig(plugin);
|
|
1223
|
-
const
|
|
1256
|
+
const log = new AnsiLogger({ logName: plugin.description, logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logDebug: this.debugEnabled });
|
|
1257
|
+
//log.setCallback(this.wssSendMessage.bind(this));
|
|
1258
|
+
const platform = pluginInstance.default(this, log, config);
|
|
1224
1259
|
platform.name = packageJson.name;
|
|
1225
1260
|
platform.config = config;
|
|
1226
1261
|
plugin.name = packageJson.name;
|
|
@@ -1482,20 +1517,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
1482
1517
|
await this.matterServer?.addCommissioningServer(this.commissioningServer, { uniqueStorageKey: 'Matterbridge' });
|
|
1483
1518
|
this.log.debug(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1484
1519
|
this.commissioningServer.setReachability(true);
|
|
1485
|
-
this.log.debug('Starting matter server...');
|
|
1486
1520
|
await this.startMatterServer();
|
|
1487
1521
|
this.log.info('Matter server started');
|
|
1488
1522
|
await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
|
|
1489
1523
|
//if (hasParameter('advertise')) await this.commissioningServer.advertise();
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
}, 30 * 1000);
|
|
1498
|
-
*/
|
|
1524
|
+
setTimeout(() => {
|
|
1525
|
+
this.log.info(`*Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1526
|
+
if (this.commissioningServer)
|
|
1527
|
+
this.setCommissioningServerReachability(this.commissioningServer, true);
|
|
1528
|
+
if (this.matterAggregator)
|
|
1529
|
+
this.setAggregatorReachability(this.matterAggregator, true);
|
|
1530
|
+
}, 60 * 1000);
|
|
1499
1531
|
}, 1000);
|
|
1500
1532
|
}
|
|
1501
1533
|
if (this.bridgeMode === 'childbridge') {
|
|
@@ -1532,6 +1564,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1532
1564
|
plugin.commissioningServer = await this.createCommisioningServer(plugin.storageContext, plugin.name);
|
|
1533
1565
|
this.log.debug(`Adding device ${dev}${registeredDevice.device.name}${db} to commissioning server for plugin ${plg}${plugin.name}${db}`);
|
|
1534
1566
|
plugin.commissioningServer.addDevice(registeredDevice.device);
|
|
1567
|
+
if (!plugin.device)
|
|
1568
|
+
plugin.device = registeredDevice.device;
|
|
1535
1569
|
}
|
|
1536
1570
|
this.log.debug(`Adding commissioning server to matter server for plugin ${plg}${plugin.name}${db}`);
|
|
1537
1571
|
await this.matterServer?.addCommissioningServer(plugin.commissioningServer, { uniqueStorageKey: plugin.name });
|
|
@@ -1572,21 +1606,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1572
1606
|
return;
|
|
1573
1607
|
clearInterval(startMatterInterval);
|
|
1574
1608
|
this.log.info('Starting matter server...');
|
|
1575
|
-
// Setting reachability to true
|
|
1576
|
-
this.registeredPlugins.forEach((plugin) => {
|
|
1577
|
-
if (!plugin.enabled)
|
|
1578
|
-
return;
|
|
1579
|
-
this.log.debug(`Setting reachability to true for ${plg}${plugin.name}${db}`);
|
|
1580
|
-
plugin.commissioningServer?.setReachability(true);
|
|
1581
|
-
this.registeredDevices.forEach((registeredDevice) => {
|
|
1582
|
-
if (registeredDevice.plugin === plugin.name) {
|
|
1583
|
-
if (plugin.type === 'AccessoryPlatform')
|
|
1584
|
-
this.setReachableAttribute(registeredDevice.device, true);
|
|
1585
|
-
if (plugin.type === 'DynamicPlatform')
|
|
1586
|
-
registeredDevice.device.setBridgedDeviceReachability(true);
|
|
1587
|
-
}
|
|
1588
|
-
});
|
|
1589
|
-
});
|
|
1590
1609
|
await this.startMatterServer();
|
|
1591
1610
|
this.log.info('Matter server started');
|
|
1592
1611
|
for (const plugin of this.registeredPlugins) {
|
|
@@ -1605,6 +1624,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
1605
1624
|
continue;
|
|
1606
1625
|
}
|
|
1607
1626
|
await this.showCommissioningQRCode(plugin.commissioningServer, plugin.storageContext, plugin.nodeContext, plugin.name);
|
|
1627
|
+
// Setting reachability to true
|
|
1628
|
+
this.log.info(`*Setting reachability to true for ${plg}${plugin.name}${db}`);
|
|
1629
|
+
if (plugin.commissioningServer)
|
|
1630
|
+
this.setCommissioningServerReachability(plugin.commissioningServer, true);
|
|
1631
|
+
if (plugin.type === 'AccessoryPlatform' && plugin.device)
|
|
1632
|
+
this.setDeviceReachability(plugin.device, true);
|
|
1633
|
+
if (plugin.type === 'DynamicPlatform' && plugin.aggregator)
|
|
1634
|
+
this.setAggregatorReachability(plugin.aggregator, true);
|
|
1608
1635
|
}
|
|
1609
1636
|
Logger.defaultLogLevel = this.debugEnabled ? Level.DEBUG : Level.INFO;
|
|
1610
1637
|
//clearInterval(startMatterInterval);
|
|
@@ -1621,7 +1648,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1621
1648
|
await this.cleanup('No matter server initialized');
|
|
1622
1649
|
return;
|
|
1623
1650
|
}
|
|
1624
|
-
this.log.debug('Starting matter server');
|
|
1651
|
+
this.log.debug('Starting matter server...');
|
|
1625
1652
|
await this.matterServer.start();
|
|
1626
1653
|
this.log.debug('Started matter server');
|
|
1627
1654
|
}
|
|
@@ -1741,6 +1768,49 @@ export class Matterbridge extends EventEmitter {
|
|
|
1741
1768
|
}
|
|
1742
1769
|
return plugin;
|
|
1743
1770
|
}
|
|
1771
|
+
/**
|
|
1772
|
+
* Sets the reachability of a commissioning server and trigger.
|
|
1773
|
+
*
|
|
1774
|
+
* @param {CommissioningServer} commissioningServer - The commissioning server to set the reachability for.
|
|
1775
|
+
* @param {boolean} reachable - The new reachability status.
|
|
1776
|
+
*/
|
|
1777
|
+
setCommissioningServerReachability(commissioningServer, reachable) {
|
|
1778
|
+
const basicInformationCluster = commissioningServer?.getRootClusterServer(BasicInformationCluster);
|
|
1779
|
+
if (basicInformationCluster && basicInformationCluster.attributes.reachable !== undefined)
|
|
1780
|
+
basicInformationCluster.setReachableAttribute(reachable);
|
|
1781
|
+
if (basicInformationCluster && basicInformationCluster.triggerReachableChangedEvent)
|
|
1782
|
+
basicInformationCluster.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
1783
|
+
}
|
|
1784
|
+
/**
|
|
1785
|
+
* Sets the reachability of the specified matter aggregator and its bridged devices and trigger.
|
|
1786
|
+
* @param {Aggregator} matterAggregator - The matter aggregator to set the reachability for.
|
|
1787
|
+
* @param {boolean} reachable - A boolean indicating the reachability status to set.
|
|
1788
|
+
*/
|
|
1789
|
+
setAggregatorReachability(matterAggregator, reachable) {
|
|
1790
|
+
const basicInformationCluster = matterAggregator.getClusterServer(BasicInformationCluster);
|
|
1791
|
+
if (basicInformationCluster && basicInformationCluster.attributes.reachable !== undefined)
|
|
1792
|
+
basicInformationCluster.setReachableAttribute(reachable);
|
|
1793
|
+
if (basicInformationCluster && basicInformationCluster.triggerReachableChangedEvent)
|
|
1794
|
+
basicInformationCluster.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
1795
|
+
matterAggregator.getBridgedDevices().forEach((device) => {
|
|
1796
|
+
this.log.debug(`*Setting reachability to true for bridged device: ${dev}${device.name}${nf}`);
|
|
1797
|
+
device.getClusterServer(BridgedDeviceBasicInformationCluster)?.setReachableAttribute(reachable);
|
|
1798
|
+
device.getClusterServer(BridgedDeviceBasicInformationCluster)?.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
1799
|
+
});
|
|
1800
|
+
}
|
|
1801
|
+
/**
|
|
1802
|
+
* Sets the reachability of a device and trigger.
|
|
1803
|
+
*
|
|
1804
|
+
* @param {MatterbridgeDevice} device - The device to set the reachability for.
|
|
1805
|
+
* @param {boolean} reachable - The new reachability status of the device.
|
|
1806
|
+
*/
|
|
1807
|
+
setDeviceReachability(device, reachable) {
|
|
1808
|
+
const basicInformationCluster = device.getClusterServer(BasicInformationCluster);
|
|
1809
|
+
if (basicInformationCluster && basicInformationCluster.attributes.reachable !== undefined)
|
|
1810
|
+
basicInformationCluster.setReachableAttribute(reachable);
|
|
1811
|
+
if (basicInformationCluster && basicInformationCluster.triggerReachableChangedEvent)
|
|
1812
|
+
basicInformationCluster.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
1813
|
+
}
|
|
1744
1814
|
/**
|
|
1745
1815
|
* Creates a matter commissioning server.
|
|
1746
1816
|
*
|
|
@@ -1793,7 +1863,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
1793
1863
|
info.forEach((session) => {
|
|
1794
1864
|
this.log.debug(`***Active session changed on fabric ${fabricIndex} ${session.fabric?.rootVendorId}/${session.fabric?.label} for ${plg}${pluginName}${nf}`, debugStringify(session));
|
|
1795
1865
|
if (session.isPeerActive === true && session.secure === true && session.numberOfActiveSubscriptions >= 1) {
|
|
1796
|
-
|
|
1866
|
+
let controllerName = '';
|
|
1867
|
+
if (session.fabric?.rootVendorId === 4937)
|
|
1868
|
+
controllerName = 'AppleHome';
|
|
1869
|
+
if (session.fabric?.rootVendorId === 4362)
|
|
1870
|
+
controllerName = 'SmartThings';
|
|
1871
|
+
if (session.fabric?.rootVendorId === 4939)
|
|
1872
|
+
controllerName = 'HomeAssistant';
|
|
1873
|
+
this.log.info(`***Controller ${session.fabric?.rootVendorId}${controllerName !== '' ? '(' + controllerName + ')' : ''}/${session.fabric?.label} connected to ${plg}${pluginName}${nf}`);
|
|
1797
1874
|
connected = true;
|
|
1798
1875
|
}
|
|
1799
1876
|
});
|
|
@@ -2028,11 +2105,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2028
2105
|
this.matterbridgeInformation.rootDirectory = this.rootDirectory;
|
|
2029
2106
|
this.log.debug(`Root Directory: ${this.rootDirectory}`);
|
|
2030
2107
|
// Global node_modules directory
|
|
2031
|
-
/*
|
|
2032
|
-
this.globalModulesDirectory = await this.getGlobalNodeModules();
|
|
2033
|
-
this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory;
|
|
2034
|
-
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
2035
|
-
*/
|
|
2036
2108
|
if (this.nodeContext)
|
|
2037
2109
|
this.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
|
|
2038
2110
|
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
@@ -2100,11 +2172,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2100
2172
|
this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeVersion;
|
|
2101
2173
|
this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
|
|
2102
2174
|
// Matterbridge latest version
|
|
2103
|
-
/*
|
|
2104
|
-
this.matterbridgeLatestVersion = await this.getLatestVersion('matterbridge');
|
|
2105
|
-
this.matterbridgeInformation.matterbridgeLatestVersion = this.matterbridgeLatestVersion;
|
|
2106
|
-
this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
|
|
2107
|
-
*/
|
|
2108
2175
|
if (this.nodeContext)
|
|
2109
2176
|
this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', '');
|
|
2110
2177
|
this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
|
|
@@ -2174,6 +2241,99 @@ export class Matterbridge extends EventEmitter {
|
|
|
2174
2241
|
}));
|
|
2175
2242
|
return baseRegisteredPlugins;
|
|
2176
2243
|
}
|
|
2244
|
+
/**
|
|
2245
|
+
* Spawns a child process with the given command and arguments.
|
|
2246
|
+
* @param command - The command to execute.
|
|
2247
|
+
* @param args - The arguments to pass to the command (default: []).
|
|
2248
|
+
* @returns A promise that resolves when the child process exits successfully, or rejects if there is an error.
|
|
2249
|
+
*/
|
|
2250
|
+
async spawnCommand(command, args = []) {
|
|
2251
|
+
/*
|
|
2252
|
+
npm > npm.cmd on windows
|
|
2253
|
+
cmd.exe ['dir'] on windows
|
|
2254
|
+
await this.spawnCommand('npm', ['install', '-g', 'matterbridge']);
|
|
2255
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
2256
|
+
this.log.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
2257
|
+
});
|
|
2258
|
+
*/
|
|
2259
|
+
if (process.platform === 'win32' && command === 'npm') {
|
|
2260
|
+
// Must be spawn('cmd.exe', ['/c', 'npm -g install <package>']);
|
|
2261
|
+
const argstring = 'npm ' + args.join(' ');
|
|
2262
|
+
args.splice(0, args.length, '/c', argstring);
|
|
2263
|
+
command = 'cmd.exe';
|
|
2264
|
+
}
|
|
2265
|
+
if (process.platform === 'linux' && command === 'npm' && !hasParameter('docker')) {
|
|
2266
|
+
args.unshift(command);
|
|
2267
|
+
command = 'sudo';
|
|
2268
|
+
}
|
|
2269
|
+
this.log.debug(`Spawning command ${command} with ${debugStringify(args)}`);
|
|
2270
|
+
return new Promise((resolve, reject) => {
|
|
2271
|
+
const childProcess = spawn(command, args, {
|
|
2272
|
+
stdio: ['inherit', 'pipe', 'pipe'],
|
|
2273
|
+
});
|
|
2274
|
+
childProcess.on('error', (err) => {
|
|
2275
|
+
this.log.error(`Failed to start child process: ${err.message}`);
|
|
2276
|
+
reject(err); // Reject the promise on error
|
|
2277
|
+
});
|
|
2278
|
+
childProcess.on('close', (code) => {
|
|
2279
|
+
if (code === 0) {
|
|
2280
|
+
this.log.debug(`Child process stdio streams have closed with code ${code}`);
|
|
2281
|
+
resolve();
|
|
2282
|
+
}
|
|
2283
|
+
else {
|
|
2284
|
+
this.log.error(`Child process stdio streams have closed with code ${code}`);
|
|
2285
|
+
reject(new Error(`Child process stdio streams have closed with code ${code}`));
|
|
2286
|
+
}
|
|
2287
|
+
});
|
|
2288
|
+
childProcess.on('exit', (code, signal) => {
|
|
2289
|
+
if (code === 0) {
|
|
2290
|
+
this.log.debug(`Child process exited with code ${code} and signal ${signal}`);
|
|
2291
|
+
resolve();
|
|
2292
|
+
}
|
|
2293
|
+
else {
|
|
2294
|
+
this.log.error(`Child process exited with code ${code} and signal ${signal}`);
|
|
2295
|
+
reject(new Error(`Child process exited with code ${code} and signal ${signal}`));
|
|
2296
|
+
}
|
|
2297
|
+
});
|
|
2298
|
+
childProcess.on('disconnect', () => {
|
|
2299
|
+
this.log.debug('Child process has been disconnected from the parent');
|
|
2300
|
+
resolve();
|
|
2301
|
+
});
|
|
2302
|
+
if (childProcess.stdout) {
|
|
2303
|
+
childProcess.stdout.on('data', (data) => {
|
|
2304
|
+
const message = data.toString().trim();
|
|
2305
|
+
//this.log.info('\n' + message);
|
|
2306
|
+
this.wssSendMessage('Matterbridge:spawn', 'spawn', message);
|
|
2307
|
+
});
|
|
2308
|
+
}
|
|
2309
|
+
if (childProcess.stderr) {
|
|
2310
|
+
childProcess.stderr.on('data', (data) => {
|
|
2311
|
+
const message = data.toString().trim();
|
|
2312
|
+
//this.log.debug('\n' + message);
|
|
2313
|
+
this.wssSendMessage('Matterbridge:spawn', 'spawn', message);
|
|
2314
|
+
});
|
|
2315
|
+
}
|
|
2316
|
+
});
|
|
2317
|
+
}
|
|
2318
|
+
/**
|
|
2319
|
+
* Sends a WebSocket message to all connected clients.
|
|
2320
|
+
*
|
|
2321
|
+
* @param {string} type - The type of the message: Matterbridge, Plugin, Device, ...
|
|
2322
|
+
* @param {string} subType - The subtype of the message: debug info warn error ....
|
|
2323
|
+
* @param {string} message - The content of the message.
|
|
2324
|
+
*/
|
|
2325
|
+
wssSendMessage(type, subType, message) {
|
|
2326
|
+
// Remove ANSI escape codes from the message
|
|
2327
|
+
// eslint-disable-next-line no-control-regex
|
|
2328
|
+
const cleanMessage = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
2329
|
+
// Remove leading asterisks from the message
|
|
2330
|
+
const finalMessage = cleanMessage.replace(/^\*+/, '');
|
|
2331
|
+
this.webSocketServer?.clients.forEach((client) => {
|
|
2332
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
2333
|
+
client.send(JSON.stringify({ type, subType, message: finalMessage }));
|
|
2334
|
+
}
|
|
2335
|
+
});
|
|
2336
|
+
}
|
|
2177
2337
|
/**
|
|
2178
2338
|
* Initializes the frontend of Matterbridge.
|
|
2179
2339
|
*
|
|
@@ -2181,10 +2341,57 @@ export class Matterbridge extends EventEmitter {
|
|
|
2181
2341
|
*/
|
|
2182
2342
|
async initializeFrontend(port = 8283) {
|
|
2183
2343
|
this.log.debug(`Initializing the frontend on port ${YELLOW}${port}${db} static ${UNDERLINE}${path.join(this.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
|
|
2184
|
-
|
|
2344
|
+
const wssPort = 8284;
|
|
2345
|
+
const useHttps = false;
|
|
2346
|
+
//const wssHost = (useHttps ? 'wss://' : 'ws://') + `${os.hostname().toLowerCase()}:${wssPort}`;
|
|
2347
|
+
const wssHost = (useHttps ? 'wss://' : 'ws://') + `${this.systemInformation.ipv4Address}:${wssPort}`;
|
|
2348
|
+
if (!useHttps) {
|
|
2349
|
+
// Create a WebSocket server no certificate required
|
|
2350
|
+
this.webSocketServer = new WebSocketServer({ port: wssPort });
|
|
2351
|
+
this.log.info(`WebSocket server listening on ${UNDERLINE}${wssHost}${UNDERLINEOFF}${rs}`);
|
|
2352
|
+
}
|
|
2353
|
+
else {
|
|
2354
|
+
// Define the options for HTTPS server
|
|
2355
|
+
// openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout mykey.key -out mycert.pem -config openssl.cnf
|
|
2356
|
+
// For wss connect the browser to https://laptop5_luca:8284/ https://192.168.1.189/log and accept the certificate
|
|
2357
|
+
const serverOptions = {
|
|
2358
|
+
cert: await fs.readFile(path.join(this.rootDirectory, 'frontend/certificates/mycert.pem')), // Ensure the path is correct
|
|
2359
|
+
key: await fs.readFile(path.join(this.rootDirectory, 'frontend/certificates/mykey.key')), // Ensure the path is correct
|
|
2360
|
+
// cert: await fs.readFile(path.join(this.rootDirectory, 'frontend/certificates/laptop5_luca.pem')), // Ensure the path is correct
|
|
2361
|
+
// key: await fs.readFile(path.join(this.rootDirectory, 'frontend/certificates/laptop5_luca.key')), // Ensure the path is correct
|
|
2362
|
+
};
|
|
2363
|
+
// Create an HTTPS server
|
|
2364
|
+
const httpsServer = https.createServer(serverOptions);
|
|
2365
|
+
// Attach WebSocket server to HTTPS server
|
|
2366
|
+
this.webSocketServer = new WebSocketServer({ server: httpsServer });
|
|
2367
|
+
// Listen on a specific port
|
|
2368
|
+
httpsServer.listen(wssPort, () => {
|
|
2369
|
+
this.log.info(`WebSocket server listening on ${UNDERLINE}${wssHost}${UNDERLINEOFF}${rs}`);
|
|
2370
|
+
});
|
|
2371
|
+
}
|
|
2372
|
+
this.webSocketServer.on('connection', (ws) => {
|
|
2373
|
+
this.log.info('WebSocketServer client connected');
|
|
2374
|
+
this.log.setGlobalCallback(this.wssSendMessage.bind(this));
|
|
2375
|
+
this.log.debug('WebSocketServer activated logger callback');
|
|
2376
|
+
this.wssSendMessage('Matterbridge', 'info', 'WebSocketServer client connected to Matterbridge');
|
|
2377
|
+
ws.on('message', (message) => {
|
|
2378
|
+
this.log.info(`WebSocketServer received message => ${message}`);
|
|
2379
|
+
});
|
|
2380
|
+
ws.on('close', () => {
|
|
2381
|
+
this.log.info('WebSocketServer client disconnected');
|
|
2382
|
+
if (this.webSocketServer?.clients.size === 0) {
|
|
2383
|
+
this.log.setGlobalCallback(undefined);
|
|
2384
|
+
this.log.debug('WebSocketServer deactivated logger callback');
|
|
2385
|
+
}
|
|
2386
|
+
});
|
|
2387
|
+
ws.on('error', (error) => {
|
|
2388
|
+
this.log.error(`WebSocketServer error: ${error}`);
|
|
2389
|
+
});
|
|
2390
|
+
});
|
|
2185
2391
|
// Serve React build directory
|
|
2392
|
+
this.expressApp = express();
|
|
2186
2393
|
this.expressApp.use(express.static(path.join(this.rootDirectory, 'frontend/build')));
|
|
2187
|
-
// Endpoint to
|
|
2394
|
+
// Endpoint to validate login code
|
|
2188
2395
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
2189
2396
|
const { password } = req.body;
|
|
2190
2397
|
this.log.debug('The frontend sent /api/login', password);
|
|
@@ -2204,53 +2411,30 @@ export class Matterbridge extends EventEmitter {
|
|
|
2204
2411
|
res.json({ valid: false });
|
|
2205
2412
|
}
|
|
2206
2413
|
});
|
|
2207
|
-
// Endpoint to provide
|
|
2208
|
-
this.expressApp.get('/api/
|
|
2209
|
-
this.log.debug('The frontend sent /api/pairing-code');
|
|
2414
|
+
// Endpoint to provide settings
|
|
2415
|
+
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
2210
2416
|
if (!this.matterbridgeContext) {
|
|
2211
|
-
this.log.error('/api/
|
|
2212
|
-
res.json([]);
|
|
2213
|
-
return;
|
|
2214
|
-
}
|
|
2215
|
-
try {
|
|
2216
|
-
const qrData = { qrPairingCode: this.matterbridgeContext.get('qrPairingCode'), manualPairingCode: this.matterbridgeContext.get('manualPairingCode') };
|
|
2217
|
-
res.json(qrData);
|
|
2218
|
-
}
|
|
2219
|
-
catch (error) {
|
|
2220
|
-
if (this.bridgeMode === 'bridge')
|
|
2221
|
-
this.log.error('qrPairingCode for /api/qr-code not found');
|
|
2417
|
+
this.log.error('/api/settings matterbridgeContext not found');
|
|
2222
2418
|
res.json({});
|
|
2223
|
-
}
|
|
2224
|
-
});
|
|
2225
|
-
// Endpoint to provide QR pairing code
|
|
2226
|
-
this.expressApp.get('/api/qr-code', (req, res) => {
|
|
2227
|
-
this.log.debug('The frontend sent /api/qr-code');
|
|
2228
|
-
if (!this.matterbridgeContext) {
|
|
2229
|
-
this.log.error('/api/qr-code matterbridgeContext not found');
|
|
2230
|
-
res.json([]);
|
|
2231
2419
|
return;
|
|
2232
2420
|
}
|
|
2421
|
+
let qrPairingCode = '';
|
|
2422
|
+
let manualPairingCode = '';
|
|
2233
2423
|
try {
|
|
2234
|
-
|
|
2235
|
-
|
|
2424
|
+
qrPairingCode = await this.matterbridgeContext.get('qrPairingCode');
|
|
2425
|
+
manualPairingCode = await this.matterbridgeContext.get('manualPairingCode');
|
|
2236
2426
|
}
|
|
2237
2427
|
catch (error) {
|
|
2238
2428
|
if (this.bridgeMode === 'bridge')
|
|
2239
|
-
this.log.error('
|
|
2429
|
+
this.log.error('pairingCode for /api/settings not found');
|
|
2240
2430
|
res.json({});
|
|
2241
2431
|
}
|
|
2242
|
-
});
|
|
2243
|
-
// Endpoint to provide system information
|
|
2244
|
-
this.expressApp.get('/api/system-info', (req, res) => {
|
|
2245
|
-
this.log.debug('The frontend sent /api/system-info');
|
|
2246
|
-
res.json(this.systemInformation);
|
|
2247
|
-
});
|
|
2248
|
-
// Endpoint to provide matterbridge information
|
|
2249
|
-
this.expressApp.get('/api/matterbridge-info', (req, res) => {
|
|
2250
|
-
this.log.debug('The frontend sent /api/matterbridge-info');
|
|
2251
2432
|
this.matterbridgeInformation.bridgeMode = this.bridgeMode;
|
|
2252
2433
|
this.matterbridgeInformation.debugEnabled = this.debugEnabled;
|
|
2253
|
-
|
|
2434
|
+
const response = { wssHost, qrPairingCode, manualPairingCode, systemInformation: this.systemInformation, matterbridgeInformation: this.matterbridgeInformation };
|
|
2435
|
+
this.log.debug('The frontend sent /api/settings');
|
|
2436
|
+
this.log.debug('Response:', debugStringify(response));
|
|
2437
|
+
res.json(response);
|
|
2254
2438
|
});
|
|
2255
2439
|
// Endpoint to provide plugins
|
|
2256
2440
|
this.expressApp.get('/api/plugins', (req, res) => {
|
|
@@ -2336,11 +2520,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
2336
2520
|
res.status(400).json({ error: 'No command provided' });
|
|
2337
2521
|
return;
|
|
2338
2522
|
}
|
|
2339
|
-
this.log.
|
|
2523
|
+
this.log.debug(`Received frontend command: ${command}:${param}`);
|
|
2340
2524
|
// Handle the command setpassword from Settings
|
|
2341
2525
|
if (command === 'setpassword') {
|
|
2342
2526
|
const password = param.slice(1, -1); // Remove the first and last characters
|
|
2343
|
-
this.log.
|
|
2527
|
+
this.log.debug('setpassword', param, password);
|
|
2344
2528
|
await this.nodeContext?.set('password', password);
|
|
2345
2529
|
}
|
|
2346
2530
|
// Handle the command debugLevel from Settings
|
|
@@ -2374,9 +2558,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
2374
2558
|
}
|
|
2375
2559
|
// Handle the command update from Header
|
|
2376
2560
|
if (command === 'update') {
|
|
2377
|
-
this.log.
|
|
2561
|
+
this.log.info('Updating matterbridge...');
|
|
2378
2562
|
try {
|
|
2379
|
-
await this.spawnCommand('npm', ['install', '-g', 'matterbridge']);
|
|
2563
|
+
await this.spawnCommand('npm', ['install', '-g', 'matterbridge', '--loglevel=verbose']);
|
|
2380
2564
|
this.log.info('Matterbridge has been updated. Full restart required.');
|
|
2381
2565
|
}
|
|
2382
2566
|
catch (error) {
|
|
@@ -2389,10 +2573,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
2389
2573
|
// Handle the command installplugin from Home
|
|
2390
2574
|
if (command === 'installplugin') {
|
|
2391
2575
|
param = param.replace(/\*/g, '\\');
|
|
2392
|
-
this.log.
|
|
2576
|
+
this.log.info(`Installing plugin ${plg}${param}${db}...`);
|
|
2393
2577
|
try {
|
|
2394
|
-
await this.spawnCommand('npm', ['install', '-g', param]);
|
|
2395
|
-
this.log.info(`Plugin ${plg}${param}${nf} installed.
|
|
2578
|
+
await this.spawnCommand('npm', ['install', '-g', param, '--loglevel=verbose']);
|
|
2579
|
+
this.log.info(`Plugin ${plg}${param}${nf} installed. Full restart required.`);
|
|
2396
2580
|
}
|
|
2397
2581
|
catch (error) {
|
|
2398
2582
|
this.log.error(`Error installing plugin ${plg}${param}${er}`);
|
|
@@ -2518,9 +2702,26 @@ export class Matterbridge extends EventEmitter {
|
|
|
2518
2702
|
this.log.debug('The frontend sent *', req.url);
|
|
2519
2703
|
res.sendFile(path.join(this.rootDirectory, 'frontend/build/index.html'));
|
|
2520
2704
|
});
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2705
|
+
if (!useHttps) {
|
|
2706
|
+
// Listen on HTTP
|
|
2707
|
+
this.expressServer = this.expressApp.listen(port, () => {
|
|
2708
|
+
this.log.info(`The frontend is running on ${UNDERLINE}http://localhost:${port}${UNDERLINEOFF}${rs}`);
|
|
2709
|
+
});
|
|
2710
|
+
}
|
|
2711
|
+
else {
|
|
2712
|
+
// SSL certificate and private key paths
|
|
2713
|
+
const options = {
|
|
2714
|
+
cert: await fs.readFile(path.join(this.rootDirectory, 'frontend/certificates/laptop5_luca.pem')), // Ensure the path is correct
|
|
2715
|
+
key: await fs.readFile(path.join(this.rootDirectory, 'frontend/certificates/laptop5_luca.key')), // Ensure the path is correct
|
|
2716
|
+
};
|
|
2717
|
+
// Create HTTPS server
|
|
2718
|
+
const httpsServer = https.createServer(options, this.expressApp);
|
|
2719
|
+
// Specify the port to listen on, for example 443 for default HTTPS
|
|
2720
|
+
const PORT = 443;
|
|
2721
|
+
httpsServer.listen(PORT, () => {
|
|
2722
|
+
this.log.info(`The frontend is running on ${UNDERLINE}https://localhost:${PORT}${UNDERLINEOFF}${rs}`);
|
|
2723
|
+
});
|
|
2724
|
+
}
|
|
2524
2725
|
this.log.debug(`Frontend initialized on port ${YELLOW}${port}${db} static ${UNDERLINE}${path.join(this.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
|
|
2525
2726
|
}
|
|
2526
2727
|
/**
|
|
@@ -2588,7 +2789,6 @@ function restartProcess() {
|
|
|
2588
2789
|
}
|
|
2589
2790
|
|
|
2590
2791
|
import * as WebSocket from 'ws';
|
|
2591
|
-
const globalModulesDir = require('global-modules');
|
|
2592
2792
|
|
|
2593
2793
|
const wss = new WebSocket.Server({ port: 8080 });
|
|
2594
2794
|
|
|
@@ -2607,13 +2807,6 @@ ws.onmessage = (event) => {
|
|
|
2607
2807
|
console.log(`Received message => ${event.data}`);
|
|
2608
2808
|
};
|
|
2609
2809
|
|
|
2610
|
-
*/
|
|
2611
|
-
/*
|
|
2612
|
-
// In Matterbridge
|
|
2613
|
-
global.matterbridgeInstance = Matterbridge.loadInstance();
|
|
2614
|
-
|
|
2615
|
-
// In plugins
|
|
2616
|
-
const matterbridge = global.matterbridgeInstance;
|
|
2617
2810
|
*/
|
|
2618
2811
|
/*
|
|
2619
2812
|
npx create-react-app matterbridge-frontend
|