matterbridge 3.2.8-dev-20250916-da71182 → 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 (104) hide show
  1. package/CHANGELOG.md +12 -2
  2. package/dist/frontend.js +130 -199
  3. package/dist/frontendTypes.js +25 -0
  4. package/dist/matterbridge.js +3 -7
  5. package/dist/shelly.js +4 -5
  6. package/dist/utils/spawn.js +4 -4
  7. package/frontend/build/assets/index.css +1 -0
  8. package/frontend/build/assets/index.js +23 -0
  9. package/frontend/build/assets/vendor_emotion.js +1 -0
  10. package/frontend/build/assets/vendor_lodash.js +1 -0
  11. package/frontend/build/assets/vendor_mdi.js +1 -0
  12. package/frontend/build/assets/vendor_mui.js +137 -0
  13. package/frontend/build/assets/vendor_node_modules.js +100 -0
  14. package/frontend/build/assets/vendor_notistack.js +5 -0
  15. package/frontend/build/assets/vendor_qrcode.js +9 -0
  16. package/frontend/build/assets/vendor_react_table.js +1 -0
  17. package/frontend/build/assets/vendor_rjsf.js +10 -0
  18. package/frontend/build/index.html +27 -1
  19. package/frontend/index.html +17 -0
  20. package/frontend/package-lock.json +5697 -17190
  21. package/frontend/package.json +60 -53
  22. package/npm-shrinkwrap.json +5 -5
  23. package/package.json +1 -1
  24. package/frontend/build/asset-manifest.json +0 -85
  25. package/frontend/build/favicon.ico +0 -0
  26. package/frontend/build/manifest.json +0 -15
  27. package/frontend/build/robots.txt +0 -3
  28. package/frontend/build/static/css/main.56c16a87.css +0 -2
  29. package/frontend/build/static/css/main.56c16a87.css.map +0 -1
  30. package/frontend/build/static/js/main.565f820c.js +0 -3
  31. package/frontend/build/static/js/main.565f820c.js.LICENSE.txt +0 -97
  32. package/frontend/build/static/js/main.565f820c.js.map +0 -1
  33. package/frontend/build/static/media/roboto-cyrillic-300-normal.44340549d94d10899346.woff +0 -0
  34. package/frontend/build/static/media/roboto-cyrillic-300-normal.89d0351bce4bc857dba6.woff2 +0 -0
  35. package/frontend/build/static/media/roboto-cyrillic-400-normal.86d5c52f4588f9f221d7.woff2 +0 -0
  36. package/frontend/build/static/media/roboto-cyrillic-400-normal.d67ac585bb6a05dbf71c.woff +0 -0
  37. package/frontend/build/static/media/roboto-cyrillic-500-normal.1fb2c6d685bfb888cfa3.woff2 +0 -0
  38. package/frontend/build/static/media/roboto-cyrillic-500-normal.36f79cc7e73a69da4438.woff +0 -0
  39. package/frontend/build/static/media/roboto-cyrillic-700-normal.e00802373a2c2db6b30d.woff +0 -0
  40. package/frontend/build/static/media/roboto-cyrillic-700-normal.fd3dfdd6cb1a9175b63d.woff2 +0 -0
  41. package/frontend/build/static/media/roboto-cyrillic-ext-300-normal.a80c0d0719b1acb8f731.woff +0 -0
  42. package/frontend/build/static/media/roboto-cyrillic-ext-300-normal.b9d87b04a9119d8d2fdf.woff2 +0 -0
  43. package/frontend/build/static/media/roboto-cyrillic-ext-400-normal.31476620b88eec076438.woff2 +0 -0
  44. package/frontend/build/static/media/roboto-cyrillic-ext-400-normal.5e3f232f89080810567d.woff +0 -0
  45. package/frontend/build/static/media/roboto-cyrillic-ext-500-normal.634ee2238bf30f362d52.woff2 +0 -0
  46. package/frontend/build/static/media/roboto-cyrillic-ext-500-normal.d6c661248da2fde17768.woff +0 -0
  47. package/frontend/build/static/media/roboto-cyrillic-ext-700-normal.361cdfd3a3f9c4bb09ca.woff2 +0 -0
  48. package/frontend/build/static/media/roboto-cyrillic-ext-700-normal.6b08bc756cd72f5af9e8.woff +0 -0
  49. package/frontend/build/static/media/roboto-greek-300-normal.8300b541aa89b8301a6f.woff +0 -0
  50. package/frontend/build/static/media/roboto-greek-300-normal.fdd1f928a606aa116a44.woff2 +0 -0
  51. package/frontend/build/static/media/roboto-greek-400-normal.98a717d5a38e77c0f657.woff2 +0 -0
  52. package/frontend/build/static/media/roboto-greek-400-normal.ecd8572d631f20ff5bd5.woff +0 -0
  53. package/frontend/build/static/media/roboto-greek-500-normal.4fe733bc436afc295c24.woff +0 -0
  54. package/frontend/build/static/media/roboto-greek-500-normal.5c8100481d4e784afbf2.woff2 +0 -0
  55. package/frontend/build/static/media/roboto-greek-700-normal.d23e03cf87ba44e5af6f.woff +0 -0
  56. package/frontend/build/static/media/roboto-greek-700-normal.d7dfd0b02cd8311e2a97.woff2 +0 -0
  57. package/frontend/build/static/media/roboto-greek-ext-300-normal.60729cafbded24073dfb.woff +0 -0
  58. package/frontend/build/static/media/roboto-greek-ext-300-normal.a88b77bb10633a8045e3.woff2 +0 -0
  59. package/frontend/build/static/media/roboto-greek-ext-400-normal.2d5875b032a1cca91eb2.woff2 +0 -0
  60. package/frontend/build/static/media/roboto-greek-ext-400-normal.a0baf7d6726d8f751a27.woff +0 -0
  61. package/frontend/build/static/media/roboto-greek-ext-500-normal.1964239c2800b6bd7e39.woff +0 -0
  62. package/frontend/build/static/media/roboto-greek-ext-500-normal.bef9c15c7164d6435aad.woff2 +0 -0
  63. package/frontend/build/static/media/roboto-greek-ext-700-normal.1aff9f4cd71608489b9a.woff +0 -0
  64. package/frontend/build/static/media/roboto-greek-ext-700-normal.eb28a447335ba6d54fcb.woff2 +0 -0
  65. package/frontend/build/static/media/roboto-latin-300-normal.cb14f8e80cc69ddbac34.woff +0 -0
  66. package/frontend/build/static/media/roboto-latin-300-normal.db56943a88e4852343ae.woff2 +0 -0
  67. package/frontend/build/static/media/roboto-latin-400-normal.50a0a61e29c19a2f05cb.woff +0 -0
  68. package/frontend/build/static/media/roboto-latin-400-normal.df1be0be92f6f19b8115.woff2 +0 -0
  69. package/frontend/build/static/media/roboto-latin-500-normal.599f66a60bdf974e578e.woff2 +0 -0
  70. package/frontend/build/static/media/roboto-latin-500-normal.c320def131b39bceabd8.woff +0 -0
  71. package/frontend/build/static/media/roboto-latin-700-normal.bcfbe8accc968a375a8e.woff +0 -0
  72. package/frontend/build/static/media/roboto-latin-700-normal.c4d6cab43bec89049809.woff2 +0 -0
  73. package/frontend/build/static/media/roboto-latin-ext-300-normal.6ddd1cfdbc5e74bcdab8.woff +0 -0
  74. package/frontend/build/static/media/roboto-latin-ext-300-normal.948c05192b1e64d931b1.woff2 +0 -0
  75. package/frontend/build/static/media/roboto-latin-ext-400-normal.0f86a30ca7e981fcfc99.woff2 +0 -0
  76. package/frontend/build/static/media/roboto-latin-ext-400-normal.2bfbba2d51a85c8702dd.woff +0 -0
  77. package/frontend/build/static/media/roboto-latin-ext-500-normal.8f02573e78730021ef49.woff2 +0 -0
  78. package/frontend/build/static/media/roboto-latin-ext-500-normal.aecaab4c4da2bf91377a.woff +0 -0
  79. package/frontend/build/static/media/roboto-latin-ext-700-normal.2d3c3ba6fe2d9c1026a5.woff +0 -0
  80. package/frontend/build/static/media/roboto-latin-ext-700-normal.8e656eff240311c6050a.woff2 +0 -0
  81. package/frontend/build/static/media/roboto-math-300-normal.90364ecfad5101ceb1a0.woff +0 -0
  82. package/frontend/build/static/media/roboto-math-300-normal.acc9c7c1d1fe3a1c7d44.woff2 +0 -0
  83. package/frontend/build/static/media/roboto-math-400-normal.3d3a272e5233c5fb1969.woff +0 -0
  84. package/frontend/build/static/media/roboto-math-400-normal.b60d9fba1e21da7497e6.woff2 +0 -0
  85. package/frontend/build/static/media/roboto-math-500-normal.41db483cb764343fca71.woff2 +0 -0
  86. package/frontend/build/static/media/roboto-math-500-normal.c3014a611cd9d8fa6252.woff +0 -0
  87. package/frontend/build/static/media/roboto-math-700-normal.a6fde3ddcb1629fd58b7.woff +0 -0
  88. package/frontend/build/static/media/roboto-math-700-normal.f6f4b54add6ab9d60a0f.woff2 +0 -0
  89. package/frontend/build/static/media/roboto-symbols-300-normal.52cdf8344b378f0c4580.woff +0 -0
  90. package/frontend/build/static/media/roboto-symbols-300-normal.616638ec44336b3da884.woff2 +0 -0
  91. package/frontend/build/static/media/roboto-symbols-400-normal.bb5b5d1459beb07bd3d5.woff2 +0 -0
  92. package/frontend/build/static/media/roboto-symbols-400-normal.f4f7e3bd8264f1a640cb.woff +0 -0
  93. package/frontend/build/static/media/roboto-symbols-500-normal.09b674875029289fd9a7.woff +0 -0
  94. package/frontend/build/static/media/roboto-symbols-500-normal.a5457b0ec984fd4cc8da.woff2 +0 -0
  95. package/frontend/build/static/media/roboto-symbols-700-normal.017e476ef02f62144169.woff +0 -0
  96. package/frontend/build/static/media/roboto-symbols-700-normal.634070e045ac99822c21.woff2 +0 -0
  97. package/frontend/build/static/media/roboto-vietnamese-300-normal.53f399e4522b647bafa7.woff +0 -0
  98. package/frontend/build/static/media/roboto-vietnamese-300-normal.6f0bf63e956c09377ef8.woff2 +0 -0
  99. package/frontend/build/static/media/roboto-vietnamese-400-normal.1cffe58e71a9109191a2.woff +0 -0
  100. package/frontend/build/static/media/roboto-vietnamese-400-normal.b1b8baa94fbcaa57d098.woff2 +0 -0
  101. package/frontend/build/static/media/roboto-vietnamese-500-normal.148734d63bd96c6e964f.woff2 +0 -0
  102. package/frontend/build/static/media/roboto-vietnamese-500-normal.72dbf2a25dd55b80b137.woff +0 -0
  103. package/frontend/build/static/media/roboto-vietnamese-700-normal.44a103f706f3ffe6a041.woff2 +0 -0
  104. 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');
