matterbridge 3.5.0-dev-20260117-6067fc2 → 3.5.0-dev-20260119-f9ea00e

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -32,7 +32,10 @@ Advantages:
32
32
 
33
33
  ### Breaking Changes
34
34
 
35
- - [matter]: This release brings the upgrade to matter 1.4.2 and matter.js 0.16.x.
35
+ - [matter]: This release brings the upgrade to matter 1.4.2 and matter.js 0.16.x. It may cause the controllers to see a new device.
36
+ - [childbridge]: Fixed nodeLabel from deviceName in childbridge mode. It may cause the controllers to see a new device.
37
+ - [modeserver]: Fixed nodeLabel from deviceName for server node devices. It may cause the controllers to see a new device.
38
+ - [matterbridge]: On restart (typically on the Home Assistant addon), when the plugin is not found and is reinstalled, it is reinstalled from the latest dev if it was on the dev branch.
36
39
 
37
40
  ### Changed device types in Matter 1.4.2
38
41
 
@@ -59,9 +62,9 @@ Advantages:
59
62
  - [Thermostat]: Revision 9 - Removed AlarmMask attribute and AlarmCodeBitmap Type.
60
63
  - [Identify]: Revision 6 - Added Q quality for IdentifyTime attribute.
61
64
  - [WindowCovering]: Revision 6 - Marked AbsolutePosition feature and associated elements provisional.
62
- - [RvcCleanMode]: Revision 4 - Add VacuumThenMop cleaning mode.
63
- - [RvcOperationalState]: Revision 4 - Add several operational states and errors.
64
- - [ServiceArea]: Revision 2 - Rename InitialTimeEstimate to EstimatedTime.
65
+ - [RvcCleanMode]: Revision 4 - Added VacuumThenMop cleaning mode.
66
+ - [RvcOperationalState]: Revision 4 - Added several operational states and errors.
67
+ - [ServiceArea]: Revision 2 - Renamed InitialTimeEstimate to EstimatedTime.
65
68
 
66
69
  ### Added
67
70
 
@@ -69,18 +72,23 @@ Advantages:
69
72
  - [frontend]: Enhanced logging for HTTP/HTTPS server binding in Frontend class.
70
73
  - [matterbridge]: Excluded specific network interfaces when looking for the first external interface.
71
74
  - [thermostat]: Conformance to new matter.js internal attributes externalMeasuredIndoorTemperature and externallyMeasuredOccupancy (not in Matter 1.4.2 specs).
75
+ - [scenes]: Added SceneManagement cluster when required.
76
+ - [server]: Added start and stop devices server mode in plugin restart.
77
+ - [server]: Added start and stop devices server mode in plugin enable and disable.
72
78
  - [matter.js]: Bump to matter.j v. 0.16.0.
73
79
  - [matter.js]: Bump to matter.j v. 0.16.1.
74
80
  - [matter.js]: Bump to matter.j v. 0.16.2.
75
81
  - [matter.js]: Bump to matter.j v. 0.16.3.
76
82
  - [matter.js]: Bump to matter.j v. 0.16.4.
77
83
  - [matter.js]: Bump to matter.j v. 0.16.5.
84
+ - [frontend]: Added the --bind parameter to bind the frontend to a specific address. Defaults to all addresses.
78
85
 
79
86
  ### Changed
80
87
 
81
88
  - [package]: Updated dependencies.
82
89
  - [eslint]: Moved cache in .cache/.eslintcache.
83
90
  - [prettier]: Moved cache in .cache/.prettiercache.
91
+ - [DevContainer]: Refactor DevContainer to use bridge mode and matterbridge docker network.
84
92
 
85
93
  ### Fixed
86
94
 
package/README.md CHANGED
@@ -165,13 +165,13 @@ From the frontend you can do all operations in an easy way.
165
165
 
166
166
  Home page
167
167
 
168
- ![Home page](./screenshot/Screenshot%20home.jpg)
168
+ ![Home page](./screenshots/Screenshot%20home.jpg)
169
169
 
170
- [Devices page](./screenshot/Screenshot%20devices.jpg)
170
+ [Devices page](./screenshots/Screenshot%20devices.jpg)
171
171
 
172
- [Logs page](./screenshot/Screenshot%20logs.jpg)
172
+ [Logs page](./screenshots/Screenshot%20logs.jpg)
173
173
 
