matterbridge 3.2.8-dev-20250916-967e0b8 → 3.2.8-dev-20250918-52dc90c

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.
Files changed (105) hide show
  1. package/CHANGELOG.md +12 -2
  2. package/dist/frontend.js +132 -223
  3. package/dist/frontendTypes.js +25 -0
  4. package/dist/matterbridge.js +9 -40
  5. package/dist/pluginManager.js +0 -2
  6. package/dist/shelly.js +4 -5
  7. package/dist/utils/spawn.js +4 -4
  8. package/frontend/build/assets/index.css +1 -0
  9. package/frontend/build/assets/index.js +23 -0
  10. package/frontend/build/assets/vendor_emotion.js +1 -0
  11. package/frontend/build/assets/vendor_lodash.js +1 -0
  12. package/frontend/build/assets/vendor_mdi.js +1 -0
  13. package/frontend/build/assets/vendor_mui.js +137 -0
  14. package/frontend/build/assets/vendor_node_modules.js +100 -0
  15. package/frontend/build/assets/vendor_notistack.js +5 -0
  16. package/frontend/build/assets/vendor_qrcode.js +9 -0
  17. package/frontend/build/assets/vendor_react_table.js +1 -0
  18. package/frontend/build/assets/vendor_rjsf.js +10 -0
  19. package/frontend/build/index.html +27 -1
  20. package/frontend/index.html +17 -0
  21. package/frontend/package-lock.json +5697 -17190
  22. package/frontend/package.json +60 -53
  23. package/npm-shrinkwrap.json +5 -5
  24. package/package.json +1 -1
  25. package/frontend/build/asset-manifest.json +0 -85
  26. package/frontend/build/favicon.ico +0 -0
  27. package/frontend/build/manifest.json +0 -15
  28. package/frontend/build/robots.txt +0 -3
  29. package/frontend/build/static/css/main.56c16a87.css +0 -2
  30. package/frontend/build/static/css/main.56c16a87.css.map +0 -1
  31. package/frontend/build/static/js/main.c1ca9eaf.js +0 -3
  32. package/frontend/build/static/js/main.c1ca9eaf.js.LICENSE.txt +0 -97
  33. package/frontend/build/static/js/main.c1ca9eaf.js.map +0 -1
  34. package/frontend/build/static/media/roboto-cyrillic-300-normal.44340549d94d10899346.woff +0 -0
  35. package/frontend/build/static/media/roboto-cyrillic-300-normal.89d0351bce4bc857dba6.woff2 +0 -0
  36. package/frontend/build/static/media/roboto-cyrillic-400-normal.86d5c52f4588f9f221d7.woff2 +0 -0
  37. package/frontend/build/static/media/roboto-cyrillic-400-normal.d67ac585bb6a05dbf71c.woff +0 -0
  38. package/frontend/build/static/media/roboto-cyrillic-500-normal.1fb2c6d685bfb888cfa3.woff2 +0 -0
  39. package/frontend/build/static/media/roboto-cyrillic-500-normal.36f79cc7e73a69da4438.woff +0 -0
  40. package/frontend/build/static/media/roboto-cyrillic-700-normal.e00802373a2c2db6b30d.woff +0 -0
  41. package/frontend/build/static/media/roboto-cyrillic-700-normal.fd3dfdd6cb1a9175b63d.woff2 +0 -0
  42. package/frontend/build/static/media/roboto-cyrillic-ext-300-normal.a80c0d0719b1acb8f731.woff +0 -0
  43. package/frontend/build/static/media/roboto-cyrillic-ext-300-normal.b9d87b04a9119d8d2fdf.woff2 +0 -0
  44. package/frontend/build/static/media/roboto-cyrillic-ext-400-normal.31476620b88eec076438.woff2 +0 -0
  45. package/frontend/build/static/media/roboto-cyrillic-ext-400-normal.5e3f232f89080810567d.woff +0 -0
  46. package/frontend/build/static/media/roboto-cyrillic-ext-500-normal.634ee2238bf30f362d52.woff2 +0 -0
  47. package/frontend/build/static/media/roboto-cyrillic-ext-500-normal.d6c661248da2fde17768.woff +0 -0
  48. package/frontend/build/static/media/roboto-cyrillic-ext-700-normal.361cdfd3a3f9c4bb09ca.woff2 +0 -0
  49. package/frontend/build/static/media/roboto-cyrillic-ext-700-normal.6b08bc756cd72f5af9e8.woff +0 -0
  50. package/frontend/build/static/media/roboto-greek-300-normal.8300b541aa89b8301a6f.woff +0 -0
  51. package/frontend/build/static/media/roboto-greek-300-normal.fdd1f928a606aa116a44.woff2 +0 -0
  52. package/frontend/build/static/media/roboto-greek-400-normal.98a717d5a38e77c0f657.woff2 +0 -0
  53. package/frontend/build/static/media/roboto-greek-400-normal.ecd8572d631f20ff5bd5.woff +0 -0
  54. package/frontend/build/static/media/roboto-greek-500-normal.4fe733bc436afc295c24.woff +0 -0
  55. package/frontend/build/static/media/roboto-greek-500-normal.5c8100481d4e784afbf2.woff2 +0 -0
  56. package/frontend/build/static/media/roboto-greek-700-normal.d23e03cf87ba44e5af6f.woff +0 -0
  57. package/frontend/build/static/media/roboto-greek-700-normal.d7dfd0b02cd8311e2a97.woff2 +0 -0
  58. package/frontend/build/static/media/roboto-greek-ext-300-normal.60729cafbded24073dfb.woff +0 -0
  59. package/frontend/build/static/media/roboto-greek-ext-300-normal.a88b77bb10633a8045e3.woff2 +0 -0
  60. package/frontend/build/static/media/roboto-greek-ext-400-normal.2d5875b032a1cca91eb2.woff2 +0 -0
  61. package/frontend/build/static/media/roboto-greek-ext-400-normal.a0baf7d6726d8f751a27.woff +0 -0
  62. package/frontend/build/static/media/roboto-greek-ext-500-normal.1964239c2800b6bd7e39.woff +0 -0
  63. package/frontend/build/static/media/roboto-greek-ext-500-normal.bef9c15c7164d6435aad.woff2 +0 -0
  64. package/frontend/build/static/media/roboto-greek-ext-700-normal.1aff9f4cd71608489b9a.woff +0 -0
  65. package/frontend/build/static/media/roboto-greek-ext-700-normal.eb28a447335ba6d54fcb.woff2 +0 -0
  66. package/frontend/build/static/media/roboto-latin-300-normal.cb14f8e80cc69ddbac34.woff +0 -0
  67. package/frontend/build/static/media/roboto-latin-300-normal.db56943a88e4852343ae.woff2 +0 -0
  68. package/frontend/build/static/media/roboto-latin-400-normal.50a0a61e29c19a2f05cb.woff +0 -0
  69. package/frontend/build/static/media/roboto-latin-400-normal.df1be0be92f6f19b8115.woff2 +0 -0
  70. package/frontend/build/static/media/roboto-latin-500-normal.599f66a60bdf974e578e.woff2 +0 -0
  71. package/frontend/build/static/media/roboto-latin-500-normal.c320def131b39bceabd8.woff +0 -0
  72. package/frontend/build/static/media/roboto-latin-700-normal.bcfbe8accc968a375a8e.woff +0 -0
  73. package/frontend/build/static/media/roboto-latin-700-normal.c4d6cab43bec89049809.woff2 +0 -0
  74. package/frontend/build/static/media/roboto-latin-ext-300-normal.6ddd1cfdbc5e74bcdab8.woff +0 -0
  75. package/frontend/build/static/media/roboto-latin-ext-300-normal.948c05192b1e64d931b1.woff2 +0 -0
  76. package/frontend/build/static/media/roboto-latin-ext-400-normal.0f86a30ca7e981fcfc99.woff2 +0 -0
  77. package/frontend/build/static/media/roboto-latin-ext-400-normal.2bfbba2d51a85c8702dd.woff +0 -0
  78. package/frontend/build/static/media/roboto-latin-ext-500-normal.8f02573e78730021ef49.woff2 +0 -0
  79. package/frontend/build/static/media/roboto-latin-ext-500-normal.aecaab4c4da2bf91377a.woff +0 -0
  80. package/frontend/build/static/media/roboto-latin-ext-700-normal.2d3c3ba6fe2d9c1026a5.woff +0 -0
  81. package/frontend/build/static/media/roboto-latin-ext-700-normal.8e656eff240311c6050a.woff2 +0 -0
  82. package/frontend/build/static/media/roboto-math-300-normal.90364ecfad5101ceb1a0.woff +0 -0
  83. package/frontend/build/static/media/roboto-math-300-normal.acc9c7c1d1fe3a1c7d44.woff2 +0 -0
  84. package/frontend/build/static/media/roboto-math-400-normal.3d3a272e5233c5fb1969.woff +0 -0
  85. package/frontend/build/static/media/roboto-math-400-normal.b60d9fba1e21da7497e6.woff2 +0 -0
  86. package/frontend/build/static/media/roboto-math-500-normal.41db483cb764343fca71.woff2 +0 -0
  87. package/frontend/build/static/media/roboto-math-500-normal.c3014a611cd9d8fa6252.woff +0 -0
  88. package/frontend/build/static/media/roboto-math-700-normal.a6fde3ddcb1629fd58b7.woff +0 -0
  89. package/frontend/build/static/media/roboto-math-700-normal.f6f4b54add6ab9d60a0f.woff2 +0 -0
  90. package/frontend/build/static/media/roboto-symbols-300-normal.52cdf8344b378f0c4580.woff +0 -0
  91. package/frontend/build/static/media/roboto-symbols-300-normal.616638ec44336b3da884.woff2 +0 -0
  92. package/frontend/build/static/media/roboto-symbols-400-normal.bb5b5d1459beb07bd3d5.woff2 +0 -0
  93. package/frontend/build/static/media/roboto-symbols-400-normal.f4f7e3bd8264f1a640cb.woff +0 -0
  94. package/frontend/build/static/media/roboto-symbols-500-normal.09b674875029289fd9a7.woff +0 -0
  95. package/frontend/build/static/media/roboto-symbols-500-normal.a5457b0ec984fd4cc8da.woff2 +0 -0
  96. package/frontend/build/static/media/roboto-symbols-700-normal.017e476ef02f62144169.woff +0 -0
  97. package/frontend/build/static/media/roboto-symbols-700-normal.634070e045ac99822c21.woff2 +0 -0
  98. package/frontend/build/static/media/roboto-vietnamese-300-normal.53f399e4522b647bafa7.woff +0 -0
  99. package/frontend/build/static/media/roboto-vietnamese-300-normal.6f0bf63e956c09377ef8.woff2 +0 -0
  100. package/frontend/build/static/media/roboto-vietnamese-400-normal.1cffe58e71a9109191a2.woff +0 -0
  101. package/frontend/build/static/media/roboto-vietnamese-400-normal.b1b8baa94fbcaa57d098.woff2 +0 -0
  102. package/frontend/build/static/media/roboto-vietnamese-500-normal.148734d63bd96c6e964f.woff2 +0 -0
  103. package/frontend/build/static/media/roboto-vietnamese-500-normal.72dbf2a25dd55b80b137.woff +0 -0
  104. package/frontend/build/static/media/roboto-vietnamese-700-normal.44a103f706f3ffe6a041.woff2 +0 -0
  105. package/frontend/build/static/media/roboto-vietnamese-700-normal.fa58a041a3336692af1e.woff +0 -0