@@ -956,46 +926,53 @@ export class Frontend extends EventEmitter {
956
926
  }
957
927
  async wsMessageHandler(client, message) {
958
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
+ };
959
936
  try {
960
937
  data = JSON.parse(message.toString());
961
- 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') {
962
939
  this.log.error(`Invalid message from websocket client: ${debugStringify(data)}`);
963
- 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' });
964
941
  return;
965
942
  }
966
943
  this.log.debug(`Received message from websocket client: ${debugStringify(data)}`);
967
944
  if (data.method === 'ping') {
968
- 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' });
969
946
  return;
970
947
  }
971
948
  else if (data.method === '/api/login') {
972
949
  if (!this.matterbridge.nodeContext) {
973
950
  this.log.error('Login nodeContext not found');
974
- 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' });
975
952
  return;
976
953
  }
977
954
  const storedPassword = await this.matterbridge.nodeContext.get('password', '');
978
955
  if (storedPassword === '' || storedPassword === data.params.password) {
979
956
  this.log.debug('Login password valid');
980
- 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 });
981
958
  return;
982
959
  }
983
960
  else {
984
961
  this.log.debug('Error wrong password');
985
- 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' });
986
963
  return;
987
964
  }
988
965
  }
989
966
  else if (data.method === '/api/install') {
990
967
  if (!isValidString(data.params.packageName, 10) || !isValidBoolean(data.params.restart)) {
991
- 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' });
992
969
  return;
993
970
  }
994
971
  this.wssSendSnackbarMessage(`Installing package ${data.params.packageName}...`, 0);
995
972
  const { spawnCommand } = await import('./utils/spawn.js');
996
973
  spawnCommand(this.matterbridge, 'npm', ['install', '-g', data.params.packageName, '--omit=dev', '--verbose'])
997
- .then((response) => {
998
- 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 });
999
976
  this.wssSendCloseSnackbarMessage(`Installing package ${data.params.packageName}...`);
1000
977
  this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`, 5, 'success');
1001
978
  const packageName = data.params.packageName.replace(/@.*$/, '');
@@ -1040,14 +1017,14 @@ export class Frontend extends EventEmitter {
1040
1017
  return;
1041
1018
  })
1042
1019
  .catch((error) => {
1043
- 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 });
1044
1021
  this.wssSendCloseSnackbarMessage(`Installing package ${data.params.packageName}...`);
1045
1022
  this.wssSendSnackbarMessage(`Package ${data.params.packageName} not installed`, 10, 'error');
1046
1023
  });
1047
1024
  }
1048
1025
  else if (data.method === '/api/uninstall') {
1049
1026
  if (!isValidString(data.params.packageName, 10)) {
1050
- 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' });
1051
1028
  return;
1052
1029
  }
1053
1030
  const plugin = this.matterbridge.plugins.get(data.params.packageName);
@@ -1061,14 +1038,14 @@ export class Frontend extends EventEmitter {
1061
1038
  this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
1062
1039
  const { spawnCommand } = await import('./utils/spawn.js');
1063
1040
  spawnCommand(this.matterbridge, 'npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
1064
- .then((response) => {
1065
- 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 });
1066
1043
  this.wssSendCloseSnackbarMessage(`Uninstalling package ${data.params.packageName}...`);
1067
1044
  this.wssSendSnackbarMessage(`Uninstalled package ${data.params.packageName}`, 5, 'success');
1068
1045
  return;
1069
1046
  })
1070
1047
  .catch((error) => {
1071
- 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 });
1072
1049
  this.wssSendCloseSnackbarMessage(`Uninstalling package ${data.params.packageName}...`);
1073
1050
  this.wssSendSnackbarMessage(`Package ${data.params.packageName} not uninstalled`, 10, 'error');
1074
1051
  this.wssSendSnackbarMessage(`Restart required`, 0);
@@ -1076,18 +1053,18 @@ export class Frontend extends EventEmitter {
1076
1053
  }
1077
1054
  else if (data.method === '/api/addplugin') {
1078
1055
  if (!isValidString(data.params.pluginNameOrPath, 10)) {
1079
- 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' });
1080
1057
  return;
1081
1058
  }
1082
1059
  data.params.pluginNameOrPath = data.params.pluginNameOrPath.replace(/@.*$/, '');
1083
1060
  if (this.matterbridge.plugins.has(data.params.pluginNameOrPath)) {
1084
- 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` });
1085
1062
  this.wssSendSnackbarMessage(`Plugin ${data.params.pluginNameOrPath} already added`, 10, 'warning');
