matterbridge 3.5.1-dev-20260121-22e98b4 → 3.5.1-dev-20260123-4fcee18

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
@@ -35,6 +35,11 @@ Advantages:
35
35
  - [mb_mdns]: Added additional mDNS service types for discovery.
36
36
  - [workflows]: Added npm registry URL and caching to Node.js setup in workflow files.
37
37
  - [workflows]: Added concurrency settings in build.yml to cancel previous runs in GitHub Actions.
38
+ - [preset]: Added Thermostat with preset feature. Thanks Ludovic BOUÉ (https://github.com/Luligu/matterbridge/pull/482).
39
+ - [matter.js]: Bump to matter.j v. 0.16.6.
40
+ - [mb_mdns]: Added --ip-filter params to filter incoming mDns messages by sender ip.
41
+ - [express]: Added a login check for internal express api. Now only /health and /memory are opened. Thanks Rogibaer (https://github.com/Luligu/matterbridge-hass/issues/149).
42
+ - [readme]: Added a section with the data structure and backup/restore guidelines.
38
43
 
39
44
  ### Changed
40
45
 
package/README-DOCKER.md CHANGED
@@ -18,12 +18,14 @@
18
18
 
19
19
  ## Run matterbridge with docker and docker compose
20
20
 
21
- The Matterbridge Docker image, which includes a manifest list for the linux/amd64, linux/arm64 and linux/arm/v7 architectures, is published on [**Docker Hub**](https://hub.docker.com/r/luligu/matterbridge).
21
+ The Matterbridge Docker image, which includes a manifest list for the linux/amd64 and linux/arm64 architectures, is published on [**Docker Hub**](https://hub.docker.com/r/luligu/matterbridge).
22
22
 
23
23
  The image (tag **latest**) includes matterbridge and all official plugins with the latest release (as published on npm). You can just pull the new image and matterbridge with all plugins will be the latest release published on npm.
24
24
 
25
25
  The image (tag **dev**) includes matterbridge and all plugins with the dev release (as pushed on GitHub). You can just pull the new image and matterbridge with all plugins will be the latest dev release pushed on GitHub. It is possible that the devs are outdated by published latests.
26
26
 
27
+ For development and testing see also the [Development Images](README-DOCKER.md#development-images).
28
+
27
29
  You can directly select and add a plugin without installing it.
28
30
 
29
31
  It is based on node:22-bookworm-slim and integrates the health check.
@@ -240,6 +242,6 @@ sudo systemctl restart docker
240
242
 
241
243
  On [**Docker Hub**](https://hub.docker.com/r/luligu/matterbridge) are also published **for test and development** the Matterbridge ubuntu and alpine images, which include a manifest list for the linux/amd64, linux/arm64 architectures and are based on node 24.x.
242
244
 
243
- The image (tag **ubuntu**) includes only matterbridge with the latest release (as published on npm). The plugins are not included in the image but they will be reinstalled on the first run.
245
+ The image (tag **ubuntu**) includes only matterbridge with the latest release (as published on npm). The plugins are not included in the image but they will be reinstalled on the first run. This image has preinstalled bluetooth essentials and python (it can be used with plugins that require bluetooth, build-essential and python).
244
246
 
245
247
  The image (tag **alpine**) includes only matterbridge with the latest release (as published on npm). The plugins are not included in the image but they will be reinstalled on the first run.
package/README.md CHANGED
@@ -72,11 +72,14 @@ https://blog.adafruit.com/2025/11/03/matterbridge-a-matter-plugin-manager/
72
72
  To run Matterbridge, you need either a [Node.js](https://nodejs.org/en) environment or [Docker](https://docs.docker.com/get-started/get-docker/) installed on your system.
73
73
 
74
74
  If you don't have Node.js already install, please use this method to install it on a debian device: https://github.com/nodesource/distributions.
75
- The supported versions of node are 20 and 22. Please install Node.js 22 LTS. Don't use Node.js Current but always the Node.js LTS.
75
+ The supported versions of node are 20, 22 and 24. Please install Node.js 22 LTS. Don't use Node.js Current but always the Node.js LTS.
76
76
  Node.js 23, like all odd-numbered versions, is not supported.
77
77
  Nvm is not a good choice and should not be used for production.
78
78
 
79
79
  If you don't have Docker already install, please use this method to install it on a debian device: https://docs.docker.com/engine/install.
80
+
81
+ If you don't have Docker already install, please use this method to install it on a Windows or macOS: https://docs.docker.com/get-started/introduction/get-docker-desktop/.
82
+
80
83
  After follow the guidelines for the [Docker configurations](README-DOCKER.md).
81
84
 
82
85
  I suggest using Docker for its simplicity.
@@ -159,7 +162,13 @@ Here's how to specify a different port number:
159
162
  matterbridge --frontend [port number]
160
163
  ```
161
164
 
162
- To use the frontend with ssl see below.
165
+ The frontend binds, by default, to all IPv4 and IPv6 addresses. You can override this and bind to a specific address:
166
+
167
+ ```bash
168
+ matterbridge --bind [address]
169
+ ```
170
+
171
+ To use the frontend with SSL, see below.
163
172
 
164
173
  From the frontend you can do all operations in an easy way.
165
174
 
@@ -548,6 +557,37 @@ So depending on the controller you pair with, you should see 100 for fully close
548
557
 
549
558
  Some controllers invert the position so you need to verify your controller.
550
559
 
560
+ ## Data structure
561
+
562
+ Matterbridge uses three directories. These are the default locations (some advanced setups may change them, so check your configuration):
563
+
564
+ ```text
565
+ ~/.matterbridge
566
+ ~/Matterbridge
567
+ ~/.mattercert
568
+ ```
569
+
570
+ ### Backup
571
+
572
+ From the frontend (three-dots menu), select **Create backup**, then **Download backup** when it is ready.
573
+ The backup file is a standard `.zip` archive.
574
+
575
+ ### Restore
576
+
577
+ Restore must be done manually because the archive paths depend on your setup.
578
+
579
+ Make sure Matterbridge is not running. Stop it if needed.
580
+
581
+ Extract the backup and replace the corresponding directories on your system with the ones from the backup:
582
+
583
+ ```text
584
+ .matterbridge
585
+ Matterbridge
586
+ .mattercert
587
+ ```
588
+
589
+ Ensure permissions are correct for the restored directories.
590
+
551
591
  # Known general issues
552
592
 
553
593
  ## Session XYZ does not exist or Cannot find a session for ID XYZ
@@ -564,17 +604,13 @@ All issues have been solved from the version 17.5 of the HomePod/AppleTV. Now th
564
604
 
565
605
  If you have more then one Apple TV or Home Pod, you can herve better results setting to disabled "Automatic Selection" in "Home Setting", "Home Hubs & Bridges". When "Automatic selection" is disabled, select your Apple Tv if you have one or any of your Home Pod. In this way you should not have anymore more then one session for fabric.
566
606
 
567
- ### Manufacturer Serial Number and Model
568
-
569
- The Home app forgets about them when you restart the node.
570
-
571
607
  ### Appliances
572
608
 
573
609
  As of version 18.4.x, all Appliances device types are not supported by the Home app. They don't even appear like unsupported accessories.
574
610
 
575
611
  ### Robot
576
612
 
577
- As of version 18.4.x, the Robot is supported by the Home app only as a single, non-bridged device or if it is the only device in the bridge.
613
+ As of version 18.4.x, the Robot is supported by the Home app only as a single, non-bridged device or if it is the only device in the bridge. Furthermore the device cannot be a composed device. The only device type supported is the rvc.
578
614
 
579
615
  If a Robot is present alongside other devices in the bridge, the entire bridge becomes unstable in the Home app.
580
616
 
@@ -588,7 +624,6 @@ So far is the only controller supporting all Matter 1.2, 1.3 and 1.4 device type
588
624
 
589
625
  - If HA doesn't show all devices, reload the Matter Server Integration or reboot HA
590
626
  - Home Assistant doesn't seem to always react when a device is removed from the bridge: they remain in HA unavailable forever. A full Home Assistant restart solves the problem.
591
- - Use Apple Home when you have to choose the controller type even if you pair Matterbridge directly with HA.
592
627
 
593
628
  ## Google Home
594
629
 
@@ -598,6 +633,8 @@ If you face a problem changing the brightness check this for the explanation: ht
598
633
 
599
634
  If you encounter a “Something Went Wrong” screen while commissioning MatterBridge devices in Google Home on Android, it’s due to an Android bug. Android fails to send the country code, which is mandatory under the Matter specification.
600
635
 
636
+ There is also a known issue with the thermostat Fahrenheit to Celsius Conversion (https://github.com/Luligu/matterbridge/issues/462).
637
+
601
638
  ### Workaround
602
639
 
603
640
  Install Google Home on an iPhone and complete the commissioning there. Once set up, the devices will appear and function normally on your Android phone and other Nest devices in your home. By [Artem Kovalov](https://github.com/artemkovalyov).
@@ -17,6 +17,7 @@ export declare class Frontend extends EventEmitter<FrontendEvents> {
17
17
  private port;
18
18
  private listening;
19
19
  private storedPassword;
20
+ private authClients;
20
21
  private expressApp;
21
22
  private httpServer;
22
23
  private httpsServer;
@@ -28,6 +29,9 @@ export declare class Frontend extends EventEmitter<FrontendEvents> {
28
29
  destroy(): void;
29
30
  private msgHandler;
30
31
  set logLevel(logLevel: LogLevel);
32
+ validateReq(req: import('express').Request<unknown, unknown, unknown, {
33
+ password?: string;
34
+ }>, res: import('express').Response): boolean;
31
35
  start(port?: number): Promise<void>;
32
36
  stop(): Promise<void>;
33
37
  private getApiSettings;
package/dist/frontend.js CHANGED
@@ -22,6 +22,7 @@ export class Frontend extends EventEmitter {
22
22
  port = 8283;
23
23
  listening = false;
24
24
  storedPassword = undefined;
25
+ authClients = [];
25
26
  expressApp;
26
27
  httpServer;
27
28
  httpsServer;
@@ -130,6 +131,14 @@ export class Frontend extends EventEmitter {
130
131
  set logLevel(logLevel) {
131
132
  this.log.logLevel = logLevel;
132
133
  }
134
+ validateReq(req, res) {
135
+ if (req.ip && !this.authClients.includes(req.ip)) {
136
+ this.log.warn(`Warning blocked unauthorized access request ${req.originalUrl ?? req.url} from ${req.ip}`);
137
+ res.status(401).json({ error: 'Unauthorized' });
138
+ return false;
139
+ }
140
+ return true;
141
+ }
133
142
  async start(port = 8283) {
134
143
  this.port = port;
135
144
  this.storedPassword = await this.matterbridge.nodeContext?.get('password', '');
@@ -169,6 +178,7 @@ export class Frontend extends EventEmitter {
169
178
  if (this.webSocketServer?.clients.size === 0) {
170
179
  AnsiLogger.setGlobalCallback(undefined);
171
180
  this.log.debug('All WebSocket clients disconnected. WebSocketServer logger global callback removed');
181
+ this.authClients = [];
172
182
  }
173
183
  });
174
184
  ws.on('error', (error) => {
@@ -228,6 +238,8 @@ export class Frontend extends EventEmitter {
228
238
  return socket.destroy();
229
239
  }
230
240
  this.log.debug(`WebSocket upgrade success host ${url.host} password ${password ? '[redacted]' : '(empty)'}`);
241
+ if (req.socket.remoteAddress)
242
+ this.authClients.push(req.socket.remoteAddress);
231
243
  this.webSocketServer?.handleUpgrade(req, socket, head, (ws) => {
232
244
  this.webSocketServer?.emit('connection', ws, req);
233
245
  });
@@ -363,6 +375,8 @@ export class Frontend extends EventEmitter {
363
375
  return socket.destroy();
364
376
  }
365
377
  this.log.debug(`WebSocket upgrade success host ${url.host} password ${password ? '[redacted]' : '(empty)'}`);
378
+ if (req.socket.remoteAddress)
379
+ this.authClients.push(req.socket.remoteAddress);
366
380
  this.webSocketServer?.handleUpgrade(req, socket, head, (ws) => {
367
381
  this.webSocketServer?.emit('connection', ws, req);
368
382
  });
@@ -405,6 +419,8 @@ export class Frontend extends EventEmitter {
405
419
  if (this.storedPassword === '' || password === this.storedPassword) {
406
420
  this.log.debug('/api/login password valid');
407
421
  res.json({ valid: true });
422
+ if (req.ip)
423
+ this.authClients.push(req.ip);
408
424
  }
409
425
  else {
410
426
  this.log.warn('/api/login error wrong password');
@@ -454,18 +470,26 @@ export class Frontend extends EventEmitter {
454
470
  });
455
471
  this.expressApp.get('/api/settings', express.json(), async (req, res) => {
456
472
  this.log.debug('The frontend sent /api/settings');
473
+ if (!this.validateReq(req, res))
474
+ return;
457
475
  res.json(await this.getApiSettings());
458
476
  });
459
477
  this.expressApp.get('/api/plugins', async (req, res) => {
460
478
  this.log.debug('The frontend sent /api/plugins');
479
+ if (!this.validateReq(req, res))
480
+ return;
461
481
  res.json(this.matterbridge.hasCleanupStarted ? [] : this.getPlugins());
462
482
  });
463
483
  this.expressApp.get('/api/devices', async (req, res) => {
464
484
  this.log.debug('The frontend sent /api/devices');
485
+ if (!this.validateReq(req, res))
486
+ return;
465
487
  res.json(this.matterbridge.hasCleanupStarted ? [] : this.getDevices());
466
488
  });
467
489
  this.expressApp.get('/api/view-mblog', async (req, res) => {
468
490
  this.log.debug('The frontend sent /api/view-mblog');
491
+ if (!this.validateReq(req, res))
492
+ return;
469
493
  try {
470
494
  const fs = await import('node:fs');
471
495
  const data = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE), 'utf8');
@@ -479,6 +503,8 @@ export class Frontend extends EventEmitter {
479
503
  });
480
504
  this.expressApp.get('/api/view-mjlog', async (req, res) => {
481
505
  this.log.debug('The frontend sent /api/view-mjlog');
506
+ if (!this.validateReq(req, res))
507
+ return;
482
508
  try {
483
509
  const fs = await import('node:fs');
484
510
  const data = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, MATTER_LOGGER_FILE), 'utf8');
@@ -492,6 +518,8 @@ export class Frontend extends EventEmitter {
492
518
  });
493
519
  this.expressApp.get('/api/view-diagnostic', async (req, res) => {
494
520
  this.log.debug('The frontend sent /api/view-diagnostic');
521
+ if (!this.validateReq(req, res))
522
+ return;
495
523
  await this.generateDiagnostic();
496
524
  try {
497
525
  const fs = await import('node:fs');
@@ -506,6 +534,8 @@ export class Frontend extends EventEmitter {
506
534
  });
507
535
  this.expressApp.get('/api/download-diagnostic', async (req, res) => {
508
536
  this.log.debug(`The frontend sent /api/download-diagnostic`);
537
+ if (!this.validateReq(req, res))
538
+ return;
509
539
  await this.generateDiagnostic();
510
540
  try {
511
541
  const fs = await import('node:fs');
@@ -526,6 +556,8 @@ export class Frontend extends EventEmitter {
526
556
  });
527
557
  this.expressApp.get('/api/viewhistory', async (req, res) => {
528
558
  this.log.debug('The frontend sent /api/viewhistory');
559
+ if (!this.validateReq(req, res))
560
+ return;
529
561
  try {
530
562
  const fs = await import('node:fs');
531
563
  const data = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_HISTORY_FILE), 'utf8');
@@ -539,6 +571,8 @@ export class Frontend extends EventEmitter {
539
571
  });
540
572
  this.expressApp.get('/api/downloadhistory', async (req, res) => {
541
573
  this.log.debug(`The frontend sent /api/downloadhistory`);
574
+ if (!this.validateReq(req, res))
575
+ return;
542
576
  try {
543
577
  const fs = await import('node:fs');
544
578
  await fs.promises.access(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_HISTORY_FILE), fs.constants.F_OK);
@@ -559,6 +593,8 @@ export class Frontend extends EventEmitter {
559
593
  });
560
594
  this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
561
595
  this.log.debug('The frontend sent /api/shellyviewsystemlog');
596
+ if (!this.validateReq(req, res))
597
+ return;
562
598
  try {
563
599
  const fs = await import('node:fs');
564
600
  const data = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), 'utf8');
@@ -572,6 +608,8 @@ export class Frontend extends EventEmitter {
572
608
  });
573
609
  this.expressApp.get('/api/download-mblog', async (req, res) => {
574
610
  this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE)}`);
611
+ if (!this.validateReq(req, res))
612
+ return;
575
613
  const fs = await import('node:fs');
576
614
  try {
577
615
  await fs.promises.access(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE), fs.constants.F_OK);
@@ -592,6 +630,8 @@ export class Frontend extends EventEmitter {
592
630
  });
593
631
  this.expressApp.get('/api/download-mjlog', async (req, res) => {
594
632
  this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE)}`);
633
+ if (!this.validateReq(req, res))
634
+ return;
595
635
  const fs = await import('node:fs');
596
636
  try {
597
637
  await fs.promises.access(path.join(this.matterbridge.matterbridgeDirectory, MATTER_LOGGER_FILE), fs.constants.F_OK);
@@ -612,6 +652,8 @@ export class Frontend extends EventEmitter {
612
652
  });
613
653
  this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
614
654
  this.log.debug('The frontend sent /api/shellydownloadsystemlog');
655
+ if (!this.validateReq(req, res))
656
+ return;
615
657
  const fs = await import('node:fs');
616
658
  try {
617
659
  await fs.promises.access(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), fs.constants.F_OK);
@@ -632,6 +674,8 @@ export class Frontend extends EventEmitter {
632
674
  });
633
675
  this.expressApp.get('/api/download-mbstorage', async (req, res) => {
634
676
  this.log.debug('The frontend sent /api/download-mbstorage');
677
+ if (!this.validateReq(req, res))
678
+ return;
635
679
  await createZip(path.join(os.tmpdir(), `matterbridge.${NODE_STORAGE_DIR}.zip`), path.join(this.matterbridge.matterbridgeDirectory, NODE_STORAGE_DIR));
636
680
  res.download(path.join(os.tmpdir(), `matterbridge.${NODE_STORAGE_DIR}.zip`), `matterbridge.${NODE_STORAGE_DIR}.zip`, (error) => {
637
681
  if (error) {
@@ -642,6 +686,8 @@ export class Frontend extends EventEmitter {
642
686
  });
643
687
  this.expressApp.get('/api/download-mjstorage', async (req, res) => {
644
688
  this.log.debug('The frontend sent /api/download-mjstorage');
689
+ if (!this.validateReq(req, res))
690
+ return;
645
691
  await createZip(path.join(os.tmpdir(), `matterbridge.${MATTER_STORAGE_NAME}.zip`), path.join(this.matterbridge.matterbridgeDirectory, MATTER_STORAGE_NAME));
646
692
  res.download(path.join(os.tmpdir(), `matterbridge.${MATTER_STORAGE_NAME}.zip`), `matterbridge.${MATTER_STORAGE_NAME}.zip`, (error) => {
647
693
  if (error) {
@@ -652,6 +698,8 @@ export class Frontend extends EventEmitter {
652
698
  });
653
699
  this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
654
700
  this.log.debug('The frontend sent /api/download-pluginstorage');
701
+ if (!this.validateReq(req, res))
702
+ return;
655
703
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
656
704
  res.download(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), `matterbridge.pluginstorage.zip`, (error) => {
657
705
  if (error) {
@@ -662,6 +710,8 @@ export class Frontend extends EventEmitter {
662
710
  });
663
711
  this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
664
712
  this.log.debug('The frontend sent /api/download-pluginconfig');
713
+ if (!this.validateReq(req, res))
714
+ return;
665
715
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
666
716
  res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
667
717
  if (error) {
@@ -672,6 +722,8 @@ export class Frontend extends EventEmitter {
672
722
  });
673
723
  this.expressApp.get('/api/download-backup', async (req, res) => {
674
724
  this.log.debug('The frontend sent /api/download-backup');
725
+ if (!this.validateReq(req, res))
726
+ return;
675
727
  res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
676
728
  if (error) {
677
729
  this.log.error(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
@@ -680,6 +732,8 @@ export class Frontend extends EventEmitter {
680
732
  });
681
733
  });
682
734
  this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
735
+ if (!this.validateReq(req, res))
736
+ return;
683
737
  const { filename } = req.body;
684
738
  const file = req.file;
685
739
  if (!file || !filename) {
@@ -1434,7 +1488,7 @@ export class Frontend extends EventEmitter {
1434
1488
  else if (data.method === '/api/create-backup') {
1435
1489
  this.wssSendSnackbarMessage('Creating backup...', 0);
1436
1490
  this.log.notice(`Creating the backup...`);
1437
- await createZip(path.join(os.tmpdir(), `matterbridge.backup.zip`), path.join(this.matterbridge.matterbridgeDirectory), path.join(this.matterbridge.matterbridgePluginDirectory));
1491
+ await createZip(path.join(os.tmpdir(), `matterbridge.backup.zip`), path.join(this.matterbridge.matterbridgeDirectory), path.join(this.matterbridge.matterbridgePluginDirectory), path.join(this.matterbridge.matterbridgeCertDirectory));
1438
1492
  this.log.notice(`Backup ready to be downloaded.`);
1439
1493
  this.wssSendCloseSnackbarMessage('Creating backup...');
1440
1494
  this.wssSendSnackbarMessage('Backup ready to be downloaded', 10);
@@ -519,7 +519,7 @@ export async function startServerNode(name, port, deviceType = bridge.code) {
519
519
  expect(aggregator.lifecycle.isPartsReady).toBeTruthy();
520
520
  expect(aggregator.lifecycle.hasId).toBeTruthy();
521
521
  expect(aggregator.lifecycle.hasNumber).toBeTruthy();
522
- await flushAsync();
522
+ await flushAsync(3, 3, 10);
523
523
  return [server, aggregator];
524
524
  }
525
525
  export async function stopServerNode(server) {
@@ -531,12 +531,7 @@ export async function stopServerNode(server) {
531
531
  await server.close();
532
532
  expect(server.lifecycle.isReady).toBeFalsy();
533
533
  expect(server.lifecycle.isOnline).toBeFalsy();
534
- const mdns = environment.get(MdnsService);
535
- if (mdns && typeof mdns[Symbol.asyncDispose] === 'function')
536
- await mdns[Symbol.asyncDispose]();
537
- if (mdns && typeof mdns.close === 'function')
538
- await mdns.close();
539
- await flushAsync();
534
+ await flushAsync(3, 3, 10);
540
535
  }
541
536
  export async function addDevice(owner, device, pause = 10) {
542
537
  expect(owner).toBeDefined();
@@ -1420,19 +1420,30 @@ export class Matterbridge extends EventEmitter {
1420
1420
  this.log.debug(`- nodeLabel: ${await storageContext.get('nodeLabel')}`);
1421
1421
  this.log.debug(`- serialNumber: ${await storageContext.get('serialNumber')}`);
1422
1422
  this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
1423
- this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
1424
- this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
1423
+ this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')}`);
1424
+ this.log.debug(`- softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
1425
+ this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')}`);
1426
+ this.log.debug(`- hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
1425
1427
  return storageContext;
1426
1428
  }
1427
1429
  async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
1428
1430
  const storeId = await storageContext.get('storeId');
1429
1431
  this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
1432
+ this.log.debug(`- storeId: ${await storageContext.get('storeId')}`);
1430
1433
  this.log.debug(`- deviceName: ${await storageContext.get('deviceName')}`);
1431
1434
  this.log.debug(`- deviceType: ${await storageContext.get('deviceType')}(0x${(await storageContext.get('deviceType'))?.toString(16).padStart(4, '0')})`);
1435
+ this.log.debug(`- vendorId: ${await storageContext.get('vendorId')}`);
1436
+ this.log.debug(`- vendorName: ${await storageContext.get('vendorName')}`);
1437
+ this.log.debug(`- productId: ${await storageContext.get('productId')}`);
1438
+ this.log.debug(`- productName: ${await storageContext.get('productName')}`);
1439
+ this.log.debug(`- productLabel: ${await storageContext.get('productLabel')}`);
1440
+ this.log.debug(`- nodeLabel: ${await storageContext.get('nodeLabel')}`);
1432
1441
  this.log.debug(`- serialNumber: ${await storageContext.get('serialNumber')}`);
1433
1442
  this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
1434
- this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
1435
- this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
1443
+ this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')}`);
1444
+ this.log.debug(`- softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
1445
+ this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')}`);
1446
+ this.log.debug(`- hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
1436
1447
  const serverNode = await ServerNode.create({
1437
1448
  id: storeId,
1438
1449
  environment: this.environment,
@@ -277,7 +277,7 @@ export async function invokeSubscribeHandler(endpoint, cluster, attribute, newVa
277
277
  endpoint.log.error(`invokeSubscribeHandler ${hk}${event}${er} error: cluster ${behaviorId} not found on endpoint ${or}${endpoint.id}${er}:${or}${endpoint.number}${er}`);
278
278
  return false;
279
279
  }
280
- await endpoint.act((agent) => agent[behaviorId].events[event].emit(newValue, oldValue, { ...agent.context, offline: false }));
280
+ await endpoint.act((agent) => agent[behaviorId].events[event].emit(newValue, oldValue, { ...agent.context, offline: false, fabric: 1 }));
281
281
  return true;
282
282
  }
283
283
  export function addRequiredClusterServers(endpoint) {
package/dist/mb_mdns.js CHANGED
@@ -18,6 +18,7 @@ Options:
18
18
  --advertise <interval> Enable matterbridge mDNS advertisement each ms (default interval: 10000ms).
19
19
  --query <interval> Enable common mDNS services query each ms (default interval: 10000ms).
20
20
  --filter <string...> Filter strings to match in the mDNS record name (default: no filter).
21
+ --ip-filter <string...> Filter strings to match in the mDNS sender ip address (default: no filter).
21
22
  --noIpv4 Disable IPv4 mDNS server (default: enabled).
22
23
  --noIpv6 Disable IPv6 mDNS server (default: enabled).
23
24
  --no-timeout Disable automatic timeout of 10 minutes. Reflector mode disables timeout automatically.
@@ -35,6 +36,9 @@ Examples:
35
36
  # Listen for Matter commissioner and discovery service records on all interfaces
36
37
  mb_mdns --filter _matterc._udp _matter._tcp
37
38
 
39
+ # Listen for Matter commissioner and discovery service records on all interfaces from specific ipv4 and ipv6 ips
40
+ mb_mdns --filter _matterc._udp _matter._tcp --ip-filter 192.168.69.20 fe80::1077:2e0d:2c91:aa90
41
+
38
42
  # Query for mDNS devices every 10s on a specific interface
39
43
  mb_mdns --interfaceName eth0 --query
40
44
 
@@ -162,6 +166,9 @@ Examples:
162
166
  const filters = getStringArrayParameter('filter');
163
167
  if (filters)
164
168
  mdnsIpv4.filters.push(...filters);
169
+ const ipFilters = getStringArrayParameter('ip-filter');
170
+ if (ipFilters)
171
+ mdnsIpv4.ipFilters.push(...ipFilters);
165
172
  mdnsIpv4.on('error', (err) => {
166
173
  mdnsIpv4?.log.error(`mDNS udp4 Server error: ${err.message}\n${err.stack}`);
167
174
  });
@@ -186,6 +193,9 @@ Examples:
186
193
  const filters = getStringArrayParameter('filter');
187
194
  if (filters)
188
195
  mdnsIpv6.filters.push(...filters);
196
+ const ipFilters = getStringArrayParameter('ip-filter');
197
+ if (ipFilters)
198
+ mdnsIpv6.ipFilters.push(...ipFilters);
189
199
  mdnsIpv6.on('error', (err) => {
190
200
  mdnsIpv6?.log.error(`mDNS udp6 Server error: ${err.message}\n${err.stack}`);
191
201
  });
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "matterbridge",
3
- "version": "3.5.1-dev-20260121-22e98b4",
3
+ "version": "3.5.1-dev-20260123-4fcee18",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "matterbridge",
9
- "version": "3.5.1-dev-20260121-22e98b4",
9
+ "version": "3.5.1-dev-20260123-4fcee18",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
- "@matter/main": "0.16.5",
12
+ "@matter/main": "0.16.6",
13
13
  "@matterbridge/dgram": "0.0.2",
14
14
  "@matterbridge/jest-utils": "0.0.1",
15
15
  "@matterbridge/utils": "0.0.1",
@@ -74,86 +74,86 @@
74
74
  }
75
75
  },
76
76
  "node_modules/@matter/general": {
77
- "version": "0.16.5",
78
- "resolved": "https://registry.npmjs.org/@matter/general/-/general-0.16.5.tgz",
79
- "integrity": "sha512-KKTUpE7Gz/yEnMSDWfQzTENZO2vCCw0txGNfAcTT7c5F5nRpBpZDSACNqfJV2x51Hj7EQtSbcJ2+TC580JYsOg==",
77
+ "version": "0.16.6",
78
+ "resolved": "https://registry.npmjs.org/@matter/general/-/general-0.16.6.tgz",
79
+ "integrity": "sha512-MyMLj9MEk6e0J+qjFVxuNxySTy2RcjdtYog7d9xGrWWI1mszCiT61Q5/aE198z+v5+Lca8CX+GDfeoNIT6hdgw==",
80
80
  "license": "Apache-2.0",
81
81
  "dependencies": {
82
82
  "@noble/curves": "^2.0.1"
83
83
  }
84
84
  },
85
85
  "node_modules/@matter/main": {
86
- "version": "0.16.5",
87
- "resolved": "https://registry.npmjs.org/@matter/main/-/main-0.16.5.tgz",
88
- "integrity": "sha512-zb7ipavx+lLphbjmnIKH7pMtPc+wo+UCu0qP839zgo6dLIdVJlXsZT2EnHFA1dQNtIFWvPQfgx9Db/naSn+bAg==",
86
+ "version": "0.16.6",
87
+ "resolved": "https://registry.npmjs.org/@matter/main/-/main-0.16.6.tgz",
88
+ "integrity": "sha512-QTShS4OVEXoDyjI7jeLx35EPNUho8V/RZu971aBoHU3ueIxj4/gPWecN5NBTsmEl2VaCKHnmNaT6z11lNgdIaA==",
89
89
  "license": "Apache-2.0",
90
90
  "dependencies": {
91
- "@matter/general": "0.16.5",
92
- "@matter/model": "0.16.5",
93
- "@matter/node": "0.16.5",
94
- "@matter/protocol": "0.16.5",
95
- "@matter/types": "0.16.5"
91
+ "@matter/general": "0.16.6",
92
+ "@matter/model": "0.16.6",
93
+ "@matter/node": "0.16.6",
94
+ "@matter/protocol": "0.16.6",
95
+ "@matter/types": "0.16.6"
96
96
  },
97
97
  "optionalDependencies": {
98
- "@matter/nodejs": "0.16.5"
98
+ "@matter/nodejs": "0.16.6"
99
99
  }
100
100
  },
101
101
  "node_modules/@matter/model": {
102
- "version": "0.16.5",
103
- "resolved": "https://registry.npmjs.org/@matter/model/-/model-0.16.5.tgz",
104
- "integrity": "sha512-kz1ik7ccZHZrZEd9mxtOcVgQs1R2JmdyrszHEZUk9Zaqp7hyWWcmeS9p+8FVVMdUU24oBhDc2WrmjA9WNJsW1A==",
102
+ "version": "0.16.6",
103
+ "resolved": "https://registry.npmjs.org/@matter/model/-/model-0.16.6.tgz",
104
+ "integrity": "sha512-eTLer9EibjA87AKn1Kj/oQv3nabaWJ2Pns3bGCUbgVycgGkLELJQs8TiA2QqorGbH2upTFmYfyaVUoxtMtyHbA==",
105
105
  "license": "Apache-2.0",
106
106
  "dependencies": {
107
- "@matter/general": "0.16.5"
107
+ "@matter/general": "0.16.6"
108
108
  }
109
109
  },
110
110
  "node_modules/@matter/node": {
111
- "version": "0.16.5",
112
- "resolved": "https://registry.npmjs.org/@matter/node/-/node-0.16.5.tgz",
113
- "integrity": "sha512-c9AVRsrWFBy68VLPUJvsrENOd6aQLAiNyrZ9rfTy/MGIMuQTIzF6az/3CGof8HttHEU/ZItJLn6VZzsH2YCr7A==",
111
+ "version": "0.16.6",
112
+ "resolved": "https://registry.npmjs.org/@matter/node/-/node-0.16.6.tgz",
113
+ "integrity": "sha512-gPmxxX9OG4QnAPHdQsSbs1B/gB5WIrCaTkjp/kqLKCYWuYtFbDQr+PXTWn7SdQZWY/aHZ9/e3xP4VeVd8sk4/A==",
114
114
  "license": "Apache-2.0",
115
115
  "dependencies": {
116
- "@matter/general": "0.16.5",
117
- "@matter/model": "0.16.5",
118
- "@matter/protocol": "0.16.5",
119
- "@matter/types": "0.16.5"
116
+ "@matter/general": "0.16.6",
117
+ "@matter/model": "0.16.6",
118
+ "@matter/protocol": "0.16.6",
119
+ "@matter/types": "0.16.6"
120
120
  }
121
121
  },
122
122
  "node_modules/@matter/nodejs": {
123
- "version": "0.16.5",
124
- "resolved": "https://registry.npmjs.org/@matter/nodejs/-/nodejs-0.16.5.tgz",
125
- "integrity": "sha512-2ilpEHvqEYZQopbXOO6qB1FZv26b91kBFQySho0mH9yzMwiLqe1SrRQcrVo6vSM3shIEdDfEpT8Z6JsyUyaxdQ==",
123
+ "version": "0.16.6",
124
+ "resolved": "https://registry.npmjs.org/@matter/nodejs/-/nodejs-0.16.6.tgz",
125
+ "integrity": "sha512-r4UgxNWEsOCan315Bne3k1n3KFExBW5k263wGsef6a1GViJdjAAx4IhO6GxXtPH+bPFsjYQO4N7AZ5BCR8Knqw==",
126
126
  "license": "Apache-2.0",
127
127
  "optional": true,
128
128
  "dependencies": {
129
- "@matter/general": "0.16.5",
130
- "@matter/node": "0.16.5",
131
- "@matter/protocol": "0.16.5",
132
- "@matter/types": "0.16.5"
129
+ "@matter/general": "0.16.6",
130
+ "@matter/node": "0.16.6",
131
+ "@matter/protocol": "0.16.6",
132
+ "@matter/types": "0.16.6"
133
133
  },
134
134
  "engines": {
135
135
  "node": ">=20.19.0 <22.0.0 || >=22.13.0"
136
136
  }
137
137
  },
138
138
  "node_modules/@matter/protocol": {
139
- "version": "0.16.5",
140
- "resolved": "https://registry.npmjs.org/@matter/protocol/-/protocol-0.16.5.tgz",
141
- "integrity": "sha512-TtBXeYRWheWHIx7xP1Q9G9KnG2y9x4xuenWZzNlzw/cT5EQcmqgoUm42l5TseUFpGxNiuFhNwRLOzQPQQcdW+A==",
139
+ "version": "0.16.6",
140
+ "resolved": "https://registry.npmjs.org/@matter/protocol/-/protocol-0.16.6.tgz",
141
+ "integrity": "sha512-Mw5F+TZrlcrofsH84Jc1F2tldn0tLRFDVC9s5d1cwUh5196xlSyapuFGKA2TdDgjS7jSZvcMsOutSVUlVtpgqw==",
142
142
  "license": "Apache-2.0",
143
143
  "dependencies": {
144
- "@matter/general": "0.16.5",
145
- "@matter/model": "0.16.5",
146
- "@matter/types": "0.16.5"
144
+ "@matter/general": "0.16.6",
145
+ "@matter/model": "0.16.6",
146
+ "@matter/types": "0.16.6"
147
147
  }
148
148
  },
149
149
  "node_modules/@matter/types": {
150
- "version": "0.16.5",
151
- "resolved": "https://registry.npmjs.org/@matter/types/-/types-0.16.5.tgz",
152
- "integrity": "sha512-YUnIrRAPzr7qzSdAukOJ7I1pQb47mERDRp7IZPQ4bGKZGuZzsy0eYLWcAyEqzGco+xvaWktTJYI975Qbb2X2zg==",
150
+ "version": "0.16.6",
151
+ "resolved": "https://registry.npmjs.org/@matter/types/-/types-0.16.6.tgz",
152
+ "integrity": "sha512-QUuFNJPKVj4pwudH1dX5UQx+WLGqUq2Gxg5ZMrOidWAl7jElucMJWEwMLVGDfL1vq6XXzGH5oxV2l+mVdcx2mQ==",
153
153
  "license": "Apache-2.0",
154
154
  "dependencies": {
155
- "@matter/general": "0.16.5",
156
- "@matter/model": "0.16.5"
155
+ "@matter/general": "0.16.6",
156
+ "@matter/model": "0.16.6"
157
157
  }
158
158
  },
159
159
  "node_modules/@matterbridge/dgram": {
@@ -1279,9 +1279,9 @@
1279
1279
  }
1280
1280
  },
1281
1281
  "node_modules/lodash": {
1282
- "version": "4.17.21",
1283
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
1284
- "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
1282
+ "version": "4.17.23",
1283
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
1284
+ "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
1285
1285
  "license": "MIT"
1286
1286
  },
1287
1287
  "node_modules/lru-cache": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "matterbridge",
3
- "version": "3.5.1-dev-20260121-22e98b4",
3
+ "version": "3.5.1-dev-20260123-4fcee18",
4
4
  "description": "Matterbridge plugin manager for Matter",
5
5
  "author": "https://github.com/Luligu",
6
6
  "license": "Apache-2.0",
@@ -104,7 +104,7 @@
104
104
  }
105
105
  },
106
106
  "dependencies": {
107
- "@matter/main": "0.16.5",
107
+ "@matter/main": "0.16.6",
108
108
  "@matterbridge/dgram": "0.0.2",
109
109
  "@matterbridge/jest-utils": "0.0.1",
110
110
  "@matterbridge/utils": "0.0.1",
@@ -118,12 +118,12 @@
118
118
  "ws": "8.19.0"
119
119
  },
120
120
  "build": {
121
- "sha": "e333c4a7e80ef23848f41deb21562ec89e3ab90b",
122
- "sha7": "e333c4a",
123
- "event": "schedule",
121
+ "sha": "4fcee18598592191a42c1f07464efd96fbf0288f",
122
+ "sha7": "4fcee18",
123
+ "event": "workflow_dispatch",
124
124
  "workflow": "Daily Dev Publish to npm",
125
125
  "type": "branch",
126
- "name": "main",
126
+ "name": "dev",
127
127
  "docker": "false"
128
128
  }
129
129
  }