matterbridge 3.2.8-dev-20250916-da71182 → 3.2.8-dev-20250919-e967b55

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 +190 -256
  3. package/dist/frontendTypes.js +25 -0
  4. package/dist/matterbridge.js +18 -54
  5. package/dist/shelly.js +4 -5
  6. package/dist/update.js +0 -2
  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 +5678 -17171
  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.565f820c.js +0 -3
  32. package/frontend/build/static/js/main.565f820c.js.LICENSE.txt +0 -97
  33. package/frontend/build/static/js/main.565f820c.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');
@@ -666,20 +636,12 @@ export class Frontend extends EventEmitter {
666
636
  this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
667
637
  this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
668
638
  this.matterbridge.matterbridgeInformation.matterLoggerLevel = Logger.defaultLogLevel;
669
- this.matterbridge.matterbridgeInformation.mattermdnsinterface = this.matterbridge.mdnsInterface;
670
- this.matterbridge.matterbridgeInformation.matteripv4address = this.matterbridge.ipv4address;
671
- this.matterbridge.matterbridgeInformation.matteripv6address = this.matterbridge.ipv6address;
639
+ this.matterbridge.matterbridgeInformation.matterMdnsInterface = this.matterbridge.mdnsInterface;
640
+ this.matterbridge.matterbridgeInformation.matterIpv4Address = this.matterbridge.ipv4address;
641
+ this.matterbridge.matterbridgeInformation.matterIpv6Address = this.matterbridge.ipv6address;
672
642
  this.matterbridge.matterbridgeInformation.matterPort = (await this.matterbridge.nodeContext?.get('matterport', 5540)) ?? 5540;
673
643
  this.matterbridge.matterbridgeInformation.matterDiscriminator = await this.matterbridge.nodeContext?.get('matterdiscriminator');
674
644
  this.matterbridge.matterbridgeInformation.matterPasscode = await this.matterbridge.nodeContext?.get('matterpasscode');
675
- if (this.matterbridge.bridgeMode === 'bridge' && this.matterbridge.serverNode && !this.matterbridge.hasCleanupStarted) {
676
- this.matterbridge.matterbridgeInformation.matter = this.matterbridge.getServerNodeData(this.matterbridge.serverNode);
677
- this.matterbridge.matterbridgeInformation.matterbridgePaired = this.matterbridge.serverNode.state.commissioning.commissioned;
678
- this.matterbridge.matterbridgeInformation.matterbridgeQrPairingCode = this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : this.matterbridge.serverNode.state.commissioning.pairingCodes.qrPairingCode;
679
- this.matterbridge.matterbridgeInformation.matterbridgeManualPairingCode = this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : this.matterbridge.serverNode.state.commissioning.pairingCodes.manualPairingCode;
680
- this.matterbridge.matterbridgeInformation.matterbridgeFabricInformations = this.matterbridge.sanitizeFabricInformations(Object.values(this.matterbridge.serverNode.state.commissioning.fabrics));
681
- this.matterbridge.matterbridgeInformation.matterbridgeSessionInformations = this.matterbridge.sanitizeSessionInformation(Object.values(this.matterbridge.serverNode.state.sessions.sessions));
682
- }
683
645
  return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
684
646
  }
685
647
  getReachability(device) {
@@ -956,50 +918,66 @@ export class Frontend extends EventEmitter {
956
918
  }
957
919
  async wsMessageHandler(client, message) {
958
920
  let data;
921
+ const sendResponse = (data) => {
922
+ if (client.readyState === WebSocket.OPEN) {
923
+ if ('response' in data) {
924
+ const { response, ...rest } = data;
925
+ this.log.debug(`Sending api response message: ${debugStringify(rest)}`);
926
+ }
927
+ else if ('error' in data) {
928
+ this.log.debug(`Sending api error message: ${debugStringify(data)}`);
929
+ }
930
+ else {
931
+ this.log.debug(`Sending api response message: ${debugStringify(data)}`);
932
+ }
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') {
967
+ const localData = data;
990
968
  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' }));
969
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter in /api/install' });
992
970
  return;
993
971
  }
994
972
  this.wssSendSnackbarMessage(`Installing package ${data.params.packageName}...`, 0);
995
973
  const { spawnCommand } = await import('./utils/spawn.js');
996
974
  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 }));
