matterbridge 3.0.1-dev-20250501-4f463f9 → 3.0.1-dev-20250502-f374923
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 +13 -10
- package/README-DOCKER.md +29 -15
- package/dist/frontend.js +375 -466
- package/dist/matterbridge.js +8 -5
- package/dist/utils/isvalid.js +16 -0
- package/frontend/build/asset-manifest.json +3 -3
- package/frontend/build/index.html +1 -1
- package/frontend/build/static/js/{main.356788d7.js → main.53d64feb.js} +3 -3
- package/frontend/build/static/js/{main.356788d7.js.map → main.53d64feb.js.map} +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- /package/frontend/build/static/js/{main.356788d7.js.LICENSE.txt → main.53d64feb.js.LICENSE.txt} +0 -0
package/dist/frontend.js
CHANGED
|
@@ -8,7 +8,7 @@ import express from 'express';
|
|
|
8
8
|
import WebSocket, { WebSocketServer } from 'ws';
|
|
9
9
|
import multer from 'multer';
|
|
10
10
|
import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, wr, YELLOW, nt } from './logger/export.js';
|
|
11
|
-
import { createZip, deepCopy, isValidArray, isValidNumber, isValidObject, isValidString } from './utils/export.js';
|
|
11
|
+
import { createZip, deepCopy, isValidArray, isValidNumber, isValidObject, isValidString, isValidBoolean } from './utils/export.js';
|
|
12
12
|
import { plg } from './matterbridgeTypes.js';
|
|
13
13
|
import { hasParameter } from './utils/export.js';
|
|
14
14
|
import { BridgedDeviceBasicInformation, PowerSource } from '@matter/main/clusters';
|
|
@@ -21,6 +21,7 @@ export const WS_ID_UPTIME_UPDATE = 5;
|
|
|
21
21
|
export const WS_ID_SNACKBAR = 6;
|
|
22
22
|
export const WS_ID_UPDATE_NEEDED = 7;
|
|
23
23
|
export const WS_ID_STATEUPDATE = 8;
|
|
24
|
+
export const WS_ID_CLOSE_SNACKBAR = 9;
|
|
24
25
|
export const WS_ID_SHELLY_SYS_UPDATE = 100;
|
|
25
26
|
export const WS_ID_SHELLY_MAIN_UPDATE = 101;
|
|
26
27
|
export class Frontend {
|
|
@@ -257,16 +258,6 @@ export class Frontend {
|
|
|
257
258
|
};
|
|
258
259
|
res.status(200).json(memoryReport);
|
|
259
260
|
});
|
|
260
|
-
this.expressApp.get('/api/advertise', express.json(), async (req, res) => {
|
|
261
|
-
const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
|
|
262
|
-
if (pairingCodes) {
|
|
263
|
-
const { manualPairingCode, qrPairingCode } = pairingCodes;
|
|
264
|
-
res.json({ manualPairingCode, qrPairingCode: 'https://project-chip.github.io/connectedhomeip/qrcode.html?data=' + qrPairingCode });
|
|
265
|
-
}
|
|
266
|
-
else {
|
|
267
|
-
res.status(500).json({ error: 'Failed to generate pairing codes' });
|
|
268
|
-
}
|
|
269
|
-
});
|
|
270
261
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
271
262
|
this.log.debug('The frontend sent /api/settings');
|
|
272
263
|
res.json(await this.getApiSettings());
|
|
@@ -275,100 +266,11 @@ export class Frontend {
|
|
|
275
266
|
this.log.debug('The frontend sent /api/plugins');
|
|
276
267
|
res.json(this.getBaseRegisteredPlugins());
|
|
277
268
|
});
|
|
278
|
-
this.expressApp.get('/api/devices', (req, res) => {
|
|
269
|
+
this.expressApp.get('/api/devices', async (req, res) => {
|
|
279
270
|
this.log.debug('The frontend sent /api/devices');
|
|
280
|
-
const devices =
|
|
281
|
-
this.matterbridge.devices.forEach(async (device) => {
|
|
282
|
-
if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
283
|
-
return;
|
|
284
|
-
const cluster = this.getClusterTextFromDevice(device);
|
|
285
|
-
devices.push({
|
|
286
|
-
pluginName: device.plugin,
|
|
287
|
-
type: device.name + ' (0x' + device.deviceType.toString(16).padStart(4, '0') + ')',
|
|
288
|
-
endpoint: device.number,
|
|
289
|
-
name: device.deviceName,
|
|
290
|
-
serial: device.serialNumber,
|
|
291
|
-
productUrl: device.productUrl,
|
|
292
|
-
configUrl: device.configUrl,
|
|
293
|
-
uniqueId: device.uniqueId,
|
|
294
|
-
reachable: this.getReachability(device),
|
|
295
|
-
cluster: cluster,
|
|
296
|
-
});
|
|
297
|
-
});
|
|
271
|
+
const devices = await this.getDevices();
|
|
298
272
|
res.json(devices);
|
|
299
273
|
});
|
|
300
|
-
this.expressApp.get('/api/devices_clusters/:selectedPluginName/:selectedDeviceEndpoint', (req, res) => {
|
|
301
|
-
const selectedPluginName = req.params.selectedPluginName;
|
|
302
|
-
const selectedDeviceEndpoint = parseInt(req.params.selectedDeviceEndpoint, 10);
|
|
303
|
-
this.log.debug(`The frontend sent /api/devices_clusters plugin:${selectedPluginName} endpoint:${selectedDeviceEndpoint}`);
|
|
304
|
-
if (selectedPluginName === 'none') {
|
|
305
|
-
res.json([]);
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
308
|
-
const data = [];
|
|
309
|
-
this.matterbridge.devices.forEach(async (device) => {
|
|
310
|
-
const pluginName = device.plugin;
|
|
311
|
-
if (pluginName === selectedPluginName && device.number === selectedDeviceEndpoint) {
|
|
312
|
-
const endpointServer = EndpointServer.forEndpoint(device);
|
|
313
|
-
const clusterServers = endpointServer.getAllClusterServers();
|
|
314
|
-
clusterServers.forEach((clusterServer) => {
|
|
315
|
-
Object.entries(clusterServer.attributes).forEach(([key, value]) => {
|
|
316
|
-
if (clusterServer.name === 'EveHistory')
|
|
317
|
-
return;
|
|
318
|
-
let attributeValue;
|
|
319
|
-
try {
|
|
320
|
-
if (typeof value.getLocal() === 'object')
|
|
321
|
-
attributeValue = stringify(value.getLocal());
|
|
322
|
-
else
|
|
323
|
-
attributeValue = value.getLocal().toString();
|
|
324
|
-
}
|
|
325
|
-
catch (error) {
|
|
326
|
-
attributeValue = 'Fabric-Scoped';
|
|
327
|
-
this.log.debug(`GetLocal value ${error} in clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute: ${key}(${value.id})`);
|
|
328
|
-
}
|
|
329
|
-
data.push({
|
|
330
|
-
endpoint: device.number ? device.number.toString() : '...',
|
|
331
|
-
clusterName: clusterServer.name,
|
|
332
|
-
clusterId: '0x' + clusterServer.id.toString(16).padStart(2, '0'),
|
|
333
|
-
attributeName: key,
|
|
334
|
-
attributeId: '0x' + value.id.toString(16).padStart(2, '0'),
|
|
335
|
-
attributeValue,
|
|
336
|
-
});
|
|
337
|
-
});
|
|
338
|
-
});
|
|
339
|
-
endpointServer.getChildEndpoints().forEach((childEndpoint) => {
|
|
340
|
-
const name = childEndpoint.name;
|
|
341
|
-
const clusterServers = childEndpoint.getAllClusterServers();
|
|
342
|
-
clusterServers.forEach((clusterServer) => {
|
|
343
|
-
Object.entries(clusterServer.attributes).forEach(([key, value]) => {
|
|
344
|
-
if (clusterServer.name === 'EveHistory')
|
|
345
|
-
return;
|
|
346
|
-
let attributeValue;
|
|
347
|
-
try {
|
|
348
|
-
if (typeof value.getLocal() === 'object')
|
|
349
|
-
attributeValue = stringify(value.getLocal());
|
|
350
|
-
else
|
|
351
|
-
attributeValue = value.getLocal().toString();
|
|
352
|
-
}
|
|
353
|
-
catch (error) {
|
|
354
|
-
attributeValue = 'Fabric-Scoped';
|
|
355
|
-
this.log.debug(`GetLocal error ${error} in clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute: ${key}(${value.id})`);
|
|
356
|
-
}
|
|
357
|
-
data.push({
|
|
358
|
-
endpoint: (childEndpoint.number ? childEndpoint.number.toString() : '...') + (name ? ' (' + name + ')' : ''),
|
|
359
|
-
clusterName: clusterServer.name,
|
|
360
|
-
clusterId: '0x' + clusterServer.id.toString(16).padStart(2, '0'),
|
|
361
|
-
attributeName: key,
|
|
362
|
-
attributeId: '0x' + value.id.toString(16).padStart(2, '0'),
|
|
363
|
-
attributeValue,
|
|
364
|
-
});
|
|
365
|
-
});
|
|
366
|
-
});
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
});
|
|
370
|
-
res.json(data);
|
|
371
|
-
});
|
|
372
274
|
this.expressApp.get('/api/view-mblog', async (req, res) => {
|
|
373
275
|
this.log.debug('The frontend sent /api/view-mblog');
|
|
374
276
|
try {
|
|
@@ -511,316 +413,6 @@ export class Frontend {
|
|
|
511
413
|
}
|
|
512
414
|
});
|
|
513
415
|
});
|
|
514
|
-
this.expressApp.post('/api/command/:command/:param', express.json(), async (req, res) => {
|
|
515
|
-
const command = req.params.command;
|
|
516
|
-
let param = req.params.param;
|
|
517
|
-
this.log.debug(`The frontend sent /api/command/${command}/${param}`);
|
|
518
|
-
if (!command) {
|
|
519
|
-
res.status(400).json({ error: 'No command provided' });
|
|
520
|
-
return;
|
|
521
|
-
}
|
|
522
|
-
this.log.debug(`Received frontend command: ${command}:${param}`);
|
|
523
|
-
if (command === 'setpassword') {
|
|
524
|
-
const password = param.slice(1, -1);
|
|
525
|
-
this.log.debug('setpassword', param, password);
|
|
526
|
-
await this.matterbridge.nodeContext?.set('password', password);
|
|
527
|
-
res.json({ message: 'Command received' });
|
|
528
|
-
return;
|
|
529
|
-
}
|
|
530
|
-
if (command === 'setbridgemode') {
|
|
531
|
-
this.log.debug(`setbridgemode: ${param}`);
|
|
532
|
-
this.wssSendRestartRequired();
|
|
533
|
-
await this.matterbridge.nodeContext?.set('bridgeMode', param);
|
|
534
|
-
res.json({ message: 'Command received' });
|
|
535
|
-
return;
|
|
536
|
-
}
|
|
537
|
-
if (command === 'backup') {
|
|
538
|
-
this.log.notice(`Prepairing the backup...`);
|
|
539
|
-
await createZip(path.join(os.tmpdir(), `matterbridge.backup.zip`), path.join(this.matterbridge.matterbridgeDirectory), path.join(this.matterbridge.matterbridgePluginDirectory));
|
|
540
|
-
this.log.notice(`Backup ready to be downloaded.`);
|
|
541
|
-
this.wssSendSnackbarMessage('Backup ready to be downloaded', 10);
|
|
542
|
-
res.json({ message: 'Command received' });
|
|
543
|
-
return;
|
|
544
|
-
}
|
|
545
|
-
if (command === 'setmbloglevel') {
|
|
546
|
-
this.log.debug('Matterbridge log level:', param);
|
|
547
|
-
if (param === 'Debug') {
|
|
548
|
-
this.log.logLevel = "debug";
|
|
549
|
-
}
|
|
550
|
-
else if (param === 'Info') {
|
|
551
|
-
this.log.logLevel = "info";
|
|
552
|
-
}
|
|
553
|
-
else if (param === 'Notice') {
|
|
554
|
-
this.log.logLevel = "notice";
|
|
555
|
-
}
|
|
556
|
-
else if (param === 'Warn') {
|
|
557
|
-
this.log.logLevel = "warn";
|
|
558
|
-
}
|
|
559
|
-
else if (param === 'Error') {
|
|
560
|
-
this.log.logLevel = "error";
|
|
561
|
-
}
|
|
562
|
-
else if (param === 'Fatal') {
|
|
563
|
-
this.log.logLevel = "fatal";
|
|
564
|
-
}
|
|
565
|
-
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
566
|
-
await this.matterbridge.setLogLevel(this.log.logLevel);
|
|
567
|
-
res.json({ message: 'Command received' });
|
|
568
|
-
return;
|
|
569
|
-
}
|
|
570
|
-
if (command === 'setmjloglevel') {
|
|
571
|
-
this.log.debug('Matter.js log level:', param);
|
|
572
|
-
if (param === 'Debug') {
|
|
573
|
-
Logger.defaultLogLevel = MatterLogLevel.DEBUG;
|
|
574
|
-
}
|
|
575
|
-
else if (param === 'Info') {
|
|
576
|
-
Logger.defaultLogLevel = MatterLogLevel.INFO;
|
|
577
|
-
}
|
|
578
|
-
else if (param === 'Notice') {
|
|
579
|
-
Logger.defaultLogLevel = MatterLogLevel.NOTICE;
|
|
580
|
-
}
|
|
581
|
-
else if (param === 'Warn') {
|
|
582
|
-
Logger.defaultLogLevel = MatterLogLevel.WARN;
|
|
583
|
-
}
|
|
584
|
-
else if (param === 'Error') {
|
|
585
|
-
Logger.defaultLogLevel = MatterLogLevel.ERROR;
|
|
586
|
-
}
|
|
587
|
-
else if (param === 'Fatal') {
|
|
588
|
-
Logger.defaultLogLevel = MatterLogLevel.FATAL;
|
|
589
|
-
}
|
|
590
|
-
await this.matterbridge.nodeContext?.set('matterLogLevel', Logger.defaultLogLevel);
|
|
591
|
-
res.json({ message: 'Command received' });
|
|
592
|
-
return;
|
|
593
|
-
}
|
|
594
|
-
if (command === 'setmdnsinterface') {
|
|
595
|
-
if (param === 'json' && isValidString(req.body.value)) {
|
|
596
|
-
this.matterbridge.matterbridgeInformation.mattermdnsinterface = req.body.value;
|
|
597
|
-
this.log.debug(`Matter.js mdns interface: ${req.body.value === '' ? 'all interfaces' : req.body.value}`);
|
|
598
|
-
await this.matterbridge.nodeContext?.set('mattermdnsinterface', req.body.value);
|
|
599
|
-
}
|
|
600
|
-
res.json({ message: 'Command received' });
|
|
601
|
-
return;
|
|
602
|
-
}
|
|
603
|
-
if (command === 'setipv4address') {
|
|
604
|
-
if (param === 'json' && isValidString(req.body.value)) {
|
|
605
|
-
this.log.debug(`Matter.js ipv4 address: ${req.body.value === '' ? 'all ipv4 addresses' : req.body.value}`);
|
|
606
|
-
this.matterbridge.matterbridgeInformation.matteripv4address = req.body.value;
|
|
607
|
-
await this.matterbridge.nodeContext?.set('matteripv4address', req.body.value);
|
|
608
|
-
}
|
|
609
|
-
res.json({ message: 'Command received' });
|
|
610
|
-
return;
|
|
611
|
-
}
|
|
612
|
-
if (command === 'setipv6address') {
|
|
613
|
-
if (param === 'json' && isValidString(req.body.value)) {
|
|
614
|
-
this.log.debug(`Matter.js ipv6 address: ${req.body.value === '' ? 'all ipv6 addresses' : req.body.value}`);
|
|
615
|
-
this.matterbridge.matterbridgeInformation.matteripv6address = req.body.value;
|
|
616
|
-
await this.matterbridge.nodeContext?.set('matteripv6address', req.body.value);
|
|
617
|
-
}
|
|
618
|
-
res.json({ message: 'Command received' });
|
|
619
|
-
return;
|
|
620
|
-
}
|
|
621
|
-
if (command === 'setmatterport') {
|
|
622
|
-
const port = Math.min(Math.max(parseInt(req.body.value), 5540), 5560);
|
|
623
|
-
this.matterbridge.matterbridgeInformation.matterPort = port;
|
|
624
|
-
this.log.debug(`Set matter commissioning port to ${CYAN}${port}${db}`);
|
|
625
|
-
await this.matterbridge.nodeContext?.set('matterport', port);
|
|
626
|
-
res.json({ message: 'Command received' });
|
|
627
|
-
return;
|
|
628
|
-
}
|
|
629
|
-
if (command === 'setmatterdiscriminator') {
|
|
630
|
-
const discriminator = Math.min(Math.max(parseInt(req.body.value), 1000), 4095);
|
|
631
|
-
this.matterbridge.matterbridgeInformation.matterDiscriminator = discriminator;
|
|
632
|
-
this.log.debug(`Set matter commissioning discriminator to ${CYAN}${discriminator}${db}`);
|
|
633
|
-
await this.matterbridge.nodeContext?.set('matterdiscriminator', discriminator);
|
|
634
|
-
res.json({ message: 'Command received' });
|
|
635
|
-
return;
|
|
636
|
-
}
|
|
637
|
-
if (command === 'setmatterpasscode') {
|
|
638
|
-
const passcode = Math.min(Math.max(parseInt(req.body.value), 10000000), 90000000);
|
|
639
|
-
this.matterbridge.matterbridgeInformation.matterPasscode = passcode;
|
|
640
|
-
this.log.debug(`Set matter commissioning passcode to ${CYAN}${passcode}${db}`);
|
|
641
|
-
await this.matterbridge.nodeContext?.set('matterpasscode', passcode);
|
|
642
|
-
res.json({ message: 'Command received' });
|
|
643
|
-
return;
|
|
644
|
-
}
|
|
645
|
-
if (command === 'setmblogfile') {
|
|
646
|
-
this.log.debug('Matterbridge file log:', param);
|
|
647
|
-
this.matterbridge.matterbridgeInformation.fileLogger = param === 'true';
|
|
648
|
-
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', param === 'true');
|
|
649
|
-
if (param === 'true')
|
|
650
|
-
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug", true);
|
|
651
|
-
else
|
|
652
|
-
AnsiLogger.setGlobalLogfile(undefined);
|
|
653
|
-
res.json({ message: 'Command received' });
|
|
654
|
-
return;
|
|
655
|
-
}
|
|
656
|
-
if (command === 'setmjlogfile') {
|
|
657
|
-
this.log.debug('Matter file log:', param);
|
|
658
|
-
this.matterbridge.matterbridgeInformation.matterFileLogger = param === 'true';
|
|
659
|
-
await this.matterbridge.nodeContext?.set('matterFileLog', param === 'true');
|
|
660
|
-
if (param === 'true') {
|
|
661
|
-
try {
|
|
662
|
-
Logger.addLogger('matterfilelogger', await this.matterbridge.createMatterFileLogger(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), true), {
|
|
663
|
-
defaultLogLevel: MatterLogLevel.DEBUG,
|
|
664
|
-
logFormat: MatterLogFormat.PLAIN,
|
|
665
|
-
});
|
|
666
|
-
}
|
|
667
|
-
catch (error) {
|
|
668
|
-
this.log.debug(`Error adding the matterfilelogger for file ${CYAN}${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
else {
|
|
672
|
-
try {
|
|
673
|
-
Logger.removeLogger('matterfilelogger');
|
|
674
|
-
}
|
|
675
|
-
catch (error) {
|
|
676
|
-
this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
res.json({ message: 'Command received' });
|
|
680
|
-
return;
|
|
681
|
-
}
|
|
682
|
-
if (command === 'unregister') {
|
|
683
|
-
await this.matterbridge.unregisterAndShutdownProcess();
|
|
684
|
-
res.json({ message: 'Command received' });
|
|
685
|
-
return;
|
|
686
|
-
}
|
|
687
|
-
if (command === 'reset') {
|
|
688
|
-
await this.matterbridge.shutdownProcessAndReset();
|
|
689
|
-
res.json({ message: 'Command received' });
|
|
690
|
-
return;
|
|
691
|
-
}
|
|
692
|
-
if (command === 'factoryreset') {
|
|
693
|
-
await this.matterbridge.shutdownProcessAndFactoryReset();
|
|
694
|
-
res.json({ message: 'Command received' });
|
|
695
|
-
return;
|
|
696
|
-
}
|
|
697
|
-
if (command === 'shutdown') {
|
|
698
|
-
await this.matterbridge.shutdownProcess();
|
|
699
|
-
res.json({ message: 'Command received' });
|
|
700
|
-
return;
|
|
701
|
-
}
|
|
702
|
-
if (command === 'restart') {
|
|
703
|
-
await this.matterbridge.restartProcess();
|
|
704
|
-
res.json({ message: 'Command received' });
|
|
705
|
-
return;
|
|
706
|
-
}
|
|
707
|
-
if (command === 'update') {
|
|
708
|
-
await this.matterbridge.updateProcess();
|
|
709
|
-
this.wssSendRestartRequired();
|
|
710
|
-
res.json({ message: 'Command received' });
|
|
711
|
-
return;
|
|
712
|
-
}
|
|
713
|
-
if (command === 'saveconfig') {
|
|
714
|
-
param = param.replace(/\*/g, '\\');
|
|
715
|
-
this.log.info(`Saving config for plugin ${plg}${param}${nf}...`);
|
|
716
|
-
if (!this.matterbridge.plugins.has(param)) {
|
|
717
|
-
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
718
|
-
}
|
|
719
|
-
else {
|
|
720
|
-
const plugin = this.matterbridge.plugins.get(param);
|
|
721
|
-
if (!plugin)
|
|
722
|
-
return;
|
|
723
|
-
this.matterbridge.plugins.saveConfigFromJson(plugin, req.body);
|
|
724
|
-
this.wssSendSnackbarMessage(`Saved config for plugin ${param}`);
|
|
725
|
-
this.wssSendRestartRequired();
|
|
726
|
-
}
|
|
727
|
-
res.json({ message: 'Command received' });
|
|
728
|
-
return;
|
|
729
|
-
}
|
|
730
|
-
if (command === 'installplugin') {
|
|
731
|
-
param = param.replace(/\*/g, '\\');
|
|
732
|
-
this.log.info(`Installing plugin ${plg}${param}${nf}...`);
|
|
733
|
-
this.wssSendSnackbarMessage(`Installing package ${param}. Please wait...`);
|
|
734
|
-
try {
|
|
735
|
-
await this.matterbridge.spawnCommand('npm', ['install', '-g', param, '--omit=dev', '--verbose']);
|
|
736
|
-
this.log.info(`Plugin ${plg}${param}${nf} installed. Full restart required.`);
|
|
737
|
-
this.wssSendSnackbarMessage(`Installed package ${param}`, 10, 'success');
|
|
738
|
-
}
|
|
739
|
-
catch (error) {
|
|
740
|
-
this.log.error(`Error installing plugin ${plg}${param}${er}`);
|
|
741
|
-
this.wssSendSnackbarMessage(`Package ${param} not installed`, 10, 'error');
|
|
742
|
-
}
|
|
743
|
-
this.wssSendRestartRequired();
|
|
744
|
-
param = param.split('@')[0];
|
|
745
|
-
if (param === 'matterbridge') {
|
|
746
|
-
res.json({ message: 'Command received' });
|
|
747
|
-
return;
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
if (command === 'addplugin' || command === 'installplugin') {
|
|
751
|
-
param = param.replace(/\*/g, '\\');
|
|
752
|
-
const plugin = await this.matterbridge.plugins.add(param);
|
|
753
|
-
if (plugin) {
|
|
754
|
-
this.wssSendSnackbarMessage(`Added plugin ${param}`);
|
|
755
|
-
if (this.matterbridge.bridgeMode === 'childbridge') {
|
|
756
|
-
this.matterbridge.createDynamicPlugin(plugin, true);
|
|
757
|
-
}
|
|
758
|
-
this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true).then(() => {
|
|
759
|
-
this.wssSendRefreshRequired('plugins');
|
|
760
|
-
});
|
|
761
|
-
}
|
|
762
|
-
res.json({ message: 'Command received' });
|
|
763
|
-
return;
|
|
764
|
-
}
|
|
765
|
-
if (command === 'removeplugin') {
|
|
766
|
-
if (!this.matterbridge.plugins.has(param)) {
|
|
767
|
-
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
768
|
-
}
|
|
769
|
-
else {
|
|
770
|
-
const plugin = this.matterbridge.plugins.get(param);
|
|
771
|
-
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
772
|
-
await this.matterbridge.plugins.remove(param);
|
|
773
|
-
this.wssSendSnackbarMessage(`Removed plugin ${param}`);
|
|
774
|
-
this.wssSendRefreshRequired('plugins');
|
|
775
|
-
}
|
|
776
|
-
res.json({ message: 'Command received' });
|
|
777
|
-
return;
|
|
778
|
-
}
|
|
779
|
-
if (command === 'enableplugin') {
|
|
780
|
-
if (!this.matterbridge.plugins.has(param)) {
|
|
781
|
-
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
782
|
-
}
|
|
783
|
-
else {
|
|
784
|
-
const plugin = this.matterbridge.plugins.get(param);
|
|
785
|
-
if (plugin && !plugin.enabled) {
|
|
786
|
-
plugin.locked = undefined;
|
|
787
|
-
plugin.error = undefined;
|
|
788
|
-
plugin.loaded = undefined;
|
|
789
|
-
plugin.started = undefined;
|
|
790
|
-
plugin.configured = undefined;
|
|
791
|
-
plugin.platform = undefined;
|
|
792
|
-
plugin.registeredDevices = undefined;
|
|
793
|
-
plugin.addedDevices = undefined;
|
|
794
|
-
await this.matterbridge.plugins.enable(param);
|
|
795
|
-
this.wssSendSnackbarMessage(`Enabled plugin ${param}`);
|
|
796
|
-
if (this.matterbridge.bridgeMode === 'childbridge' && plugin.type === 'DynamicPlatform') {
|
|
797
|
-
this.matterbridge.createDynamicPlugin(plugin, true);
|
|
798
|
-
}
|
|
799
|
-
this.matterbridge.plugins.load(plugin, true, 'The plugin has been enabled', true).then(() => {
|
|
800
|
-
this.wssSendRefreshRequired('plugins');
|
|
801
|
-
});
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
res.json({ message: 'Command received' });
|
|
805
|
-
return;
|
|
806
|
-
}
|
|
807
|
-
if (command === 'disableplugin') {
|
|
808
|
-
if (!this.matterbridge.plugins.has(param)) {
|
|
809
|
-
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
810
|
-
}
|
|
811
|
-
else {
|
|
812
|
-
const plugin = this.matterbridge.plugins.get(param);
|
|
813
|
-
if (plugin && plugin.enabled) {
|
|
814
|
-
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true);
|
|
815
|
-
await this.matterbridge.plugins.disable(param);
|
|
816
|
-
this.wssSendSnackbarMessage(`Disabled plugin ${param}`);
|
|
817
|
-
this.wssSendRefreshRequired('plugins');
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
res.json({ message: 'Command received' });
|
|
821
|
-
return;
|
|
822
|
-
}
|
|
823
|
-
});
|
|
824
416
|
this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
|
|
825
417
|
const { filename } = req.body;
|
|
826
418
|
const file = req.file;
|
|
@@ -829,7 +421,7 @@ export class Frontend {
|
|
|
829
421
|
res.status(400).send('Invalid request: file and filename are required');
|
|
830
422
|
return;
|
|
831
423
|
}
|
|
832
|
-
this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait
|
|
424
|
+
this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
|
|
833
425
|
const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
|
|
834
426
|
try {
|
|
835
427
|
await fs.rename(file.path, filePath);
|
|
@@ -837,6 +429,7 @@ export class Frontend {
|
|
|
837
429
|
if (filename.endsWith('.tgz')) {
|
|
838
430
|
await this.matterbridge.spawnCommand('npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
|
|
839
431
|
this.log.info(`Plugin package ${plg}${filename}${nf} installed successfully. Full restart required.`);
|
|
432
|
+
this.wssSendCloseSnackbarMessage(`Installing package ${filename}. Please wait...`);
|
|
840
433
|
this.wssSendSnackbarMessage(`Installed package ${filename}`, 10, 'success');
|
|
841
434
|
this.wssSendRestartRequired();
|
|
842
435
|
res.send(`Plugin package ${filename} uploaded and installed successfully`);
|
|
@@ -846,6 +439,7 @@ export class Frontend {
|
|
|
846
439
|
}
|
|
847
440
|
catch (err) {
|
|
848
441
|
this.log.error(`Error uploading or installing plugin package file ${plg}${filename}${er}:`, err);
|
|
442
|
+
this.wssSendCloseSnackbarMessage(`Installing package ${filename}. Please wait...`);
|
|
849
443
|
this.wssSendSnackbarMessage(`Error uploading or installing plugin package ${filename}`, 10, 'error');
|
|
850
444
|
res.status(500).send(`Error uploading or installing plugin package ${filename}`);
|
|
851
445
|
}
|
|
@@ -1133,6 +727,7 @@ export class Frontend {
|
|
|
1133
727
|
started: plugin.started,
|
|
1134
728
|
configured: plugin.configured,
|
|
1135
729
|
paired: plugin.paired,
|
|
730
|
+
restartRequired: plugin.restartRequired,
|
|
1136
731
|
fabricInformations: plugin.fabricInformations,
|
|
1137
732
|
sessionInformations: plugin.sessionInformations,
|
|
1138
733
|
registeredDevices: plugin.registeredDevices,
|
|
@@ -1147,6 +742,30 @@ export class Frontend {
|
|
|
1147
742
|
}
|
|
1148
743
|
return baseRegisteredPlugins;
|
|
1149
744
|
}
|
|
745
|
+
async getDevices(pluginName) {
|
|
746
|
+
const devices = [];
|
|
747
|
+
this.matterbridge.devices.forEach(async (device) => {
|
|
748
|
+
if (pluginName && pluginName !== device.plugin)
|
|
749
|
+
return;
|
|
750
|
+
if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
751
|
+
return;
|
|
752
|
+
const cluster = this.getClusterTextFromDevice(device);
|
|
753
|
+
devices.push({
|
|
754
|
+
pluginName: device.plugin,
|
|
755
|
+
type: device.name + ' (0x' + device.deviceType.toString(16).padStart(4, '0') + ')',
|
|
756
|
+
endpoint: device.number,
|
|
757
|
+
name: device.deviceName,
|
|
758
|
+
serial: device.serialNumber,
|
|
759
|
+
productUrl: device.productUrl,
|
|
760
|
+
configUrl: device.configUrl,
|
|
761
|
+
uniqueId: device.uniqueId,
|
|
762
|
+
reachable: this.getReachability(device),
|
|
763
|
+
powerSource: this.getPowerSource(device),
|
|
764
|
+
cluster: cluster,
|
|
765
|
+
});
|
|
766
|
+
});
|
|
767
|
+
return devices;
|
|
768
|
+
}
|
|
1150
769
|
async wsMessageHandler(client, message) {
|
|
1151
770
|
let data;
|
|
1152
771
|
try {
|
|
@@ -1180,97 +799,230 @@ export class Frontend {
|
|
|
1180
799
|
}
|
|
1181
800
|
}
|
|
1182
801
|
else if (data.method === '/api/install') {
|
|
1183
|
-
if (!isValidString(data.params.packageName, 10)) {
|
|
1184
|
-
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter
|
|
802
|
+
if (!isValidString(data.params.packageName, 10) || !isValidBoolean(data.params.restart)) {
|
|
803
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter in /api/install' }));
|
|
1185
804
|
return;
|
|
1186
805
|
}
|
|
1187
|
-
this.wssSendSnackbarMessage(`Installing package ${data.params.packageName}
|
|
806
|
+
this.wssSendSnackbarMessage(`Installing package ${data.params.packageName}...`, 0);
|
|
1188
807
|
this.matterbridge
|
|
1189
808
|
.spawnCommand('npm', ['install', '-g', data.params.packageName, '--omit=dev', '--verbose'])
|
|
1190
809
|
.then((response) => {
|
|
1191
810
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response }));
|
|
1192
|
-
this.
|
|
1193
|
-
|
|
1194
|
-
|
|
811
|
+
this.wssSendCloseSnackbarMessage(`Installing package ${data.params.packageName}...`);
|
|
812
|
+
this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`, 5, 'success');
|
|
813
|
+
if (data.params.restart === false) {
|
|
814
|
+
data.params.packageName = data.params.packageName.replace(/@.*$/, '');
|
|
815
|
+
this.matterbridge.plugins.add(data.params.packageName).then((plugin) => {
|
|
816
|
+
if (plugin) {
|
|
817
|
+
this.wssSendSnackbarMessage(`Added plugin ${data.params.pluginNameOrPath}`, 5, 'success');
|
|
818
|
+
this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true).then(() => {
|
|
819
|
+
this.wssSendSnackbarMessage(`Started plugin ${data.params.pluginNameOrPath}`, 5, 'success');
|
|
820
|
+
this.wssSendRefreshRequired('plugins');
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
else {
|
|
824
|
+
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
825
|
+
this.wssSendRefreshRequired('plugins');
|
|
826
|
+
this.wssSendRestartRequired();
|
|
827
|
+
}
|
|
828
|
+
});
|
|
1195
829
|
}
|
|
1196
830
|
else {
|
|
1197
831
|
if (this.matterbridge.restartMode !== '') {
|
|
1198
832
|
this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
|
|
1199
833
|
this.matterbridge.shutdownProcess();
|
|
1200
834
|
}
|
|
1201
|
-
else
|
|
835
|
+
else {
|
|
1202
836
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
837
|
+
this.wssSendRestartRequired();
|
|
838
|
+
}
|
|
1203
839
|
}
|
|
1204
840
|
})
|
|
1205
841
|
.catch((error) => {
|
|
1206
842
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: error instanceof Error ? error.message : error }));
|
|
1207
|
-
this.
|
|
843
|
+
this.wssSendCloseSnackbarMessage(`Installing package ${data.params.packageName}...`);
|
|
844
|
+
this.wssSendSnackbarMessage(`Package ${data.params.packageName} not installed`, 10, 'error');
|
|
1208
845
|
});
|
|
1209
|
-
return;
|
|
1210
846
|
}
|
|
1211
847
|
else if (data.method === '/api/uninstall') {
|
|
1212
848
|
if (!isValidString(data.params.packageName, 10)) {
|
|
1213
849
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' }));
|
|
1214
850
|
return;
|
|
1215
851
|
}
|
|
1216
|
-
this.
|
|
852
|
+
const plugin = this.matterbridge.plugins.get(data.params.packageName);
|
|
853
|
+
if (plugin) {
|
|
854
|
+
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
855
|
+
await this.matterbridge.plugins.remove(data.params.packageName);
|
|
856
|
+
this.wssSendSnackbarMessage(`Removed plugin ${data.params.packageName}`, 5, 'success');
|
|
857
|
+
this.wssSendRefreshRequired('plugins');
|
|
858
|
+
this.wssSendRefreshRequired('devices');
|
|
859
|
+
}
|
|
860
|
+
this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
|
|
1217
861
|
this.matterbridge
|
|
1218
862
|
.spawnCommand('npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
|
|
1219
863
|
.then((response) => {
|
|
1220
864
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response }));
|
|
865
|
+
this.wssSendCloseSnackbarMessage(`Uninstalling package ${data.params.packageName}...`);
|
|
866
|
+
this.wssSendSnackbarMessage(`Uninstalled package ${data.params.packageName}`, 5, 'success');
|
|
1221
867
|
})
|
|
1222
868
|
.catch((error) => {
|
|
1223
869
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: error instanceof Error ? error.message : error }));
|
|
1224
|
-
this.
|
|
870
|
+
this.wssSendCloseSnackbarMessage(`Uninstalling package ${data.params.packageName}...`);
|
|
871
|
+
this.wssSendSnackbarMessage(`Package ${data.params.packageName} not uninstalled`, 10, 'error');
|
|
1225
872
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1226
873
|
});
|
|
1227
|
-
|
|
874
|
+
}
|
|
875
|
+
else if (data.method === '/api/addplugin') {
|
|
876
|
+
if (!isValidString(data.params.pluginNameOrPath, 10)) {
|
|
877
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginNameOrPath in /api/addplugin' }));
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
data.params.pluginNameOrPath = data.params.pluginNameOrPath.replace(/@.*$/, '');
|
|
881
|
+
if (this.matterbridge.plugins.has(data.params.pluginNameOrPath)) {
|
|
882
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Plugin ${data.params.pluginNameOrPath} already added` }));
|
|
883
|
+
this.wssSendSnackbarMessage(`Plugin ${data.params.pluginNameOrPath} already added`, 10, 'warning');
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
const plugin = await this.matterbridge.plugins.add(data.params.pluginNameOrPath);
|
|
887
|
+
if (plugin) {
|
|
888
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: plugin.name }));
|
|
889
|
+
this.wssSendSnackbarMessage(`Added plugin ${data.params.pluginNameOrPath}`, 5, 'success');
|
|
890
|
+
this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true).then(() => {
|
|
891
|
+
this.wssSendRefreshRequired('plugins');
|
|
892
|
+
this.wssSendRefreshRequired('devices');
|
|
893
|
+
this.wssSendSnackbarMessage(`Started plugin ${data.params.pluginNameOrPath}`, 5, 'success');
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
else {
|
|
897
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Plugin ${data.params.pluginNameOrPath} not added` }));
|
|
898
|
+
this.wssSendSnackbarMessage(`Plugin ${data.params.pluginNameOrPath} not added`, 10, 'error');
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
else if (data.method === '/api/removeplugin') {
|
|
902
|
+
if (!isValidString(data.params.pluginName, 10) || !this.matterbridge.plugins.has(data.params.pluginName)) {
|
|
903
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/removeplugin' }));
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
const plugin = this.matterbridge.plugins.get(data.params.pluginName);
|
|
907
|
+
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
908
|
+
await this.matterbridge.plugins.remove(data.params.pluginName);
|
|
909
|
+
this.wssSendSnackbarMessage(`Removed plugin ${data.params.pluginName}`, 5, 'success');
|
|
910
|
+
this.wssSendRefreshRequired('plugins');
|
|
911
|
+
this.wssSendRefreshRequired('devices');
|
|
912
|
+
}
|
|
913
|
+
else if (data.method === '/api/enableplugin') {
|
|
914
|
+
if (!isValidString(data.params.pluginName, 10) || !this.matterbridge.plugins.has(data.params.pluginName)) {
|
|
915
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/enableplugin' }));
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
const plugin = this.matterbridge.plugins.get(data.params.pluginName);
|
|
919
|
+
if (plugin && !plugin.enabled) {
|
|
920
|
+
plugin.locked = undefined;
|
|
921
|
+
plugin.error = undefined;
|
|
922
|
+
plugin.loaded = undefined;
|
|
923
|
+
plugin.started = undefined;
|
|
924
|
+
plugin.configured = undefined;
|
|
925
|
+
plugin.platform = undefined;
|
|
926
|
+
plugin.registeredDevices = undefined;
|
|
927
|
+
plugin.addedDevices = undefined;
|
|
928
|
+
await this.matterbridge.plugins.enable(data.params.pluginName);
|
|
929
|
+
this.wssSendSnackbarMessage(`Enabled plugin ${data.params.pluginName}`, 5, 'success');
|
|
930
|
+
this.matterbridge.plugins.load(plugin, true, 'The plugin has been enabled', true).then(() => {
|
|
931
|
+
this.wssSendRefreshRequired('plugins');
|
|
932
|
+
this.wssSendRefreshRequired('devices');
|
|
933
|
+
this.wssSendSnackbarMessage(`Started plugin ${data.params.pluginName}`, 5, 'success');
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
else if (data.method === '/api/disableplugin') {
|
|
938
|
+
if (!isValidString(data.params.pluginName, 10) || !this.matterbridge.plugins.has(data.params.pluginName)) {
|
|
939
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/disableplugin' }));
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
942
|
+
const plugin = this.matterbridge.plugins.get(data.params.pluginName);
|
|
943
|
+
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true);
|
|
944
|
+
await this.matterbridge.plugins.disable(data.params.pluginName);
|
|
945
|
+
this.wssSendSnackbarMessage(`Disabled plugin ${data.params.pluginName}`, 5, 'success');
|
|
946
|
+
this.wssSendRefreshRequired('plugins');
|
|
947
|
+
this.wssSendRefreshRequired('devices');
|
|
948
|
+
}
|
|
949
|
+
else if (data.method === '/api/savepluginconfig') {
|
|
950
|
+
if (!isValidString(data.params.pluginName, 10) || !this.matterbridge.plugins.has(data.params.pluginName)) {
|
|
951
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/savepluginconfig' }));
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
954
|
+
if (!isValidObject(data.params.formData, 5)) {
|
|
955
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter formData in /api/savepluginconfig' }));
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
this.log.info(`Saving config for plugin ${plg}${data.params.pluginName}${nf}...`);
|
|
959
|
+
const plugin = this.matterbridge.plugins.get(data.params.pluginName);
|
|
960
|
+
if (!plugin) {
|
|
961
|
+
this.log.warn(`Plugin ${plg}${data.params.pluginName}${wr} not found in matterbridge`);
|
|
962
|
+
}
|
|
963
|
+
else {
|
|
964
|
+
this.matterbridge.plugins.saveConfigFromJson(plugin, data.params.formData);
|
|
965
|
+
this.wssSendSnackbarMessage(`Saved config for plugin ${data.params.pluginName}`);
|
|
966
|
+
this.wssSendRefreshRequired('plugins');
|
|
967
|
+
this.wssSendRestartRequired();
|
|
968
|
+
}
|
|
1228
969
|
}
|
|
1229
970
|
else if (data.method === '/api/shellysysupdate') {
|
|
1230
971
|
const { triggerShellySysUpdate } = await import('./shelly.js');
|
|
1231
972
|
triggerShellySysUpdate(this.matterbridge);
|
|
1232
|
-
return;
|
|
1233
973
|
}
|
|
1234
974
|
else if (data.method === '/api/shellymainupdate') {
|
|
1235
975
|
const { triggerShellyMainUpdate } = await import('./shelly.js');
|
|
1236
976
|
triggerShellyMainUpdate(this.matterbridge);
|
|
1237
|
-
return;
|
|
1238
977
|
}
|
|
1239
978
|
else if (data.method === '/api/shellycreatesystemlog') {
|
|
1240
979
|
const { createShellySystemLog } = await import('./shelly.js');
|
|
1241
980
|
createShellySystemLog(this.matterbridge);
|
|
1242
|
-
return;
|
|
1243
981
|
}
|
|
1244
982
|
else if (data.method === '/api/shellynetconfig') {
|
|
1245
983
|
this.log.debug('/api/shellynetconfig:', data.params);
|
|
1246
984
|
const { triggerShellyChangeIp: triggerShellyChangeNet } = await import('./shelly.js');
|
|
1247
985
|
triggerShellyChangeNet(this.matterbridge, data.params);
|
|
1248
|
-
return;
|
|
1249
986
|
}
|
|
1250
987
|
else if (data.method === '/api/softreset') {
|
|
1251
988
|
const { triggerShellySoftReset } = await import('./shelly.js');
|
|
1252
989
|
triggerShellySoftReset(this.matterbridge);
|
|
1253
|
-
return;
|
|
1254
990
|
}
|
|
1255
991
|
else if (data.method === '/api/hardreset') {
|
|
1256
992
|
const { triggerShellyHardReset } = await import('./shelly.js');
|
|
1257
993
|
triggerShellyHardReset(this.matterbridge);
|
|
1258
|
-
return;
|
|
1259
994
|
}
|
|
1260
995
|
else if (data.method === '/api/reboot') {
|
|
1261
996
|
const { triggerShellyReboot } = await import('./shelly.js');
|
|
1262
997
|
triggerShellyReboot(this.matterbridge);
|
|
1263
|
-
return;
|
|
1264
998
|
}
|
|
1265
999
|
else if (data.method === '/api/restart') {
|
|
1266
1000
|
this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
|
|
1267
1001
|
await this.matterbridge.restartProcess();
|
|
1268
|
-
return;
|
|
1269
1002
|
}
|
|
1270
1003
|
else if (data.method === '/api/shutdown') {
|
|
1271
1004
|
this.wssSendSnackbarMessage(`Shutting down matterbridge...`, 0);
|
|
1272
1005
|
await this.matterbridge.shutdownProcess();
|
|
1273
|
-
|
|
1006
|
+
}
|
|
1007
|
+
else if (data.method === '/api/create-backup') {
|
|
1008
|
+
this.wssSendSnackbarMessage('Creating backup...', 0);
|
|
1009
|
+
this.log.notice(`Creating the backup...`);
|
|
1010
|
+
await createZip(path.join(os.tmpdir(), `matterbridge.backup.zip`), path.join(this.matterbridge.matterbridgeDirectory), path.join(this.matterbridge.matterbridgePluginDirectory));
|
|
1011
|
+
this.log.notice(`Backup ready to be downloaded.`);
|
|
1012
|
+
this.wssSendCloseSnackbarMessage('Creating backup...');
|
|
1013
|
+
this.wssSendSnackbarMessage('Backup ready to be downloaded', 10);
|
|
1014
|
+
}
|
|
1015
|
+
else if (data.method === '/api/unregister') {
|
|
1016
|
+
this.wssSendSnackbarMessage('Uregistering all bridged devices...', 10);
|
|
1017
|
+
await this.matterbridge.unregisterAndShutdownProcess();
|
|
1018
|
+
}
|
|
1019
|
+
else if (data.method === '/api/reset') {
|
|
1020
|
+
this.wssSendSnackbarMessage('Resetting matterbridge commissioning...', 10);
|
|
1021
|
+
await this.matterbridge.shutdownProcessAndReset();
|
|
1022
|
+
}
|
|
1023
|
+
else if (data.method === '/api/factoryreset') {
|
|
1024
|
+
this.wssSendSnackbarMessage('Factory reset of matterbridge...', 10);
|
|
1025
|
+
await this.matterbridge.shutdownProcessAndFactoryReset();
|
|
1274
1026
|
}
|
|
1275
1027
|
else if (data.method === '/api/advertise') {
|
|
1276
1028
|
const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
|
|
@@ -1280,7 +1032,6 @@ export class Frontend {
|
|
|
1280
1032
|
this.wssSendRefreshRequired('matterbridgeAdvertise');
|
|
1281
1033
|
this.wssSendSnackbarMessage(`Started fabrics share`, 0);
|
|
1282
1034
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: pairingCodes }));
|
|
1283
|
-
return;
|
|
1284
1035
|
}
|
|
1285
1036
|
else if (data.method === '/api/stopadvertise') {
|
|
1286
1037
|
await this.matterbridge.stopAdvertiseServerNode(this.matterbridge.serverNode);
|
|
@@ -1288,41 +1039,17 @@ export class Frontend {
|
|
|
1288
1039
|
this.wssSendRefreshRequired('matterbridgeAdvertise');
|
|
1289
1040
|
this.wssSendSnackbarMessage(`Stopped fabrics share`, 0);
|
|
1290
1041
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src }));
|
|
1291
|
-
return;
|
|
1292
1042
|
}
|
|
1293
1043
|
else if (data.method === '/api/settings') {
|
|
1294
1044
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: await this.getApiSettings() }));
|
|
1295
|
-
return;
|
|
1296
1045
|
}
|
|
1297
1046
|
else if (data.method === '/api/plugins') {
|
|
1298
1047
|
const response = this.getBaseRegisteredPlugins();
|
|
1299
1048
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response }));
|
|
1300
|
-
return;
|
|
1301
1049
|
}
|
|
1302
1050
|
else if (data.method === '/api/devices') {
|
|
1303
|
-
const devices =
|
|
1304
|
-
this.matterbridge.devices.forEach(async (device) => {
|
|
1305
|
-
if (data.params.pluginName && data.params.pluginName !== device.plugin)
|
|
1306
|
-
return;
|
|
1307
|
-
if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
1308
|
-
return;
|
|
1309
|
-
const cluster = this.getClusterTextFromDevice(device);
|
|
1310
|
-
devices.push({
|
|
1311
|
-
pluginName: device.plugin,
|
|
1312
|
-
type: device.name + ' (0x' + device.deviceType.toString(16).padStart(4, '0') + ')',
|
|
1313
|
-
endpoint: device.number,
|
|
1314
|
-
name: device.deviceName,
|
|
1315
|
-
serial: device.serialNumber,
|
|
1316
|
-
productUrl: device.productUrl,
|
|
1317
|
-
configUrl: device.configUrl,
|
|
1318
|
-
uniqueId: device.uniqueId,
|
|
1319
|
-
reachable: this.getReachability(device),
|
|
1320
|
-
powerSource: this.getPowerSource(device),
|
|
1321
|
-
cluster: cluster,
|
|
1322
|
-
});
|
|
1323
|
-
});
|
|
1051
|
+
const devices = await this.getDevices(isValidString(data.params.pluginName) ? data.params.pluginName : undefined);
|
|
1324
1052
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: devices }));
|
|
1325
|
-
return;
|
|
1326
1053
|
}
|
|
1327
1054
|
else if (data.method === '/api/clusters') {
|
|
1328
1055
|
if (!isValidString(data.params.plugin, 10)) {
|
|
@@ -1429,7 +1156,6 @@ export class Frontend {
|
|
|
1429
1156
|
});
|
|
1430
1157
|
});
|
|
1431
1158
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, plugin: data.params.plugin, deviceName, serialNumber, endpoint: data.params.endpoint, deviceTypes, response: clusters }));
|
|
1432
|
-
return;
|
|
1433
1159
|
}
|
|
1434
1160
|
else if (data.method === '/api/select' || data.method === '/api/select/devices') {
|
|
1435
1161
|
if (!isValidString(data.params.plugin, 10)) {
|
|
@@ -1443,7 +1169,6 @@ export class Frontend {
|
|
|
1443
1169
|
}
|
|
1444
1170
|
const selectDeviceValues = plugin.platform?.getSelectDevices().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
|
|
1445
1171
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, plugin: data.params.plugin, response: selectDeviceValues }));
|
|
1446
|
-
return;
|
|
1447
1172
|
}
|
|
1448
1173
|
else if (data.method === '/api/select/entities') {
|
|
1449
1174
|
if (!isValidString(data.params.plugin, 10)) {
|
|
@@ -1457,7 +1182,6 @@ export class Frontend {
|
|
|
1457
1182
|
}
|
|
1458
1183
|
const selectEntityValues = plugin.platform?.getSelectEntities().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
|
|
1459
1184
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, plugin: data.params.plugin, response: selectEntityValues }));
|
|
1460
|
-
return;
|
|
1461
1185
|
}
|
|
1462
1186
|
else if (data.method === '/api/action') {
|
|
1463
1187
|
if (!isValidString(data.params.plugin, 5) || !isValidString(data.params.action, 1)) {
|
|
@@ -1474,6 +1198,185 @@ export class Frontend {
|
|
|
1474
1198
|
this.log.error(`Error in plugin ${plugin.name} action ${data.params.action}: ${error}`);
|
|
1475
1199
|
});
|
|
1476
1200
|
}
|
|
1201
|
+
else if (data.method === '/api/config') {
|
|
1202
|
+
if (!isValidString(data.params.name, 5) || data.params.value === undefined) {
|
|
1203
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter in /api/config' }));
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1206
|
+
this.log.debug(`Received /api/config name ${CYAN}${data.params.name}${db} value ${CYAN}${data.params.value}${db}`);
|
|
1207
|
+
this.log.fatal(`Received /api/config name ${CYAN}${data.params.name}${db} value(${typeof data.params.value}) ${CYAN}${data.params.value}${db}`);
|
|
1208
|
+
switch (data.params.name) {
|
|
1209
|
+
case 'setpassword':
|
|
1210
|
+
if (isValidString(data.params.value)) {
|
|
1211
|
+
await this.matterbridge.nodeContext?.set('password', data.params.value);
|
|
1212
|
+
}
|
|
1213
|
+
break;
|
|
1214
|
+
case 'setbridgemode':
|
|
1215
|
+
if (isValidString(data.params.value, 5)) {
|
|
1216
|
+
await this.matterbridge.nodeContext?.set('bridgeMode', data.params.value);
|
|
1217
|
+
this.wssSendRestartRequired();
|
|
1218
|
+
}
|
|
1219
|
+
break;
|
|
1220
|
+
case 'setmbloglevel':
|
|
1221
|
+
if (isValidString(data.params.value, 4)) {
|
|
1222
|
+
this.log.debug('Matterbridge logger level:', data.params.value);
|
|
1223
|
+
if (data.params.value === 'Debug') {
|
|
1224
|
+
await this.matterbridge.setLogLevel("debug");
|
|
1225
|
+
}
|
|
1226
|
+
else if (data.params.value === 'Info') {
|
|
1227
|
+
await this.matterbridge.setLogLevel("info");
|
|
1228
|
+
}
|
|
1229
|
+
else if (data.params.value === 'Notice') {
|
|
1230
|
+
await this.matterbridge.setLogLevel("notice");
|
|
1231
|
+
}
|
|
1232
|
+
else if (data.params.value === 'Warn') {
|
|
1233
|
+
await this.matterbridge.setLogLevel("warn");
|
|
1234
|
+
}
|
|
1235
|
+
else if (data.params.value === 'Error') {
|
|
1236
|
+
await this.matterbridge.setLogLevel("error");
|
|
1237
|
+
}
|
|
1238
|
+
else if (data.params.value === 'Fatal') {
|
|
1239
|
+
await this.matterbridge.setLogLevel("fatal");
|
|
1240
|
+
}
|
|
1241
|
+
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
1242
|
+
}
|
|
1243
|
+
break;
|
|
1244
|
+
case 'setmblogfile':
|
|
1245
|
+
if (isValidBoolean(data.params.value)) {
|
|
1246
|
+
this.log.debug('Matterbridge file log:', data.params.value);
|
|
1247
|
+
this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
|
|
1248
|
+
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
|
|
1249
|
+
if (data.params.value)
|
|
1250
|
+
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
|
|
1251
|
+
else
|
|
1252
|
+
AnsiLogger.setGlobalLogfile(undefined);
|
|
1253
|
+
}
|
|
1254
|
+
break;
|
|
1255
|
+
case 'setmjloglevel':
|
|
1256
|
+
if (isValidString(data.params.value, 4)) {
|
|
1257
|
+
this.log.debug('Matter logger level:', data.params.value);
|
|
1258
|
+
if (data.params.value === 'Debug') {
|
|
1259
|
+
Logger.level = MatterLogLevel.DEBUG;
|
|
1260
|
+
}
|
|
1261
|
+
else if (data.params.value === 'Info') {
|
|
1262
|
+
Logger.level = MatterLogLevel.INFO;
|
|
1263
|
+
}
|
|
1264
|
+
else if (data.params.value === 'Notice') {
|
|
1265
|
+
Logger.level = MatterLogLevel.NOTICE;
|
|
1266
|
+
}
|
|
1267
|
+
else if (data.params.value === 'Warn') {
|
|
1268
|
+
Logger.level = MatterLogLevel.WARN;
|
|
1269
|
+
}
|
|
1270
|
+
else if (data.params.value === 'Error') {
|
|
1271
|
+
Logger.level = MatterLogLevel.ERROR;
|
|
1272
|
+
}
|
|
1273
|
+
else if (data.params.value === 'Fatal') {
|
|
1274
|
+
Logger.level = MatterLogLevel.FATAL;
|
|
1275
|
+
}
|
|
1276
|
+
this.matterbridge.matterbridgeInformation.matterLoggerLevel = Logger.level;
|
|
1277
|
+
await this.matterbridge.nodeContext?.set('matterLogLevel', Logger.level);
|
|
1278
|
+
}
|
|
1279
|
+
break;
|
|
1280
|
+
case 'setmjlogfile':
|
|
1281
|
+
if (isValidBoolean(data.params.value)) {
|
|
1282
|
+
this.log.debug('Matter file log:', data.params.value);
|
|
1283
|
+
this.matterbridge.matterbridgeInformation.matterFileLogger = data.params.value;
|
|
1284
|
+
await this.matterbridge.nodeContext?.set('matterFileLog', data.params.value);
|
|
1285
|
+
if (data.params.value) {
|
|
1286
|
+
try {
|
|
1287
|
+
Logger.addLogger('matterfilelogger', await this.matterbridge.createMatterFileLogger(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), true), {
|
|
1288
|
+
defaultLogLevel: this.matterbridge.matterbridgeInformation.matterLoggerLevel,
|
|
1289
|
+
logFormat: MatterLogFormat.PLAIN,
|
|
1290
|
+
});
|
|
1291
|
+
}
|
|
1292
|
+
catch (error) {
|
|
1293
|
+
this.log.debug(`Error adding the matterfilelogger for file ${CYAN}${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
else {
|
|
1297
|
+
try {
|
|
1298
|
+
Logger.removeLogger('matterfilelogger');
|
|
1299
|
+
}
|
|
1300
|
+
catch (error) {
|
|
1301
|
+
this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
break;
|
|
1306
|
+
case 'setmdnsinterface':
|
|
1307
|
+
if (isValidString(data.params.value)) {
|
|
1308
|
+
this.log.debug(`Matter.js mdns interface: ${data.params.value === '' ? 'all interfaces' : data.params.value}`);
|
|
1309
|
+
this.matterbridge.mdnsInterface = data.params.value === '' ? undefined : data.params.value;
|
|
1310
|
+
this.matterbridge.matterbridgeInformation.mattermdnsinterface = this.matterbridge.mdnsInterface;
|
|
1311
|
+
await this.matterbridge.nodeContext?.set('mattermdnsinterface', data.params.value);
|
|
1312
|
+
this.wssSendRestartRequired();
|
|
1313
|
+
}
|
|
1314
|
+
break;
|
|
1315
|
+
case 'setipv4address':
|
|
1316
|
+
if (isValidString(data.params.value)) {
|
|
1317
|
+
this.log.debug(`Matter.js ipv4 address: ${data.params.value === '' ? 'all ipv4 addresses' : data.params.value}`);
|
|
1318
|
+
this.matterbridge.ipv4address = data.params.value === '' ? undefined : data.params.value;
|
|
1319
|
+
this.matterbridge.matterbridgeInformation.matteripv4address = this.matterbridge.ipv4address;
|
|
1320
|
+
await this.matterbridge.nodeContext?.set('matteripv4address', data.params.value);
|
|
1321
|
+
this.wssSendRestartRequired();
|
|
1322
|
+
}
|
|
1323
|
+
break;
|
|
1324
|
+
case 'setipv6address':
|
|
1325
|
+
if (isValidString(data.params.value)) {
|
|
1326
|
+
this.log.debug(`Matter.js ipv6 address: ${data.params.value === '' ? 'all ipv6 addresses' : data.params.value}`);
|
|
1327
|
+
this.matterbridge.ipv6address = data.params.value === '' ? undefined : data.params.value;
|
|
1328
|
+
this.matterbridge.matterbridgeInformation.matteripv6address = this.matterbridge.ipv6address;
|
|
1329
|
+
await this.matterbridge.nodeContext?.set('matteripv6address', data.params.value);
|
|
1330
|
+
this.wssSendRestartRequired();
|
|
1331
|
+
}
|
|
1332
|
+
break;
|
|
1333
|
+
case 'setmatterport':
|
|
1334
|
+
data.params.value = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
1335
|
+
if (isValidNumber(data.params.value, 5540, 5580)) {
|
|
1336
|
+
this.log.debug(`Set matter commissioning port to ${CYAN}${data.params.value}${db}`);
|
|
1337
|
+
this.matterbridge.matterbridgeInformation.matterPort = data.params.value;
|
|
1338
|
+
await this.matterbridge.nodeContext?.set('matterport', data.params.value);
|
|
1339
|
+
this.wssSendRestartRequired();
|
|
1340
|
+
}
|
|
1341
|
+
else {
|
|
1342
|
+
this.log.debug(`Reset matter commissioning port to ${CYAN}5040${db}`);
|
|
1343
|
+
this.matterbridge.matterbridgeInformation.matterPort = 5040;
|
|
1344
|
+
await this.matterbridge.nodeContext?.set('matterport', 5040);
|
|
1345
|
+
this.wssSendRestartRequired();
|
|
1346
|
+
}
|
|
1347
|
+
break;
|
|
1348
|
+
case 'setmatterdiscriminator':
|
|
1349
|
+
data.params.value = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
1350
|
+
if (isValidNumber(data.params.value, 1000, 4095)) {
|
|
1351
|
+
this.log.debug(`Set matter commissioning discriminator to ${CYAN}${data.params.value}${db}`);
|
|
1352
|
+
this.matterbridge.matterbridgeInformation.matterDiscriminator = data.params.value;
|
|
1353
|
+
await this.matterbridge.nodeContext?.set('matterdiscriminator', data.params.value);
|
|
1354
|
+
this.wssSendRestartRequired();
|
|
1355
|
+
}
|
|
1356
|
+
else {
|
|
1357
|
+
this.log.debug(`Reset matter commissioning discriminator to ${CYAN}undefined${db}`);
|
|
1358
|
+
this.matterbridge.matterbridgeInformation.matterDiscriminator = undefined;
|
|
1359
|
+
await this.matterbridge.nodeContext?.remove('matterdiscriminator');
|
|
1360
|
+
this.wssSendRestartRequired();
|
|
1361
|
+
}
|
|
1362
|
+
break;
|
|
1363
|
+
case 'setmatterpasscode':
|
|
1364
|
+
data.params.value = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
1365
|
+
if (isValidNumber(data.params.value, 10000000, 90000000)) {
|
|
1366
|
+
this.matterbridge.matterbridgeInformation.matterPasscode = data.params.value;
|
|
1367
|
+
this.log.debug(`Set matter commissioning passcode to ${CYAN}${data.params.value}${db}`);
|
|
1368
|
+
await this.matterbridge.nodeContext?.set('matterpasscode', data.params.value);
|
|
1369
|
+
this.wssSendRestartRequired();
|
|
1370
|
+
}
|
|
1371
|
+
else {
|
|
1372
|
+
this.log.debug(`Reset matter commissioning passcode to ${CYAN}undefined${db}`);
|
|
1373
|
+
this.matterbridge.matterbridgeInformation.matterPasscode = undefined;
|
|
1374
|
+
await this.matterbridge.nodeContext?.remove('matterpasscode');
|
|
1375
|
+
this.wssSendRestartRequired();
|
|
1376
|
+
}
|
|
1377
|
+
break;
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1477
1380
|
else if (data.method === '/api/command') {
|
|
1478
1381
|
if (!isValidString(data.params.command, 5)) {
|
|
1479
1382
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter command in /api/command' }));
|
|
@@ -1561,12 +1464,10 @@ export class Frontend {
|
|
|
1561
1464
|
else {
|
|
1562
1465
|
this.log.error(`Invalid method from websocket client: ${debugStringify(data)}`);
|
|
1563
1466
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Invalid method' }));
|
|
1564
|
-
return;
|
|
1565
1467
|
}
|
|
1566
1468
|
}
|
|
1567
1469
|
catch (error) {
|
|
1568
1470
|
this.log.error(`Error parsing message "${message}" from websocket client:`, error instanceof Error ? error.message : error);
|
|
1569
|
-
return;
|
|
1570
1471
|
}
|
|
1571
1472
|
}
|
|
1572
1473
|
wssSendMessage(level, time, name, message) {
|
|
@@ -1652,7 +1553,15 @@ export class Frontend {
|
|
|
1652
1553
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
1653
1554
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1654
1555
|
if (client.readyState === WebSocket.OPEN) {
|
|
1655
|
-
client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend',
|
|
1556
|
+
client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message, timeout, severity } }));
|
|
1557
|
+
}
|
|
1558
|
+
});
|
|
1559
|
+
}
|
|
1560
|
+
wssSendCloseSnackbarMessage(message) {
|
|
1561
|
+
this.log.debug('Sending a close snackbar message to all connected clients');
|
|
1562
|
+
this.webSocketServer?.clients.forEach((client) => {
|
|
1563
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
1564
|
+
client.send(JSON.stringify({ id: WS_ID_CLOSE_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message } }));
|
|
1656
1565
|
}
|
|
1657
1566
|
});
|
|
1658
1567
|
}
|