matterbridge 3.3.1-dev-20251007-4e5eaac → 3.3.1-dev-20251008-e61b8db

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.
package/CHANGELOG.md CHANGED
@@ -36,7 +36,7 @@ Advantages:
36
36
  - [network]: Added getInterfaceName() function.
37
37
  - [network]: Optimized code.
38
38
  - [matterbridge]: Added SharedMatterbridge readonly type.
39
- - [thread]: Add BroadcastServer to frontend plugins and devices.
39
+ - [thread]: Add BroadcastServer to frontend, plugins and devices.
40
40
 
41
41
  ### Changed
42
42
 
@@ -46,6 +46,11 @@ Advantages:
46
46
  - [frontend]: Refactored InstallProgressDialog.
47
47
  - [spawn]: Refactored spawnCommand for compatibility with InstallProgressDialog.
48
48
  - [matter.js]: Bumped `matter.js` to 0.15.5. Thanks matter.js!
49
+ - [backend]: Optimized imports.
50
+
51
+ ### Fixed
52
+
53
+ - [frontend]: Fixed matter log on file not setting correctly.
49
54
 
50
55
  <a href="https://www.buymeacoffee.com/luligugithub">
51
56
  <img src="bmc-button.svg" alt="Buy me a coffee" width="80">
package/dist/cli.js CHANGED
@@ -1,6 +1,8 @@
1
+ if (process.argv.includes('--loader') || process.argv.includes('-loader'))
2
+ console.log('\u001B[32mCli loaded.\u001B[40;0m');
1
3
  import os from 'node:os';
2
4
  import { inspect } from 'node:util';
3
- import { AnsiLogger, BRIGHT, CYAN, db, YELLOW } from 'node-ansi-logger';
5
+ import { AnsiLogger, BRIGHT, CYAN, db, RED, YELLOW } from 'node-ansi-logger';
4
6
  import { getIntParameter, hasParameter } from './utils/export.js';
5
7
  import { Matterbridge } from './matterbridge.js';
6
8
  import { cliEmitter, lastCpuUsage, setLastCpuUsage } from './cliEmitter.js';
@@ -8,9 +10,12 @@ export let instance;
8
10
  let session;
9
11
  let snapshotInterval;
10
12
  let memoryCheckInterval;
13
+ let memoryPeakResetTimeout;
11
14
  let prevCpus;
12
15
  let peakCpu = 0;
13
16
  let peakRss = 0;