999
- this.wssSendCloseSnackbarMessage(`Installing package ${data.params.packageName}...`);
1000
- this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`, 5, 'success');
1001
- const packageName = data.params.packageName.replace(/@.*$/, '');
1002
- if (data.params.restart === false && packageName !== 'matterbridge') {
975
+ .then((_response) => {
976
+ sendResponse({ id: localData.id, method: localData.method, src: 'Matterbridge', dst: data.src, success: true });
977
+ this.wssSendCloseSnackbarMessage(`Installing package ${localData.params.packageName}...`);
978
+ this.wssSendSnackbarMessage(`Installed package ${localData.params.packageName}`, 5, 'success');
979
+ const packageName = localData.params.packageName.replace(/@.*$/, '');
980
+ if (localData.params.restart === false && packageName !== 'matterbridge') {
1003
981
  this.matterbridge.plugins
1004
982
  .add(packageName)
1005
983
  .then((plugin) => {
@@ -1040,14 +1018,15 @@ export class Frontend extends EventEmitter {
1040
1018
  return;
1041
1019
  })
1042
1020
  .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 }));
1044
- this.wssSendCloseSnackbarMessage(`Installing package ${data.params.packageName}...`);
1045
- this.wssSendSnackbarMessage(`Package ${data.params.packageName} not installed`, 10, 'error');
1021
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: error instanceof Error ? error.message : error });
1022
+ this.wssSendCloseSnackbarMessage(`Installing package ${localData.params.packageName}...`);
1023
+ this.wssSendSnackbarMessage(`Package ${localData.params.packageName} not installed`, 10, 'error');
1046
1024
  });
1047
1025
  }
1048
1026
  else if (data.method === '/api/uninstall') {
1027
+ const localData = data;
1049
1028
  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' }));
1029
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' });
1051
1030
  return;
1052
1031
  }
1053
1032
  const plugin = this.matterbridge.plugins.get(data.params.packageName);
@@ -1061,53 +1040,54 @@ export class Frontend extends EventEmitter {
1061
1040
  this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
1062
1041
  const { spawnCommand } = await import('./utils/spawn.js');
1063
1042
  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 }));
1066
- this.wssSendCloseSnackbarMessage(`Uninstalling package ${data.params.packageName}...`);
1067
- this.wssSendSnackbarMessage(`Uninstalled package ${data.params.packageName}`, 5, 'success');
1043
+ .then((_response) => {
1044
+ sendResponse({ id: localData.id, method: localData.method, src: 'Matterbridge', dst: data.src, success: true });
1045
+ this.wssSendCloseSnackbarMessage(`Uninstalling package ${localData.params.packageName}...`);
1046
+ this.wssSendSnackbarMessage(`Uninstalled package ${localData.params.packageName}`, 5, 'success');
1068
1047
  return;
1069
1048
  })
1070
1049
  .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 }));
1072
- this.wssSendCloseSnackbarMessage(`Uninstalling package ${data.params.packageName}...`);
1073
- this.wssSendSnackbarMessage(`Package ${data.params.packageName} not uninstalled`, 10, 'error');
1050
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: error instanceof Error ? error.message : error });
1051
+ this.wssSendCloseSnackbarMessage(`Uninstalling package ${localData.params.packageName}...`);
1052
+ this.wssSendSnackbarMessage(`Package ${localData.params.packageName} not uninstalled`, 10, 'error');
1074
1053
  this.wssSendSnackbarMessage(`Restart required`, 0);
1075
1054
  });
1076
1055
  }
1077
1056
  else if (data.method === '/api/addplugin') {
1057
+ const localData = data;
1078
1058
  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' }));
1059
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginNameOrPath in /api/addplugin' });
1080
1060
  return;
1081
1061
  }
1082
1062
  data.params.pluginNameOrPath = data.params.pluginNameOrPath.replace(/@.*$/, '');
1083
1063
  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` }));
