matterbridge 3.1.6-dev-20250721-75fab6b → 3.1.7-dev-20250723-aab81fe

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.
@@ -0,0 +1,113 @@
1
+ import os from 'node:os';
2
+ import { BLUE, CYAN, db, RED, YELLOW } from 'node-ansi-logger';
3
+ import { Dgram } from './dgram.js';
4
+ export const MDNS_MULTICAST_IPV4_ADDRESS = '224.0.0.251';
5
+ export const MDNS_MULTICAST_IPV6_ADDRESS = 'ff02::fb';
6
+ export const MDNS_MULTICAST_PORT = 5353;
7
+ export const COAP_MULTICAST_IPV4_ADDRESS = '224.0.1.187';
8
+ export const COAP_MULTICAST_IPV6_ADDRESS = 'ff02::fd';
9
+ export const COAP_MULTICAST_PORT = 5683;
10
+ export class Multicast extends Dgram {
11
+ multicastAddress;
12
+ multicastPort;
13
+ joinedInterfaces = [];
14
+ constructor(name, multicastAddress, multicastPort, socketType, reuseAddr = true, interfaceName, interfaceAddress) {
15
+ super(name, socketType, reuseAddr, interfaceName, interfaceAddress);
16
+ this.multicastAddress = multicastAddress;
17
+ this.multicastPort = multicastPort;
18
+ }
19
+ start() {
20
+ if (this.socketType === 'udp4') {
21
+ this.log.debug(`Starting ipv4 dgram multicast socket...`);
22
+ this.interfaceAddress = this.interfaceAddress ?? this.getIpv4InterfaceAddress(this.interfaceName);
23
+ }
24
+ else {
25
+ this.log.debug(`Starting ipv6 dgram multicast socket...`);
26
+ this.interfaceAddress = this.interfaceAddress ?? this.getIpv6InterfaceAddress(this.interfaceName);
27
+ }
28
+ this.log.debug(`Binding dgram multicast socket to ${BLUE}${this.interfaceAddress}${db}:${BLUE}${this.multicastPort}${db} on interface ${CYAN}${this.interfaceName}${db}`);
29
+ this.socket.bind(this.multicastPort, this.interfaceAddress, () => {
30
+ const address = this.socket.address();
31
+ this.log.debug(`Dgram multicast socket bound to ${BLUE}${address.family}${db} ${BLUE}${address.address}${db}:${BLUE}${address.port}${db}`);
32
+ this.emit('bound', address);
33
+ });
34
+ }
35
+ onListening(address) {
36
+ this.log.debug(`Dgram multicast socket listening on ${BLUE}${address.family}${db} ${BLUE}${address.address}${db}:${BLUE}${address.port}${db}`);
37
+ this.socket.setBroadcast(true);
38
+ this.log.debug(`Dgram multicast socket broadcast enabled`);
39
+ this.socket.setTTL(255);
40
+ this.log.debug(`Dgram multicast socket TTL set to 255`);
41
+ this.socket.setMulticastTTL(255);
42
+ this.log.debug(`Dgram multicast socket multicast TTL set to 255`);
43
+ this.socket.setMulticastLoopback(true);
44
+ this.log.debug(`Dgram multicast socket multicast loopback enabled`);
45
+ Object.entries(os.networkInterfaces()).forEach(([name, interfaces]) => {
46
+ this.log.debug(`Dgram multicast socket processing interface ${CYAN}${name}${db}`);
47
+ if (!interfaces)
48
+ return;
49
+ if (this.interfaceName && name !== this.interfaceName)
50
+ return;
51
+ let iface;
52
+ let membershipInterface;
53
+ const ifaceIpv4 = interfaces.find((iface) => iface.family === 'IPv4' && this.socketType === 'udp4');
54
+ if (ifaceIpv4) {
55
+ iface = ifaceIpv4;
56
+ membershipInterface = ifaceIpv4.address;
57
+ }
58
+ const ifaceIpv6 = interfaces.find((iface) => iface.family === 'IPv6' && this.socketType === 'udp6');
59
+ if (ifaceIpv6) {
60
+ iface = ifaceIpv6;
61
+ membershipInterface = ifaceIpv6.address + (ifaceIpv6.scopeid !== undefined ? (process.platform === 'win32' ? '%' + String(ifaceIpv6.scopeid) : '%' + name) : '');
62
+ }
63
+ const ifaceUla = interfaces.find((iface) => iface.family === 'IPv6' && this.socketType === 'udp6' && iface.address.startsWith('fd'));
64
+ if (ifaceUla) {
65
+ iface = ifaceUla;
66
+ membershipInterface = ifaceUla.address + (ifaceUla.scopeid !== undefined ? (process.platform === 'win32' ? '%' + String(ifaceUla.scopeid) : '%' + name) : '');
67
+ }
68
+ const ifaceUla64 = interfaces.find((iface) => iface.family === 'IPv6' && this.socketType === 'udp6' && iface.address.startsWith('fd') && iface.netmask === 'ffff:ffff:ffff:ffff::');
69
+ if (ifaceUla64) {
70
+ iface = ifaceUla64;
71
+ membershipInterface = ifaceUla64.address + (ifaceUla64.scopeid !== undefined ? (process.platform === 'win32' ? '%' + String(ifaceUla64.scopeid) : '%' + name) : '');
72
+ }
73
+ const ifaceLinkLocal = interfaces.find((iface) => iface.family === 'IPv6' && this.socketType === 'udp6' && iface.address.startsWith('fe80'));
74
+ if (ifaceLinkLocal) {
75
+ iface = ifaceLinkLocal;
76
+ membershipInterface = ifaceLinkLocal.address + (ifaceLinkLocal.scopeid !== undefined ? (process.platform === 'win32' ? '%' + String(ifaceLinkLocal.scopeid) : '%' + name) : '');
77
+ }
78
+ if (iface && membershipInterface) {
79
+ try {
80
+ this.socket.addMembership(this.multicastAddress, membershipInterface);
81
+ this.joinedInterfaces.push(membershipInterface);
82
+ this.log.debug(`Dgram multicast socket joined multicast group ${BLUE}${this.multicastAddress}${db} on interface ${CYAN}${name}${db} ${BLUE}${iface.family}${db} ${BLUE}${iface.address}${db} ${BLUE}${iface.scopeid}${db} >>> ${YELLOW}${membershipInterface}${db}`);
83
+ }
84
+ catch (error) {
85
+ this.log.debug(`Dgram multicast socket failed to join multicast group ${BLUE}${this.multicastAddress}${db} on interface ${CYAN}${name}${db} ${BLUE}${iface.family}${db} ${BLUE}${iface.address}${db} ${BLUE}${iface.scopeid}${db} >>> ${RED}${membershipInterface}${db}: ${error instanceof Error ? error.message : error}`);
86
+ }
87
+ }
88
+ });
89
+ let interfaceAddress = this.interfaceAddress;
90
+ if (this.socketType === 'udp6' && this.interfaceAddress === '::') {
91
+ interfaceAddress = '::' + this.getIpv6ScopeIdForAllInterfacesAddress();
92
+ }
93
+ this.log.debug(`Dgram multicast socket setting multicastInterface to ${BLUE}${interfaceAddress}${db} for ${BLUE}${address.family}${db} ${BLUE}${address.address}${db}:${BLUE}${address.port}${db}`);
94
+ this.socket.setMulticastInterface(interfaceAddress);
95
+ this.log.debug(`Dgram multicast socket multicastInterface set to ${BLUE}${interfaceAddress}${db}`);
96
+ this.emit('ready', address);
97
+ }
98
+ stop() {
99
+ this.log.debug('Stopping dgram multicast socket...');
100
+ this.joinedInterfaces.forEach((membershipInterface) => {
101
+ try {
102
+ this.socket.dropMembership(this.multicastAddress, membershipInterface);
103
+ this.log.debug(`Dgram multicast socket dropped multicast group ${BLUE}${this.multicastAddress}${db} on interface ${YELLOW}${membershipInterface}${db}`);
104
+ }
105
+ catch (error) {
106
+ this.log.debug(`Dgram multicast socket failed to drop multicast group ${BLUE}${this.multicastAddress}${db} on interface ${RED}${membershipInterface}${db}: ${error}`);
107
+ }
108
+ });
109
+ this.joinedInterfaces = [];
110
+ this.socket.close();
111
+ this.log.debug('Stopped dgram multicast socket.');
112
+ }
113
+ }
@@ -0,0 +1,37 @@
1
+ import { BLUE, db } from 'node-ansi-logger';
2
+ import { Dgram } from './dgram.js';
3
+ export class Unicast extends Dgram {
4
+ port;
5
+ constructor(name, socketType, reuseAddr = true, interfaceName, interfaceAddress, port) {
6
+ super(name, socketType, reuseAddr, interfaceName, interfaceAddress);
7
+ this.port = port;
8
+ }
9
+ start() {
10
+ if (this.socketType === 'udp4') {
11
+ this.log.debug(`Starting ipv4 dgram unicast socket...`);
12
+ this.interfaceAddress = this.interfaceAddress ?? (this.interfaceName ? this.getIpv4InterfaceAddress(this.interfaceName) : undefined);
13
+ }
14
+ else {
15
+ this.log.debug(`Starting ipv6 dgram unicast socket...`);
16
+ this.interfaceAddress = this.interfaceAddress ?? (this.interfaceName ? this.getIpv6InterfaceAddress(this.interfaceName) : undefined);
17
+ }
18
+ this.interfaceNetmask = this.interfaceAddress ? this.getNetmask(this.interfaceAddress) : undefined;
19
+ this.socket.bind(this.port, this.interfaceAddress, () => {
20
+ const address = this.socket.address();
21
+ this.log.debug(`Dgram unicast socket bound to ${BLUE}${address.family}${db} ${BLUE}${address.address}${db}:${BLUE}${address.port}${db}`);
22
+ this.emit('bound', address);
23
+ });
24
+ }
25
+ onListening(address) {
26
+ this.log.debug(`Dgram unicast socket listening on ${BLUE}${address.family}${db} ${BLUE}${address.address}${db}:${BLUE}${address.port}${db}`);
27
+ this.socket.setBroadcast(true);
28
+ this.log.debug(`Dgram unicast socket broadcast enabled`);
29
+ this.emit('ready', address);
30
+ this.onReady(address);
31
+ }
32
+ stop() {
33
+ this.log.debug('Stopping dgram unicast socket...');
34
+ this.socket.close();
35
+ this.log.debug('Stopped dgram unicast socket.');
36
+ }
37
+ }
@@ -93,7 +93,6 @@ export class Matterbridge extends EventEmitter {
93
93
  restartMode = '';
94
94
  profile = getParameter('profile');
95
95
  shutdown = false;
96
- edge = true;
97
96
  failCountLimit = hasParameter('shelly') ? 600 : 120;
98
97
  log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
99
98
  matterbridgeLoggerFile = 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.log';
@@ -108,6 +107,7 @@ export class Matterbridge extends EventEmitter {
108
107
  initialized = false;
109
108
  execRunningCount = 0;
110
109
  startMatterInterval;
110
+ startMatterIntervalMs = 1000;
111
111
  checkUpdateInterval;
112
112
  checkUpdateTimeout;
113
113
  configureTimeout;
@@ -985,7 +985,11 @@ export class Matterbridge extends EventEmitter {
985
985
  for (const plugin of this.plugins.array()) {
986
986
  if (plugin.error || !plugin.enabled)
987
987
  continue;
988
+ const registeredDevices = plugin.registeredDevices;
989
+ const addedDevices = plugin.addedDevices;
988
990
  await this.plugins.shutdown(plugin, 'unregistering all devices and shutting down...', false, true);
991
+ plugin.registeredDevices = registeredDevices;
992
+ plugin.addedDevices = addedDevices;
989
993
  await this.removeAllBridgedEndpoints(plugin.name, 100);
990
994
  }
991
995
  this.log.debug('Waiting for the MessageExchange to finish...');
@@ -1274,7 +1278,7 @@ export class Matterbridge extends EventEmitter {
1274
1278
  }, 60 * 1000).unref();
1275
1279
  this.emit('bridge_started');
1276
1280
  this.log.notice('Matterbridge bridge started successfully');
1277
- }, 1000);
1281
+ }, this.startMatterIntervalMs);
1278
1282
  }