1086
1063
  return;
1087
1064
  }
1088
1065
  const plugin = await this.matterbridge.plugins.add(data.params.pluginNameOrPath);
1089
1066
  if (plugin) {
1090
- 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 });
1091
1068
  this.wssSendSnackbarMessage(`Added plugin ${data.params.pluginNameOrPath}`, 5, 'success');
1092
1069
  this.matterbridge.plugins
1093
1070
  .load(plugin, true, 'The plugin has been added', true)
@@ -1101,13 +1078,13 @@ export class Frontend extends EventEmitter {
1101
1078
  });
1102
1079
  }
1103
1080
  else {
1104
- 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` });
1105
1082
  this.wssSendSnackbarMessage(`Plugin ${data.params.pluginNameOrPath} not added`, 10, 'error');
1106
1083
  }
1107
1084
  }
1108
1085
  else if (data.method === '/api/removeplugin') {
1109
1086
  if (!isValidString(data.params.pluginName, 10) || !this.matterbridge.plugins.has(data.params.pluginName)) {
1110
- 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' });
1111
1088
  return;
1112
1089
  }
1113
1090
  const plugin = this.matterbridge.plugins.get(data.params.pluginName);
@@ -1116,11 +1093,11 @@ export class Frontend extends EventEmitter {
1116
1093
  this.wssSendSnackbarMessage(`Removed plugin ${data.params.pluginName}`, 5, 'success');
1117
1094
  this.wssSendRefreshRequired('plugins');
1118
1095
  this.wssSendRefreshRequired('devices');
1119
- 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 });
1120
1097
  }
1121
1098
  else if (data.method === '/api/enableplugin') {
1122
1099
  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/enableplugin' }));
1100
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/enableplugin' });
1124
1101
  return;
1125
1102
  }
1126
1103
  const plugin = this.matterbridge.plugins.get(data.params.pluginName);
@@ -1146,11 +1123,11 @@ export class Frontend extends EventEmitter {
1146
1123
  .catch((_error) => {
1147
1124
  });
1148
1125
  }
1149
- 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 });
1150
1127
  }
1151
1128
  else if (data.method === '/api/disableplugin') {
1152
1129
  if (!isValidString(data.params.pluginName, 10) || !this.matterbridge.plugins.has(data.params.pluginName)) {
1153
- 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' });
1154
1131
  return;
1155
1132
  }
1156
1133
  const plugin = this.matterbridge.plugins.get(data.params.pluginName);
@@ -1159,11 +1136,11 @@ export class Frontend extends EventEmitter {
1159
1136
  this.wssSendSnackbarMessage(`Disabled plugin ${data.params.pluginName}`, 5, 'success');
1160
1137
  this.wssSendRefreshRequired('plugins');
1161
1138
  this.wssSendRefreshRequired('devices');
1162
- 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 });
1163
1140
  }
1164
1141
  else if (data.method === '/api/restartplugin') {
1165
1142
  if (!isValidString(data.params.pluginName, 10) || !this.matterbridge.plugins.has(data.params.pluginName)) {
1166
- 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' });
1167
1144
  return;
1168
1145
  }
1169
1146
  const plugin = this.matterbridge.plugins.get(data.params.pluginName);
@@ -1195,15 +1172,15 @@ export class Frontend extends EventEmitter {
1195
1172
  this.wssSendSnackbarMessage(`Restarted plugin ${data.params.pluginName}`, 5, 'success');
1196
1173
  this.wssSendRefreshRequired('plugins');
1197
1174
  this.wssSendRefreshRequired('devices');
1198
- 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 });
1199
1176
  }
1200
1177
  else if (data.method === '/api/savepluginconfig') {
1201
1178
  if (!isValidString(data.params.pluginName, 10) || !this.matterbridge.plugins.has(data.params.pluginName)) {
1202
- 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' });
1203
1180
  return;
1204
1181
  }
1205
1182
  if (!isValidObject(data.params.formData, 5)) {
1206
- 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' });
1207
1184
  return;
1208
1185
  }
1209
1186
  this.log.info(`Saving config for plugin ${plg}${data.params.pluginName}${nf}...`);
@@ -1213,59 +1190,59 @@ export class Frontend extends EventEmitter {
1213
1190
  this.wssSendSnackbarMessage(`Saved config for plugin ${data.params.pluginName}`);
1214
1191
  this.wssSendRefreshRequired('pluginsRestart');
1215
1192
  this.wssSendRestartRequired();
1216
- 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 });
1217
1194
  }
1218
1195
  }
1219
1196
  else if (data.method === '/api/checkupdates') {
1220
1197
  const { checkUpdates } = await import('./update.js');
1221
1198
  checkUpdates(this.matterbridge);
1222
- 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 });
1223
1200
  }
1224
1201
  else if (data.method === '/api/shellysysupdate') {
1225
1202
  const { triggerShellySysUpdate } = await import('./shelly.js');
1226
1203
  triggerShellySysUpdate(this.matterbridge);
1227
- 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 });
1228
1205
  }
1229
1206
  else if (data.method === '/api/shellymainupdate') {
1230
1207
  const { triggerShellyMainUpdate } = await import('./shelly.js');
1231
1208
  triggerShellyMainUpdate(this.matterbridge);
1232
- 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 });
1233
1210
  }
1234
1211
  else if (data.method === '/api/shellycreatesystemlog') {
1235
1212
  const { createShellySystemLog } = await import('./shelly.js');
1236
1213
  createShellySystemLog(this.matterbridge);
1237
- 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 });
1238
1215
  }
1239
1216
  else if (data.method === '/api/shellynetconfig') {
1240
1217
  this.log.debug('/api/shellynetconfig:', data.params);
1241
1218
  const { triggerShellyChangeIp: triggerShellyChangeNet } = await import('./shelly.js');
1242
1219
  triggerShellyChangeNet(this.matterbridge, data.params);
1243
- 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 });
1244
1221
  }
1245
1222
  else if (data.method === '/api/softreset') {
1246
1223
  const { triggerShellySoftReset } = await import('./shelly.js');
1247
1224
  triggerShellySoftReset(this.matterbridge);
1248
- 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 });
1249
1226
  }
1250
1227
  else if (data.method === '/api/hardreset') {
1251
1228
  const { triggerShellyHardReset } = await import('./shelly.js');
1252
1229
  triggerShellyHardReset(this.matterbridge);
1253
- 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 });
1254
1231
  }
1255
1232
  else if (data.method === '/api/reboot') {
1256
1233
  const { triggerShellyReboot } = await import('./shelly.js');
1257
1234
  triggerShellyReboot(this.matterbridge);
1258
- 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 });
1259
1236
  }
1260
1237
  else if (data.method === '/api/restart') {
1261
1238
  this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
1262
1239
  await this.matterbridge.restartProcess();
1263
- 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 });
1264
1241
  }
1265
1242
  else if (data.method === '/api/shutdown') {
1266
1243
  this.wssSendSnackbarMessage(`Shutting down matterbridge...`, 0);
1267
1244
  await this.matterbridge.shutdownProcess();
1268
- 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 });
1269
1246
  }
1270
1247
  else if (data.method === '/api/create-backup') {
1271
1248
  this.wssSendSnackbarMessage('Creating backup...', 0);
@@ -1274,27 +1251,27 @@ export class Frontend extends EventEmitter {
1274
1251
  this.log.notice(`Backup ready to be downloaded.`);
1275
1252
  this.wssSendCloseSnackbarMessage('Creating backup...');
1276
1253
  this.wssSendSnackbarMessage('Backup ready to be downloaded', 10);
1277
- 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 });
1278
1255
  }
1279
1256
  else if (data.method === '/api/unregister') {
1280
1257
  this.wssSendSnackbarMessage('Unregistering all bridged devices...', 0);
1281
1258
  await this.matterbridge.unregisterAndShutdownProcess();
1282
1259
  this.wssSendCloseSnackbarMessage('Unregistering all bridged devices...');
1283
- 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 });
1284
1261
  }
1285
1262
  else if (data.method === '/api/reset') {
1286
1263
  this.wssSendSnackbarMessage('Resetting matterbridge commissioning...', 10);
1287
1264
  await this.matterbridge.shutdownProcessAndReset();
1288
- 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 });
1289
1266
  }
1290
1267
  else if (data.method === '/api/factoryreset') {
1291
1268
  this.wssSendSnackbarMessage('Factory reset of matterbridge...', 10);
1292
1269
  await this.matterbridge.shutdownProcessAndFactoryReset();
1293
- 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 });
1294
1271
  }
1295
1272
  else if (data.method === '/api/matter') {
1296
1273
  if (!isValidString(data.params.id)) {
1297
- 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' });
1298
1275
  return;
1299
1276
  }
1300
1277
  let serverNode;
@@ -1303,129 +1280,125 @@ export class Frontend extends EventEmitter {
1303
1280
  else
1304
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;
1305
1282
  if (!serverNode) {
1306
- 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' });
1307
1284
  return;
1308
1285
  }
1286
+ const matter = this.matterbridge.getServerNodeData(serverNode);
1309
1287
  this.log.debug(`*Server node ${serverNode.id}: commissioned ${serverNode.state.commissioning.commissioned} upTime ${serverNode.state.generalDiagnostics.upTime}.`);
1310
1288
  if (data.params.server) {
1311
1289
  this.log.debug(`*Sending data for node ${data.params.id}`);
1312
- this.wssSendRefreshRequired('matter', { matter: { ...this.matterbridge.getServerNodeData(serverNode) } });
1290
+ this.wssSendRefreshRequired('matter', { matter: { ...matter } });
1313
1291
  }
1314
1292
  if (data.params.startCommission) {
1315
1293
  await serverNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
1316
1294
  this.matterbridge.advertisingNodes.set(serverNode.id, Date.now());
1317
1295
  this.log.debug(`*Commissioning has been sent for node ${data.params.id}`);
1318
- this.wssSendRefreshRequired('matter', { matter: { ...this.matterbridge.getServerNodeData(serverNode), advertising: true } });
1296
+ this.wssSendRefreshRequired('matter', { matter: { ...matter, advertising: true } });
1319
1297
  }
1320
1298
  if (data.params.stopCommission) {
1321
1299
  await serverNode.env.get(DeviceCommissioner)?.endCommissioning();
1322
1300
  this.matterbridge.advertisingNodes.delete(serverNode.id);
1323
1301
  this.log.debug(`*Stop commissioning has been sent for node ${data.params.id}`);
1324
- this.wssSendRefreshRequired('matter', { matter: { ...this.matterbridge.getServerNodeData(serverNode), advertising: false } });
1302
+ this.wssSendRefreshRequired('matter', { matter: { ...matter, advertising: false } });
1325
1303
  }
1326
1304
  if (data.params.advertise) {
1327
1305
  await serverNode.env.get(DeviceAdvertiser)?.advertise(true);
1328
1306
  this.log.debug(`*Advertising has been sent for node ${data.params.id}`);
1329
- this.wssSendRefreshRequired('matter', { matter: { ...this.matterbridge.getServerNodeData(serverNode), advertising: true } });
1307
+ this.wssSendRefreshRequired('matter', { matter: { ...matter, advertising: true } });
1330
1308
  }
1331
1309
  if (data.params.removeFabric) {
1332
1310
  if (serverNode.env.get(FabricManager).has(FabricIndex(data.params.removeFabric)))
1333
1311
  await serverNode.env.get(FabricManager).removeFabric(FabricIndex(data.params.removeFabric));
1334
1312
  this.log.debug(`*Removed fabric index ${data.params.removeFabric} for node ${data.params.id}`);
1335
- this.wssSendRefreshRequired('matter', { matter: { ...this.matterbridge.getServerNodeData(serverNode) } });
1313
+ this.wssSendRefreshRequired('matter', { matter: { ...matter } });
1336
1314
  }
1337
- 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 });
1338
1316
  }
1339
1317
  else if (data.method === '/api/settings') {
1340
- 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() });
1341
1319
  }
1342
1320
  else if (data.method === '/api/plugins') {
1343
- const response = this.getPlugins();
1344
- 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 });
1345
1323
  }
1346
1324
  else if (data.method === '/api/devices') {
1347
1325
  const devices = await this.getDevices(isValidString(data.params.pluginName) ? data.params.pluginName : undefined);
1348
- 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 });
1349
1327
  }
1350
1328
  else if (data.method === '/api/clusters') {
1351
1329
  if (!isValidString(data.params.plugin, 10)) {
1352
- 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' });
1353
1331
  return;
1354
1332
  }
1355
1333
  if (!isValidNumber(data.params.endpoint, 1)) {
1356
- 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' });
1357
1335
  return;
1358
1336
  }
1359
1337
  const response = this.getClusters(data.params.plugin, data.params.endpoint);
1360
1338
  if (response) {
1361
- client.send(JSON.stringify({
1339
+ sendResponse({
1362
1340
  id: data.id,
1363
1341
  method: data.method,
1364
1342
  src: 'Matterbridge',
1365
1343
  dst: data.src,
1366
- plugin: data.params.plugin,
1367
- deviceName: response.deviceName,
1368
- serialNumber: response.serialNumber,
1369
- endpoint: response.endpoint,
1370
- deviceTypes: response.deviceTypes,
1371
- response: response.clusters,
1372
- }));
1344
+ response,
1345
+ });
1373
1346
  }
1374
1347
  else {
1375
- 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' });
1376
1349
  }
1377
1350
  }
1378
- else if (data.method === '/api/select' || data.method === '/api/select/devices') {
1351
+ else if (data.method === '/api/select/devices') {
1379
1352
  if (!isValidString(data.params.plugin, 10)) {
1380
- 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' });
1381
1354
  return;
1382
1355
  }
1383
1356
  const plugin = this.matterbridge.plugins.get(data.params.plugin);
1384
1357
  if (!plugin) {
1385
- 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' });
1386
1359
  return;
1387
1360
  }
1388
1361
  const selectDeviceValues = plugin.platform?.getSelectDevices().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
1389
- 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 });
1390
1363
  }
1391
1364
  else if (data.method === '/api/select/entities') {
1392
1365
  if (!isValidString(data.params.plugin, 10)) {
1393
- 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' });
1394
1367
  return;
1395
1368
  }
1396
1369
  const plugin = this.matterbridge.plugins.get(data.params.plugin);
1397
1370
  if (!plugin) {
1398
- 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' });
1399
1372
  return;
1400
1373
  }
1401
1374
  const selectEntityValues = plugin.platform?.getSelectEntities().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
1402
- 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 });
1403
1376
  }
1404
1377
  else if (data.method === '/api/action') {
1405
1378
  if (!isValidString(data.params.plugin, 5) || !isValidString(data.params.action, 1)) {
1406
- 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' });
1407
1380
  return;
1408
1381
  }
1409
1382
  const plugin = this.matterbridge.plugins.get(data.params.plugin);
1410
1383
  if (!plugin) {
1411
- 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' });
1412
1385
  return;
1413
1386
  }
1414
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}`);
1415
1388
  plugin.platform