174
- [Config editor](./screenshot/Screenshot%20config%20editor.jpg)
174
+ [Config editor](./screenshots/Screenshot%20config%20editor.jpg)
175
175
 
176
176
  ## Advanced configurations
177
177
 
@@ -216,7 +216,7 @@ The other Home Assistant Community Add-ons and plugins are not verified to work
216
216
  ### Shelly
217
217
 
218
218
  <a href="https://github.com/Luligu/matterbridge-shelly">
219
- <img src="./screenshot/Shelly.svg" alt="Shelly plugin logo" width="100" />
219
+ <img src="./screenshots/Shelly.svg" alt="Shelly plugin logo" width="100" />
220
220
  </a>
221
221
 
222
222
  Matterbridge shelly plugin allows you to expose all Shelly Gen 1, Gen 2, Gen 3 and Gen 4 and BLU devices to Matter.
@@ -245,7 +245,7 @@ Features:
245
245
  ### Zigbee2MQTT
246
246
 
247
247
  <a href="https://github.com/Luligu/matterbridge-zigbee2mqtt">
248
- <img src="./screenshot/Zigbee2MQTT.svg" alt="Zigbee2MQTT plugin logo" width="100" />
248
+ <img src="./screenshots/Zigbee2MQTT.svg" alt="Zigbee2MQTT plugin logo" width="100" />
249
249
  </a>
250
250
 
251
251
  Matterbridge zigbee2mqtt is a matterbridge production-level plugin that expose all zigbee2mqtt devices and groups to Matter.
@@ -255,7 +255,7 @@ No hub or dedicated hardware needed.
255
255
  ### Somfy tahoma
256
256
 
257
257
  <a href="https://github.com/Luligu/matterbridge-somfy-tahoma">
258
- <img src="./screenshot/Somfy.svg" alt="Somfy plugin logo" width="100" />
258
+ <img src="./screenshots/Somfy.svg" alt="Somfy plugin logo" width="100" />
259
259
  </a>
260
260
 
261
261
  Matterbridge Somfy Tahoma is a matterbridge production-level plugin that expose the Somfy Tahoma screen devices to Matter.
@@ -263,7 +263,7 @@ Matterbridge Somfy Tahoma is a matterbridge production-level plugin that expose
263
263
  ### Home Assistant
264
264
 
265
265
  <a href="https://github.com/Luligu/matterbridge-hass">
266
- <img src="./screenshot/HomeAssistant.svg" alt="Hass logo" width="100" />
266
+ <img src="./screenshots/HomeAssistant.svg" alt="Hass logo" width="100" />
267
267
  </a>
268
268
 
269
269
  Matterbridge Home Assistant plugin allows you to expose the Home Assistant devices and entities to Matter.
@@ -445,7 +445,7 @@ Place your own certificates in the `.matterbridge/cert` directory:
445
445
  - `key.pem`
446
446
  - `ca.pem` (optional)
447
447
 
448
- ![image](./screenshot/Screenshot%20Certificates.png)
448
+ ![image](./screenshots/Screenshot%20Certificates.png)
449
449
 
450
450
  Matterbridge looks first for .p12 certificate and if it is not found it looks for cert.pem and key.pem.
451
451
 
@@ -480,7 +480,7 @@ matterbridge --ssl --mtls --frontend 443
480
480
 
481
481
  If the certificate are correctly configured, you will be able to connect with https to the frontend.
482
482
 
483
- ![image](./screenshot/Screenshot%20Browser%20Secured.png)
483
+ ![image](./screenshots/Screenshot%20Browser%20Secured.png)
484
484
 
485
485
  ## How to send the debug log files
486
486
 
@@ -488,9 +488,9 @@ If the certificate are correctly configured, you will be able to connect with ht
488
488
 
489
489
  In the frontend, go to settings and enable debug mode as shown below:
490
490
 
491
- ![Debug Matterbridge Settings](./screenshot/Screenshot%20Matterbridge%20Logger%20Debug.png)
491
+ ![Debug Matterbridge Settings](./screenshots/Screenshot%20Matterbridge%20Logger%20Debug.png)
492
492
 
493
- ![Debug Matter Settings](./screenshot/Screenshot%20Matter%20Logger%20Debug.png)
493
+ ![Debug Matter Settings](./screenshots/Screenshot%20Matter%20Logger%20Debug.png)
494
494
 
495
495
  ### Restart
496
496
 
@@ -498,7 +498,7 @@ Wait a few minutes to allow the logs to to accumulate.
498
498
 