17
+ let peakHeapUsed = 0;
18
+ let peakHeapTotal = 0;
14
19
  const log = new AnsiLogger({ logName: 'Cli', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
15
20
  const formatMemoryUsage = (bytes) => {
16
21
  if (bytes >= 1024 ** 3) {
@@ -54,8 +59,21 @@ async function startCpuMemoryCheck() {
54
59
  const heapUsed = formatMemoryUsage(memoryUsageRaw.heapUsed);
55
60
  const external = formatMemoryUsage(memoryUsageRaw.external);
56
61
  const arrayBuffers = formatMemoryUsage(memoryUsageRaw.arrayBuffers);
57
- if (memoryUsageRaw.rss > peakRss)
62
+ if (memoryUsageRaw.rss > peakRss) {
63
+ if (peakRss)
64
+ log.debug(`****${RED}${BRIGHT}Rss peak detected.${db} Peak rss from ${CYAN}${formatMemoryUsage(peakRss)}${db} to ${CYAN}${formatMemoryUsage(memoryUsageRaw.rss)}${db}`);
58
65
  peakRss = memoryUsageRaw.rss;
66
+ }
67
+ if (memoryUsageRaw.heapUsed > peakHeapUsed) {
68
+ if (peakHeapUsed)
69
+ log.debug(`****${RED}${BRIGHT}HeapUsed peak detected.${db} Peak heapUsed from ${CYAN}${formatMemoryUsage(peakHeapUsed)}${db} to ${CYAN}${formatMemoryUsage(memoryUsageRaw.heapUsed)}${db}`);
70
+ peakHeapUsed = memoryUsageRaw.heapUsed;
71
+ }
72
+ if (memoryUsageRaw.heapTotal > peakHeapTotal) {
73
+ if (peakHeapTotal)
74
+ log.debug(`****${RED}${BRIGHT}HeapTotal peak detected.${db} Peak heapTotal from ${CYAN}${formatMemoryUsage(peakHeapTotal)}${db} to ${CYAN}${formatMemoryUsage(memoryUsageRaw.heapTotal)}${db}`);
75
+ peakHeapTotal = memoryUsageRaw.heapTotal;
76
+ }
59
77
  cliEmitter.emit('memory', totalMememory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers);
60
78
  const currCpus = os.cpus();
61
79
  let cpuUsageLog;
@@ -74,27 +92,37 @@ async function startCpuMemoryCheck() {
74
92
  });
75
93
  const cpuUsage = 100 - (totalIdle / totalTick) * 100;
76
94
  if (totalTick === 0 || isNaN(cpuUsage) || !isFinite(cpuUsage) || cpuUsage <= 0) {
77
- if (lastCpuUsage != 0)
78
- log.debug(`Cpu check failed, using previous cpus`);
95
+ log.debug(`Cpu check failed, using previous cpus`);
79
96
  cpuUsageLog = lastCpuUsage.toFixed(2);
80
97
  }
81
98
  else {
82
99
  cpuUsageLog = cpuUsage.toFixed(2);
83
100
  setLastCpuUsage(cpuUsage);
84
- if (lastCpuUsage > peakCpu)
101
+ if (lastCpuUsage > peakCpu) {
85
102
  peakCpu = lastCpuUsage;
103
+ if (peakCpu)
104
+ log.debug(`****${RED}${BRIGHT}Cpu peak detected.${db} Peak cpu from ${CYAN}${peakCpu.toFixed(2)}%${db} to ${CYAN}${lastCpuUsage.toFixed(2)}%${db}`);
105
+ }
86
106
  cliEmitter.emit('cpu', lastCpuUsage);
87
107
  }
88
108
  prevCpus = currCpus;
89
- log.debug(`***${YELLOW}${BRIGHT}Cpu usage:${db} ${CYAN}${cpuUsageLog.padStart(6, ' ')} %${db} ${YELLOW}${BRIGHT}Memory usage:${db} rss ${CYAN}${rss}${db} heapTotal ${CYAN}${heapTotal}${db} heapUsed ${CYAN}${heapUsed}${db} external ${external} arrayBuffers ${arrayBuffers}`);
109
+ log.debug(`***${YELLOW}${BRIGHT}Cpu usage:${db} ${CYAN}${cpuUsageLog.padStart(6, ' ')}%${db} (peak ${peakCpu.toFixed(2)}%) ${YELLOW}${BRIGHT}Memory usage:${db} rss ${CYAN}${rss}${db} (peak ${formatMemoryUsage(peakRss)}) heapUsed ${CYAN}${heapUsed}${db} (peak ${formatMemoryUsage(peakHeapUsed)}) heapTotal ${CYAN}${heapTotal}${db} (peak ${formatMemoryUsage(peakHeapTotal)}) external ${external} arrayBuffers ${arrayBuffers}`);
90
110
  };
91
- interval();
92
111
  clearInterval(memoryCheckInterval);
93
112
  memoryCheckInterval = setInterval(interval, getIntParameter('memoryinterval') ?? 10 * 1000).unref();
113
+ clearTimeout(memoryPeakResetTimeout);
114
+ memoryPeakResetTimeout = setTimeout(() => {
115
+ log.debug(`****${RED}${BRIGHT}Cpu and memory peaks reset after first 5 minutes.${db}`);
116
+ peakCpu = 0;
117
+ peakRss = 0;
118
+ peakHeapUsed = 0;
119
+ peakHeapTotal = 0;
120
+ }, 5 * 60 * 1000).unref();
94
121
  }
95
122
  async function stopCpuMemoryCheck() {
96
- log.debug(`***Cpu memory check stopped. Peak cpu: ${CYAN}${peakCpu.toFixed(2)} %${db}. Peak rss: ${CYAN}${formatMemoryUsage(peakRss)}${db}.`);
123
+ log.debug(`***Cpu memory check stopped. Peak cpu: ${CYAN}${peakCpu.toFixed(2)} %${db}. Peak rss: ${CYAN}${formatMemoryUsage(peakRss)}${db}. Peak heapUsed: ${CYAN}${formatMemoryUsage(peakHeapUsed)}${db}. Peak heapTotal: ${CYAN}${formatMemoryUsage(peakHeapTotal)}${db}`);
97
124
  clearInterval(memoryCheckInterval);
125
+ clearTimeout(memoryPeakResetTimeout);
98
126
  }
99
127
  async function startInspector() {
100
128
  const { Session } = await import('node:inspector');
package/dist/frontend.js CHANGED
@@ -1,16 +1,15 @@
1
- import { createServer } from 'node:http';
2
- import https from 'node:https';
1
+ if (process.argv.includes('--loader') || process.argv.includes('-loader'))
2
+ console.log('\u001B[32mFrontend loaded.\u001B[40;0m');
3
3
  import os from 'node:os';
4
4
  import path from 'node:path';
5
- import { existsSync, promises as fs, unlinkSync } from 'node:fs';
6
5
  import EventEmitter from 'node:events';
7
- import { appendFile } from 'node:fs/promises';
8
6
  import express from 'express';
9
7
  import WebSocket, { WebSocketServer } from 'ws';
10
8
  import multer from 'multer';
11
9
  import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, YELLOW, nt, wr } from 'node-ansi-logger';
12
10
  import { Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, Lifecycle, LogDestination, Diagnostic, Time, FabricIndex } from '@matter/main';
13
- import { BridgedDeviceBasicInformation, PowerSource } from '@matter/main/clusters';
11
+ import { BridgedDeviceBasicInformation } from '@matter/main/clusters/bridged-device-basic-information';
12
+ import { PowerSource } from '@matter/main/clusters/power-source';
14
13
  import { DeviceAdvertiser, DeviceCommissioner, FabricManager } from '@matter/main/protocol';
15
14
  import { CommissioningOptions } from '@matter/main/types';
16
15
  import { MATTER_LOGGER_FILE, MATTER_STORAGE_NAME, MATTERBRIDGE_LOGGER_FILE, NODE_STORAGE_DIR, plg } from './matterbridgeTypes.js';
@@ -42,8 +41,18 @@ export class Frontend extends EventEmitter {
42
41
  }
43
42
  async msgHandler(msg) {
44
43
  if (this.server.isWorkerRequest(msg, msg.type)) {
44
+ if (!msg.id || (msg.dst !== 'all' && msg.dst !== 'frontend'))
45
+ return;
45
46
  this.log.debug(`**Received broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
46
47
  switch (msg.type) {
48
+ case 'frontend_start':
49
+ await this.start(msg.params.port);
50
+ this.server.respond({ ...msg, id: msg.id, response: { success: true } });
51
+ break;
52
+ case 'frontend_stop':
53
+ await this.stop();
54
+ this.server.respond({ ...msg, id: msg.id, response: { success: true } });
55
+ break;
47
56
  default:
48
57
  this.log.warn(`Unknown broadcast request ${CYAN}${msg.type}${wr} from ${CYAN}${msg.src}${wr}`);
49
58
  }
@@ -89,9 +98,10 @@ export class Frontend extends EventEmitter {
89
98
  this.expressApp = express();
90
99
  this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
91
100
  if (!hasParameter('ssl')) {
101
+ const http = await import('node:http');
92
102
  try {
93
103
  this.log.debug(`Creating HTTP server...`);
94
- this.httpServer = createServer(this.expressApp);
104
+ this.httpServer = http.createServer(this.expressApp);
95
105
  }
96
106
  catch (error) {
97
107
  this.log.error(`Failed to create HTTP server: ${error}`);
@@ -137,9 +147,10 @@ export class Frontend extends EventEmitter {
137
147
  let pfx;
138
148
  let passphrase;
139
149
  let httpsServerOptions = {};
140
- if (existsSync(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12'))) {
150
+ const fs = await import('node:fs');
151
+ if (fs.existsSync(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12'))) {
141
152
  try {
142
- pfx = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12'));
153
+ pfx = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12'));
143
154
  this.log.info(`Loaded p12 certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12')}`);
144
155
  }
145
156
  catch (error) {
@@ -148,7 +159,7 @@ export class Frontend extends EventEmitter {
148
159
  return;
149
160
  }
150
161
  try {
151
- passphrase = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pass'), 'utf8');
162
+ passphrase = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pass'), 'utf8');
152
163
  passphrase = passphrase.trim();
153
164
  this.log.info(`Loaded p12 passphrase file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pass')}`);
154
165
  }
@@ -161,7 +172,7 @@ export class Frontend extends EventEmitter {
161
172
  }
162
173
  else {
163
174
  try {
164
- cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
175
+ cert = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
165
176
  this.log.info(`Loaded certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem')}`);
166
177
  }
167
178
  catch (error) {
@@ -170,7 +181,7 @@ export class Frontend extends EventEmitter {
170
181
  return;
171
182
  }
172
183
  try {
173
- key = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/key.pem'), 'utf8');
184
+ key = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/key.pem'), 'utf8');
174
185
  this.log.info(`Loaded key file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/key.pem')}`);
175
186
  }
176
187
  catch (error) {
@@ -179,7 +190,7 @@ export class Frontend extends EventEmitter {
179
190
  return;
180
191
  }
181
192
  try {
182
- ca = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem'), 'utf8');
193
+ ca = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem'), 'utf8');
183
194
  fullChain = `${cert}\n${ca}`;
184
195
  this.log.info(`Loaded CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem')}`);
185
196
  }
@@ -192,6 +203,7 @@ export class Frontend extends EventEmitter {
192
203
  httpsServerOptions.requestCert = true;
193
204
  httpsServerOptions.rejectUnauthorized = true;
194
205
  }
206
+ const https = await import('node:https');
195
207
  try {
196
208
  this.log.debug(`Creating HTTPS server...`);
197
209
  this.httpsServer = https.createServer(httpsServerOptions, this.expressApp);
@@ -365,7 +377,8 @@ export class Frontend extends EventEmitter {
365
377
  this.expressApp.get('/api/view-mblog', async (req, res) => {
366
378
  this.log.debug('The frontend sent /api/view-mblog');
367
379
  try {
368
- const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE), 'utf8');
380
+ const fs = await import('node:fs');
381
+ const data = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE), 'utf8');
369
382
  res.type('text/plain');
370
383
  res.send(data);
371
384
  }
@@ -377,7 +390,8 @@ export class Frontend extends EventEmitter {
377
390
  this.expressApp.get('/api/view-mjlog', async (req, res) => {
378
391
  this.log.debug('The frontend sent /api/view-mjlog');
379
392
  try {
380
- const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, MATTER_LOGGER_FILE), 'utf8');
393
+ const fs = await import('node:fs');
394
+ const data = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, MATTER_LOGGER_FILE), 'utf8');
381
395
  res.type('text/plain');
382
396
  res.send(data);
383
397
  }
@@ -403,11 +417,12 @@ export class Frontend extends EventEmitter {
403
417
  if (device.serverNode)
404
418
  serverNodes.push(device.serverNode);
405
419
  }
406
- if (existsSync(path.join(this.matterbridge.matterbridgeDirectory, 'diagnostic.log')))
407
- unlinkSync(path.join(this.matterbridge.matterbridgeDirectory, 'diagnostic.log'));
420
+ const fs = await import('node:fs');
421
+ if (fs.existsSync(path.join(this.matterbridge.matterbridgeDirectory, 'diagnostic.log')))
422
+ fs.unlinkSync(path.join(this.matterbridge.matterbridgeDirectory, 'diagnostic.log'));
408
423
  const diagnosticDestination = LogDestination({ name: 'diagnostic', level: MatterLogLevel.INFO, format: MatterLogFormat.formats.plain });
409
424
  diagnosticDestination.write = async (text, _message) => {
410
- await appendFile(path.join(this.matterbridge.matterbridgeDirectory, 'diagnostic.log'), text + '\n', { encoding: 'utf8' });
425
+ await fs.promises.appendFile(path.join(this.matterbridge.matterbridgeDirectory, 'diagnostic.log'), text + '\n', { encoding: 'utf8' });
411
426
  };
412
427
  Logger.destinations.diagnostic = diagnosticDestination;
413
428
  if (!diagnosticDestination.context) {
@@ -423,7 +438,8 @@ export class Frontend extends EventEmitter {
423
438
  delete Logger.destinations.diagnostic;
424
439
  await wait(500);
425
440
  try {
426
- const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'diagnostic.log'), 'utf8');
441
+ const fs = await import('node:fs');
442
+ const data = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'diagnostic.log'), 'utf8');
427
443
  res.type('text/plain');
428
444
  res.send(data.slice(29));
429
445
  }
@@ -435,7 +451,8 @@ export class Frontend extends EventEmitter {
435
451
  this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
436
452
  this.log.debug('The frontend sent /api/shellyviewsystemlog');
437
453
  try {
438
- const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), 'utf8');
454
+ const fs = await import('node:fs');
455
+ const data = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), 'utf8');
439
456
  res.type('text/plain');
440
457
  res.send(data);
441
458
  }
@@ -446,13 +463,14 @@ export class Frontend extends EventEmitter {
446
463
  });
447
464
  this.expressApp.get('/api/download-mblog', async (req, res) => {
448
465
  this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE)}`);
466
+ const fs = await import('node:fs');
449
467
  try {
450
- await fs.access(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE), fs.constants.F_OK);
451
- const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE), 'utf8');
452
- await fs.writeFile(path.join(os.tmpdir(), MATTERBRIDGE_LOGGER_FILE), data, 'utf-8');
468
+ await fs.promises.access(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE), fs.constants.F_OK);
469
+ const data = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE), 'utf8');
470
+ await fs.promises.writeFile(path.join(os.tmpdir(), MATTERBRIDGE_LOGGER_FILE), data, 'utf-8');
453
471
  }
454
472
  catch (error) {
455
- await fs.writeFile(path.join(os.tmpdir(), MATTERBRIDGE_LOGGER_FILE), 'Enable the matterbridge log on file in the settings to download the matterbridge log.', 'utf-8');
473
+ await fs.promises.writeFile(path.join(os.tmpdir(), MATTERBRIDGE_LOGGER_FILE), 'Enable the matterbridge log on file in the settings to download the matterbridge log.', 'utf-8');
456
474
  this.log.debug(`Error in /api/download-mblog: ${error instanceof Error ? error.message : error}`);
457
475
  }
458
476
  res.type('text/plain');
@@ -465,13 +483,14 @@ export class Frontend extends EventEmitter {
465
483
  });
466
484
  this.expressApp.get('/api/download-mjlog', async (req, res) => {
467
485
  this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE)}`);
