matterbridge 1.6.8-dev.2 → 1.6.8-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.
Files changed (83) hide show
  1. package/CHANGELOG.md +8 -7
  2. package/README.md +1 -1
  3. package/dist/cli.js +0 -26
  4. package/dist/cluster/export.js +0 -2
  5. package/dist/defaultConfigSchema.js +3 -24
  6. package/dist/deviceManager.js +1 -26
  7. package/dist/index.js +0 -30
  8. package/dist/logger/export.js +0 -1
  9. package/dist/matter/export.js +0 -1
  10. package/dist/matterbridge.js +167 -660
  11. package/dist/matterbridgeAccessoryPlatform.js +0 -33
  12. package/dist/matterbridgeBehaviors.js +1 -29
  13. package/dist/matterbridgeDevice.js +9 -995
  14. package/dist/matterbridgeDeviceTypes.js +11 -82
  15. package/dist/matterbridgeDynamicPlatform.js +0 -33
  16. package/dist/matterbridgeEdge.js +2 -531
  17. package/dist/matterbridgeEndpoint.js +11 -1120
  18. package/dist/matterbridgePlatform.js +11 -91
  19. package/dist/matterbridgeTypes.js +0 -24
  20. package/dist/matterbridgeWebsocket.js +0 -45
  21. package/dist/pluginManager.js +3 -238
  22. package/dist/storage/export.js +0 -1
  23. package/dist/utils/colorUtils.js +2 -205
  24. package/dist/utils/export.js +0 -1
  25. package/dist/utils/utils.js +7 -252
  26. package/npm-shrinkwrap.json +6 -6
  27. package/package.json +1 -1
  28. package/dist/cli.d.ts.map +0 -1
  29. package/dist/cli.js.map +0 -1
  30. package/dist/cluster/export.d.ts.map +0 -1
  31. package/dist/cluster/export.js.map +0 -1
  32. package/dist/defaultConfigSchema.d.ts.map +0 -1
  33. package/dist/defaultConfigSchema.js.map +0 -1
  34. package/dist/deviceManager.d.ts +0 -46
  35. package/dist/deviceManager.d.ts.map +0 -1
  36. package/dist/deviceManager.js.map +0 -1
  37. package/dist/index.d.ts.map +0 -1
  38. package/dist/index.js.map +0 -1
  39. package/dist/logger/export.d.ts.map +0 -1
  40. package/dist/logger/export.js.map +0 -1
  41. package/dist/matter/export.d.ts.map +0 -1
  42. package/dist/matter/export.js.map +0 -1
  43. package/dist/matterbridge.d.ts +0 -466
  44. package/dist/matterbridge.d.ts.map +0 -1
  45. package/dist/matterbridge.js.map +0 -1
  46. package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
  47. package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
  48. package/dist/matterbridgeBehaviors.d.ts +0 -116
  49. package/dist/matterbridgeBehaviors.d.ts.map +0 -1
  50. package/dist/matterbridgeBehaviors.js.map +0 -1
  51. package/dist/matterbridgeDevice.d.ts +0 -1142
  52. package/dist/matterbridgeDevice.d.ts.map +0 -1
  53. package/dist/matterbridgeDevice.js.map +0 -1
  54. package/dist/matterbridgeDeviceTypes.d.ts +0 -109
  55. package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
  56. package/dist/matterbridgeDeviceTypes.js.map +0 -1
  57. package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
  58. package/dist/matterbridgeDynamicPlatform.js.map +0 -1
  59. package/dist/matterbridgeEdge.d.ts +0 -90
  60. package/dist/matterbridgeEdge.d.ts.map +0 -1
  61. package/dist/matterbridgeEdge.js.map +0 -1
  62. package/dist/matterbridgeEndpoint.d.ts +0 -1134
  63. package/dist/matterbridgeEndpoint.d.ts.map +0 -1
  64. package/dist/matterbridgeEndpoint.js.map +0 -1
  65. package/dist/matterbridgePlatform.d.ts +0 -114
  66. package/dist/matterbridgePlatform.d.ts.map +0 -1
  67. package/dist/matterbridgePlatform.js.map +0 -1
  68. package/dist/matterbridgeTypes.d.ts.map +0 -1
  69. package/dist/matterbridgeTypes.js.map +0 -1
  70. package/dist/matterbridgeWebsocket.d.ts.map +0 -1
  71. package/dist/matterbridgeWebsocket.js.map +0 -1
  72. package/dist/pluginManager.d.ts +0 -238
  73. package/dist/pluginManager.d.ts.map +0 -1
  74. package/dist/pluginManager.js.map +0 -1
  75. package/dist/storage/export.d.ts.map +0 -1
  76. package/dist/storage/export.js.map +0 -1
  77. package/dist/utils/colorUtils.d.ts.map +0 -1
  78. package/dist/utils/colorUtils.js.map +0 -1
  79. package/dist/utils/export.d.ts.map +0 -1
  80. package/dist/utils/export.js.map +0 -1
  81. package/dist/utils/utils.d.ts +0 -221
  82. package/dist/utils/utils.d.ts.map +0 -1
  83. package/dist/utils/utils.js.map +0 -1
@@ -1,26 +1,3 @@
1
- /**
2
- * This file contains the class Matterbridge.
3
- *
4
- * @file matterbridge.ts
5
- * @author Luca Liguori
6
- * @date 2023-12-29
7
- * @version 1.5.2
8
- *
9
- * Copyright 2023, 2024, 2025 Luca Liguori.
10
- *
11
- * Licensed under the Apache License, Version 2.0 (the "License");
12
- * you may not use this file except in compliance with the License.
13
- * You may obtain a copy of the License at
14
- *
15
- * http://www.apache.org/licenses/LICENSE-2.0
16
- *
17
- * Unless required by applicable law or agreed to in writing, software
18
- * distributed under the License is distributed on an "AS IS" BASIS,
19
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
- * See the License for the specific language governing permissions and
21
- * limitations under the License. *
22
- */
23
- // Node.js modules
24
1
  import { fileURLToPath } from 'url';
25
2
  import { promises as fs } from 'fs';
26
3
  import { exec, spawn } from 'child_process';
@@ -29,36 +6,27 @@ import EventEmitter from 'events';
29
6
  import os from 'os';
30
7
  import path from 'path';
31
8
  import { randomBytes } from 'crypto';
32
- // Package modules
33
9
  import https from 'https';
34
10
  import express from 'express';
35
11
  import WebSocket, { WebSocketServer } from 'ws';
36
- // NodeStorage and AnsiLogger modules
37
12
  import { NodeStorageManager } from 'node-persist-manager';
38
13
  import { AnsiLogger, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, stringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN, nt, idn, or, hk, BLUE } from 'node-ansi-logger';
39
- // Matterbridge
40
14
  import { MatterbridgeDevice } from './matterbridgeDevice.js';
41
15
  import { WS_ID_LOG, WS_ID_REFRESH_NEEDED, WS_ID_RESTART_NEEDED, wsMessageHandler } from './matterbridgeWebsocket.js';
42
16
  import { logInterfaces, wait, waiter, createZip, copyDirectory, getParameter, getIntParameter, hasParameter } from './utils/utils.js';
43
17
  import { PluginManager } from './pluginManager.js';
44
18
  import { DeviceManager } from './deviceManager.js';
45
19
  import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
46
- // @matter
47
- import { DeviceTypeId, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, VendorId, StorageManager, EndpointServer } from '@matter/main';
20
+ import { DeviceTypeId, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, VendorId, StorageManager, EndpointServer, StorageService, Environment } from '@matter/main';
48
21
  import { BasicInformationCluster, BridgedDeviceBasicInformation, BridgedDeviceBasicInformationCluster, FixedLabelCluster, GeneralCommissioning, PowerSourceCluster, SwitchCluster, ThreadNetworkDiagnosticsCluster, UserLabelCluster, } from '@matter/main/clusters';
49
22
  import { getClusterNameById, ManualPairingCodeCodec, QrCodeSchema } from '@matter/main/types';
50
23
  import { StorageBackendDisk, StorageBackendJsonFile } from '@matter/nodejs';
51
- // @project-chip
52
24
  import { CommissioningController, CommissioningServer, MatterServer } from '@project-chip/matter.js';
53
25
  import { Aggregator, DeviceTypes, NodeStateInformation } from '@project-chip/matter.js/device';
54
26
  import { aggregator } from './matterbridgeDeviceTypes.js';
55
- // Default colors
56
27
  const plg = '\u001B[38;5;33m';
57
28
  const dev = '\u001B[38;5;79m';
58
29
  const typ = '\u001B[38;5;207m';
