matterbridge 3.3.3-dev-20251019-a73923c → 3.3.4-dev-20251020-4d2dd49

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
@@ -23,6 +23,27 @@ Advantages:
23
23
  - isolation between threads;
24
24
  - individual plugin isolation in childbridge mode;
25
25
 
26
+ ## [3.3.4] - Not released
27
+
28
+ ### Breaking Changes
29
+
30
+ - [nodejs]: Matterbridge will not start if the Node.js version is less then 20.x.x.
31
+
32
+ ### Added
33
+
34
+ - [frontend]: Added debounce to MatterSettings.
35
+ - [cli]: Bumped `cli` version to 3.0.0 with backport of Traker and Inspector from thread module.
36
+
37
+ ### Changed
38
+
39
+ - [package]: Updated dependencies.
40
+ - [matter]: Bumped `matter.js` version to 0.15.6. Thanks matter.js!
41
+ - [frontend]: Bumped `frontend` version to 3.2.3.
42
+
43
+ <a href="https://www.buymeacoffee.com/luligugithub">
44
+ <img src="bmc-button.svg" alt="Buy me a coffee" width="80">
45
+ </a>
46
+
26
47
  ## [3.3.3] - 2025-10-18
27
48
 
28
49
  ### Added
package/dist/cli.js CHANGED
@@ -1,331 +1,66 @@
1
1
  if (process.argv.includes('--loader') || process.argv.includes('-loader'))
2
2
  console.log('\u001B[32mCLI loaded.\u001B[40;0m');
3
- import os from 'node:os';
4
- import { inspect } from 'node:util';
5
- import { AnsiLogger, BRIGHT, CYAN, db, RED, YELLOW } from 'node-ansi-logger';
6
- import { cliEmitter, setLastOsCpuUsage, setLastProcessCpuUsage } from './cliEmitter.js';
7
- import { history, historyIndex, historySize, setHistoryIndex } from './cliHistory.js';
8
- import { getIntParameter, hasParameter } from './utils/commandLine.js';
3
+ import { AnsiLogger } from 'node-ansi-logger';
4
+ import { cliEmitter } from './cliEmitter.js';
5
+ import { hasParameter, hasAnyParameter } from './utils/commandLine.js';
9
6
  import { Matterbridge } from './matterbridge.js';
7
+ import { inspectError } from './utils/error.js';
8
+ import { Tracker } from './utils/tracker.js';
9
+ import { Inspector } from './utils/inspector.js';
10
10
  export let instance;