1064
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Plugin ${data.params.pluginNameOrPath} already added` });
1085
1065
  this.wssSendSnackbarMessage(`Plugin ${data.params.pluginNameOrPath} already added`, 10, 'warning');
1086
1066
  return;
1087
1067
  }
1088
1068
  const plugin = await this.matterbridge.plugins.add(data.params.pluginNameOrPath);
1089
1069
  if (plugin) {
1090
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: plugin.name }));
1070
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1091
1071
  this.wssSendSnackbarMessage(`Added plugin ${data.params.pluginNameOrPath}`, 5, 'success');
1092
1072
  this.matterbridge.plugins
1093
1073
  .load(plugin, true, 'The plugin has been added', true)
1094
1074
  .then(() => {
1095
1075
  this.wssSendRefreshRequired('plugins');
1096
1076
  this.wssSendRefreshRequired('devices');
1097
- this.wssSendSnackbarMessage(`Started plugin ${data.params.pluginNameOrPath}`, 5, 'success');
1077
+ this.wssSendSnackbarMessage(`Started plugin ${localData.params.pluginNameOrPath}`, 5, 'success');
1098
1078
  return;
1099
1079
  })
1100
1080
  .catch((_error) => {
1101
1081
  });
1102
1082
  }
1103
1083
  else {
1104
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Plugin ${data.params.pluginNameOrPath} not added` }));
1084
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Plugin ${data.params.pluginNameOrPath} not added` });
1105
1085
  this.wssSendSnackbarMessage(`Plugin ${data.params.pluginNameOrPath} not added`, 10, 'error');
1106
1086
  }
1107
1087
  }
1108
1088
  else if (data.method === '/api/removeplugin') {
1109
1089
  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' }));
1090
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/removeplugin' });
1111
1091
  return;
1112
1092
  }
1113
1093
  const plugin = this.matterbridge.plugins.get(data.params.pluginName);
@@ -1116,11 +1096,12 @@ export class Frontend extends EventEmitter {
1116
1096
  this.wssSendSnackbarMessage(`Removed plugin ${data.params.pluginName}`, 5, 'success');
1117
1097
  this.wssSendRefreshRequired('plugins');
1118
1098
  this.wssSendRefreshRequired('devices');
1119
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1099
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1120
1100
  }
1121
1101
  else if (data.method === '/api/enableplugin') {
1102
+ const localData = data;
1122
1103
  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' }));
1104
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/enableplugin' });
1124
1105
  return;
1125
1106
  }
1126
1107
  const plugin = this.matterbridge.plugins.get(data.params.pluginName);
@@ -1140,17 +1121,17 @@ export class Frontend extends EventEmitter {
1140
1121
  .then(() => {
1141
1122
  this.wssSendRefreshRequired('plugins');
1142
1123
  this.wssSendRefreshRequired('devices');
1143
- this.wssSendSnackbarMessage(`Started plugin ${data.params.pluginName}`, 5, 'success');
1124
+ this.wssSendSnackbarMessage(`Started plugin ${localData.params.pluginName}`, 5, 'success');
1144
1125
  return;
1145
1126
  })
1146
1127
  .catch((_error) => {
1147
1128
  });
1148
1129
  }
1149
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1130
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1150
1131
  }
1151
1132
  else if (data.method === '/api/disableplugin') {
1152
1133
  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' }));
1134
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/disableplugin' });
1154
1135
  return;
1155
1136
  }
1156
1137
  const plugin = this.matterbridge.plugins.get(data.params.pluginName);
@@ -1159,11 +1140,11 @@ export class Frontend extends EventEmitter {
1159
1140
  this.wssSendSnackbarMessage(`Disabled plugin ${data.params.pluginName}`, 5, 'success');
1160
1141
  this.wssSendRefreshRequired('plugins');
1161
1142
  this.wssSendRefreshRequired('devices');
1162
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1143
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1163
1144
  }
1164
1145
  else if (data.method === '/api/restartplugin') {
1165
1146
  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' }));
1147
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/restartplugin' });
1167
1148
  return;
1168
1149
  }
1169
1150
  const plugin = this.matterbridge.plugins.get(data.params.pluginName);
@@ -1195,15 +1176,15 @@ export class Frontend extends EventEmitter {
1195
1176
  this.wssSendSnackbarMessage(`Restarted plugin ${data.params.pluginName}`, 5, 'success');
1196
1177
  this.wssSendRefreshRequired('plugins');
1197
1178
  this.wssSendRefreshRequired('devices');
1198
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1179
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1199
1180
  }
1200
1181
  else if (data.method === '/api/savepluginconfig') {
1201
1182
  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' }));
1183
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/savepluginconfig' });
1203
1184
  return;
1204
1185
  }
1205
1186
  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' }));
1187
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter formData in /api/savepluginconfig' });
1207
1188
  return;
1208
1189
  }
1209
1190
  this.log.info(`Saving config for plugin ${plg}${data.params.pluginName}${nf}...`);
@@ -1211,61 +1192,60 @@ export class Frontend extends EventEmitter {
1211
1192
  if (plugin) {
1212
1193
  this.matterbridge.plugins.saveConfigFromJson(plugin, data.params.formData, true);
1213
1194
  this.wssSendSnackbarMessage(`Saved config for plugin ${data.params.pluginName}`);
1214
- this.wssSendRefreshRequired('pluginsRestart');
1215
1195
  this.wssSendRestartRequired();
1216
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1196
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1217
1197
  }
1218
1198
  }
1219
1199
  else if (data.method === '/api/checkupdates') {
1220
1200
  const { checkUpdates } = await import('./update.js');
1221
1201
  checkUpdates(this.matterbridge);
1222
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1202
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1223
1203
  }
1224
1204
  else if (data.method === '/api/shellysysupdate') {
1225
1205
  const { triggerShellySysUpdate } = await import('./shelly.js');
1226
1206
  triggerShellySysUpdate(this.matterbridge);
1227
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1207
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1228
1208
  }
1229
1209
  else if (data.method === '/api/shellymainupdate') {
1230
1210
  const { triggerShellyMainUpdate } = await import('./shelly.js');
1231
1211
  triggerShellyMainUpdate(this.matterbridge);
1232
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1212
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1233
1213
  }
1234
1214
  else if (data.method === '/api/shellycreatesystemlog') {
1235
1215
  const { createShellySystemLog } = await import('./shelly.js');
1236
1216
  createShellySystemLog(this.matterbridge);
1237
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1217
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1238
1218
  }
1239
1219
  else if (data.method === '/api/shellynetconfig') {
1240
1220
  this.log.debug('/api/shellynetconfig:', data.params);
1241
1221
  const { triggerShellyChangeIp: triggerShellyChangeNet } = await import('./shelly.js');
1242
1222
  triggerShellyChangeNet(this.matterbridge, data.params);
1243
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1223
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1244
1224
  }
1245
1225
  else if (data.method === '/api/softreset') {
1246
1226
  const { triggerShellySoftReset } = await import('./shelly.js');
1247
1227
  triggerShellySoftReset(this.matterbridge);
1248
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1228
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1249
1229
  }
1250
1230
  else if (data.method === '/api/hardreset') {
1251
1231
  const { triggerShellyHardReset } = await import('./shelly.js');
1252
1232
  triggerShellyHardReset(this.matterbridge);
1253
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1233
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1254
1234
  }
1255
1235
  else if (data.method === '/api/reboot') {
1256
1236
  const { triggerShellyReboot } = await import('./shelly.js');
1257
1237
  triggerShellyReboot(this.matterbridge);
1258
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1238
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1259
1239
  }
1260
1240
  else if (data.method === '/api/restart') {
1261
1241
  this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
1262
1242
  await this.matterbridge.restartProcess();
1263
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1243
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1264
1244
  }
1265
1245
  else if (data.method === '/api/shutdown') {
1266
1246
  this.wssSendSnackbarMessage(`Shutting down matterbridge...`, 0);
1267
1247
  await this.matterbridge.shutdownProcess();
1268
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1248
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1269
1249
  }
1270
1250
  else if (data.method === '/api/create-backup') {
1271
1251
  this.wssSendSnackbarMessage('Creating backup...', 0);
@@ -1274,158 +1254,157 @@ export class Frontend extends EventEmitter {
1274
1254
  this.log.notice(`Backup ready to be downloaded.`);
1275
1255
  this.wssSendCloseSnackbarMessage('Creating backup...');
1276
1256
  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 }));
1257
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1278
1258
  }
1279
1259
  else if (data.method === '/api/unregister') {
1280
1260
  this.wssSendSnackbarMessage('Unregistering all bridged devices...', 0);
1281
1261
  await this.matterbridge.unregisterAndShutdownProcess();
1282
1262
  this.wssSendCloseSnackbarMessage('Unregistering all bridged devices...');
1283
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1263
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1284
1264
  }
1285
1265
  else if (data.method === '/api/reset') {
1286
1266
  this.wssSendSnackbarMessage('Resetting matterbridge commissioning...', 10);
1287
1267
  await this.matterbridge.shutdownProcessAndReset();
1288
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1268
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1289
1269
  }
1290
1270
  else if (data.method === '/api/factoryreset') {
1291
1271
  this.wssSendSnackbarMessage('Factory reset of matterbridge...', 10);
1292
1272
  await this.matterbridge.shutdownProcessAndFactoryReset();
1293
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1273
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1294
1274
  }
1295
1275
  else if (data.method === '/api/matter') {
1276
+ const localData = data;
1296
1277
  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' }));
1278
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter id in /api/matter' });
1298
1279
  return;
1299
1280
  }
1300
1281
  let serverNode;
1301
1282
  if (data.params.id === 'Matterbridge')
1302
1283
  serverNode = this.matterbridge.serverNode;
1303
1284
  else
1304
- 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;
1285
+ serverNode = this.matterbridge.getPlugins().find((p) => p.serverNode && p.serverNode.id === localData.params.id)?.serverNode || this.matterbridge.getDevices().find((d) => d.serverNode && d.serverNode.id === localData.params.id)?.serverNode;
1305
1286
  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' }));
1287
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Unknown server node id in /api/matter' });
1307
1288
  return;
1308
1289
  }
1290
+ const matter = this.matterbridge.getServerNodeData(serverNode);
1309
1291
  this.log.debug(`*Server node ${serverNode.id}: commissioned ${serverNode.state.commissioning.commissioned} upTime ${serverNode.state.generalDiagnostics.upTime}.`);
1310
1292
  if (data.params.server) {
1311
1293
  this.log.debug(`*Sending data for node ${data.params.id}`);
1312
- this.wssSendRefreshRequired('matter', { matter: { ...this.matterbridge.getServerNodeData(serverNode) } });
1294
+ this.wssSendRefreshRequired('matter', { matter: { ...matter } });
1313
1295
  }
1314
1296
  if (data.params.startCommission) {
1315
1297
  await serverNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
1316
1298
  this.matterbridge.advertisingNodes.set(serverNode.id, Date.now());
1317
- this.log.debug(`*Commissioning has been sent for node ${data.params.id}`);
1318
- this.wssSendRefreshRequired('matter', { matter: { ...this.matterbridge.getServerNodeData(serverNode), advertising: true } });
1299
+ this.log.debug(`*Allow commissioning has been sent for node ${data.params.id}`);
1300
+ this.wssSendRefreshRequired('matter', { matter: { ...matter, advertiseTime: Date.now(), advertising: true } });
1319
1301
  }
1320
1302
  if (data.params.stopCommission) {
1321
1303
  await serverNode.env.get(DeviceCommissioner)?.endCommissioning();
1322
1304
  this.matterbridge.advertisingNodes.delete(serverNode.id);
1323
- this.log.debug(`*Stop commissioning has been sent for node ${data.params.id}`);
1324
- this.wssSendRefreshRequired('matter', { matter: { ...this.matterbridge.getServerNodeData(serverNode), advertising: false } });
1305
+ this.log.debug(`*End commissioning has been sent for node ${data.params.id}`);
1306
+ this.wssSendRefreshRequired('matter', { matter: { ...matter, advertiseTime: 0, advertising: false } });
1325
1307
  }
1326
1308
  if (data.params.advertise) {
1327
1309
  await serverNode.env.get(DeviceAdvertiser)?.advertise(true);
1328
1310
  this.log.debug(`*Advertising has been sent for node ${data.params.id}`);
1329
- this.wssSendRefreshRequired('matter', { matter: { ...this.matterbridge.getServerNodeData(serverNode), advertising: true } });
1311
+ this.wssSendRefreshRequired('matter', { matter: { ...matter, advertising: true } });
1330
1312
  }
1331
1313
  if (data.params.removeFabric) {
1332
1314
  if (serverNode.env.get(FabricManager).has(FabricIndex(data.params.removeFabric)))
1333
1315
  await serverNode.env.get(FabricManager).removeFabric(FabricIndex(data.params.removeFabric));
1334
1316
  this.log.debug(`*Removed fabric index ${data.params.removeFabric} for node ${data.params.id}`);
1335
- this.wssSendRefreshRequired('matter', { matter: { ...this.matterbridge.getServerNodeData(serverNode) } });
1317
+ this.wssSendRefreshRequired('matter', { matter: { ...matter } });
1336
1318
  }
1337
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1319
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true, response: matter });
1338
1320
  }
1339
1321
  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() }));
1322
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true, response: await this.getApiSettings() });
1341
1323
  }
1342
1324
  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 }));
1325
+ const plugins = this.getPlugins();
1326
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true, response: plugins });
1345
1327
  }
1346
1328
  else if (data.method === '/api/devices') {
1347
1329
  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 }));
1330
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true, response: devices });
1349
1331
  }
1350
1332
  else if (data.method === '/api/clusters') {
1351
1333
  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' }));
1334
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter plugin in /api/clusters' });
1353
1335
  return;
1354
1336
  }
1355
1337
  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' }));
1338
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter endpoint in /api/clusters' });
1357
1339
  return;
1358
1340
  }
1359
1341
  const response = this.getClusters(data.params.plugin, data.params.endpoint);
1360
1342
  if (response) {
1361
- client.send(JSON.stringify({
1343
+ sendResponse({
1362
1344
  id: data.id,
1363
1345
  method: data.method,
1364
1346
  src: 'Matterbridge',
1365
1347
  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
- }));
1348
+ success: true,
1349
+ response,
1350
+ });
1373
1351
  }
1374
1352
  else {
1375
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Endpoint not found in /api/clusters' }));
1353
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Endpoint not found in /api/clusters' });
1376
1354
  }
1377
1355
  }
1378
- else if (data.method === '/api/select' || data.method === '/api/select/devices') {
1356
+ else if (data.method === '/api/select/devices') {
1379
1357
  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' }));
1358
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter plugin in /api/select/devices' });
1381
1359
  return;
1382
1360
  }
1383
1361
  const plugin = this.matterbridge.plugins.get(data.params.plugin);
1384
1362
  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' }));
1363
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select/devices' });
1386
1364
  return;
1387
1365
  }
1388
1366
  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 }));
1367
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true, response: selectDeviceValues });
1390
1368
  }
1391
1369
  else if (data.method === '/api/select/entities') {
1392
1370
  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' }));
1371
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter plugin in /api/select/entities' });
1394
1372
  return;
1395
1373
  }
1396
1374
  const plugin = this.matterbridge.plugins.get(data.params.plugin);
1397
1375
  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' }));
1376
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select/entities' });
1399
1377
  return;
1400
1378
  }
1401
1379
  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 }));
1380
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true, response: selectEntityValues });
1403
1381
  }
1404
1382
  else if (data.method === '/api/action') {
1383
+ const localData = data;
1405
1384
  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' }));
1385
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter in /api/action' });
1407
1386
  return;
1408
1387
  }
1409
1388
  const plugin = this.matterbridge.plugins.get(data.params.plugin);
1410
1389
  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' }));
1390
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/action' });
1412
1391
  return;
1413
1392
  }
1414
1393
  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
1394
  plugin.platform
1416
1395
  ?.onAction(data.params.action, data.params.value, data.params.id, data.params.formData)
1417
1396
  .then(() => {
1418
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1397
+ sendResponse({ id: localData.id, method: localData.method, src: 'Matterbridge', dst: localData.src, success: true });
1419
1398
  return;
1420
1399
  })
1421
1400
  .catch((error) => {
1422
- 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}` }));
1401
+ this.log.error(`Error in plugin ${plugin.name} action ${localData.params.action}: ${error}`);
1402
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Error in plugin ${plugin.name} action ${localData.params.action}: ${error}` });
1424
1403
  });
1425
1404
  }
1426
1405
  else if (data.method === '/api/config') {
1427
1406
  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' }));
1407
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter in /api/config' });
1429
1408
  return;
1430
1409
  }
1431
1410
  this.log.debug(`Received /api/config name ${CYAN}${data.params.name}${db} value ${CYAN}${data.params.value}${db}`);
@@ -1433,14 +1412,14 @@ export class Frontend extends EventEmitter {
1433
1412
  case 'setpassword':
1434
1413
  if (isValidString(data.params.value)) {
1435
1414
  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 }));
1415
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1437
1416
  }
1438
1417
  break;
1439
1418
  case 'setbridgemode':
1440
1419
  if (isValidString(data.params.value) && ['bridge', 'childbridge'].includes(data.params.value)) {
1441
1420
  await this.matterbridge.nodeContext?.set('bridgeMode', data.params.value);
1442
1421
  this.wssSendRestartRequired();
1443
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1422
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1444
1423
  }
1445
1424
  break;
1446
1425
  case 'setmbloglevel':
@@ -1465,7 +1444,7 @@ export class Frontend extends EventEmitter {
1465
1444
  await this.matterbridge.setLogLevel("fatal");
1466
1445
  }
1467
1446
  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 }));
1447
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1469
1448
  }
1470
1449
  break;
1471
1450
  case 'setmblogfile':
@@ -1477,7 +1456,7 @@ export class Frontend extends EventEmitter {
1477
1456
  AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
1478
1457
  else
1479
1458
  AnsiLogger.setGlobalLogfile(undefined);
1480
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1459
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1481
1460
  }
1482
1461
  break;
1483
1462
  case 'setmjloglevel':
@@ -1503,7 +1482,7 @@ export class Frontend extends EventEmitter {
1503
1482
  }
1504
1483
  this.matterbridge.matterbridgeInformation.matterLoggerLevel = Logger.level;
1505
1484
  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 }));
1485
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1507
1486
  }
1508
1487
  break;
1509
1488
  case 'setmjlogfile':
@@ -1517,87 +1496,88 @@ export class Frontend extends EventEmitter {
1517
1496
  else {
1518
1497
  this.matterbridge.matterLog.logFilePath = undefined;
1519
1498
  }
1520
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1499
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1521
1500
  }
1522
1501
  break;
1523
1502
  case 'setmdnsinterface':
1524
1503
  if (isValidString(data.params.value)) {
1525
1504
  this.log.debug(`Matter.js mdns interface: ${data.params.value === '' ? 'all interfaces' : data.params.value}`);
1526
1505
  this.matterbridge.mdnsInterface = data.params.value === '' ? undefined : data.params.value;
1527
- this.matterbridge.matterbridgeInformation.mattermdnsinterface = this.matterbridge.mdnsInterface;
1506
+ this.matterbridge.matterbridgeInformation.matterMdnsInterface = this.matterbridge.mdnsInterface;
1528
1507
  await this.matterbridge.nodeContext?.set('mattermdnsinterface', data.params.value);
1529
1508
  this.wssSendRestartRequired();
1530
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1509
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1531
1510
  }
1532
1511
  break;
1533
1512
  case 'setipv4address':
1534
1513
  if (isValidString(data.params.value)) {
1535
1514
  this.log.debug(`Matter.js ipv4 address: ${data.params.value === '' ? 'all ipv4 addresses' : data.params.value}`);
1536
1515
  this.matterbridge.ipv4address = data.params.value === '' ? undefined : data.params.value;
1537
- this.matterbridge.matterbridgeInformation.matteripv4address = this.matterbridge.ipv4address;
1516
+ this.matterbridge.matterbridgeInformation.matterIpv4Address = this.matterbridge.ipv4address;
1538
1517
  await this.matterbridge.nodeContext?.set('matteripv4address', data.params.value);
1539
1518
  this.wssSendRestartRequired();
1540
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1519
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1541
1520
  }
1542
1521
  break;
1543
1522
  case 'setipv6address':
1544
1523
  if (isValidString(data.params.value)) {
1545
1524
  this.log.debug(`Matter.js ipv6 address: ${data.params.value === '' ? 'all ipv6 addresses' : data.params.value}`);
1546
1525
  this.matterbridge.ipv6address = data.params.value === '' ? undefined : data.params.value;
1547
- this.matterbridge.matterbridgeInformation.matteripv6address = this.matterbridge.ipv6address;
1526
+ this.matterbridge.matterbridgeInformation.matterIpv6Address = this.matterbridge.ipv6address;
1548
1527
  await this.matterbridge.nodeContext?.set('matteripv6address', data.params.value);
1549
1528
  this.wssSendRestartRequired();
1550
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1529
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1551
1530
  }
1552
1531
  break;
1553
1532
  case 'setmatterport':
1554
- data.params.value = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
1555
- if (isValidNumber(data.params.value, 5540, 5580)) {
1556
- this.log.debug(`Set matter commissioning port to ${CYAN}${data.params.value}${db}`);
1557
- this.matterbridge.matterbridgeInformation.matterPort = data.params.value;
1558
- await this.matterbridge.nodeContext?.set('matterport', data.params.value);
1533
+ const port = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
1534
+ if (isValidNumber(port, 5540, 5600)) {
1535
+ this.log.debug(`Set matter commissioning port to ${CYAN}${port}${db}`);
1536
+ this.matterbridge.matterbridgeInformation.matterPort = port;
1537
+ await this.matterbridge.nodeContext?.set('matterport', port);
1559
1538
  this.wssSendRestartRequired();
1539
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1560
1540
  }
1561
1541
  else {
1562
1542
  this.log.debug(`Reset matter commissioning port to ${CYAN}5540${db}`);
1563
1543
  this.matterbridge.matterbridgeInformation.matterPort = 5540;
1564
1544
  await this.matterbridge.nodeContext?.set('matterport', 5540);
1565
1545
  this.wssSendRestartRequired();
1546
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Invalid value: reset matter commissioning port to default 5540' });
1566
1547
  }
1567
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1568
1548
  break;
1569
1549
  case 'setmatterdiscriminator':
1570
- data.params.value = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
1571
- if (isValidNumber(data.params.value, 1000, 4095)) {
1572
- this.log.debug(`Set matter commissioning discriminator to ${CYAN}${data.params.value}${db}`);
1573
- this.matterbridge.matterbridgeInformation.matterDiscriminator = data.params.value;
1574
- await this.matterbridge.nodeContext?.set('matterdiscriminator', data.params.value);
1550
+ const discriminator = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
1551
+ if (isValidNumber(discriminator, 1000, 4095)) {
1552
+ this.log.debug(`Set matter commissioning discriminator to ${CYAN}${discriminator}${db}`);
1553
+ this.matterbridge.matterbridgeInformation.matterDiscriminator = discriminator;
1554
+ await this.matterbridge.nodeContext?.set('matterdiscriminator', discriminator);
1575
1555
  this.wssSendRestartRequired();
1576
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1556
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1577
1557
  }
1578
1558
  else {
1579
1559
  this.log.debug(`Reset matter commissioning discriminator to ${CYAN}undefined${db}`);
1580
1560
  this.matterbridge.matterbridgeInformation.matterDiscriminator = undefined;
1581
1561
  await this.matterbridge.nodeContext?.remove('matterdiscriminator');
1582
1562
  this.wssSendRestartRequired();
1583
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: false }));
1563
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Invalid value: reset matter commissioning discriminator to default undefined' });
1584
1564
  }
1585
1565
  break;
1586
1566
  case 'setmatterpasscode':
1587
- data.params.value = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
1588
- if (isValidNumber(data.params.value, 10000000, 90000000)) {
1589
- this.matterbridge.matterbridgeInformation.matterPasscode = data.params.value;
1590
- this.log.debug(`Set matter commissioning passcode to ${CYAN}${data.params.value}${db}`);
1591
- await this.matterbridge.nodeContext?.set('matterpasscode', data.params.value);
1567
+ const passcode = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
1568
+ if (isValidNumber(passcode, 10000000, 90000000)) {
1569
+ this.matterbridge.matterbridgeInformation.matterPasscode = passcode;
1570
+ this.log.debug(`Set matter commissioning passcode to ${CYAN}${passcode}${db}`);
1571
+ await this.matterbridge.nodeContext?.set('matterpasscode', passcode);
1592
1572
  this.wssSendRestartRequired();
1593
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1573
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1594
1574
  }
1595
1575
  else {
1596
1576
  this.log.debug(`Reset matter commissioning passcode to ${CYAN}undefined${db}`);
1597
1577
  this.matterbridge.matterbridgeInformation.matterPasscode = undefined;
1598
1578
  await this.matterbridge.nodeContext?.remove('matterpasscode');
1599
1579
  this.wssSendRestartRequired();
1600
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: false }));
1580
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Invalid value: reset matter commissioning passcode to default undefined' });
1601
1581
  }
1602
1582
  break;
1603
1583
  case 'setvirtualmode':
@@ -1607,22 +1587,23 @@ export class Frontend extends EventEmitter {
1607
1587
  await this.matterbridge.nodeContext?.set('virtualmode', data.params.value);
1608
1588
  this.wssSendRestartRequired();
1609
1589
  }
1610
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1590
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1611
1591
  break;
1612
1592
  default:
1613
1593
  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` }));