1416
1389
  ?.onAction(data.params.action, data.params.value, data.params.id, data.params.formData)
1417
1390
  .then(() => {
1418
- 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 });
1419
1392
  return;
1420
1393
  })
1421
1394
  .catch((error) => {
1422
1395
  this.log.error(`Error in plugin ${plugin.name} action ${data.params.action}: ${error}`);
1423
- 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}` });
1424
1397
  });
1425
1398
  }
1426
1399
  else if (data.method === '/api/config') {
1427
1400
  if (!isValidString(data.params.name, 5) || data.params.value === undefined) {
1428
- 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' });
1429
1402
  return;
1430
1403
  }
1431
1404
  this.log.debug(`Received /api/config name ${CYAN}${data.params.name}${db} value ${CYAN}${data.params.value}${db}`);
@@ -1433,14 +1406,14 @@ export class Frontend extends EventEmitter {
1433
1406
  case 'setpassword':
1434
1407
  if (isValidString(data.params.value)) {
1435
1408
  await this.matterbridge.nodeContext?.set('password', data.params.value);
1436
- 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 });
1437
1410
  }
1438
1411
  break;
1439
1412
  case 'setbridgemode':
1440
1413
  if (isValidString(data.params.value) && ['bridge', 'childbridge'].includes(data.params.value)) {
1441
1414
  await this.matterbridge.nodeContext?.set('bridgeMode', data.params.value);
1442
1415
  this.wssSendRestartRequired();
1443
- 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 });
1444
1417
  }
1445
1418
  break;
1446
1419
  case 'setmbloglevel':
@@ -1465,7 +1438,7 @@ export class Frontend extends EventEmitter {
1465
1438
  await this.matterbridge.setLogLevel("fatal");
1466
1439
  }
1467
1440
  await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
1468
- 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 });
1469
1442
  }
1470
1443
  break;
1471
1444
  case 'setmblogfile':
@@ -1477,7 +1450,7 @@ export class Frontend extends EventEmitter {
1477
1450
  AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
1478
1451
  else
1479
1452
  AnsiLogger.setGlobalLogfile(undefined);
1480
- 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 });
1481
1454
  }
1482
1455
  break;
1483
1456
  case 'setmjloglevel':
@@ -1503,7 +1476,7 @@ export class Frontend extends EventEmitter {
1503
1476
  }
1504
1477
  this.matterbridge.matterbridgeInformation.matterLoggerLevel = Logger.level;
1505
1478
  await this.matterbridge.nodeContext?.set('matterLogLevel', Logger.level);
1506
- 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 });
1507
1480
  }
1508
1481
  break;
1509
1482
  case 'setmjlogfile':
@@ -1517,7 +1490,7 @@ export class Frontend extends EventEmitter {
1517
1490
  else {
1518
1491
  this.matterbridge.matterLog.logFilePath = undefined;
1519
1492
  }
1520
- 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 });
1521
1494
  }
1522
1495
  break;
1523
1496
  case 'setmdnsinterface':
@@ -1527,7 +1500,7 @@ export class Frontend extends EventEmitter {
1527
1500
  this.matterbridge.matterbridgeInformation.mattermdnsinterface = this.matterbridge.mdnsInterface;
1528
1501
  await this.matterbridge.nodeContext?.set('mattermdnsinterface', data.params.value);
1529
1502
  this.wssSendRestartRequired();
1530
- 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 });
1531
1504
  }
1532
1505
  break;
1533
1506
  case 'setipv4address':
@@ -1537,7 +1510,7 @@ export class Frontend extends EventEmitter {
1537
1510
  this.matterbridge.matterbridgeInformation.matteripv4address = this.matterbridge.ipv4address;
1538
1511
  await this.matterbridge.nodeContext?.set('matteripv4address', data.params.value);
1539
1512
  this.wssSendRestartRequired();
1540
- 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 });
1541
1514
  }
1542
1515
  break;
1543
1516
  case 'setipv6address':
@@ -1547,7 +1520,7 @@ export class Frontend extends EventEmitter {
1547
1520
  this.matterbridge.matterbridgeInformation.matteripv6address = this.matterbridge.ipv6address;
1548
1521
  await this.matterbridge.nodeContext?.set('matteripv6address', data.params.value);
1549
1522
  this.wssSendRestartRequired();
1550
- 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 });
1551
1524
  }
1552
1525
  break;
1553
1526
  case 'setmatterport':
@@ -1564,7 +1537,7 @@ export class Frontend extends EventEmitter {
1564
1537
  await this.matterbridge.nodeContext?.set('matterport', 5540);
1565
1538
  this.wssSendRestartRequired();
1566
1539
  }
1567
- 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 });
1568
1541
  break;
1569
1542
  case 'setmatterdiscriminator':
1570
1543
  data.params.value = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
@@ -1573,14 +1546,14 @@ export class Frontend extends EventEmitter {
1573
1546
  this.matterbridge.matterbridgeInformation.matterDiscriminator = data.params.value;
1574
1547
  await this.matterbridge.nodeContext?.set('matterdiscriminator', data.params.value);
1575
1548
  this.wssSendRestartRequired();
1576
- 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 });
1577
1550
  }
1578
1551
  else {
1579
1552
  this.log.debug(`Reset matter commissioning discriminator to ${CYAN}undefined${db}`);
1580
1553
  this.matterbridge.matterbridgeInformation.matterDiscriminator = undefined;
1581
1554
  await this.matterbridge.nodeContext?.remove('matterdiscriminator');
1582
1555
  this.wssSendRestartRequired();
1583
- 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 });
1584
1557
  }
1585
1558
  break;
1586
1559
  case 'setmatterpasscode':
@@ -1590,14 +1563,14 @@ export class Frontend extends EventEmitter {
1590
1563
  this.log.debug(`Set matter commissioning passcode to ${CYAN}${data.params.value}${db}`);
1591
1564
  await this.matterbridge.nodeContext?.set('matterpasscode', data.params.value);
1592
1565
  this.wssSendRestartRequired();
1593
- 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 });
1594
1567
  }
1595
1568
  else {
1596
1569
  this.log.debug(`Reset matter commissioning passcode to ${CYAN}undefined${db}`);
1597
1570
  this.matterbridge.matterbridgeInformation.matterPasscode = undefined;
1598
1571
  await this.matterbridge.nodeContext?.remove('matterpasscode');
1599
1572
  this.wssSendRestartRequired();
1600
- 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 });
1601
1574
  }
1602
1575
  break;
1603
1576
  case 'setvirtualmode':
@@ -1607,22 +1580,22 @@ export class Frontend extends EventEmitter {
1607
1580
  await this.matterbridge.nodeContext?.set('virtualmode', data.params.value);
1608
1581
  this.wssSendRestartRequired();
1609
1582
  }
1610
- 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 });
1611
1584
  break;
1612
1585
  default:
1613
1586
  this.log.warn(`Unknown parameter ${data.params.name} in /api/config`);
1614
- 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` });
1615
1588
  }