11
- let session;
12
- let snapshotInterval;
13
- const trace = hasParameter('trace');
14
- let memoryCheckInterval;
15
- const memoryCheckIntervalMs = getIntParameter('memoryinterval') ?? 10 * 1000;
16
- let memoryPeakResetTimeout;
17
- let prevCpus;
18
- let prevProcessCpu;
19
- let peakCpu = 0;
20
- let peakProcessCpu = 0;
21
- let peakRss = 0;
22
- let peakHeapUsed = 0;
23
- let peakHeapTotal = 0;
24
- let peakExternal = 0;
25
- let peakArrayBuffers = 0;
11
+ export const tracker = new Tracker('Cli', true, true);
12
+ export const inspector = new Inspector('Cli', true, true);
26
13
  const log = new AnsiLogger({ logName: 'Cli', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
27
- const formatCpuUsage = (percent) => {
28
- return `${percent.toFixed(2).padStart(5, ' ')} %`;
29
- };
30
- const formatMemoryUsage = (bytes) => {
31
- if (bytes >= 1024 ** 3) {
32
- return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
33
- }
34
- else if (bytes >= 1024 ** 2) {
35
- return `${(bytes / 1024 ** 2).toFixed(2)} MB`;
36
- }
37
- else {
38
- return `${(bytes / 1024).toFixed(2)} KB`;
39
- }
40
- };
41
- const formatOsUpTime = (seconds) => {
42
- if (seconds >= 86400) {
43
- const days = Math.floor(seconds / 86400);
44
- return `${days} day${days !== 1 ? 's' : ''}`;
45
- }
46
- if (seconds >= 3600) {
47
- const hours = Math.floor(seconds / 3600);
48
- return `${hours} hour${hours !== 1 ? 's' : ''}`;
49
- }
50
- if (seconds >= 60) {
51
- const minutes = Math.floor(seconds / 60);
52
- return `${minutes} minute${minutes !== 1 ? 's' : ''}`;
53
- }
54
- return `${seconds} second${seconds !== 1 ? 's' : ''}`;
55
- };
56
- async function startCpuMemoryCheck() {
14
+ function startCpuMemoryCheck() {
15
+ log.debug(`Cpu memory check starting...`);
16
+ tracker.start();
17
+ tracker.on('uptime', (os, process) => {
18
+ cliEmitter.emit('uptime', tracker.formatOsUpTime(Math.floor(os)), tracker.formatOsUpTime(Math.floor(process)));
19
+ });
20
+ tracker.on('snapshot', (snapshot) => {
21
+ cliEmitter.emit('memory', tracker.formatBytes(snapshot.totalMemory), tracker.formatBytes(snapshot.freeMemory), tracker.formatBytes(snapshot.rss), tracker.formatBytes(snapshot.heapTotal), tracker.formatBytes(snapshot.heapUsed), tracker.formatBytes(snapshot.external), tracker.formatBytes(snapshot.arrayBuffers));
22
+ cliEmitter.emit('cpu', snapshot.osCpu, snapshot.processCpu);
23
+ });
57
24
  log.debug(`Cpu memory check started`);
58
- prevCpus = os.cpus();
59
- prevProcessCpu = process.cpuUsage();
60
- const interval = () => {
61
- const systemUptime = formatOsUpTime(Math.floor(os.uptime()));
62
- const processUptime = formatOsUpTime(Math.floor(process.uptime()));
63
- cliEmitter.emit('uptime', systemUptime, processUptime);
64
- const totalMemory = formatMemoryUsage(os.totalmem());
65
- const freeMemory = formatMemoryUsage(os.freemem());
66
- const memoryUsageRaw = process.memoryUsage();
67
- const rss = formatMemoryUsage(memoryUsageRaw.rss);
68
- const heapTotal = formatMemoryUsage(memoryUsageRaw.heapTotal);
69
- const heapUsed = formatMemoryUsage(memoryUsageRaw.heapUsed);
70
- const external = formatMemoryUsage(memoryUsageRaw.external);
71
- const arrayBuffers = formatMemoryUsage(memoryUsageRaw.arrayBuffers);
72
- if (memoryUsageRaw.rss > peakRss) {
73
- if (peakRss && trace)
74
- log.debug(`****${RED}${BRIGHT}Rss peak detected.${db} Peak rss from ${CYAN}${formatMemoryUsage(peakRss)}${db} to ${CYAN}${formatMemoryUsage(memoryUsageRaw.rss)}${db}`);
75
- peakRss = memoryUsageRaw.rss;
76
- }
77
- if (memoryUsageRaw.heapUsed > peakHeapUsed) {
78
- if (peakHeapUsed && trace)
79
- log.debug(`****${RED}${BRIGHT}HeapUsed peak detected.${db} Peak heapUsed from ${CYAN}${formatMemoryUsage(peakHeapUsed)}${db} to ${CYAN}${formatMemoryUsage(memoryUsageRaw.heapUsed)}${db}`);
80
- peakHeapUsed = memoryUsageRaw.heapUsed;
81
- }
82
- if (memoryUsageRaw.heapTotal > peakHeapTotal) {
83
- if (peakHeapTotal && trace)
84
- log.debug(`****${RED}${BRIGHT}HeapTotal peak detected.${db} Peak heapTotal from ${CYAN}${formatMemoryUsage(peakHeapTotal)}${db} to ${CYAN}${formatMemoryUsage(memoryUsageRaw.heapTotal)}${db}`);
85
- peakHeapTotal = memoryUsageRaw.heapTotal;
86
- }
87
- if (memoryUsageRaw.external > peakExternal) {
88
- if (peakExternal && trace)
89
- log.debug(`****${RED}${BRIGHT}External peak detected.${db} Peak external from ${CYAN}${formatMemoryUsage(peakExternal)}${db} to ${CYAN}${formatMemoryUsage(memoryUsageRaw.external)}${db}`);
90
- peakExternal = memoryUsageRaw.external;
91
- }
92
- if (memoryUsageRaw.arrayBuffers > peakArrayBuffers) {
93
- if (peakArrayBuffers && trace)
94
- log.debug(`****${RED}${BRIGHT}ArrayBuffers peak detected.${db} Peak arrayBuffers from ${CYAN}${formatMemoryUsage(peakArrayBuffers)}${db} to ${CYAN}${formatMemoryUsage(memoryUsageRaw.arrayBuffers)}${db}`);
95
- peakArrayBuffers = memoryUsageRaw.arrayBuffers;
96
- }
97
- cliEmitter.emit('memory', totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers);
98
- const currCpus = os.cpus();
99
- if (currCpus.length !== prevCpus.length) {
100
- prevCpus = currCpus;
101
- log.debug(`Cpu check length failed, resetting previous cpus`);
102
- return;
103
- }
104
- let totalIdle = 0, totalTick = 0;
105
- prevCpus.forEach((prevCpu, i) => {
106
- const currCpu = currCpus[i];
107
- const idleDiff = currCpu.times.idle - prevCpu.times.idle;
108
- const totalDiff = Object.keys(currCpu.times).reduce((acc, key) => acc + (currCpu.times[key] - prevCpu.times[key]), 0);
109
- totalIdle += idleDiff;
110
- totalTick += totalDiff;
111
- });
112
- const osCpuUsage = 100 - (totalIdle / totalTick) * 100;
113
- if (totalTick === 0 || isNaN(osCpuUsage) || !isFinite(osCpuUsage) || osCpuUsage <= 0) {
114
- log.debug(`Cpu check failed, using previous cpus`);
115
- }
116
- else {
117
- setLastOsCpuUsage(osCpuUsage);
118
- if (osCpuUsage > peakCpu) {
119
- peakCpu = osCpuUsage;
120
- if (peakCpu && trace)
121
- log.debug(`****${RED}${BRIGHT}Cpu peak detected.${db} Peak cpu from ${CYAN}${formatCpuUsage(peakCpu)}${db} to ${CYAN}${formatCpuUsage(osCpuUsage)}${db}`);
122
- }
123
- }
124
- prevCpus = currCpus;
125
- const diff = process.cpuUsage(prevProcessCpu);
126
- const userMs = diff.user / 1000;
127
- const systemMs = diff.system / 1000;
128
- const totalMs = userMs + systemMs;
129
- const processCpuUsage = Number((((totalMs / memoryCheckIntervalMs) * 100) / currCpus.length).toFixed(2));
130
- if (processCpuUsage > peakProcessCpu) {
131
- peakProcessCpu = processCpuUsage;
132
- if (peakProcessCpu && trace)
133
- log.debug(`****${RED}${BRIGHT}Process cpu peak detected.${db} Peak process cpu from ${CYAN}${formatCpuUsage(peakProcessCpu)}${db} to ${CYAN}${formatCpuUsage(processCpuUsage)}${db}`);
134
- }
135
- prevProcessCpu = process.cpuUsage();
136
- setLastProcessCpuUsage(processCpuUsage);
137
- cliEmitter.emit('cpu', osCpuUsage, processCpuUsage);
138
- const entry = history[historyIndex];
139
- entry.timestamp = Date.now();
140
- entry.cpu = osCpuUsage;
141
- entry.peakCpu = peakCpu;
142
- entry.processCpu = processCpuUsage;
143
- entry.peakProcessCpu = peakProcessCpu;
144
- entry.rss = memoryUsageRaw.rss;
145
- entry.peakRss = peakRss;
146
- entry.heapUsed = memoryUsageRaw.heapUsed;
147
- entry.peakHeapUsed = peakHeapUsed;
148
- entry.heapTotal = memoryUsageRaw.heapTotal;
149
- entry.peakHeapTotal = peakHeapTotal;
150
- entry.external = memoryUsageRaw.external;
151
- entry.peakExternal = peakExternal;
152
- entry.arrayBuffers = memoryUsageRaw.arrayBuffers;
153
- entry.peakArrayBuffers = peakArrayBuffers;
154
- setHistoryIndex((historyIndex + 1) % historySize);
155
- if (trace)
156
- log.debug(`***${YELLOW}${BRIGHT}Host cpu:${db} ` +
157
- `${CYAN}${formatCpuUsage(osCpuUsage)}${db} (peak ${formatCpuUsage(peakCpu)}) ` +
158
- `${YELLOW}${BRIGHT}Process cpu:${db} ` +
159
- `${CYAN}${formatCpuUsage(processCpuUsage)}${db} (peak ${formatCpuUsage(peakProcessCpu)}) ` +
160
- `${YELLOW}${BRIGHT}Process memory:${db} ` +
161
- `rss ${CYAN}${rss}${db} (peak ${formatMemoryUsage(peakRss)}) ` +
162
- `heapUsed ${CYAN}${heapUsed}${db} (peak ${formatMemoryUsage(peakHeapUsed)}) ` +
163
- `heapTotal ${CYAN}${heapTotal}${db} (peak ${formatMemoryUsage(peakHeapTotal)}) ` +
164
- `external ${CYAN}${external}${db} (peak ${formatMemoryUsage(peakExternal)}) ` +
165
- `arrayBuffers ${CYAN}${arrayBuffers}${db} (peak ${formatMemoryUsage(peakArrayBuffers)})`);
166
- };
167
- clearInterval(memoryCheckInterval);
168
- memoryCheckInterval = setInterval(interval, memoryCheckIntervalMs).unref();
169
- clearTimeout(memoryPeakResetTimeout);
170
- memoryPeakResetTimeout = setTimeout(() => {
171
- if (trace)
172
- log.debug(`****${RED}${BRIGHT}Cpu and memory peaks reset after first 5 minutes.${db}`);
173
- peakCpu = 0;
174
- peakProcessCpu = 0;
175
- peakRss = 0;
176
- peakHeapUsed = 0;
177
- peakHeapTotal = 0;
178
- }, 5 * 60 * 1000).unref();
179
25
  }
180
- async function stopCpuMemoryCheck() {
181
- if (trace) {
182
- log.debug(`***Cpu memory check stopped. ` +
183
- `Peak cpu: ${CYAN}${peakCpu.toFixed(2)} %${db}. ` +
184
- `Peak rss: ${CYAN}${formatMemoryUsage(peakRss)}${db}. ` +
185
- `Peak heapUsed: ${CYAN}${formatMemoryUsage(peakHeapUsed)}${db}. ` +
186
- `Peak heapTotal: ${CYAN}${formatMemoryUsage(peakHeapTotal)}${db}. ` +
187
- `Peak external: ${CYAN}${formatMemoryUsage(peakExternal)}${db}. ` +
188
- `Peak arrayBuffers: ${CYAN}${formatMemoryUsage(peakArrayBuffers)}${db}.`);
189
- }
190
- clearInterval(memoryCheckInterval);
191
- clearTimeout(memoryPeakResetTimeout);
26
+ function stopCpuMemoryCheck() {
27
+ log.debug(`Cpu memory check stopping...`);
28
+ tracker.stop();
29
+ tracker.removeAllListeners();
30
+ log.debug(`Cpu memory check stopped`);
192
31
  }
193
32
  async function startInspector() {
194
- const { Session } = await import('node:inspector');
195
- const { mkdirSync } = await import('node:fs');
196
- log.debug(`***Starting heap sampling...`);
197
- mkdirSync('heap_profile', { recursive: true });
198
- try {
199
- session = new Session();
200
- session.connect();
201
- await new Promise((resolve, reject) => {
202
- session?.post('HeapProfiler.startSampling', (err) => (err ? reject(err) : resolve()));
203
- });
204
- log.debug(`***Started heap sampling`);
205
- const interval = getIntParameter('snapshotinterval');
206
- if (interval && interval >= 30000) {
207
- log.debug(`***Started heap snapshot interval of ${CYAN}${interval}${db} ms`);
208
- clearInterval(snapshotInterval);
209
- snapshotInterval = setInterval(async () => {
210
- log.debug(`Run heap snapshot interval`);
211
- await takeHeapSnapshot();
212
- }, interval).unref();
213
- }
214
- }
215
- catch (err) {
216
- log.error(`***Failed to start heap sampling: ${err instanceof Error ? err.message : err}`);
217
- session?.disconnect();
218
- session = undefined;
219
- return;
220
- }
33
+ await inspector.start();
221
34
  }
222
35
  async function stopInspector() {
223
- const { writeFileSync } = await import('node:fs');
224
- const path = await import('node:path');
225
- log.debug(`***Stopping heap sampling...`);
226
- if (snapshotInterval) {
227
- log.debug(`***Clearing heap snapshot interval...`);
228
- clearInterval(snapshotInterval);
229
- await takeHeapSnapshot();
230
- }
231
- if (!session) {
232
- log.error('***No active inspector session.');
233
- return;
234
- }
235
- try {
236
- const result = await new Promise((resolve, reject) => {
237
- session?.post('HeapProfiler.stopSampling', (err, result) => (err ? reject(err) : resolve(result)));
238
- });
239
- const profile = JSON.stringify(result.profile);
240
- const filename = path.join('heap_profile', `Heap-profile-${new Date().toISOString().replace(/[:]/g, '-')}.heapprofile`);
241
- writeFileSync(filename, profile);
242
- log.debug(`***Heap sampling profile saved to ${CYAN}${filename}${db}`);
243
- }
244
- catch (err) {
245
- log.error(`***Failed to stop heap sampling: ${err instanceof Error ? err.message : err}`);
246
- }
247
- finally {
248
- session.disconnect();
249
- session = undefined;
250
- log.debug(`***Stopped heap sampling`);
251
- }
36
+ await inspector.stop();
252
37
  }
253
38
  async function takeHeapSnapshot() {
254
- const { writeFileSync } = await import('node:fs');
255
- const path = await import('node:path');
256
- const filename = path.join('heap_profile', `Heap-snapshot-${new Date().toISOString().replace(/[:]/g, '-')}.heapsnapshot`);
257
- if (!session) {
258
- log.error('No active inspector session.');
259
- return;
260
- }
261
- triggerGarbageCollection();
262
- log.debug(`Taking heap snapshot...`);
263
- const chunks = [];
264
- const chunksListener = (notification) => {
265
- chunks.push(Buffer.from(notification.params.chunk));
266
- };
267
- session.on('HeapProfiler.addHeapSnapshotChunk', chunksListener);
268
- await new Promise((resolve) => {
269
- session?.post('HeapProfiler.takeHeapSnapshot', (err) => {
270
- if (!err) {
271
- session?.off('HeapProfiler.addHeapSnapshotChunk', chunksListener);
272
- writeFileSync(filename, Buffer.concat(chunks));
273
- chunks.length = 0;
274
- log.debug(`***Heap sampling snapshot saved to ${CYAN}${filename}${db}`);
275
- triggerGarbageCollection();
276
- resolve();
277
- }
278
- else {
279
- session?.off('HeapProfiler.addHeapSnapshotChunk', chunksListener);
280
- chunks.length = 0;
281
- log.error(`***Failed to take heap snapshot: ${err instanceof Error ? err.message : err}`);
282
- triggerGarbageCollection();
283
- resolve();
284
- }
285
- });
286
- });
39
+ await inspector.takeHeapSnapshot();
287
40
  }
288
41
  function triggerGarbageCollection() {
289
- if (global.gc && typeof global.gc === 'function') {
290
- try {
291
- global.gc({ type: 'major', execution: 'sync' });
292
- }
293
- catch {
294
- global.gc();
295
- }
296
- log.debug('Manual garbage collection triggered via global.gc().');
297
- }
298
- else {
299
- log.debug('Garbage collection is not exposed. Start Node.js with --expose-gc to enable manual GC.');
300
- }
42
+ inspector.runGarbageCollector();
301
43
  }
302
44
  function registerHandlers() {
303
45
  log.debug('Registering event handlers...');
304
- if (instance)
305
- instance.on('shutdown', async () => shutdown());
306
- if (instance)
307
- instance.on('restart', async () => restart());
308
- if (instance)
309
- instance.on('update', async () => update());
310
- if (instance)
311
- instance.on('startmemorycheck', async () => start());
312
- if (instance)
313
- instance.on('stopmemorycheck', async () => stop());
314
- if (instance)
315
- instance.on('startinspector', async () => startInspector());
316
- if (instance)
317
- instance.on('stopinspector', async () => stopInspector());
318
- if (instance)
319
- instance.on('takeheapsnapshot', async () => takeHeapSnapshot());
320
- if (instance)
321
- instance.on('triggergarbagecollection', async () => triggerGarbageCollection());
46
+ if (!instance)
47
+ return;
48
+ instance.on('shutdown', () => shutdown());
49
+ instance.on('restart', () => restart());
50
+ instance.on('update', () => update());
51
+ instance.on('startmemorycheck', () => start());
52
+ instance.on('stopmemorycheck', () => stop());
53
+ instance.on('startinspector', () => startInspector());
54
+ instance.on('stopinspector', () => stopInspector());
55
+ instance.on('takeheapsnapshot', () => takeHeapSnapshot());
56
+ instance.on('triggergarbagecollection', () => triggerGarbageCollection());
322
57
  log.debug('Registered event handlers');
323
58
  }
324
59
  async function shutdown() {
325
60
  log.debug('Received shutdown event, exiting...');
326
61
  if (hasParameter('inspect'))
327
62
  await stopInspector();
328
- await stopCpuMemoryCheck();
63
+ stopCpuMemoryCheck();
329
64
  cliEmitter.emit('shutdown');
330
65
  process.exit(0);
331
66
  }
@@ -339,17 +74,17 @@ async function update() {
339
74
  instance = await Matterbridge.loadInstance(true);
340
75
  registerHandlers();
341
76
  }
342
- async function start() {
77
+ function start() {
343
78
  log.debug('Received start memory check event');
344
- await startCpuMemoryCheck();
79
+ startCpuMemoryCheck();
345
80
  }
346
- async function stop() {
81
+ function stop() {
347
82
  log.debug('Received stop memory check event');
348
- await stopCpuMemoryCheck();
83
+ stopCpuMemoryCheck();
349
84
  }
350
85
  async function main() {
351
86
  log.debug(`Cli main() started`);
352
- await startCpuMemoryCheck();
87
+ startCpuMemoryCheck();
353
88
  if (hasParameter('inspect'))
354
89
  await startInspector();
355
90
  log.debug(`***Matterbridge.loadInstance(true) called`);
@@ -361,11 +96,72 @@ async function main() {
361
96
  else {
362
97
  registerHandlers();
363
98
  cliEmitter.emit('ready');
99
+ log.debug(`Cli main() ready`);
364
100
  }
365
101
  }
366
- process.title = 'matterbridge';
102
+ if (hasAnyParameter('help', 'h'))
103
+ help();
104
+ if (hasAnyParameter('version', 'v'))
105
+ await version();
367
106
  main().catch((error) => {
368
- const errorMessage = error instanceof Error ? error.message : error;
369
- const errorInspect = inspect(error, { depth: 10 });
370
- log.error(`Matterbridge.loadInstance() failed with error: ${errorMessage}\nstack: ${errorInspect}`);
107
+ inspectError(log, 'Matterbridge.loadInstance() failed with error', error);
108
+ shutdown();
371
109
  });
110
+ async function version() {
111
+ const { default: pkg } = await import('../package.json', { with: { type: 'json' } });
112
+ console.log(`Matterbridge version ${pkg.version}`);
113
+ process.exit(0);
114
+ }
115
+ function help() {
116
+ console.log(`
117
+ Usage: matterbridge [options] [command]
118
+
119
+ Commands:
120
+ --help: show the help
121
+ --version: show the version
122
+ --add [plugin path]: register the plugin from the given absolute or relative path
123
+ --add [plugin name]: register the globally installed plugin with the given name
124
+ --remove [plugin path]: remove the plugin from the given absolute or relative path
125
+ --remove [plugin name]: remove the globally installed plugin with the given name
126
+ --enable [plugin path]: enable the plugin from the given absolute or relative path
127
+ --enable [plugin name]: enable the globally installed plugin with the given name
128
+ --disable [plugin path]: disable the plugin from the given absolute or relative path
129
+ --disable [plugin name]: disable the globally installed plugin with the given name
130
+
131
+ Reset Commands:
132
+ --reset: remove the commissioning for Matterbridge (bridge mode and childbridge mode). Shutdown Matterbridge before using it!
133
+ --reset [plugin path]: remove the commissioning for the plugin from the given absolute or relative path (childbridge mode). Shutdown Matterbridge before using it!
134
+ --reset [plugin name]: remove the commissioning for the globally installed plugin (childbridge mode). Shutdown Matterbridge before using it!
135
+ --factoryreset: remove all commissioning information and reset all internal storages. Shutdown Matterbridge before using it!
136
+
137
+ Options:
138
+ --bridge: start Matterbridge in bridge mode
139
+ --childbridge: start Matterbridge in childbridge mode
140
+ --port [port]: start the matter commissioning server on the given port (default 5540)
141
+ --mdnsinterface [name]: set the interface to use for the matter server mdnsInterface (default all interfaces)
142
+ --ipv4address [address]: set the ipv4 interface address to use for the matter server listener (default all addresses)
143
+ --ipv6address [address]: set the ipv6 interface address to use for the matter server listener (default all addresses)
144
+ --frontend [port]: start the frontend on the given port (default 8283)
145
+ --logger: set the matterbridge logger level: debug | info | notice | warn | error | fatal (default info)
146
+ --filelogger: enable the matterbridge file logger (matterbridge.log)
147
+ --matterlogger: set the matter.js logger level: debug | info | notice | warn | error | fatal (default info)
148
+ --matterfilelogger: enable the matter.js file logger (matter.log)
149
+ --list: list the registered plugins
150
+ --loginterfaces: log the network interfaces (usefull for finding the name of the interface to use with -mdnsinterface option)
151
+ --logstorage: log the node storage
152
+ --sudo: force the use of sudo to install or update packages if the internal logic fails
153
+ --nosudo: force not to use sudo to install or update packages if the internal logic fails
154
+ --norestore: force not to automatically restore the matterbridge node storage and the matter storage from backup if it is corrupted
155
+ --novirtual: disable the creation of the virtual devices Restart, Update and Reboot Matterbridge
156
+ --ssl: enable SSL for the frontend and the WebSocketServer (the server will use the certificates and switch to https)
157
+ --mtls: enable mTLS for the frontend and the WebSocketServer (both server and client will use and require the certificates and switch to https)
158
+ --vendorId: override the default vendorId 0xfff1
159
+ --vendorName: override the default vendorName "Matterbridge"
160
+ --productId: override the default productId 0x8000
161
+ --productName: override the default productName "Matterbridge aggregator"
162
+ --service: enable the service mode (used in the linux systemctl configuration file and macOS launchctl plist)
163
+ --docker: enable the docker mode (used in the docker image)
164
+ --homedir: override the home directory (default the os homedir)
165
+ `);
166
+ process.exit(0);
167
+ }
@@ -3,47 +3,15 @@ if (process.argv.includes('--loader') || process.argv.includes('-loader'))
3
3
  import { writeFileSync } from 'node:fs';
4
4
  import path from 'node:path';
5
5
  import os from 'node:os';
6
- export const historySize = 2880;
7
- export let historyIndex = 0;
8
- export function setHistoryIndex(index) {
9
- if (!Number.isFinite(index) || !Number.isSafeInteger(index)) {
10
- throw new TypeError('historyIndex must be a finite, safe integer.');
11
- }
12
- if (index < 0 || index >= historySize) {
13
- throw new RangeError(`historyIndex must be between 0 and ${historySize - 1}.`);
14
- }
15
- historyIndex = index;
16
- }
17
- export const history = Array.from({ length: historySize }, () => ({
18
- timestamp: 0,
19
- cpu: 0,
20
- peakCpu: 0,
21
- processCpu: 0,
22
- peakProcessCpu: 0,
23
- rss: 0,
24
- peakRss: 0,
25
- heapUsed: 0,
26
- peakHeapUsed: 0,
27
- heapTotal: 0,
28
- peakHeapTotal: 0,
29
- external: 0,
30
- peakExternal: 0,
31
- arrayBuffers: 0,
32
- peakArrayBuffers: 0,
33
- }));
6
+ import { Tracker } from './utils/tracker.js';
34
7
  export function generateHistoryPage(options = {}) {
35
8
  const pageTitle = options.pageTitle ?? 'Matterbridge CPU & Memory History';
36
9
  const hostname = options.hostname ?? os.hostname();
37
10
  const outputPath = path.resolve(options.outputPath ?? path.join(process.cwd(), 'history.html'));
38
- const bufferLength = history.length;
39
- if (bufferLength === 0) {
40
- return undefined;
41
- }
42
- const startIndex = ((Math.trunc(historyIndex) % bufferLength) + bufferLength) % bufferLength;
43
11
  const normalizedHistory = [];
44
- for (let offset = 0; offset < bufferLength; offset += 1) {
45
- const index = (startIndex + offset) % bufferLength;
46
- const entry = history[index];
12
+ for (let offset = 0; offset < Tracker.historySize; offset += 1) {
13
+ const index = (Tracker.historyIndex + offset) % Tracker.historySize;
14
+ const entry = Tracker.history[index];
47
15
  if (!entry || entry.timestamp === 0)
48
16
  continue;
49
17
  normalizedHistory.push(entry);
@@ -51,7 +19,7 @@ export function generateHistoryPage(options = {}) {
51
19
  if (normalizedHistory.length === 0) {
52
20
  return undefined;
53
21
  }
54
- const peakCpu = Math.max(...normalizedHistory.map((entry) => entry.peakCpu ?? entry.cpu));
22
+ const peakOsCpu = Math.max(...normalizedHistory.map((entry) => entry.peakOsCpu ?? entry.osCpu));
55
23
  const peakProcessCpu = Math.max(...normalizedHistory.map((entry) => entry.peakProcessCpu ?? entry.processCpu));
56
24
  const peakRss = Math.max(...normalizedHistory.map((entry) => entry.peakRss ?? entry.rss));
57
25
  const peakHeapUsed = Math.max(...normalizedHistory.map((entry) => entry.peakHeapUsed ?? entry.heapUsed));
@@ -63,7 +31,7 @@ export function generateHistoryPage(options = {}) {
63
31
  const summary = {
64
32
  entries: normalizedHistory.length,
65
33
  timeRange: `${new Date(firstTimestamp).toLocaleString()} → ${new Date(lastTimestamp).toLocaleString()}`,
66
- peakCpu,
34
+ peakOsCpu,
67
35
  peakProcessCpu,
68
36
  peakRss,
69
37
  peakHeapUsed,
@@ -295,7 +263,7 @@ export function generateHistoryPage(options = {}) {
295
263
  const summaryEntries = [
296
264
  { label: 'Samples', value: SUMMARY_DATA.entries.toLocaleString() },
297
265
  { label: 'Time Range', value: SUMMARY_DATA.timeRange },
298
- { label: 'Host CPU Peak', value: SUMMARY_DATA.peakCpu.toFixed(2) + ' %' },
266
+ { label: 'Host CPU Peak', value: SUMMARY_DATA.peakOsCpu.toFixed(2) + ' %' },
299
267
  { label: 'Process CPU Peak', value: SUMMARY_DATA.peakProcessCpu.toFixed(2) + ' %' },
300
268
  { label: 'RSS Peak', value: formatBytes(SUMMARY_DATA.peakRss) },
301
269
  { label: 'Heap Used Peak', value: formatBytes(SUMMARY_DATA.peakHeapUsed) },
@@ -316,8 +284,8 @@ export function generateHistoryPage(options = {}) {
316
284
  const row = document.createElement('tr');
317
285
  const cells = [
318
286
  new Date(entry.timestamp).toLocaleString(),
319
- entry.cpu.toFixed(2),
320
- entry.peakCpu.toFixed(2),
287
+ entry.osCpu.toFixed(2),
288
+ entry.peakOsCpu.toFixed(2),
321
289
  (Number.isFinite(entry.processCpu) ? entry.processCpu : 0).toFixed(2),
322
290
  (
323
291
  Number.isFinite(entry.peakProcessCpu)
@@ -342,16 +310,12 @@ export function generateHistoryPage(options = {}) {
342
310
  });
343
311
 
344
312
  const cpuPeakValue = HISTORY_DATA.reduce(function (acc, entry) {
345
- return Math.max(acc, Number.isFinite(entry.peakCpu) ? entry.peakCpu : 0, Number.isFinite(entry.cpu) ? entry.cpu : 0);
313
+ return Math.max(acc, Number.isFinite(entry.peakOsCpu) ? entry.peakOsCpu : 0, Number.isFinite(entry.osCpu) ? entry.osCpu : 0);
346
314
  }, 0);
347
315
  const cpuMaxYAxis = cpuPeakValue > 0 ? cpuPeakValue * 1.05 : undefined;
348
316
 
349
317
  const processCpuPeakValue = HISTORY_DATA.reduce(function (acc, entry) {
350
- return Math.max(
351
- acc,
352
- Number.isFinite(entry.peakProcessCpu) ? entry.peakProcessCpu : 0,
353
- Number.isFinite(entry.processCpu) ? entry.processCpu : 0
354
- );
318
+ return Math.max(acc, Number.isFinite(entry.peakProcessCpu) ? entry.peakProcessCpu : 0, Number.isFinite(entry.processCpu) ? entry.processCpu : 0);
355
319
  }, 0);
356
320
  const processCpuMaxYAxis = processCpuPeakValue > 0 ? processCpuPeakValue * 1.05 : undefined;
357
321
  const useProcessCpuDecimals = (processCpuMaxYAxis ?? 0) <= 3;
@@ -418,14 +382,14 @@ export function generateHistoryPage(options = {}) {
418
382
  {
419
383
  label: 'Host CPU %',
420
384
  values: HISTORY_DATA.map(function (entry) {
421
- return Number.isFinite(entry.cpu) ? Number(entry.cpu.toFixed(2)) : 0;
385
+ return Number.isFinite(entry.osCpu) ? Number(entry.osCpu.toFixed(2)) : 0;
422
386
  }),
423
387
  color: '#38bdf8',
424
388
  fill: 'rgba(56, 189, 248, 0.18)',
425
389
  markPeaks: true,
426
390
  markerPeakValues: HISTORY_DATA.map(function (entry) {
427
- if (Number.isFinite(entry.peakCpu)) return entry.peakCpu;
428
- if (Number.isFinite(entry.cpu)) return entry.cpu;
391
+ if (Number.isFinite(entry.peakOsCpu)) return entry.peakOsCpu;
392
+ if (Number.isFinite(entry.osCpu)) return entry.osCpu;
429
393
  return Number.NEGATIVE_INFINITY;
430
394
  }),
431
395
  markerRadius: 2.5
@@ -433,7 +397,7 @@ export function generateHistoryPage(options = {}) {
433
397
  {
434
398
  label: 'Host Peak CPU %',
435
399
  values: HISTORY_DATA.map(function (entry) {
436
- return Number.isFinite(entry.peakCpu) ? Number(entry.peakCpu.toFixed(2)) : 0;
400
+ return Number.isFinite(entry.peakOsCpu) ? Number(entry.peakOsCpu.toFixed(2)) : 0;
437
401
  }),
438
402
  color: '#facc15',
439
403
  dashed: [6, 4]