matterbridge 2.1.6-dev.4 → 2.1.6-dev.5

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
@@ -49,6 +49,7 @@ matterbridge-hass v. 0.0.8
49
49
  - [package]: Update matter.js to 0.12.4-alpha.0-20250212-b2729c9eb
50
50
  - [package]: Update matter.js to 0.12.4-alpha.0-20250213-1187f81eb
51
51
  - [package]: Update matter.js to 0.12.4-alpha.0-20250215-5af08a8d6
52
+ - [package]: Update matter.js to 0.12.4-alpha.0-20250217-b0bba5179
52
53
 
53
54
  <a href="https://www.buymeacoffee.com/luligugithub">
54
55
  <img src="./yellow-button.png" alt="Buy me a coffee" width="120">
package/dist/cli.js CHANGED
@@ -1,62 +1,48 @@
1
1
  #!/usr/bin/env node
2
2
  import { Matterbridge } from './matterbridge.js';
3
- import { EventEmitter } from 'events';
3
+ import { getIntParameter, hasParameter } from './utils/export.js';
4
+ import { AnsiLogger, BRIGHT, CYAN, db, YELLOW } from './logger/export.js';
5
+ import { EventEmitter } from 'node:events';
4
6
  export const cliEmitter = new EventEmitter();
5
7
  export let instance;
6
8
  let session;
7
9
  let memoryCheckInterval;
8
10
  let prevCpus;
9
11
  export let lastCpuUsage = 0;
