matterbridge 3.2.9-dev-20250924-2e9594f → 3.2.9-dev-20250926-85736bb

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
@@ -22,6 +22,7 @@ If you like this project and find it useful, please consider giving it a star on
22
22
  - [frontend]: Optimized WebSocker message handlers. Now, the handler targets the component.
23
23
  - [frontend]: Removed dangerouslySetInnerHTML from log rendering.
24
24
  - [frontend]: Added push update to Icon view and table view cluster panel.
25
+ - [frontend]: Added install progress dialog when installing or uploading packages.
25
26
  - [endpoint]: Added occupancy feature to all Thermostat cluster helpers. When provided (either false or true) it will create a Thermostat with occupancy feature.
26
27
  - [endpoint]: Added outdoorTemperature to all Thermostat cluster helpers. Default is undefined (it will be ignored).
27
28
 
package/dist/frontend.js CHANGED
@@ -21,6 +21,7 @@ export class Frontend extends EventEmitter {
21
21
  matterbridge;
22
22
  log;
23
23
  port = 8283;
24
+ listening = false;
24
25
  expressApp;
25
26
  httpServer;
26
27
  httpsServer;
@@ -63,6 +64,7 @@ export class Frontend extends EventEmitter {
63
64
  if (hasParameter('ingress')) {
64
65
  this.httpServer.listen(this.port, '0.0.0.0', () => {
65
66
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
67
+ this.listening = true;
66
68
  this.emit('server_listening', 'http', this.port, '0.0.0.0');
67
69
  });
68
70
  }
@@ -72,6 +74,7 @@ export class Frontend extends EventEmitter {
72
74
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://${this.matterbridge.systemInformation.ipv4Address}:${this.port}${UNDERLINEOFF}${rs}`);
73
75
  if (this.matterbridge.systemInformation.ipv6Address !== '')
74
76
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
77
+ this.listening = true;
75
78
  this.emit('server_listening', 'http', this.port);
76
79
  });
77
80
  }
@@ -164,6 +167,7 @@ export class Frontend extends EventEmitter {
164
167
  if (hasParameter('ingress')) {
165
168
  this.httpsServer.listen(this.port, '0.0.0.0', () => {
166
169
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
170
+ this.listening = true;
167
171
  this.emit('server_listening', 'https', this.port, '0.0.0.0');
168
172
  });
169
173
  }
@@ -173,6 +177,7 @@ export class Frontend extends EventEmitter {
173
177
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://${this.matterbridge.systemInformation.ipv4Address}:${this.port}${UNDERLINEOFF}${rs}`);
174
178
  if (this.matterbridge.systemInformation.ipv6Address !== '')
175
179
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
180
+ this.listening = true;
176
181
  this.emit('server_listening', 'https', this.port);
177
182
  });
178
183
  }
