matterbridge 2.1.5 → 2.1.6-dev.1

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 (118) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README-DOCKER.md +17 -7
  3. package/dist/cli.js +0 -26
  4. package/dist/cluster/export.js +0 -2
  5. package/dist/defaultConfigSchema.js +0 -23
  6. package/dist/deviceManager.js +1 -94
  7. package/dist/frontend.js +55 -280
  8. package/dist/index.js +0 -28
  9. package/dist/logger/export.js +0 -1
  10. package/dist/matter/behaviors.js +0 -2
  11. package/dist/matter/clusters.js +0 -2
  12. package/dist/matter/devices.js +0 -2
  13. package/dist/matter/endpoints.js +0 -2
  14. package/dist/matter/export.js +0 -2
  15. package/dist/matter/types.js +0 -2
  16. package/dist/matterbridge.js +68 -765
  17. package/dist/matterbridgeAccessoryPlatform.js +0 -33
  18. package/dist/matterbridgeBehaviors.js +1 -32
  19. package/dist/matterbridgeDeviceTypes.js +11 -112
  20. package/dist/matterbridgeDynamicPlatform.js +0 -33
  21. package/dist/matterbridgeEndpoint.js +6 -690
  22. package/dist/matterbridgeEndpointHelpers.js +9 -117
  23. package/dist/matterbridgePlatform.js +41 -123
  24. package/dist/matterbridgeTypes.js +0 -24
  25. package/dist/pluginManager.js +3 -230
  26. package/dist/storage/export.js +0 -1
  27. package/dist/utils/colorUtils.js +2 -205
  28. package/dist/utils/export.js +0 -1
  29. package/dist/utils/utils.js +10 -264
  30. package/frontend/build/asset-manifest.json +3 -3
  31. package/frontend/build/index.html +1 -1
  32. package/frontend/build/static/js/{main.cd192588.js → main.a241d4f0.js} +9 -9
  33. package/frontend/build/static/js/main.a241d4f0.js.map +1 -0
  34. package/npm-shrinkwrap.json +47 -47
  35. package/package.json +2 -3
  36. package/dist/cli.d.ts +0 -25
  37. package/dist/cli.d.ts.map +0 -1
  38. package/dist/cli.js.map +0 -1
  39. package/dist/cluster/export.d.ts +0 -2
  40. package/dist/cluster/export.d.ts.map +0 -1
  41. package/dist/cluster/export.js.map +0 -1
  42. package/dist/defaultConfigSchema.d.ts +0 -27
  43. package/dist/defaultConfigSchema.d.ts.map +0 -1
  44. package/dist/defaultConfigSchema.js.map +0 -1
  45. package/dist/deviceManager.d.ts +0 -114
  46. package/dist/deviceManager.d.ts.map +0 -1
  47. package/dist/deviceManager.js.map +0 -1
  48. package/dist/frontend.d.ts +0 -143
  49. package/dist/frontend.d.ts.map +0 -1
  50. package/dist/frontend.js.map +0 -1
  51. package/dist/index.d.ts +0 -35
  52. package/dist/index.d.ts.map +0 -1
  53. package/dist/index.js.map +0 -1
  54. package/dist/logger/export.d.ts +0 -2
  55. package/dist/logger/export.d.ts.map +0 -1
  56. package/dist/logger/export.js.map +0 -1
  57. package/dist/matter/behaviors.d.ts +0 -2
  58. package/dist/matter/behaviors.d.ts.map +0 -1
  59. package/dist/matter/behaviors.js.map +0 -1
  60. package/dist/matter/clusters.d.ts +0 -2
  61. package/dist/matter/clusters.d.ts.map +0 -1
  62. package/dist/matter/clusters.js.map +0 -1
  63. package/dist/matter/devices.d.ts +0 -2
  64. package/dist/matter/devices.d.ts.map +0 -1
  65. package/dist/matter/devices.js.map +0 -1
  66. package/dist/matter/endpoints.d.ts +0 -2
  67. package/dist/matter/endpoints.d.ts.map +0 -1
  68. package/dist/matter/endpoints.js.map +0 -1
  69. package/dist/matter/export.d.ts +0 -5
  70. package/dist/matter/export.d.ts.map +0 -1
  71. package/dist/matter/export.js.map +0 -1
  72. package/dist/matter/types.d.ts +0 -3
  73. package/dist/matter/types.d.ts.map +0 -1
  74. package/dist/matter/types.js.map +0 -1
  75. package/dist/matterbridge.d.ts +0 -409
  76. package/dist/matterbridge.d.ts.map +0 -1
  77. package/dist/matterbridge.js.map +0 -1
  78. package/dist/matterbridgeAccessoryPlatform.d.ts +0 -39
  79. package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
  80. package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
  81. package/dist/matterbridgeBehaviors.d.ts +0 -1056
  82. package/dist/matterbridgeBehaviors.d.ts.map +0 -1
  83. package/dist/matterbridgeBehaviors.js.map +0 -1
  84. package/dist/matterbridgeDeviceTypes.d.ts +0 -177
  85. package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
  86. package/dist/matterbridgeDeviceTypes.js.map +0 -1
  87. package/dist/matterbridgeDynamicPlatform.d.ts +0 -39
  88. package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
  89. package/dist/matterbridgeDynamicPlatform.js.map +0 -1
  90. package/dist/matterbridgeEndpoint.d.ts +0 -835
  91. package/dist/matterbridgeEndpoint.d.ts.map +0 -1
  92. package/dist/matterbridgeEndpoint.js.map +0 -1
  93. package/dist/matterbridgeEndpointHelpers.d.ts +0 -2275
  94. package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
  95. package/dist/matterbridgeEndpointHelpers.js.map +0 -1
  96. package/dist/matterbridgePlatform.d.ts +0 -159
  97. package/dist/matterbridgePlatform.d.ts.map +0 -1
  98. package/dist/matterbridgePlatform.js.map +0 -1
  99. package/dist/matterbridgeTypes.d.ts +0 -169
  100. package/dist/matterbridgeTypes.d.ts.map +0 -1
  101. package/dist/matterbridgeTypes.js.map +0 -1
  102. package/dist/pluginManager.d.ts +0 -236
  103. package/dist/pluginManager.d.ts.map +0 -1
  104. package/dist/pluginManager.js.map +0 -1
  105. package/dist/storage/export.d.ts +0 -2
  106. package/dist/storage/export.d.ts.map +0 -1
  107. package/dist/storage/export.js.map +0 -1
  108. package/dist/utils/colorUtils.d.ts +0 -61
  109. package/dist/utils/colorUtils.d.ts.map +0 -1
  110. package/dist/utils/colorUtils.js.map +0 -1
  111. package/dist/utils/export.d.ts +0 -3
  112. package/dist/utils/export.d.ts.map +0 -1
  113. package/dist/utils/export.js.map +0 -1
  114. package/dist/utils/utils.d.ts +0 -231
  115. package/dist/utils/utils.d.ts.map +0 -1
  116. package/dist/utils/utils.js.map +0 -1
  117. package/frontend/build/static/js/main.cd192588.js.map +0 -1
  118. /package/frontend/build/static/js/{main.cd192588.js.LICENSE.txt → main.a241d4f0.js.LICENSE.txt} +0 -0