59
- /**
60
- * Represents the Matterbridge application.
61
- */
62
30
  export class Matterbridge extends EventEmitter {
63
31
  systemInformation = {
64
32
  interfaceName: '',
@@ -95,7 +63,7 @@ export class Matterbridge extends EventEmitter {
95
63
  edge: hasParameter('edge'),
96
64
  readOnly: hasParameter('readonly'),
97
65
  profile: getParameter('profile'),
98
- loggerLevel: "info" /* LogLevel.INFO */,
66
+ loggerLevel: "info",
99
67
  fileLogger: false,
100
68
  matterLoggerLevel: MatterLogLevel.INFO,
101
69
  matterFileLogger: false,
@@ -134,7 +102,6 @@ export class Matterbridge extends EventEmitter {
134
102
  nodeContext;
135
103
  matterStorageName = 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.json';
136
104
  nodeStorageName = 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
137
- // Cleanup
138
105
  hasCleanupStarted = false;
139
106
  initialized = false;
140
107
  execRunningCount = 0;
@@ -146,18 +113,16 @@ export class Matterbridge extends EventEmitter {
146
113
  sigtermHandler;
147
114
  exceptionHandler;
148
115
  rejectionHandler;
149
- // Frontend
150
116
  expressApp;
151
117
  httpServer;
152
118
  httpsServer;
153
119
  webSocketServer;
154
- // Matter
155
- mdnsInterface; // matter server mdnsInterface: e.g. 'eth0' or 'wlan0' or 'WiFi'
156
- ipv4address; // matter commissioning server listeningAddressIpv4
157
- ipv6address; // matter commissioning server listeningAddressIpv6
158
- port = 5540; // first commissioning server port
159
- passcode; // first commissioning server passcode
160
- discriminator; // first commissioning server discriminator
120
+ mdnsInterface;
121
+ ipv4address;
122
+ ipv6address;
123
+ port = 5540;
124
+ passcode;
125
+ discriminator;
161
126
  storageManager;
162
127
  matterbridgeContext;
163
128
  mattercontrollerContext;
@@ -168,26 +133,13 @@ export class Matterbridge extends EventEmitter {
168
133
  aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
169
134
  aggregatorProductId = getIntParameter('productId') ?? 0x8000;
170
135
  static instance;
171
- // We load asyncronously so is private
172
136
  constructor() {
173
137
  super();
174
- // Bind the handler to the instance
175
138
  this.matterbridgeMessageHandler = wsMessageHandler.bind(this);
176
139
  }
177
140
  matterbridgeMessageHandler;
178
- /** ***********************************************************************************************************************************/
179
- /** loadInstance() and cleanup() methods */
180
- /** ***********************************************************************************************************************************/
181
- /**
182
- * Loads an instance of the Matterbridge class.
183
- * If an instance already exists, return that instance.
184
- *
185
- * @param initialize - Whether to initialize the Matterbridge instance after loading.
186
- * @returns The loaded Matterbridge instance.
187
- */
188
141
  static async loadInstance(initialize = false) {
189
142
  if (!Matterbridge.instance) {
190
- // eslint-disable-next-line no-console
191
143
  if (hasParameter('debug'))
192
144
  console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
193
145
  Matterbridge.instance = new Matterbridge();
@@ -196,11 +148,6 @@ export class Matterbridge extends EventEmitter {
196
148
  }
197
149
  return Matterbridge.instance;
198
150
  }
199
- /**
200
- * Call cleanup().
201
- * @deprecated This method is deprecated and is only used for jest tests.
202
- *
203
- */
204
151
  async destroyInstance() {
205
152
  await this.cleanup('destroying instance...', false);
206
153
  await waiter('destroying instance...', () => {
@@ -208,60 +155,39 @@ export class Matterbridge extends EventEmitter {
208
155
  }, false, 60000, 100, false);
209
156
  await wait(1000, 'Wait for the global node_modules and matterbridge version', false);
210
157
  }
211
- /**
212
- * Initializes the Matterbridge application.
213
- *
214
- * @remarks
215
- * This method performs the necessary setup and initialization steps for the Matterbridge application.
216
- * It displays the help information if the 'help' parameter is provided, sets up the logger, checks the
217
- * node version, registers signal handlers, initializes storage, and parses the command line.
218
- *
219
- * @returns A Promise that resolves when the initialization is complete.
220
- */
221
158
  async initialize() {
222
- // Set the restart mode
223
159
  if (hasParameter('service'))
224
160
  this.restartMode = 'service';
225
161
  if (hasParameter('docker'))
226
162
  this.restartMode = 'docker';
227
- // Set the matterbridge directory
228
163
  this.homeDirectory = getParameter('homedir') ?? os.homedir();
229
164
  this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
230
- // Create matterbridge logger
231
- this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
232
- // Initialize nodeStorage and nodeContext
165
+ this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
233
166
  try {
234
167
  this.log.debug(`Creating node storage manager: ${CYAN}${this.nodeStorageName}${db}`);
235
168
  this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, this.nodeStorageName), writeQueue: false, expiredInterval: undefined, logging: false });
236
169
  this.log.debug('Creating node storage context for matterbridge');
237
170
  this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
238
- // TODO: Remove this code when node-persist-manager is updated
239
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
240
171
  const keys = (await this.nodeStorage?.storage.keys());
241
172
  for (const key of keys) {
242
173
  this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
243
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
244
174
  await this.nodeStorage?.storage.get(key);
245
175
  }
246
176
  const storages = await this.nodeStorage.getStorageNames();
247
177
  for (const storage of storages) {
248
178
  this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
249
179
  const nodeContext = await this.nodeStorage?.createStorage(storage);
250
- // TODO: Remove this code when node-persist-manager is updated
251
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
252
180
  const keys = (await nodeContext?.storage.keys());
253
181
  keys.forEach(async (key) => {
254
182
  this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
255
183
  await nodeContext?.get(key);
256
184
  });
257
185
  }
258
- // Creating a backup of the node storage since it is not corrupted
259
186
  this.log.debug('Creating node storage backup...');
260
187
  await copyDirectory(path.join(this.matterbridgeDirectory, this.nodeStorageName), path.join(this.matterbridgeDirectory, this.nodeStorageName + '.backup'));
261
188
  this.log.debug('Created node storage backup');
262
189
  }
263
190
  catch (error) {
264
- // Restoring the backup of the node storage since it is corrupted
265
191
  this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
266
192
  if (hasParameter('norestore')) {
267
193
  this.log.fatal(`The matterbridge node storage is corrupted. Parameter -norestore found: exiting...`);
@@ -276,51 +202,45 @@ export class Matterbridge extends EventEmitter {
276
202
  this.log.fatal('Fatal error creating node storage manager and context for matterbridge');
277
203
  throw new Error('Fatal error creating node storage manager and context for matterbridge');
278
204
  }
279
- // Set the first port to use for the commissioning server (will be incremented in childbridge mode)
280
205
  this.port = getIntParameter('port') ?? (await this.nodeContext.get('matterport', 5540)) ?? 5540;
281
- // Set the first passcode to use for the commissioning server (will be incremented in childbridge mode)
282
206
  this.passcode = this.passcode ?? getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode'));
283
- // Set the first discriminator to use for the commissioning server (will be incremented in childbridge mode)
284
207
  this.discriminator = this.discriminator ?? getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator'));
285
208
  this.log.debug(`Initializing commissioning server for Matterbridge... on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
286
- // Set matterbridge logger level (context: matterbridgeLogLevel)
287
209
  if (hasParameter('logger')) {
288
210
  const level = getParameter('logger');
289
211
  if (level === 'debug') {
290
- this.log.logLevel = "debug" /* LogLevel.DEBUG */;
212
+ this.log.logLevel = "debug";
291
213
  }
292
214
  else if (level === 'info') {
293
- this.log.logLevel = "info" /* LogLevel.INFO */;
215
+ this.log.logLevel = "info";
294
216
  }
295
217
  else if (level === 'notice') {
296
- this.log.logLevel = "notice" /* LogLevel.NOTICE */;
218
+ this.log.logLevel = "notice";
297
219
  }
298
220
  else if (level === 'warn') {
299
- this.log.logLevel = "warn" /* LogLevel.WARN */;
221
+ this.log.logLevel = "warn";
300
222
  }
301
223
  else if (level === 'error') {
302
- this.log.logLevel = "error" /* LogLevel.ERROR */;
224
+ this.log.logLevel = "error";
303
225
  }
304
226
  else if (level === 'fatal') {
305
- this.log.logLevel = "fatal" /* LogLevel.FATAL */;
227
+ this.log.logLevel = "fatal";
306
228
  }
307
229
  else {
308
230
  this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
309
- this.log.logLevel = "info" /* LogLevel.INFO */;
231
+ this.log.logLevel = "info";
310
232
  }
311
233
  }
312
234
  else {
313
- this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', "info" /* LogLevel.INFO */);
235
+ this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', "info");
314
236
  }
315
237
  MatterbridgeDevice.logLevel = this.log.logLevel;
316
- // Create the file logger for matterbridge (context: matterbridgeFileLog)
317
238
  if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
318
239
  AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), this.log.logLevel, true);
319
240
  this.matterbridgeInformation.fileLogger = true;
320
241
  }
321
242
  this.log.notice('Matterbridge is starting...');
322
243
  this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.matterbridgeInformation.fileLogger}.`);
323
- // Set matter.js logger level, format and logger (context: matterLogLevel)
324
244
  if (hasParameter('matterlogger')) {
325
245
  const level = getParameter('matterlogger');
326
246
  if (level === 'debug') {
@@ -351,7 +271,6 @@ export class Matterbridge extends EventEmitter {
351
271
  }
352
272
  Logger.format = MatterLogFormat.ANSI;
353
273
  Logger.setLogger('default', this.createMatterLogger());
354
- // Create the file logger for matter.js (context: matterFileLog)
355
274
  if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
356
275
  this.matterbridgeInformation.matterFileLogger = true;
357
276
  Logger.addLogger('matterfilelogger', await this.createMatterFileLogger(path.join(this.matterbridgeDirectory, this.matterLoggerFile), true), {
@@ -360,7 +279,6 @@ export class Matterbridge extends EventEmitter {
360
279
  });
361
280
  }
362
281
  this.log.debug(`Matter logLevel: ${Logger.defaultLogLevel} fileLoger: ${this.matterbridgeInformation.matterFileLogger}.`);
363
- // Set the interface to use for the matter server mdnsInterface
364
282
  if (hasParameter('mdnsinterface')) {
365
283
  this.mdnsInterface = getParameter('mdnsinterface');
366
284
  }
@@ -369,7 +287,6 @@ export class Matterbridge extends EventEmitter {
369
287
  if (this.mdnsInterface === '')
370
288
  this.mdnsInterface = undefined;
371
289
  }
372
- // Set the listeningAddressIpv4 for the matter commissioning server
373
290
  if (hasParameter('ipv4address')) {
374
291
  this.ipv4address = getParameter('ipv4address');
375
292
  }
@@ -378,7 +295,6 @@ export class Matterbridge extends EventEmitter {
378
295
  if (this.ipv4address === '')
379
296
  this.ipv4address = undefined;
380
297
  }
381
- // Set the listeningAddressIpv6 for the matter commissioning server
382
298
  if (hasParameter('ipv6address')) {
383
299
  this.ipv6address = getParameter('ipv6address');
384
300
  }
@@ -387,23 +303,17 @@ export class Matterbridge extends EventEmitter {
387
303
  if (this.ipv6address === '')
388
304
  this.ipv6address = undefined;
389
305
  }
390
- // Initialize PluginManager
391
306
  this.plugins = new PluginManager(this);
392
307
  await this.plugins.loadFromStorage();
393
- // Initialize DeviceManager
394
308
  this.devices = new DeviceManager(this, this.nodeContext);
395
- // Get the plugins from node storage and create the plugins node storage contexts
396
309
  for (const plugin of this.plugins) {
397
310
  const packageJson = await this.plugins.parse(plugin);
398
311
  if (packageJson === null && !hasParameter('add')) {
399
- // Try to reinstall the plugin from npm (for Docker pull and external plugins)
400
- // We don't do this when the add parameter is set because we shut down the process after adding the plugin
401
312
  this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
402
313
  try {
403
314
  await this.spawnCommand('npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose']);
404
315
  this.log.info(`Plugin ${plg}${plugin.name}${nf} reinstalled.`);
405
316
  plugin.error = false;
406
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
407
317
  }
408
318
  catch (error) {
409
319
  plugin.error = true;
@@ -420,7 +330,6 @@ export class Matterbridge extends EventEmitter {
420
330
  await plugin.nodeContext.set('description', plugin.description);
421
331
  await plugin.nodeContext.set('author', plugin.author);
422
332
  }
423
- // Log system info and create .matterbridge directory
424
333
  await this.logNodeAndSystemInfo();
425
334
  this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
426
335
  `${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
@@ -428,7 +337,6 @@ export class Matterbridge extends EventEmitter {
428
337
  `${hasParameter('controller') ? 'mode controller ' : ''}` +
429
338
  `${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
430
339
  `running on ${this.systemInformation.osType} (v.${this.systemInformation.osRelease}) platform ${this.systemInformation.osPlatform} arch ${this.systemInformation.osArch}`);
431
- // Check node version and throw error
432
340
  const minNodeVersion = 18;
433
341
  const nodeVersion = process.versions.node;
434
342
  const versionMajor = parseInt(nodeVersion.split('.')[0]);
@@ -436,17 +344,10 @@ export class Matterbridge extends EventEmitter {
436
344
  this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
437
345
  throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
438
346
  }
439
- // Register process handlers
440
347
  this.registerProcessHandlers();
441
- // Parse command line
442
348
  await this.parseCommandLine();
443
349
  this.initialized = true;
444
350
  }
445
- /**
446
- * Parses the command line arguments and performs the corresponding actions.
447
- * @private
448
- * @returns {Promise<void>} A promise that resolves when the command line arguments have been processed, or the process exits.
449
- */
450
351
  async parseCommandLine() {
451
352
  if (hasParameter('help')) {
452
353
  this.log.info(`\nUsage: matterbridge [options]\n
@@ -554,14 +455,12 @@ export class Matterbridge extends EventEmitter {
554
455
  }
555
456
  if (hasParameter('factoryreset')) {
556
457
  try {
557
- // Delete matter storage file
558
458
  await fs.unlink(path.join(this.matterbridgeDirectory, this.matterStorageName));
559
459
  }
560
460
  catch (err) {
561
461
  this.log.error(`Error deleting storage: ${err}`);
562
462
  }
563
463
  try {
564
- // Delete node storage directory with its subdirectories
565
464
  await fs.rm(path.join(this.matterbridgeDirectory, this.nodeStorageName), { recursive: true });
566
465
  }
567
466
  catch (err) {
@@ -575,7 +474,6 @@ export class Matterbridge extends EventEmitter {
575
474
  this.emit('shutdown');
576
475
  return;
577
476
  }
578
- // Start the matter storage and create the matterbridge context
579
477
  try {
580
478
  await this.startMatterStorage('json', path.join(this.matterbridgeDirectory, this.matterStorageName));
581
479
  }
@@ -610,34 +508,28 @@ export class Matterbridge extends EventEmitter {
610
508
  this.emit('shutdown');
611
509
  return;
612
510
  }
613
- // Initialize frontend
614
511
  if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
615
512
  await this.initializeFrontend(getIntParameter('frontend'));
616
- // Check each 60 minutes the latest versions
617
513
  this.checkUpdateInterval = setInterval(() => {
618
514
  this.getMatterbridgeLatestVersion();
619
515
  for (const plugin of this.plugins) {
620
516
  this.getPluginLatestVersion(plugin);
621
517
  }
622
518
  }, 60 * 60 * 1000);
623
- // Start the matterbridge in mode test
624
519
  if (hasParameter('test')) {
625
520
  this.bridgeMode = 'bridge';
626
521
  MatterbridgeDevice.bridgeMode = 'bridge';
627
522
  return;
628
523
  }
629
- // Start the matterbridge in mode controller
630
524
  if (hasParameter('controller')) {
631
525
  this.bridgeMode = 'controller';
632
526
  await this.startController();
633
527
  return;
634
528
  }
635
- // Check if the bridge mode is set and start matterbridge in bridge mode if not set
636
529
  if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
637
530
  this.log.info('Setting default matterbridge start mode to bridge');
638
531
  await this.nodeContext?.set('bridgeMode', 'bridge');
639
532
  }
640
- // Start matterbridge in bridge mode
641
533
  if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
642
534
  this.bridgeMode = 'bridge';
643
535
  MatterbridgeDevice.bridgeMode = 'bridge';
@@ -646,7 +538,6 @@ export class Matterbridge extends EventEmitter {
646
538
  await this.startBridge();
647
539
  return;
648
540
  }
649
- // Start matterbridge in childbridge mode
650
541
  if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
651
542
  this.bridgeMode = 'childbridge';
652
543
  MatterbridgeDevice.bridgeMode = 'childbridge';
@@ -656,28 +547,17 @@ export class Matterbridge extends EventEmitter {
656
547
  return;
657
548
  }
658
549
  }
659
- /**
660
- * Asynchronously loads and starts the registered plugins.
661
- *
662
- * This method is responsible for initializing and staarting all enabled plugins.
663
- * It ensures that each plugin is properly loaded and started before the ridge starts.
664
- *
665
- * @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
666
- */
667
550
  async startPlugins() {
668
- // Check, load and start the plugins
669
551
  for (const plugin of this.plugins) {
670
552
  plugin.configJson = await this.plugins.loadConfig(plugin);
671
553
  plugin.schemaJson = await this.plugins.loadSchema(plugin);
672
- // Check if the plugin is available
673
554
  if (!(await this.plugins.resolve(plugin.path))) {
674
555
  this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
675
556
  plugin.enabled = false;
676
557
  plugin.error = true;
677
558
  continue;
678
559
  }
679
- // Check if the plugin has a new version
680
- this.getPluginLatestVersion(plugin); // No await do it asyncronously
560
+ this.getPluginLatestVersion(plugin);
681
561
  if (!plugin.enabled) {
682
562
  this.log.info(`Plugin ${plg}${plugin.name}${nf} not enabled`);
683
563
  continue;
@@ -692,26 +572,20 @@ export class Matterbridge extends EventEmitter {
692
572
  plugin.addedDevices = undefined;
693
573
  plugin.qrPairingCode = undefined;
694
574
  plugin.manualPairingCode = undefined;
695
- this.plugins.load(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
575
+ this.plugins.load(plugin, true, 'Matterbridge is starting');
696
576
  }
697
577
  this.wssSendRefreshRequired();
698
578
  }
699
- /**
700
- * Registers the process handlers for uncaughtException, unhandledRejection, SIGINT and SIGTERM.
701
- * When either of these signals are received, the cleanup method is called with an appropriate message.
702
- */
703
579
  registerProcessHandlers() {
704
580
  this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
705
581
  process.removeAllListeners('uncaughtException');
706
582
  process.removeAllListeners('unhandledRejection');
707
583
  this.exceptionHandler = async (error) => {
708
584
  this.log.fatal('Unhandled Exception detected at:', error.stack || error, rs);
709
- // await this.cleanup('Unhandled Exception detected, cleaning up...');
710
585
  };
711
586
  process.on('uncaughtException', this.exceptionHandler);
712
587
  this.rejectionHandler = async (reason, promise) => {
713
588
  this.log.fatal('Unhandled Rejection detected at:', promise, 'reason:', reason instanceof Error ? reason.stack : reason, rs);
714
- // await this.cleanup('Unhandled Rejection detected, cleaning up...');
715
589
  };
716
590
  process.on('unhandledRejection', this.rejectionHandler);
717
591
  this.log.debug(`Registering SIGINT and SIGTERM signal handlers...`);
@@ -724,9 +598,6 @@ export class Matterbridge extends EventEmitter {
724
598
  };
725
599
  process.on('SIGTERM', this.sigtermHandler);
726
600
  }
727
- /**
728
- * Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
729
- */
730
601
  deregisterProcesslHandlers() {
731
602
  this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
732
603
  if (this.exceptionHandler)
@@ -743,11 +614,7 @@ export class Matterbridge extends EventEmitter {
743
614
  process.off('SIGTERM', this.sigtermHandler);
744
615
  this.sigtermHandler = undefined;
745
616
  }
746
- /**
747
- * Logs the node and system information.
748
- */
749
617
  async logNodeAndSystemInfo() {
750
- // IP address information
751
618
  const networkInterfaces = os.networkInterfaces();
752
619
  this.systemInformation.ipv4Address = '';
753
620
  this.systemInformation.ipv6Address = '';
@@ -767,7 +634,7 @@ export class Matterbridge extends EventEmitter {
767
634
  this.systemInformation.macAddress = detail.mac;
768
635
  }
769
636
  }
770
- if (this.systemInformation.ipv4Address !== '' /* && this.systemInformation.ipv6Address !== ''*/) {
637
+ if (this.systemInformation.ipv4Address !== '') {
771
638
  this.log.debug(`Using interface: '${this.systemInformation.interfaceName}'`);
772
639
  this.log.debug(`- with MAC address: '${this.systemInformation.macAddress}'`);
773
640
  this.log.debug(`- with IPv4 address: '${this.systemInformation.ipv4Address}'`);
@@ -775,22 +642,19 @@ export class Matterbridge extends EventEmitter {
775
642
  break;
776
643
  }
777
644
  }
778
- // Node information
779
645
  this.systemInformation.nodeVersion = process.versions.node;
780
646
  const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
781
647
  const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
782
648
  const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
783
- // Host system information
784
649
  this.systemInformation.hostname = os.hostname();
785
650
  this.systemInformation.user = os.userInfo().username;
786
- this.systemInformation.osType = os.type(); // "Windows_NT", "Darwin", etc.
787
- this.systemInformation.osRelease = os.release(); // Kernel version
788
- this.systemInformation.osPlatform = os.platform(); // "win32", "linux", "darwin", etc.
789
- this.systemInformation.osArch = os.arch(); // "x64", "arm", etc.
790
- this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB'; // Convert to GB
791
- this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB'; // Convert to GB
792
- this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours'; // Convert to hours
793
- // Log the system information
651
+ this.systemInformation.osType = os.type();
652
+ this.systemInformation.osRelease = os.release();
653
+ this.systemInformation.osPlatform = os.platform();
654
+ this.systemInformation.osArch = os.arch();
655
+ this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
656
+ this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
657
+ this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
794
658
  this.log.debug('Host System Information:');
795
659
  this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
796
660
  this.log.debug(`- User: ${this.systemInformation.user}`);
@@ -806,19 +670,15 @@ export class Matterbridge extends EventEmitter {
806
670
  this.log.debug(`- Total Memory: ${this.systemInformation.totalMemory}`);
807
671
  this.log.debug(`- Free Memory: ${this.systemInformation.freeMemory}`);
808
672
  this.log.debug(`- System Uptime: ${this.systemInformation.systemUptime}`);
809
- // Home directory
810
673
  this.homeDirectory = getParameter('homedir') ?? os.homedir();
811
674
  this.matterbridgeInformation.homeDirectory = this.homeDirectory;
812
675
  this.log.debug(`Home Directory: ${this.homeDirectory}`);
813
- // Package root directory
814
676
  const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
815
677
  this.rootDirectory = path.resolve(currentFileDirectory, '../');
816
678
  this.matterbridgeInformation.rootDirectory = this.rootDirectory;
817
679
  this.log.debug(`Root Directory: ${this.rootDirectory}`);
818
- // Global node_modules directory
819
680
  if (this.nodeContext)
820
681
  this.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
821
- // First run of Matterbridge so the node storage is empty
822
682
  if (this.globalModulesDirectory === '') {
823
683
  try {
824
684
  this.globalModulesDirectory = await this.getGlobalNodeModules();
@@ -842,7 +702,6 @@ export class Matterbridge extends EventEmitter {
842
702
  this.log.error(`Error getting global node_modules directory: ${error}`);
843
703
  });
844
704
  }
845
- // Create the data directory .matterbridge in the home directory
846
705
  this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
847
706
  this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
848
707
  try {
@@ -866,7 +725,6 @@ export class Matterbridge extends EventEmitter {
866
725
  }
867
726
  }
868
727
  this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
869
- // Create the plugin directory Matterbridge in the home directory
870
728
  this.matterbridgePluginDirectory = path.join(this.homeDirectory, 'Matterbridge');
871
729
  this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
872
730
  try {
@@ -890,28 +748,19 @@ export class Matterbridge extends EventEmitter {
890
748
  }
891
749
  }
892
750
  this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
893
- // Matterbridge version
894
751
  const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
895
752
  this.matterbridgeVersion = packageJson.version;
896
753
  this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeVersion;
897
754
  this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
898
- // Matterbridge latest version
899
755
  if (this.nodeContext)
900
756
  this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', '');
901
757
  this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
902
758
  this.getMatterbridgeLatestVersion();
903
- // Current working directory
904
759
  const currentDir = process.cwd();
905
760
  this.log.debug(`Current Working Directory: ${currentDir}`);
906
- // Command line arguments (excluding 'node' and the script name)
907
761
  const cmdArgs = process.argv.slice(2).join(' ');
908
762
  this.log.debug(`Command Line Arguments: ${cmdArgs}`);
909
763
  }
910
- /**
911
- * Retrieves the latest version of a package from the npm registry.
912
- * @param packageName - The name of the package.
913
- * @returns A Promise that resolves to the latest version of the package.
914
- */
915
764
  async getLatestVersion(packageName) {
916
765
  return new Promise((resolve, reject) => {
917
766
  this.execRunningCount++;
@@ -926,10 +775,6 @@ export class Matterbridge extends EventEmitter {
926
775
  });
927
776
  });
928
777
  }
929
- /**
930
- * Retrieves the path to the global Node.js modules directory.
931
- * @returns A promise that resolves to the path of the global Node.js modules directory.
932
- */
933
778
  async getGlobalNodeModules() {
934
779
  return new Promise((resolve, reject) => {
935
780
  this.execRunningCount++;
@@ -944,11 +789,6 @@ export class Matterbridge extends EventEmitter {
944
789
  });
945
790
  });
946
791
  }
947
- /**
948
- * Retrieves the latest version of Matterbridge and performs necessary actions based on the version comparison.
949
- * @private
950
- * @returns {Promise<void>} A promise that resolves when the latest version is retrieved and actions are performed.
951
- */
952
792
  async getMatterbridgeLatestVersion() {
953
793
  this.getLatestVersion('matterbridge')
954
794
  .then(async (matterbridgeLatestVersion) => {
@@ -965,19 +805,8 @@ export class Matterbridge extends EventEmitter {
965
805
  })
966
806
  .catch((error) => {
967
807
  this.log.error(`Error getting Matterbridge latest version: ${error.message}`);
968
- // error.stack && this.log.debug(error.stack);
969
808
  });
970
809
  }
971
- /**
972
- * Retrieves the latest version of a plugin and updates the plugin's latestVersion property.
973
- * If the plugin's version is different from the latest version, logs a warning message.
974
- * If the plugin's version is the same as the latest version, logs an info message.
975
- * If there is an error retrieving the latest version, logs an error message.
976
- *
977
- * @private
978
- * @param {RegisteredPlugin} plugin - The plugin for which to retrieve the latest version.
979
- * @returns {Promise<void>} A promise that resolves when the latest version is retrieved and actions are performed.
980
- */
981
810
  async getPluginLatestVersion(plugin) {
982
811
  this.getLatestVersion(plugin.name)
983
812
  .then(async (latestVersion) => {
@@ -989,54 +818,40 @@ export class Matterbridge extends EventEmitter {
989
818
  })
990
819
  .catch((error) => {
991
820
  this.log.error(`Error getting ${plg}${plugin.name}${er} latest version: ${error.message}`);
992
- // error.stack && this.log.debug(error.stack);
993
821
  });
994
822
  }
995
- /**
996
- * Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (for the frontend).
997
- *
998
- * @returns {Function} The MatterLogger function.
999
- */
1000
823
  createMatterLogger() {
1001
- const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: "debug" /* LogLevel.DEBUG */ });
824
+ const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
1002
825
  return (_level, formattedLog) => {
1003
826
  const logger = formattedLog.slice(44, 44 + 20).trim();
1004
827
  const message = formattedLog.slice(65);
1005
828
  matterLogger.logName = logger;
1006
829
  switch (_level) {
1007
830
  case MatterLogLevel.DEBUG:
1008
- matterLogger.log("debug" /* LogLevel.DEBUG */, message);
831
+ matterLogger.log("debug", message);
1009
832
  break;
1010
833
  case MatterLogLevel.INFO:
1011
- matterLogger.log("info" /* LogLevel.INFO */, message);
834
+ matterLogger.log("info", message);
1012
835
  break;
1013
836
  case MatterLogLevel.NOTICE:
1014
- matterLogger.log("notice" /* LogLevel.NOTICE */, message);
837
+ matterLogger.log("notice", message);
1015
838
  break;
1016
839
  case MatterLogLevel.WARN:
1017
- matterLogger.log("warn" /* LogLevel.WARN */, message);
840
+ matterLogger.log("warn", message);
1018
841
  break;
1019
842
  case MatterLogLevel.ERROR:
1020
- matterLogger.log("error" /* LogLevel.ERROR */, message);
843
+ matterLogger.log("error", message);
1021
844
  break;
1022
845
  case MatterLogLevel.FATAL:
1023
- matterLogger.log("fatal" /* LogLevel.FATAL */, message);
846
+ matterLogger.log("fatal", message);
1024
847
  break;
1025
848
  default:
1026
- matterLogger.log("debug" /* LogLevel.DEBUG */, message);
849
+ matterLogger.log("debug", message);
1027
850
  break;
1028
851
  }
1029
852
  };
1030
853
  }
1031
- /**
1032
- * Creates a Matter File Logger.
1033
- *
1034
- * @param {string} filePath - The path to the log file.
1035
- * @param {boolean} [unlink=false] - Whether to unlink the log file before creating a new one.
1036
- * @returns {Function} - A function that logs formatted messages to the log file.
1037
- */
1038
854
  async createMatterFileLogger(filePath, unlink = false) {
1039
- // 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
1040
855
  let fileSize = 0;
1041
856
  if (unlink) {
1042
857
  try {
@@ -1085,83 +900,53 @@ export class Matterbridge extends EventEmitter {
1085
900
  }
1086
901
  };
1087
902
  }
1088
- /**
1089
- * Update matterbridge and cleanup.
1090
- */
1091
903
  async updateProcess() {
1092
904
  await this.cleanup('updating...', false);
1093
905
  }
1094
- /**
1095
- * Restarts the process by spawning a new process and exiting the current process.
1096
- */
1097
906
  async restartProcess() {
1098
907
  await this.cleanup('restarting...', true);
1099
908
  }
1100
- /**
1101
- * Shut down the process by exiting the current process.
1102
- */
1103
909
  async shutdownProcess() {
1104
910
  await this.cleanup('shutting down...', false);
1105
911
  }
1106
- /**
1107
- * Shut down the process and reset.
1108
- */
1109
912
  async unregisterAndShutdownProcess() {
1110
913
  this.log.info('Unregistering all devices and shutting down...');
1111
- for (const plugin of this.plugins /* .filter((plugin) => plugin.enabled && !plugin.error))*/) {
914
+ for (const plugin of this.plugins) {
1112
915
  await this.removeAllBridgedDevices(plugin.name);
1113
916
  }
1114
917
  await this.cleanup('unregistered all devices and shutting down...', false);
1115
918
  }
1116
- /**
1117
- * Shut down the process and reset.
1118
- */
1119
919
  async shutdownProcessAndReset() {
1120
920
  await this.cleanup('shutting down with reset...', false);
1121
921
  }
1122
- /**
1123
- * Shut down the process and factory reset.
1124
- */
1125
922
  async shutdownProcessAndFactoryReset() {
1126
923
  await this.cleanup('shutting down with factory reset...', false);
1127
924
  }
1128
- /**
1129
- * Cleans up the Matterbridge instance.
1130
- * @param message - The cleanup message.
1131
- * @param restart - Indicates whether to restart the instance after cleanup. Default is `false`.
1132
- * @returns A promise that resolves when the cleanup is completed.
1133
- */
1134
925
  async cleanup(message, restart = false) {
1135
926
  if (this.initialized && !this.hasCleanupStarted) {
1136
927
  this.hasCleanupStarted = true;
1137
928
  this.log.info(message);
1138
- // Deregisters the process handlers
1139
929
  this.deregisterProcesslHandlers();
1140
- // Clear the start matter interval
1141
930
  if (this.startMatterInterval) {
1142
931
  clearInterval(this.startMatterInterval);
1143
932
  this.startMatterInterval = undefined;
1144
933
  this.log.debug('Start matter interval cleared');
1145
934
  }
1146
- // Clear the check update interval
1147
935
  if (this.checkUpdateInterval) {
1148
936
  clearInterval(this.checkUpdateInterval);
1149
937
  this.checkUpdateInterval = undefined;
1150
938
  this.log.debug('Check update interval cleared');
1151
939
  }
1152
- // Clear the configure timeout
1153
940
  if (this.configureTimeout) {
1154
941
  clearTimeout(this.configureTimeout);
1155
942
  this.configureTimeout = undefined;
1156
943
  this.log.debug('Matterbridge configure timeout cleared');
1157
944
  }
1158
- // Clear the reachability timeout
1159
945
  if (this.reachabilityTimeout) {
1160
946
  clearTimeout(this.reachabilityTimeout);
1161
947
  this.reachabilityTimeout = undefined;
1162
948
  this.log.debug('Matterbridge reachability timeout cleared');
1163
949
  }
1164
- // Calling the shutdown method of each plugin and clear the reachability timeout
1165
950
  for (const plugin of this.plugins) {
1166
951
  if (!plugin.enabled || plugin.error)
1167
952
  continue;
@@ -1172,29 +957,24 @@ export class Matterbridge extends EventEmitter {
1172
957
  this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
1173
958
  }
1174
959
  }
1175
- // Close the http server
1176
960
  if (this.httpServer) {
1177
961
  this.httpServer.close();
1178
962
  this.httpServer.removeAllListeners();
1179
963
  this.httpServer = undefined;
1180
964
  this.log.debug('Frontend http server closed successfully');
1181
965
  }
1182
- // Close the https server
1183
966
  if (this.httpsServer) {
1184
967
  this.httpsServer.close();
1185
968
  this.httpsServer.removeAllListeners();
1186
969
  this.httpsServer = undefined;
1187
970
  this.log.debug('Frontend https server closed successfully');
1188
971
  }
1189
- // Remove listeners from the express app
1190
972
  if (this.expressApp) {
1191
973
  this.expressApp.removeAllListeners();
1192
974
  this.expressApp = undefined;
1193
975
  this.log.debug('Frontend app closed successfully');
1194
976
  }
1195
- // Close the WebSocket server
1196
977
  if (this.webSocketServer) {
1197
- // Close all active connections
1198
978
  this.webSocketServer.clients.forEach((client) => {
1199
979
  if (client.readyState === WebSocket.OPEN) {
1200
980
  client.close();
@@ -1210,35 +990,29 @@ export class Matterbridge extends EventEmitter {
1210
990
  });
1211
991
  this.webSocketServer = undefined;
1212
992
  }
1213
- // Closing matter
993
+ if (this.edge === false && this.matterbridgeContext && ['updating...', 'restarting...', 'shutting down...'].includes(message)) {
994
+ this.convertStorage(this.matterbridgeContext, 'Mattebridge');
995
+ }
1214
996
  await this.stopMatterServer();
1215
- // Closing matter storage
1216
997
  await this.stopMatterStorage();
1217
- // Remove the matterfilelogger
1218
998
  try {
1219
999
  Logger.removeLogger('matterfilelogger');
1220
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1221
1000
  }
1222
1001
  catch (error) {
1223
- // this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridgeDirectory, this.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
1224
1002
  }
1225
- // Serialize registeredDevices
1226
1003
  if (this.nodeStorage && this.nodeContext) {
1227
1004
  this.log.info('Saving registered devices...');
1228
1005
  const serializedRegisteredDevices = [];
1229
1006
  this.devices.forEach(async (device) => {
1230
1007
  const serializedMatterbridgeDevice = device.serialize();
1231
- // this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
1232
1008
  if (serializedMatterbridgeDevice)
1233
1009
  serializedRegisteredDevices.push(serializedMatterbridgeDevice);
1234
1010
  });
1235
1011
  await this.nodeContext.set('devices', serializedRegisteredDevices);
1236
1012
  this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
1237
- // Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
1238
1013
  this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
1239
1014
  await this.nodeContext.close();
1240
1015
  this.nodeContext = undefined;
1241
- // Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
1242
1016
  for (const plugin of this.plugins) {
1243
1017
  if (plugin.nodeContext) {
1244
1018
  this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
@@ -1269,16 +1043,13 @@ export class Matterbridge extends EventEmitter {
1269
1043
  }
1270
1044
  else {
1271
1045
  if (message === 'shutting down with reset...') {
1272
- // Delete matter storage file
1273
1046
  this.log.info('Resetting Matterbridge commissioning information...');
1274
1047
  await fs.unlink(path.join(this.matterbridgeDirectory, this.matterStorageName));
1275
1048
  this.log.info('Reset done! Remove all paired devices from the controllers.');
1276
1049
  }
1277
1050
  if (message === 'shutting down with factory reset...') {
1278
- // Delete matter storage file
1279
1051
  this.log.info('Resetting Matterbridge commissioning information...');
1280
1052
  await fs.unlink(path.join(this.matterbridgeDirectory, this.matterStorageName));
1281
- // Delete node storage directory with its subdirectories
1282
1053
  this.log.info('Resetting Matterbridge storage...');
1283
1054
  await fs.rm(path.join(this.matterbridgeDirectory, this.nodeStorageName), { recursive: true });
1284
1055
  this.log.info('Factory reset done! Remove all paired devices from the controllers.');
@@ -1291,33 +1062,19 @@ export class Matterbridge extends EventEmitter {
1291
1062
  this.initialized = false;
1292
1063
  }
1293
1064
  }
1294
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1295
1065
  async addBridgedEndpoint(pluginName, device) {
1296
- // Nothing to do here
1297
1066
  }
1298
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1299
1067
  async removeBridgedEndpoint(pluginName, device) {
1300
- // Nothing to do here
1301
1068
  }
1302
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1303
1069
  async removeAllBridgedEndpoints(pluginName) {
1304
- // Nothing to do here
1305
1070
  }
1306
- /**
1307
- * Adds a bridged device to the Matterbridge.
1308
- * @param pluginName - The name of the plugin.
1309
- * @param device - The bridged device to add.
1310
- * @returns {Promise<void>} - A promise that resolves when the device is added.
1311
- */
1312
1071
  async addBridgedDevice(pluginName, device) {
1313
1072
  this.log.debug(`Adding bridged device ${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
1314
- // Check if the plugin is registered
1315
1073
  const plugin = this.plugins.get(pluginName);
1316
1074
  if (!plugin) {
1317
1075
  this.log.error(`Error adding bridged device ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) plugin ${plg}${pluginName}${er} not found`);
1318
1076
  return;
1319
1077
  }
1320
- // Register and add the device to matterbridge aggregator in bridge mode
1321
1078
  if (this.bridgeMode === 'bridge') {
1322
1079
  if (!this.matterAggregator) {
1323
1080
  this.log.error(`Adding bridged device ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er} error: matterAggregator not found`);
@@ -1325,11 +1082,8 @@ export class Matterbridge extends EventEmitter {
1325
1082
  }
1326
1083
  this.matterAggregator.addBridgedDevice(device);
1327
1084
  }
1328
- // The first time create the commissioning server and the aggregator for DynamicPlatform
1329
- // Register and add the device in childbridge mode
1330
1085
  if (this.bridgeMode === 'childbridge') {
1331
1086
  if (plugin.type === 'AccessoryPlatform') {
1332
- // Check if the plugin is locked with the commissioning server
1333
1087
  if (!plugin.locked) {
1334
1088
  plugin.locked = true;
1335
1089
  plugin.storageContext = await this.importCommissioningServerContext(plugin.name, device);
@@ -1343,7 +1097,6 @@ export class Matterbridge extends EventEmitter {
1343
1097
  }
1344
1098
  }
1345
1099
  if (plugin.type === 'DynamicPlatform') {
1346
- // Check if the plugin is locked with the commissioning server and the aggregator
1347
1100
  if (!plugin.locked) {
1348
1101
  plugin.locked = true;
1349
1102
  this.log.debug(`Creating commissioning server context for ${plg}${plugin.name}${db}`);
@@ -1364,25 +1117,16 @@ export class Matterbridge extends EventEmitter {
1364
1117
  plugin.registeredDevices++;
1365
1118
  if (plugin.addedDevices !== undefined)
1366
1119
  plugin.addedDevices++;
1367
- // Add the device to the DeviceManager
1368
1120
  this.devices.set(device);
1369
1121
  this.log.info(`Added and registered bridged device (${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
1370
1122
  }
1371
- /**
1372
- * Removes a bridged device from the Matterbridge.
1373
- * @param pluginName - The name of the plugin.
1374
- * @param device - The device to be removed.
1375
- * @returns A Promise that resolves when the device is successfully removed.
1376
- */
1377
1123
  async removeBridgedDevice(pluginName, device) {
1378
1124
  this.log.debug(`Removing bridged device ${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
1379
- // Check if the plugin is registered
1380
1125
  const plugin = this.plugins.get(pluginName);
1381
1126
  if (!plugin) {
1382
1127
  this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
1383
1128
  return;
1384
1129
  }
1385
- // Remove the device from matterbridge aggregator in bridge mode
1386
1130
  if (this.bridgeMode === 'bridge') {
1387
1131
  if (!this.matterAggregator) {
1388
1132
  this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: matterAggregator not found`);
@@ -1392,8 +1136,6 @@ export class Matterbridge extends EventEmitter {
1392
1136
  device.setBridgedDeviceReachability(false);
1393
1137
  device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
1394
1138
  }
1395
- // device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerShutDownEvent({});
1396
- // device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerLeaveEvent({});
1397
1139
  this.matterAggregator?.removeBridgedDevice(device);
1398
1140
  this.log.info(`Removed bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${zb}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
1399
1141
  if (plugin.registeredDevices !== undefined)
@@ -1401,7 +1143,6 @@ export class Matterbridge extends EventEmitter {
1401
1143
  if (plugin.addedDevices !== undefined)
1402
1144
  plugin.addedDevices--;
1403
1145
  }
1404
- // Remove the device in childbridge mode
1405
1146
  if (this.bridgeMode === 'childbridge') {
1406
1147
  if (plugin.type === 'AccessoryPlatform') {
1407
1148
  if (!plugin.commissioningServer) {
@@ -1425,22 +1166,14 @@ export class Matterbridge extends EventEmitter {
1425
1166
  plugin.registeredDevices--;
1426
1167
  if (plugin.addedDevices !== undefined)
1427
1168
  plugin.addedDevices--;
1428
- // Remove the commissioning server
1429
1169
  if (plugin.registeredDevices === 0 && plugin.addedDevices === 0 && plugin.commissioningServer) {
1430
1170
  this.matterServer?.removeCommissioningServer(plugin.commissioningServer);
1431
1171
  plugin.commissioningServer = undefined;
1432
1172
  this.log.info(`Removed commissioning server for plugin ${plg}${pluginName}${nf}`);
1433
1173
  }
1434
1174
  }
1435
- // Remove the device from the DeviceManager
1436
1175
  this.devices.remove(device);
1437
1176
  }
1438
- /**
1439
- * Removes all bridged devices associated with a specific plugin.
1440
- *
1441
- * @param pluginName - The name of the plugin.
1442
- * @returns A promise that resolves when all devices have been removed.
1443
- */
1444
1177
  async removeAllBridgedDevices(pluginName) {
1445
1178
  this.log.debug(`Removing all bridged devices for plugin ${plg}${pluginName}${db}`);
1446
1179
  this.devices.forEach(async (device) => {
@@ -1449,13 +1182,7 @@ export class Matterbridge extends EventEmitter {
1449
1182
  }
1450
1183
  });
1451
1184
  }
1452
- /**
1453
- * Starts the Matterbridge in bridge mode.
1454
- * @private
1455
- * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1456
- */
1457
1185
  async startBridge() {
1458
- // Plugins are configured by a timer when matter server is started and plugin.configured is set to true
1459
1186
  if (!this.storageManager)
1460
1187
  throw new Error('No storage manager initialized');
1461
1188
  if (!this.matterbridgeContext)
@@ -1474,7 +1201,6 @@ export class Matterbridge extends EventEmitter {
1474
1201
  let failCount = 0;
1475
1202
  this.startMatterInterval = setInterval(async () => {
1476
1203
  for (const plugin of this.plugins) {
1477
- // new code to not start the bridge if one plugin is in error cause the controllers will delete the devices loosing all the configuration
1478
1204
  if (!plugin.enabled)
1479
1205
  continue;
1480
1206
  if (plugin.error) {
@@ -1499,18 +1225,15 @@ export class Matterbridge extends EventEmitter {
1499
1225
  clearInterval(this.startMatterInterval);
1500
1226
  this.startMatterInterval = undefined;
1501
1227
  this.log.debug('Cleared startMatterInterval interval for Matterbridge');
1502
- // Start the Matter server
1503
1228
  await this.startMatterServer();
1504
1229
  this.log.notice('Matter server started');
1505
- // Show the QR code for commissioning or log the already commissioned message
1506
1230
  await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
1507
- // Configure the plugins
1508
1231
  this.configureTimeout = setTimeout(async () => {
1509
1232
  for (const plugin of this.plugins) {
1510
1233
  if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
1511
1234
  continue;
1512
1235
  try {
1513
- await this.plugins.configure(plugin); // TODO No await do it in parallel
1236
+ await this.plugins.configure(plugin);
1514
1237
  }
1515
1238
  catch (error) {
1516
1239
  plugin.error = true;
@@ -1519,7 +1242,6 @@ export class Matterbridge extends EventEmitter {
1519
1242
  }
1520
1243
  this.wssSendRefreshRequired();
1521
1244
  }, 30 * 1000);
1522
- // Setting reachability to true
1523
1245
  this.reachabilityTimeout = setTimeout(() => {
1524
1246
  this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
1525
1247
  if (this.commissioningServer)
@@ -1529,14 +1251,7 @@ export class Matterbridge extends EventEmitter {
1529
1251
  }, 60 * 1000);
1530
1252
  }, 1000);
1531
1253
  }
1532
- /**
1533
- * Starts the Matterbridge in childbridge mode.
1534
- * @private
1535
- * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1536
- */
1537
1254
  async startChildbridge() {
1538
- // Matterbridge.addBridgedDevice creates the commissionig servers and add the devices to the the commissioning server or to the aggregator
1539
- // Plugins are configured by a timer when matter server is started and plugin.configured is set to true
1540
1255
  if (!this.storageManager)
1541
1256
  throw new Error('No storage manager initialized');
1542
1257
  this.matterServer = this.createMatterServer(this.storageManager);
@@ -1546,7 +1261,6 @@ export class Matterbridge extends EventEmitter {
1546
1261
  this.startMatterInterval = setInterval(async () => {
1547
1262
  let allStarted = true;
1548
1263
  for (const plugin of this.plugins) {
1549
- // new code to not start the bridge if one plugin is in error cause the controllers will delete the devices loosing all the configuration
1550
1264
  if (!plugin.enabled)
1551
1265
  continue;
1552
1266
  if (plugin.error) {
@@ -1574,16 +1288,14 @@ export class Matterbridge extends EventEmitter {
1574
1288
  clearInterval(this.startMatterInterval);
1575
1289
  this.startMatterInterval = undefined;
1576
1290
  this.log.debug('Cleared startMatterInterval interval in childbridge mode');
1577
- // Start the Matter server
1578
1291
  await this.startMatterServer();
1579
1292
  this.log.notice('Matter server started');
1580
- // Configure the plugins
1581
1293
  this.configureTimeout = setTimeout(async () => {
1582
1294
  for (const plugin of this.plugins) {
1583
1295
  if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
1584
1296
  continue;
1585
1297
  try {
1586
- await this.plugins.configure(plugin); // TODO No await do it in parallel
1298
+ await this.plugins.configure(plugin);
1587
1299
  }
1588
1300
  catch (error) {
1589
1301
  plugin.error = true;
@@ -1612,7 +1324,6 @@ export class Matterbridge extends EventEmitter {
1612
1324
  continue;
1613
1325
  }
1614
1326
  await this.showCommissioningQRCode(plugin.commissioningServer, plugin.storageContext, plugin.nodeContext, plugin.name);
1615
- // Setting reachability to true
1616
1327
  plugin.reachabilityTimeout = setTimeout(() => {
1617
1328
  this.log.info(`Setting reachability to true for ${plg}${plugin.name}${db}`);
1618
1329
  if (plugin.commissioningServer)
@@ -1625,11 +1336,6 @@ export class Matterbridge extends EventEmitter {
1625
1336
  }
1626
1337
  }, 1000);
1627
1338
  }
1628
- /**
1629
- * Starts the Matterbridge controller.
1630
- * @private
1631
- * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1632
- */
1633
1339
  async startController() {
1634
1340
  if (!this.storageManager) {
1635
1341
  this.log.error('No storage manager initialized');
@@ -1692,7 +1398,7 @@ export class Matterbridge extends EventEmitter {
1692
1398
  const nodeId = await this.commissioningController.commissionNode(options);
1693
1399
  this.log.info(`Commissioning successfully done with nodeId: ${nodeId}`);
1694
1400
  this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
1695
- } // (hasParameter('pairingcode'))
1401
+ }
1696
1402
  if (hasParameter('unpairall')) {
1697
1403
  this.log.info('***Commissioning controller unpairing all nodes...');
1698
1404
  const nodeIds = this.commissioningController.getCommissionedNodes();
@@ -1703,8 +1409,6 @@ export class Matterbridge extends EventEmitter {
1703
1409
  return;
1704
1410
  }
1705
1411
  if (hasParameter('discover')) {
1706
- // const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
1707
- // console.log(discover);
1708
1412
  }
1709
1413
  if (!this.commissioningController.isCommissioned()) {
1710
1414
  this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
@@ -1745,12 +1449,10 @@ export class Matterbridge extends EventEmitter {
1745
1449
  },
1746
1450
  });
1747
1451
  node.logStructure();
1748
- // Get the interaction client
1749
1452
  this.log.info('Getting the interaction client');
1750
1453
  const interactionClient = await node.getInteractionClient();
1751
1454
  let cluster;
1752
1455
  let attributes;
1753
- // Log BasicInformationCluster
1754
1456
  cluster = BasicInformationCluster;
1755
1457
  attributes = await interactionClient.getMultipleAttributes({
1756
1458
  attributes: [{ clusterId: cluster.id }],
@@ -1760,7 +1462,6 @@ export class Matterbridge extends EventEmitter {
1760
1462
  attributes.forEach((attribute) => {
1761
1463
  this.log.info(`- endpoint ${or}${attribute.path.endpointId}${nf} cluster ${hk}${getClusterNameById(attribute.path.clusterId)}${nf} (${hk}0x${attribute.path.clusterId.toString(16)}${nf}) attribute ${zb}${attribute.path.attributeName}${nf} (${zb}0x${attribute.path.attributeId.toString(16)}${nf}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`);
1762
1464
  });
1763
- // Log PowerSourceCluster
1764
1465
  cluster = PowerSourceCluster;
1765
1466
  attributes = await interactionClient.getMultipleAttributes({
1766
1467
  attributes: [{ clusterId: cluster.id }],
@@ -1770,7 +1471,6 @@ export class Matterbridge extends EventEmitter {
1770
1471
  attributes.forEach((attribute) => {
1771
1472
  this.log.info(`- endpoint ${or}${attribute.path.endpointId}${nf} cluster ${hk}${getClusterNameById(attribute.path.clusterId)}${nf} (${hk}0x${attribute.path.clusterId.toString(16)}${nf}) attribute ${zb}${attribute.path.attributeName}${nf} (${zb}0x${attribute.path.attributeId.toString(16)}${nf}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`);
1772
1473
  });
1773
- // Log ThreadNetworkDiagnostics
1774
1474
  cluster = ThreadNetworkDiagnosticsCluster;
1775
1475
  attributes = await interactionClient.getMultipleAttributes({
1776
1476
  attributes: [{ clusterId: cluster.id }],
@@ -1780,7 +1480,6 @@ export class Matterbridge extends EventEmitter {
1780
1480
  attributes.forEach((attribute) => {
1781
1481
  this.log.info(`- endpoint ${or}${attribute.path.endpointId}${nf} cluster ${hk}${getClusterNameById(attribute.path.clusterId)}${nf} (${hk}0x${attribute.path.clusterId.toString(16)}${nf}) attribute ${zb}${attribute.path.attributeName}${nf} (${zb}0x${attribute.path.attributeId.toString(16)}${nf}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`);
1782
1482
  });
1783
- // Log SwitchCluster
1784
1483
  cluster = SwitchCluster;
1785
1484
  attributes = await interactionClient.getMultipleAttributes({
1786
1485
  attributes: [{ clusterId: cluster.id }],
@@ -1801,15 +1500,6 @@ export class Matterbridge extends EventEmitter {
1801
1500
  this.log.info('Subscribed to all attributes and events');
1802
1501
  }
1803
1502
  }
1804
- /** ***********************************************************************************************************************************/
1805
- /** Matter.js methods */
1806
- /** ***********************************************************************************************************************************/
1807
- /**
1808
- * Starts the matter storage process based on the specified storage type and name.
1809
- * @param {string} storageType - The type of storage to start (e.g., 'disk', 'json').
1810
- * @param {string} storageName - The name of the storage file.
1811
- * @returns {Promise<void>} - A promise that resolves when the storage process is started.
1812
- */
1813
1503
  async startMatterStorage(storageType, storageName) {
1814
1504
  this.log.debug(`Starting matter ${storageType} storage ${CYAN}${storageName}${db}`);
1815
1505
  if (storageType === 'disk') {
@@ -1854,13 +1544,109 @@ export class Matterbridge extends EventEmitter {
1854
1544
  }
1855
1545
  this.log.debug(`Creating commissioning server context for ${plg}Matterbridge${db}`);
1856
1546
  this.matterbridgeContext = await this.createCommissioningServerContext('Matterbridge', 'Matterbridge', aggregator.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge aggregator');
1547
+ await this.matterbridgeContext.set('port', this.port);
1548
+ await this.matterbridgeContext.set('passcode', this.passcode);
1549
+ await this.matterbridgeContext.set('discriminator', this.discriminator);
1550
+ }
1551
+ async convertStorage(context, pluginName) {
1552
+ const storageService = Environment.default.get(StorageService);
1553
+ Environment.default.vars.set('path.root', path.join(this.matterbridgeDirectory, 'matterstorage' + (this.profile ? '.' + this.profile : '')));
1554
+ const nodeStorage = await storageService.open('Matterbridge');
1555
+ if ((await nodeStorage.createContext('persist').get('converted', false)) === true) {
1556
+ this.log.info(`Matter node storage already converted to Matterbridge edge for ${plg}${pluginName}${nf}`);
1557
+ return;
1558
+ }
1559
+ else {
1560
+ this.log.notice(`Converting matter node storage to Matterbridge edge for ${plg}${pluginName}${nt}...`);
1561
+ }
1562
+ const fabricManagerContext = context.createContext('FabricManager');
1563
+ const fabrics = (await fabricManagerContext.get('fabrics', []));
1564
+ const nextFabricIndex = await fabricManagerContext.get('nextFabricIndex', 1);
1565
+ const eventHandlerContext = context.createContext('EventHandler');
1566
+ const sessionManagerContext = context.createContext('SessionManager');
1567
+ const endpointStructureContext = context.createContext('EndpointStructure');
1568
+ const generalCommissioningContext = context.createContext('Cluster-0-48');
1569
+ const basicInformationContext = context.createContext('Cluster-0-40');
1570
+ const fabricInfo = {};
1571
+ const fabricInfoArray = [];
1572
+ const nocArray = [];
1573
+ const trcArray = [];
1574
+ const aclArray = [];
1575
+ this.log.info(`Found ${CYAN}${fabrics.length}${nf} fabrics (nextFabricIndex ${CYAN}${nextFabricIndex}${nf}) for ${plg}${pluginName}${nf}:`);
1576
+ for (const fabric of fabrics) {
1577
+ this.log.info(`- fabricIndex ${CYAN}${fabric.fabricIndex}${nf} fabricId ${CYAN}${fabric.fabricId}${nf} nodeId ${CYAN}${fabric.nodeId}${nf} rootNodeId ${CYAN}${fabric.rootNodeId}${nf} rootVendorId ${CYAN}${fabric.rootVendorId}${nf} label ${CYAN}${fabric.label}${nf}`);
1578
+ fabricInfo[fabric.fabricIndex] = {
1579
+ fabricIndex: fabric.fabricIndex,
1580
+ fabricId: fabric.fabricId,
1581
+ nodeId: fabric.nodeId,
1582
+ rootNodeId: fabric.rootNodeId,
1583
+ rootVendorId: fabric.rootVendorId,
1584
+ label: fabric.label,
1585
+ };
1586
+ fabricInfoArray.push({
1587
+ fabricIndex: fabric.fabricIndex,
1588
+ fabricId: fabric.fabricId,
1589
+ nodeId: fabric.nodeId,
1590
+ vendorId: fabric.rootVendorId,
1591
+ rootPublicKey: fabric.rootPublicKey,
1592
+ label: fabric.label,
1593
+ });
1594
+ nocArray.push({ noc: fabric.operationalCert, icac: null, fabricIndex: fabric.fabricIndex });
1595
+ trcArray.push('{\"__object__\":\"Uint8Array\",\"__value__\":\"' + Buffer.from(fabric.rootCert).toString('hex') + '\"}');
1596
+ aclArray.push({ fabricIndex: fabric.fabricIndex, privilege: 5, authMode: 2, subjects: ['{\"__object__\":\"BigInt\",\"__value__\":\"' + fabric.rootNodeId.toString().replace('n', '') + '\"}'], targets: null });
1597
+ }
1598
+ await nodeStorage.createContext('fabrics').set('fabrics', fabrics);
1599
+ await nodeStorage.createContext('fabrics').set('nextFabricIndex', nextFabricIndex);
1600
+ await nodeStorage.createContext('sessions').set('resumptionRecords', await sessionManagerContext.get('resumptionRecords', []));
1601
+ await nodeStorage.createContext('events').set('lastEventNumber', await eventHandlerContext.get('lastEventNumber', 1));
1602
+ await nodeStorage.createContext('root').set('__number__', 0);
1603
+ await nodeStorage.createContext('root').set('__nextNumber__', 1);
1604
+ await nodeStorage.createContext('root').createContext('commissioning').set('enabled', true);
1605
+ await nodeStorage.createContext('root').createContext('commissioning').set('commissioned', true);
1606
+ await nodeStorage.createContext('root').createContext('commissioning').set('fabrics', fabricInfo);
1607
+ await nodeStorage.createContext('root').createContext('operationalCredentials').set('commissionedFabrics', fabricInfoArray.length);
1608
+ await nodeStorage.createContext('root').createContext('operationalCredentials').set('fabrics', fabricInfoArray);
1609
+ await nodeStorage.createContext('root').createContext('operationalCredentials').set('nocs', nocArray);
1610
+ await nodeStorage.createContext('root').createContext('operationalCredentials').set('trustedRootCertificates', trcArray);
1611
+ await nodeStorage.createContext('root').createContext('accessControl').set('acl', aclArray);
1612
+ await nodeStorage
1613
+ .createContext('root')
1614
+ .createContext('generalCommissioning')
1615
+ .set('breadcrumb', await generalCommissioningContext.get('breadcrumb', BigInt(0)));
1616
+ await nodeStorage
1617
+ .createContext('root')
1618
+ .createContext('basicInformation')
1619
+ .set('location', await basicInformationContext.get('location', 'XX'));
1620
+ await nodeStorage.createContext('root').createContext('network').set('ble', false);
1621
+ await nodeStorage.createContext('root').createContext('network').set('operationalPort', 5540);
1622
+ await nodeStorage.createContext('root').createContext('productDescription').set('productId', 0x8000);
1623
+ await nodeStorage.createContext('root').createContext('productDescription').set('vendorId', 0xfff1);
1624
+ for (const key of await endpointStructureContext.keys()) {
1625
+ if (key === 'nextEndpointId')
1626
+ continue;
1627
+ const parts = key.split('-');
1628
+ const number = await endpointStructureContext.get(key);
1629
+ if (parts.length === 2) {
1630
+ this.log.debug(`Converting Matterbridge.EndpointStructure:${key}:${number} to root.parts.Matterbridge.__number__:${number}`);
1631
+ await nodeStorage.createContext('root').createContext('parts').createContext('Matterbridge').set('__number__', number);
1632
+ }
1633
+ else if (parts.length === 3 && parts[2].startsWith('custom_')) {
1634
+ this.log.debug(`Converting Matterbridge.EndpointStructure:${key}:${number} to root.parts.Matterbridge.parts.${parts[2].replace('custom_', '')}.__number__:${number}`);
1635
+ await nodeStorage.createContext('root').createContext('parts').createContext('Matterbridge').createContext('parts').createContext(parts[2].replace('custom_', '')).set('__number__', number);
1636
+ }
1637
+ else if (parts.length === 3 && parts[2].startsWith('unique_')) {
1638
+ const device = this.devices.get(parts[2].replace('unique_', ''));
1639
+ if (device && device.deviceName && device.maybeNumber) {
1640
+ this.log.debug(`Converting Matterbridge.EndpointStructure:${key}:${number} to root.parts.Matterbridge.parts.${device.deviceName.replace(/[ .]/g, '')}.__number__:${device.maybeNumber}`);
1641
+ await nodeStorage.createContext('root').createContext('parts').createContext('Matterbridge').createContext('parts').createContext(device.deviceName.replace(/[ .]/g, '')).set('__number__', device.maybeNumber);
1642
+ }
1643
+ }
1644
+ }
1645
+ await nodeStorage.createContext('root').createContext('parts').createContext('Matterbridge').set('__number__', 1);
1646
+ await nodeStorage.createContext('persist').set('converted', true);
1647
+ await context.set('converted', true);
1648
+ this.log.notice(`Matter storage converted to Matterbridge edge for ${plg}${pluginName}${nt}`);
1857
1649
  }
1858
- /**
1859
- * Makes a backup copy of the specified matter JSON storage file.
1860
- *
1861
- * @param storageName - The name of the JSON storage file to be backed up.
1862
- * @param backupName - The name of the backup file to be created.
1863
- */
1864
1650
  async backupMatterStorage(storageName, backupName) {
1865
1651
  try {
1866
1652
  this.log.debug(`Making backup copy of ${storageName}`);
@@ -1881,12 +1667,6 @@ export class Matterbridge extends EventEmitter {
1881
1667
  }
1882
1668
  }
1883
1669
  }
1884
- /**
1885
- * Restore the specified matter JSON storage file.
1886
- *
1887
- * @param backupName - The name of the backup file to restore from.
1888
- * @param storageName - The name of the JSON storage file to restored.
1889
- */
1890
1670
  async restoreMatterStorage(backupName, storageName) {
1891
1671
  try {
1892
1672
  this.log.notice(`Restoring the backup copy of ${storageName}`);
@@ -1907,10 +1687,6 @@ export class Matterbridge extends EventEmitter {
1907
1687
  }
1908
1688
  }
1909
1689
  }
1910
- /**
1911
- * Stops the matter storage.
1912
- * @returns {Promise<void>} A promise that resolves when the storage is stopped.
1913
- */
1914
1690
  async stopMatterStorage() {
1915
1691
  this.log.debug('Stopping storage');
1916
1692
  await this.storageManager?.close();
@@ -1919,14 +1695,8 @@ export class Matterbridge extends EventEmitter {
1919
1695
  this.matterbridgeContext = undefined;
1920
1696
  this.mattercontrollerContext = undefined;
1921
1697
  }
1922
- /**
1923
- * Creates a Matter server using the provided storage manager and the provided mdnsInterface.
1924
- * @param storageManager The storage manager to be used by the Matter server.
1925
- *
1926
- */
1927
1698
  createMatterServer(storageManager) {
1928
1699
  this.log.debug('Creating matter server');
1929
- // Validate mdnsInterface
1930
1700
  if (this.mdnsInterface) {
1931
1701
  const networkInterfaces = os.networkInterfaces();
1932
1702
  const availableInterfaces = Object.keys(networkInterfaces);
@@ -1942,10 +1712,6 @@ export class Matterbridge extends EventEmitter {
1942
1712
  this.log.debug(`Created matter server with mdnsInterface: ${this.mdnsInterface ?? 'all available interfaces'}`);
1943
1713
  return matterServer;
1944
1714
  }
1945
- /**
1946
- * Starts the Matter server.
1947
- * If the Matter server is not initialized, it logs an error and performs cleanup.
1948
- */
1949
1715
  async startMatterServer() {
1950
1716
  if (!this.matterServer) {
1951
1717
  this.log.error('No matter server initialized');
@@ -1955,11 +1721,7 @@ export class Matterbridge extends EventEmitter {
1955
1721
  this.log.debug('Starting matter server...');
1956
1722
  await this.matterServer.start();
1957
1723
  this.log.debug('Started matter server');
1958
- // this.commissioningServer?.getRootEndpoint() && logEndpoint(this.commissioningServer?.getRootEndpoint());
1959
1724
  }
1960
- /**
1961
- * Stops the Matter server, commissioningServer and commissioningController.
1962
- */
1963
1725
  async stopMatterServer() {
1964
1726
  this.log.debug('Stopping matter commissioningServer');
1965
1727
  await this.commissioningServer?.close();
@@ -1973,35 +1735,23 @@ export class Matterbridge extends EventEmitter {
1973
1735
  this.matterAggregator = undefined;
1974
1736
  this.matterServer = undefined;
1975
1737
  }
1976
- /**
1977
- * Creates a Matter Aggregator.
1978
- * @param {StorageContext} context - The storage context.
1979
- * @returns {Aggregator} - The created Matter Aggregator.
1980
- */
1981
1738
  async createMatterAggregator(context, pluginName) {
1982
1739
  this.log.debug(`Creating matter aggregator for ${plg}${pluginName}${db}`);
1983
1740
  const matterAggregator = new Aggregator();
1984
1741
  return matterAggregator;
1985
1742
  }
1986
- /**
1987
- * Creates a matter commissioning server.
1988
- *
1989
- * @param {StorageContext} context - The storage context.
1990
- * @param {string} pluginName - The name of the commissioning server.
1991
- * @returns {CommissioningServer} The created commissioning server.
1992
- */
1993
1743
  async createCommisioningServer(context, pluginName) {
1994
1744
  this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db}`);
1995
1745
  const deviceName = await context.get('deviceName');
1996
1746
  const deviceType = await context.get('deviceType');
1997
1747
  const vendorId = await context.get('vendorId');
1998
- const vendorName = await context.get('vendorName'); // Home app = Manufacturer
1748
+ const vendorName = await context.get('vendorName');
1999
1749
  const productId = await context.get('productId');
2000
- const productName = await context.get('productName'); // Home app = Model
1750
+ const productName = await context.get('productName');
2001
1751
  const serialNumber = await context.get('serialNumber');
2002
1752
  const uniqueId = await context.get('uniqueId');
2003
1753
  const softwareVersion = await context.get('softwareVersion', 1);
2004
- const softwareVersionString = await context.get('softwareVersionString', '1.0.0'); // Home app = Firmware Revision
1754
+ const softwareVersionString = await context.get('softwareVersionString', '1.0.0');
2005
1755
  const hardwareVersion = await context.get('hardwareVersion', 1);
2006
1756
  const hardwareVersionString = await context.get('hardwareVersionString', '1.0.0');
2007
1757
  this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with deviceName '${deviceName}' deviceType ${deviceType}(0x${deviceType.toString(16).padStart(4, '0')})`);
@@ -2009,7 +1759,6 @@ export class Matterbridge extends EventEmitter {
2009
1759
  this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with softwareVersion ${softwareVersion} softwareVersionString ${softwareVersionString}`);
2010
1760
  this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with hardwareVersion ${hardwareVersion} hardwareVersionString ${hardwareVersionString}`);
2011
1761
  this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with nodeLabel '${productName}' port ${CYAN}${this.port}${db} discriminator ${CYAN}${this.discriminator}${db} passcode ${CYAN}${this.passcode}${db} `);
2012
- // Validate ipv4address
2013
1762
  if (this.ipv4address) {
2014
1763
  const networkInterfaces = os.networkInterfaces();
2015
1764
  const availableAddresses = Object.values(networkInterfaces)
@@ -2024,7 +1773,6 @@ export class Matterbridge extends EventEmitter {
2024
1773
  this.log.info(`Using ipv4address '${this.ipv4address}' for the Matter commissioning server.`);
2025
1774
  }
2026
1775
  }
2027
- // Validate ipv6address
2028
1776
  if (this.ipv6address) {
2029
1777
  const networkInterfaces = os.networkInterfaces();
2030
1778
  const availableAddresses = Object.values(networkInterfaces)
@@ -2055,7 +1803,7 @@ export class Matterbridge extends EventEmitter {
2055
1803
  nodeLabel: productName,
2056
1804
  productLabel: productName,
2057
1805
  softwareVersion,
2058
- softwareVersionString, // Home app = Firmware Revision
1806
+ softwareVersionString,
2059
1807
  hardwareVersion,
2060
1808
  hardwareVersionString,
2061
1809
  uniqueId,
@@ -2151,24 +1899,6 @@ export class Matterbridge extends EventEmitter {
2151
1899
  commissioningServer.addCommandHandler('testEventTrigger', async ({ request: { enableKey, eventTrigger } }) => this.log.info(`testEventTrigger called on GeneralDiagnostic cluster: ${enableKey} ${eventTrigger}`));
2152
1900
  return commissioningServer;
2153
1901
  }
2154
- /**
2155
- * Creates a commissioning server storage context.
2156
- *
2157
- * @param pluginName - The name of the plugin.
2158
- * @param deviceName - The name of the device.
2159
- * @param deviceType - The type of the device.
2160
- * @param vendorId - The vendor ID.
2161
- * @param vendorName - The vendor name.
2162
- * @param productId - The product ID.
2163
- * @param productName - The product name.
2164
- * @param serialNumber - The serial number of the device (optional).
2165
- * @param uniqueId - The unique ID of the device (optional).
2166
- * @param softwareVersion - The software version of the device (optional).
2167
- * @param softwareVersionString - The software version string of the device (optional).
2168
- * @param hardwareVersion - The hardware version of the device (optional).
2169
- * @param hardwareVersionString - The hardware version string of the device (optional).
2170
- * @returns The storage context for the commissioning server.
2171
- */
2172
1902
  async createCommissioningServerContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName) {
2173
1903
  if (!this.storageManager)
2174
1904
  throw new Error('No storage manager initialized');
@@ -2196,13 +1926,6 @@ export class Matterbridge extends EventEmitter {
2196
1926
  this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
2197
1927
  return storageContext;
2198
1928
  }
2199
- /**
2200
- * Imports the commissioning server context for a specific plugin and device.
2201
- * @param pluginName - The name of the plugin.
2202
- * @param device - The MatterbridgeDevice object representing the device.
2203
- * @returns The commissioning server context.
2204
- * @throws Error if the BasicInformationCluster is not found.
2205
- */
2206
1929
  async importCommissioningServerContext(pluginName, device) {
2207
1930
  this.log.debug(`Importing matter commissioning server storage context from device for ${plg}${pluginName}${db}`);
2208
1931
  const basic = device.getClusterServer(BasicInformationCluster);
@@ -2237,14 +1960,6 @@ export class Matterbridge extends EventEmitter {
2237
1960
  this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
2238
1961
  return storageContext;
2239
1962
  }
2240
- /**
2241
- * Shows the commissioning server QR code for a given plugin.
2242
- * @param {CommissioningServer} commissioningServer - The commissioning server instance.
2243
- * @param {StorageContext} storageContext - The storage context instance.
2244
- * @param {NodeStorage} nodeContext - The node storage instance.
2245
- * @param {string} pluginName - The name of the plugin of Matterbridge in bridge mode.
2246
- * @returns {Promise<void>} - A promise that resolves when the QR code is shown.
2247
- */
2248
1963
  async showCommissioningQRCode(commissioningServer, storageContext, nodeContext, pluginName) {
2249
1964
  if (!commissioningServer || !storageContext || !nodeContext || !pluginName) {
2250
1965
  this.log.error(`showCommissioningQRCode error: commissioningServer: ${!commissioningServer} storageContext: ${!storageContext} nodeContext: ${!nodeContext} pluginName: ${pluginName}`);
@@ -2255,8 +1970,7 @@ export class Matterbridge extends EventEmitter {
2255
1970
  const { qrPairingCode, manualPairingCode } = commissioningServer.getPairingCode();
2256
1971
  const QrCode = new QrCodeSchema();
2257
1972
  this.log.info(`*The commissioning server on port ${commissioningServer.getPort()} for ${plg}${pluginName}${nf} is not commissioned. Pair it scanning the QR code:\n\n`);
2258
- // eslint-disable-next-line no-console
2259
- if (this.log.logLevel === "debug" /* LogLevel.DEBUG */ || this.log.logLevel === "info" /* LogLevel.INFO */)
1973
+ if (this.log.logLevel === "debug" || this.log.logLevel === "info")
2260
1974
  console.log(`${QrCode.encode(qrPairingCode)}\n`);
2261
1975
  this.log.info(`${plg}${pluginName}${nf} \n\nqrPairingCode: ${qrPairingCode} \n\nManual pairing code: ${manualPairingCode}\n`);
2262
1976
  if (pluginName === 'Matterbridge') {
@@ -2303,12 +2017,6 @@ export class Matterbridge extends EventEmitter {
2303
2017
  }
2304
2018
  this.wssSendRefreshRequired();
2305
2019
  }
2306
- /**
2307
- * Sanitizes the fabric information by converting bigint properties to string cause res.json doesn't know bigint.
2308
- *
2309
- * @param fabricInfo - The array of exposed fabric information objects.
2310
- * @returns An array of sanitized exposed fabric information objects.
2311
- */
2312
2020
  sanitizeFabricInformations(fabricInfo) {
2313
2021
  return fabricInfo.map((info) => {
2314
2022
  return {
@@ -2322,12 +2030,6 @@ export class Matterbridge extends EventEmitter {
2322
2030
  };
2323
2031
  });
2324
2032
  }
2325
- /**
2326
- * Sanitizes the session information by converting bigint properties to string.
2327
- *
2328
- * @param sessionInfo - The array of session information objects.
2329
- * @returns An array of sanitized session information objects.
2330
- */
2331
2033
  sanitizeSessionInformation(sessionInfo) {
2332
2034
  return sessionInfo
2333
2035
  .filter((session) => session.isPeerActive)
@@ -2355,12 +2057,6 @@ export class Matterbridge extends EventEmitter {
2355
2057
  };
2356
2058
  });
2357
2059
  }
2358
- /**
2359
- * Sets the reachability of a commissioning server and trigger.
2360
- *
2361
- * @param {CommissioningServer} commissioningServer - The commissioning server to set the reachability for.
2362
- * @param {boolean} reachable - The new reachability status.
2363
- */
2364
2060
  setCommissioningServerReachability(commissioningServer, reachable) {
2365
2061
  const basicInformationCluster = commissioningServer?.getRootClusterServer(BasicInformationCluster);
2366
2062
  if (basicInformationCluster && basicInformationCluster.attributes.reachable !== undefined)
@@ -2368,11 +2064,6 @@ export class Matterbridge extends EventEmitter {
2368
2064
  if (basicInformationCluster && basicInformationCluster.triggerReachableChangedEvent)
2369
2065
  basicInformationCluster.triggerReachableChangedEvent({ reachableNewValue: reachable });
2370
2066
  }
2371
- /**
2372
- * Sets the reachability of the specified matter aggregator and its bridged devices and trigger.
2373
- * @param {Aggregator} matterAggregator - The matter aggregator to set the reachability for.
2374
- * @param {boolean} reachable - A boolean indicating the reachability status to set.
2375
- */
2376
2067
  setAggregatorReachability(matterAggregator, reachable) {
2377
2068
  const basicInformationCluster = matterAggregator.getClusterServer(BasicInformationCluster);
2378
2069
  if (basicInformationCluster && basicInformationCluster.attributes.reachable !== undefined)
@@ -2385,12 +2076,6 @@ export class Matterbridge extends EventEmitter {
2385
2076
  device.getClusterServer(BridgedDeviceBasicInformationCluster)?.triggerReachableChangedEvent({ reachableNewValue: reachable });
2386
2077
  });
2387
2078
  }
2388
- /**
2389
- * Sets the reachability of a device and trigger.
2390
- *
2391
- * @param {MatterbridgeDevice} device - The device to set the reachability for.
2392
- * @param {boolean} reachable - The new reachability status of the device.
2393
- */
2394
2079
  setDeviceReachability(device, reachable) {
2395
2080
  const basicInformationCluster = device.getClusterServer(BasicInformationCluster);
2396
2081
  if (basicInformationCluster && basicInformationCluster.attributes.reachable !== undefined)
@@ -2439,10 +2124,6 @@ export class Matterbridge extends EventEmitter {
2439
2124
  }
2440
2125
  return vendorName;
2441
2126
  };
2442
- /**
2443
- * Retrieves the base registered plugins sanitized for res.json().
2444
- * @returns {BaseRegisteredPlugin[]} A promise that resolves to an array of BaseRegisteredPlugin objects.
2445
- */
2446
2127
  async getBaseRegisteredPlugins() {
2447
2128
  const baseRegisteredPlugins = [];
2448
2129
  for (const plugin of this.plugins) {
@@ -2474,36 +2155,13 @@ export class Matterbridge extends EventEmitter {
2474
2155
  }
2475
2156
  return baseRegisteredPlugins;
2476
2157
  }
2477
- /**
2478
- * Spawns a child process with the given command and arguments.
2479
- * @param {string} command - The command to execute.
2480
- * @param {string[]} args - The arguments to pass to the command (default: []).
2481
- * @returns {Promise<boolean>} A promise that resolves when the child process exits successfully, or rejects if there is an error.
2482
- */
2483
2158
  async spawnCommand(command, args = []) {
2484
- /*
2485
- npm > npm.cmd on windows
2486
- cmd.exe ['dir'] on windows
2487
- await this.spawnCommand('npm', ['install', '-g', 'matterbridge']);
2488
- process.on('unhandledRejection', (reason, promise) => {
2489
- this.log.error('Unhandled Rejection at:', promise, 'reason:', reason);
2490
- });
2491
-
2492
- spawn - [14:27:21.125] [Matterbridge:spawn]: changed 38 packages in 4s
2493
- spawn - [14:27:21.125] [Matterbridge:spawn]: 10 packages are looking for funding run `npm fund` for details
2494
- debug - [14:27:21.131] [Matterbridge]: Child process exited with code 0 and signal null
2495
- debug - [14:27:21.131] [Matterbridge]: Child process stdio streams have closed with code 0
2496
- */
2497
2159
  const cmdLine = command + ' ' + args.join(' ');
2498
2160
  if (process.platform === 'win32' && command === 'npm') {
2499
- // Must be spawn('cmd.exe', ['/c', 'npm -g install <package>']);
2500
2161
  const argstring = 'npm ' + args.join(' ');
2501
2162
  args.splice(0, args.length, '/c', argstring);
2502
2163
  command = 'cmd.exe';
2503
2164
  }
2504
- // Decide when using sudo on linux
2505
- // When you need sudo: Spawn stderr: npm error Error: EACCES: permission denied
2506
- // When you don't need sudo: Failed to start child process "npm install -g matterbridge-eve-door": spawn sudo ENOENT
2507
2165
  if (hasParameter('sudo') || (process.platform === 'linux' && command === 'npm' && !hasParameter('docker') && !hasParameter('nosudo'))) {
2508
2166
  args.unshift(command);
2509
2167
  command = 'sudo';
@@ -2561,102 +2219,55 @@ export class Matterbridge extends EventEmitter {
2561
2219
  }
2562
2220
  });
2563
2221
  }
2564
- /**
2565
- * Sends a WebSocket message to all connected clients.
2566
- *
2567
- * @param {string} level - The logger level of the message: debug info notice warn error fatal...
2568
- * @param {string} time - The time string of the message
2569
- * @param {string} name - The logger name of the message
2570
- * @param {string} message - The content of the message.
2571
- */
2572
2222
  wssSendMessage(level, time, name, message) {
2573
2223
  if (!level || !time || !name || !message)
2574
2224
  return;
2575
- // Remove ANSI escape codes from the message
2576
- // eslint-disable-next-line no-control-regex
2577
2225
  message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
2578
- // Remove leading asterisks from the message
2579
2226
  message = message.replace(/^\*+/, '');
2580
- // Replace all occurrences of \t and \n
2581
2227
  message = message.replace(/[\t\n]/g, '');
2582
- // Remove non-printable characters
2583
- // eslint-disable-next-line no-control-regex
2584
2228
  message = message.replace(/[\x00-\x1F\x7F]/g, '');
2585
- // Replace all occurrences of \" with "
2586
2229
  message = message.replace(/\\"/g, '"');
2587
- // Define the maximum allowed length for continuous characters without a space
2588
2230
  const maxContinuousLength = 100;
2589
2231
  const keepStartLength = 20;
2590
2232
  const keepEndLength = 20;
2591
- // Split the message into words
2592
2233
  message = message
2593
2234
  .split(' ')
2594
2235
  .map((word) => {
2595
- // If the word length exceeds the max continuous length, insert spaces and truncate
2596
2236
  if (word.length > maxContinuousLength) {
2597
2237
  return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
2598
2238
  }
2599
2239
  return word;
2600
2240
  })
2601
2241
  .join(' ');
2602
- // Send the message to all connected clients
2603
2242
  this.webSocketServer?.clients.forEach((client) => {
2604
2243
  if (client.readyState === WebSocket.OPEN) {
2605
2244
  client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
2606
2245
  }
2607
2246
  });
2608
2247
  }
2609
- /**
2610
- * Sends a need to refresh WebSocket message to all connected clients.
2611
- *
2612
- */
2613
2248
  wssSendRefreshRequired() {
2614
2249
  this.matterbridgeInformation.refreshRequired = true;
2615
- // Send the message to all connected clients
2616
2250
  this.webSocketServer?.clients.forEach((client) => {
2617
2251
  if (client.readyState === WebSocket.OPEN) {
2618
2252
  client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Matterbridge', method: 'refresh_required', params: {} }));
2619
2253
  }
2620
2254
  });
2621
2255
  }
2622
- /**
2623
- * Sends a need to restart WebSocket message to all connected clients.
2624
- *
2625
- */
2626
2256
  wssSendRestartRequired() {
2627
2257
  this.matterbridgeInformation.restartRequired = true;
2628
- // Send the message to all connected clients
2629
2258
  this.webSocketServer?.clients.forEach((client) => {
2630
2259
  if (client.readyState === WebSocket.OPEN) {
2631
2260
  client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Matterbridge', method: 'restart_required', params: {} }));
2632
2261
  }
2633
2262
  });
2634
2263
  }
2635
- /**
2636
- * Initializes the frontend of Matterbridge.
2637
- *
2638
- * @param port The port number to run the frontend server on. Default is 8283.
2639
- */
2640
2264
  async initializeFrontend(port = 8283) {
2641
2265
  let initializeError = false;
2642
2266
  this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${port}${db}`);
2643
- // Create the express app that serves the frontend
2644
2267
  this.expressApp = express();
2645
- // Log all requests to the server for debugging
2646
- /*
2647
- if (hasParameter('homedir')) {
2648
- this.expressApp.use((req, res, next) => {
2649
- this.log.debug(`Received request on expressApp: ${req.method} ${req.url}`);
2650
- next();
2651
- });
2652
- }
2653
- */
2654
- // Serve static files from '/static' endpoint
2655
2268
  this.expressApp.use(express.static(path.join(this.rootDirectory, 'frontend/build')));
2656
2269
  if (!hasParameter('ssl')) {
2657
- // Create an HTTP server and attach the express app
2658
2270
  this.httpServer = createServer(this.expressApp);
2659
- // Listen on the specified port
2660
2271
  if (hasParameter('ingress')) {
2661
2272
  this.httpServer.listen(port, '0.0.0.0', () => {
2662
2273
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${port}${UNDERLINEOFF}${rs}`);
@@ -2670,7 +2281,6 @@ export class Matterbridge extends EventEmitter {
2670
2281
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.systemInformation.ipv6Address}]:${port}${UNDERLINEOFF}${rs}`);
2671
2282
  });
2672
2283
  }
2673
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
2674
2284
  this.httpServer.on('error', (error) => {
2675
2285
  this.log.error(`Frontend http server error listening on ${port}`);
2676
2286
  switch (error.code) {
@@ -2686,7 +2296,6 @@ export class Matterbridge extends EventEmitter {
2686
2296
  });
2687
2297
  }
2688
2298
  else {
2689
- // Load the SSL certificate, the private key and optionally the CA certificate
2690
2299
  let cert;
2691
2300
  try {
2692
2301
  cert = await fs.readFile(path.join(this.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
@@ -2714,9 +2323,7 @@ export class Matterbridge extends EventEmitter {
2714
2323
  this.log.info(`CA certificate file ${path.join(this.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
2715
2324
  }
2716
2325
  const serverOptions = { cert, key, ca };
2717
- // Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
2718
2326
  this.httpsServer = https.createServer(serverOptions, this.expressApp);
2719
- // Listen on the specified port
2720
2327
  if (hasParameter('ingress')) {
2721
2328
  this.httpsServer.listen(port, '0.0.0.0', () => {
2722
2329
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${port}${UNDERLINEOFF}${rs}`);
@@ -2730,7 +2337,6 @@ export class Matterbridge extends EventEmitter {
2730
2337
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.systemInformation.ipv6Address}]:${port}${UNDERLINEOFF}${rs}`);
2731
2338
  });
2732
2339
  }
2733
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
2734
2340
  this.httpsServer.on('error', (error) => {
2735
2341
  this.log.error(`Frontend https server error listening on ${port}`);
2736
2342
  switch (error.code) {
@@ -2747,13 +2353,12 @@ export class Matterbridge extends EventEmitter {
2747
2353
  }
2748
2354
  if (initializeError)
2749
2355
  return;
2750
- // Createe a WebSocket server and attach it to the http or https server
2751
2356
  const wssPort = port;
2752
2357
  const wssHost = hasParameter('ssl') ? `wss://${this.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.systemInformation.ipv4Address}:${wssPort}`;
2753
2358
  this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
2754
2359
  this.webSocketServer.on('connection', (ws, request) => {
2755
2360
  const clientIp = request.socket.remoteAddress;
2756
- AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), "debug" /* LogLevel.DEBUG */);
2361
+ AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), "debug");
2757
2362
  this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
2758
2363
  ws.on('message', (message) => {
2759
2364
  this.log.debug(`WebSocket client message: ${message}`);
@@ -2786,7 +2391,6 @@ export class Matterbridge extends EventEmitter {
2786
2391
  this.webSocketServer.on('error', (ws, error) => {
2787
2392
  this.log.error(`WebSocketServer error: ${error}`);
2788
2393
  });
2789
- // Endpoint to validate login code
2790
2394
  this.expressApp.post('/api/login', express.json(), async (req, res) => {
2791
2395
  const { password } = req.body;
2792
2396
  this.log.debug('The frontend sent /api/login', password);
@@ -2805,14 +2409,12 @@ export class Matterbridge extends EventEmitter {
2805
2409
  this.log.warn('/api/login error wrong password');
2806
2410
  res.json({ valid: false });
2807
2411
  }
2808
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
2809
2412
  }
2810
2413
  catch (error) {
2811
2414
  this.log.error('/api/login error getting password');
2812
2415
  res.json({ valid: false });
2813
2416
  }
2814
2417
  });
2815
- // Endpoint to provide settings
2816
2418
  this.expressApp.get('/api/settings', express.json(), async (req, res) => {
2817
2419
  this.log.debug('The frontend sent /api/settings');
2818
2420
  this.matterbridgeInformation.bridgeMode = this.bridgeMode;
@@ -2833,17 +2435,13 @@ export class Matterbridge extends EventEmitter {
2833
2435
  this.matterbridgeInformation.matterbridgeSessionInformations = Array.from(this.matterbridgeSessionInformations.values());
2834
2436
  this.matterbridgeInformation.profile = this.profile;
2835
2437
  const response = { wssHost, ssl: hasParameter('ssl'), systemInformation: this.systemInformation, matterbridgeInformation: this.matterbridgeInformation };
2836
- // this.log.debug('Response:', debugStringify(response));
2837
2438
  res.json(response);
2838
2439
  });
2839
- // Endpoint to provide plugins
2840
2440
  this.expressApp.get('/api/plugins', async (req, res) => {
2841
2441
  this.log.debug('The frontend sent /api/plugins');
2842
2442
  const response = await this.getBaseRegisteredPlugins();
2843
- // this.log.debug('Response:', debugStringify(response));
2844
2443
  res.json(response);
2845
2444
  });
2846
- // Endpoint to provide devices
2847
2445
  this.expressApp.get('/api/devices', (req, res) => {
2848
2446
  this.log.debug('The frontend sent /api/devices');
2849
2447
  const devices = [];
@@ -2876,10 +2474,8 @@ export class Matterbridge extends EventEmitter {
2876
2474
  cluster: cluster,
2877
2475
  });
2878
2476
  });
2879
- // this.log.debug('Response:', debugStringify(data));
2880
2477
  res.json(devices);
2881
2478
  });
2882
- // Endpoint to provide the cluster servers of the devices
2883
2479
  this.expressApp.get('/api/devices_clusters/:selectedPluginName/:selectedDeviceEndpoint', (req, res) => {
2884
2480
  const selectedPluginName = req.params.selectedPluginName;
2885
2481
  const selectedDeviceEndpoint = parseInt(req.params.selectedDeviceEndpoint, 10);
@@ -2899,7 +2495,6 @@ export class Matterbridge extends EventEmitter {
2899
2495
  Object.entries(clusterServer.attributes).forEach(([key, value]) => {
2900
2496
  if (clusterServer.name === 'EveHistory')
2901
2497
  return;
2902
- // this.log.debug(`***--clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute:${key}(${value.id}) ${value.isFixed} ${value.isWritable} ${value.isWritable}`);
2903
2498
  let attributeValue;
2904
2499
  try {
2905
2500
  if (typeof value.getLocal() === 'object')
@@ -2910,7 +2505,6 @@ export class Matterbridge extends EventEmitter {
2910
2505
  catch (error) {
2911
2506
  attributeValue = 'Fabric-Scoped';
2912
2507
  this.log.debug(`GetLocal value ${error} in clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute: ${key}(${value.id})`);
2913
- // console.log(error);
2914
2508
  }
2915
2509
  data.push({
2916
2510
  endpoint: device.number ? device.number.toString() : '...',
@@ -2923,14 +2517,12 @@ export class Matterbridge extends EventEmitter {
2923
2517
  });
2924
2518
  });
2925
2519
  device.getChildEndpoints().forEach((childEndpoint) => {
2926
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
2927
2520
  const name = this.edge ? childEndpoint.endpoint?.id : childEndpoint.uniqueStorageKey;
2928
2521
  const clusterServers = childEndpoint.getAllClusterServers();
2929
2522
  clusterServers.forEach((clusterServer) => {
2930
2523
  Object.entries(clusterServer.attributes).forEach(([key, value]) => {
2931
2524
  if (clusterServer.name === 'EveHistory')
2932
2525
  return;
2933
- // this.log.debug(`***--clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute:${key}(${value.id}) ${value.isFixed} ${value.isWritable} ${value.isWritable}`);
2934
2526
  let attributeValue;
2935
2527
  try {
2936
2528
  if (typeof value.getLocal() === 'object')
@@ -2941,7 +2533,6 @@ export class Matterbridge extends EventEmitter {
2941
2533
  catch (error) {
2942
2534
  attributeValue = 'Unavailable';
2943
2535
  this.log.debug(`GetLocal error ${error} in clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute: ${key}(${value.id})`);
2944
- // console.log(error);
2945
2536
  }
2946
2537
  data.push({
2947
2538
  endpoint: (childEndpoint.number ? childEndpoint.number.toString() : '...') + (name ? ' (' + name + ')' : ''),
@@ -2958,7 +2549,6 @@ export class Matterbridge extends EventEmitter {
2958
2549
  });
2959
2550
  res.json(data);
2960
2551
  });
2961
- // Endpoint to view the log
2962
2552
  this.expressApp.get('/api/view-log', async (req, res) => {
2963
2553
  this.log.debug('The frontend sent /api/log');
2964
2554
  try {
@@ -2971,12 +2561,10 @@ export class Matterbridge extends EventEmitter {
2971
2561
  res.status(500).send('Error reading log file');
2972
2562
  }
2973
2563
  });
2974
- // Endpoint to download the matterbridge log
2975
2564
  this.expressApp.get('/api/download-mblog', async (req, res) => {
2976
2565
  this.log.debug('The frontend sent /api/download-mblog');
2977
2566
  try {
2978
2567
  await fs.access(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), fs.constants.F_OK);
2979
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
2980
2568
  }
2981
2569
  catch (error) {
2982
2570
  fs.appendFile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), 'Enable the log on file in the settings to enable the file logger');
@@ -2988,12 +2576,10 @@ export class Matterbridge extends EventEmitter {
2988
2576
  }
2989
2577
  });
2990
2578
  });
2991
- // Endpoint to download the matter log
2992
2579
  this.expressApp.get('/api/download-mjlog', async (req, res) => {
2993
2580
  this.log.debug('The frontend sent /api/download-mjlog');
2994
2581
  try {
2995
2582
  await fs.access(path.join(this.matterbridgeDirectory, this.matterLoggerFile), fs.constants.F_OK);
2996
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
2997
2583
  }
2998
2584
  catch (error) {
2999
2585
  fs.appendFile(path.join(this.matterbridgeDirectory, this.matterLoggerFile), 'Enable the log on file in the settings to enable the file logger');
@@ -3005,7 +2591,6 @@ export class Matterbridge extends EventEmitter {
3005
2591
  }
3006
2592
  });
3007
2593
  });
3008
- // Endpoint to download the matter storage file
3009
2594
  this.expressApp.get('/api/download-mjstorage', (req, res) => {
3010
2595
  this.log.debug('The frontend sent /api/download-mjstorage');
3011
2596
  res.download(path.join(this.matterbridgeDirectory, this.matterStorageName), 'matterbridge.json', (error) => {
@@ -3015,7 +2600,6 @@ export class Matterbridge extends EventEmitter {
3015
2600
  }
3016
2601
  });
3017
2602
  });
3018
- // Endpoint to download the matterbridge storage directory
3019
2603
  this.expressApp.get('/api/download-mbstorage', async (req, res) => {
3020
2604
  this.log.debug('The frontend sent /api/download-mbstorage');
3021
2605
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.nodeStorageName}.zip`), path.join(this.matterbridgeDirectory, this.nodeStorageName));
@@ -3026,7 +2610,6 @@ export class Matterbridge extends EventEmitter {
3026
2610
  }
3027
2611
  });
3028
2612
  });
3029
- // Endpoint to download the matterbridge plugin directory
3030
2613
  this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
3031
2614
  this.log.debug('The frontend sent /api/download-pluginstorage');
3032
2615
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridgePluginDirectory);
@@ -3037,11 +2620,9 @@ export class Matterbridge extends EventEmitter {
3037
2620
  }
3038
2621
  });
3039
2622
  });
3040
- // Endpoint to download the matterbridge plugin config files
3041
2623
  this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
3042
2624
  this.log.debug('The frontend sent /api/download-pluginconfig');
3043
2625
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridgeDirectory, '*.config.json')));
3044
- // await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridgeDirectory, 'certs', '*.*')), path.relative(process.cwd(), path.join(this.matterbridgeDirectory, '*.config.json')));
3045
2626
  res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
3046
2627
  if (error) {
3047
2628
  this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
@@ -3049,7 +2630,6 @@ export class Matterbridge extends EventEmitter {
3049
2630
  }
3050
2631
  });
3051
2632
  });
3052
- // Endpoint to download the matterbridge plugin config files
3053
2633
  this.expressApp.get('/api/download-backup', async (req, res) => {
3054
2634
  this.log.debug('The frontend sent /api/download-backup');
3055
2635
  res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
@@ -3059,7 +2639,6 @@ export class Matterbridge extends EventEmitter {
3059
2639
  }
3060
2640
  });
3061
2641
  });
3062
- // Endpoint to receive commands
3063
2642
  this.expressApp.post('/api/command/:command/:param', express.json(), async (req, res) => {
3064
2643
  const command = req.params.command;
3065
2644
  let param = req.params.param;
@@ -3069,15 +2648,13 @@ export class Matterbridge extends EventEmitter {
3069
2648
  return;
3070
2649
  }
3071
2650
  this.log.debug(`Received frontend command: ${command}:${param}`);
3072
- // Handle the command setpassword from Settings
3073
2651
  if (command === 'setpassword') {
3074
- const password = param.slice(1, -1); // Remove the first and last characters
2652
+ const password = param.slice(1, -1);
3075
2653
  this.log.debug('setpassword', param, password);
3076
2654
  await this.nodeContext?.set('password', password);
3077
2655
  res.json({ message: 'Command received' });
3078
2656
  return;
3079
2657
  }
3080
- // Handle the command setbridgemode from Settings
3081
2658
  if (command === 'setbridgemode') {
3082
2659
  this.log.debug(`setbridgemode: ${param}`);
3083
2660
  this.wssSendRestartRequired();
@@ -3085,7 +2662,6 @@ export class Matterbridge extends EventEmitter {
3085
2662
  res.json({ message: 'Command received' });
3086
2663
  return;
3087
2664
  }
3088
- // Handle the command backup from Settings
3089
2665
  if (command === 'backup') {
3090
2666
  this.log.notice(`Prepairing the backup...`);
3091
2667
  await createZip(path.join(os.tmpdir(), `matterbridge.backup.zip`), path.join(this.matterbridgeDirectory), path.join(this.matterbridgePluginDirectory));
@@ -3093,26 +2669,25 @@ export class Matterbridge extends EventEmitter {
3093
2669
  res.json({ message: 'Command received' });
3094
2670
  return;
3095
2671
  }
3096
- // Handle the command setmbloglevel from Settings
3097
2672
  if (command === 'setmbloglevel') {
3098
2673
  this.log.debug('Matterbridge log level:', param);
3099
2674
  if (param === 'Debug') {
3100
- this.log.logLevel = "debug" /* LogLevel.DEBUG */;
2675
+ this.log.logLevel = "debug";
3101
2676
  }
3102
2677
  else if (param === 'Info') {
3103
- this.log.logLevel = "info" /* LogLevel.INFO */;
2678
+ this.log.logLevel = "info";
3104
2679
  }
3105
2680
  else if (param === 'Notice') {
3106
- this.log.logLevel = "notice" /* LogLevel.NOTICE */;
2681
+ this.log.logLevel = "notice";
3107
2682
  }
3108
2683
  else if (param === 'Warn') {
3109
- this.log.logLevel = "warn" /* LogLevel.WARN */;
2684
+ this.log.logLevel = "warn";
3110
2685
  }
3111
2686
  else if (param === 'Error') {
3112
- this.log.logLevel = "error" /* LogLevel.ERROR */;
2687
+ this.log.logLevel = "error";
3113
2688
  }
3114
2689
  else if (param === 'Fatal') {
3115
- this.log.logLevel = "fatal" /* LogLevel.FATAL */;
2690
+ this.log.logLevel = "fatal";
3116
2691
  }
3117
2692
  await this.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
3118
2693
  MatterbridgeDevice.logLevel = this.log.logLevel;
@@ -3120,13 +2695,12 @@ export class Matterbridge extends EventEmitter {
3120
2695
  for (const plugin of this.plugins) {
3121
2696
  if (!plugin.platform || !plugin.platform.config)
3122
2697
  continue;
3123
- plugin.platform.log.logLevel = plugin.platform.config.debug ? "debug" /* LogLevel.DEBUG */ : this.log.logLevel;
3124
- await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug ? "debug" /* LogLevel.DEBUG */ : this.log.logLevel);
2698
+ plugin.platform.log.logLevel = plugin.platform.config.debug ? "debug" : this.log.logLevel;
2699
+ await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug ? "debug" : this.log.logLevel);
3125
2700
  }
3126
2701
  res.json({ message: 'Command received' });
3127
2702
  return;
3128
2703
  }
3129
- // Handle the command setmbloglevel from Settings
3130
2704
  if (command === 'setmjloglevel') {
3131
2705
  this.log.debug('Matter.js log level:', param);
3132
2706
  if (param === 'Debug') {
@@ -3151,34 +2725,30 @@ export class Matterbridge extends EventEmitter {
3151
2725
  res.json({ message: 'Command received' });
3152
2726
  return;
3153
2727
  }
3154
- // Handle the command setmdnsinterface from Settings
3155
2728
  if (command === 'setmdnsinterface') {
3156
- param = param.slice(1, -1); // Remove the first and last characters *mdns*
2729
+ param = param.slice(1, -1);
3157
2730
  this.matterbridgeInformation.mattermdnsinterface = param;
3158
2731
  this.log.debug('Matter.js mdns interface:', param === '' ? 'All interfaces' : param);
3159
2732
  await this.nodeContext?.set('mattermdnsinterface', param);
3160
2733
  res.json({ message: 'Command received' });
3161
2734
  return;
3162
2735
  }
3163
- // Handle the command setipv4address from Settings
3164
2736
  if (command === 'setipv4address') {
3165
- param = param.slice(1, -1); // Remove the first and last characters *ip*
2737
+ param = param.slice(1, -1);
3166
2738
  this.matterbridgeInformation.matteripv4address = param;
3167
2739
  this.log.debug('Matter.js ipv4 address:', param === '' ? 'All ipv4 addresses' : param);
3168
2740
  await this.nodeContext?.set('matteripv4address', param);
3169
2741
  res.json({ message: 'Command received' });
3170
2742
  return;
3171
2743
  }
3172
- // Handle the command setipv6address from Settings
3173
2744
  if (command === 'setipv6address') {
3174
- param = param.slice(1, -1); // Remove the first and last characters *ip*
2745
+ param = param.slice(1, -1);
3175
2746
  this.matterbridgeInformation.matteripv6address = param;
3176
2747
  this.log.debug('Matter.js ipv6 address:', param === '' ? 'All ipv6 addresses' : param);
3177
2748
  await this.nodeContext?.set('matteripv6address', param);
3178
2749
  res.json({ message: 'Command received' });
3179
2750
  return;
3180
2751
  }
3181
- // Handle the command setmatterport from Settings
3182
2752
  if (command === 'setmatterport') {
3183
2753
  const port = Math.min(Math.max(parseInt(param), 5540), 5560);
3184
2754
  this.matterbridgeInformation.matterPort = port;
@@ -3187,7 +2757,6 @@ export class Matterbridge extends EventEmitter {
3187
2757
  res.json({ message: 'Command received' });
3188
2758
  return;
3189
2759
  }
3190
- // Handle the command setmatterdiscriminator from Settings
3191
2760
  if (command === 'setmatterdiscriminator') {
3192
2761
  const discriminator = Math.min(Math.max(parseInt(param), 1000), 4095);
3193
2762
  this.matterbridgeInformation.matterDiscriminator = discriminator;
@@ -3196,7 +2765,6 @@ export class Matterbridge extends EventEmitter {
3196
2765
  res.json({ message: 'Command received' });
3197
2766
  return;
3198
2767
  }
3199
- // Handle the command setmatterpasscode from Settings
3200
2768
  if (command === 'setmatterpasscode') {
3201
2769
  const passcode = Math.min(Math.max(parseInt(param), 10000000), 90000000);
3202
2770
  this.matterbridgeInformation.matterPasscode = passcode;
@@ -3205,20 +2773,17 @@ export class Matterbridge extends EventEmitter {
3205
2773
  res.json({ message: 'Command received' });
3206
2774
  return;
3207
2775
  }
3208
- // Handle the command setmbloglevel from Settings
3209
2776
  if (command === 'setmblogfile') {
3210
2777
  this.log.debug('Matterbridge file log:', param);
3211
2778
  this.matterbridgeInformation.fileLogger = param === 'true';
3212
2779
  await this.nodeContext?.set('matterbridgeFileLog', param === 'true');
3213
- // Create the file logger for matterbridge
3214
2780
  if (param === 'true')
3215
- AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), "debug" /* LogLevel.DEBUG */, true);
2781
+ AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), "debug", true);
3216
2782
  else
3217
2783
  AnsiLogger.setGlobalLogfile(undefined);
3218
2784
  res.json({ message: 'Command received' });
3219
2785
  return;
3220
2786
  }
3221
- // Handle the command setmbloglevel from Settings
3222
2787
  if (command === 'setmjlogfile') {
3223
2788
  this.log.debug('Matter file log:', param);
3224
2789
  this.matterbridgeInformation.matterFileLogger = param === 'true';
@@ -3245,43 +2810,36 @@ export class Matterbridge extends EventEmitter {
3245
2810
  res.json({ message: 'Command received' });
3246
2811
  return;
3247
2812
  }
3248
- // Handle the command unregister from Settings
3249
2813
  if (command === 'unregister') {
3250
2814
  await this.unregisterAndShutdownProcess();
3251
2815
  res.json({ message: 'Command received' });
3252
2816
  return;
3253
2817
  }
3254
- // Handle the command reset from Settings
3255
2818
  if (command === 'reset') {
3256
2819
  await this.shutdownProcessAndReset();
3257
2820
  res.json({ message: 'Command received' });
3258
2821
  return;
3259
2822
  }
3260
- // Handle the command factoryreset from Settings
3261
2823
  if (command === 'factoryreset') {
3262
2824
  await this.shutdownProcessAndFactoryReset();
3263
2825
  res.json({ message: 'Command received' });
3264
2826
  return;
3265
2827
  }
3266
- // Handle the command shutdown from Header
3267
2828
  if (command === 'shutdown') {
3268
2829
  await this.shutdownProcess();
3269
2830
  res.json({ message: 'Command received' });
3270
2831
  return;
3271
2832
  }
3272
- // Handle the command restart from Header
3273
2833
  if (command === 'restart') {
3274
2834
  await this.restartProcess();
3275
2835
  res.json({ message: 'Command received' });
3276
2836
  return;
3277
2837
  }
3278
- // Handle the command update from Header
3279
2838
  if (command === 'update') {
3280
2839
  this.log.info('Updating matterbridge...');
3281
2840
  try {
3282
2841
  await this.spawnCommand('npm', ['install', '-g', 'matterbridge', '--omit=dev', '--verbose']);
3283
2842
  this.log.info('Matterbridge has been updated. Full restart required.');
3284
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
3285
2843
  }
3286
2844
  catch (error) {
3287
2845
  this.log.error('Error updating matterbridge');
@@ -3291,11 +2849,9 @@ export class Matterbridge extends EventEmitter {
3291
2849
  res.json({ message: 'Command received' });
3292
2850
  return;
3293
2851
  }
3294
- // Handle the command saveconfig from Home
3295
2852
  if (command === 'saveconfig') {
3296
2853
  param = param.replace(/\*/g, '\\');
3297
2854
  this.log.info(`Saving config for plugin ${plg}${param}${nf}...`);
3298
- // console.log('Req.body:', JSON.stringify(req.body, null, 2));
3299
2855
  if (!this.plugins.has(param)) {
3300
2856
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
3301
2857
  }
@@ -3309,39 +2865,33 @@ export class Matterbridge extends EventEmitter {
3309
2865
  res.json({ message: 'Command received' });
3310
2866
  return;
3311
2867
  }
3312
- // Handle the command installplugin from Home
3313
2868
  if (command === 'installplugin') {
3314
2869
  param = param.replace(/\*/g, '\\');
3315
2870
  this.log.info(`Installing plugin ${plg}${param}${nf}...`);
3316
2871
  try {
3317
2872
  await this.spawnCommand('npm', ['install', '-g', param, '--omit=dev', '--verbose']);
3318
2873
  this.log.info(`Plugin ${plg}${param}${nf} installed. Full restart required.`);
3319
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
3320
2874
  }
3321
2875
  catch (error) {
3322
2876
  this.log.error(`Error installing plugin ${plg}${param}${er}`);
3323
2877
  }
3324
2878
  this.wssSendRestartRequired();
3325
2879
  param = param.split('@')[0];
3326
- // Also add the plugin to matterbridge so no return!
3327
2880
  if (param === 'matterbridge') {
3328
- // If we used the command installplugin to install a dev or a specific version of matterbridge we don't want to add it to matterbridge
3329
2881
  res.json({ message: 'Command received' });
3330
2882
  return;
3331
2883
  }
3332
2884
  }
3333
- // Handle the command addplugin from Home
3334
2885
  if (command === 'addplugin' || command === 'installplugin') {
3335
2886
  param = param.replace(/\*/g, '\\');
3336
2887
  const plugin = await this.plugins.add(param);
3337
2888
  if (plugin) {
3338
- this.plugins.load(plugin, true, 'The plugin has been added', true); // No await do it in the background
2889
+ this.plugins.load(plugin, true, 'The plugin has been added', true);
3339
2890
  }
3340
2891
  res.json({ message: 'Command received' });
3341
2892
  this.wssSendRefreshRequired();
3342
2893
  return;
3343
2894
  }
3344
- // Handle the command removeplugin from Home
3345
2895
  if (command === 'removeplugin') {
3346
2896
  if (!this.plugins.has(param)) {
3347
2897
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
@@ -3355,7 +2905,6 @@ export class Matterbridge extends EventEmitter {
3355
2905
  this.wssSendRefreshRequired();
3356
2906
  return;
3357
2907
  }
3358
- // Handle the command enableplugin from Home
3359
2908
  if (command === 'enableplugin') {
3360
2909
  if (!this.plugins.has(param)) {
3361
2910
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
@@ -3373,14 +2922,13 @@ export class Matterbridge extends EventEmitter {
3373
2922
  plugin.registeredDevices = undefined;
3374
2923
  plugin.addedDevices = undefined;
3375
2924
  await this.plugins.enable(param);
3376
- this.plugins.load(plugin, true, 'The plugin has been enabled', true); // No await do it in the background
2925
+ this.plugins.load(plugin, true, 'The plugin has been enabled', true);
3377
2926
  }
3378
2927
  }
3379
2928
  res.json({ message: 'Command received' });
3380
2929
  this.wssSendRefreshRequired();
3381
2930
  return;
3382
2931
  }
3383
- // Handle the command disableplugin from Home
3384
2932
  if (command === 'disableplugin') {
3385
2933
  if (!this.plugins.has(param)) {
3386
2934
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
@@ -3397,7 +2945,6 @@ export class Matterbridge extends EventEmitter {
3397
2945
  return;
3398
2946
  }
3399
2947
  });
3400
- // Fallback for routing (must be the last route)
3401
2948
  this.expressApp.get('*', (req, res) => {
3402
2949
  this.log.debug('The frontend sent:', req.url);
3403
2950
  this.log.debug('Response send file:', path.join(this.rootDirectory, 'frontend/build/index.html'));
@@ -3405,11 +2952,6 @@ export class Matterbridge extends EventEmitter {
3405
2952
  });
3406
2953
  this.log.debug(`Frontend initialized on port ${YELLOW}${port}${db} static ${UNDERLINE}${path.join(this.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
3407
2954
  }
3408
- /**
3409
- * Retrieves the cluster text description from a given device.
3410
- * @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
3411
- * @returns {string} The attributes description of the cluster servers in the device.
3412
- */
3413
2955
  getClusterTextFromDevice(device) {
3414
2956
  const stringifyUserLabel = (endpoint) => {
3415
2957
  const labelList = endpoint.getClusterServer(UserLabelCluster)?.attributes.labelList.getLocal();
@@ -3432,11 +2974,9 @@ export class Matterbridge extends EventEmitter {
3432
2974
  return '';
3433
2975
  };
3434
2976
  let attributes = '';
3435
- // this.log.debug(`***getClusterTextFromDevice: ${device.deviceName} (${device.name})`);
3436
2977
  const clusterServers = device.getAllClusterServers();
3437
2978
  clusterServers.forEach((clusterServer) => {
3438
2979
  try {
3439
- // this.log.debug(`**--clusterServer: ${clusterServer.id} (${clusterServer.name})`);
3440
2980
  if (clusterServer.name === 'OnOff')
3441
2981
  attributes += `OnOff: ${clusterServer.attributes.onOff.getLocal()} `;
3442
2982
  if (clusterServer.name === 'Switch')
@@ -3487,30 +3027,18 @@ export class Matterbridge extends EventEmitter {
3487
3027
  attributes += `${stringifyFixedLabel(device)} `;
3488
3028
  if (clusterServer.name === 'UserLabel')
3489
3029
  attributes += `${stringifyUserLabel(device)} `;
3490
- // this.log.debug(`*--clusterServer: ${clusterServer.id} (${clusterServer.name})`);
3491
3030
  }
3492
3031
  catch (error) {
3493
3032
  this.log.error(`getClusterTextFromDevice with ${clusterServer.name} error: ${error}`);
3494
3033
  }
3495
3034
  });
3496
- // this.log.debug(`*getClusterTextFromDevice: ${device.deviceName} (${device.name})`);
3497
3035
  return attributes;
3498
3036
  }
3499
- /**
3500
- * Initializes the Matterbridge instance as extension for zigbee2mqtt.
3501
- * @deprecated This method is deprecated and will be removed in a future version.
3502
- *
3503
- * @returns A Promise that resolves when the initialization is complete.
3504
- */
3505
3037
  async startExtension(dataPath, extensionVersion, port = 5540) {
3506
- // Set the bridge mode
3507
3038
  this.bridgeMode = 'bridge';
3508
- // Set the first port to use
3509
3039
  this.port = port;
3510
- // Set Matterbridge logger
3511
- this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: "info" /* LogLevel.INFO */ });
3040
+ this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: "info" });
3512
3041
  this.log.debug('Matterbridge extension is starting...');
3513
- // Initialize NodeStorage
3514
3042
  this.matterbridgeDirectory = dataPath;
3515
3043
  this.log.debug('Creating node storage manager dir: ' + path.join(this.matterbridgeDirectory, 'node_storage'));
3516
3044
  this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, 'node_storage'), logging: false });
@@ -3529,13 +3057,10 @@ export class Matterbridge extends EventEmitter {
3529
3057
  };
3530
3058
  this.plugins.set(plugin);
3531
3059
  this.plugins.saveToStorage();
3532
- // Log system info and create .matterbridge directory
3533
3060
  await this.logNodeAndSystemInfo();
3534
3061
  this.matterbridgeDirectory = dataPath;
3535
- // Set matter.js logger level and format
3536
3062
  Logger.defaultLogLevel = MatterLogLevel.INFO;
3537
3063
  Logger.format = MatterLogFormat.ANSI;
3538
- // Start the storage and create matterbridgeContext
3539
3064
  await this.startMatterStorage('json', path.join(this.matterbridgeDirectory, this.matterStorageName));
3540
3065
  if (!this.storageManager)
3541
3066
  return false;
@@ -3545,7 +3070,7 @@ export class Matterbridge extends EventEmitter {
3545
3070
  await this.matterbridgeContext.set('softwareVersion', 1);
3546
3071
  await this.matterbridgeContext.set('softwareVersionString', this.matterbridgeVersion);
3547
3072
  await this.matterbridgeContext.set('hardwareVersion', 1);
3548
- await this.matterbridgeContext.set('hardwareVersionString', extensionVersion); // Update with the extension version
3073
+ await this.matterbridgeContext.set('hardwareVersionString', extensionVersion);
3549
3074
  this.matterServer = this.createMatterServer(this.storageManager);
3550
3075
  this.log.debug(`Creating commissioning server for ${plg}Matterbridge${db}`);
3551
3076
  this.commissioningServer = await this.createCommisioningServer(this.matterbridgeContext, 'Matterbridge');
@@ -3558,7 +3083,6 @@ export class Matterbridge extends EventEmitter {
3558
3083
  await this.startMatterServer();
3559
3084
  this.log.info('Matter server started');
3560
3085
  await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
3561
- // Set reachability to true and trigger event after 60 seconds
3562
3086
  setTimeout(() => {
3563
3087
  this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
3564
3088
  if (this.commissioningServer)
@@ -3568,31 +3092,14 @@ export class Matterbridge extends EventEmitter {
3568
3092
  }, 60 * 1000);
3569
3093
  return this.commissioningServer.isCommissioned();
3570
3094
  }
3571
- /**
3572
- * Close the Matterbridge instance as extension for zigbee2mqtt.
3573
- * @deprecated This method is deprecated and will be removed in a future version.
3574
- *
3575
- * @returns A Promise that resolves when the initialization is complete.
3576
- */
3577
3095
  async stopExtension() {
3578
- // Closing matter
3579
3096
  await this.stopMatterServer();
3580
- // Clearing the session manager
3581
- // this.matterbridgeContext?.createContext('SessionManager').clear();
3582
- // Closing storage
3583
3097
  await this.stopMatterStorage();
3584
3098
  this.log.info('Matter server stopped');
3585
3099
  }
3586
- /**
3587
- * Checks if the extension is commissioned.
3588
- * @deprecated This method is deprecated and will be removed in a future version.
3589
- *
3590
- * @returns {boolean} Returns true if the extension is commissioned, false otherwise.
3591
- */
3592
3100
  isExtensionCommissioned() {
3593
3101
  if (!this.commissioningServer)
3594
3102
  return false;
3595
3103
  return this.commissioningServer.isCommissioned();
3596
3104
  }
3597
3105
  }
3598
- //# sourceMappingURL=matterbridge.js.map