1594
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Unknown parameter ${data.params.name} in /api/config` });
1615
1595
  }
1616
1596
  }
1617
1597
  else if (data.method === '/api/command') {
1598
+ const localData = data;
1618
1599
  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' }));
1600
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter command in /api/command' });
1620
1601
  return;
1621
1602
  }
1622
1603
  if (data.params.command === 'selectdevice' && isValidString(data.params.plugin, 10) && isValidString(data.params.serial, 1) && isValidString(data.params.name, 1)) {
1623
1604
  const plugin = this.matterbridge.plugins.get(data.params.plugin);
1624
1605
  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' }));
1606
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/command' });
1626
1607
  return;
1627
1608
  }
1628
1609
  const config = plugin.configJson;
@@ -1645,31 +1626,28 @@ export class Frontend extends EventEmitter {
1645
1626
  }
1646
1627
  if (isValidArray(config.blackList, 1)) {
1647
1628
  if (select === 'serial' && config.blackList.includes(data.params.serial)) {
1648
- config.blackList = config.blackList.filter((item) => item !== data.params.serial);
1629
+ config.blackList = config.blackList.filter((item) => item !== localData.params.serial);
1649
1630
  }
1650
1631
  else if (select === 'name' && config.blackList.includes(data.params.name)) {
1651
- config.blackList = config.blackList.filter((item) => item !== data.params.name);
1632
+ config.blackList = config.blackList.filter((item) => item !== localData.params.name);
1652
1633
  }
1653
1634
  }
1654
1635
  if (plugin.platform)
1655
1636
  plugin.platform.config = config;
1656
1637
  plugin.configJson = config;
1657
- const restartRequired = plugin.restartRequired;
1658
1638
  await this.matterbridge.plugins.saveConfigFromPlugin(plugin, true);
1659
- if (!restartRequired)
1660
- this.wssSendRefreshRequired('pluginsRestart');
1661
1639
  this.wssSendRestartRequired(false);
1662
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1640
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1663
1641
  }
1664
1642
  else {
1665
1643
  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` }));