1279
1283
  async startChildbridge(delay = 1000) {
1280
1284
  if (!this.matterStorageManager)
@@ -1367,7 +1371,7 @@ export class Matterbridge extends EventEmitter {
1367
1371
  }
1368
1372
  this.emit('childbridge_started');
1369
1373
  this.log.notice('Matterbridge childbridge started successfully');
1370
- }, 1000);
1374
+ }, this.startMatterIntervalMs);
1371
1375
  }
1372
1376
  async startController() {
1373
1377
  }
@@ -822,6 +822,15 @@ export class MatterbridgeEndpoint extends Endpoint {
822
822
  });
823
823
  return this;
824
824
  }
825
+ createBaseFanControlClusterServer(fanMode = FanControl.FanMode.Off, fanModeSequence = FanControl.FanModeSequence.OffLowMedHigh, percentSetting = 0, percentCurrent = 0) {
826
+ this.behaviors.require(FanControlServer, {
827
+ fanMode,
828
+ fanModeSequence,
829
+ percentSetting,
830
+ percentCurrent,
831
+ });
832
+ return this;
833
+ }
825
834
  createMultiSpeedFanControlClusterServer(fanMode = FanControl.FanMode.Off, fanModeSequence = FanControl.FanModeSequence.OffLowMedHighAuto, percentSetting = 0, percentCurrent = 0, speedMax = 10, speedSetting = 0, speedCurrent = 0) {
826
835
  this.behaviors.require(MatterbridgeFanControlServer.with(FanControl.Feature.MultiSpeed, FanControl.Feature.Auto, FanControl.Feature.Step), {
827
836
  fanMode,
@@ -851,15 +860,6 @@ export class MatterbridgeEndpoint extends Endpoint {
851
860
  });
852
861
  return this;
853
862
  }
854
- createBaseFanControlClusterServer(fanMode = FanControl.FanMode.Off, fanModeSequence = FanControl.FanModeSequence.OffLowMedHigh, percentSetting = 0, percentCurrent = 0) {
855
- this.behaviors.require(FanControlServer, {
856
- fanMode,
857
- fanModeSequence,
858
- percentSetting,
859
- percentCurrent,
860
- });
861
- return this;
862
- }
863
863
  createDefaultHepaFilterMonitoringClusterServer(condition = 100, changeIndication = ResourceMonitoring.ChangeIndication.Ok, inPlaceIndicator = true, lastChangedTime = null, replacementProductList = []) {
864
864
  this.behaviors.require(MatterbridgeHepaFilterMonitoringServer.with(ResourceMonitoring.Feature.Condition, ResourceMonitoring.Feature.Warning, ResourceMonitoring.Feature.ReplacementProductList), {
865
865
  condition,
@@ -536,6 +536,7 @@ export function getDefaultOperationalStateClusterServer(operationalState = Opera
536
536
  return optionsFor(MatterbridgeOperationalStateServer, {
537
537
  phaseList: [],
538
538
  currentPhase: null,
539
+ countdownTime: null,
539
540
  operationalStateList: [
540
541
  { operationalStateId: OperationalState.OperationalStateEnum.Stopped, operationalStateLabel: 'Stopped' },
541
542
  { operationalStateId: OperationalState.OperationalStateEnum.Running, operationalStateLabel: 'Running' },
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "matterbridge",
3
- "version": "3.1.6-dev-20250721-75fab6b",
3
+ "version": "3.1.7-dev-20250723-aab81fe",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "matterbridge",
9
- "version": "3.1.6-dev-20250721-75fab6b",
9
+ "version": "3.1.7-dev-20250723-aab81fe",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "@matter/main": "0.15.1",
@@ -19,7 +19,9 @@
19
19
  "ws": "8.18.3"
20
20
  },
21
21
  "bin": {
22
- "matterbridge": "bin/matterbridge.js"
22
+ "matterbridge": "bin/matterbridge.js",
23
+ "mb_coap": "bin/mb_coap.js",
24
+ "mb_mdns": "bin/mb_mdns.js"
23
25
  },
24
26
  "engines": {
25
27
  "node": ">=18.0.0 <19.0.0 || >=20.0.0 <21.0.0 || >=22.0.0 <23.0.0 || >=24.0.0 <25.0.0"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "matterbridge",
3
- "version": "3.1.6-dev-20250721-75fab6b",
3
+ "version": "3.1.7-dev-20250723-aab81fe",
4
4
  "description": "Matterbridge plugin manager for Matter",
5
5
  "author": "https://github.com/Luligu",
6
6
  "license": "Apache-2.0",
@@ -42,7 +42,9 @@
42
42
  "type": "module",
43
43
  "main": "dist/index.js",
44
44
  "bin": {
45
- "matterbridge": "bin/matterbridge.js"
45
+ "matterbridge": "bin/matterbridge.js",
46
+ "mb_mdns": "bin/mb_mdns.js",
47
+ "mb_coap": "bin/mb_coap.js"
46
48
  },
47
49
  "engines": {
48
50
  "node": ">=18.0.0 <19.0.0 || >=20.0.0 <21.0.0 || >=22.0.0 <23.0.0 || >=24.0.0 <25.0.0"