@@ -525,7 +530,7 @@ export class Frontend extends EventEmitter {
525
530
  this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
526
531
  if (filename.endsWith('.tgz')) {
527
532
  const { spawnCommand } = await import('./utils/spawn.js');
528
- await spawnCommand(this.matterbridge, 'npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
533
+ await spawnCommand(this.matterbridge, 'npm', ['install', '-g', filePath, '--omit=dev', '--verbose'], filename);
529
534
  this.log.info(`Plugin package ${plg}${filename}${nf} installed successfully. Full restart required.`);
530
535
  this.wssSendCloseSnackbarMessage(`Installing package ${filename}. Please wait...`);
531
536
  this.wssSendSnackbarMessage(`Installed package ${filename}`, 10, 'success');
@@ -581,6 +586,7 @@ export class Frontend extends EventEmitter {
581
586
  this.log.debug('Closing http server...');
582
587
  this.httpServer.close();
583
588
  this.log.debug('Http server closed successfully');
589
+ this.listening = false;
584
590
  this.emit('server_stopped');
585
591
  this.httpServer.removeAllListeners();
586
592
  this.httpServer = undefined;
@@ -590,6 +596,7 @@ export class Frontend extends EventEmitter {
590
596
  this.log.debug('Closing https server...');
591
597
  this.httpsServer.close();
592
598
  this.log.debug('Https server closed successfully');
599
+ this.listening = false;
593
600
  this.emit('server_stopped');
594
601
  this.httpsServer.removeAllListeners();
595
602
  this.httpsServer = undefined;
@@ -969,7 +976,7 @@ export class Frontend extends EventEmitter {
969
976
  }
970
977
  this.wssSendSnackbarMessage(`Installing package ${data.params.packageName}...`, 0);
971
978
  const { spawnCommand } = await import('./utils/spawn.js');
972
- spawnCommand(this.matterbridge, 'npm', ['install', '-g', data.params.packageName, '--omit=dev', '--verbose'])
979
+ spawnCommand(this.matterbridge, 'npm', ['install', '-g', data.params.packageName, '--omit=dev', '--verbose'], data.params.packageName)
973
980
  .then((_response) => {
974
981
  sendResponse({ id: localData.id, method: localData.method, src: 'Matterbridge', dst: data.src, success: true });
975
982
  this.wssSendCloseSnackbarMessage(`Installing package ${localData.params.packageName}...`);
@@ -1037,7 +1044,7 @@ export class Frontend extends EventEmitter {
1037
1044
  }
1038
1045
  this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
1039
1046
  const { spawnCommand } = await import('./utils/spawn.js');
1040
- spawnCommand(this.matterbridge, 'npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
1047
+ spawnCommand(this.matterbridge, 'npm', ['uninstall', '-g', data.params.packageName, '--verbose'], data.params.packageName)
1041
1048
  .then((_response) => {
1042
1049
  sendResponse({ id: localData.id, method: localData.method, src: 'Matterbridge', dst: data.src, success: true });
1043
1050
  this.wssSendCloseSnackbarMessage(`Uninstalling package ${localData.params.packageName}...`);
@@ -507,7 +507,7 @@ export class Matterbridge extends EventEmitter {
507
507
  this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
508
508
  try {
509
509
  const { spawnCommand } = await import('./utils/spawn.js');
510
- await spawnCommand(this, 'npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose']);
510
+ await spawnCommand(this, 'npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose'], plugin.name);
511
511
  this.log.info(`Plugin ${plg}${plugin.name}${nf} reinstalled.`);
512
512
  plugin.error = false;
513
513
  }
@@ -654,6 +654,8 @@ export class Matterbridge extends EventEmitter {
654
654
  this.shutdown = true;
655
655
  return;
656
656
  }
657
+ if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
658
+ await this.frontend.start(getIntParameter('frontend'));
657
659
  try {
658
660
  await this.startMatterStorage();
659
661
  if (this.aggregatorSerialNumber && this.aggregatorUniqueId && this.matterStorageService) {
@@ -699,8 +701,6 @@ export class Matterbridge extends EventEmitter {
699
701
  this.shutdown = true;
700
702
  return;
701
703
  }
702
- if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
703
- await this.frontend.start(getIntParameter('frontend'));
704
704
  clearTimeout(this.checkUpdateTimeout);
705
705
  this.checkUpdateTimeout = setTimeout(async () => {
706
706
  const { checkUpdates } = await import('./update.js');
@@ -737,7 +737,7 @@ export class Matterbridge extends EventEmitter {
737
737
  return;
738
738
  }
739
739
  }
740
- async startPlugins() {
740
+ async startPlugins(wait = false, start = true) {
741
741
  for (const plugin of this.plugins) {
742
742
  plugin.configJson = await this.plugins.loadConfig(plugin);
743
743
  plugin.schemaJson = await this.plugins.loadSchema(plugin);
@@ -757,7 +757,10 @@ export class Matterbridge extends EventEmitter {
757
757
  plugin.started = false;
758
758
  plugin.configured = false;
759
759
  plugin.registeredDevices = undefined;
760
- this.plugins.load(plugin, true, 'Matterbridge is starting');
760
+ if (wait)
761
+ await this.plugins.load(plugin, start, 'Matterbridge is starting');
762
+ else
763
+ this.plugins.load(plugin, start, 'Matterbridge is starting');
761
764
  }
762
765
  this.frontend.wssSendRefreshRequired('plugins');
763
766
  }
@@ -949,7 +952,7 @@ export class Matterbridge extends EventEmitter {
949
952
  this.log.info('Updating matterbridge...');
950
953
  try {
951
954
  const { spawnCommand } = await import('./utils/spawn.js');
952
- await spawnCommand(this, 'npm', ['install', '-g', 'matterbridge', '--omit=dev', '--verbose']);
955
+ await spawnCommand(this, 'npm', ['install', '-g', 'matterbridge', '--omit=dev', '--verbose'], 'matterbridge');
953
956
  this.log.info('Matterbridge has been updated. Full restart required.');
954
957
  }
955
958
  catch (error) {
@@ -1252,7 +1255,14 @@ export class Matterbridge extends EventEmitter {
1252
1255
  async startChildbridge(delay = 1000) {
1253
1256
  if (!this.matterStorageManager)
1254
1257
  throw new Error('No storage manager initialized');
1255
- await this.startPlugins();
1258
+ this.log.debug('Loading all plugins in childbridge mode...');
1259
+ await this.startPlugins(true, false);
1260
+ this.log.debug('Creating server nodes for DynamicPlatform plugins and starting all plugins in childbridge mode...');
1261
+ for (const plugin of this.plugins.array().filter((p) => p.enabled && !p.error)) {
1262
+ if (plugin.type === 'DynamicPlatform')
1263
+ await this.createDynamicPlugin(plugin);
1264
+ this.plugins.start(plugin, 'Matterbridge is starting');
1265
+ }
1256
1266
  this.log.debug('Starting start matter interval in childbridge mode...');
1257
1267
  let failCount = 0;
1258
1268
  this.startMatterInterval = setInterval(async () => {
@@ -1280,9 +1290,6 @@ export class Matterbridge extends EventEmitter {
1280
1290
  plugin.error = true;
1281
1291
  }
1282
1292
  }
1283
- if (plugin.type === 'DynamicPlatform' && !plugin.locked) {
1284
- await this.createDynamicPlugin(plugin);
1285
- }
1286
1293
  }
1287
1294
  if (!allStarted)
1288
1295
  return;
@@ -1,5 +1,5 @@
1
1
  import { hasParameter } from './commandLine.js';
2
- export async function spawnCommand(matterbridge, command, args) {
2
+ export async function spawnCommand(matterbridge, command, args, packageName) {
3
3
  const { spawn } = await import('node:child_process');
4
4
  const cmdLine = command + ' ' + args.join(' ');
5
5
  if (process.platform === 'win32' && command === 'npm') {
@@ -13,6 +13,7 @@ export async function spawnCommand(matterbridge, command, args) {
13
13
  }
14
14
  matterbridge.log.debug(`Spawn command ${command} with ${args.join(' ')}`);
15
15
  return new Promise((resolve, reject) => {
16
+ matterbridge.frontend.wssSendLogMessage('spawn', matterbridge.log.now(), 'Matterbridge:spawn-init', packageName || `unknown-package`);
16
17
  const childProcess = spawn(command, args, {
17
18
  stdio: ['inherit', 'pipe', 'pipe'],
18
19
  });
@@ -22,6 +23,7 @@ export async function spawnCommand(matterbridge, command, args) {
22
23
  });
23
24
  childProcess.on('close', (code, signal) => {
24
25
  matterbridge.frontend.wssSendLogMessage('spawn', matterbridge.log.now(), 'Matterbridge:spawn', `child process closed with code ${code} and signal ${signal}`);
26
+ matterbridge.frontend.wssSendLogMessage('spawn', matterbridge.log.now(), 'Matterbridge:spawn-exit', 'Child process closed');
25
27
  if (code === 0) {
26
28
  if (cmdLine.startsWith('npm install -g'))
27
29
  matterbridge.log.notice(`Package ${cmdLine.replace('npm install -g ', '').replace('--verbose', '').replace('--omit=dev', '')} installed correctly`);
@@ -35,6 +37,7 @@ export async function spawnCommand(matterbridge, command, args) {
35
37
  });
36
38
  childProcess.on('exit', (code, signal) => {
37
39
  matterbridge.frontend.wssSendLogMessage('spawn', matterbridge.log.now(), 'Matterbridge:spawn', `child process exited with code ${code} and signal ${signal}`);
40
+ matterbridge.frontend.wssSendLogMessage('spawn', matterbridge.log.now(), 'Matterbridge:spawn-exit', 'Child process exited');
38
41
  if (code === 0) {
39
42
  matterbridge.log.debug(`Child process "${cmdLine}" exited with code ${code} and signal ${signal}`);
40
43
  resolve(true);