package/dist/frontend.js CHANGED
@@ -1,28 +1,4 @@
1
- /**
2
- * This file contains the class Frontend.
3
- *
4
- * @file frontend.ts
5
- * @author Luca Liguori
6
- * @date 2025-01-13
7
- * @version 1.0.2
8
- *
9
- * Copyright 2025, 2026, 2027 Luca Liguori.
10
- *
11
- * Licensed under the Apache License, Version 2.0 (the "License");
12
- * you may not use this file except in compliance with the License.
13
- * You may obtain a copy of the License at
14
- *
15
- * http://www.apache.org/licenses/LICENSE-2.0
16
- *
17
- * Unless required by applicable law or agreed to in writing, software
18
- * distributed under the License is distributed on an "AS IS" BASIS,
19
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
- * See the License for the specific language governing permissions and
21
- * limitations under the License. *
22
- */
23
- // @matter
24
1
  import { EndpointServer, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat } from '@matter/main';
25
- // Node modules
26
2
  import { createServer } from 'http';
27
3
  import https from 'https';
28
4
  import express from 'express';
@@ -30,47 +6,15 @@ import WebSocket, { WebSocketServer } from 'ws';
30
6
  import os from 'os';
31
7
  import path from 'path';
32
8
  import { promises as fs } from 'fs';
33
- // AnsiLogger module
34
9
  import { AnsiLogger, CYAN, db, debugStringify, er, nf, rs, stringify, UNDERLINE, UNDERLINEOFF, wr, YELLOW } from './logger/export.js';
35
- // Matterbridge
36
10
  import { createZip, deepCopy, getIntParameter, hasParameter, isValidNumber, isValidObject, isValidString } from './utils/utils.js';
37
11
  import { plg } from './matterbridgeTypes.js';
38
- import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
39
- /**
40
- * Websocket message ID for logging.
41
- * @constant {number}
42
- */
43
12
  export const WS_ID_LOG = 0;
44
- /**
45
- * Websocket message ID indicating a refresh is needed.
46
- * @constant {number}
47
- */
48
13
  export const WS_ID_REFRESH_NEEDED = 1;
49
- /**
50
- * Websocket message ID indicating a restart is needed.
51
- * @constant {number}
52
- */
53
14
  export const WS_ID_RESTART_NEEDED = 2;
54
- /**
55
- * Websocket message ID indicating a cpu update.
56
- * @constant {number}
57
- */
58
15
  export const WS_ID_CPU_UPDATE = 3;
59
- /**
60
- * Websocket message ID indicating a memory update.
61
- * @constant {number}
62
- */
63
16
  export const WS_ID_MEMORY_UPDATE = 4;
64
- /**
65
- * Websocket message ID indicating a memory update.
66
- * @constant {number}
67
- */
68
17
  export const WS_ID_SNACKBAR = 5;