486
+ const fs = await import('node:fs');
468
487
  try {
469
- await fs.access(path.join(this.matterbridge.matterbridgeDirectory, MATTER_LOGGER_FILE), fs.constants.F_OK);
470
- const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, MATTER_LOGGER_FILE), 'utf8');
471
- await fs.writeFile(path.join(os.tmpdir(), MATTER_LOGGER_FILE), data, 'utf-8');
488
+ await fs.promises.access(path.join(this.matterbridge.matterbridgeDirectory, MATTER_LOGGER_FILE), fs.constants.F_OK);
489
+ const data = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, MATTER_LOGGER_FILE), 'utf8');
490
+ await fs.promises.writeFile(path.join(os.tmpdir(), MATTER_LOGGER_FILE), data, 'utf-8');
472
491
  }
473
492
  catch (error) {
474
- await fs.writeFile(path.join(os.tmpdir(), MATTER_LOGGER_FILE), 'Enable the matter log on file in the settings to download the matter log.', 'utf-8');
493
+ await fs.promises.writeFile(path.join(os.tmpdir(), MATTER_LOGGER_FILE), 'Enable the matter log on file in the settings to download the matter log.', 'utf-8');
475
494
  this.log.debug(`Error in /api/download-mblog: ${error instanceof Error ? error.message : error}`);
476
495
  }
