matterbridge 3.0.1-dev-20250430-a3ea2b7 → 3.0.1-dev-20250502-6d36575

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/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 {
@@ -679,37 +581,6 @@ export class Frontend {
679
581
  res.json({ message: 'Command received' });
680
582
  return;
681
583
  }
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
584
  if (command === 'saveconfig') {
714
585
  param = param.replace(/\*/g, '\\');
715
586
  this.log.info(`Saving config for plugin ${plg}${param}${nf}...`);
@@ -727,99 +598,6 @@ export class Frontend {
727
598
  res.json({ message: 'Command received' });
728
599
  return;
729
600
  }
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
601
  });
824
602
  this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
825
603
  const { filename } = req.body;
@@ -1147,6 +925,30 @@ export class Frontend {
1147
925
  }
1148
926
  return baseRegisteredPlugins;
1149
927
  }
928
+ async getDevices(pluginName) {
929
+ const devices = [];
930
+ this.matterbridge.devices.forEach(async (device) => {
931
+ if (pluginName && pluginName !== device.plugin)
932
+ return;
933
+ if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
934
+ return;
935
+ const cluster = this.getClusterTextFromDevice(device);
936
+ devices.push({
937
+ pluginName: device.plugin,
938
+ type: device.name + ' (0x' + device.deviceType.toString(16).padStart(4, '0') + ')',
939
+ endpoint: device.number,
940
+ name: device.deviceName,
941
+ serial: device.serialNumber,
942
+ productUrl: device.productUrl,
943
+ configUrl: device.configUrl,
944
+ uniqueId: device.uniqueId,
945
+ reachable: this.getReachability(device),
946
+ powerSource: this.getPowerSource(device),
947
+ cluster: cluster,
948
+ });
949
+ });
950
+ return devices;
951
+ }
1150
952
  async wsMessageHandler(client, message) {
1151
953
  let data;
1152
954
  try {
@@ -1180,31 +982,49 @@ export class Frontend {
1180
982
  }
1181
983
  }
1182
984
  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 packageName in /api/install' }));
985
+ if (!isValidString(data.params.packageName, 10) || !isValidBoolean(data.params.restart)) {
986
+ client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter in /api/install' }));
1185
987
  return;
1186
988
  }
1187
- this.wssSendSnackbarMessage(`Installing package ${data.params.packageName}`);
989
+ this.wssSendSnackbarMessage(`Installing package ${data.params.packageName}...`, 0);
1188
990
  this.matterbridge
1189
991
  .spawnCommand('npm', ['install', '-g', data.params.packageName, '--omit=dev', '--verbose'])
1190
992
  .then((response) => {
1191
993
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response }));
1192
- this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`);
1193
- if (data.params.restart !== true) {
1194
- this.wssSendSnackbarMessage(`Restart required`, 0);
994
+ this.wssSendCloseSnackbarMessage(`Installing package ${data.params.packageName}...`);
995
+ this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`, 5, 'success');
996
+ if (data.params.restart === false) {
997
+ data.params.packageName = data.params.packageName.replace(/@.*$/, '');
998
+ this.matterbridge.plugins.add(data.params.packageName).then((plugin) => {
999
+ if (plugin) {
1000
+ this.wssSendSnackbarMessage(`Added plugin ${data.params.pluginNameOrPath}`, 5, 'success');
1001
+ this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true).then(() => {
1002
+ this.wssSendSnackbarMessage(`Started plugin ${data.params.pluginNameOrPath}`, 5, 'success');
1003
+ this.wssSendRefreshRequired('plugins');
1004
+ });
1005
+ }
1006
+ else {
1007
+ this.wssSendSnackbarMessage(`Restart required`, 0);
1008
+ this.wssSendRefreshRequired('plugins');
1009
+ this.wssSendRestartRequired();
1010
+ }
1011
+ });
1195
1012
  }