10
- const cli = '\u001B[32m';
11
- const er = '\u001B[38;5;9m';
12
- const rs = '\u001B[40;0m';
13
- const db = '\u001B[38;5;245m';
14
- const CYAN = '\u001B[36m';
15
- const YELLOW = '\u001B[33m';
16
- const BRIGHT = '\u001B[1m';
17
- async function main() {
18
- await startCpuMemoryCheck();
19
- if (process.argv.includes('-inspector'))
20
- await startInspector();
21
- if (process.argv.includes('-debug'))
22
- console.log(cli + `CLI: Matterbridge.loadInstance() called` + rs);
23
- instance = await Matterbridge.loadInstance(true);
24
- registerHandlers();
25
- if (process.argv.includes('-debug'))
26
- console.log(cli + `CLI: Matterbridge.loadInstance() exited` + rs);
27
- }
12
+ let peakCpu = 0;
13
+ let peakRss = 0;
14
+ const log = new AnsiLogger({ logName: 'Cli', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
15
+ const formatMemoryUsage = (bytes) => {
16
+ if (bytes >= 1024 ** 3) {
17
+ return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
18
+ }
19
+ else if (bytes >= 1024 ** 2) {
20
+ return `${(bytes / 1024 ** 2).toFixed(2)} MB`;
21
+ }
22
+ else {
23
+ return `${(bytes / 1024).toFixed(2)} KB`;
24
+ }
25
+ };
26
+ const formatOsUpTime = (seconds) => {
27
+ if (seconds >= 86400) {
28
+ const days = Math.floor(seconds / 86400);
29
+ return `${days} day${days !== 1 ? 's' : ''}`;
30
+ }
31
+ if (seconds >= 3600) {
32
+ const hours = Math.floor(seconds / 3600);
33
+ return `${hours} hour${hours !== 1 ? 's' : ''}`;
34
+ }
35
+ if (seconds >= 60) {
36
+ const minutes = Math.floor(seconds / 60);
37
+ return `${minutes} minute${minutes !== 1 ? 's' : ''}`;
38
+ }
39
+ return `${seconds} second${seconds !== 1 ? 's' : ''}`;
40
+ };
28
41
  async function startCpuMemoryCheck() {
29
42
  const os = await import('node:os');
30
- if (process.argv.includes('-debug'))
31
- console.log(cli + `CLI: Cpu memory check started` + rs);
43
+ log.debug(`Cpu memory check started`);
32
44
  prevCpus = os.cpus();
33
45
  clearInterval(memoryCheckInterval);
34
- const formatMemoryUsage = (bytes) => {
35
- if (bytes >= 1024 ** 3) {
36
- return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
37
- }
38
- else if (bytes >= 1024 ** 2) {
39
- return `${(bytes / 1024 ** 2).toFixed(2)} MB`;
40
- }
41
- else {
42
- return `${(bytes / 1024).toFixed(2)} KB`;
43
- }
44
- };
45
- const formatOsUpTime = (seconds) => {
46
- if (seconds >= 86400) {
47
- const days = Math.floor(seconds / 86400);
48
- return `${days} day${days !== 1 ? 's' : ''}`;
49
- }
50
- if (seconds >= 3600) {
51
- const hours = Math.floor(seconds / 3600);
52
- return `${hours} hour${hours !== 1 ? 's' : ''}`;
53
- }
54
- if (seconds >= 60) {
55
- const minutes = Math.floor(seconds / 60);
56
- return `${minutes} minute${minutes !== 1 ? 's' : ''}`;
57
- }
58
- return `${seconds} second${seconds !== 1 ? 's' : ''}`;
59
- };
60
46
  const interval = () => {
61
47
  const systemUptime = formatOsUpTime(Math.floor(os.uptime()));
62
48
  const processUptime = formatOsUpTime(Math.floor(process.uptime()));
@@ -69,13 +55,14 @@ async function startCpuMemoryCheck() {
69
55
  const heapUsed = formatMemoryUsage(memoryUsageRaw.heapUsed);
70
56
  const external = formatMemoryUsage(memoryUsageRaw.external);
71
57
  const arrayBuffers = formatMemoryUsage(memoryUsageRaw.arrayBuffers);
58
+ if (memoryUsageRaw.rss > peakRss)
59
+ peakRss = memoryUsageRaw.rss;
72
60
  cliEmitter.emit('memory', totalMememory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers);
73
61
  const currCpus = os.cpus();
74
62
  let cpuUsageLog;
75
63
  if (currCpus.length !== prevCpus.length) {
76
64
  prevCpus = currCpus;
77
- if (process.argv.includes('-debug'))
78
- console.log(cli + `CLI: Cpu check length failed, resetting previous cpus` + rs);
65
+ log.debug(`Cpu check length failed, resetting previous cpus`);
79
66
  return;
80
67
  }
81
68
  let totalIdle = 0, totalTick = 0;
@@ -88,59 +75,63 @@ async function startCpuMemoryCheck() {
88
75
  });
89
76
  const cpuUsage = 100 - (totalIdle / totalTick) * 100;
90
77
  if (totalTick === 0 || isNaN(cpuUsage) || !isFinite(cpuUsage) || cpuUsage <= 0) {
91
- if (process.argv.includes('-debug') && lastCpuUsage != 0)
92
- console.log(cli + `CLI: Cpu check failed, using previous cpus` + rs);
78
+ if (lastCpuUsage != 0)
79
+ log.debug(`Cpu check failed, using previous cpus`);
93
80
  cpuUsageLog = lastCpuUsage.toFixed(2);
94
81
  }
95
82
  else {
96
83
  cpuUsageLog = cpuUsage.toFixed(2);
97
84
  lastCpuUsage = cpuUsage;
85
+ if (lastCpuUsage > peakCpu)
86
+ peakCpu = lastCpuUsage;
98
87
  cliEmitter.emit('cpu', lastCpuUsage);
99
88
  }
100
89
  prevCpus = currCpus;
101
- if (process.argv.includes('-debug'))
102
- console.log(`${YELLOW}${BRIGHT}Cpu usage:${db} ${CYAN}${cpuUsageLog.padStart(6, ' ')} %${db} ${YELLOW}${BRIGHT}Memory usage:${db} rss ${CYAN}${rss}${db} heapTotal ${CYAN}${heapTotal}${db} heapUsed ${CYAN}${heapUsed}${db} external ${external} arrayBuffers ${arrayBuffers}` +
103
- rs);
90
+ log.debug(`***${YELLOW}${BRIGHT}Cpu usage:${db} ${CYAN}${cpuUsageLog.padStart(6, ' ')} %${db} ${YELLOW}${BRIGHT}Memory usage:${db} rss ${CYAN}${rss}${db} heapTotal ${CYAN}${heapTotal}${db} heapUsed ${CYAN}${heapUsed}${db} external ${external} arrayBuffers ${arrayBuffers}`);
104
91
  };
105
92
  interval();
106
- memoryCheckInterval = setInterval(interval, 10 * 1000);
93
+ memoryCheckInterval = setInterval(interval, getIntParameter('memoryinterval') ?? 10 * 1000);
107
94
  }
108
95
  async function stopCpuMemoryCheck() {
109
- if (process.argv.includes('-debug'))
110
- console.log(cli + `CLI: Cpu memory check stopped` + rs);
96
+ log.debug(`***Cpu memory check stopped. Peak cpu: ${CYAN}${peakCpu.toFixed(2)} %${db}. Peak rss: ${CYAN}${formatMemoryUsage(peakRss)}${db}.`);
111
97
  clearInterval(memoryCheckInterval);
112
98
  }
113
99
  async function startInspector() {
114
100
  const { Session } = await import('node:inspector');
115
- if (process.argv.includes('-debug'))
116
- console.log(cli + `CLI: Starting heap sampling...` + rs);
101
+ log.debug(`Starting heap sampling...`);
117
102
  session = new Session();
118
103
  session.connect();
119
- session.post('HeapProfiler.startSampling', {}, (err) => {
120
- if (err)
121
- console.error(err);
122
- else
123
- console.log(cli + `CLI: Heap sampling started` + rs);
104
+ await new Promise((resolve, reject) => {
105
+ session?.post('HeapProfiler.startSampling', {}, (err) => (err ? reject(err) : resolve()));
124
106
  });
107
+ log.debug(`Started heap sampling`);
125
108
  }
126
109
  async function stopInspector() {
127
110
  const { writeFileSync } = await import('node:fs');
128
- if (process.argv.includes('-debug'))
129
- console.log(cli + `CLI: Stopping heap sampling...` + rs);
130
- session?.post('HeapProfiler.stopSampling', (err, result) => {
131
- if (err) {
132
- console.error(err);
133
- }
134
- else {
135
- const profile = JSON.stringify(result.profile, null, 2);
136
- writeFileSync('heap-sampling-profile.heapsnapshot', profile);
137
- console.log(cli + `CLI: Heap sampling profile saved to heap-sampling-profile.heapsnapshot` + rs);
138
- }
139
- session?.disconnect();
111
+ log.debug(`Stopping heap sampling...`);
112
+ if (!session) {
113
+ log.error('No active inspector session.');
114
+ return;
115
+ }
116
+ try {
117
+ const result = await new Promise((resolve, reject) => {
118
+ session?.post('HeapProfiler.stopSampling', (err, result) => (err ? reject(err) : resolve(result)));
119
+ });
120
+ const profile = JSON.stringify(result.profile);
121
+ writeFileSync('Heap-sampling-profile.heapprofile', profile);
122
+ log.debug('Heap sampling profile saved to Heap-sampling-profile.heapprofile');
123
+ }
124
+ catch (err) {
125
+ log.error(`Failed to stop heap sampling: ${err instanceof Error ? err.message : err}`);
126
+ }
127
+ finally {
128
+ session.disconnect();
140
129
  session = undefined;
141
- });
130
+ log.debug(`Stopped heap sampling`);
131
+ }
142
132
  }
143
133
  function registerHandlers() {
134
+ log.debug('Registering event handlers...');
144
135
  if (instance)
145
136
  instance.on('shutdown', async () => shutdown());
146
137
  if (instance)
@@ -151,38 +142,49 @@ function registerHandlers() {
151
142
  instance.on('startmemorycheck', async () => start());
152
143
  if (instance)
153
144
  instance.on('stopmemorycheck', async () => stop());
145
+ log.debug('Registered event handlers');
154
146
  }
155
147
  async function shutdown() {
156
- if (process.argv.includes('-debug'))
157
- console.log(cli + 'CLI: received shutdown event, exiting...' + rs);
158
- if (process.argv.includes('-inspector'))
148
+ log.debug('Received shutdown event, exiting...');
149
+ if (hasParameter('inspect'))
159
150
  await stopInspector();
160
151
  await stopCpuMemoryCheck();
161
152
  process.exit(0);
162
153
  }
163
154
  async function restart() {
164
- if (process.argv.includes('-debug'))
165
- console.log(cli + 'CLI: received restart event, loading...' + rs);
155
+ log.debug('Received restart event, loading...');
166
156
  instance = await Matterbridge.loadInstance(true);
167
157
  registerHandlers();
168
158
  }
169
159
  async function update() {
170
- if (process.argv.includes('-debug'))
171
- console.log(cli + 'CLI: received update event, updating...' + rs);
160
+ log.debug('Received update event, updating...');
172
161
  instance = await Matterbridge.loadInstance(true);
173
162
  registerHandlers();
174
163
  }
175
164
  async function start() {
176
- if (process.argv.includes('-debug'))
177
- console.log(cli + 'CLI: received start memory check event' + rs);
165
+ log.debug('Received start memory check event');
178
166
  startCpuMemoryCheck();
179
167
  }
180
168
  async function stop() {
181
- if (process.argv.includes('-debug'))
182
- console.log(cli + 'CLI: received stop memory check event' + rs);
169
+ log.debug('Received stop memory check event');
183
170
  stopCpuMemoryCheck();
184
171
  }
172
+ async function main() {
173
+ log.debug(`Cli main() started`);
174
+ await startCpuMemoryCheck();
175
+ if (hasParameter('inspect'))
176
+ await startInspector();
177
+ log.debug(`***Matterbridge.loadInstance(true) called`);
178
+ instance = await Matterbridge.loadInstance(true);
179
+ log.debug(`***Matterbridge.loadInstance(true) exited`);
180
+ if (instance.shutdown) {
181
+ shutdown();
182
+ }
183
+ else {
184
+ registerHandlers();
185
+ }
186
+ }
185
187
  process.title = 'matterbridge';
186
188
  main().catch((error) => {
187
- console.error(er + `CLI: Matterbridge.loadInstance() failed with error: ${error}` + rs);
189
+ log.error(`Matterbridge.loadInstance() failed with error: ${error instanceof Error ? error.message : error}`);
188
190
  });
package/dist/frontend.js CHANGED
@@ -1,14 +1,15 @@
1
1
  import { EndpointServer, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat } from '@matter/main';
2
- import { createServer } from 'http';
2
+ import { createServer } from 'node:http';
3
3
  import https from 'https';
4
4
  import express from 'express';
5
5
  import WebSocket, { WebSocketServer } from 'ws';
6
- import os from 'os';
7
- import path from 'path';
8
- import { promises as fs } from 'fs';
6
+ import os from 'node:os';
7
+ import path from 'node:path';
8
+ import { promises as fs } from 'node:fs';
9
9
  import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, wr, YELLOW } from './logger/export.js';
10
- import { createZip, deepCopy, hasParameter, isValidNumber, isValidObject, isValidString } from './utils/utils.js';
10
+ import { createZip, deepCopy, isValidNumber, isValidObject, isValidString } from './utils/export.js';
11
11
  import { plg } from './matterbridgeTypes.js';
12
+ import { hasParameter } from './utils/export.js';
12
13
  export const WS_ID_LOG = 0;
13
14
  export const WS_ID_REFRESH_NEEDED = 1;
14
15
  export const WS_ID_RESTART_NEEDED = 2;
@@ -236,7 +237,7 @@ export class Frontend {
236
237
  space_available_size: this.formatMemoryUsage(space.space_available_size),
237
238
  physical_space_size: this.formatMemoryUsage(space.physical_space_size),
238
239
  }));
239
- const { default: module } = await import('module');
240
+ const { default: module } = await import('node:module');
240
241
  const loadedModules = module._cache ? Object.keys(module._cache).sort() : [];
241
242
  const memoryReport = {
242
243
  memoryUsage,
package/dist/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import { Matterbridge } from './matterbridge.js';
2
+ import { hasParameter } from './utils/export.js';
3
+ import { AnsiLogger } from './logger/export.js';
2
4
  export { SemanticNamespace, ClosureTag, CompassDirectionTag, CompassLocationTag, DirectionTag, ElectricalMeasurementTag, LaundryTag, LevelTag, LocationTag, NumberTag, PositionTag, PowerSourceTag, RefrigeratorTag, RoomAirConditionerTag, SwitchesTag, } from '@matter/main';
3
5
  export * from '@matter/main/clusters';
4
6
  export * from '@matter/main/types';
@@ -11,16 +13,12 @@ export * from './matterbridgeDeviceTypes.js';
11
13
  export * from './matterbridgePlatform.js';
12
14
  export * from './matterbridgeAccessoryPlatform.js';
13
15
  export * from './matterbridgeDynamicPlatform.js';
14
- const cli = '\u001B[32m';
15
- const er = '\u001B[38;5;9m';
16
- const rs = '\u001B[40;0m';
16
+ const log = new AnsiLogger({ logName: 'Main', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
17
17
  async function main() {
18
- if (process.argv.includes('-debug'))
19
- console.log(cli + 'MAIN: Matterbridge.loadInstance() called' + rs);
18
+ log.debug('***Matterbridge.loadInstance() called');
20
19
  await Matterbridge.loadInstance();
21
- if (process.argv.includes('-debug'))
22
- console.log(cli + 'MAIN: Matterbridge.loadInstance() exited' + rs);
20
+ log.debug('***Matterbridge.loadInstance() exited');
23
21
  }
24
22
  main().catch((error) => {
25
- console.error(er + `MAIN: Matterbridge.loadInstance() failed with error: ${error}` + rs);
23
+ log.error(`Matterbridge.loadInstance() failed with error: ${error instanceof Error ? error.message : error}`);
26
24
  });
@@ -1,13 +1,11 @@
1
- import { fileURLToPath } from 'url';
2
- import { promises as fs } from 'fs';
3
- import { exec, spawn } from 'child_process';
4
- import EventEmitter from 'events';
5
- import os from 'os';
6
- import path from 'path';
7
- import { randomBytes } from 'crypto';
8
- import { NodeStorageManager } from './storage/export.js';
1
+ import os from 'node:os';
2
+ import path from 'node:path';
3
+ import { promises as fs } from 'node:fs';
4
+ import EventEmitter from 'node:events';
9
5
  import { AnsiLogger, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN, nt } from './logger/export.js';
10
- import { logInterfaces, copyDirectory, getParameter, getIntParameter, hasParameter, getNpmPackageVersion } from './utils/utils.js';
6
+ import { NodeStorageManager } from './storage/export.js';
7
+ import { getParameter, getIntParameter, hasParameter } from './utils/export.js';
8
+ import { logInterfaces, copyDirectory, getNpmPackageVersion, getGlobalNodeModules } from './utils/utils.js';
11
9
  import { PluginManager } from './pluginManager.js';
12
10
  import { DeviceManager } from './deviceManager.js';
13
11
  import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
@@ -86,6 +84,7 @@ export class Matterbridge extends EventEmitter {
86
84
  bridgeMode = '';
87
85
  restartMode = '';
88
86
  profile = getParameter('profile');
87
+ shutdown = false;
89
88
  edge = true;
90
89
  log;
91
90
  matterbrideLoggerFile = 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.log';
@@ -174,6 +173,7 @@ export class Matterbridge extends EventEmitter {
174
173
  return Matterbridge.instance;
175
174
  }
176
175
  async destroyInstance() {
176
+ this.log.info(`Destroy instance...`);
177
177
  const servers = [];
178
178
  if (this.bridgeMode === 'bridge') {
179
179
  if (this.serverNode)
@@ -186,6 +186,7 @@ export class Matterbridge extends EventEmitter {
186
186
  }
187
187
  }
188
188
  await this.cleanup('destroying instance...', false);
189
+ this.log.info(`Dispose ${servers.length} MdnsService...`);
189
190
  for (const server of servers) {
190
191
  await server.env.get(MdnsService)[Symbol.asyncDispose]();
191
192
  this.log.info(`Closed ${server.id} MdnsService`);
@@ -443,7 +444,7 @@ export class Matterbridge extends EventEmitter {
443
444
  - disable [plugin name]: disable the globally installed plugin with the given name
444
445
  - reset [plugin path]: remove the commissioning for the plugin from the given absolute or relative path (childbridge mode). Shutdown Matterbridge before using it!
445
446
  - reset [plugin name]: remove the commissioning for the globally installed plugin (childbridge mode). Shutdown Matterbridge before using it!${rs}`);
446
- this.emit('shutdown');
447
+ this.shutdown = true;
447
448
  return;
448
449
  }
449
450
  if (hasParameter('list')) {
@@ -472,7 +473,7 @@ export class Matterbridge extends EventEmitter {
472
473
  this.log.info(` └─ endpoint ${RED}${device.endpoint}${nf} ${typ}${device.endpointName}${nf} ${debugStringify(device.clusterServersId)}`);
473
474
  }
474
475
  });
475
- this.emit('shutdown');
476
+ this.shutdown = true;
476
477
  return;
477
478
  }
478
479
  if (hasParameter('logstorage')) {
@@ -482,41 +483,42 @@ export class Matterbridge extends EventEmitter {
482
483
  this.log.info(`${plg}${plugin.name}${nf} storage log`);
483
484
  await plugin.nodeContext?.logStorage();
484
485
  }
485
- this.emit('shutdown');
486
+ this.shutdown = true;
486
487
  return;
487
488
  }
488
489
  if (hasParameter('loginterfaces')) {
489
490
  this.log.info(`${plg}Matterbridge${nf} network interfaces log`);
490
491
  logInterfaces();
491
- this.emit('shutdown');
492
+ this.shutdown = true;
492
493
  return;
493
494
  }
494
495
  if (getParameter('add')) {
495
496
  this.log.debug(`Adding plugin ${getParameter('add')}`);
496
497
  await this.plugins.add(getParameter('add'));
497
- this.emit('shutdown');
498
+ this.shutdown = true;
498
499
  return;
499
500
  }
500
501
  if (getParameter('remove')) {
501
502
  this.log.debug(`Removing plugin ${getParameter('remove')}`);
502
503
  await this.plugins.remove(getParameter('remove'));
503
- this.emit('shutdown');
504
+ this.shutdown = true;
504
505
  return;
505
506
  }
506
507
  if (getParameter('enable')) {
507
508
  this.log.debug(`Enabling plugin ${getParameter('enable')}`);
508
509
  await this.plugins.enable(getParameter('enable'));
509
- this.emit('shutdown');
510
+ this.shutdown = true;
510
511
  return;
511
512
  }
512
513
  if (getParameter('disable')) {
513
514
  this.log.debug(`Disabling plugin ${getParameter('disable')}`);
514
515
  await this.plugins.disable(getParameter('disable'));
515
- this.emit('shutdown');
516
+ this.shutdown = true;
516
517
  return;
517
518
  }
518
519
  if (hasParameter('factoryreset')) {
519
520
  await this.shutdownProcessAndFactoryReset();
521
+ this.shutdown = true;
520
522
  return;
521
523
  }
522
524
  try {
@@ -528,6 +530,7 @@ export class Matterbridge extends EventEmitter {
528
530
  }
529
531
  if (hasParameter('reset') && getParameter('reset') === undefined) {
530
532
  await this.shutdownProcessAndReset();
533
+ this.shutdown = true;
531
534
  return;
532
535
  }
533
536
  if (hasParameter('reset') && getParameter('reset') !== undefined) {
@@ -535,20 +538,23 @@ export class Matterbridge extends EventEmitter {
535
538
  const plugin = this.plugins.get(getParameter('reset'));
536
539
  if (plugin) {
537
540
  const matterStorageManager = await this.matterStorageService?.open(plugin.name);
538
- if (!matterStorageManager)
541
+ if (!matterStorageManager) {
539
542
  this.log.error(`Plugin ${plg}${plugin.name}${er} storageManager not found`);
540
- await matterStorageManager?.createContext('events')?.clearAll();
541
- await matterStorageManager?.createContext('fabrics')?.clearAll();
542
- await matterStorageManager?.createContext('root')?.clearAll();
543
- await matterStorageManager?.createContext('sessions')?.clearAll();
544
- await matterStorageManager?.createContext('persist')?.clearAll();
545
- this.log.info(`Reset commissionig for plugin ${plg}${plugin.name}${nf} done! Remove the device from the controller.`);
543
+ }
544
+ else {
545
+ await matterStorageManager?.createContext('events')?.clearAll();
546
+ await matterStorageManager?.createContext('fabrics')?.clearAll();
547
+ await matterStorageManager?.createContext('root')?.clearAll();
548
+ await matterStorageManager?.createContext('sessions')?.clearAll();
549
+ await matterStorageManager?.createContext('persist')?.clearAll();
550
+ this.log.info(`Reset commissionig for plugin ${plg}${plugin.name}${nf} done! Remove the device from the controller.`);
551
+ }
546
552
  }
547
553
  else {
548
554
  this.log.warn(`Plugin ${plg}${getParameter('reset')}${wr} not registerd in matterbridge`);
549
555
  }
550
556
  await this.stopMatterStorage();
551
- this.emit('shutdown');
557
+ this.shutdown = true;
552
558
  return;
553
559
  }
554
560
  if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
@@ -720,6 +726,7 @@ export class Matterbridge extends EventEmitter {
720
726
  this.homeDirectory = getParameter('homedir') ?? os.homedir();
721
727
  this.matterbridgeInformation.homeDirectory = this.homeDirectory;
722
728
  this.log.debug(`Home Directory: ${this.homeDirectory}`);
729
+ const { fileURLToPath } = await import('node:url');
723
730
  const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
724
731
  this.rootDirectory = path.resolve(currentFileDirectory, '../');
725
732
  this.matterbridgeInformation.rootDirectory = this.rootDirectory;
@@ -728,7 +735,9 @@ export class Matterbridge extends EventEmitter {
728
735
  this.globalModulesDirectory = this.matterbridgeInformation.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
729
736
  if (this.globalModulesDirectory === '') {
730
737
  try {
731
- this.globalModulesDirectory = await this.getGlobalNodeModules();
738
+ this.execRunningCount++;
739
+ this.globalModulesDirectory = await getGlobalNodeModules();
740
+ this.execRunningCount--;
732
741
  this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory;
733
742
  this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
734
743
  await this.nodeContext?.set('globalModulesDirectory', this.globalModulesDirectory);
@@ -797,20 +806,6 @@ export class Matterbridge extends EventEmitter {
797
806
  const cmdArgs = process.argv.slice(2).join(' ');
798
807
  this.log.debug(`Command Line Arguments: ${cmdArgs}`);
799
808
  }
800
- async getGlobalNodeModules() {
801
- return new Promise((resolve, reject) => {
802
- this.execRunningCount++;
803
- exec('npm root -g', (error, stdout) => {
804
- this.execRunningCount--;
805
- if (error) {
806
- reject(error);
807
- }
808
- else {
809
- resolve(stdout.trim());
810
- }
811
- });
812
- });
813
- }
814
809
  async getMatterbridgeLatestVersion() {
815
810
  getNpmPackageVersion('matterbridge')
816
811
  .then(async (version) => {
@@ -1202,11 +1197,7 @@ export class Matterbridge extends EventEmitter {
1202
1197
  if (!plugin.enabled)
1203
1198
  continue;
1204
1199
  if (plugin.type === 'DynamicPlatform') {
1205
- plugin.locked = true;
1206
- plugin.storageContext = await this.createServerNodeContext(plugin.name, 'Matterbridge', bridge.code, this.aggregatorVendorId, 'Matterbridge', this.aggregatorProductId, plugin.description);
1207
- plugin.serverNode = await this.createServerNode(plugin.storageContext, this.port ? this.port++ : undefined, this.passcode ? this.passcode++ : undefined, this.discriminator ? this.discriminator++ : undefined);
1208
- plugin.aggregatorNode = await this.createAggregatorNode(plugin.storageContext);
1209
- await plugin.serverNode.add(plugin.aggregatorNode);
1200
+ await this.createDynamicPlugin(plugin);
1210
1201
  }
1211
1202
  }
1212
1203
  await this.startPlugins();
@@ -1259,7 +1250,7 @@ export class Matterbridge extends EventEmitter {
1259
1250
  for (const plugin of this.plugins) {
1260
1251
  if (!plugin.enabled || plugin.error)
1261
1252
  continue;
1262
- if (!plugin.addedDevices || plugin.addedDevices === 0) {
1253
+ if (plugin.type !== 'DynamicPlatform' && (!plugin.addedDevices || plugin.addedDevices === 0)) {
1263
1254
  this.log.error(`Plugin ${plg}${plugin.name}${er} didn't add any devices to Matterbridge. Verify the plugin configuration.`);
1264
1255
  continue;
1265
1256
  }
@@ -1315,6 +1306,7 @@ export class Matterbridge extends EventEmitter {
1315
1306
  this.log.info('Matter node storage closed');
1316
1307
  }
1317
1308
  async createServerNodeContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber) {
1309
+ const { randomBytes } = await import('node:crypto');
1318
1310
  if (!this.matterStorageService)
1319
1311
  throw new Error('No storage service initialized');
1320
1312
  this.log.info(`Creating server node storage context "${pluginName}.persist" for ${pluginName}...`);
@@ -1557,13 +1549,18 @@ export class Matterbridge extends EventEmitter {
1557
1549
  }
1558
1550
  }
1559
1551
  async advertiseServerNode(matterServerNode) {
1560
- if (matterServerNode && matterServerNode.lifecycle.isCommissioned) {
1552
+ if (matterServerNode) {
1561
1553
  await matterServerNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
1562
1554
  const { qrPairingCode, manualPairingCode } = matterServerNode.state.commissioning.pairingCodes;
1563
1555
  this.log.notice(`Advertising for ${matterServerNode.id} is now started with the following pairing codes: qrPairingCode ${qrPairingCode}, manualPairingCode ${manualPairingCode}`);
1564
1556
  return { qrPairingCode, manualPairingCode };
1565
1557
  }
1566
- return undefined;
1558
+ }
1559
+ async stopAdvertiseServerNode(matterServerNode) {
1560
+ if (matterServerNode && matterServerNode.lifecycle.isOnline) {
1561
+ await matterServerNode.env.get(DeviceCommissioner)?.endCommissioning();
1562
+ this.log.notice(`Stopped advertising for ${matterServerNode.id}`);
1563
+ }
1567
1564
  }
1568
1565
  async createAggregatorNode(storageContext) {
1569
1566
  this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator `);
@@ -1635,15 +1632,6 @@ export class Matterbridge extends EventEmitter {
1635
1632
  plugin.registeredDevices--;
1636
1633
  if (plugin.addedDevices !== undefined)
1637
1634
  plugin.addedDevices--;
1638
- if (plugin.registeredDevices === 0 && plugin.addedDevices === 0) {
1639
- if (plugin.serverNode) {
1640
- await this.stopServerNode(plugin.serverNode);
1641
- plugin.locked = false;
1642
- plugin.aggregatorNode = undefined;
1643
- plugin.serverNode = undefined;
1644
- this.log.info(`Stopped server node for plugin ${plg}${pluginName}${nf}`);
1645
- }
1646
- }
1647
1635
  }
1648
1636
  this.devices.remove(device);
1649
1637
  }
@@ -1702,7 +1690,7 @@ export class Matterbridge extends EventEmitter {
1702
1690
  getVendorIdName = (vendorId) => {
1703
1691
  if (!vendorId)
1704
1692
  return '';
1705
- let vendorName = '';
1693
+ let vendorName = '(Unknown vendorId)';
1706
1694
  switch (vendorId) {
1707
1695
  case 4937:
1708
1696
  vendorName = '(AppleHome)';
@@ -1732,15 +1720,13 @@ export class Matterbridge extends EventEmitter {
1732
1720
  vendorName = '(eWeLink)';
1733
1721
  break;
1734
1722
  case 65521:
1735
- vendorName = '(PythonMatterServer)';
1736
- break;
1737
- default:
1738
- vendorName = '(unknown)';
1723
+ vendorName = '(MatterServer)';
1739
1724
  break;
1740
1725
  }
1741
1726
  return vendorName;
1742
1727
  };
1743
1728
  async spawnCommand(command, args = []) {
1729
+ const { spawn } = await import('node:child_process');
1744
1730
  const cmdLine = command + ' ' + args.join(' ');
1745
1731
  if (process.platform === 'win32' && command === 'npm') {
1746
1732
  const argstring = 'npm ' + args.join(' ');
@@ -1,6 +1,6 @@
1
1
  import { AnsiLogger, BLUE, CYAN, YELLOW, db, debugStringify, er, hk, or, zb } from './logger/export.js';
2
2
  import { bridgedNode } from './matterbridgeDeviceTypes.js';
3
- import { isValidNumber, isValidObject } from './utils/utils.js';
3
+ import { isValidNumber, isValidObject } from './utils/export.js';
4
4
  import { MatterbridgeBehavior, MatterbridgeBehaviorDevice, MatterbridgeIdentifyServer, MatterbridgeOnOffServer, MatterbridgeLevelControlServer, MatterbridgeColorControlServer, MatterbridgeWindowCoveringServer, MatterbridgeThermostatServer, MatterbridgeFanControlServer, MatterbridgeDoorLockServer, MatterbridgeModeSelectServer, MatterbridgeValveConfigurationAndControlServer, MatterbridgeSmokeCoAlarmServer, MatterbridgeBooleanStateConfigurationServer, MatterbridgeSwitchServer, } from './matterbridgeBehaviors.js';
5
5
  import { addClusterServers, addFixedLabel, addOptionalClusterServers, addRequiredClusterServers, addUserLabel, capitalizeFirstLetter, createUniqueId, getBehavior, getBehaviourTypesFromClusterClientIds, getBehaviourTypesFromClusterServerIds, getDefaultFlowMeasurementClusterServer, getDefaultIlluminanceMeasurementClusterServer, getDefaultPressureMeasurementClusterServer, getDefaultRelativeHumidityMeasurementClusterServer, getDefaultTemperatureMeasurementClusterServer, getDefaultOccupancySensingClusterServer, lowercaseFirstLetter, updateAttribute, getClusterId, getAttributeId, setAttribute, getAttribute, checkNotLatinCharacters, generateUniqueId, subscribeAttribute, } from './matterbridgeEndpointHelpers.js';
6
6
  import { Endpoint, Lifecycle, MutableEndpoint, NamedHandler, SupportedBehaviors, VendorId } from '@matter/main';
@@ -1,5 +1,6 @@
1
- import { createHash } from 'crypto';
1
+ import { createHash } from 'node:crypto';
2
2
  import { BLUE, CYAN, db, debugStringify, er, hk, or, YELLOW, zb } from './logger/export.js';
3
+ import { deepCopy, deepEqual, isValidArray } from './utils/export.js';
3
4
  import { MatterbridgeIdentifyServer, MatterbridgeOnOffServer, MatterbridgeLevelControlServer, MatterbridgeColorControlServer, MatterbridgeWindowCoveringServer, MatterbridgeThermostatServer, MatterbridgeFanControlServer, MatterbridgeDoorLockServer, MatterbridgeModeSelectServer, MatterbridgeValveConfigurationAndControlServer, MatterbridgeSmokeCoAlarmServer, MatterbridgeBooleanStateConfigurationServer, } from './matterbridgeBehaviors.js';
4
5
  import { Lifecycle } from '@matter/main';
5
6
  import { getClusterNameById } from '@matter/main/types';
@@ -73,7 +74,6 @@ import { Pm25ConcentrationMeasurementServer } from '@matter/main/behaviors/pm25-
73
74
  import { Pm10ConcentrationMeasurementServer } from '@matter/main/behaviors/pm10-concentration-measurement';
74
75
  import { RadonConcentrationMeasurementServer } from '@matter/main/behaviors/radon-concentration-measurement';
75
76
  import { TotalVolatileOrganicCompoundsConcentrationMeasurementServer } from '@matter/main/behaviors/total-volatile-organic-compounds-concentration-measurement';
76
- import { deepCopy, deepEqual, isValidArray } from './utils/utils.js';
77
77
  export function capitalizeFirstLetter(name) {
78
78
  if (!name)
79
79
  return name;