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 +6 -1
- package/dist/cli.js +36 -8
- package/dist/frontend.js +64 -33
- package/dist/index.js +2 -0
- package/dist/matterbridge.js +8 -8
- package/dist/matterbridgePlatform.js +2 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
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
|
-
|
|
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, ' ')}
|
|
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
|
-
|
|
2
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
407
|
-
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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
package/dist/matterbridge.js
CHANGED
|
@@ -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
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
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';
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "matterbridge",
|
|
3
|
-
"version": "3.3.1-dev-
|
|
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-
|
|
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