1616
1589
  }
1617
1590
  else if (data.method === '/api/command') {
1618
1591
  if (!isValidString(data.params.command, 5)) {
1619
- 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' });
1620
1593
  return;
1621
1594
  }
1622
1595
  if (data.params.command === 'selectdevice' && isValidString(data.params.plugin, 10) && isValidString(data.params.serial, 1) && isValidString(data.params.name, 1)) {
1623
1596
  const plugin = this.matterbridge.plugins.get(data.params.plugin);
1624
1597
  if (!plugin) {
1625
- 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' });
1626
1599
  return;
1627
1600
  }
1628
1601
  const config = plugin.configJson;
@@ -1659,17 +1632,17 @@ export class Frontend extends EventEmitter {
1659
1632
  if (!restartRequired)
1660
1633
  this.wssSendRefreshRequired('pluginsRestart');
1661
1634
  this.wssSendRestartRequired(false);
1662
- 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 });
1663
1636
  }
1664
1637
  else {
1665
1638
  this.log.error(`SelectDevice: select ${select} not supported`);
1666
- 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` });
1667
1640
  }
1668
1641
  }
1669
1642
  else if (data.params.command === 'unselectdevice' && isValidString(data.params.plugin, 10) && isValidString(data.params.serial, 1) && isValidString(data.params.name, 1)) {
1670
1643
  const plugin = this.matterbridge.plugins.get(data.params.plugin);
1671
1644
  if (!plugin) {
1672
- 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' });
1673
1646
  return;
1674
1647
  }
1675
1648
  const config = plugin.configJson;
@@ -1706,24 +1679,24 @@ export class Frontend extends EventEmitter {
1706
1679
  if (!restartRequired)
1707
1680
  this.wssSendRefreshRequired('pluginsRestart');
1708
1681
  this.wssSendRestartRequired(false);
1709
- 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 });
1710
1683
  }
1711
1684
  else {
1712
1685
  this.log.error(`SelectDevice: select ${select} not supported`);
1713
- 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` });
1714
1687
  }