1196
1013
  else {
1197
1014
  if (this.matterbridge.restartMode !== '') {
1198
1015
  this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
1199
1016
  this.matterbridge.shutdownProcess();
1200
1017
  }
1201
- else
1018
+ else {
1202
1019
  this.wssSendSnackbarMessage(`Restart required`, 0);
1020
+ this.wssSendRestartRequired();
1021
+ }
1203
1022
  }
1204
1023
  })
1205
1024
  .catch((error) => {
1206
1025
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: error instanceof Error ? error.message : error }));
1207
- this.wssSendSnackbarMessage(`Package ${data.params.packageName} not installed`);
1026
+ this.wssSendCloseSnackbarMessage(`Installing package ${data.params.packageName}...`);
1027
+ this.wssSendSnackbarMessage(`Package ${data.params.packageName} not installed`, 10, 'error');
1208
1028
  });
1209
1029
  return;
1210
1030
  }
@@ -1213,18 +1033,102 @@ export class Frontend {
1213
1033
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' }));
1214
1034
  return;
1215
1035
  }
1216
- this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}`);
1036
+ const plugin = this.matterbridge.plugins.get(data.params.packageName);
1037
+ if (plugin) {
1038
+ await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
1039
+ await this.matterbridge.plugins.remove(data.params.packageName);
1040
+ this.wssSendSnackbarMessage(`Removed plugin ${data.params.packageName}`, 5, 'success');
1041
+ this.wssSendRefreshRequired('plugins');
1042
+ this.wssSendRefreshRequired('devices');
1043
+ }
1044
+ this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
1217
1045
  this.matterbridge
1218
1046
  .spawnCommand('npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
1219
1047
  .then((response) => {
1220
1048
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response }));
1049
+ this.wssSendCloseSnackbarMessage(`Uninstalling package ${data.params.packageName}...`);
1050
+ this.wssSendSnackbarMessage(`Uninstalled package ${data.params.packageName}`, 5, 'success');
1221
1051
  })
1222
1052
  .catch((error) => {
1223
1053
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: error instanceof Error ? error.message : error }));
1224
- this.wssSendSnackbarMessage(`Uninstalled package ${data.params.packageName}`);
1054
+ this.wssSendCloseSnackbarMessage(`Uninstalling package ${data.params.packageName}...`);
1055
+ this.wssSendSnackbarMessage(`Package ${data.params.packageName} not uninstalled`, 10, 'error');
1225
1056
  this.wssSendSnackbarMessage(`Restart required`, 0);
1226
1057
  });
1227
- return;
1058
+ }
1059
+ else if (data.method === '/api/addplugin') {
1060
+ if (!isValidString(data.params.pluginNameOrPath, 10)) {
1061
+ client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginNameOrPath in /api/addplugin' }));
1062
+ return;
1063
+ }
1064
+ data.params.pluginNameOrPath = data.params.pluginNameOrPath.replace(/@.*$/, '');
1065
+ if (this.matterbridge.plugins.has(data.params.pluginNameOrPath)) {
1066
+ client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Plugin ${data.params.pluginNameOrPath} already added` }));
1067
+ this.wssSendSnackbarMessage(`Plugin ${data.params.pluginNameOrPath} already added`, 10, 'warning');
1068
+ return;
1069
+ }
1070
+ const plugin = await this.matterbridge.plugins.add(data.params.pluginNameOrPath);
1071
+ if (plugin) {
1072
+ client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: plugin.name }));
1073
+ this.wssSendSnackbarMessage(`Added plugin ${data.params.pluginNameOrPath}`, 5, 'success');
1074
+ this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true).then(() => {
1075
+ this.wssSendRefreshRequired('plugins');
1076
+ this.wssSendRefreshRequired('devices');
1077
+ this.wssSendSnackbarMessage(`Started plugin ${data.params.pluginNameOrPath}`, 5, 'success');
1078
+ });
1079
+ }
1080
+ else {
1081
+ client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Plugin ${data.params.pluginNameOrPath} not added` }));
1082
+ this.wssSendSnackbarMessage(`Plugin ${data.params.pluginNameOrPath} not added`, 10, 'error');
1083
+ }
1084
+ }
1085
+ else if (data.method === '/api/removeplugin') {
1086
+ if (!isValidString(data.params.pluginName, 10) || !this.matterbridge.plugins.has(data.params.pluginName)) {
1087
+ client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/removeplugin' }));
1088
+ return;
1089
+ }
1090
+ const plugin = this.matterbridge.plugins.get(data.params.pluginName);
1091
+ await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
1092
+ await this.matterbridge.plugins.remove(data.params.pluginName);
1093
+ this.wssSendSnackbarMessage(`Removed plugin ${data.params.pluginName}`, 5, 'success');
1094
+ this.wssSendRefreshRequired('plugins');
1095
+ this.wssSendRefreshRequired('devices');
1096
+ }
1097
+ else if (data.method === '/api/enableplugin') {
1098
+ if (!isValidString(data.params.pluginName, 10) || !this.matterbridge.plugins.has(data.params.pluginName)) {
1099
+ client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/enableplugin' }));
1100
+ return;
1101
+ }
1102
+ const plugin = this.matterbridge.plugins.get(data.params.pluginName);
1103
+ if (plugin && !plugin.enabled) {
1104
+ plugin.locked = undefined;
1105
+ plugin.error = undefined;
1106
+ plugin.loaded = undefined;
1107
+ plugin.started = undefined;
1108
+ plugin.configured = undefined;
1109
+ plugin.platform = undefined;
1110
+ plugin.registeredDevices = undefined;
1111
+ plugin.addedDevices = undefined;
1112
+ await this.matterbridge.plugins.enable(data.params.pluginName);
1113
+ this.wssSendSnackbarMessage(`Enabled plugin ${data.params.pluginName}`, 5, 'success');
1114
+ this.matterbridge.plugins.load(plugin, true, 'The plugin has been enabled', true).then(() => {
1115
+ this.wssSendRefreshRequired('plugins');
1116
+ this.wssSendRefreshRequired('devices');
1117
+ this.wssSendSnackbarMessage(`Started plugin ${data.params.pluginName}`, 5, 'success');
1118
+ });
1119
+ }
1120
+ }
1121
+ else if (data.method === '/api/disableplugin') {
1122
+ if (!isValidString(data.params.pluginName, 10) || !this.matterbridge.plugins.has(data.params.pluginName)) {
1123
+ client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/disableplugin' }));
1124
+ return;
1125
+ }
1126
+ const plugin = this.matterbridge.plugins.get(data.params.pluginName);
1127
+ await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true);
1128
+ await this.matterbridge.plugins.disable(data.params.pluginName);
1129
+ this.wssSendSnackbarMessage(`Disabled plugin ${data.params.pluginName}`, 5, 'success');
1130
+ this.wssSendRefreshRequired('plugins');
1131
+ this.wssSendRefreshRequired('devices');
1228
1132
  }
1229
1133
  else if (data.method === '/api/shellysysupdate') {
1230
1134
  const { triggerShellySysUpdate } = await import('./shelly.js');
@@ -1272,6 +1176,26 @@ export class Frontend {
1272
1176
  await this.matterbridge.shutdownProcess();
1273
1177
  return;
1274
1178
  }
1179
+ else if (data.method === '/api/create-backup') {
1180
+ this.wssSendSnackbarMessage('Creating backup...', 0);
1181
+ this.log.notice(`Creating the backup...`);
1182
+ await createZip(path.join(os.tmpdir(), `matterbridge.backup.zip`), path.join(this.matterbridge.matterbridgeDirectory), path.join(this.matterbridge.matterbridgePluginDirectory));
1183
+ this.log.notice(`Backup ready to be downloaded.`);
1184
+ this.wssSendCloseSnackbarMessage('Creating backup...');
1185
+ this.wssSendSnackbarMessage('Backup ready to be downloaded', 10);
1186
+ }
1187
+ else if (data.method === '/api/unregister') {
1188
+ this.wssSendSnackbarMessage('Uregistering all bridged devices...', 10);
1189
+ await this.matterbridge.unregisterAndShutdownProcess();
1190
+ }
1191
+ else if (data.method === '/api/reset') {
1192
+ this.wssSendSnackbarMessage('Resetting matterbridge commissioning...', 10);
1193
+ await this.matterbridge.shutdownProcessAndReset();
1194
+ }
1195
+ else if (data.method === '/api/factoryreset') {
1196
+ this.wssSendSnackbarMessage('Factory reset of matterbridge...', 10);
1197
+ await this.matterbridge.shutdownProcessAndFactoryReset();
1198
+ }
1275
1199
  else if (data.method === '/api/advertise') {
1276
1200
  const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
1277
1201
  this.matterbridge.matterbridgeInformation.matterbridgeAdvertise = true;
@@ -1300,27 +1224,7 @@ export class Frontend {
1300
1224
  return;
1301
1225
  }
1302
1226
  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
- });
1227
+ const devices = await this.getDevices(isValidString(data.params.pluginName) ? data.params.pluginName : undefined);
1324
1228
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: devices }));
1325
1229
  return;
1326
1230
  }
@@ -1652,7 +1556,15 @@ export class Frontend {
1652
1556
  this.log.debug('Sending a snackbar message to all connected clients');
1653
1557
  this.webSocketServer?.clients.forEach((client) => {
1654
1558
  if (client.readyState === WebSocket.OPEN) {
1655
- client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { message, timeout, severity } }));
1559
+ client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message, timeout, severity } }));
1560
+ }
1561
+ });
1562
+ }
1563
+ wssSendCloseSnackbarMessage(message) {
1564
+ this.log.debug('Sending a close snackbar message to all connected clients');
1565
+ this.webSocketServer?.clients.forEach((client) => {
1566
+ if (client.readyState === WebSocket.OPEN) {
1567
+ client.send(JSON.stringify({ id: WS_ID_CLOSE_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message } }));
1656
1568
  }
1657
1569
  });
1658
1570
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "files": {
3
3
  "main.css": "./static/css/main.944b63c3.css",
4
- "main.js": "./static/js/main.356788d7.js",
4
+ "main.js": "./static/js/main.2093c348.js",
5
5
  "static/js/453.d855a71b.chunk.js": "./static/js/453.d855a71b.chunk.js",
6
6
  "static/media/roboto-latin-700-normal.woff2": "./static/media/roboto-latin-700-normal.c4d6cab43bec89049809.woff2",
7
7
  "static/media/roboto-latin-500-normal.woff2": "./static/media/roboto-latin-500-normal.599f66a60bdf974e578e.woff2",
@@ -77,11 +77,11 @@
77
77
  "static/media/roboto-greek-ext-300-normal.woff": "./static/media/roboto-greek-ext-300-normal.60729cafbded24073dfb.woff",
78
78
  "index.html": "./index.html",
79
79
  "main.944b63c3.css.map": "./static/css/main.944b63c3.css.map",
80
- "main.356788d7.js.map": "./static/js/main.356788d7.js.map",
80
+ "main.2093c348.js.map": "./static/js/main.2093c348.js.map",
81
81
  "453.d855a71b.chunk.js.map": "./static/js/453.d855a71b.chunk.js.map"
82
82
  },
83
83
  "entrypoints": [
84
84
  "static/css/main.944b63c3.css",
85
- "static/js/main.356788d7.js"
85
+ "static/js/main.2093c348.js"
86
86
  ]
87
87
  }
@@ -1 +1 @@
1
- <!doctype html><html lang="en"><head><meta charset="utf-8"/><base href="./"><link rel="icon" href="./matterbridge 32x32.png"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><title>Matterbridge</title><link rel="manifest" href="./manifest.json"/><script defer="defer" src="./static/js/main.356788d7.js"></script><link href="./static/css/main.944b63c3.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
1
+ <!doctype html><html lang="en"><head><meta charset="utf-8"/><base href="./"><link rel="icon" href="./matterbridge 32x32.png"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><title>Matterbridge</title><link rel="manifest" href="./manifest.json"/><script defer="defer" src="./static/js/main.2093c348.js"></script><link href="./static/css/main.944b63c3.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>