499
499
  Then, from the dots menu in the frontend, download the `matterbridge.log` and `matter.log` files.
500
500
 
501
- ![image](./screenshot/Screenshot%20Debug%20Download%20Logs.png)
501
+ ![image](./screenshots/Screenshot%20Debug%20Download%20Logs.png)
502
502
 
503
503
  Don't forget to unselect the debug mode when is no more needed. The network traffic and cpu usage is very high in debug mode.
504
504
 
@@ -510,7 +510,7 @@ There are two ways to pair a second controller:
510
510
 
511
511
  - from Matterbridge frontend click `Turn on pairing mode` in the `Paired fabrics` panel and proceed like for the first controller.
512
512
 
513
- ![alt text](./screenshot/Turn%20on%20pairing%20mode.png)
513
+ ![alt text](./screenshots/Turn%20on%20pairing%20mode.png)
514
514
 
515
515
  Be patient cause the procedure can fail sometimes.
516
516
 
package/dist/cli.js CHANGED
@@ -145,6 +145,7 @@ function help() {
145
145
  --ipv4address [address]: set the ipv4 interface address to use for the matter server listener (default all addresses)
146
146
  --ipv6address [address]: set the ipv6 interface address to use for the matter server listener (default all addresses)
147
147
  --frontend [port]: start the frontend on the given port (default 8283)
148
+ --bind [address]: bind the frontend to the given address (default all addresses)
148
149
  --logger: set the matterbridge logger level: debug | info | notice | warn | error | fatal (default info)
149
150
  --filelogger: enable the matterbridge file logger (matterbridge.log)
150
151
  --matterlogger: set the matter.js logger level: debug | info | notice | warn | error | fatal (default info)
package/dist/frontend.js CHANGED
@@ -13,7 +13,7 @@ import { PowerSource } from '@matter/types/clusters/power-source';
13
13
  import { MATTER_LOGGER_FILE, MATTER_STORAGE_NAME, MATTERBRIDGE_DIAGNOSTIC_FILE, MATTERBRIDGE_HISTORY_FILE, MATTERBRIDGE_LOGGER_FILE, NODE_STORAGE_DIR, plg } from './matterbridgeTypes.js';
14
14
  import { isValidArray, isValidNumber, isValidObject, isValidString, isValidBoolean } from './utils/isValid.js';
15
15
  import { createZip } from './utils/createZip.js';
16
- import { hasParameter } from './utils/commandLine.js';
16
+ import { hasParameter, getParameter } from './utils/commandLine.js';
17
17
  import { withTimeout, wait } from './utils/wait.js';
18
18
  import { inspectError } from './utils/error.js';
19
19
  import { formatBytes, formatUptime, formatPercent } from './utils/format.js';
@@ -205,14 +205,14 @@ export class Frontend extends EventEmitter {
205
205
  });
206
206
  }