1715
1688
  }
1716
1689
  }
1717
1690
  else {
1718
1691
  this.log.error(`Invalid method from websocket client: ${debugStringify(data)}`);
1719
- 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' });
1720
1693
  }
1721
1694
  }
1722
1695
  catch (error) {
1723
1696
  inspectError(this.log, `Error processing message "${message}" from websocket client`, error);
1724
1697
  }
1725
1698
  }
1726
- wssSendMessage(level, time, name, message) {
1699
+ wssSendLogMessage(level, time, name, message) {
1727
1700
  if (!level || !time || !name || !message)
1728
1701
  return;
1729
1702
  message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
@@ -1744,19 +1717,11 @@ export class Frontend extends EventEmitter {
1744
1717
  return word;
1745
1718
  })
1746
1719
  .join(' ');
1747
- this.webSocketServer?.clients.forEach((client) => {
1748
- if (client.readyState === WebSocket.OPEN) {
1749
- client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
1750
- }
1751
- });
1720
+ this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'log', params: { level, time, name, message } });
1752
1721
  }
1753
- wssSendRefreshRequired(changed = null, params = {}) {
1722
+ wssSendRefreshRequired(changed = null, params) {
1754
1723
  this.log.debug('Sending a refresh required message to all connected clients');
1755
- this.webSocketServer?.clients.forEach((client) => {
1756
- if (client.readyState === WebSocket.OPEN) {
1757
- client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed, ...params } }));
1758
- }
1759
- });
1724
+ this.wssBroadcastMessage({ id: 1, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed, ...params } });
1760
1725
  }
