matterbridge 3.0.5-dev-20250609-a5046db → 3.0.6-dev-20250610-56cd483
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 +18 -0
- package/README.md +1 -1
- package/dist/frontend.js +6 -5
- package/dist/matterbridge.js +55 -133
- package/dist/pluginManager.js +2 -8
- package/dist/shelly.js +1 -1
- package/dist/utils/copyDirectory.js +15 -0
- package/dist/utils/createDirectory.js +21 -0
- package/dist/utils/export.js +1 -0
- package/dist/utils/spawn.js +68 -0
- package/dist/utils/wait.js +2 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,24 @@ If you like this project and find it useful, please consider giving it a star on
|
|
|
8
8
|
<img src="bmc-button.svg" alt="Buy me a coffee" width="120">
|
|
9
9
|
</a>
|
|
10
10
|
|
|
11
|
+
## [3.0.6] - 2025-06-??
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- [tests] Update Jest test coverage on addBridgedEndpoint and removeBridgedEndpoint.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- [package]: Updated dependencies.
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- [evse]: Fixed jsdoc on Evse.
|
|
24
|
+
|
|
25
|
+
<a href="https://www.buymeacoffee.com/luligugithub">
|
|
26
|
+
<img src="bmc-button.svg" alt="Buy me a coffee" width="80">
|
|
27
|
+
</a>
|
|
28
|
+
|
|
11
29
|
## [3.0.5] - 2025-06-07
|
|
12
30
|
|
|
13
31
|
### Added
|
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://hub.docker.com/r/luligu/matterbridge)
|
|
6
6
|
[](https://hub.docker.com/r/luligu/matterbridge)
|
|
7
7
|

|
|
8
|
-

|
|
9
9
|
|
|
10
10
|
[](https://www.npmjs.com/package/matter-history)
|
|
11
11
|
[](https://www.npmjs.com/package/node-ansi-logger)
|
package/dist/frontend.js
CHANGED
|
@@ -13,6 +13,7 @@ import { createZip, isValidArray, isValidNumber, isValidObject, isValidString, i
|
|
|
13
13
|
import { plg } from './matterbridgeTypes.js';
|
|
14
14
|
import { hasParameter } from './utils/export.js';
|
|
15
15
|
import { capitalizeFirstLetter } from './matterbridgeEndpointHelpers.js';
|
|
16
|
+
import spawn from './utils/spawn.js';
|
|
16
17
|
export const WS_ID_LOG = 0;
|
|
17
18
|
export const WS_ID_REFRESH_NEEDED = 1;
|
|
18
19
|
export const WS_ID_RESTART_NEEDED = 2;
|
|
@@ -423,7 +424,7 @@ export class Frontend {
|
|
|
423
424
|
await fs.rename(file.path, filePath);
|
|
424
425
|
this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
|
|
425
426
|
if (filename.endsWith('.tgz')) {
|
|
426
|
-
await this.matterbridge
|
|
427
|
+
await spawn.spawnCommand(this.matterbridge, 'npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
|
|
427
428
|
this.log.info(`Plugin package ${plg}${filename}${nf} installed successfully. Full restart required.`);
|
|
428
429
|
this.wssSendCloseSnackbarMessage(`Installing package ${filename}. Please wait...`);
|
|
429
430
|
this.wssSendSnackbarMessage(`Installed package ${filename}`, 10, 'success');
|
|
@@ -884,8 +885,8 @@ export class Frontend {
|
|
|
884
885
|
return;
|
|
885
886
|
}
|
|
886
887
|
this.wssSendSnackbarMessage(`Installing package ${data.params.packageName}...`, 0);
|
|
887
|
-
|
|
888
|
-
.spawnCommand('npm', ['install', '-g', data.params.packageName, '--omit=dev', '--verbose'])
|
|
888
|
+
spawn
|
|
889
|
+
.spawnCommand(this.matterbridge, 'npm', ['install', '-g', data.params.packageName, '--omit=dev', '--verbose'])
|
|
889
890
|
.then((response) => {
|
|
890
891
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response }));
|
|
891
892
|
this.wssSendCloseSnackbarMessage(`Installing package ${data.params.packageName}...`);
|
|
@@ -938,8 +939,8 @@ export class Frontend {
|
|
|
938
939
|
this.wssSendRefreshRequired('devices');
|
|
939
940
|
}
|
|
940
941
|
this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
|
|
941
|
-
|
|
942
|
-
.spawnCommand('npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
|
|
942
|
+
spawn
|
|
943
|
+
.spawnCommand(this.matterbridge, 'npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
|
|
943
944
|
.then((response) => {
|
|
944
945
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response }));
|
|
945
946
|
this.wssSendCloseSnackbarMessage(`Uninstalling package ${data.params.packageName}...`);
|
package/dist/matterbridge.js
CHANGED
|
@@ -5,7 +5,7 @@ import EventEmitter from 'node:events';
|
|
|
5
5
|
import { inspect } from 'node:util';
|
|
6
6
|
import { AnsiLogger, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN } from './logger/export.js';
|
|
7
7
|
import { NodeStorageManager } from './storage/export.js';
|
|
8
|
-
import { getParameter, getIntParameter, hasParameter, copyDirectory, withTimeout, waiter, isValidString, parseVersionString, isValidNumber } from './utils/export.js';
|
|
8
|
+
import { getParameter, getIntParameter, hasParameter, copyDirectory, withTimeout, waiter, isValidString, parseVersionString, isValidNumber, createDirectory } from './utils/export.js';
|
|
9
9
|
import { logInterfaces, getGlobalNodeModules } from './utils/network.js';
|
|
10
10
|
import { dev, plg, typ } from './matterbridgeTypes.js';
|
|
11
11
|
import { PluginManager } from './pluginManager.js';
|
|
@@ -14,6 +14,7 @@ import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
|
|
|
14
14
|
import { bridge } from './matterbridgeDeviceTypes.js';
|
|
15
15
|
import { Frontend } from './frontend.js';
|
|
16
16
|
import { addVirtualDevices } from './helpers.js';
|
|
17
|
+
import spawn from './utils/spawn.js';
|
|
17
18
|
import { DeviceTypeId, Endpoint, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, VendorId, StorageService, Environment, ServerNode, UINT32_MAX, UINT16_MAX, } from '@matter/main';
|
|
18
19
|
import { DeviceCommissioner, FabricAction, MdnsService, PaseClient } from '@matter/main/protocol';
|
|
19
20
|
import { AggregatorEndpoint } from '@matter/main/endpoints';
|
|
@@ -116,6 +117,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
116
117
|
checkUpdateTimeout;
|
|
117
118
|
configureTimeout;
|
|
118
119
|
reachabilityTimeout;
|
|
120
|
+
endAdvertiseTimeout;
|
|
119
121
|
sigintHandler;
|
|
120
122
|
sigtermHandler;
|
|
121
123
|
exceptionHandler;
|
|
@@ -221,18 +223,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
221
223
|
this.restartMode = 'docker';
|
|
222
224
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
223
225
|
this.matterbridgeInformation.homeDirectory = this.homeDirectory;
|
|
224
|
-
await
|
|
226
|
+
await createDirectory(this.homeDirectory, 'Matterbridge Home Directory', this.log);
|
|
225
227
|
this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
|
|
226
228
|
this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
|
|
227
|
-
await
|
|
228
|
-
await
|
|
229
|
-
await
|
|
229
|
+
await createDirectory(this.matterbridgeDirectory, 'Matterbridge Directory', this.log);
|
|
230
|
+
await createDirectory(path.join(this.matterbridgeDirectory, 'certs'), 'Matterbridge Frontend Certificate Directory', this.log);
|
|
231
|
+
await createDirectory(path.join(this.matterbridgeDirectory, 'uploads'), 'Matterbridge Frontend Uploads Directory', this.log);
|
|
230
232
|
this.matterbridgePluginDirectory = path.join(this.homeDirectory, 'Matterbridge');
|
|
231
233
|
this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
|
|
232
|
-
await
|
|
234
|
+
await createDirectory(this.matterbridgePluginDirectory, 'Matterbridge Plugin Directory', this.log);
|
|
233
235
|
this.matterbridgeCertDirectory = path.join(this.homeDirectory, '.mattercert');
|
|
234
236
|
this.matterbridgeInformation.matterbridgeCertDirectory = this.matterbridgeCertDirectory;
|
|
235
|
-
await
|
|
237
|
+
await createDirectory(this.matterbridgeCertDirectory, 'Matterbridge Matter Certificate Directory', this.log);
|
|
236
238
|
const { fileURLToPath } = await import('node:url');
|
|
237
239
|
const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
238
240
|
this.rootDirectory = path.resolve(currentFileDirectory, '../');
|
|
@@ -502,7 +504,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
502
504
|
if (packageJson === null && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
|
|
503
505
|
this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
|
|
504
506
|
try {
|
|
505
|
-
await
|
|
507
|
+
await spawn.spawnCommand(this, 'npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose']);
|
|
506
508
|
this.log.info(`Plugin ${plg}${plugin.name}${nf} reinstalled.`);
|
|
507
509
|
plugin.error = false;
|
|
508
510
|
}
|
|
@@ -989,7 +991,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
989
991
|
async updateProcess() {
|
|
990
992
|
this.log.info('Updating matterbridge...');
|
|
991
993
|
try {
|
|
992
|
-
await
|
|
994
|
+
await spawn.spawnCommand(this, 'npm', ['install', '-g', 'matterbridge', '--omit=dev', '--verbose']);
|
|
993
995
|
this.log.info('Matterbridge has been updated. Full restart required.');
|
|
994
996
|
}
|
|
995
997
|
catch (error) {
|
|
@@ -1338,19 +1340,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1338
1340
|
}, 1000);
|
|
1339
1341
|
}
|
|
1340
1342
|
async startController() {
|
|
1341
|
-
if (!this.matterStorageManager) {
|
|
1342
|
-
this.log.error('No storage manager initialized');
|
|
1343
|
-
await this.cleanup('No storage manager initialized');
|
|
1344
|
-
return;
|
|
1345
|
-
}
|
|
1346
|
-
this.log.info('Creating context: mattercontrollerContext');
|
|
1347
|
-
this.controllerContext = this.matterStorageManager.createContext('mattercontrollerContext');
|
|
1348
|
-
if (!this.controllerContext) {
|
|
1349
|
-
this.log.error('No storage context mattercontrollerContext initialized');
|
|
1350
|
-
await this.cleanup('No storage context mattercontrollerContext initialized');
|
|
1351
|
-
return;
|
|
1352
|
-
}
|
|
1353
|
-
this.log.debug('Starting matterbridge in mode', this.bridgeMode);
|
|
1354
1343
|
}
|
|
1355
1344
|
async startMatterStorage() {
|
|
1356
1345
|
this.log.info(`Starting matter node storage...`);
|
|
@@ -1365,8 +1354,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1365
1354
|
}
|
|
1366
1355
|
async backupMatterStorage(storageName, backupName) {
|
|
1367
1356
|
this.log.info('Creating matter node storage backup...');
|
|
1368
|
-
|
|
1369
|
-
|
|
1357
|
+
try {
|
|
1358
|
+
await copyDirectory(storageName, backupName);
|
|
1359
|
+
this.log.info('Created matter node storage backup');
|
|
1360
|
+
}
|
|
1361
|
+
catch (error) {
|
|
1362
|
+
this.log.error(`Error creating matter node storage backup from ${storageName} to ${backupName}:`, error);
|
|
1363
|
+
}
|
|
1370
1364
|
}
|
|
1371
1365
|
async stopMatterStorage() {
|
|
1372
1366
|
this.log.info('Closing matter node storage...');
|
|
@@ -1471,7 +1465,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
1471
1465
|
}
|
|
1472
1466
|
}
|
|
1473
1467
|
};
|
|
1474
|
-
serverNode.lifecycle.commissioned.on(() =>
|
|
1468
|
+
serverNode.lifecycle.commissioned.on(() => {
|
|
1469
|
+
this.log.notice(`Server node for ${storeId} was initially commissioned successfully!`);
|
|
1470
|
+
clearTimeout(this.endAdvertiseTimeout);
|
|
1471
|
+
});
|
|
1475
1472
|
serverNode.lifecycle.decommissioned.on(() => this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`));
|
|
1476
1473
|
serverNode.lifecycle.online.on(async () => {
|
|
1477
1474
|
this.log.notice(`Server node for ${storeId} is online`);
|
|
@@ -1511,27 +1508,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1511
1508
|
}
|
|
1512
1509
|
}
|
|
1513
1510
|
}
|
|
1514
|
-
|
|
1515
|
-
if (serverNode.lifecycle.isCommissioned)
|
|
1516
|
-
return;
|
|
1517
|
-
if (this.bridgeMode === 'bridge') {
|
|
1518
|
-
this.matterbridgeQrPairingCode = undefined;
|
|
1519
|
-
this.matterbridgeManualPairingCode = undefined;
|
|
1520
|
-
}
|
|
1521
|
-
if (this.bridgeMode === 'childbridge') {
|
|
1522
|
-
const plugin = this.plugins.get(storeId);
|
|
1523
|
-
if (plugin) {
|
|
1524
|
-
plugin.qrPairingCode = undefined;
|
|
1525
|
-
plugin.manualPairingCode = undefined;
|
|
1526
|
-
}
|
|
1527
|
-
}
|
|
1528
|
-
this.frontend.wssSendRefreshRequired('plugins');
|
|
1529
|
-
this.frontend.wssSendRefreshRequired('settings');
|
|
1530
|
-
this.frontend.wssSendRefreshRequired('fabrics');
|
|
1531
|
-
this.frontend.wssSendRefreshRequired('sessions');
|
|
1532
|
-
this.frontend.wssSendSnackbarMessage(`Advertising on server node for ${storeId} stopped. Restart to commission.`, 0);
|
|
1533
|
-
this.log.notice(`Advertising on server node for ${storeId} stopped. Restart to commission.`);
|
|
1534
|
-
}, 15 * 60 * 1000).unref();
|
|
1511
|
+
this.startEndAdvertiseTimer(serverNode);
|
|
1535
1512
|
}
|
|
1536
1513
|
else {
|
|
1537
1514
|
this.log.notice(`Server node for ${storeId} is already commissioned. Waiting for controllers to connect ...`);
|
|
@@ -1540,6 +1517,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1540
1517
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
1541
1518
|
this.frontend.wssSendRefreshRequired('settings');
|
|
1542
1519
|
this.frontend.wssSendSnackbarMessage(`${storeId} is online`, 5, 'success');
|
|
1520
|
+
this.emit('online', storeId);
|
|
1543
1521
|
});
|
|
1544
1522
|
serverNode.lifecycle.offline.on(() => {
|
|
1545
1523
|
this.log.notice(`Server node for ${storeId} is offline`);
|
|
@@ -1563,6 +1541,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1563
1541
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
1564
1542
|
this.frontend.wssSendRefreshRequired('settings');
|
|
1565
1543
|
this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
|
|
1544
|
+
this.emit('offline', storeId);
|
|
1566
1545
|
});
|
|
1567
1546
|
serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
|
|
1568
1547
|
let action = '';
|
|
@@ -1615,18 +1594,46 @@ export class Matterbridge extends EventEmitter {
|
|
|
1615
1594
|
this.log.info(`Created server node for ${storeId}`);
|
|
1616
1595
|
return serverNode;
|
|
1617
1596
|
}
|
|
1597
|
+
startEndAdvertiseTimer(matterServerNode) {
|
|
1598
|
+
if (this.endAdvertiseTimeout) {
|
|
1599
|
+
this.log.debug(`***Clear ${matterServerNode.id} server node end advertise timer`);
|
|
1600
|
+
clearTimeout(this.endAdvertiseTimeout);
|
|
1601
|
+
}
|
|
1602
|
+
this.log.debug(`***Starting ${matterServerNode.id} server node end advertise timer`);
|
|
1603
|
+
this.endAdvertiseTimeout = setTimeout(() => {
|
|
1604
|
+
if (matterServerNode.lifecycle.isCommissioned)
|
|
1605
|
+
return;
|
|
1606
|
+
if (this.bridgeMode === 'bridge') {
|
|
1607
|
+
this.matterbridgeQrPairingCode = undefined;
|
|
1608
|
+
this.matterbridgeManualPairingCode = undefined;
|
|
1609
|
+
}
|
|
1610
|
+
if (this.bridgeMode === 'childbridge') {
|
|
1611
|
+
const plugin = this.plugins.get(matterServerNode.id);
|
|
1612
|
+
if (plugin) {
|
|
1613
|
+
plugin.qrPairingCode = undefined;
|
|
1614
|
+
plugin.manualPairingCode = undefined;
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
this.frontend.wssSendRefreshRequired('plugins');
|
|
1618
|
+
this.frontend.wssSendRefreshRequired('settings');
|
|
1619
|
+
this.frontend.wssSendRefreshRequired('fabrics');
|
|
1620
|
+
this.frontend.wssSendRefreshRequired('sessions');
|
|
1621
|
+
this.frontend.wssSendSnackbarMessage(`Advertising on server node for ${matterServerNode.id} stopped. Restart to commission.`, 0);
|
|
1622
|
+
this.log.notice(`Advertising on server node for ${matterServerNode.id} stopped. Restart to commission.`);
|
|
1623
|
+
}, 15 * 60 * 1000).unref();
|
|
1624
|
+
}
|
|
1618
1625
|
async startServerNode(matterServerNode) {
|
|
1619
1626
|
if (!matterServerNode)
|
|
1620
1627
|
return;
|
|
1621
1628
|
this.log.notice(`Starting ${matterServerNode.id} server node`);
|
|
1622
1629
|
await matterServerNode.start();
|
|
1623
1630
|
}
|
|
1624
|
-
async stopServerNode(matterServerNode) {
|
|
1631
|
+
async stopServerNode(matterServerNode, timeout = 30000) {
|
|
1625
1632
|
if (!matterServerNode)
|
|
1626
1633
|
return;
|
|
1627
1634
|
this.log.notice(`Closing ${matterServerNode.id} server node`);
|
|
1628
1635
|
try {
|
|
1629
|
-
await withTimeout(matterServerNode.close(),
|
|
1636
|
+
await withTimeout(matterServerNode.close(), timeout);
|
|
1630
1637
|
this.log.info(`Closed ${matterServerNode.id} server node`);
|
|
1631
1638
|
}
|
|
1632
1639
|
catch (error) {
|
|
@@ -1864,89 +1871,4 @@ export class Matterbridge extends EventEmitter {
|
|
|
1864
1871
|
}
|
|
1865
1872
|
return vendorName;
|
|
1866
1873
|
};
|
|
1867
|
-
async spawnCommand(command, args = []) {
|
|
1868
|
-
const { spawn } = await import('node:child_process');
|
|
1869
|
-
const cmdLine = command + ' ' + args.join(' ');
|
|
1870
|
-
if (process.platform === 'win32' && command === 'npm') {
|
|
1871
|
-
const argstring = 'npm ' + args.join(' ');
|
|
1872
|
-
args.splice(0, args.length, '/c', argstring);
|
|
1873
|
-
command = 'cmd.exe';
|
|
1874
|
-
}
|
|
1875
|
-
if (hasParameter('sudo') || (process.platform === 'linux' && command === 'npm' && !hasParameter('docker') && !hasParameter('nosudo'))) {
|
|
1876
|
-
args.unshift(command);
|
|
1877
|
-
command = 'sudo';
|
|
1878
|
-
}
|
|
1879
|
-
this.log.debug(`Spawn command ${command} with ${debugStringify(args)}`);
|
|
1880
|
-
return new Promise((resolve, reject) => {
|
|
1881
|
-
const childProcess = spawn(command, args, {
|
|
1882
|
-
stdio: ['inherit', 'pipe', 'pipe'],
|
|
1883
|
-
});
|
|
1884
|
-
childProcess.on('error', (err) => {
|
|
1885
|
-
this.log.error(`Failed to start child process "${cmdLine}": ${err.message}`);
|
|
1886
|
-
reject(err);
|
|
1887
|
-
});
|
|
1888
|
-
childProcess.on('close', (code, signal) => {
|
|
1889
|
-
this.frontend.wssSendMessage('spawn', this.log.now(), 'Matterbridge:spawn', `child process closed with code ${code} and signal ${signal}`);
|
|
1890
|
-
if (code === 0) {
|
|
1891
|
-
if (cmdLine.startsWith('npm install -g'))
|
|
1892
|
-
this.log.notice(`Package ${cmdLine.replace('npm install -g ', '').replace('--verbose', '').replace('--omit=dev', '')} installed correctly`);
|
|
1893
|
-
this.log.debug(`Child process "${cmdLine}" closed with code ${code} and signal ${signal}`);
|
|
1894
|
-
resolve(true);
|
|
1895
|
-
}
|
|
1896
|
-
else {
|
|
1897
|
-
this.log.error(`Child process "${cmdLine}" closed with code ${code} and signal ${signal}`);
|
|
1898
|
-
reject(new Error(`Child process "${cmdLine}" closed with code ${code} and signal ${signal}`));
|
|
1899
|
-
}
|
|
1900
|
-
});
|
|
1901
|
-
childProcess.on('exit', (code, signal) => {
|
|
1902
|
-
this.frontend.wssSendMessage('spawn', this.log.now(), 'Matterbridge:spawn', `child process exited with code ${code} and signal ${signal}`);
|
|
1903
|
-
if (code === 0) {
|
|
1904
|
-
this.log.debug(`Child process "${cmdLine}" exited with code ${code} and signal ${signal}`);
|
|
1905
|
-
resolve(true);
|
|
1906
|
-
}
|
|
1907
|
-
else {
|
|
1908
|
-
this.log.error(`Child process "${cmdLine}" exited with code ${code} and signal ${signal}`);
|
|
1909
|
-
reject(new Error(`Child process "${cmdLine}" exited with code ${code} and signal ${signal}`));
|
|
1910
|
-
}
|
|
1911
|
-
});
|
|
1912
|
-
childProcess.on('disconnect', () => {
|
|
1913
|
-
this.log.debug(`Child process "${cmdLine}" has been disconnected from the parent`);
|
|
1914
|
-
resolve(true);
|
|
1915
|
-
});
|
|
1916
|
-
if (childProcess.stdout) {
|
|
1917
|
-
childProcess.stdout.on('data', (data) => {
|
|
1918
|
-
const message = data.toString().trim();
|
|
1919
|
-
this.log.debug(`Spawn output (stdout): ${message}`);
|
|
1920
|
-
this.frontend.wssSendMessage('spawn', this.log.now(), 'Matterbridge:spawn', message);
|
|
1921
|
-
});
|
|
1922
|
-
}
|
|
1923
|
-
if (childProcess.stderr) {
|
|
1924
|
-
childProcess.stderr.on('data', (data) => {
|
|
1925
|
-
const message = data.toString().trim();
|
|
1926
|
-
this.log.debug(`Spawn verbose (stderr): ${message}`);
|
|
1927
|
-
this.frontend.wssSendMessage('spawn', this.log.now(), 'Matterbridge:spawn', message);
|
|
1928
|
-
});
|
|
1929
|
-
}
|
|
1930
|
-
});
|
|
1931
|
-
}
|
|
1932
|
-
async createDirectory(path, name) {
|
|
1933
|
-
try {
|
|
1934
|
-
await fs.access(path);
|
|
1935
|
-
this.log.debug(`Directory ${name} already exists at path: ${path}`);
|
|
1936
|
-
}
|
|
1937
|
-
catch (err) {
|
|
1938
|
-
if (err.code === 'ENOENT') {
|
|
1939
|
-
try {
|
|
1940
|
-
await fs.mkdir(path, { recursive: true });
|
|
1941
|
-
this.log.info(`Created ${name}: ${path}`);
|
|
1942
|
-
}
|
|
1943
|
-
catch (err) {
|
|
1944
|
-
this.log.error(`Error creating dir ${name} path ${path}: ${err}`);
|
|
1945
|
-
}
|
|
1946
|
-
}
|
|
1947
|
-
else {
|
|
1948
|
-
this.log.error(`Error accessing dir ${name} path ${path}: ${err}`);
|
|
1949
|
-
}
|
|
1950
|
-
}
|
|
1951
|
-
}
|
|
1952
1874
|
}
|
package/dist/pluginManager.js
CHANGED
|
@@ -750,14 +750,8 @@ export class PluginManager {
|
|
|
750
750
|
this.log.debug(`Loaded schema file ${schemaFile} for plugin ${plg}${plugin.name}${db}.`);
|
|
751
751
|
return schema;
|
|
752
752
|
}
|
|
753
|
-
catch (
|
|
754
|
-
|
|
755
|
-
if (nodeErr.code === 'ENOENT') {
|
|
756
|
-
this.log.debug(`Schema file ${schemaFile} for plugin ${plg}${plugin.name}${db} not found. Loading default schema.`);
|
|
757
|
-
}
|
|
758
|
-
else {
|
|
759
|
-
this.log.debug(`Schema file ${schemaFile} for plugin ${plg}${plugin.name}${db} not found. Loading default schema. Error: ${err instanceof Error ? err.message + '\n' + err.stack : err}`);
|
|
760
|
-
}
|
|
753
|
+
catch (_err) {
|
|
754
|
+
this.log.debug(`Schema file ${schemaFile} for plugin ${plg}${plugin.name}${db} not found. Loading default schema.`);
|
|
761
755
|
return this.getDefaultSchema(plugin);
|
|
762
756
|
}
|
|
763
757
|
}
|
package/dist/shelly.js
CHANGED
|
@@ -70,7 +70,7 @@ export async function triggerShellyMainUpdate(matterbridge) {
|
|
|
70
70
|
matterbridge.log.error(`Error triggering Shelly main update: ${err instanceof Error ? err.message : String(err)}`);
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
|
-
async function verifyShellyUpdate(matterbridge, api, name) {
|
|
73
|
+
export async function verifyShellyUpdate(matterbridge, api, name) {
|
|
74
74
|
return new Promise((resolve) => {
|
|
75
75
|
const timeout = setTimeout(() => {
|
|
76
76
|
matterbridge.log.error(`${name} check timed out`);
|
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
import { AnsiLogger } from '../logger/export.js';
|
|
2
2
|
export async function copyDirectory(srcDir, destDir) {
|
|
3
|
+
if (srcDir === '') {
|
|
4
|
+
throw new Error('Source directory must be specified.');
|
|
5
|
+
}
|
|
6
|
+
if (destDir === '') {
|
|
7
|
+
throw new Error('Destination directory must be specified.');
|
|
8
|
+
}
|
|
9
|
+
if (!srcDir) {
|
|
10
|
+
throw new Error('Source directory must be specified.');
|
|
11
|
+
}
|
|
12
|
+
if (!destDir) {
|
|
13
|
+
throw new Error('Destination directory must be specified.');
|
|
14
|
+
}
|
|
15
|
+
if (srcDir === destDir) {
|
|
16
|
+
throw new Error('Source and destination directories must be different.');
|
|
17
|
+
}
|
|
3
18
|
const log = new AnsiLogger({ logName: 'Archive', logTimestampFormat: 4, logLevel: "info" });
|
|
4
19
|
const fs = await import('node:fs').then((mod) => mod.promises);
|
|
5
20
|
const path = await import('node:path');
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
export async function createDirectory(path, name, log) {
|
|
3
|
+
try {
|
|
4
|
+
await fs.access(path);
|
|
5
|
+
log.debug(`Directory ${name} already exists at path: ${path}`);
|
|
6
|
+
}
|
|
7
|
+
catch (err) {
|
|
8
|
+
if (err.code === 'ENOENT') {
|
|
9
|
+
try {
|
|
10
|
+
await fs.mkdir(path, { recursive: true });
|
|
11
|
+
log.info(`Created ${name}: ${path}`);
|
|
12
|
+
}
|
|
13
|
+
catch (err) {
|
|
14
|
+
log.error(`Error creating dir ${name} path ${path}: ${err}`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
log.error(`Error accessing dir ${name} path ${path}: ${err}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
package/dist/utils/export.js
CHANGED
|
@@ -5,6 +5,7 @@ export * from './colorUtils.js';
|
|
|
5
5
|
export * from './deepCopy.js';
|
|
6
6
|
export * from './deepEqual.js';
|
|
7
7
|
export * from './copyDirectory.js';
|
|
8
|
+
export * from './createDirectory.js';
|
|
8
9
|
export * from './createZip.js';
|
|
9
10
|
export * from './wait.js';
|
|
10
11
|
export * from './hex.js';
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { hasParameter } from './commandLine.js';
|
|
2
|
+
export default {
|
|
3
|
+
async spawnCommand(matterbridge, command, args) {
|
|
4
|
+
const { spawn } = await import('node:child_process');
|
|
5
|
+
const cmdLine = command + ' ' + args.join(' ');
|
|
6
|
+
if (process.platform === 'win32' && command === 'npm') {
|
|
7
|
+
const argstring = 'npm ' + args.join(' ');
|
|
8
|
+
args.splice(0, args.length, '/c', argstring);
|
|
9
|
+
command = 'cmd.exe';
|
|
10
|
+
}
|
|
11
|
+
if (hasParameter('sudo') || (process.platform !== 'win32' && command === 'npm' && !hasParameter('docker') && !hasParameter('nosudo'))) {
|
|
12
|
+
args.unshift(command);
|
|
13
|
+
command = 'sudo';
|
|
14
|
+
}
|
|
15
|
+
matterbridge.log.debug(`Spawn command ${command} with ${args.join(' ')}`);
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
const childProcess = spawn(command, args, {
|
|
18
|
+
stdio: ['inherit', 'pipe', 'pipe'],
|
|
19
|
+
});
|
|
20
|
+
childProcess.on('error', (err) => {
|
|
21
|
+
matterbridge.log.error(`Failed to start child process "${cmdLine}": ${err.message}`);
|
|
22
|
+
reject(err);
|
|
23
|
+
});
|
|
24
|
+
childProcess.on('close', (code, signal) => {
|
|
25
|
+
matterbridge.frontend.wssSendMessage('spawn', matterbridge.log.now(), 'Matterbridge:spawn', `child process closed with code ${code} and signal ${signal}`);
|
|
26
|
+
if (code === 0) {
|
|
27
|
+
if (cmdLine.startsWith('npm install -g'))
|
|
28
|
+
matterbridge.log.notice(`Package ${cmdLine.replace('npm install -g ', '').replace('--verbose', '').replace('--omit=dev', '')} installed correctly`);
|
|
29
|
+
matterbridge.log.debug(`Child process "${cmdLine}" closed with code ${code} and signal ${signal}`);
|
|
30
|
+
resolve(true);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
matterbridge.log.error(`Child process "${cmdLine}" closed with code ${code} and signal ${signal}`);
|
|
34
|
+
reject(new Error(`Child process "${cmdLine}" closed with code ${code} and signal ${signal}`));
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
childProcess.on('exit', (code, signal) => {
|
|
38
|
+
matterbridge.frontend.wssSendMessage('spawn', matterbridge.log.now(), 'Matterbridge:spawn', `child process exited with code ${code} and signal ${signal}`);
|
|
39
|
+
if (code === 0) {
|
|
40
|
+
matterbridge.log.debug(`Child process "${cmdLine}" exited with code ${code} and signal ${signal}`);
|
|
41
|
+
resolve(true);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
matterbridge.log.error(`Child process "${cmdLine}" exited with code ${code} and signal ${signal}`);
|
|
45
|
+
reject(new Error(`Child process "${cmdLine}" exited with code ${code} and signal ${signal}`));
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
childProcess.on('disconnect', () => {
|
|
49
|
+
matterbridge.log.debug(`Child process "${cmdLine}" has been disconnected from the parent`);
|
|
50
|
+
resolve(true);
|
|
51
|
+
});
|
|
52
|
+
if (childProcess.stdout) {
|
|
53
|
+
childProcess.stdout.on('data', (data) => {
|
|
54
|
+
const message = data.toString().trim();
|
|
55
|
+
matterbridge.log.debug(`Spawn output (stdout): ${message}`);
|
|
56
|
+
matterbridge.frontend.wssSendMessage('spawn', matterbridge.log.now(), 'Matterbridge:spawn', message);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
if (childProcess.stderr) {
|
|
60
|
+
childProcess.stderr.on('data', (data) => {
|
|
61
|
+
const message = data.toString().trim();
|
|
62
|
+
matterbridge.log.debug(`Spawn verbose (stderr): ${message}`);
|
|
63
|
+
matterbridge.frontend.wssSendMessage('spawn', matterbridge.log.now(), 'Matterbridge:spawn', message);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
},
|
|
68
|
+
};
|
package/dist/utils/wait.js
CHANGED
|
@@ -18,7 +18,7 @@ export async function waiter(name, check, exitWithReject = false, resolveTimeout
|
|
|
18
18
|
else
|
|
19
19
|
resolve(false);
|
|
20
20
|
}, resolveTimeout);
|
|
21
|
-
const intervalId = setInterval(() => {
|
|
21
|
+
const intervalId = setInterval(async () => {
|
|
22
22
|
if (check()) {
|
|
23
23
|
if (debug)
|
|
24
24
|
log.debug(`Waiter "${name}" finished for true condition...`);
|
|
@@ -26,6 +26,7 @@ export async function waiter(name, check, exitWithReject = false, resolveTimeout
|
|
|
26
26
|
clearInterval(intervalId);
|
|
27
27
|
resolve(true);
|
|
28
28
|
}
|
|
29
|
+
await Promise.resolve();
|
|
29
30
|
}, resolveInterval);
|
|
30
31
|
});
|
|
31
32
|
}
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "matterbridge",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.6-dev-20250610-56cd483",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "matterbridge",
|
|
9
|
-
"version": "3.0.
|
|
9
|
+
"version": "3.0.6-dev-20250610-56cd483",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@matter/main": "0.14.0",
|
package/package.json
CHANGED