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.
- package/CHANGELOG.md +36 -3
- package/README-DOCKER.md +35 -0
- package/bin/mb_coap.js +2 -0
- package/bin/mb_mdns.js +2 -0
- package/dist/dgram/coap.js +252 -0
- package/dist/dgram/dgram.js +237 -0
- package/dist/dgram/mb_coap.js +52 -0
- package/dist/dgram/mb_mdns.js +60 -0
- package/dist/dgram/mdns.js +595 -0
- package/dist/dgram/multicast.js +113 -0
- package/dist/dgram/unicast.js +37 -0
- package/dist/matterbridge.js +7 -3
- package/dist/matterbridgeEndpoint.js +9 -9
- package/dist/matterbridgeEndpointHelpers.js +1 -0
- package/npm-shrinkwrap.json +5 -3
- package/package.json +4 -2
|
@@ -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
|
+
}
|
package/dist/matterbridge.js
CHANGED
|
@@ -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
|
-
},
|
|
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
|
-
},
|
|
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' },
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "matterbridge",
|
|
3
|
-
"version": "3.1.
|
|
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.
|
|
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.
|
|
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"
|