1761
1726
  wssSendRestartRequired(snackbar = true, fixed = false) {
1762
1727
  this.log.debug('Sending a restart required message to all connected clients');
@@ -1764,88 +1729,54 @@ export class Frontend extends EventEmitter {
1764
1729
  this.matterbridge.matterbridgeInformation.fixedRestartRequired = fixed;
1765
1730
  if (snackbar === true)
1766
1731
  this.wssSendSnackbarMessage(`Restart required`, 0);
1767
- this.webSocketServer?.clients.forEach((client) => {
1768
- if (client.readyState === WebSocket.OPEN) {
1769
- client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: { fixed } }));
1770
- }
1771
- });
1732
+ this.wssBroadcastMessage({ id: 2, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: { fixed } });
1772
1733
  }
1773
1734
  wssSendRestartNotRequired(snackbar = true) {
1774
1735
  this.log.debug('Sending a restart not required message to all connected clients');
1775
1736
  this.matterbridge.matterbridgeInformation.restartRequired = false;
1776
1737
  if (snackbar === true)
1777
1738
  this.wssSendCloseSnackbarMessage(`Restart required`);
1778
- this.webSocketServer?.clients.forEach((client) => {
1779
- if (client.readyState === WebSocket.OPEN) {
1780
- client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required', params: {} }));
1781
- }
1782
- });
1739
+ this.wssBroadcastMessage({ id: 3, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required' });
1783
1740
  }
1784
1741
  wssSendUpdateRequired(devVersion = false) {
1785
1742
  this.log.debug('Sending an update required message to all connected clients');
1786
1743
  this.matterbridge.matterbridgeInformation.updateRequired = true;
1787
- this.webSocketServer?.clients.forEach((client) => {
1788
- if (client.readyState === WebSocket.OPEN) {
1789
- client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: devVersion ? 'update_required_dev' : 'update_required', params: {} }));
1790
- }
1791
- });
1744
+ this.wssBroadcastMessage({ id: 8, src: 'Matterbridge', dst: 'Frontend', method: devVersion ? 'update_required_dev' : 'update_required' });
1792
1745
  }