1644
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `SelectDevice: select ${select} not supported` });
1667
1645
  }
1668
1646
  }
1669
1647
  else if (data.params.command === 'unselectdevice' && isValidString(data.params.plugin, 10) && isValidString(data.params.serial, 1) && isValidString(data.params.name, 1)) {
1670
1648
  const plugin = this.matterbridge.plugins.get(data.params.plugin);
1671
1649
  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' }));
1650
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/command' });
1673
1651
  return;
1674
1652
  }
1675
1653
  const config = plugin.configJson;
@@ -1684,10 +1662,10 @@ export class Frontend extends EventEmitter {
1684
1662
  }
1685
1663
  if (isValidArray(config.whiteList, 1)) {
1686
1664
  if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
1687
- config.whiteList = config.whiteList.filter((item) => item !== data.params.serial);
1665
+ config.whiteList = config.whiteList.filter((item) => item !== localData.params.serial);
1688
1666
  }
1689
1667
  else if (select === 'name' && config.whiteList.includes(data.params.name)) {
1690
- config.whiteList = config.whiteList.filter((item) => item !== data.params.name);
1668
+ config.whiteList = config.whiteList.filter((item) => item !== localData.params.name);
1691
1669
  }
1692
1670
  }
1693
1671
  if (isValidArray(config.blackList)) {
@@ -1701,29 +1679,27 @@ export class Frontend extends EventEmitter {
1701
1679
  if (plugin.platform)
1702
1680
  plugin.platform.config = config;
1703
1681
  plugin.configJson = config;
1704
- const restartRequired = plugin.restartRequired;
1705
1682
  await this.matterbridge.plugins.saveConfigFromPlugin(plugin, true);
1706
- if (!restartRequired)
1707
- this.wssSendRefreshRequired('pluginsRestart');
1708
1683
  this.wssSendRestartRequired(false);
1709
- client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1684
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1710
1685
  }
1711
1686
  else {
1712
1687
  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` }));
1688
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `SelectDevice: select ${select} not supported` });
1714
1689
  }
1715
1690
  }
1716
1691
  }
1717
1692
  else {
1718
- 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' }));
1693
+ const localData = data;
1694
+ this.log.error(`Invalid method from websocket client: ${debugStringify(localData)}`);
1695
+ sendResponse({ id: localData.id, method: localData.method, src: 'Matterbridge', dst: localData.src, error: 'Invalid method' });
1720
1696
  }
1721
1697
  }
1722
1698
  catch (error) {
1723
1699
  inspectError(this.log, `Error processing message "${message}" from websocket client`, error);
1724
1700
  }
1725
1701
  }
1726
- wssSendMessage(level, time, name, message) {
1702
+ wssSendLogMessage(level, time, name, message) {
1727
1703
  if (!level || !time || !name || !message)
1728
1704
  return;
1729
1705
  message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
@@ -1744,19 +1720,11 @@ export class Frontend extends EventEmitter {
1744
1720
  return word;
1745
1721
  })
1746
1722
  .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
- });
1723
+ this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'log', params: { level, time, name, message } });
1752
1724
  }
1753
- wssSendRefreshRequired(changed = null, params = {}) {
1725
+ wssSendRefreshRequired(changed, params) {
1754
1726
  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
- });
1727
+ this.wssBroadcastMessage({ id: 1, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed, ...params } });
1760
1728
  }
1761
1729
  wssSendRestartRequired(snackbar = true, fixed = false) {
1762
1730
  this.log.debug('Sending a restart required message to all connected clients');
@@ -1764,88 +1732,54 @@ export class Frontend extends EventEmitter {
1764
1732
  this.matterbridge.matterbridgeInformation.fixedRestartRequired = fixed;
1765
1733
  if (snackbar === true)
1766
1734
  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
- });
1735
+ this.wssBroadcastMessage({ id: 2, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: { fixed } });
1772
1736
  }
1773
1737
  wssSendRestartNotRequired(snackbar = true) {
1774
1738
  this.log.debug('Sending a restart not required message to all connected clients');
1775
1739
  this.matterbridge.matterbridgeInformation.restartRequired = false;
1776
1740
  if (snackbar === true)
1777
1741
  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
- });
1742
+ this.wssBroadcastMessage({ id: 3, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required' });
1783
1743
  }
1784
1744
  wssSendUpdateRequired(devVersion = false) {
1785
1745
  this.log.debug('Sending an update required message to all connected clients');
1786
1746
  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
- });
1747
+ this.wssBroadcastMessage({ id: 8, src: 'Matterbridge', dst: 'Frontend', method: devVersion ? 'update_required_dev' : 'update_required' });
1792
1748
  }
1793
1749
  wssSendCpuUpdate(cpuUsage) {
1794
1750
  if (hasParameter('debug'))
1795
1751
  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
- });
1752
+ this.wssBroadcastMessage({ id: 4, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage: Math.round(cpuUsage * 100) / 100 } });
1801
1753
  }
1802
1754
  wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
1803
1755
  if (hasParameter('debug'))
1804
1756
  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
- });
1757
+ this.wssBroadcastMessage({ id: 5, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } });
1810
1758
  }
1811
1759
  wssSendUptimeUpdate(systemUptime, processUptime) {
1812
1760
  if (hasParameter('debug'))
1813
1761
  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
- });
1762
+ this.wssBroadcastMessage({ id: 6, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } });
1819
1763
  }
1820
1764
  wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
1821
1765
  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
- });
1766
+ this.wssBroadcastMessage({ id: 7, src: 'Matterbridge', dst: 'Frontend', method: 'snackbar', params: { message, timeout, severity } });
1827
1767
  }
1828
1768
  wssSendCloseSnackbarMessage(message) {
1829
1769
  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
- });
1770
+ this.wssBroadcastMessage({ id: 10, src: 'Matterbridge', dst: 'Frontend', method: 'close_snackbar', params: { message } });
1835
1771
  }
1836
1772
  wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
1837
1773
  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
- });
1774
+ this.wssBroadcastMessage({ id: 9, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } });
1843
1775
  }
1844
- wssBroadcastMessage(id, method, params) {
1845
- this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
1776
+ wssBroadcastMessage(msg) {
1777
+ const stringifiedMsg = JSON.stringify(msg);
1778
+ if (msg.id !== 0)
1779
+ this.log.debug(`Sending a broadcast message: ${debugStringify(msg)}`);
1846
1780
  this.webSocketServer?.clients.forEach((client) => {
1847
1781
  if (client.readyState === WebSocket.OPEN) {
1848
- client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
1782
+ client.send(stringifiedMsg);
1849
1783
  }
1850
1784
  });
1851
1785
  }