package/dist/frontend.js CHANGED
@@ -16,18 +16,6 @@ import { createZip, isValidArray, isValidNumber, isValidObject, isValidString, i
16
16
  import { plg } from './matterbridgeTypes.js';
17
17
  import { capitalizeFirstLetter, getAttribute } from './matterbridgeEndpointHelpers.js';
18
18
  import { cliEmitter, lastCpuUsage } from './cliEmitter.js';
19
- export const WS_ID_LOG = 0;
20
- export const WS_ID_REFRESH_NEEDED = 1;
21
- export const WS_ID_RESTART_NEEDED = 2;
22
- export const WS_ID_CPU_UPDATE = 3;
23
- export const WS_ID_MEMORY_UPDATE = 4;
24
- export const WS_ID_UPTIME_UPDATE = 5;
25
- export const WS_ID_SNACKBAR = 6;
26
- export const WS_ID_UPDATE_NEEDED = 7;
27
- export const WS_ID_STATEUPDATE = 8;
28
- export const WS_ID_CLOSE_SNACKBAR = 9;
29
- export const WS_ID_SHELLY_SYS_UPDATE = 100;
30
- export const WS_ID_SHELLY_MAIN_UPDATE = 101;
31
19
  export class Frontend extends EventEmitter {
32
20
  matterbridge;
33
21
  log;
@@ -212,7 +200,7 @@ export class Frontend extends EventEmitter {
212
200
  callbackLogLevel = "info";
213
201
  if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
214
202
  callbackLogLevel = "debug";
215
- AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), callbackLogLevel);
203
+ AnsiLogger.setGlobalCallback(this.wssSendLogMessage.bind(this), callbackLogLevel);
216
204
  this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
217
205
  this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
218
206
  ws.on('message', (message) => {
@@ -590,36 +578,18 @@ export class Frontend extends EventEmitter {
590
578
  }
591
579
  if (this.httpServer) {
592
580
  this.log.debug('Closing http server...');
593
- await withTimeout(new Promise((resolve) => {
594
- this.httpServer?.close((error) => {
595
- if (error) {
596
- this.log.error(`Error closing http server: ${error}`);
597
- }
598
- else {
599
- this.log.debug('Http server closed successfully');
600
- this.emit('server_stopped');
601
- }
602
- resolve();
603
- });
604
- }), 5000, false);
581
+ this.httpServer.close();
582
+ this.log.debug('Http server closed successfully');
583
+ this.emit('server_stopped');
605
584
  this.httpServer.removeAllListeners();
606
585
  this.httpServer = undefined;
607
586
  this.log.debug('Frontend http server closed successfully');
608
587
  }
609
588
  if (this.httpsServer) {
610
589
  this.log.debug('Closing https server...');
611
- await withTimeout(new Promise((resolve) => {
612
- this.httpsServer?.close((error) => {
613
- if (error) {
614
- this.log.error(`Error closing https server: ${error}`);
615
- }
616
- else {
617
- this.log.debug('Https server closed successfully');
618
- this.emit('server_stopped');
619
- }
620
- resolve();
621
- });
622
- }), 5000, false);
590
+ this.httpsServer.close();
591
+ this.log.debug('Https server closed successfully');
592
+ this.emit('server_stopped');
623
593
  this.httpsServer.removeAllListeners();
624
594
  this.httpsServer = undefined;
625
595
  this.log.debug('Frontend https server closed successfully');
@@ -853,7 +823,6 @@ export class Frontend extends EventEmitter {
853
823
  funding: plugin.funding,
854
824
  latestVersion: plugin.latestVersion,
855
825
  devVersion: plugin.devVersion,
856
- serialNumber: plugin.serialNumber,
857
826
  locked: plugin.locked,
858
827
  error: plugin.error,
859
828
  enabled: plugin.enabled,
@@ -862,17 +831,11 @@ export class Frontend extends EventEmitter {
862
831
  configured: plugin.configured,
863
832
  restartRequired: plugin.restartRequired,
864
833
  registeredDevices: plugin.registeredDevices,
865
- addedDevices: plugin.addedDevices,
866
834
  configJson: plugin.configJson,
867
835
  schemaJson: plugin.schemaJson,
868
836
  hasWhiteList: plugin.configJson?.whiteList !== undefined,
869
837
  hasBlackList: plugin.configJson?.blackList !== undefined,
870
838
  matter: plugin.serverNode ? this.matterbridge.getServerNodeData(plugin.serverNode) : undefined,
871
- paired: plugin.serverNode && plugin.serverNode.lifecycle.isOnline ? plugin.serverNode.state.commissioning.commissioned : undefined,
872
- qrPairingCode: this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : plugin.serverNode && plugin.serverNode.lifecycle.isOnline ? plugin.serverNode.state.commissioning.pairingCodes.qrPairingCode : undefined,
873
- manualPairingCode: this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : plugin.serverNode && plugin.serverNode.lifecycle.isOnline ? plugin.serverNode.state.commissioning.pairingCodes.manualPairingCode : undefined,
874
- fabricInformations: plugin.serverNode && plugin.serverNode.lifecycle.isOnline ? this.matterbridge.sanitizeFabricInformations(Object.values(plugin.serverNode.state.commissioning.fabrics)) : undefined,
875
- sessionInformations: plugin.serverNode && plugin.serverNode.lifecycle.isOnline ? this.matterbridge.sanitizeSessionInformation(Object.values(plugin.serverNode.state.sessions.sessions)) : undefined,
876
839
  });
877
840
  }
878
841
  return baseRegisteredPlugins;
@@ -963,46 +926,53 @@ export class Frontend extends EventEmitter {
963
926
  }
964
927
  async wsMessageHandler(client, message) {
965
928
  let data;
929
+ const sendResponse = (data) => {
930
+ if (client.readyState === WebSocket.OPEN) {
931
+ const { response, ...rest } = data;
932
+ this.log.debug(`Sending api response broadcast message: ${debugStringify(rest)}`);
933
+ client.send(JSON.stringify(data));
934
+ }
935
+ };
966
936
  try {
967
937
  data = JSON.parse(message.toString());
968
- if (!isValidNumber(data.id) || !isValidString(data.dst) || !isValidString(data.src) || !isValidString(data.method) || !isValidObject(data.params) || data.dst !== 'Matterbridge') {
938
+ if (!isValidNumber(data.id) || !isValidString(data.dst) || !isValidString(data.src) || !isValidString(data.method) || data.dst !== 'Matterbridge') {
969
939
  this.log.error(`Invalid message from websocket client: ${debugStringify(data)}`);
970
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Invalid message' }));
940
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Invalid message' });
971
941
  return;
972
942
  }
973
943
  this.log.debug(`Received message from websocket client: ${debugStringify(data)}`);
974
944
  if (data.method === 'ping') {
975
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: 'pong' }));
945
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true, response: 'pong' });
976
946
  return;
977
947
  }
978
948
  else if (data.method === '/api/login') {
979
949
  if (!this.matterbridge.nodeContext) {
980
950
  this.log.error('Login nodeContext not found');
981
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Internal error: nodeContext not found' }));
951
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Internal error: nodeContext not found' });
982
952
  return;
983
953
  }
984
954
  const storedPassword = await this.matterbridge.nodeContext.get('password', '');
985
955
  if (storedPassword === '' || storedPassword === data.params.password) {
986
956
  this.log.debug('Login password valid');
987
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: { valid: true } }));
957
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
988
958
  return;