1793
1746
  wssSendCpuUpdate(cpuUsage) {
1794
1747
  if (hasParameter('debug'))
1795
1748
  this.log.debug('Sending a cpu update message to all connected clients');
1796
- this.webSocketServer?.clients.forEach((client) => {
1797
- if (client.readyState === WebSocket.OPEN) {
1798
- client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
1799
- }
1800
- });
1749
+ this.wssBroadcastMessage({ id: 4, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } });
1801
1750
  }
1802
1751
  wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
1803
1752
  if (hasParameter('debug'))
1804
1753
  this.log.debug('Sending a memory update message to all connected clients');
1805
- this.webSocketServer?.clients.forEach((client) => {
1806
- if (client.readyState === WebSocket.OPEN) {
1807
- client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
1808
- }
1809
- });
1754
+ this.wssBroadcastMessage({ id: 5, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } });
1810
1755
  }
1811
1756
  wssSendUptimeUpdate(systemUptime, processUptime) {
1812
1757
  if (hasParameter('debug'))
1813
1758
  this.log.debug('Sending a uptime update message to all connected clients');
1814
- this.webSocketServer?.clients.forEach((client) => {
1815
- if (client.readyState === WebSocket.OPEN) {
1816
- client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
1817
- }
1818
- });
1759
+ this.wssBroadcastMessage({ id: 6, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } });
1819
1760
  }
1820
1761
  wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
1821
1762
  this.log.debug('Sending a snackbar message to all connected clients');
1822
- this.webSocketServer?.clients.forEach((client) => {
1823
- if (client.readyState === WebSocket.OPEN) {
1824
- client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message, timeout, severity } }));
1825
- }
1826
- });
1763
+ this.wssBroadcastMessage({ id: 7, src: 'Matterbridge', dst: 'Frontend', method: 'snackbar', params: { message, timeout, severity } });
1827
1764
  }
1828
1765
  wssSendCloseSnackbarMessage(message) {
1829
1766
  this.log.debug('Sending a close snackbar message to all connected clients');
1830
- this.webSocketServer?.clients.forEach((client) => {
1831
- if (client.readyState === WebSocket.OPEN) {
1832
- client.send(JSON.stringify({ id: WS_ID_CLOSE_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message } }));
1833
- }
1834
- });
1767
+ this.wssBroadcastMessage({ id: 10, src: 'Matterbridge', dst: 'Frontend', method: 'close_snackbar', params: { message } });
1835
1768
  }
1836
1769
  wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
1837
1770
  this.log.debug('Sending an attribute update message to all connected clients');
1838
- this.webSocketServer?.clients.forEach((client) => {
1839
- if (client.readyState === WebSocket.OPEN) {
1840
- client.send(JSON.stringify({ id: WS_ID_STATEUPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } }));
1841
- }
1842
- });
1771
+ this.wssBroadcastMessage({ id: 9, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } });
1843
1772
  }
1844
- wssBroadcastMessage(id, method, params) {
1845
- 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)}`);
1846
1777
  this.webSocketServer?.clients.forEach((client) => {
1847
1778
  if (client.readyState === WebSocket.OPEN) {
1848
- client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
1779
+ client.send(stringifiedMsg);
1849
1780
  }
1850
1781
  });
1851
1782
  }