477
496
  res.type('text/plain');
@@ -484,13 +503,14 @@ export class Frontend extends EventEmitter {
484
503
  });
485
504
  this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
486
505
  this.log.debug('The frontend sent /api/shellydownloadsystemlog');
506
+ const fs = await import('node:fs');
487
507
  try {
488
- await fs.access(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), fs.constants.F_OK);
489
- const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), 'utf8');
490
- await fs.writeFile(path.join(os.tmpdir(), 'shelly.log'), data, 'utf-8');
508
+ await fs.promises.access(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), fs.constants.F_OK);
509
+ const data = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), 'utf8');
510
+ await fs.promises.writeFile(path.join(os.tmpdir(), 'shelly.log'), data, 'utf-8');
491
511
  }
492
512
  catch (error) {
493
- await fs.writeFile(path.join(os.tmpdir(), 'shelly.log'), 'Create the Shelly system log before downloading it.', 'utf-8');
513
+ await fs.promises.writeFile(path.join(os.tmpdir(), 'shelly.log'), 'Create the Shelly system log before downloading it.', 'utf-8');
494
514
  this.log.debug(`Error in /api/shellydownloadsystemlog: ${error instanceof Error ? error.message : error}`);
495
515
  }
496
516
  res.type('text/plain');
@@ -561,7 +581,8 @@ export class Frontend extends EventEmitter {
561
581
  this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
562
582
  const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
563
583
  try {
564
- await fs.rename(file.path, filePath);
584
+ const fs = await import('node:fs');
585
+ await fs.promises.rename(file.path, filePath);
565
586
  this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
566
587
  if (filename.endsWith('.tgz')) {
567
588
  const { spawnCommand } = await import('./utils/spawn.js');
@@ -684,6 +705,8 @@ export class Frontend extends EventEmitter {
684
705
  return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: info };
685
706
  }
686
707
  getReachability(device) {
708
+ if (this.matterbridge.hasCleanupStarted)
709
+ return false;
687
710
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
688
711
  return false;
689
712
  if (device.hasClusterServer(BridgedDeviceBasicInformation.Cluster.id))
@@ -695,6 +718,8 @@ export class Frontend extends EventEmitter {
695
718
  return false;
696
719
  }
697
720
  getPowerSource(endpoint) {
721
+ if (this.matterbridge.hasCleanupStarted)
722
+ return;
698
723
  if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
699
724
  return undefined;
700
725
  const powerSource = (device) => {
@@ -717,6 +742,8 @@ export class Frontend extends EventEmitter {
717
742
  }
718
743
  }
719
744
  getClusterTextFromDevice(device) {
745
+ if (this.matterbridge.hasCleanupStarted)
746
+ return '';
720
747
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
721
748
  return '';
722
749
  const getUserLabel = (device) => {
@@ -832,6 +859,8 @@ export class Frontend extends EventEmitter {
832
859
  return attributes.trimStart().trimEnd();
833
860
  }
834
861
  getPlugins() {
862
+ if (this.matterbridge.hasCleanupStarted)
863
+ return [];
835
864
  const plugins = [];
836
865
  for (const plugin of this.matterbridge.plugins.array()) {
837
866
  plugins.push({
@@ -865,6 +894,8 @@ export class Frontend extends EventEmitter {
865
894
  return plugins;
866
895
  }
867
896
  getDevices(pluginName) {
897
+ if (this.matterbridge.hasCleanupStarted)
898
+ return [];
868
899
  const devices = [];
869
900
  for (const device of this.matterbridge.devices.array()) {
870
901
  if (pluginName && pluginName !== device.plugin)
@@ -1453,7 +1484,7 @@ export class Frontend extends EventEmitter {
1453
1484
  case 'setmjlogfile':
1454
1485
  if (isValidBoolean(data.params.value)) {
1455
1486
  this.log.debug('Matter file log:', data.params.value);
1456
- this.matterbridge.fileLogger = data.params.value;
1487
+ this.matterbridge.matterFileLogger = data.params.value;
1457
1488
  await this.matterbridge.nodeContext?.set('matterFileLog', data.params.value);
1458
1489
  if (data.params.value) {
1459
1490
  this.matterbridge.matterLog.logFilePath = path.join(this.matterbridge.matterbridgeDirectory, MATTER_LOGGER_FILE);
package/dist/index.js CHANGED
@@ -1,3 +1,5 @@
1
+ if (process.argv.includes('--loader') || process.argv.includes('-loader'))
2
+ console.log('\u001B[32mIndex loaded.\u001B[40;0m');
1
3
  export * from './matterbridge.js';
2
4
  export * from './matterbridgeTypes.js';
3
5
  export * from './matterbridgeEndpoint.js';
@@ -1,3 +1,5 @@
1
+ if (process.argv.includes('--loader') || process.argv.includes('-loader'))
2
+ console.log('\u001B[32mMatterbridge loaded.\u001B[40;0m');
1
3
  import os from 'node:os';
2
4
  import path from 'node:path';
3
5
  import { promises as fs } from 'node:fs';
@@ -1771,14 +1773,12 @@ export class Matterbridge extends EventEmitter {
1771
1773
  });
1772
1774
  }
1773
1775
  for (const child of device.getChildEndpoints()) {
1774
- for (const sub of subscriptions) {
1775
- if (child.hasAttributeServer(sub.cluster, sub.attribute)) {
1776
- this.log.debug(`Subscribing to child endpoint ${or}${child.id}${db}:${or}${child.number}${db} attribute ${dev}${sub.cluster}${db}.${dev}${sub.attribute}${db} changes...`);
1777
- await child.subscribeAttribute(sub.cluster, sub.attribute, (value) => {
1778
- this.log.debug(`Bridged child endpoint ${or}${child.id}${db}:${or}${child.number}${db} attribute ${dev}${sub.cluster}${db}.${dev}${sub.attribute}${db} changed to ${CYAN}${value}${db}`);
1779
- this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, child.number, child.id, sub.cluster, sub.attribute, value);
1780
- });
1781
- }
1776
+ if (child.hasAttributeServer(sub.cluster, sub.attribute)) {
1777
+ this.log.debug(`Subscribing to child endpoint ${or}${child.id}${db}:${or}${child.number}${db} attribute ${dev}${sub.cluster}${db}.${dev}${sub.attribute}${db} changes...`);
1778
+ await child.subscribeAttribute(sub.cluster, sub.attribute, (value) => {
1779
+ this.log.debug(`Bridged child endpoint ${or}${child.id}${db}:${or}${child.number}${db} attribute ${dev}${sub.cluster}${db}.${dev}${sub.attribute}${db} changed to ${CYAN}${value}${db}`);
1780
+ this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, child.number, child.id, sub.cluster, sub.attribute, value);
1781
+ });
1782
1782
  }
1783
1783
  }
1784
1784
  }
@@ -1,3 +1,5 @@
1
+ if (process.argv.includes('--loader') || process.argv.includes('-loader'))
2
+ console.log('\u001B[32mMatterbridgePlatform loaded.\u001B[40;0m');
1
3
  import path from 'node:path';
2
4
  import { Descriptor } from '@matter/main/clusters/descriptor';
3
5
  import { BridgedDeviceBasicInformation } from '@matter/main/clusters/bridged-device-basic-information';
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "matterbridge",
3
- "version": "3.3.1-dev-20251007-4e5eaac",
3
+ "version": "3.3.1-dev-20251008-e61b8db",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "matterbridge",
9
- "version": "3.3.1-dev-20251007-4e5eaac",
9
+ "version": "3.3.1-dev-20251008-e61b8db",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "@matter/main": "0.15.5",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "matterbridge",
3
- "version": "3.3.1-dev-20251007-4e5eaac",
3
+ "version": "3.3.1-dev-20251008-e61b8db",
4
4
  "description": "Matterbridge plugin manager for Matter",
5
5
  "author": "https://github.com/Luligu",
6
6
  "license": "Apache-2.0",