989
959
  }
990
960
  else {
991
961
  this.log.debug('Error wrong password');
992
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong password' }));
962
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong password' });
993
963
  return;
994
964
  }
995
965
  }
996
966
  else if (data.method === '/api/install') {
997
967
  if (!isValidString(data.params.packageName, 10) || !isValidBoolean(data.params.restart)) {
998
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter in /api/install' }));
968
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter in /api/install' });
999
969
  return;
1000
970
  }
1001
971
  this.wssSendSnackbarMessage(`Installing package ${data.params.packageName}...`, 0);
1002
972
  const { spawnCommand } = await import('./utils/spawn.js');
1003
973
  spawnCommand(this.matterbridge, 'npm', ['install', '-g', data.params.packageName, '--omit=dev', '--verbose'])
1004
- .then((response) => {
1005
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response }));
974
+ .then((_response) => {
975
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1006
976
  this.wssSendCloseSnackbarMessage(`Installing package ${data.params.packageName}...`);
1007
977
  this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`, 5, 'success');
1008
978
  const packageName = data.params.packageName.replace(/@.*$/, '');
@@ -1047,14 +1017,14 @@ export class Frontend extends EventEmitter {
1047
1017
  return;
1048
1018
  })
1049
1019
  .catch((error) => {
1050
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: error instanceof Error ? error.message : error }));
1020
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: error instanceof Error ? error.message : error });
1051
1021
  this.wssSendCloseSnackbarMessage(`Installing package ${data.params.packageName}...`);
1052
1022
  this.wssSendSnackbarMessage(`Package ${data.params.packageName} not installed`, 10, 'error');
1053
1023
  });
1054
1024
  }
1055
1025
  else if (data.method === '/api/uninstall') {
1056
1026
  if (!isValidString(data.params.packageName, 10)) {
1057
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' }));
1027
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' });
1058
1028
  return;
1059
1029
  }
1060
1030
  const plugin = this.matterbridge.plugins.get(data.params.packageName);
@@ -1068,14 +1038,14 @@ export class Frontend extends EventEmitter {
1068
1038
  this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
1069
1039
  const { spawnCommand } = await import('./utils/spawn.js');
1070
1040
  spawnCommand(this.matterbridge, 'npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
1071
- .then((response) => {
1072
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response }));
1041
+ .then((_response) => {
1042
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1073
1043
  this.wssSendCloseSnackbarMessage(`Uninstalling package ${data.params.packageName}...`);
1074
1044
  this.wssSendSnackbarMessage(`Uninstalled package ${data.params.packageName}`, 5, 'success');
1075
1045
  return;
1076
1046
  })
1077
1047
  .catch((error) => {
1078
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: error instanceof Error ? error.message : error }));
1048
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: error instanceof Error ? error.message : error });
1079
1049
  this.wssSendCloseSnackbarMessage(`Uninstalling package ${data.params.packageName}...`);
1080
1050
  this.wssSendSnackbarMessage(`Package ${data.params.packageName} not uninstalled`, 10, 'error');
1081
1051
  this.wssSendSnackbarMessage(`Restart required`, 0);
@@ -1083,18 +1053,18 @@ export class Frontend extends EventEmitter {
1083
1053
  }
1084
1054
  else if (data.method === '/api/addplugin') {
1085
1055
  if (!isValidString(data.params.pluginNameOrPath, 10)) {
1086
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginNameOrPath in /api/addplugin' }));
1056
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginNameOrPath in /api/addplugin' });
1087
1057
  return;
1088
1058
  }
1089
1059
  data.params.pluginNameOrPath = data.params.pluginNameOrPath.replace(/@.*$/, '');
1090
1060
  if (this.matterbridge.plugins.has(data.params.pluginNameOrPath)) {
1091
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Plugin ${data.params.pluginNameOrPath} already added` }));
1061
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Plugin ${data.params.pluginNameOrPath} already added` });
1092
1062
  this.wssSendSnackbarMessage(`Plugin ${data.params.pluginNameOrPath} already added`, 10, 'warning');
1093
1063
  return;
1094
1064
  }
1095
1065
  const plugin = await this.matterbridge.plugins.add(data.params.pluginNameOrPath);
1096
1066
  if (plugin) {
1097
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: plugin.name }));
1067
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: plugin.name });
1098
1068
  this.wssSendSnackbarMessage(`Added plugin ${data.params.pluginNameOrPath}`, 5, 'success');
1099
1069
  this.matterbridge.plugins
1100
1070
  .load(plugin, true, 'The plugin has been added', true)
@@ -1108,13 +1078,13 @@ export class Frontend extends EventEmitter {
1108
1078
  });
1109
1079
  }
1110
1080
  else {
1111
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Plugin ${data.params.pluginNameOrPath} not added` }));
1081
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Plugin ${data.params.pluginNameOrPath} not added` });
1112
1082
  this.wssSendSnackbarMessage(`Plugin ${data.params.pluginNameOrPath} not added`, 10, 'error');
1113
1083
  }
1114
1084
  }
1115
1085
  else if (data.method === '/api/removeplugin') {
1116
1086
  if (!isValidString(data.params.pluginName, 10) || !this.matterbridge.plugins.has(data.params.pluginName)) {
1117
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/removeplugin' }));
1087
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/removeplugin' });
1118
1088
  return;
1119
1089
  }
1120
1090
  const plugin = this.matterbridge.plugins.get(data.params.pluginName);
@@ -1123,11 +1093,11 @@ export class Frontend extends EventEmitter {
1123
1093
  this.wssSendSnackbarMessage(`Removed plugin ${data.params.pluginName}`, 5, 'success');
1124
1094
  this.wssSendRefreshRequired('plugins');
1125
1095
  this.wssSendRefreshRequired('devices');
1126
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1096
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1127
1097
  }
1128
1098
  else if (data.method === '/api/enableplugin') {
1129
1099
  if (!isValidString(data.params.pluginName, 10) || !this.matterbridge.plugins.has(data.params.pluginName)) {
1130
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/enableplugin' }));
1100
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/enableplugin' });
1131
1101
  return;
1132
1102
  }
1133
1103
  const plugin = this.matterbridge.plugins.get(data.params.pluginName);
@@ -1139,7 +1109,7 @@ export class Frontend extends EventEmitter {
1139
1109
  plugin.configured = undefined;
1140
1110
  plugin.platform = undefined;
1141
1111
  plugin.registeredDevices = undefined;
1142
- plugin.addedDevices = undefined;
1112
+ plugin.matter = undefined;
1143
1113
  await this.matterbridge.plugins.enable(data.params.pluginName);
1144
1114
  this.wssSendSnackbarMessage(`Enabled plugin ${data.params.pluginName}`, 5, 'success');
1145
1115
  this.matterbridge.plugins
@@ -1153,11 +1123,11 @@ export class Frontend extends EventEmitter {
1153
1123
  .catch((_error) => {
1154
1124
  });
1155
1125
  }
1156
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1126
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1157
1127
  }
1158
1128
  else if (data.method === '/api/disableplugin') {
1159
1129
  if (!isValidString(data.params.pluginName, 10) || !this.matterbridge.plugins.has(data.params.pluginName)) {
1160
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/disableplugin' }));
1130
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/disableplugin' });
1161
1131
  return;
1162
1132
  }
1163
1133
  const plugin = this.matterbridge.plugins.get(data.params.pluginName);
@@ -1166,11 +1136,11 @@ export class Frontend extends EventEmitter {
1166
1136
  this.wssSendSnackbarMessage(`Disabled plugin ${data.params.pluginName}`, 5, 'success');
1167
1137
  this.wssSendRefreshRequired('plugins');
1168
1138
  this.wssSendRefreshRequired('devices');
1169
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1139
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1170
1140
  }
1171
1141
  else if (data.method === '/api/restartplugin') {
1172
1142
  if (!isValidString(data.params.pluginName, 10) || !this.matterbridge.plugins.has(data.params.pluginName)) {
1173
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/restartplugin' }));
1143
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/restartplugin' });
1174
1144
  return;
1175
1145
  }
1176
1146
  const plugin = this.matterbridge.plugins.get(data.params.pluginName);
@@ -1185,9 +1155,8 @@ export class Frontend extends EventEmitter {
1185
1155
  this.matterbridge.devices.remove(device);
1186
1156
  }
1187
1157
  }
1188
- if (plugin.type === 'DynamicPlatform' && !plugin.locked) {
1158
+ if (plugin.type === 'DynamicPlatform' && !plugin.locked)
1189
1159
  await this.matterbridge.createDynamicPlugin(plugin);
1190
- }
1191
1160
  await this.matterbridge.plugins.load(plugin, true, 'The plugin has been restarted', true);
1192
1161
  plugin.restartRequired = false;
1193
1162
  let needRestart = 0;
@@ -1203,15 +1172,15 @@ export class Frontend extends EventEmitter {
1203
1172
  this.wssSendSnackbarMessage(`Restarted plugin ${data.params.pluginName}`, 5, 'success');
1204
1173
  this.wssSendRefreshRequired('plugins');
1205
1174
  this.wssSendRefreshRequired('devices');
1206
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1175
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1207
1176
  }
1208
1177
  else if (data.method === '/api/savepluginconfig') {
1209
1178
  if (!isValidString(data.params.pluginName, 10) || !this.matterbridge.plugins.has(data.params.pluginName)) {
1210
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/savepluginconfig' }));
1179
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/savepluginconfig' });
1211
1180
  return;
1212
1181
  }
1213
1182
  if (!isValidObject(data.params.formData, 5)) {
1214
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter formData in /api/savepluginconfig' }));
1183
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter formData in /api/savepluginconfig' });
1215
1184
  return;
1216
1185
  }
1217
1186
  this.log.info(`Saving config for plugin ${plg}${data.params.pluginName}${nf}...`);
@@ -1221,59 +1190,59 @@ export class Frontend extends EventEmitter {
1221
1190
  this.wssSendSnackbarMessage(`Saved config for plugin ${data.params.pluginName}`);
1222
1191
  this.wssSendRefreshRequired('pluginsRestart');
1223
1192
  this.wssSendRestartRequired();
1224
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1193
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1225
1194
  }
1226
1195
  }
1227
1196
  else if (data.method === '/api/checkupdates') {
1228
1197
  const { checkUpdates } = await import('./update.js');
1229
1198
  checkUpdates(this.matterbridge);
1230
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1199
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1231
1200
  }
1232
1201
  else if (data.method === '/api/shellysysupdate') {
1233
1202
  const { triggerShellySysUpdate } = await import('./shelly.js');
1234
1203
  triggerShellySysUpdate(this.matterbridge);
1235
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1204
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1236
1205
  }
1237
1206
  else if (data.method === '/api/shellymainupdate') {
1238
1207
  const { triggerShellyMainUpdate } = await import('./shelly.js');
1239
1208
  triggerShellyMainUpdate(this.matterbridge);
1240
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1209
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1241
1210
  }
1242
1211
  else if (data.method === '/api/shellycreatesystemlog') {
1243
1212
  const { createShellySystemLog } = await import('./shelly.js');
1244
1213
  createShellySystemLog(this.matterbridge);
1245
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1214
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1246
1215
  }
1247
1216
  else if (data.method === '/api/shellynetconfig') {
1248
1217
  this.log.debug('/api/shellynetconfig:', data.params);
1249
1218
  const { triggerShellyChangeIp: triggerShellyChangeNet } = await import('./shelly.js');
1250
1219
  triggerShellyChangeNet(this.matterbridge, data.params);
1251
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1220
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1252
1221
  }
1253
1222
  else if (data.method === '/api/softreset') {
1254
1223
  const { triggerShellySoftReset } = await import('./shelly.js');
1255
1224
  triggerShellySoftReset(this.matterbridge);
1256
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1225
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1257
1226
  }
1258
1227
  else if (data.method === '/api/hardreset') {
1259
1228
  const { triggerShellyHardReset } = await import('./shelly.js');
1260
1229
  triggerShellyHardReset(this.matterbridge);
1261
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1230
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1262
1231
  }
1263
1232
  else if (data.method === '/api/reboot') {
1264
1233
  const { triggerShellyReboot } = await import('./shelly.js');
1265
1234
  triggerShellyReboot(this.matterbridge);
1266
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1235
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1267
1236
  }
1268
1237
  else if (data.method === '/api/restart') {
1269
1238
  this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
1270
1239
  await this.matterbridge.restartProcess();
1271
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1240
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1272
1241
  }
1273
1242
  else if (data.method === '/api/shutdown') {
1274
1243
  this.wssSendSnackbarMessage(`Shutting down matterbridge...`, 0);
1275
1244
  await this.matterbridge.shutdownProcess();
1276
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1245
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1277
1246
  }
1278
1247
  else if (data.method === '/api/create-backup') {
1279
1248
  this.wssSendSnackbarMessage('Creating backup...', 0);
@@ -1282,41 +1251,27 @@ export class Frontend extends EventEmitter {
1282
1251
  this.log.notice(`Backup ready to be downloaded.`);
1283
1252
  this.wssSendCloseSnackbarMessage('Creating backup...');
1284
1253
  this.wssSendSnackbarMessage('Backup ready to be downloaded', 10);
1285
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1254
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1286
1255
  }
1287
1256
  else if (data.method === '/api/unregister') {
1288
1257
  this.wssSendSnackbarMessage('Unregistering all bridged devices...', 0);
1289
1258
  await this.matterbridge.unregisterAndShutdownProcess();
1290
1259
  this.wssSendCloseSnackbarMessage('Unregistering all bridged devices...');
1291
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1260
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1292
1261
  }
1293
1262
  else if (data.method === '/api/reset') {
1294
1263
  this.wssSendSnackbarMessage('Resetting matterbridge commissioning...', 10);
1295
1264
  await this.matterbridge.shutdownProcessAndReset();
1296
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1265
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1297
1266
  }
1298
1267
  else if (data.method === '/api/factoryreset') {
1299
1268
  this.wssSendSnackbarMessage('Factory reset of matterbridge...', 10);
1300
1269
  await this.matterbridge.shutdownProcessAndFactoryReset();
1301
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1302
- }
1303
- else if (data.method === '/api/advertise') {
1304
- const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
1305
- this.matterbridge.matterbridgeInformation.matterbridgeAdvertise = true;
1306
- this.wssSendRefreshRequired('matterbridgeAdvertise');
1307
- this.wssSendSnackbarMessage(`Started fabrics share`, 0);
1308
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: pairingCodes, success: true }));
1309
- }
1310
- else if (data.method === '/api/stopadvertise') {
1311
- await this.matterbridge.stopAdvertiseServerNode(this.matterbridge.serverNode);
1312
- this.matterbridge.matterbridgeInformation.matterbridgeAdvertise = false;
1313
- this.wssSendRefreshRequired('matterbridgeAdvertise');
1314
- this.wssSendSnackbarMessage(`Stopped fabrics share`, 0);
1315
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1270
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1316
1271
  }
1317
1272
  else if (data.method === '/api/matter') {
1318
1273
  if (!isValidString(data.params.id)) {
1319
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter id in /api/matter' }));
1274
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter id in /api/matter' });
1320
1275
  return;
1321
1276
  }
1322
1277
  let serverNode;
@@ -1325,129 +1280,125 @@ export class Frontend extends EventEmitter {
1325
1280
  else
1326
1281
  serverNode = this.matterbridge.getPlugins().find((p) => p.serverNode && p.serverNode.id === data.params.id)?.serverNode || this.matterbridge.getDevices().find((d) => d.serverNode && d.serverNode.id === data.params.id)?.serverNode;
1327
1282
  if (!serverNode) {
1328
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Unknown server node id in /api/matter' }));
1283
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Unknown server node id in /api/matter' });
1329
1284
  return;
1330
1285
  }
1286
+ const matter = this.matterbridge.getServerNodeData(serverNode);
1331
1287
  this.log.debug(`*Server node ${serverNode.id}: commissioned ${serverNode.state.commissioning.commissioned} upTime ${serverNode.state.generalDiagnostics.upTime}.`);
1332
1288
  if (data.params.server) {
1333
1289
  this.log.debug(`*Sending data for node ${data.params.id}`);
1334
- this.wssSendRefreshRequired('matter', { matter: { ...this.matterbridge.getServerNodeData(serverNode) } });
1290
+ this.wssSendRefreshRequired('matter', { matter: { ...matter } });
1335
1291
  }
1336
1292
  if (data.params.startCommission) {
1337
1293
  await serverNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
1338
1294
  this.matterbridge.advertisingNodes.set(serverNode.id, Date.now());
1339
1295
  this.log.debug(`*Commissioning has been sent for node ${data.params.id}`);
1340
- this.wssSendRefreshRequired('matter', { matter: { ...this.matterbridge.getServerNodeData(serverNode), advertising: true } });
1296
+ this.wssSendRefreshRequired('matter', { matter: { ...matter, advertising: true } });
1341
1297
  }
1342
1298
  if (data.params.stopCommission) {
1343
1299
  await serverNode.env.get(DeviceCommissioner)?.endCommissioning();
1344
1300
  this.matterbridge.advertisingNodes.delete(serverNode.id);
1345
1301
  this.log.debug(`*Stop commissioning has been sent for node ${data.params.id}`);
1346
- this.wssSendRefreshRequired('matter', { matter: { ...this.matterbridge.getServerNodeData(serverNode), advertising: false } });
1302
+ this.wssSendRefreshRequired('matter', { matter: { ...matter, advertising: false } });
1347
1303
  }
1348
1304
  if (data.params.advertise) {
1349
1305
  await serverNode.env.get(DeviceAdvertiser)?.advertise(true);
1350
1306
  this.log.debug(`*Advertising has been sent for node ${data.params.id}`);
1351
- this.wssSendRefreshRequired('matter', { matter: { ...this.matterbridge.getServerNodeData(serverNode), advertising: true } });
1307
+ this.wssSendRefreshRequired('matter', { matter: { ...matter, advertising: true } });
1352
1308
  }
1353
1309
  if (data.params.removeFabric) {
1354
1310
  if (serverNode.env.get(FabricManager).has(FabricIndex(data.params.removeFabric)))
1355
1311
  await serverNode.env.get(FabricManager).removeFabric(FabricIndex(data.params.removeFabric));
1356
1312
  this.log.debug(`*Removed fabric index ${data.params.removeFabric} for node ${data.params.id}`);
1357
- this.wssSendRefreshRequired('matter', { matter: { ...this.matterbridge.getServerNodeData(serverNode) } });
1313
+ this.wssSendRefreshRequired('matter', { matter: { ...matter } });
1358
1314
  }
1359
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1315
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true, response: matter });
1360
1316
  }
1361
1317
  else if (data.method === '/api/settings') {
1362
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: await this.getApiSettings() }));
1318
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: await this.getApiSettings() });
1363
1319
  }
1364
1320
  else if (data.method === '/api/plugins') {
1365
- const response = this.getPlugins();
1366
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response }));
1321
+ const plugins = this.getPlugins();
1322
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: plugins });
1367
1323
  }
1368
1324
  else if (data.method === '/api/devices') {
1369
1325
  const devices = await this.getDevices(isValidString(data.params.pluginName) ? data.params.pluginName : undefined);
1370
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: devices }));
1326
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: devices });
1371
1327
  }
1372
1328
  else if (data.method === '/api/clusters') {
1373
1329
  if (!isValidString(data.params.plugin, 10)) {
1374
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter plugin in /api/clusters' }));
1330
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter plugin in /api/clusters' });
1375
1331
  return;
1376
1332
  }
1377
1333
  if (!isValidNumber(data.params.endpoint, 1)) {
1378
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter endpoint in /api/clusters' }));
1334
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter endpoint in /api/clusters' });
1379
1335
  return;
1380
1336
  }
1381
1337
  const response = this.getClusters(data.params.plugin, data.params.endpoint);
1382
1338
  if (response) {
1383
- client.send(JSON.stringify({
1339
+ sendResponse({
1384
1340
  id: data.id,
1385
1341
  method: data.method,
1386
1342
  src: 'Matterbridge',
1387
1343
  dst: data.src,
1388
- plugin: data.params.plugin,
1389
- deviceName: response.deviceName,
1390
- serialNumber: response.serialNumber,
1391
- endpoint: response.endpoint,
1392
- deviceTypes: response.deviceTypes,
1393
- response: response.clusters,
1394
- }));
1344
+ response,
1345
+ });
1395
1346
  }
1396
1347
  else {
1397
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Endpoint not found in /api/clusters' }));
1348
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Endpoint not found in /api/clusters' });
1398
1349
  }
1399
1350
  }
1400
- else if (data.method === '/api/select' || data.method === '/api/select/devices') {
1351
+ else if (data.method === '/api/select/devices') {
1401
1352
  if (!isValidString(data.params.plugin, 10)) {
1402
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter plugin in /api/select' }));
1353
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter plugin in /api/select/devices' });
1403
1354
  return;
1404
1355
  }
1405
1356
  const plugin = this.matterbridge.plugins.get(data.params.plugin);
1406
1357
  if (!plugin) {
1407
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select' }));
1358
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select/devices' });
1408
1359
  return;
1409
1360
  }
1410
1361
  const selectDeviceValues = plugin.platform?.getSelectDevices().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
1411
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, plugin: data.params.plugin, response: selectDeviceValues }));
1362
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: selectDeviceValues });
1412
1363
  }
1413
1364
  else if (data.method === '/api/select/entities') {
1414
1365
  if (!isValidString(data.params.plugin, 10)) {
1415
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter plugin in /api/select/entities' }));
1366
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter plugin in /api/select/entities' });
1416
1367
  return;
1417
1368
  }
1418
1369
  const plugin = this.matterbridge.plugins.get(data.params.plugin);
1419
1370
  if (!plugin) {
1420
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select/entities' }));
1371
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select/entities' });
1421
1372
  return;
1422
1373
  }
1423
1374
  const selectEntityValues = plugin.platform?.getSelectEntities().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
1424
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, plugin: data.params.plugin, response: selectEntityValues }));
1375
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: selectEntityValues });
1425
1376
  }
1426
1377
  else if (data.method === '/api/action') {
1427
1378
  if (!isValidString(data.params.plugin, 5) || !isValidString(data.params.action, 1)) {
1428
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter in /api/action' }));
1379
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter in /api/action' });
1429
1380
  return;
1430
1381
  }
1431
1382
  const plugin = this.matterbridge.plugins.get(data.params.plugin);
1432
1383
  if (!plugin) {
1433
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/action' }));
1384
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/action' });
1434
1385
  return;
1435
1386
  }
1436
1387
  this.log.notice(`Action ${CYAN}${data.params.action}${nt}${data.params.value ? ' with ' + CYAN + data.params.value + nt : ''} for plugin ${CYAN}${plugin.name}${nt}`);
1437
1388
  plugin.platform
1438
1389
  ?.onAction(data.params.action, data.params.value, data.params.id, data.params.formData)
1439
1390
  .then(() => {
1440
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1391
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1441
1392
  return;
1442
1393
  })
1443
1394
  .catch((error) => {
1444
1395
  this.log.error(`Error in plugin ${plugin.name} action ${data.params.action}: ${error}`);
1445
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Error in plugin ${plugin.name} action ${data.params.action}: ${error}` }));
1396
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Error in plugin ${plugin.name} action ${data.params.action}: ${error}` });
1446
1397
  });
1447
1398
  }
1448
1399
  else if (data.method === '/api/config') {
1449
1400
  if (!isValidString(data.params.name, 5) || data.params.value === undefined) {
1450
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter in /api/config' }));
1401
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter in /api/config' });
1451
1402
  return;
1452
1403
  }
1453
1404
  this.log.debug(`Received /api/config name ${CYAN}${data.params.name}${db} value ${CYAN}${data.params.value}${db}`);
@@ -1455,14 +1406,14 @@ export class Frontend extends EventEmitter {
1455
1406
  case 'setpassword':
1456
1407
  if (isValidString(data.params.value)) {
1457
1408
  await this.matterbridge.nodeContext?.set('password', data.params.value);
1458
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1409
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1459
1410
  }
1460
1411
  break;
1461
1412
  case 'setbridgemode':
1462
1413
  if (isValidString(data.params.value) && ['bridge', 'childbridge'].includes(data.params.value)) {
1463
1414
  await this.matterbridge.nodeContext?.set('bridgeMode', data.params.value);
1464
1415
  this.wssSendRestartRequired();
1465
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1416
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1466
1417
  }
1467
1418
  break;
1468
1419
  case 'setmbloglevel':
@@ -1487,7 +1438,7 @@ export class Frontend extends EventEmitter {
1487
1438
  await this.matterbridge.setLogLevel("fatal");
1488
1439
  }
1489
1440
  await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
1490
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1441
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1491
1442
  }
1492
1443
  break;
1493
1444
  case 'setmblogfile':
@@ -1499,7 +1450,7 @@ export class Frontend extends EventEmitter {
1499
1450
  AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
1500
1451
  else
1501
1452
  AnsiLogger.setGlobalLogfile(undefined);
1502
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1453
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1503
1454
  }
1504
1455
  break;
1505
1456
  case 'setmjloglevel':
@@ -1525,7 +1476,7 @@ export class Frontend extends EventEmitter {
1525
1476
  }
1526
1477
  this.matterbridge.matterbridgeInformation.matterLoggerLevel = Logger.level;
1527
1478
  await this.matterbridge.nodeContext?.set('matterLogLevel', Logger.level);
1528
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1479
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1529
1480
  }
1530
1481
  break;
1531
1482
  case 'setmjlogfile':
@@ -1539,7 +1490,7 @@ export class Frontend extends EventEmitter {
1539
1490
  else {
1540
1491
  this.matterbridge.matterLog.logFilePath = undefined;
1541
1492
  }
1542
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1493
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1543
1494
  }
1544
1495
  break;
1545
1496
  case 'setmdnsinterface':
@@ -1549,7 +1500,7 @@ export class Frontend extends EventEmitter {
1549
1500
  this.matterbridge.matterbridgeInformation.mattermdnsinterface = this.matterbridge.mdnsInterface;
1550
1501
  await this.matterbridge.nodeContext?.set('mattermdnsinterface', data.params.value);
1551
1502
  this.wssSendRestartRequired();
1552
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1503
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1553
1504
  }
1554
1505
  break;
1555
1506
  case 'setipv4address':
@@ -1559,7 +1510,7 @@ export class Frontend extends EventEmitter {
1559
1510
  this.matterbridge.matterbridgeInformation.matteripv4address = this.matterbridge.ipv4address;
1560
1511
  await this.matterbridge.nodeContext?.set('matteripv4address', data.params.value);
1561
1512
  this.wssSendRestartRequired();
1562
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1513
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1563
1514
  }
1564
1515
  break;
1565
1516
  case 'setipv6address':
@@ -1569,7 +1520,7 @@ export class Frontend extends EventEmitter {
1569
1520
  this.matterbridge.matterbridgeInformation.matteripv6address = this.matterbridge.ipv6address;
1570
1521
  await this.matterbridge.nodeContext?.set('matteripv6address', data.params.value);
1571
1522
  this.wssSendRestartRequired();
1572
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1523
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1573
1524
  }
1574
1525
  break;
1575
1526
  case 'setmatterport':
@@ -1586,7 +1537,7 @@ export class Frontend extends EventEmitter {
1586
1537
  await this.matterbridge.nodeContext?.set('matterport', 5540);
1587
1538
  this.wssSendRestartRequired();
1588
1539
  }
1589
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1540
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1590
1541
  break;
1591
1542
  case 'setmatterdiscriminator':
1592
1543
  data.params.value = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
@@ -1595,14 +1546,14 @@ export class Frontend extends EventEmitter {
1595
1546
  this.matterbridge.matterbridgeInformation.matterDiscriminator = data.params.value;
1596
1547
  await this.matterbridge.nodeContext?.set('matterdiscriminator', data.params.value);
1597
1548
  this.wssSendRestartRequired();
1598
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1549
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1599
1550
  }
1600
1551
  else {
1601
1552
  this.log.debug(`Reset matter commissioning discriminator to ${CYAN}undefined${db}`);
1602
1553
  this.matterbridge.matterbridgeInformation.matterDiscriminator = undefined;
1603
1554
  await this.matterbridge.nodeContext?.remove('matterdiscriminator');
1604
1555
  this.wssSendRestartRequired();
1605
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: false }));
1556
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: false });
1606
1557
  }
1607
1558
  break;
1608
1559
  case 'setmatterpasscode':
@@ -1612,14 +1563,14 @@ export class Frontend extends EventEmitter {
1612
1563
  this.log.debug(`Set matter commissioning passcode to ${CYAN}${data.params.value}${db}`);
1613
1564
  await this.matterbridge.nodeContext?.set('matterpasscode', data.params.value);
1614
1565
  this.wssSendRestartRequired();
1615
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1566
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1616
1567
  }
1617
1568
  else {
1618
1569
  this.log.debug(`Reset matter commissioning passcode to ${CYAN}undefined${db}`);
1619
1570
  this.matterbridge.matterbridgeInformation.matterPasscode = undefined;
1620
1571
  await this.matterbridge.nodeContext?.remove('matterpasscode');
1621
1572
  this.wssSendRestartRequired();
1622
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: false }));
1573
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: false });
1623
1574
  }
1624
1575
  break;
1625
1576
  case 'setvirtualmode':
@@ -1629,22 +1580,22 @@ export class Frontend extends EventEmitter {
1629
1580
  await this.matterbridge.nodeContext?.set('virtualmode', data.params.value);
1630
1581
  this.wssSendRestartRequired();
1631
1582
  }
1632
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1583
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1633
1584
  break;
1634
1585
  default:
1635
1586
  this.log.warn(`Unknown parameter ${data.params.name} in /api/config`);
1636
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Unknown parameter ${data.params.name} in /api/config` }));
1587
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Unknown parameter ${data.params.name} in /api/config` });
1637
1588
  }
1638
1589
  }
1639
1590
  else if (data.method === '/api/command') {
1640
1591
  if (!isValidString(data.params.command, 5)) {
1641
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter command in /api/command' }));
1592
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter command in /api/command' });
1642
1593
  return;
1643
1594
  }
1644
1595
  if (data.params.command === 'selectdevice' && isValidString(data.params.plugin, 10) && isValidString(data.params.serial, 1) && isValidString(data.params.name, 1)) {
1645
1596
  const plugin = this.matterbridge.plugins.get(data.params.plugin);
1646
1597
  if (!plugin) {
1647
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/command' }));
1598
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/command' });
1648
1599
  return;
1649
1600
  }
1650
1601
  const config = plugin.configJson;
@@ -1681,17 +1632,17 @@ export class Frontend extends EventEmitter {
1681
1632
  if (!restartRequired)
1682
1633
  this.wssSendRefreshRequired('pluginsRestart');
1683
1634
  this.wssSendRestartRequired(false);
1684
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1635
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1685
1636
  }
1686
1637
  else {
1687
1638
  this.log.error(`SelectDevice: select ${select} not supported`);
1688
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `SelectDevice: select ${select} not supported` }));
1639
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `SelectDevice: select ${select} not supported` });
1689
1640
  }
1690
1641
  }
1691
1642
  else if (data.params.command === 'unselectdevice' && isValidString(data.params.plugin, 10) && isValidString(data.params.serial, 1) && isValidString(data.params.name, 1)) {
1692
1643
  const plugin = this.matterbridge.plugins.get(data.params.plugin);
1693
1644
  if (!plugin) {
1694
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/command' }));
1645
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/command' });
1695
1646
  return;
1696
1647
  }
1697
1648
  const config = plugin.configJson;
@@ -1728,24 +1679,24 @@ export class Frontend extends EventEmitter {
1728
1679
  if (!restartRequired)
1729
1680
  this.wssSendRefreshRequired('pluginsRestart');
1730
1681
  this.wssSendRestartRequired(false);
1731
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1682
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1732
1683
  }
1733
1684
  else {
1734
1685
  this.log.error(`SelectDevice: select ${select} not supported`);
1735
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `SelectDevice: select ${select} not supported` }));
1686
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `SelectDevice: select ${select} not supported` });
1736
1687
  }
1737
1688
  }
1738
1689
  }
1739
1690
  else {
1740
1691
  this.log.error(`Invalid method from websocket client: ${debugStringify(data)}`);
1741
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Invalid method' }));
1692
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Invalid method' });
1742
1693
  }
1743
1694
  }
1744
1695
  catch (error) {
1745
1696
  inspectError(this.log, `Error processing message "${message}" from websocket client`, error);
1746
1697
  }
1747
1698
  }
1748
- wssSendMessage(level, time, name, message) {
1699
+ wssSendLogMessage(level, time, name, message) {
1749
1700
  if (!level || !time || !name || !message)
1750
1701
  return;
1751
1702
  message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
@@ -1766,19 +1717,11 @@ export class Frontend extends EventEmitter {
1766
1717
  return word;
1767
1718
  })
1768
1719
  .join(' ');
1769
- this.webSocketServer?.clients.forEach((client) => {
1770
- if (client.readyState === WebSocket.OPEN) {
1771
- client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
1772
- }
1773
- });
1720
+ this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'log', params: { level, time, name, message } });
1774
1721
  }
1775
- wssSendRefreshRequired(changed = null, params = {}) {
1722
+ wssSendRefreshRequired(changed = null, params) {
1776
1723
  this.log.debug('Sending a refresh required message to all connected clients');
1777
- this.webSocketServer?.clients.forEach((client) => {
1778
- if (client.readyState === WebSocket.OPEN) {
1779
- client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed, ...params } }));
1780
- }
1781
- });
1724
+ this.wssBroadcastMessage({ id: 1, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed, ...params } });
1782
1725
  }
1783
1726
  wssSendRestartRequired(snackbar = true, fixed = false) {
1784
1727
  this.log.debug('Sending a restart required message to all connected clients');
@@ -1786,88 +1729,54 @@ export class Frontend extends EventEmitter {
1786
1729
  this.matterbridge.matterbridgeInformation.fixedRestartRequired = fixed;
1787
1730
  if (snackbar === true)
1788
1731
  this.wssSendSnackbarMessage(`Restart required`, 0);
1789
- this.webSocketServer?.clients.forEach((client) => {
1790
- if (client.readyState === WebSocket.OPEN) {
1791
- client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: { fixed } }));
1792
- }
1793
- });
1732
+ this.wssBroadcastMessage({ id: 2, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: { fixed } });
1794
1733
  }
1795
1734
  wssSendRestartNotRequired(snackbar = true) {
1796
1735
  this.log.debug('Sending a restart not required message to all connected clients');
1797
1736
  this.matterbridge.matterbridgeInformation.restartRequired = false;
1798
1737
  if (snackbar === true)
1799
1738
  this.wssSendCloseSnackbarMessage(`Restart required`);
1800
- this.webSocketServer?.clients.forEach((client) => {
1801
- if (client.readyState === WebSocket.OPEN) {
1802
- client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required', params: {} }));
1803
- }
1804
- });
1739
+ this.wssBroadcastMessage({ id: 3, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required' });
1805
1740
  }
1806
1741
  wssSendUpdateRequired(devVersion = false) {
1807
1742
  this.log.debug('Sending an update required message to all connected clients');
1808
1743
  this.matterbridge.matterbridgeInformation.updateRequired = true;
1809
- this.webSocketServer?.clients.forEach((client) => {
1810
- if (client.readyState === WebSocket.OPEN) {
1811
- client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: devVersion ? 'update_required_dev' : 'update_required', params: {} }));
1812
- }
1813
- });
1744
+ this.wssBroadcastMessage({ id: 8, src: 'Matterbridge', dst: 'Frontend', method: devVersion ? 'update_required_dev' : 'update_required' });
1814
1745
  }
1815
1746
  wssSendCpuUpdate(cpuUsage) {
1816
1747
  if (hasParameter('debug'))
1817
1748
  this.log.debug('Sending a cpu update message to all connected clients');
1818
- this.webSocketServer?.clients.forEach((client) => {
1819
- if (client.readyState === WebSocket.OPEN) {
1820
- client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
1821
- }
1822
- });
1749
+ this.wssBroadcastMessage({ id: 4, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } });
1823
1750
  }
1824
1751
  wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
1825
1752
  if (hasParameter('debug'))
1826
1753
  this.log.debug('Sending a memory update message to all connected clients');
1827
- this.webSocketServer?.clients.forEach((client) => {
1828
- if (client.readyState === WebSocket.OPEN) {
1829
- client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
1830
- }
1831
- });
1754
+ this.wssBroadcastMessage({ id: 5, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } });
1832
1755
  }
1833
1756
  wssSendUptimeUpdate(systemUptime, processUptime) {
1834
1757
  if (hasParameter('debug'))
1835
1758
  this.log.debug('Sending a uptime update message to all connected clients');
1836
- this.webSocketServer?.clients.forEach((client) => {
1837
- if (client.readyState === WebSocket.OPEN) {
1838
- client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
1839
- }
1840
- });
1759
+ this.wssBroadcastMessage({ id: 6, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } });
1841
1760
  }
1842
1761
  wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
1843
1762
  this.log.debug('Sending a snackbar message to all connected clients');
1844
- this.webSocketServer?.clients.forEach((client) => {
1845
- if (client.readyState === WebSocket.OPEN) {
1846
- client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message, timeout, severity } }));
1847
- }
1848
- });
1763
+ this.wssBroadcastMessage({ id: 7, src: 'Matterbridge', dst: 'Frontend', method: 'snackbar', params: { message, timeout, severity } });
1849
1764
  }
1850
1765
  wssSendCloseSnackbarMessage(message) {
1851
1766
  this.log.debug('Sending a close snackbar message to all connected clients');
1852
- this.webSocketServer?.clients.forEach((client) => {
1853
- if (client.readyState === WebSocket.OPEN) {
1854
- client.send(JSON.stringify({ id: WS_ID_CLOSE_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message } }));
1855
- }
1856
- });
1767
+ this.wssBroadcastMessage({ id: 10, src: 'Matterbridge', dst: 'Frontend', method: 'close_snackbar', params: { message } });
1857
1768
  }
1858
1769
  wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
1859
1770
  this.log.debug('Sending an attribute update message to all connected clients');
1860
- this.webSocketServer?.clients.forEach((client) => {
1861
- if (client.readyState === WebSocket.OPEN) {
1862
- client.send(JSON.stringify({ id: WS_ID_STATEUPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } }));
1863
- }
1864
- });
1771
+ this.wssBroadcastMessage({ id: 9, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } });
1865
1772
  }
1866
- wssBroadcastMessage(id, method, params) {
1867
- this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
1773
+ wssBroadcastMessage(msg) {
1774
+ const stringifiedMsg = JSON.stringify(msg);
1775
+ if (msg.id !== 0)
1776
+ this.log.debug(`Sending a broadcast message: ${debugStringify(msg)}`);
1868
1777
  this.webSocketServer?.clients.forEach((client) => {
1869
1778
  if (client.readyState === WebSocket.OPEN) {
1870
- client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
1779
+ client.send(stringifiedMsg);
1871
1780
  }
1872
1781
  });
1873
1782
  }