207
207
  else {
208
- this.httpServer.listen(this.port, () => {
208
+ this.httpServer.listen(this.port, getParameter('bind'), () => {
209
209
  const addr = this.httpServer?.address();
210
210
  if (addr && typeof addr !== 'string') {
211
211
  this.log.info(`The frontend http server is bound to ${addr.family} ${addr.address}:${addr.port}`);
212
212
  }
213
- if (this.matterbridge.systemInformation.ipv4Address !== '')
213
+ if (this.matterbridge.systemInformation.ipv4Address !== '' && !getParameter('bind'))
214
214
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://${this.matterbridge.systemInformation.ipv4Address}:${this.port}${UNDERLINEOFF}${rs}`);
215
- if (this.matterbridge.systemInformation.ipv6Address !== '')
215
+ if (this.matterbridge.systemInformation.ipv6Address !== '' && !getParameter('bind'))
216
216
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
217
217
  this.listening = true;
218
218
  this.emit('server_listening', 'http', this.port);
@@ -341,14 +341,14 @@ export class Frontend extends EventEmitter {
341
341
  });
342
342
  }
343
343
  else {
344
- this.httpsServer.listen(this.port, () => {
344
+ this.httpsServer.listen(this.port, getParameter('bind'), () => {
345
345
  const addr = this.httpsServer?.address();
346
346
  if (addr && typeof addr !== 'string') {
347
347
  this.log.info(`The frontend https server is bound to ${addr.family} ${addr.address}:${addr.port}`);
348
348
  }
349
- if (this.matterbridge.systemInformation.ipv4Address !== '')
349
+ if (this.matterbridge.systemInformation.ipv4Address !== '' && !getParameter('bind'))
350
350
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://${this.matterbridge.systemInformation.ipv4Address}:${this.port}${UNDERLINEOFF}${rs}`);
351
- if (this.matterbridge.systemInformation.ipv6Address !== '')
351
+ if (this.matterbridge.systemInformation.ipv6Address !== '' && !getParameter('bind'))
352
352
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
353
353
  this.listening = true;
354
354
  this.emit('server_listening', 'https', this.port);
@@ -1286,28 +1286,27 @@ export class Frontend extends EventEmitter {
1286
1286
  return;
1287
1287
  }
1288
1288
  const plugin = this.matterbridge.plugins.get(data.params.pluginName);
1289
- if (plugin && !plugin.enabled) {
1290
- plugin.locked = undefined;
1291
- plugin.error = undefined;
1292
- plugin.loaded = undefined;
1293
- plugin.started = undefined;
1294
- plugin.configured = undefined;
1295
- plugin.platform = undefined;
1296
- plugin.registeredDevices = undefined;
1297
- plugin.matter = undefined;
1298
- await this.matterbridge.plugins.enable(data.params.pluginName);
1299
- this.wssSendSnackbarMessage(`Enabled plugin ${data.params.pluginName}`, 5, 'success');
1300
- this.matterbridge.plugins
1301
- .load(plugin, true, 'The plugin has been enabled', true)
1302
- .then(() => {
1303
- this.wssSendRefreshRequired('plugins');
1304
- this.wssSendRefreshRequired('devices');
1305
- this.wssSendSnackbarMessage(`Started plugin ${localData.params.pluginName}`, 5, 'success');
1306
- return;
1307
- })
1308
- .catch((_error) => { });
1309
- }
1310
- sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1289
+ plugin.locked = undefined;
1290
+ plugin.error = undefined;
1291
+ plugin.loaded = undefined;
1292
+ plugin.started = undefined;
1293
+ plugin.configured = undefined;
1294
+ plugin.platform = undefined;
1295
+ plugin.registeredDevices = undefined;
1296
+ plugin.matter = undefined;
1297
+ await this.matterbridge.plugins.enable(data.params.pluginName);
1298
+ this.wssSendSnackbarMessage(`Enabled plugin ${data.params.pluginName}`, 5, 'success');
1299
+ setImmediate(async () => {
1300
+ await this.matterbridge.plugins.load(plugin, true, 'The plugin has been enabled', true);
1301
+ if (plugin.serverNode)
1302
+ await this.matterbridge.startServerNode(plugin.serverNode);
1303
+ for (const device of this.matterbridge.devices.array().filter((d) => d.plugin === plugin.name && d.serverNode))
1304
+ await this.matterbridge.startServerNode(device.serverNode);
1305
+ this.wssSendSnackbarMessage(`Started plugin ${localData.params.pluginName}`, 5, 'success');
1306
+ this.wssSendRefreshRequired('plugins');
1307
+ this.wssSendRefreshRequired('devices');
1308
+ sendResponse({ id: localData.id, method: localData.method, src: 'Matterbridge', dst: localData.src, success: true });
1309
+ });
1311
1310
  }
1312
1311
  else if (data.method === '/api/disableplugin') {
1313
1312
  if (!isValidString(data.params.pluginName, 10) || !this.matterbridge.plugins.has(data.params.pluginName)) {
@@ -1315,6 +1314,13 @@ export class Frontend extends EventEmitter {
1315
1314
  return;
1316
1315
  }
1317
1316
  const plugin = this.matterbridge.plugins.get(data.params.pluginName);
1317
+ for (const device of this.matterbridge.devices.array().filter((d) => d.plugin === plugin.name && d.serverNode)) {
1318
+ await this.matterbridge.stopServerNode(device.serverNode);
1319
+ device.serverNode = undefined;
1320
+ }
1321
+ if (plugin.serverNode)
1322
+ await this.matterbridge.stopServerNode(plugin.serverNode);
1323
+ plugin.serverNode = undefined;
1318
1324
  await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true);
1319
1325
  await this.matterbridge.plugins.disable(data.params.pluginName);
1320
1326
  this.wssSendSnackbarMessage(`Disabled plugin ${data.params.pluginName}`, 5, 'success');
@@ -1327,17 +1333,19 @@ export class Frontend extends EventEmitter {
1327
1333
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/restartplugin' });
1328
1334
  return;
1329
1335
  }
1336
+ this.wssSendSnackbarMessage(`Restarting plugin ${data.params.pluginName}`, 5, 'info');
1330
1337
  const plugin = this.matterbridge.plugins.get(data.params.pluginName);
1331
1338
  await this.matterbridge.plugins.shutdown(plugin, 'The plugin is restarting.', false, true);
1332
1339
  if (plugin.serverNode) {
1333
1340
  await this.matterbridge.stopServerNode(plugin.serverNode);
1334
1341
  plugin.serverNode = undefined;
1335
1342
  }
1336
- for (const device of this.matterbridge.devices) {
1337
- if (device.plugin === plugin.name) {
1338
- this.log.debug(`Removing device ${device.deviceName} from plugin ${plugin.name}`);
1339
- this.matterbridge.devices.remove(device);
1340
- }
1343
+ for (const device of this.matterbridge.devices.array().filter((d) => d.plugin === plugin.name)) {
1344
+ if (device.serverNode)
1345
+ await this.matterbridge.stopServerNode(device.serverNode);
1346
+ device.serverNode = undefined;
1347
+ this.log.debug(`Removing device ${device.deviceName} from plugin ${plugin.name}`);
1348
+ this.matterbridge.devices.remove(device);
1341
1349
  }
1342
1350
  if (plugin.type === 'DynamicPlatform' && !plugin.locked)
1343
1351
  await this.matterbridge.createDynamicPlugin(plugin);
@@ -1348,11 +1356,12 @@ export class Frontend extends EventEmitter {
1348
1356
  if (plugin.restartRequired)
1349
1357
  needRestart++;
1350
1358
  }
1351
- if (needRestart === 0) {
1359
+ if (needRestart === 0)
1352
1360
  this.wssSendRestartNotRequired(true);
1353
- }
1354
1361
  if (plugin.serverNode)
1355
1362
  await this.matterbridge.startServerNode(plugin.serverNode);
1363
+ for (const device of this.matterbridge.devices.array().filter((d) => d.plugin === plugin.name && d.serverNode))
1364
+ await this.matterbridge.startServerNode(device.serverNode);
1356
1365
  this.wssSendSnackbarMessage(`Restarted plugin ${data.params.pluginName}`, 5, 'success');
1357
1366
  this.wssSendRefreshRequired('plugins');
1358
1367
  this.wssSendRefreshRequired('devices');
@@ -685,7 +685,9 @@ export class MatterNode extends EventEmitter {
685
685
  const plugin = this.pluginManager.get(pluginName);
686
686
  if (!plugin)
687
687
  throw new Error(`Error removing bridged endpoint ${plg}${pluginName}${er}:${dev}${device.deviceName}${er} (${zb}${device.name}${er}): plugin not found`);
688
- if (this.matterbridge.bridgeMode === 'bridge') {
688
+ if (device.serverNode) {
689
+ }
690
+ else if (this.matterbridge.bridgeMode === 'bridge') {
689
691
  if (!this.aggregatorNode)
690
692
  throw new Error(`Error removing bridged endpoint ${plg}${pluginName}${er}:${dev}${device.deviceName}${er} (${zb}${device.name}${er}): aggregator node not found`);
691
693
  await device.delete();
@@ -2,7 +2,7 @@ if (process.argv.includes('--loader') || process.argv.includes('-loader'))
2
2
  console.log('\u001B[32mMatterbridge loaded.\u001B[40;0m');
3
3
  import os from 'node:os';
4
4
  import path from 'node:path';
5
- import fs from 'node:fs';
5
+ import fs, { unlinkSync } from 'node:fs';
6
6
  import EventEmitter from 'node:events';
7
7
  import { inspect } from 'node:util';
8
8
  import { AnsiLogger, UNDERLINE, UNDERLINEOFF, db, debugStringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN, nt, BLUE, or } from 'node-ansi-logger';
@@ -475,7 +475,7 @@ export class Matterbridge extends EventEmitter {
475
475
  if (!fs.existsSync(plugin.path) && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
476
476
  this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm...`);
477
477
  const { spawnCommand } = await import('./utils/spawn.js');
478
- if (await spawnCommand('npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose'], 'install', plugin.name)) {
478
+ if (await spawnCommand('npm', ['install', '-g', `${plugin.name}${plugin.version.includes('-dev-') ? '@dev' : ''}`, '--omit=dev', '--verbose'], 'install', plugin.name)) {
479
479
  this.log.info(`Plugin ${plg}${plugin.name}${nf} reinstalled.`);
480
480
  plugin.error = false;
481
481
  }
@@ -525,11 +525,11 @@ export class Matterbridge extends EventEmitter {
525
525
  let index = 0;
526
526
  for (const plugin of this.plugins) {
527
527
  if (index !== this.plugins.length - 1) {
528
- this.log.info(`├─┬─ plugin ${plg}${plugin.name}${nf}: "${plg}${BRIGHT}${plugin.description}${RESET}${nf}" type: ${typ}${plugin.type}${nf} ${plugin.enabled ? GREEN : RED}enabled${nf}`);
528
+ this.log.info(`├─┬─ plugin ${plg}${plugin.name}${nf}: "${plg}${BRIGHT}${plugin.description}${RESET}${nf}" type: ${typ}${plugin.type}${nf} version: ${plg}${plugin.version}${nf} ${plugin.enabled ? GREEN : RED}enabled${nf}`);
529
529
  this.log.info(`│ └─ entry ${UNDERLINE}${db}${plugin.path}${UNDERLINEOFF}${db}`);
530
530
  }
531
531
  else {
532
- this.log.info(`└─┬─ plugin ${plg}${plugin.name}${nf}: "${plg}${BRIGHT}${plugin.description}${RESET}${nf}" type: ${typ}${plugin.type}${nf} ${plugin.enabled ? GREEN : RED}disabled${nf}`);
532
+ this.log.info(`└─┬─ plugin ${plg}${plugin.name}${nf}: "${plg}${BRIGHT}${plugin.description}${RESET}${nf}" type: ${typ}${plugin.type}${nf} version: ${plg}${plugin.version}${nf} ${plugin.enabled ? GREEN : RED}disabled${nf}`);
533
533
  this.log.info(` └─ entry ${UNDERLINE}${db}${plugin.path}${UNDERLINEOFF}${db}`);
534
534
  }
535
535
  index++;
@@ -1053,6 +1053,30 @@ export class Matterbridge extends EventEmitter {
1053
1053
  this.log.info('Matter storage reset done!');
1054
1054
  }
1055
1055
  await this.stopMatterStorage();
1056
+ function unlinkSafe(path, log) {
1057
+ try {
1058
+ log.debug(`Removing ${path}...`);
1059
+ unlinkSync(path);
1060
+ log.debug(`Removed ${path}`);
1061
+ }
1062
+ catch {
1063
+ }
1064
+ }
1065
+ this.log.debug(`Cleaning matter storage context for ${GREEN}Matterbridge${db}...`);
1066
+ unlinkSafe(path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME, 'Matterbridge', 'sessions.resumptionRecords'), this.log);
1067
+ unlinkSafe(path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME, 'Matterbridge', 'root.subscriptions.subscriptions'), this.log);
1068
+ for (const plugin of this.plugins.array()) {
1069
+ this.log.debug(`Cleaning matter storage context for plugin ${plg}${plugin.name}${db}...`);
1070
+ unlinkSafe(path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME, plugin.name, 'sessions.resumptionRecords'), this.log);
1071
+ unlinkSafe(path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME, plugin.name, 'root.subscriptions.subscriptions'), this.log);
1072
+ }
1073
+ for (const device of this.devices.array().filter((d) => d.mode === 'server')) {
1074
+ if (!device.deviceName)
1075
+ continue;
1076
+ this.log.debug(`Cleaning matter storage context for server node device ${dev}${device.deviceName}${db}...`);
1077
+ unlinkSafe(path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME, device.deviceName.replace(/[ .]/g, ''), 'sessions.resumptionRecords'), this.log);
1078
+ unlinkSafe(path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME, device.deviceName.replace(/[ .]/g, ''), 'root.subscriptions.subscriptions'), this.log);
1079
+ }
1056
1080
  await this.frontend.stop();
1057
1081
  this.frontend.destroy();
1058
1082
  this.plugins.destroy();
@@ -1380,8 +1404,8 @@ export class Matterbridge extends EventEmitter {
1380
1404
  await storageContext.set('vendorName', vendorName.slice(0, 32));
1381
1405
  await storageContext.set('productId', productId);
1382
1406
  await storageContext.set('productName', productName.slice(0, 32));
1383
- await storageContext.set('nodeLabel', productName.slice(0, 32));
1384
1407
  await storageContext.set('productLabel', productName.slice(0, 32));
1408
+ await storageContext.set('nodeLabel', deviceName.slice(0, 32));
1385
1409
  await storageContext.set('serialNumber', await storageContext.get('serialNumber', serialNumber ? serialNumber.slice(0, 32) : 'SN' + random));
1386
1410
  await storageContext.set('uniqueId', await storageContext.get('uniqueId', uniqueId ? uniqueId.slice(0, 32) : 'UI' + random));
1387
1411
  await storageContext.set('softwareVersion', isValidNumber(parseVersionString(this.matterbridgeVersion), 0, UINT32_MAX) ? parseVersionString(this.matterbridgeVersion) : 1);
@@ -1396,8 +1420,8 @@ export class Matterbridge extends EventEmitter {
1396
1420
  this.log.debug(`- vendorName: ${await storageContext.get('vendorName')}`);
1397
1421
  this.log.debug(`- productId: ${await storageContext.get('productId')}`);
1398
1422
  this.log.debug(`- productName: ${await storageContext.get('productName')}`);
1399
- this.log.debug(`- nodeLabel: ${await storageContext.get('nodeLabel')}`);
1400
1423
  this.log.debug(`- productLabel: ${await storageContext.get('productLabel')}`);
1424
+ this.log.debug(`- nodeLabel: ${await storageContext.get('nodeLabel')}`);
1401
1425
  this.log.debug(`- serialNumber: ${await storageContext.get('serialNumber')}`);
1402
1426
  this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
1403
1427
  this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
@@ -1433,12 +1457,12 @@ export class Matterbridge extends EventEmitter {
1433
1457
  deviceType: DeviceTypeId(await storageContext.get('deviceType')),
1434
1458
  },
1435
1459
  basicInformation: {
1460
+ nodeLabel: await storageContext.get('nodeLabel'),
1436
1461
  vendorId: VendorId(await storageContext.get('vendorId')),
1437
1462
  vendorName: await storageContext.get('vendorName'),
1438
1463
  productId: await storageContext.get('productId'),
1439
1464
  productName: await storageContext.get('productName'),
1440
1465
  productLabel: await storageContext.get('productName'),
1441
- nodeLabel: await storageContext.get('productName'),
1442
1466
  serialNumber: await storageContext.get('serialNumber'),
1443
1467
  uniqueId: await storageContext.get('uniqueId'),
1444
1468
  softwareVersion: await storageContext.get('softwareVersion'),
@@ -1537,7 +1561,7 @@ export class Matterbridge extends EventEmitter {
1537
1561
  this.log.notice(`Starting ${matterServerNode.id} server node`);
1538
1562
  await matterServerNode.start();
1539
1563
  }
1540
- async stopServerNode(matterServerNode, timeout = 30000) {
1564
+ async stopServerNode(matterServerNode, timeout = 10000) {
1541
1565
  const { withTimeout } = await import('./utils/wait.js');
1542
1566
  if (!matterServerNode)
1543
1567
  return;
@@ -1571,7 +1595,7 @@ export class Matterbridge extends EventEmitter {
1571
1595
  if (!plugin.locked) {
1572
1596
  plugin.locked = true;
1573
1597
  this.log.debug(`Creating dynamic plugin ${plg}${plugin.name}${db} server node...`);
1574
- plugin.storageContext = await this.createServerNodeContext(plugin.name, 'Matterbridge', this.aggregatorDeviceType, this.aggregatorVendorId, this.aggregatorVendorName, this.aggregatorProductId, plugin.description);
1598
+ plugin.storageContext = await this.createServerNodeContext(plugin.name, plugin.description, this.aggregatorDeviceType, this.aggregatorVendorId, this.aggregatorVendorName, this.aggregatorProductId, this.aggregatorProductName);
1575
1599
  plugin.serverNode = await this.createServerNode(plugin.storageContext, this.port ? this.port++ : undefined, this.passcode ? this.passcode++ : undefined, this.discriminator ? this.discriminator++ : undefined);
1576
1600
  this.log.debug(`Creating dynamic plugin ${plg}${plugin.name}${db} aggregator node...`);
1577
1601
  plugin.aggregatorNode = await this.createAggregatorNode(plugin.storageContext);
@@ -1701,7 +1725,10 @@ export class Matterbridge extends EventEmitter {
1701
1725
  this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
1702
1726
  return;
1703
1727
  }
1704
- if (this.bridgeMode === 'bridge') {
1728
+ if (device.mode === 'server') {
1729
+ this.log.info(`Removed mode server bridged endpoint(${plugin.registeredDevices}) ${dev}${device.deviceName}${nf} (${zb}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
1730
+ }
1731
+ else if (this.bridgeMode === 'bridge') {
1705
1732
  if (!this.aggregatorNode) {
1706
1733
  this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
1707
1734
  return;
@@ -94,7 +94,7 @@ export declare class MatterbridgeEndpoint extends Endpoint {
94
94
  createDefaultDeviceEnergyManagementModeClusterServer(currentMode?: number, supportedModes?: DeviceEnergyManagementMode.ModeOption[]): this;
95
95
  createDefaultIdentifyClusterServer(identifyTime?: number, identifyType?: Identify.IdentifyType): this;
96
96
  createDefaultGroupsClusterServer(): this;
97
- createDefaultScenesClusterServer(): this;
97
+ createDefaultScenesManagementClusterServer(): this;
98
98
  createDefaultOnOffClusterServer(onOff?: boolean, globalSceneControl?: boolean, onTime?: number, offWaitTime?: number, startUpOnOff?: OnOff.StartUpOnOff | null): this;
99
99
  createOnOffClusterServer(onOff?: boolean): this;
100
100
  createDeadFrontOnOffClusterServer(onOff?: boolean): this;
@@ -521,7 +521,7 @@ export class MatterbridgeEndpoint extends Endpoint {
521
521
  this.behaviors.require(GroupsServer);
522
522
  return this;
523
523
  }
524
- createDefaultScenesClusterServer() {
524
+ createDefaultScenesManagementClusterServer() {
525
525
  this.behaviors.require(ScenesManagementServer);
526
526
  return this;
527
527
  }
@@ -12,6 +12,7 @@ import { BasicInformation } from '@matter/types/clusters/basic-information';
12
12
  import { BridgedDeviceBasicInformation } from '@matter/types/clusters/bridged-device-basic-information';
13
13
  import { Identify } from '@matter/types/clusters/identify';
14
14
  import { Groups } from '@matter/types/clusters/groups';
15
+ import { ScenesManagement } from '@matter/types/clusters/scenes-management';
15
16
  import { OnOff } from '@matter/types/clusters/on-off';
16
17
  import { LevelControl } from '@matter/types/clusters/level-control';
17
18
  import { ColorControl } from '@matter/types/clusters/color-control';
@@ -55,6 +56,7 @@ import { FixedLabelServer } from '@matter/node/behaviors/fixed-label';
55
56
  import { BasicInformationServer } from '@matter/node/behaviors/basic-information';
56
57
  import { BridgedDeviceBasicInformationServer } from '@matter/node/behaviors/bridged-device-basic-information';
57
58
  import { GroupsServer } from '@matter/node/behaviors/groups';
59
+ import { ScenesManagementServer } from '@matter/node/behaviors/scenes-management';
58
60
  import { PumpConfigurationAndControlServer } from '@matter/node/behaviors/pump-configuration-and-control';
59
61
  import { SwitchServer } from '@matter/node/behaviors/switch';
60
62
  import { BooleanStateServer } from '@matter/node/behaviors/boolean-state';
@@ -149,6 +151,8 @@ export function getBehaviourTypeFromClusterServerId(clusterId) {
149
151
  return MatterbridgeIdentifyServer;
150
152
  if (clusterId === Groups.Cluster.id)
151
153
  return GroupsServer;
154
+ if (clusterId === ScenesManagement.Cluster.id)
155
+ return ScenesManagementServer;
152
156
  if (clusterId === OnOff.Cluster.id)
153
157
  return MatterbridgeOnOffServer.with('Lighting');
154
158
  if (clusterId === LevelControl.Cluster.id)
@@ -313,6 +317,8 @@ export function addClusterServers(endpoint, serverList) {
313
317
  endpoint.createDefaultIdentifyClusterServer();
314
318
  if (serverList.includes(Groups.Cluster.id))
315
319
  endpoint.createDefaultGroupsClusterServer();
320
+ if (serverList.includes(ScenesManagement.Cluster.id))
321
+ endpoint.createDefaultScenesManagementClusterServer();
316
322
  if (serverList.includes(OnOff.Cluster.id))
317
323
  endpoint.createDefaultOnOffClusterServer();
318
324
  if (serverList.includes(LevelControl.Cluster.id))