@matterbridge/core 3.7.2-dev-20260331-ac050d8 → 3.7.2

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.
@@ -0,0 +1,307 @@
1
+ import { Logger, LogLevel as MatterLogLevel } from '@matter/general';
2
+ import { BroadcastServer } from '@matterbridge/thread';
3
+ import { hasParameter } from '@matterbridge/utils/cli';
4
+ import { inspectError } from '@matterbridge/utils/error';
5
+ import { isValidNumber, isValidString } from '@matterbridge/utils/validate';
6
+ import { withTimeout } from '@matterbridge/utils/wait';
7
+ import { AnsiLogger, CYAN, debugStringify, nf } from 'node-ansi-logger';
8
+ import WebSocket, { WebSocketServer } from 'ws';
9
+ if (hasParameter('loader'))
10
+ console.log('\u001B[32mBackendWsServer loaded.\u001B[40;0m');
11
+ export class BackendsWsServer {
12
+ debug;
13
+ verbose;
14
+ webSocketServer;
15
+ log;
16
+ backend;
17
+ matterbridge;
18
+ server;
19
+ constructor(matterbridge, backend) {
20
+ this.debug = hasParameter('debug') || hasParameter('verbose') || hasParameter('debug-frontend') || hasParameter('verbose-frontend');
21
+ this.verbose = hasParameter('verbose') || hasParameter('verbose-frontend');
22
+ this.backend = backend;
23
+ this.matterbridge = matterbridge;
24
+ this.log = new AnsiLogger({
25
+ logName: 'BackendWsServer',
26
+ logNameColor: '\x1b[38;5;97m',
27
+ logTimestampFormat: 4,
28
+ logLevel: this.debug ? "debug" : "info",
29
+ });
30
+ this.server = new BroadcastServer('frontend', this.log);
31
+ this.server.on('broadcast_message', this.broadcastMsgHandler.bind(this));
32
+ }
33
+ destroy() {
34
+ this.server.off('broadcast_message', this.broadcastMsgHandler.bind(this));
35
+ this.server.close();
36
+ }
37
+ async broadcastMsgHandler(msg) {
38
+ if (this.server.isWorkerRequest(msg)) {
39
+ switch (msg.type) {
40
+ case 'get_log_level':
41
+ this.server.respond({ ...msg, result: { logLevel: this.log.logLevel } });
42
+ break;
43
+ case 'set_log_level':
44
+ this.log.logLevel = msg.params.logLevel;
45
+ this.server.respond({ ...msg, result: { logLevel: this.log.logLevel } });
46
+ break;
47
+ }
48
+ }
49
+ }
50
+ async start() {
51
+ this.log.debug(`Creating WebSocketServer...`);
52
+ this.webSocketServer = new WebSocketServer({ noServer: true });
53
+ this.backend.emit('websocket_server_listening', hasParameter('ssl') ? 'wss' : 'ws');
54
+ this.webSocketServer.on('connection', (websocket, request) => {
55
+ const clientIp = request.socket.remoteAddress;
56
+ this.log.info(`WebSocketServer client ${CYAN}${clientIp}${nf} connected to Matterbridge`);
57
+ let callbackLogLevel = "notice";
58
+ if (this.log.logLevel === "info" || Logger.level === MatterLogLevel.INFO)
59
+ callbackLogLevel = "info";
60
+ if (this.log.logLevel === "debug" || Logger.level === MatterLogLevel.DEBUG)
61
+ callbackLogLevel = "debug";
62
+ AnsiLogger.setGlobalCallback(this.wssSendLogMessage.bind(this), callbackLogLevel);
63
+ this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
64
+ websocket.on('message', (data) => {
65
+ this.log.debug(`WebSocket client ${CYAN}${clientIp}${nf} sent a message`);
66
+ this.wsMessageHandler(websocket, data);
67
+ });
68
+ websocket.on('ping', () => {
69
+ this.log.debug(`WebSocket client ${CYAN}${clientIp}${nf} ping received`);
70
+ websocket.pong();
71
+ });
72
+ websocket.on('pong', () => {
73
+ this.log.debug(`WebSocket client ${CYAN}${clientIp}${nf} pong received`);
74
+ });
75
+ websocket.on('close', (code, reason) => {
76
+ this.log.info(`WebSocket client ${CYAN}${clientIp}${nf} disconnected code ${code} reason ${reason.toString()}`);
77
+ if (this.webSocketServer?.clients.size === 0) {
78
+ AnsiLogger.setGlobalCallback(undefined);
79
+ this.log.debug('All WebSocket clients disconnected. WebSocketServer logger global callback removed');
80
+ setTimeout(() => {
81
+ if (this.webSocketServer?.clients.size === 0) {
82
+ this.log.debug('All WebSocket clients disconnected. Auth clients list cleared');
83
+ this.backend.authClients.clear();
84
+ }
85
+ }, 1000).unref();
86
+ }
87
+ else {
88
+ this.log.debug(`WebSocketServer still has ${this.webSocketServer?.clients.size} connected clients`);
89
+ }
90
+ });
91
+ websocket.on('error', (error) => {
92
+ inspectError(this.log, `WebSocket client error`, error);
93
+ });
94
+ });
95
+ this.webSocketServer.on('close', () => {
96
+ this.log.debug(`WebSocketServer closed`);
97
+ });
98
+ this.webSocketServer.on('error', (ws, error) => {
99
+ inspectError(this.log, `WebSocketServer error`, error);
100
+ });
101
+ return this.webSocketServer;
102
+ }
103
+ async stop() {
104
+ if (this.webSocketServer) {
105
+ this.log.debug('Closing WebSocket server...');
106
+ this.webSocketServer.clients.forEach((client) => {
107
+ if (client.readyState === WebSocket.OPEN) {
108
+ client.close();
109
+ }
110
+ });
111
+ await withTimeout(new Promise((resolve) => {
112
+ this.webSocketServer?.close((error) => {
113
+ if (error) {
114
+ inspectError(this.log, `Error closing WebSocket server`, error);
115
+ }
116
+ else {
117
+ this.log.debug('WebSocket server closed successfully');
118
+ this.backend.emit('websocket_server_stopped');
119
+ }
120
+ resolve();
121
+ });
122
+ }), 10000, false);
123
+ this.webSocketServer.removeAllListeners();
124
+ this.webSocketServer = undefined;
125
+ }
126
+ else {
127
+ this.log.debug('WebSocket server is not running');
128
+ }
129
+ return this.webSocketServer;
130
+ }
131
+ hasActiveClients() {
132
+ return this.webSocketServer !== undefined && this.webSocketServer.clients.size > 0;
133
+ }
134
+ async wsMessageHandler(client, rawData) {
135
+ let data;
136
+ const sendResponse = (data) => {
137
+ if (client.readyState === client.OPEN) {
138
+ if ('response' in data) {
139
+ const { response, ...rest } = data;
140
+ this.log.debug(`Sending api response message: ${debugStringify(rest)}`);
141
+ }
142
+ else if ('error' in data) {
143
+ this.log.debug(`Sending api error message: ${debugStringify(data)}`);
144
+ }
145
+ client.send(JSON.stringify(data));
146
+ }
147
+ else {
148
+ this.log.error('Cannot send api response, client not connected');
149
+ }
150
+ };
151
+ try {
152
+ data = JSON.parse(rawData.toString());
153
+ if (!isValidNumber(data.id) || !isValidString(data.dst) || !isValidString(data.src) || !isValidString(data.method) || data.dst !== 'Matterbridge') {
154
+ this.log.error(`Invalid message from websocket client: ${debugStringify(data)}`);
155
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Invalid message' });
156
+ return;
157
+ }
158
+ this.log.debug(`Received message from websocket client: ${debugStringify(data)}`);
159
+ }
160
+ catch (error) {
161
+ inspectError(this.log, `Error parsing message from websocket client`, error);
162
+ return;
163
+ }
164
+ }
165
+ wssBroadcastMessage(msg) {
166
+ if (!this.hasActiveClients())
167
+ return;
168
+ try {
169
+ const stringifiedMsg = JSON.stringify(msg);
170
+ if (this.verbose)
171
+ this.log.debug(`Sending a broadcast message: ${debugStringify(msg)}`);
172
+ this.webSocketServer?.clients.forEach((client) => {
173
+ if (client.readyState === client.OPEN) {
174
+ client.send(stringifiedMsg);
175
+ }
176
+ });
177
+ }
178
+ catch (error) {
179
+ inspectError(this.log, `Error broadcasting message to websocket clients`, error);
180
+ }
181
+ }
182
+ wssSendLogMessage(level, time, name, message) {
183
+ if (!this.hasActiveClients())
184
+ return;
185
+ if (!level || !time || !name || !message)
186
+ return;
187
+ message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
188
+ message = message.replace(/^\*+/, '');
189
+ message = message.replace(/[\t\n]/g, '');
190
+ message = message.replace(/[\x00-\x1F\x7F]/g, '');
191
+ message = message.replace(/\\"/g, '"');
192
+ const maxContinuousLength = 100;
193
+ const keepStartLength = 20;
194
+ const keepEndLength = 20;
195
+ if (level !== 'spawn') {
196
+ message = message
197
+ .split(' ')
198
+ .map((word) => {
199
+ if (word.length > maxContinuousLength) {
200
+ return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
201
+ }
202
+ return word;
203
+ })
204
+ .join(' ');
205
+ }
206
+ this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'log', success: true, response: { level, time, name, message } });
207
+ }
208
+ wssSendRefreshRequired(changed, params) {
209
+ if (!this.hasActiveClients())
210
+ return;
211
+ if (this.verbose)
212
+ this.log.debug('Sending a refresh required message to all connected clients');
213
+ this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', success: true, response: { changed, lock: params?.lock, ...params } });
214
+ }
215
+ wssSendRestartRequired(snackbar = true, fixed = false) {
216
+ if (!this.hasActiveClients())
217
+ return;
218
+ if (this.verbose)
219
+ this.log.debug('Sending a restart required message to all connected clients');
220
+ this.backend.restartRequired = true;
221
+ this.backend.fixedRestartRequired = fixed;
222
+ if (snackbar === true)
223
+ this.wssSendSnackbarMessage(`Restart required`, 0);
224
+ this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', success: true, response: { fixed } });
225
+ }
226
+ wssSendRestartNotRequired(snackbar = true) {
227
+ if (!this.hasActiveClients())
228
+ return;
229
+ if (this.verbose)
230
+ this.log.debug('Sending a restart not required message to all connected clients');
231
+ this.backend.restartRequired = false;
232
+ if (snackbar === true)
233
+ this.wssSendCloseSnackbarMessage(`Restart required`);
234
+ this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required', success: true });
235
+ }
236
+ wssSendUpdateRequired(devVersion = false) {
237
+ if (!this.hasActiveClients())
238
+ return;
239
+ if (this.verbose)
240
+ this.log.debug('Sending an update required message to all connected clients');
241
+ this.backend.updateRequired = true;
242
+ this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', success: true, response: { devVersion } });
243
+ }
244
+ wssSendCpuUpdate(cpuUsage, processCpuUsage) {
245
+ if (!this.hasActiveClients())
246
+ return;
247
+ if (this.verbose)
248
+ this.log.debug('Sending a cpu update message to all connected clients');
249
+ this.wssBroadcastMessage({
250
+ id: 0,
251
+ src: 'Matterbridge',
252
+ dst: 'Frontend',
253
+ method: 'cpu_update',
254
+ success: true,
255
+ response: { cpuUsage: Math.round(cpuUsage * 100) / 100, processCpuUsage: Math.round(processCpuUsage * 100) / 100 },
256
+ });
257
+ }
258
+ wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
259
+ if (!this.hasActiveClients())
260
+ return;
261
+ if (this.verbose)
262
+ this.log.debug('Sending a memory update message to all connected clients');
263
+ this.wssBroadcastMessage({
264
+ id: 0,
265
+ src: 'Matterbridge',
266
+ dst: 'Frontend',
267
+ method: 'memory_update',
268
+ success: true,
269
+ response: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers },
270
+ });
271
+ }
272
+ wssSendUptimeUpdate(systemUptime, processUptime) {
273
+ if (!this.hasActiveClients())
274
+ return;
275
+ if (this.verbose)
276
+ this.log.debug('Sending a uptime update message to all connected clients');
277
+ this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', success: true, response: { systemUptime, processUptime } });
278
+ }
279
+ wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
280
+ if (!this.hasActiveClients())
281
+ return;
282
+ if (this.verbose)
283
+ this.log.debug('Sending a snackbar message to all connected clients');
284
+ this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'snackbar', success: true, response: { message, timeout, severity } });
285
+ }
286
+ wssSendCloseSnackbarMessage(message) {
287
+ if (!this.hasActiveClients())
288
+ return;
289
+ if (this.verbose)
290
+ this.log.debug('Sending a close snackbar message to all connected clients');
291
+ this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'close_snackbar', success: true, response: { message } });
292
+ }
293
+ wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, number, id, cluster, attribute, value) {
294
+ if (!this.hasActiveClients())
295
+ return;
296
+ if (this.verbose)
297
+ this.log.debug('Sending an attribute update message to all connected clients');
298
+ this.wssBroadcastMessage({
299
+ id: 0,
300
+ src: 'Matterbridge',
301
+ dst: 'Frontend',
302
+ method: 'state_update',
303
+ success: true,
304
+ response: { plugin, serialNumber, uniqueId, number, id, cluster, attribute, value },
305
+ });
306
+ }
307
+ }
package/dist/cli.js CHANGED
@@ -12,7 +12,8 @@ export let instance;
12
12
  export const tracker = new Tracker('Cli', false, false);