69
- /**
70
- * Initializes the frontend of Matterbridge.
71
- *
72
- * @param port The port number to run the frontend server on. Default is 8283.
73
- */
74
18
  export class Frontend {
75
19
  matterbridge;
76
20
  log;
@@ -87,7 +31,7 @@ export class Frontend {
87
31
  memoryTimeout;
88
32
  constructor(matterbridge) {
89
33
  this.matterbridge = matterbridge;
90
- this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
34
+ this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
91
35
  }
92
36
  set logLevel(logLevel) {
93
37
  this.log.logLevel = logLevel;
@@ -95,21 +39,10 @@ export class Frontend {
95
39
  async start(port = 8283) {
96
40
  this.port = port;
97
41
  this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
98
- // Create the express app that serves the frontend
99
42
  this.expressApp = express();
100
- // Log all requests to the server for debugging
101
- /*
102
- this.expressApp.use((req, res, next) => {
103
- this.log.debug(`Received request on expressApp: ${req.method} ${req.url}`);
104
- next();
105
- });
106
- */
107
- // Serve static files from '/static' endpoint
108
43
  this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
109
44
  if (!hasParameter('ssl')) {
110
- // Create an HTTP server and attach the express app
111
45
  this.httpServer = createServer(this.expressApp);
112
- // Listen on the specified port
113
46
  if (hasParameter('ingress')) {
114
47
  this.httpServer.listen(this.port, '0.0.0.0', () => {
115
48
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -123,7 +56,6 @@ export class Frontend {
123
56
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
124
57
  });
125
58
  }
126
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
127
59
  this.httpServer.on('error', (error) => {
128
60
  this.log.error(`Frontend http server error listening on ${this.port}`);
129
61
  switch (error.code) {
@@ -139,7 +71,6 @@ export class Frontend {
139
71
  });
140
72
  }
141
73
  else {
142
- // Load the SSL certificate, the private key and optionally the CA certificate
143
74
  let cert;
144
75
  try {
145
76
  cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
@@ -167,9 +98,7 @@ export class Frontend {
167
98
  this.log.info(`CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
168
99
  }
169
100
  const serverOptions = { cert, key, ca };
170
- // Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
171
101
  this.httpsServer = https.createServer(serverOptions, this.expressApp);
172
- // Listen on the specified port
173
102
  if (hasParameter('ingress')) {
174
103
  this.httpsServer.listen(this.port, '0.0.0.0', () => {
175
104
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -183,7 +112,6 @@ export class Frontend {
183
112
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
184
113
  });
185
114
  }
186
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
187
115
  this.httpsServer.on('error', (error) => {
188
116
  this.log.error(`Frontend https server error listening on ${this.port}`);
189
117
  switch (error.code) {
@@ -200,18 +128,16 @@ export class Frontend {
200
128
  }
201
129
  if (this.initializeError)
202
130
  return;
203
- // Create a WebSocket server and attach it to the http or https server
204
131
  const wssPort = this.port;
205
132
  const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
206
133
  this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
207
134
  this.webSocketServer.on('connection', (ws, request) => {
208
135
  const clientIp = request.socket.remoteAddress;
209
- // Set the global logger callback for the WebSocketServer
210
- let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
211
- if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" /* LogLevel.INFO */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
212
- callbackLogLevel = "info" /* LogLevel.INFO */;
213
- if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" /* LogLevel.DEBUG */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
214
- callbackLogLevel = "debug" /* LogLevel.DEBUG */;
136
+ let callbackLogLevel = "notice";
137
+ if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
138
+ callbackLogLevel = "info";
139
+ if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
140
+ callbackLogLevel = "debug";
215
141
  AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), callbackLogLevel);
216
142
  this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
217
143
  this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
@@ -245,11 +171,9 @@ export class Frontend {
245
171
  this.webSocketServer.on('error', (ws, error) => {
246
172
  this.log.error(`WebSocketServer error: ${error}`);
247
173
  });
248
- // Start the memory dump interval
249
174
  if (hasParameter('memorydump')) {
250
175
  this.startCpuMemoryDump();
251
176
  }
252
- // Endpoint to validate login code
253
177
  this.expressApp.post('/api/login', express.json(), async (req, res) => {
254
178
  const { password } = req.body;
255
179
  this.log.debug('The frontend sent /api/login', password);
@@ -268,27 +192,23 @@ export class Frontend {
268
192
  this.log.warn('/api/login error wrong password');
269
193
  res.json({ valid: false });
270
194
  }
271
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
272
195
  }
273
196
  catch (error) {
274
197
  this.log.error('/api/login error getting password');
275
198
  res.json({ valid: false });
276
199
  }
277
200
  });
278
- // Endpoint to provide health check
279
201
  this.expressApp.get('/health', (req, res) => {
280
202
  this.log.debug('Express received /health');
281
203
  const healthStatus = {
282
- status: 'ok', // Indicate service is healthy
283
- uptime: process.uptime(), // Server uptime in seconds
284
- timestamp: new Date().toISOString(), // Current timestamp
204
+ status: 'ok',
205
+ uptime: process.uptime(),
206
+ timestamp: new Date().toISOString(),
285
207
  };
286
208
  res.status(200).json(healthStatus);
287
209
  });
288
- // Endpoint to provide memory usage details
289
210
  this.expressApp.get('/memory', async (req, res) => {
290
211
  this.log.debug('Express received /memory');
291
- // Memory usage from process
292
212
  const memoryUsageRaw = process.memoryUsage();
293
213
  const memoryUsage = {
294
214
  rss: this.formatMemoryUsage(memoryUsageRaw.rss),
@@ -297,13 +217,10 @@ export class Frontend {
297
217
  external: this.formatMemoryUsage(memoryUsageRaw.external),
298
218
  arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
299
219
  };
300
- // V8 heap statistics
301
220
  const { default: v8 } = await import('node:v8');
302
221
  const heapStatsRaw = v8.getHeapStatistics();
303
222
  const heapSpacesRaw = v8.getHeapSpaceStatistics();
304
- // Format heapStats
305
223
  const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
306
- // Format heapSpaces
307
224
  const heapSpaces = heapSpacesRaw.map((space) => ({
308
225
  ...space,
309
226
  space_size: this.formatMemoryUsage(space.space_size),
@@ -313,23 +230,6 @@ export class Frontend {
313
230
  }));
314
231
  const { default: module } = await import('module');
315
232
  const loadedModules = module._cache ? Object.keys(module._cache).sort() : [];
316
- /*
317
- if (req.query.heapdump === 'true') {
318
- const { default: heapdump } = await import('heapdump');
319
- const filename = `heapdump-${Date.now()}.heapsnapshot`;
320
-
321
- heapdump.writeSnapshot(filename, (err, dumpFilename) => {
322
- if (err) {
323
- this.log.error(`Heap dump error: ${err.message}`);
324
- return res.status(500).json({ error: 'Heap dump failed', details: err.message });
325
- }
326
-
327
- this.log.info(`Heap dump written to ${dumpFilename}`);
328
- return res.status(200).json({ ...memoryReport, heapdump: dumpFilename });
329
- });
330
- return; // Exit early since heapdump response is handled inside callback
331
- }
332
- */
333
233
  const memoryReport = {
334
234
  memoryUsage,
335
235
  heapStats,
@@ -338,7 +238,6 @@ export class Frontend {
338
238
  };
339
239
  res.status(200).json(memoryReport);
340
240
  });
341
- // Endpoint to start advertising the server node
342
241
  this.expressApp.get('/api/advertise', express.json(), async (req, res) => {
343
242
  const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
344
243
  if (pairingCodes) {
@@ -349,24 +248,18 @@ export class Frontend {
349
248
  res.status(500).json({ error: 'Failed to generate pairing codes' });
350
249
  }
351
250
  });
352
- // Endpoint to provide settings
353
251
  this.expressApp.get('/api/settings', express.json(), async (req, res) => {
354
252
  this.log.debug('The frontend sent /api/settings');
355
253
  res.json(await this.getApiSettings());
356
254
  });
357
- // Endpoint to provide plugins
358
255
  this.expressApp.get('/api/plugins', async (req, res) => {
359
256
  this.log.debug('The frontend sent /api/plugins');
360
- const response = this.getBaseRegisteredPlugins();
361
- // this.log.debug('Response:', debugStringify(response));
362
- res.json(response);
257
+ res.json(this.getBaseRegisteredPlugins());
363
258
  });
364
- // Endpoint to provide devices
365
259
  this.expressApp.get('/api/devices', (req, res) => {
366
260
  this.log.debug('The frontend sent /api/devices');
367
261
  const devices = [];
368
262
  this.matterbridge.devices.forEach(async (device) => {
369
- // Check if the device has the required properties
370
263
  if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId)
371
264
  return;
372
265
  const cluster = this.getClusterTextFromDevice(device);
@@ -382,10 +275,8 @@ export class Frontend {
382
275
  cluster: cluster,
383
276
  });
384
277
  });
385
- // this.log.debug('Response:', debugStringify(data));
386
278
  res.json(devices);
387
279
  });
388
- // Endpoint to provide the cluster servers of the devices
389
280
  this.expressApp.get('/api/devices_clusters/:selectedPluginName/:selectedDeviceEndpoint', (req, res) => {
390
281
  const selectedPluginName = req.params.selectedPluginName;
391
282
  const selectedDeviceEndpoint = parseInt(req.params.selectedDeviceEndpoint, 10);
@@ -458,7 +349,6 @@ export class Frontend {
458
349
  });
459
350
  res.json(data);
460
351
  });
461
- // Endpoint to view the log
462
352
  this.expressApp.get('/api/view-log', async (req, res) => {
463
353
  this.log.debug('The frontend sent /api/log');
464
354
  try {
@@ -471,12 +361,10 @@ export class Frontend {
471
361
  res.status(500).send('Error reading log file');
472
362
  }
473
363
  });
474
- // Endpoint to download the matterbridge log
475
364
  this.expressApp.get('/api/download-mblog', async (req, res) => {
476
365
  this.log.debug('The frontend sent /api/download-mblog');
477
366
  try {
478
367
  await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), fs.constants.F_OK);
479
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
480
368
  }
481
369
  catch (error) {
482
370
  fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'Enable the log on file in the settings to enable the file logger');
@@ -488,12 +376,10 @@ export class Frontend {
488
376
  }
489
377
  });
490
378
  });
491
- // Endpoint to download the matter log
492
379
  this.expressApp.get('/api/download-mjlog', async (req, res) => {
493
380
  this.log.debug('The frontend sent /api/download-mjlog');
494
381
  try {
495
382
  await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), fs.constants.F_OK);
496
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
497
383
  }
498
384
  catch (error) {
499
385
  fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), 'Enable the log on file in the settings to enable the file logger');
@@ -505,7 +391,6 @@ export class Frontend {
505
391
  }
506
392
  });
507
393
  });
508
- // Endpoint to download the matter storage file
509
394
  this.expressApp.get('/api/download-mjstorage', async (req, res) => {
510
395
  this.log.debug('The frontend sent /api/download-mjstorage');
511
396
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
@@ -516,7 +401,6 @@ export class Frontend {
516
401
  }
517
402
  });
518
403
  });
519
- // Endpoint to download the matterbridge storage directory
520
404
  this.expressApp.get('/api/download-mbstorage', async (req, res) => {
521
405
  this.log.debug('The frontend sent /api/download-mbstorage');
522
406
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
@@ -527,7 +411,6 @@ export class Frontend {
527
411
  }
528
412
  });
529
413
  });
530
- // Endpoint to download the matterbridge plugin directory
531
414
  this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
532
415
  this.log.debug('The frontend sent /api/download-pluginstorage');
533
416
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
@@ -538,11 +421,9 @@ export class Frontend {
538
421
  }
539
422
  });
540
423
  });
541
- // Endpoint to download the matterbridge plugin config files
542
424
  this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
543
425
  this.log.debug('The frontend sent /api/download-pluginconfig');
544
426
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
545
- // await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, 'certs', '*.*')), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
546
427
  res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
547
428
  if (error) {
548
429
  this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
@@ -550,7 +431,6 @@ export class Frontend {
550
431
  }
551
432
  });
552
433
  });
553
- // Endpoint to download the matterbridge plugin config files
554
434
  this.expressApp.get('/api/download-backup', async (req, res) => {
555
435
  this.log.debug('The frontend sent /api/download-backup');
556
436
  res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
@@ -560,7 +440,6 @@ export class Frontend {
560
440
  }
561
441
  });
562
442
  });
563
- // Endpoint to receive commands
564
443
  this.expressApp.post('/api/command/:command/:param', express.json(), async (req, res) => {
565
444
  const command = req.params.command;
566
445
  let param = req.params.param;
@@ -570,15 +449,13 @@ export class Frontend {
570
449
  return;
571
450
  }
572
451
  this.log.debug(`Received frontend command: ${command}:${param}`);
573
- // Handle the command setpassword from Settings
574
452
  if (command === 'setpassword') {
575
- const password = param.slice(1, -1); // Remove the first and last characters
453
+ const password = param.slice(1, -1);
576
454
  this.log.debug('setpassword', param, password);
577
455
  await this.matterbridge.nodeContext?.set('password', password);
578
456
  res.json({ message: 'Command received' });
579
457
  return;
580
458
  }
581
- // Handle the command setbridgemode from Settings
582
459
  if (command === 'setbridgemode') {
583
460
  this.log.debug(`setbridgemode: ${param}`);
584
461
  this.wssSendRestartRequired();
@@ -586,7 +463,6 @@ export class Frontend {
586
463
  res.json({ message: 'Command received' });
587
464
  return;
588
465
  }
589
- // Handle the command backup from Settings
590
466
  if (command === 'backup') {
591
467
  this.log.notice(`Prepairing the backup...`);
592
468
  await createZip(path.join(os.tmpdir(), `matterbridge.backup.zip`), path.join(this.matterbridge.matterbridgeDirectory), path.join(this.matterbridge.matterbridgePluginDirectory));
@@ -595,42 +471,31 @@ export class Frontend {
595
471
  res.json({ message: 'Command received' });
596
472
  return;
597
473
  }
598
- // Handle the command setmbloglevel from Settings
599
474
  if (command === 'setmbloglevel') {
600
475
  this.log.debug('Matterbridge log level:', param);
601
476
  if (param === 'Debug') {
602
- this.log.logLevel = "debug" /* LogLevel.DEBUG */;
477
+ this.log.logLevel = "debug";
603
478
  }
604
479
  else if (param === 'Info') {
605
- this.log.logLevel = "info" /* LogLevel.INFO */;
480
+ this.log.logLevel = "info";
606
481
  }
607
482
  else if (param === 'Notice') {
608
- this.log.logLevel = "notice" /* LogLevel.NOTICE */;
483
+ this.log.logLevel = "notice";
609
484
  }
610
485
  else if (param === 'Warn') {
611
- this.log.logLevel = "warn" /* LogLevel.WARN */;
486
+ this.log.logLevel = "warn";
612
487
  }
613
488
  else if (param === 'Error') {
614
- this.log.logLevel = "error" /* LogLevel.ERROR */;
489
+ this.log.logLevel = "error";
615
490
  }
616
491
  else if (param === 'Fatal') {
617
- this.log.logLevel = "fatal" /* LogLevel.FATAL */;
492
+ this.log.logLevel = "fatal";
618
493
  }
619
494
  await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
620
- this.matterbridge.log.logLevel = this.log.logLevel;
621
- MatterbridgeEndpoint.logLevel = this.log.logLevel;
622
- this.matterbridge.devices.logLevel = this.log.logLevel;
623
- this.matterbridge.plugins.logLevel = this.log.logLevel;
624
- for (const plugin of this.matterbridge.plugins) {
625
- if (!plugin.platform || !plugin.platform.config)
626
- continue;
627
- plugin.platform.log.logLevel = plugin.platform.config.debug ? "debug" /* LogLevel.DEBUG */ : this.log.logLevel;
628
- await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug ? "debug" /* LogLevel.DEBUG */ : this.log.logLevel);
629
- }
495
+ await this.matterbridge.setLogLevel(this.log.logLevel);
630
496
  res.json({ message: 'Command received' });
631
497
  return;
632
498
  }
633
- // Handle the command setmbloglevel from Settings
634
499
  if (command === 'setmjloglevel') {
635
500
  this.log.debug('Matter.js log level:', param);
636
501
  if (param === 'Debug') {
@@ -655,34 +520,30 @@ export class Frontend {
655
520
  res.json({ message: 'Command received' });
656
521
  return;
657
522
  }
658
- // Handle the command setmdnsinterface from Settings
659
523
  if (command === 'setmdnsinterface') {
660
- param = param.slice(1, -1); // Remove the first and last characters *mdns*
524
+ param = param.slice(1, -1);
661
525
  this.matterbridge.matterbridgeInformation.mattermdnsinterface = param;
662
526
  this.log.debug('Matter.js mdns interface:', param === '' ? 'All interfaces' : param);
663
527
  await this.matterbridge.nodeContext?.set('mattermdnsinterface', param);
664
528
  res.json({ message: 'Command received' });
665
529
  return;
666
530
  }
667
- // Handle the command setipv4address from Settings
668
531
  if (command === 'setipv4address') {
669
- param = param.slice(1, -1); // Remove the first and last characters *ip*
532
+ param = param.slice(1, -1);
670
533
  this.matterbridge.matterbridgeInformation.matteripv4address = param;
671
534
  this.log.debug('Matter.js ipv4 address:', param === '' ? 'All ipv4 addresses' : param);
672
535
  await this.matterbridge.nodeContext?.set('matteripv4address', param);
673
536
  res.json({ message: 'Command received' });
674
537
  return;
675
538
  }
676
- // Handle the command setipv6address from Settings
677
539
  if (command === 'setipv6address') {
678
- param = param.slice(1, -1); // Remove the first and last characters *ip*
540
+ param = param.slice(1, -1);
679
541
  this.matterbridge.matterbridgeInformation.matteripv6address = param;
680
542
  this.log.debug('Matter.js ipv6 address:', param === '' ? 'All ipv6 addresses' : param);
681
543
  await this.matterbridge.nodeContext?.set('matteripv6address', param);
682
544
  res.json({ message: 'Command received' });
683
545
  return;
684
546
  }
685
- // Handle the command setmatterport from Settings
686
547
  if (command === 'setmatterport') {
687
548
  const port = Math.min(Math.max(parseInt(param), 5540), 5560);
688
549
  this.matterbridge.matterbridgeInformation.matterPort = port;
@@ -691,7 +552,6 @@ export class Frontend {
691
552
  res.json({ message: 'Command received' });
692
553
  return;
693
554
  }
694
- // Handle the command setmatterdiscriminator from Settings
695
555
  if (command === 'setmatterdiscriminator') {
696
556
  const discriminator = Math.min(Math.max(parseInt(param), 1000), 4095);
697
557
  this.matterbridge.matterbridgeInformation.matterDiscriminator = discriminator;
@@ -700,7 +560,6 @@ export class Frontend {
700
560
  res.json({ message: 'Command received' });
701
561
  return;
702
562
  }
703
- // Handle the command setmatterpasscode from Settings
704
563
  if (command === 'setmatterpasscode') {
705
564
  const passcode = Math.min(Math.max(parseInt(param), 10000000), 90000000);
706
565
  this.matterbridge.matterbridgeInformation.matterPasscode = passcode;
@@ -709,20 +568,17 @@ export class Frontend {
709
568
  res.json({ message: 'Command received' });
710
569
  return;
711
570
  }
712
- // Handle the command setmbloglevel from Settings
713
571
  if (command === 'setmblogfile') {
714
572
  this.log.debug('Matterbridge file log:', param);
715
573
  this.matterbridge.matterbridgeInformation.fileLogger = param === 'true';
716
574
  await this.matterbridge.nodeContext?.set('matterbridgeFileLog', param === 'true');
717
- // Create the file logger for matterbridge
718
575
  if (param === 'true')
719
- AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug" /* LogLevel.DEBUG */, true);
576
+ AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug", true);
720
577
  else
721
578
  AnsiLogger.setGlobalLogfile(undefined);
722
579
  res.json({ message: 'Command received' });
723
580
  return;
724
581
  }
725
- // Handle the command setmbloglevel from Settings
726
582
  if (command === 'setmjlogfile') {
727
583
  this.log.debug('Matter file log:', param);
728
584
  this.matterbridge.matterbridgeInformation.matterFileLogger = param === 'true';
@@ -749,48 +605,40 @@ export class Frontend {
749
605
  res.json({ message: 'Command received' });
750
606
  return;
751
607
  }
752
- // Handle the command unregister from Settings
753
608
  if (command === 'unregister') {
754
609
  await this.matterbridge.unregisterAndShutdownProcess();
755
610
  res.json({ message: 'Command received' });
756
611
  return;
757
612
  }
758
- // Handle the command reset from Settings
759
613
  if (command === 'reset') {
760
614
  await this.matterbridge.shutdownProcessAndReset();
761
615
  res.json({ message: 'Command received' });
762
616
  return;
763
617
  }
764
- // Handle the command factoryreset from Settings
765
618
  if (command === 'factoryreset') {
766
619
  await this.matterbridge.shutdownProcessAndFactoryReset();
767
620
  res.json({ message: 'Command received' });
768
621
  return;
769
622
  }
770
- // Handle the command shutdown from Header
771
623
  if (command === 'shutdown') {
772
624
  await this.matterbridge.shutdownProcess();
773
625
  res.json({ message: 'Command received' });
774
626
  return;
775
627
  }
776
- // Handle the command restart from Header
777
628
  if (command === 'restart') {
778
629
  await this.matterbridge.restartProcess();
779
630
  res.json({ message: 'Command received' });
780
631
  return;
781
632
  }
782
- // Handle the command update from Header
783
633
  if (command === 'update') {
784
634
  await this.matterbridge.updateProcess();
785
635
  this.wssSendRestartRequired();
786
636
  res.json({ message: 'Command received' });
787
637
  return;
788
638
  }
789
- // Handle the command saveconfig from Home
790
639
  if (command === 'saveconfig') {
791
640
  param = param.replace(/\*/g, '\\');
792
641
  this.log.info(`Saving config for plugin ${plg}${param}${nf}...`);
793
- // console.log('Req.body:', JSON.stringify(req.body, null, 2));
794
642
  if (!this.matterbridge.plugins.has(param)) {
795
643
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
796
644
  }
@@ -804,58 +652,54 @@ export class Frontend {
804
652
  res.json({ message: 'Command received' });
805
653
  return;
806
654
  }
807
- // Handle the command installplugin from Home
808
655
  if (command === 'installplugin') {
809
656
  param = param.replace(/\*/g, '\\');
810
657
  this.log.info(`Installing plugin ${plg}${param}${nf}...`);
658
+ this.wssSendSnackbarMessage(`Installing package ${param}`);
811
659
  try {
812
660
  await this.matterbridge.spawnCommand('npm', ['install', '-g', param, '--omit=dev', '--verbose']);
813
661
  this.log.info(`Plugin ${plg}${param}${nf} installed. Full restart required.`);
814
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
662
+ this.wssSendSnackbarMessage(`Installed package ${param}`);
815
663
  }
816
664
  catch (error) {
817
665
  this.log.error(`Error installing plugin ${plg}${param}${er}`);
666
+ this.wssSendSnackbarMessage(`Package ${param} not installed`);
818
667
  }
668
+ this.wssSendSnackbarMessage(`Restart required`, 0);
819
669
  this.wssSendRestartRequired();
820
670
  param = param.split('@')[0];
821
- // Also add the plugin to matterbridge so no return!
822
671
  if (param === 'matterbridge') {
823
- // If we used the command installplugin to install a dev or a specific version of matterbridge we don't want to add it to matterbridge
824
672
  res.json({ message: 'Command received' });
825
673
  return;
826
674
  }
827
675
  }
828
- // Handle the command addplugin from Home
829
676
  if (command === 'addplugin' || command === 'installplugin') {
830
677
  param = param.replace(/\*/g, '\\');
831
678
  const plugin = await this.matterbridge.plugins.add(param);
832
679
  if (plugin) {
833
680
  if (this.matterbridge.bridgeMode === 'childbridge') {
834
- // We don't know now if the plugin is a dynamic platform or an accessory platform so we create the server node and the aggregator node
835
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
836
681
  this.matterbridge.createDynamicPlugin(plugin, true);
837
682
  }
838
- this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true); // No await do it in the background
683
+ this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true).then(() => {
684
+ this.wssSendRefreshRequired();
685
+ });
839
686
  }
840
687
  res.json({ message: 'Command received' });
841
- this.wssSendRefreshRequired();
842
688
  return;
843
689
  }
844
- // Handle the command removeplugin from Home
845
690
  if (command === 'removeplugin') {
846
691
  if (!this.matterbridge.plugins.has(param)) {
847
692
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
848
693
  }
849
694
  else {
850
695
  const plugin = this.matterbridge.plugins.get(param);
851
- await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true); // This will also close the server node in childbridge mode
696
+ await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
852
697
  await this.matterbridge.plugins.remove(param);
853
698
  }
854
699
  res.json({ message: 'Command received' });
855
700
  this.wssSendRefreshRequired();
856
701
  return;
857
702
  }
858
- // Handle the command enableplugin from Home
859
703
  if (command === 'enableplugin') {
860
704
  if (!this.matterbridge.plugins.has(param)) {
861
705
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
@@ -873,17 +717,17 @@ export class Frontend {
873
717
  plugin.addedDevices = undefined;
874
718
  await this.matterbridge.plugins.enable(param);
875
719
  if (this.matterbridge.bridgeMode === 'childbridge' && plugin.type === 'DynamicPlatform') {
876
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
877
720
  this.matterbridge.createDynamicPlugin(plugin, true);
878
721
  }
879
- this.matterbridge.plugins.load(plugin, true, 'The plugin has been enabled', true); // No await do it in the background since the server node is already started
722
+ this.matterbridge.plugins.load(plugin, true, 'The plugin has been enabled', true).then(() => {
723
+ this.wssSendRefreshRequired();
724
+ });
880
725
  }
881
726
  }
882
727
  res.json({ message: 'Command received' });
883
728
  this.wssSendRefreshRequired();
884
729
  return;
885
730
  }
886
- // Handle the command disableplugin from Home
887
731
  if (command === 'disableplugin') {
888
732
  if (!this.matterbridge.plugins.has(param)) {
889
733
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
@@ -891,7 +735,7 @@ export class Frontend {
891
735
  else {
892
736
  const plugin = this.matterbridge.plugins.get(param);
893
737
  if (plugin && plugin.enabled) {
894
- await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true); // This will also close the server node in childbridge mode
738
+ await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true);
895
739
  await this.matterbridge.plugins.disable(param);
896
740
  }
897
741
  }
@@ -900,7 +744,6 @@ export class Frontend {
900
744
  return;
901
745
  }
902
746
  });
903
- // Fallback for routing (must be the last route)
904
747
  this.expressApp.get('*', (req, res) => {
905
748
  this.log.debug('The frontend sent:', req.url);
906
749
  this.log.debug('Response send file:', path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
@@ -909,7 +752,6 @@ export class Frontend {
909
752
  this.log.debug(`Frontend initialized on port ${YELLOW}${this.port}${db} static ${UNDERLINE}${path.join(this.matterbridge.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
910
753
  }
911
754
  async stop() {
912
- // Start the memory check. This will not allow the process to exit but will log the memory usage for 5 minutes.
913
755
  if (hasParameter('memorycheck')) {
914
756
  this.wssSendSnackbarMessage('Memory check started', getIntParameter('memorycheck') ?? 5 * 60 * 1000);
915
757
  await new Promise((resolve) => {
@@ -921,29 +763,24 @@ export class Frontend {
921
763
  }, getIntParameter('memorycheck') ?? 5 * 60 * 1000);
922
764
  });
923
765
  }
924
- // Close the http server
925
766
  if (this.httpServer) {
926
767
  this.httpServer.close();
927
768
  this.httpServer.removeAllListeners();
928
769
  this.httpServer = undefined;
929
770
  this.log.debug('Frontend http server closed successfully');
930
771
  }
931
- // Close the https server
932
772
  if (this.httpsServer) {
933
773
  this.httpsServer.close();
934
774
  this.httpsServer.removeAllListeners();
935
775
  this.httpsServer = undefined;
936
776
  this.log.debug('Frontend https server closed successfully');
937
777
  }
938
- // Remove listeners from the express app
939
778
  if (this.expressApp) {
940
779
  this.expressApp.removeAllListeners();
941
780
  this.expressApp = undefined;
942
781
  this.log.debug('Frontend app closed successfully');
943
782
  }
944
- // Close the WebSocket server
945
783
  if (this.webSocketServer) {
946
- // Close all active connections
947
784
  this.webSocketServer.clients.forEach((client) => {
948
785
  if (client.readyState === WebSocket.OPEN) {
949
786
  client.close();
@@ -959,12 +796,10 @@ export class Frontend {
959
796
  });
960
797
  this.webSocketServer = undefined;
961
798
  }
962
- // Stop the memory dump interval
963
799
  if (hasParameter('memorydump')) {
964
800
  this.stopCpuMemoryDump();
965
801
  }
966
802
  }
967
- // Function to format bytes to KB, MB, or GB
968
803
  formatMemoryUsage = (bytes) => {
969
804
  if (bytes >= 1024 ** 3) {
970
805
  return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
@@ -976,7 +811,6 @@ export class Frontend {
976
811
  return `${(bytes / 1024).toFixed(2)} KB`;
977
812
  }
978
813
  };
979
- // Function to format system uptime with only the most significant unit
980
814
  formatOsUpTime = () => {
981
815
  const seconds = os.uptime();
982
816
  if (seconds >= 86400) {
@@ -996,12 +830,11 @@ export class Frontend {
996
830
  getCpuUsage = () => {
997
831
  const currCpus = os.cpus();
998
832
  if (currCpus.length !== this.prevCpus.length) {
999
- this.prevCpus = deepCopy(currCpus); // Reset the previous cpus
833
+ this.prevCpus = deepCopy(currCpus);
1000
834
  this.log.debug(`***Cpu usage reset. Current cpus: ${currCpus.length}. Previous cpus: ${this.prevCpus.length}.`);
1001
835
  return this.lastCpuUsage.toFixed(2);
1002
836
  }
1003
837
  let totalIdle = 0, totalTick = 0;
1004
- // Get the cpu usage
1005
838
  this.prevCpus.forEach((prevCpu, i) => {
1006
839
  const currCpu = currCpus[i];
1007
840
  const idleDiff = currCpu.times.idle - prevCpu.times.idle;
@@ -1022,9 +855,7 @@ export class Frontend {
1022
855
  clearInterval(this.memoryInterval);
1023
856
  clearTimeout(this.memoryTimeout);
1024
857
  const interval = () => {
1025
- // Get the cpu usage
1026
858
  const cpuUsage = this.getCpuUsage();
1027
- // Get the memory usage
1028
859
  const memoryUsageRaw = process.memoryUsage();
1029
860
  this.memoryData.push({ ...memoryUsageRaw, cpu: cpuUsage });
1030
861
  const memoryUsage = {
@@ -1035,7 +866,6 @@ export class Frontend {
1035
866
  arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
1036
867
  };
1037
868
  this.log.debug(`***Cpu usage: ${CYAN}${cpuUsage.padStart(6, ' ')} %${db} - Memory usage: rss ${CYAN}${memoryUsage.rss}${db} heapTotal ${CYAN}${memoryUsage.heapTotal}${db} heapUsed ${CYAN}${memoryUsage.heapUsed}${db} external ${memoryUsage.external} arrayBuffers ${memoryUsage.arrayBuffers}`);
1038
- // Update the system information
1039
869
  this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
1040
870
  this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
1041
871
  this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime();
@@ -1047,7 +877,7 @@ export class Frontend {
1047
877
  this.wssSendMemoryUpdate(this.matterbridge.systemInformation.freeMemory, this.matterbridge.systemInformation.totalMemory, this.matterbridge.systemInformation.systemUptime, this.matterbridge.systemInformation.rss, this.matterbridge.systemInformation.heapUsed, this.matterbridge.systemInformation.heapTotal);
1048
878
  };
1049
879
  interval();
1050
- this.memoryInterval = setInterval(interval, getIntParameter('memoryinterval') ?? 1000); // 1 second
880
+ this.memoryInterval = setInterval(interval, getIntParameter('memoryinterval') ?? 1000);
1051
881
  this.memoryInterval.unref();
1052
882
  this.memoryTimeout = setTimeout(() => {
1053
883
  this.stopCpuMemoryDump();
@@ -1067,18 +897,12 @@ export class Frontend {
1067
897
  external: this.formatMemoryUsage(memory.external),
1068
898
  arrayBuffers: this.formatMemoryUsage(memory.arrayBuffers),
1069
899
  };
1070
- // eslint-disable-next-line no-console
1071
900
  console.log(`${YELLOW}Cpu usage:${db} ${CYAN}${memory.cpu.padStart(6, ' ')} %${db} - ${YELLOW}Memory usage:${db} rss ${CYAN}${memoryUsage.rss}${db} heapTotal ${CYAN}${memoryUsage.heapTotal}${db} heapUsed ${CYAN}${memoryUsage.heapUsed}${db} external ${memoryUsage.external} arrayBuffers ${memoryUsage.arrayBuffers}${rs}`);
1072
901
  }
1073
902
  this.memoryData = [];
1074
903
  this.prevCpus = [];
1075
904
  }
1076
- /**
1077
- * Retrieves the api settings data.
1078
- * @returns {Promise<object>} A promise that resolve in the api settings object.
1079
- */
1080
905
  async getApiSettings() {
1081
- // Update the system information
1082
906
  this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
1083
907
  this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
1084
908
  this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime();
@@ -1086,7 +910,6 @@ export class Frontend {
1086
910
  this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
1087
911
  this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
1088
912
  this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
1089
- // Update the matterbridge information
1090
913
  this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
1091
914
  this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
1092
915
  this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
@@ -1105,11 +928,6 @@ export class Frontend {
1105
928
  this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
1106
929
  return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
1107
930
  }
1108
- /**
1109
- * Retrieves the cluster text description from a given device.
1110
- * @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
1111
- * @returns {string} The attributes description of the cluster servers in the device.
1112
- */
1113
931
  getClusterTextFromDevice(device) {
1114
932
  const getAttribute = (device, cluster, attribute) => {
1115
933
  let value = undefined;
@@ -1148,7 +966,6 @@ export class Frontend {
1148
966
  };
1149
967
  let attributes = '';
1150
968
  device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
1151
- // console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
1152
969
  if (typeof attributeValue === 'undefined')
1153
970
  return;
1154
971
  if (clusterName === 'onOff' && attributeName === 'onOff')
@@ -1226,13 +1043,8 @@ export class Frontend {
1226
1043
  if (clusterName === 'userLabel' && attributeName === 'labelList')
1227
1044
  attributes += `${getUserLabel(device)} `;
1228
1045
  });
1229
- // console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
1230
1046
  return attributes.trimStart().trimEnd();
1231
1047
  }
1232
- /**
1233
- * Retrieves the base registered plugins sanitized for res.json().
1234
- * @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
1235
- */
1236
1048
  getBaseRegisteredPlugins() {
1237
1049
  const baseRegisteredPlugins = [];
1238
1050
  for (const plugin of this.matterbridge.plugins) {
@@ -1263,14 +1075,6 @@ export class Frontend {
1263
1075
  }
1264
1076
  return baseRegisteredPlugins;
1265
1077
  }
1266
- /**
1267
- * Handles incoming websocket messages for the Matterbridge.
1268
- *
1269
- * @param {Matterbridge} this - The Matterbridge instance.
1270
- * @param {WebSocket} client - The websocket client that sent the message.
1271
- * @param {WebSocket.RawData} message - The raw data of the message received from the client.
1272
- * @returns {Promise<void>} A promise that resolves when the message has been handled.
1273
- */
1274
1078
  async wsMessageHandler(client, message) {
1275
1079
  let data;
1276
1080
  try {
@@ -1308,13 +1112,27 @@ export class Frontend {
1308
1112
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/install' }));
1309
1113
  return;
1310
1114
  }
1115
+ this.wssSendSnackbarMessage(`Installing package ${data.params.packageName}`);
1311
1116
  this.matterbridge
1312
1117
  .spawnCommand('npm', ['install', '-g', data.params.packageName, '--omit=dev', '--verbose'])
1313
1118
  .then((response) => {
1314
1119
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response }));
1120
+ this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`);
1121
+ if (data.params.restart !== true) {
1122
+ this.wssSendSnackbarMessage(`Restart required`, 0);
1123
+ }
1124
+ else {
1125
+ if (this.matterbridge.restartMode !== '') {
1126
+ this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
1127
+ this.matterbridge.shutdownProcess();
1128
+ }
1129
+ else
1130
+ this.wssSendSnackbarMessage(`Restart required`, 0);
1131
+ }
1315
1132
  })
1316
1133
  .catch((error) => {
1317
1134
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: error instanceof Error ? error.message : error }));
1135
+ this.wssSendSnackbarMessage(`Package ${data.params.packageName} not installed`);
1318
1136
  });
1319
1137
  return;
1320
1138
  }
@@ -1323,6 +1141,7 @@ export class Frontend {
1323
1141
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' }));
1324
1142
  return;
1325
1143
  }
1144
+ this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}`);
1326
1145
  this.matterbridge
1327
1146
  .spawnCommand('npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
1328
1147
  .then((response) => {
@@ -1330,14 +1149,18 @@ export class Frontend {
1330
1149
  })
1331
1150
  .catch((error) => {
1332
1151
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: error instanceof Error ? error.message : error }));
1152
+ this.wssSendSnackbarMessage(`Uninstalled package ${data.params.packageName}`);
1153
+ this.wssSendSnackbarMessage(`Restart required`, 0);
1333
1154
  });
1334
1155
  return;
1335
1156
  }
1336
1157
  else if (data.method === '/api/restart') {
1158
+ this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
1337
1159
  await this.matterbridge.restartProcess();
1338
1160
  return;
1339
1161
  }
1340
1162
  else if (data.method === '/api/shutdown') {
1163
+ this.wssSendSnackbarMessage(`Shutting down matterbridge...`, 0);
1341
1164
  await this.matterbridge.shutdownProcess();
1342
1165
  return;
1343
1166
  }
@@ -1358,10 +1181,8 @@ export class Frontend {
1358
1181
  else if (data.method === '/api/devices') {
1359
1182
  const devices = [];
1360
1183
  this.matterbridge.devices.forEach(async (device) => {
1361
- // Filter by pluginName if provided
1362
1184
  if (data.params.pluginName && data.params.pluginName !== device.plugin)
1363
1185
  return;
1364
- // Check if the device has the required properties
1365
1186
  if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId)
1366
1187
  return;
1367
1188
  const cluster = this.getClusterTextFromDevice(device);
@@ -1444,7 +1265,6 @@ export class Frontend {
1444
1265
  });
1445
1266
  endpointServer.getChildEndpoints().forEach((childEndpoint) => {
1446
1267
  deviceTypes = [];
1447
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1448
1268
  const name = childEndpoint.endpoint?.id;
1449
1269
  const clusterServers = childEndpoint.getAllClusterServers();
1450
1270
  clusterServers.forEach((clusterServer) => {
@@ -1527,114 +1347,70 @@ export class Frontend {
1527
1347
  return;
1528
1348
  }
1529
1349
  }
1530
- /**
1531
- * Sends a WebSocket message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
1532
- *
1533
- * @param {string} level - The logger level of the message: debug info notice warn error fatal...
1534
- * @param {string} time - The time string of the message
1535
- * @param {string} name - The logger name of the message
1536
- * @param {string} message - The content of the message.
1537
- */
1538
1350
  wssSendMessage(level, time, name, message) {
1539
1351
  if (!level || !time || !name || !message)
1540
1352
  return;
1541
- // Remove ANSI escape codes from the message
1542
- // eslint-disable-next-line no-control-regex
1543
1353
  message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
1544
- // Remove leading asterisks from the message
1545
1354
  message = message.replace(/^\*+/, '');
1546
- // Replace all occurrences of \t and \n
1547
1355
  message = message.replace(/[\t\n]/g, '');
1548
- // Remove non-printable characters
1549
- // eslint-disable-next-line no-control-regex
1550
1356
  message = message.replace(/[\x00-\x1F\x7F]/g, '');
1551
- // Replace all occurrences of \" with "
1552
1357
  message = message.replace(/\\"/g, '"');
1553
- // Define the maximum allowed length for continuous characters without a space
1554
1358
  const maxContinuousLength = 100;
1555
1359
  const keepStartLength = 20;
1556
1360
  const keepEndLength = 20;
1557
- // Split the message into words
1558
1361
  message = message
1559
1362
  .split(' ')
1560
1363
  .map((word) => {
1561
- // If the word length exceeds the max continuous length, insert spaces and truncate
1562
1364
  if (word.length > maxContinuousLength) {
1563
1365
  return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
1564
1366
  }
1565
1367
  return word;
1566
1368
  })
1567
1369
  .join(' ');
1568
- // Send the message to all connected clients
1569
1370
  this.webSocketServer?.clients.forEach((client) => {
1570
1371
  if (client.readyState === WebSocket.OPEN) {
1571
1372
  client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
1572
1373
  }
1573
1374
  });
1574
1375
  }
1575
- /**
1576
- * Sends a need to refresh WebSocket message to all connected clients.
1577
- *
1578
- */
1579
1376
  wssSendRefreshRequired() {
1580
1377
  this.log.debug('Sending a refresh required message to all connected clients');
1581
1378
  this.matterbridge.matterbridgeInformation.refreshRequired = true;
1582
- // Send the message to all connected clients
1583
1379
  this.webSocketServer?.clients.forEach((client) => {
1584
1380
  if (client.readyState === WebSocket.OPEN) {
1585
1381
  client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: {} }));
1586
1382
  }
1587
1383
  });
1588
1384
  }
1589
- /**
1590
- * Sends a need to restart WebSocket message to all connected clients.
1591
- *
1592
- */
1593
1385
  wssSendRestartRequired() {
1594
1386
  this.log.debug('Sending a restart required message to all connected clients');
1595
1387
  this.matterbridge.matterbridgeInformation.restartRequired = true;
1596
- // Send the message to all connected clients
1597
1388
  this.webSocketServer?.clients.forEach((client) => {
1598
1389
  if (client.readyState === WebSocket.OPEN) {
1599
1390
  client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
1600
1391
  }
1601
1392
  });
1602
1393
  }
1603
- /**
1604
- * Sends a memory update message to all connected clients.
1605
- *
1606
- */
1607
1394
  wssSendCpuUpdate(cpuUsed) {
1608
1395
  this.log.debug('Sending a memory update message to all connected clients');
1609
1396
  this.matterbridge.matterbridgeInformation.restartRequired = true;
1610
- // Send the message to all connected clients
1611
1397
  this.webSocketServer?.clients.forEach((client) => {
1612
1398
  if (client.readyState === WebSocket.OPEN) {
1613
1399
  client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsed } }));
1614
1400
  }
1615
1401
  });
1616
1402
  }
1617
- /**
1618
- * Sends a cpu update message to all connected clients.
1619
- *
1620
- */
1621
1403
  wssSendMemoryUpdate(freeMemory, totalMemory, systemUptime, rss, heapUsed, heapTotal) {
1622
1404
  this.log.debug('Sending a cpu update message to all connected clients');
1623
1405
  this.matterbridge.matterbridgeInformation.restartRequired = true;
1624
- // Send the message to all connected clients
1625
1406
  this.webSocketServer?.clients.forEach((client) => {
1626
1407
  if (client.readyState === WebSocket.OPEN) {
1627
1408
  client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { freeMemory, totalMemory, systemUptime, rss, heapUsed, heapTotal } }));
1628
1409
  }
1629
1410
  });
1630
1411
  }
1631
- /**
1632
- * Sends a cpu update message to all connected clients.
1633
- *
1634
- */
1635
1412
  wssSendSnackbarMessage(message, timeout = 5) {
1636
1413
  this.log.debug('Sending a snackbar message to all connected clients');
1637
- // Send the message to all connected clients
1638
1414
  this.webSocketServer?.clients.forEach((client) => {
1639
1415
  if (client.readyState === WebSocket.OPEN) {
1640
1416
  client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { message, timeout } }));
@@ -1642,4 +1418,3 @@ export class Frontend {
1642
1418
  });
1643
1419
  }
1644
1420
  }
1645
- //# sourceMappingURL=frontend.js.map