13
13
  export const inspector = new Inspector('Cli', false, false);
14
14
  const manager = new ThreadsManager();
15
- if (process.argv.includes('--no-ansi') || process.argv.includes('-no-ansi'))
15
+ const colorEnabled = Boolean(process.stdout.isTTY && !process.env.NO_COLOR && process.env.TERM !== 'dumb' && process.env.FORCE_COLOR !== '0' && !hasParameter('no-ansi'));
16
+ if (!colorEnabled)
16
17
  process.env.NO_COLOR = '1';
17
18
  const log = new AnsiLogger({ logName: 'Cli', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
18
19
  function startCpuMemoryCheck() {
@@ -1,6 +1,6 @@
1
1
  import EventEmitter from 'node:events';
2
2
  import { EndpointNumber } from '@matter/types/datatype';
3
- import type { ApiMatter, RefreshRequiredChanged, WsMessageBroadcast } from '@matterbridge/types';
3
+ import type { ApiClusters, ApiDevice, ApiMatter, ApiPlugin, ApiSettings, RefreshRequiredChanged, WsMessageBroadcast } from '@matterbridge/types';
4
4
  import { LogLevel } from 'node-ansi-logger';
5
5
  import type { Matterbridge } from './matterbridge.js';
6
6
  interface FrontendEvents {
@@ -15,8 +15,8 @@ export declare class Frontend extends EventEmitter<FrontendEvents> {
15
15
  private log;
16
16
  private port;
17
17
  private listening;
18
- private storedPassword;
19
- private authClients;
18
+ storedPassword: string | undefined;
19
+ authClients: Set<string>;
20
20
  private expressApp;
21
21
  private httpServer;
22
22
  private httpsServer;
@@ -25,22 +25,29 @@ export declare class Frontend extends EventEmitter<FrontendEvents> {
25
25
  private serverFetchTimeout;
26
26
  private readonly debug;
27
27
  private readonly verbose;
28
+ readonly readOnly: boolean;
29
+ readonly shellyBoard: boolean;
30
+ shellySysUpdate: boolean;
31
+ shellyMainUpdate: boolean;
32
+ restartRequired: boolean;
33
+ fixedRestartRequired: boolean;
34
+ updateRequired: boolean;
28
35
  constructor(matterbridge: Matterbridge);
29
36
  destroy(): void;
30
- private msgHandler;
37
+ private broadcastMsgHandler;
31
38
  set logLevel(logLevel: LogLevel);
32
39
  private validateReq;
33
40
  start(port?: number): Promise<void>;
34
41
  stop(): Promise<void>;
35
- private getApiSettings;
42
+ getApiSettings(): ApiSettings;
36
43
  private getReachability;
37
44
  private getPowerSource;
38
45
  private getBatteryLevel;
39
46
  private getClusterTextFromDevice;
40
- private getPlugins;
41
- private getDevices;
42
- private getClusters;
43
- private generateDiagnostic;
47
+ getApiPlugins(): ApiPlugin[];
48
+ getApiDevices(pluginName?: string): ApiDevice[];
49
+ getClusters(pluginName: string, endpointNumber: number, serialNumber?: string, uniqueId?: string): ApiClusters | undefined;
50
+ generateDiagnostic(): Promise<void>;
44
51
  private wsMessageHandler;
45
52
  wssSendLogMessage(level: string, time: string, name: string, message: string): void;
46
53
  wssSendRefreshRequired(changed: RefreshRequiredChanged, params?: {
package/dist/frontend.js CHANGED
@@ -1,5 +1,3 @@
1
- if (process.argv.includes('--loader') || process.argv.includes('-loader'))
2
- console.log('\u001B[32mFrontend loaded.\u001B[40;0m');
3
1
  import EventEmitter from 'node:events';
4
2
  import os from 'node:os';
5
3
  import path from 'node:path';
@@ -10,7 +8,7 @@ import { PowerSource } from '@matter/types/clusters/power-source';
10
8
  import { CommissioningOptions } from '@matter/types/commissioning';
11
9
  import { FabricIndex } from '@matter/types/datatype';
12
10
  import { BroadcastServer } from '@matterbridge/thread/server';
13
- import { MATTER_LOGGER_FILE, MATTER_STORAGE_NAME, MATTERBRIDGE_DIAGNOSTIC_FILE, MATTERBRIDGE_HISTORY_FILE, MATTERBRIDGE_LOGGER_FILE, NODE_STORAGE_DIR, plg, } from '@matterbridge/types';
11
+ import { MATTER_LOGGER_FILE, MATTER_STORAGE_DIR, MATTERBRIDGE_DIAGNOSTIC_FILE, MATTERBRIDGE_HISTORY_FILE, MATTERBRIDGE_LOGGER_FILE, NODE_STORAGE_DIR, plg, } from '@matterbridge/types';
14
12
  import { getParameter, hasParameter } from '@matterbridge/utils/cli';
15
13
  import { inspectError } from '@matterbridge/utils/error';
16
14
  import { formatBytes, formatPercent, formatUptime } from '@matterbridge/utils/format';
@@ -20,6 +18,8 @@ import { AnsiLogger, CYAN, db, debugStringify, er, nf, nt, rs, stringify, UNDERL
20
18
  import { cliEmitter, lastOsCpuUsage, lastProcessCpuUsage } from './cliEmitter.js';
21
19
  import { generateHistoryPage } from './cliHistory.js';
22
20
  import { capitalizeFirstLetter, getAttribute } from './matterbridgeEndpointHelpers.js';
21
+ if (hasParameter('loader'))
22
+ console.log('\u001B[32mFrontend loaded.\u001B[40;0m');
23
23
  export class Frontend extends EventEmitter {
24
24
  matterbridge;
25
25
  log;
@@ -35,6 +35,13 @@ export class Frontend extends EventEmitter {
35
35
  serverFetchTimeout = 2000;
36
36
  debug = hasParameter('debug') || hasParameter('verbose');
37
37
  verbose = hasParameter('verbose');
38
+ readOnly = hasParameter('readonly') || hasParameter('shelly');
39
+ shellyBoard = hasParameter('shelly');
40
+ shellySysUpdate = false;
41
+ shellyMainUpdate = false;
42
+ restartRequired = false;
43
+ fixedRestartRequired = false;
44
+ updateRequired = false;
38
45
  constructor(matterbridge) {
39
46
  super();
40
47
  this.matterbridge = matterbridge;
@@ -45,13 +52,13 @@ export class Frontend extends EventEmitter {
45
52
  logLevel: hasParameter('debug') ? "debug" : "info",
46
53
  });
47
54
  this.server = new BroadcastServer('frontend', this.log);
48
- this.server.on('broadcast_message', this.msgHandler.bind(this));
55
+ this.server.on('broadcast_message', this.broadcastMsgHandler.bind(this));
49
56
  }
50
57
  destroy() {
51
- this.server.off('broadcast_message', this.msgHandler.bind(this));
58
+ this.server.off('broadcast_message', this.broadcastMsgHandler.bind(this));
52
59
  this.server.close();
53
60
  }
54
- async msgHandler(msg) {
61
+ async broadcastMsgHandler(msg) {
55
62
  if (this.server.isWorkerRequest(msg)) {
56
63
  if (this.verbose)
57
64
  this.log.debug(`Received broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
@@ -116,6 +123,8 @@ export class Frontend extends EventEmitter {
116
123
  if (msg.result && msg.result.packageCommand === 'install') {
117
124
  this.wssSendCloseSnackbarMessage(`Installing package ${msg.result.packageName}...`);
118
125
  if (msg.result.success) {
126
+ this.restartRequired = true;
127
+ this.fixedRestartRequired = true;
119
128
  this.wssSendRestartRequired(true, true);
120
129
  this.wssSendSnackbarMessage(`Installed package ${msg.result.packageName}`, 5, 'success');
121
130
  }
@@ -126,6 +135,8 @@ export class Frontend extends EventEmitter {
126
135
  if (msg.result && msg.result.packageCommand === 'uninstall') {
127
136
  this.wssSendCloseSnackbarMessage(`Uninstalling package ${msg.result.packageName}...`);
128
137
  if (msg.result.success) {
138
+ this.restartRequired = true;
139
+ this.fixedRestartRequired = true;
129
140
  this.wssSendRestartRequired(true, true);
130
141
  this.wssSendSnackbarMessage(`Uninstalled package ${msg.result.packageName}`, 5, 'success');
131
142
  }
@@ -490,19 +501,19 @@ export class Frontend extends EventEmitter {
490
501
  this.log.debug('The frontend sent /api/settings');
491
502
  if (!this.validateReq(req, res))
492
503
  return;
493
- res.json(await this.getApiSettings());
504
+ res.json(this.getApiSettings());
494
505
  });
495
506
  this.expressApp.get('/api/plugins', async (req, res) => {
496
507
  this.log.debug('The frontend sent /api/plugins');
497
508
  if (!this.validateReq(req, res))
498
509
  return;
499
- res.json(this.matterbridge.hasCleanupStarted ? [] : this.getPlugins());
510
+ res.json(this.getApiPlugins());
500
511
  });
501
512
  this.expressApp.get('/api/devices', async (req, res) => {
502
513
  this.log.debug('The frontend sent /api/devices');
503
514
  if (!this.validateReq(req, res))
504
515
  return;
505
- res.json(this.matterbridge.hasCleanupStarted ? [] : this.getDevices());
516
+ res.json(this.getApiDevices());
506
517
  });
507
518
  this.expressApp.get('/api/view-mblog', async (req, res) => {
508
519
  this.log.debug('The frontend sent /api/view-mblog');
@@ -699,14 +710,14 @@ export class Frontend extends EventEmitter {
699
710
  this.log.debug('The frontend sent /api/download-mjstorage');
700
711
  if (!this.validateReq(req, res))
701
712
  return;
702
- res.download(path.join(os.tmpdir(), `matterbridge.${MATTER_STORAGE_NAME}.zip`), `matterbridge.${MATTER_STORAGE_NAME}.zip`, (error) => {
713
+ res.download(path.join(os.tmpdir(), `matterbridge.${MATTER_STORAGE_DIR}.zip`), `matterbridge.${MATTER_STORAGE_DIR}.zip`, (error) => {
703
714
  this.wssSendCloseSnackbarMessage('Creating matter storage backup...');
704
715
  if (error) {
705
- this.log.error(`Error downloading the matter storage matterbridge.${MATTER_STORAGE_NAME}.zip: ${error instanceof Error ? error.message : error}`);
716
+ this.log.error(`Error downloading the matter storage matterbridge.${MATTER_STORAGE_DIR}.zip: ${error instanceof Error ? error.message : error}`);
706
717
  res.status(500).send('Error downloading the matter storage zip file');
707
718
  }
708
719
  else {
709
- this.log.debug(`Matter storage matterbridge.${MATTER_STORAGE_NAME}.zip downloaded successfully`);
720
+ this.log.debug(`Matter storage matterbridge.${MATTER_STORAGE_DIR}.zip downloaded successfully`);
710
721
  }
711
722
  });
712
723
  });
@@ -842,7 +853,7 @@ export class Frontend extends EventEmitter {
842
853
  }
843
854
  this.log.debug('Frontend stopped successfully');
844
855
  }
845
- async getApiSettings() {
856
+ getApiSettings() {
846
857
  this.matterbridge.systemInformation.totalMemory = formatBytes(os.totalmem());
847
858
  this.matterbridge.systemInformation.freeMemory = formatBytes(os.freemem());
848
859
  this.matterbridge.systemInformation.systemUptime = formatUptime(os.uptime());
@@ -871,23 +882,23 @@ export class Frontend extends EventEmitter {
871
882
  restartMode: this.matterbridge.restartMode,
872
883
  virtualMode: this.matterbridge.virtualMode,
873
884
  profile: this.matterbridge.profile,
874
- readOnly: this.matterbridge.readOnly,
875
- shellyBoard: this.matterbridge.shellyBoard,
876
- shellySysUpdate: this.matterbridge.shellySysUpdate,
877
- shellyMainUpdate: this.matterbridge.shellyMainUpdate,
878
- loggerLevel: await this.matterbridge.getLogLevel(),
885
+ loggerLevel: this.matterbridge.logLevel,
879
886
  fileLogger: this.matterbridge.fileLogger,
880
- matterLoggerLevel: Logger.level,
887
+ matterLoggerLevel: this.matterbridge.matterLogLevel,
881
888
  matterFileLogger: this.matterbridge.matterFileLogger,
882
889
  matterMdnsInterface: this.matterbridge.mdnsInterface,
883
890
  matterIpv4Address: this.matterbridge.ipv4Address,
884
891
  matterIpv6Address: this.matterbridge.ipv6Address,
885
- matterPort: (await this.matterbridge.nodeContext?.get('matterport', 5540)) ?? 5540,
886
- matterDiscriminator: await this.matterbridge.nodeContext?.get('matterdiscriminator'),
887
- matterPasscode: await this.matterbridge.nodeContext?.get('matterpasscode'),
888
- restartRequired: this.matterbridge.restartRequired,
889
- fixedRestartRequired: this.matterbridge.fixedRestartRequired,
890
- updateRequired: this.matterbridge.updateRequired,
892
+ matterPort: this.matterbridge.port || 5540,
893
+ matterDiscriminator: this.matterbridge.discriminator,
894
+ matterPasscode: this.matterbridge.passcode,
895
+ readOnly: this.readOnly,
896
+ shellyBoard: this.shellyBoard,
897
+ shellySysUpdate: this.shellySysUpdate,
898
+ shellyMainUpdate: this.shellyMainUpdate,
899
+ restartRequired: this.restartRequired,
900
+ fixedRestartRequired: this.fixedRestartRequired,
901
+ updateRequired: this.updateRequired,
891
902
  };
892
903
  return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: info };
893
904
  }
@@ -1065,7 +1076,7 @@ export class Frontend extends EventEmitter {
1065
1076
  });
1066
1077
  return attributes.trimStart().trimEnd();
1067
1078
  }
1068
- getPlugins() {
1079
+ getApiPlugins() {
1069
1080
  if (this.matterbridge.hasCleanupStarted)
1070
1081
  return [];
1071
1082
  const plugins = [];
@@ -1100,7 +1111,7 @@ export class Frontend extends EventEmitter {
1100
1111
  }
1101
1112
  return plugins;
1102
1113
  }
1103
- getDevices(pluginName) {
1114
+ getApiDevices(pluginName) {
1104
1115
  if (this.matterbridge.hasCleanupStarted)
1105
1116
  return [];
1106
1117
  const devices = [];
@@ -1488,7 +1499,7 @@ export class Frontend extends EventEmitter {
1488
1499
  else if (data.method === '/api/create-matter-storage-backup') {
1489
1500
  this.wssSendSnackbarMessage('Creating matter storage backup...', 0);
1490
1501
  this.log.notice(`Creating matter storage backup...`);
1491
- this.zip('zip', path.join(os.tmpdir(), `matterbridge.${MATTER_STORAGE_NAME}.zip`), [path.join(this.matterbridge.matterbridgeDirectory, MATTER_STORAGE_NAME)], '');
1502
+ this.zip('zip', path.join(os.tmpdir(), `matterbridge.${MATTER_STORAGE_DIR}.zip`), [path.join(this.matterbridge.matterbridgeDirectory, MATTER_STORAGE_DIR)], '');
1492
1503
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1493
1504
  }
1494
1505
  else if (data.method === '/api/create-plugin-backup') {
@@ -1580,14 +1591,13 @@ export class Frontend extends EventEmitter {
1580
1591
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true, response: matter });
1581
1592
  }
1582
1593
  else if (data.method === '/api/settings') {
1583
- sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true, response: await this.getApiSettings() });
1594
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true, response: this.getApiSettings() });
1584
1595
  }
1585
1596
  else if (data.method === '/api/plugins') {
1586
- const plugins = this.matterbridge.hasCleanupStarted ? [] : this.getPlugins();
1587
- sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true, response: plugins });
1597
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true, response: this.getApiPlugins() });
1588
1598
  }
1589
1599
  else if (data.method === '/api/devices') {
1590
- const devices = this.matterbridge.hasCleanupStarted ? [] : this.getDevices(isValidString(data.params.pluginName) ? data.params.pluginName : undefined);
1600
+ const devices = this.getApiDevices(isValidString(data.params.pluginName) ? data.params.pluginName : undefined);
1591
1601
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true, response: devices });
1592
1602
  }
1593
1603
  else if (data.method === '/api/clusters') {
@@ -2033,8 +2043,8 @@ export class Frontend extends EventEmitter {
2033
2043
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2034
2044
  return;
2035
2045
  this.log.debug('Sending a restart required message to all connected clients');
2036
- this.matterbridge.restartRequired = true;
2037
- this.matterbridge.fixedRestartRequired = fixed;
2046
+ this.restartRequired = true;
2047
+ this.fixedRestartRequired = fixed;
2038
2048
  if (snackbar === true)
2039
2049
  this.wssSendSnackbarMessage(`Restart required`, 0);
2040
2050
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', success: true, response: { fixed } });
@@ -2043,7 +2053,7 @@ export class Frontend extends EventEmitter {
2043
2053
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2044
2054
  return;
2045
2055
  this.log.debug('Sending a restart not required message to all connected clients');
2046
- this.matterbridge.restartRequired = false;
2056
+ this.restartRequired = false;
2047
2057
  if (snackbar === true)
2048
2058
  this.wssSendCloseSnackbarMessage(`Restart required`);
2049
2059
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required', success: true });
@@ -2052,7 +2062,7 @@ export class Frontend extends EventEmitter {
2052
2062
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2053
2063
  return;
2054
2064
  this.log.debug('Sending an update required message to all connected clients');
2055
- this.matterbridge.updateRequired = true;
2065
+ this.updateRequired = true;
2056
2066
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', success: true, response: { devVersion } });
2057
2067
  }
2058
2068
  wssSendCpuUpdate(cpuUsage, processCpuUsage) {
@@ -83,7 +83,7 @@ export declare function createTestEnvironment(name: string, createOnly?: boolean
83
83
  export declare function destroyTestEnvironment(createOnly?: boolean): Promise<void>;
84
84
  export declare function flushAsync(ticks?: number, microTurns?: number, pause?: number): Promise<void>;
85
85
  export declare function logKeepAlives(log?: AnsiLogger): number;
86
- export declare function flushAllEndpointNumberPersistence(targetServer: ServerNode, rounds?: number): Promise<void>;
86
+ export declare function flushAllEndpointNumberPersistence(targetServer: ServerNode, rounds?: number, pause?: number): Promise<void>;
87
87
  export declare function assertAllEndpointNumbersPersisted(targetServer: ServerNode): Promise<number>;
88
88
  export declare function closeServerNodeStores(targetServer?: ServerNode): Promise<void>;
89
89
  export declare function startServerNode(name: string, port: number, deviceType?: DeviceTypeId, createOnly?: boolean): Promise<[ServerNode<ServerNode.RootEndpoint>, Endpoint<AggregatorEndpoint>]>;