@matterbridge/core 3.5.4-edge-20260211-1ea4e31 → 3.5.4

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/dist/cli.js CHANGED
@@ -128,6 +128,10 @@ function help() {
128
128
  --enable [plugin name]: enable the globally installed plugin with the given name
129
129
  --disable [plugin path]: disable the plugin from the given absolute or relative path
130
130
  --disable [plugin name]: disable the globally installed plugin with the given name
131
+ --list: list the registered plugins
132
+ --loginterfaces: log the network interfaces (usefull for finding the name of the interface to use with -mdnsinterface option)
133
+ --logstorage: log the node storage
134
+ --systemcheck: perform a system check (check Node.js version and network interfaces)
131
135
 
132
136
  Reset Commands:
133
137
  --reset: remove the commissioning for Matterbridge (bridge mode and childbridge mode). Shutdown Matterbridge before using it!
@@ -148,9 +152,6 @@ function help() {
148
152
  --filelogger: enable the matterbridge file logger (matterbridge.log)
149
153
  --matterlogger: set the matter.js logger level: debug | info | notice | warn | error | fatal (default info)
150
154
  --matterfilelogger: enable the matter.js file logger (matter.log)
151
- --list: list the registered plugins
152
- --loginterfaces: log the network interfaces (usefull for finding the name of the interface to use with -mdnsinterface option)
153
- --logstorage: log the node storage
154
155
  --sudo: force the use of sudo to install or update packages if the internal logic fails
155
156
  --nosudo: force not to use sudo to install or update packages if the internal logic fails
156
157
  --norestore: force not to automatically restore the matterbridge node storage and the matter storage from backup if it is corrupted
package/dist/frontend.js CHANGED
@@ -206,27 +206,18 @@ export class Frontend extends EventEmitter {
206
206
  this.emit('server_error', error);
207
207
  return;
208
208
  }
209
- if (hasParameter('ingress')) {
210
- this.httpServer.listen(this.port, '0.0.0.0', () => {
211
- this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
212
- this.listening = true;
213
- this.emit('server_listening', 'http', this.port, '0.0.0.0');
214
- });
215
- }
216
- else {
217
- this.httpServer.listen(this.port, getParameter('bind'), () => {
218
- const addr = this.httpServer?.address();
219
- if (addr && typeof addr !== 'string') {
220
- this.log.info(`The frontend http server is bound to ${addr.family} ${addr.address}:${addr.port}`);
221
- }
222
- if (this.matterbridge.systemInformation.ipv4Address !== '' && !getParameter('bind'))
223
- this.log.info(`The frontend http server is listening on ${UNDERLINE}http://${this.matterbridge.systemInformation.ipv4Address}:${this.port}${UNDERLINEOFF}${rs}`);
224
- if (this.matterbridge.systemInformation.ipv6Address !== '' && !getParameter('bind'))
225
- this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
226
- this.listening = true;
227
- this.emit('server_listening', 'http', this.port);
228
- });
229
- }
209
+ this.httpServer.listen(this.port, getParameter('bind'), () => {
210
+ const addr = this.httpServer?.address();
211
+ if (addr && typeof addr !== 'string') {
212
+ this.log.info(`The frontend http server is bound to ${addr.family} ${addr.address}:${addr.port}`);
213
+ }
214
+ if (this.matterbridge.systemInformation.ipv4Address !== '' && !getParameter('bind'))
215
+ this.log.info(`The frontend http server is listening on ${UNDERLINE}http://${this.matterbridge.systemInformation.ipv4Address}:${this.port}${UNDERLINEOFF}${rs}`);
216
+ if (this.matterbridge.systemInformation.ipv6Address !== '' && !getParameter('bind'))
217
+ this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
218
+ this.listening = true;
219
+ this.emit('server_listening', 'http', this.port);
220
+ });
230
221
  this.httpServer.on('upgrade', async (req, socket, head) => {
231
222
  try {
232
223
  if ((req.headers.upgrade || '').toLowerCase() !== 'websocket') {
@@ -344,27 +335,18 @@ export class Frontend extends EventEmitter {
344
335
  this.emit('server_error', error);
345
336
  return;
346
337
  }
347
- if (hasParameter('ingress')) {
348
- this.httpsServer.listen(this.port, '0.0.0.0', () => {
349
- this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
350
- this.listening = true;
351
- this.emit('server_listening', 'https', this.port, '0.0.0.0');
352
- });
353
- }
354
- else {
355
- this.httpsServer.listen(this.port, getParameter('bind'), () => {
356
- const addr = this.httpsServer?.address();
357
- if (addr && typeof addr !== 'string') {
358
- this.log.info(`The frontend https server is bound to ${addr.family} ${addr.address}:${addr.port}`);
359
- }
360
- if (this.matterbridge.systemInformation.ipv4Address !== '' && !getParameter('bind'))
361
- this.log.info(`The frontend https server is listening on ${UNDERLINE}https://${this.matterbridge.systemInformation.ipv4Address}:${this.port}${UNDERLINEOFF}${rs}`);
362
- if (this.matterbridge.systemInformation.ipv6Address !== '' && !getParameter('bind'))
363
- this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
364
- this.listening = true;
365
- this.emit('server_listening', 'https', this.port);
366
- });
367
- }
338
+ this.httpsServer.listen(this.port, getParameter('bind'), () => {
339
+ const addr = this.httpsServer?.address();
340
+ if (addr && typeof addr !== 'string') {
341
+ this.log.info(`The frontend https server is bound to ${addr.family} ${addr.address}:${addr.port}`);
342
+ }
343
+ if (this.matterbridge.systemInformation.ipv4Address !== '' && !getParameter('bind'))
344
+ this.log.info(`The frontend https server is listening on ${UNDERLINE}https://${this.matterbridge.systemInformation.ipv4Address}:${this.port}${UNDERLINEOFF}${rs}`);
345
+ if (this.matterbridge.systemInformation.ipv6Address !== '' && !getParameter('bind'))
346
+ this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
347
+ this.listening = true;
348
+ this.emit('server_listening', 'https', this.port);
349
+ });
368
350
  this.httpsServer.on('upgrade', async (req, socket, head) => {
369
351
  try {
370
352
  if ((req.headers.upgrade || '').toLowerCase() !== 'websocket') {
@@ -244,10 +244,11 @@ export async function stopMatterbridge(cleanupPause = 10, destroyPause = 250) {
244
244
  await destroyMatterbridgeEnvironment(cleanupPause, destroyPause);
245
245
  }
246
246
  export async function createMatterbridgeEnvironment(name) {
247
+ log = new AnsiLogger({ logName: name, logTimestampFormat: 4, logLevel: "debug" });
247
248
  matterbridge = await Matterbridge.loadInstance(false);
248
249
  expect(matterbridge).toBeDefined();
249
250
  expect(matterbridge).toBeInstanceOf(Matterbridge);
250
- matterbridge.matterbridgeVersion = '3.5.3';
251
+ matterbridge.matterbridgeVersion = '3.5.4';
251
252
  matterbridge.bridgeMode = 'bridge';
252
253
  matterbridge.rootDirectory = path.join('jest', name);
253
254
  matterbridge.homeDirectory = path.join('jest', name);
@@ -255,7 +256,6 @@ export async function createMatterbridgeEnvironment(name) {
255
256
  matterbridge.matterbridgePluginDirectory = path.join('jest', name, 'Matterbridge');
256
257
  matterbridge.matterbridgeCertDirectory = path.join('jest', name, '.mattercert');
257
258
  matterbridge.log.logLevel = "debug";
258
- log = new AnsiLogger({ logName: name, logTimestampFormat: 4, logLevel: "debug" });
259
259
  frontend = matterbridge.frontend;
260
260
  plugins = matterbridge.plugins;
261
261
  devices = matterbridge.devices;
@@ -74,6 +74,7 @@ export declare class Matterbridge extends EventEmitter<MatterbridgeEvents> {
74
74
  private readonly startMatterIntervalMs;
75
75
  private checkUpdateInterval;
76
76
  private checkUpdateTimeout;
77
+ private systemCheckTimeout;
77
78
  private configureTimeout;
78
79
  private reachabilityTimeout;
79
80
  private sigintHandler;
@@ -15,7 +15,7 @@ import { Endpoint, ServerNode } from '@matter/node';
15
15
  import { DeviceTypeId, VendorId } from '@matter/types/datatype';
16
16
  import { AggregatorEndpoint } from '@matter/node/endpoints';
17
17
  import { BasicInformationServer } from '@matter/node/behaviors/basic-information';
18
- import { copyDirectory, createDirectory, formatBytes, formatPercent, formatUptime, getIntParameter, getParameter, hasParameter, isValidNumber, isValidObject, isValidString, parseVersionString, } from '@matterbridge/utils';
18
+ import { copyDirectory, createDirectory, formatBytes, formatPercent, formatUptime, getIntParameter, getParameter, hasParameter, isValidNumber, isValidObject, isValidString, parseVersionString, excludedInterfaceNamePattern, } from '@matterbridge/utils';
19
19
  import { dev, MATTER_LOGGER_FILE, MATTER_STORAGE_NAME, MATTERBRIDGE_LOGGER_FILE, NODE_STORAGE_DIR, plg, typ } from '@matterbridge/types';
20
20
  import { BroadcastServer } from '@matterbridge/thread';
21
21
  import { PluginManager } from './pluginManager.js';
@@ -93,6 +93,7 @@ export class Matterbridge extends EventEmitter {
93
93
  startMatterIntervalMs = 1000;
94
94
  checkUpdateInterval;
95
95
  checkUpdateTimeout;
96
+ systemCheckTimeout;
96
97
  configureTimeout;
97
98
  reachabilityTimeout;
98
99
  sigintHandler;
@@ -541,7 +542,8 @@ export class Matterbridge extends EventEmitter {
541
542
  !hasParameter('enable') &&
542
543
  !hasParameter('disable') &&
543
544
  !hasParameter('reset') &&
544
- !hasParameter('factoryreset')) {
545
+ !hasParameter('factoryreset') &&
546
+ !hasParameter('systemcheck')) {
545
547
  this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm...`);
546
548
  const { spawnCommand } = await import('./spawn.js');
547
549
  if (await spawnCommand('npm', ['install', '-g', `${plugin.name}${plugin.version.includes('-dev-') ? '@dev' : ''}`, '--omit=dev', '--verbose'], 'install', plugin.name)) {
@@ -635,6 +637,12 @@ export class Matterbridge extends EventEmitter {
635
637
  this.shutdown = true;
636
638
  return;
637
639
  }
640
+ if (hasParameter('systemcheck')) {
641
+ const { systemCheck } = await import('@matterbridge/thread');
642
+ await systemCheck();
643
+ this.shutdown = true;
644
+ return;
645
+ }
638
646
  if (getParameter('add')) {
639
647
  this.log.debug(`Adding plugin ${getParameter('add')}`);
640
648
  await this.plugins.add(getParameter('add'));
@@ -710,6 +718,11 @@ export class Matterbridge extends EventEmitter {
710
718
  this.shutdown = true;
711
719
  return;
712
720
  }
721
+ clearTimeout(this.systemCheckTimeout);
722
+ this.systemCheckTimeout = setTimeout(async () => {
723
+ const { createESMWorker } = await import('@matterbridge/thread');
724
+ createESMWorker('SystemCheck', this.resolveWorkerDistFilePath('workerSystemCheck.js'));
725
+ }, 120 * 1000).unref();
713
726
  clearTimeout(this.checkUpdateTimeout);
714
727
  this.checkUpdateTimeout = setTimeout(async () => {
715
728
  const { createESMWorker } = await import('@matterbridge/thread');
@@ -828,7 +841,6 @@ export class Matterbridge extends EventEmitter {
828
841
  this.sigtermHandler = undefined;
829
842
  }
830
843
  async logNodeAndSystemInfo() {
831
- const excludedInterfaceNamePattern = /(tailscale|wireguard|openvpn|zerotier|hamachi|\bwg\d+\b|\btun\d+\b|\btap\d+\b|\butun\d+\b|docker|podman|\bveth[a-z0-9]*\b|\bbr-[a-z0-9]+\b|cni|kube|flannel|calico|virbr\d*\b|vmware|vmnet\d*\b|virtualbox|vboxnet\d*\b|teredo|isatap)/i;
832
844
  const networkInterfaces = os.networkInterfaces();
833
845
  this.systemInformation.interfaceName = '';
834
846
  this.systemInformation.ipv4Address = '';
@@ -919,7 +931,6 @@ export class Matterbridge extends EventEmitter {
919
931
  }
920
932
  }
921
933
  else {
922
- this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
923
934
  const { createESMWorker } = await import('@matterbridge/thread');
924
935
  createESMWorker('NpmGlobalPrefix', this.resolveWorkerDistFilePath('workerGlobalPrefix.js'));
925
936
  }
@@ -1050,6 +1061,11 @@ export class Matterbridge extends EventEmitter {
1050
1061
  this.startMatterInterval = undefined;
1051
1062
  this.log.debug('Start matter interval cleared');
1052
1063
  }
1064
+ if (this.systemCheckTimeout) {
1065
+ clearTimeout(this.systemCheckTimeout);
1066
+ this.systemCheckTimeout = undefined;
1067
+ this.log.debug('System check timeout cleared');
1068
+ }
1053
1069
  if (this.checkUpdateTimeout) {
1054
1070
  clearTimeout(this.checkUpdateTimeout);
1055
1071
  this.checkUpdateTimeout = undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@matterbridge/core",
3
- "version": "3.5.4-edge-20260211-1ea4e31",
3
+ "version": "3.5.4",
4
4
  "description": "Matterbridge core library",
5
5
  "author": "https://github.com/Luligu",
6
6
  "homepage": "https://matterbridge.io/",
@@ -126,10 +126,10 @@
126
126
  },
127
127
  "dependencies": {
128
128
  "@matter/main": "0.16.8",
129
- "@matterbridge/dgram": "3.5.4-edge-20260211-1ea4e31",
130
- "@matterbridge/thread": "3.5.4-edge-20260211-1ea4e31",
131
- "@matterbridge/types": "3.5.4-edge-20260211-1ea4e31",
132
- "@matterbridge/utils": "3.5.4-edge-20260211-1ea4e31",
129
+ "@matterbridge/dgram": "3.5.4",
130
+ "@matterbridge/thread": "3.5.4",
131
+ "@matterbridge/types": "3.5.4",
132
+ "@matterbridge/utils": "3.5.4",
133
133
  "archiver": "7.0.1",
134
134
  "express": "5.2.1",
135
135
  "glob": "13.0.1",
package/dist/mb_coap.d.ts DELETED
@@ -1 +0,0 @@
1
- export {};
package/dist/mb_coap.js DELETED
@@ -1,51 +0,0 @@
1
- import { COAP_MULTICAST_IPV4_ADDRESS, COAP_MULTICAST_IPV6_ADDRESS, COAP_MULTICAST_PORT, Coap, COAP_OPTION_URI_PATH } from '@matterbridge/dgram';
2
- {
3
- const coapIpv4 = new Coap('CoAP Server udp4', COAP_MULTICAST_IPV4_ADDRESS, COAP_MULTICAST_PORT, 'udp4', true);
4
- const coapIpv6 = new Coap('CoAP Server udp6', COAP_MULTICAST_IPV6_ADDRESS, COAP_MULTICAST_PORT, 'udp6', true);
5
- coapIpv4.listNetworkInterfaces();
6
- function cleanupAndLogAndExit() {
7
- if (process.argv.includes('--coap-udp4'))
8
- coapIpv4.stop();
9
- if (process.argv.includes('--coap-udp6'))
10
- coapIpv6.stop();
11
- process.exit(0);
12
- }
13
- const requestUdp4 = () => {
14
- coapIpv4.sendRequest(32000, [
15
- { number: COAP_OPTION_URI_PATH, value: Buffer.from('cit') },
16
- { number: COAP_OPTION_URI_PATH, value: Buffer.from('d') },
17
- ], {}, undefined, COAP_MULTICAST_IPV4_ADDRESS, COAP_MULTICAST_PORT);
18
- };
19
- const requestUdp6 = () => {
20
- coapIpv6.sendRequest(32000, [
21
- { number: COAP_OPTION_URI_PATH, value: Buffer.from('cit') },
22
- { number: COAP_OPTION_URI_PATH, value: Buffer.from('d') },
23
- ], {}, undefined, COAP_MULTICAST_IPV6_ADDRESS, COAP_MULTICAST_PORT);
24
- };
25
- process.on('SIGINT', () => {
26
- cleanupAndLogAndExit();
27
- });
28
- coapIpv4.start();
29
- coapIpv4.on('ready', (address) => {
30
- coapIpv4.log.info(`coapIpv4 server ready on ${address.family} ${address.address}:${address.port}`);
31
- if (!process.argv.includes('--coap-request'))
32
- return;
33
- requestUdp4();
34
- setInterval(() => {
35
- requestUdp4();
36
- }, 10000).unref();
37
- });
38
- coapIpv6.start();
39
- coapIpv6.on('ready', (address) => {
40
- coapIpv6.log.info(`coapIpv6 server ready on ${address.family} ${address.address}:${address.port}`);
41
- if (!process.argv.includes('--coap-request'))
42
- return;
43
- requestUdp6();
44
- setInterval(() => {
45
- requestUdp6();
46
- }, 10000).unref();
47
- });
48
- setTimeout(() => {
49
- cleanupAndLogAndExit();
50
- }, 600000);
51
- }
@@ -1,10 +0,0 @@
1
- export declare function checkHealth(url: string, timeoutMs: number): Promise<boolean>;
2
- export declare function fetchHealth(url: string, timeoutMs: number): Promise<{
3
- ok: boolean;
4
- statusCode: number;
5
- body: string;
6
- json?: unknown;
7
- }>;
8
- export declare function mbHealthExitCode(url: string, timeoutMs: number): Promise<number>;
9
- export declare function mbHealthCli(url: string, timeoutMs: number, exitFn?: (code: number) => never | void): Promise<void>;
10
- export declare function mbHealthMain(exitFn?: (code: number) => never | void): Promise<void>;
package/dist/mb_health.js DELETED
@@ -1,77 +0,0 @@
1
- import http from 'node:http';
2
- import https from 'node:https';
3
- export function checkHealth(url, timeoutMs) {
4
- return fetchHealth(url, timeoutMs)
5
- .then(({ ok }) => ok)
6
- .catch(() => false);
7
- }
8
- export function fetchHealth(url, timeoutMs) {
9
- return new Promise((resolve) => {
10
- const parsedUrl = new URL(url);
11
- const requestImpl = parsedUrl.protocol === 'https:' ? https : http;
12
- const request = requestImpl.request({
13
- protocol: parsedUrl.protocol,
14
- hostname: parsedUrl.hostname,
15
- port: parsedUrl.port,
16
- path: parsedUrl.pathname + parsedUrl.search,
17
- method: 'GET',
18
- headers: {
19
- 'cache-control': 'no-store',
20
- 'accept': 'application/json',
21
- },
22
- }, (response) => {
23
- const chunks = [];
24
- response.on('data', (chunk) => {
25
- chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
26
- });
27
- response.on('end', () => {
28
- const statusCode = response.statusCode ?? 0;
29
- const ok = statusCode >= 200 && statusCode < 300;
30
- const body = Buffer.concat(chunks).toString('utf8');
31
- let json;
32
- try {
33
- if (body.trim().length > 0)
34
- json = JSON.parse(body);
35
- }
36
- catch {
37
- json = undefined;
38
- }
39
- resolve({ ok, statusCode, body, json });
40
- });
41
- response.on('error', () => {
42
- resolve({ ok: false, statusCode: response.statusCode ?? 0, body: '' });
43
- });
44
- });
45
- request.on('error', () => resolve({ ok: false, statusCode: 0, body: '' }));
46
- request.setTimeout(timeoutMs, () => {
47
- request.destroy();
48
- resolve({ ok: false, statusCode: 0, body: '' });
49
- });
50
- request.end();
51
- });
52
- }
53
- export async function mbHealthExitCode(url, timeoutMs) {
54
- try {
55
- const { ok } = await fetchHealth(url, timeoutMs);
56
- return ok ? 0 : 1;
57
- }
58
- catch {
59
- return 1;
60
- }
61
- }
62
- export async function mbHealthCli(url, timeoutMs, exitFn = process.exit) {
63
- const { ok, statusCode, body, json } = await fetchHealth(url, timeoutMs);
64
- if (json !== undefined) {
65
- console.log(JSON.stringify(json, null, 2));
66
- }
67
- else if (body) {
68
- console.log(body);
69
- }
70
- else {
71
- console.log(JSON.stringify({ ok, statusCode }, null, 2));
72
- }
73
- exitFn(ok ? 0 : 1);
74
- }
75
- export async function mbHealthMain(exitFn = process.exit) {
76
- await mbHealthCli('http://localhost:8283/health', 5000, exitFn);
77
- }
package/dist/mb_mdns.d.ts DELETED
@@ -1 +0,0 @@
1
- export {};
package/dist/mb_mdns.js DELETED
@@ -1,227 +0,0 @@
1
- import os from 'node:os';
2
- import { MDNS_MULTICAST_IPV4_ADDRESS, MDNS_MULTICAST_IPV6_ADDRESS, MDNS_MULTICAST_PORT, Mdns } from '@matterbridge/dgram';
3
- import { getIntParameter, getParameter, getStringArrayParameter, hasParameter } from '@matterbridge/utils';
4
- {
5
- if (hasParameter('h') || hasParameter('help')) {
6
- console.log(`Copyright (c) Matterbridge. All rights reserved. Version 1.0.0.\n`);
7
- console.log(`Usage: mb_mdns [options...]
8
-
9
- If no command line is provided, mb_mdns shows all incoming mDNS records on all interfaces (0.0.0.0 and ::).
10
-
11
- Options:
12
- -h, --help Show this help message and exit.
13
- --interfaceName <name> Network interface name to bind to (default all interfaces).
14
- --ipv4InterfaceAddress <address> IPv4 address of the network interface to bind to (default: 0.0.0.0).
15
- --ipv6InterfaceAddress <address> IPv6 address of the network interface to bind to (default: ::).
16
- --outgoingIpv4InterfaceAddress <address> Outgoing IPv4 address of the network interface (default first external address).
17
- --outgoingIpv6InterfaceAddress <address> Outgoing IPv6 address of the network interface (default first external address).
18
- --advertise <interval> Enable matterbridge mDNS advertisement each ms (default interval: 10000ms).
19
- --query <interval> Enable common mDNS services query each ms (default interval: 10000ms).
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).
22
- --noIpv4 Disable IPv4 mDNS server (default: enabled).
23
- --noIpv6 Disable IPv6 mDNS server (default: enabled).
24
- --no-timeout Disable automatic timeout of 10 minutes. Reflector mode disables timeout automatically.
25
- -d, --debug Enable debug logging (default: disabled).
26
- -v, --verbose Enable verbose logging (default: disabled).
27
- -s, --silent Enable silent mode, only log notices, warnings and errors.
28
-
29
- Examples:
30
- # Listen for Matter device commissioner service records only on eth0 interface
31
- mb_mdns --interfaceName eth0 --filter _matterc._udp
32
-
33
- # Listen for Matter device discovery service records only on eth0 interface
34
- mb_mdns --interfaceName eth0 --filter _matter._tcp
35
-
36
- # Listen for Matter commissioner and discovery service records on all interfaces
37
- mb_mdns --filter _matterc._udp _matter._tcp
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
-
42
- # Query for mDNS devices every 10s on a specific interface
43
- mb_mdns --interfaceName eth0 --query
44
-
45
- # Advertise _matterbridge._tcp.local every 5s with filter
46
- mb_mdns --advertise 5000 --filter _matterbridge._tcp.local
47
-
48
- # Query each 5s and listen for _matterbridge._tcp.local service records
49
- mb_mdns --query 5000 --filter _matterbridge._tcp.local
50
- `);
51
- process.exit(0);
52
- }
53
- const { default: pkg } = await import('../package.json', { with: { type: 'json' } });
54
- let mdnsIpv4QueryInterval;
55
- let mdnsIpv6QueryInterval;
56
- let mdnsIpv4AdvertiseInterval;
57
- let mdnsIpv6AdvertiseInterval;
58
- let mdnsIpv4 = undefined;
59
- let mdnsIpv6 = undefined;
60
- async function cleanupAndLogAndExit() {
61
- clearInterval(mdnsIpv4QueryInterval);
62
- clearInterval(mdnsIpv6QueryInterval);
63
- clearInterval(mdnsIpv4AdvertiseInterval);
64
- clearInterval(mdnsIpv6AdvertiseInterval);
65
- if (hasParameter('advertise')) {
66
- if (mdnsIpv4)
67
- advertise(mdnsIpv4, 0);
68
- if (mdnsIpv6)
69
- advertise(mdnsIpv6, 0);
70
- }
71
- await new Promise((resolve) => setTimeout(resolve, 250));
72
- mdnsIpv4?.stop();
73
- mdnsIpv6?.stop();
74
- mdnsIpv4?.logDevices();
75
- mdnsIpv6?.logDevices();
76
- await new Promise((resolve) => setTimeout(resolve, 250));
77
- }
78
- const query = (mdns) => {
79
- mdns.log.info('Sending mDNS query for common services...');
80
- try {
81
- mdns.sendQuery([
82
- { name: '_matterc._udp.local', type: 12, class: 1, unicastResponse: true },
83
- { name: '_matter._tcp.local', type: 12, class: 1, unicastResponse: true },
84
- { name: '_matterbridge._tcp.local', type: 12, class: 1, unicastResponse: true },
85
- { name: '_home-assistant._tcp.local', type: 12, class: 1, unicastResponse: true },
86
- { name: '_shelly._tcp.local', type: 12, class: 1, unicastResponse: true },
87
- { name: '_mqtt._tcp.local', type: 12, class: 1, unicastResponse: true },
88
- { name: '_http._tcp.local', type: 12, class: 1, unicastResponse: true },
89
- { name: '_googlecast._tcp.local', type: 12, class: 1, unicastResponse: true },
90
- { name: '_airplay._tcp.local', type: 12, class: 1, unicastResponse: true },
91
- { name: '_amzn-alexa._tcp.local', type: 12, class: 1, unicastResponse: true },
92
- { name: '_companion-link._tcp.local', type: 12, class: 1, unicastResponse: true },
93
- { name: '_hap._tcp.local', type: 12, class: 1, unicastResponse: true },
94
- { name: '_hap._udp.local', type: 12, class: 1, unicastResponse: true },
95
- { name: '_ipp._tcp.local', type: 12, class: 1, unicastResponse: true },
96
- { name: '_ipps._tcp.local', type: 12, class: 1, unicastResponse: true },
97
- { name: '_meshcop._tcp.local', type: 12, class: 1, unicastResponse: true },
98
- { name: '_printer._tcp.local', type: 12, class: 1, unicastResponse: true },
99
- { name: '_raop._tcp.local', type: 12, class: 1, unicastResponse: true },
100
- { name: '_sleep-proxy._tcp.local', type: 12, class: 1, unicastResponse: true },
101
- { name: '_ssh._tcp.local', type: 12, class: 1, unicastResponse: true },
102
- { name: '_services._dns-sd._udp.local', type: 12, class: 1, unicastResponse: true },
103
- ]);
104
- }
105
- catch (error) {
106
- mdns.log.error(`Error sending mDNS query: ${error.message}`);
107
- }
108
- };
109
- const advertise = (mdns, ttl = 120) => {
110
- mdns.log.info(`Sending mDNS advertisement for matterbridge service with TTL ${ttl ? ttl.toString() : 'goodbye'}...`);
111
- const httpServiceType = '_http._tcp.local';
112
- const matterbridgeServiceType = '_matterbridge._tcp.local';
113
- const httpInstanceName = 'matterbridge._http._tcp.local';
114
- const matterbridgeInstanceName = 'matterbridge._matterbridge._tcp.local';
115
- const hostName = 'matterbridge.local';
116
- const port = 8283;
117
- const ptrHttpServiceTypeRdata = mdns.encodeDnsName(httpServiceType);
118
- const ptrMatterbridgeServiceTypeRdata = mdns.encodeDnsName(matterbridgeServiceType);
119
- const ptrHttpInstanceRdata = mdns.encodeDnsName(httpInstanceName);
120
- const ptrMatterbridgeInstanceRdata = mdns.encodeDnsName(matterbridgeInstanceName);
121
- const srvRdata = mdns.encodeSrvRdata(0, 0, port, hostName);
122
- const txtRdata = mdns.encodeTxtRdata([`version=${pkg.version}`, 'path=/']);
123
- const answers = [
124
- { name: '_services._dns-sd._udp.local', rtype: 12, rclass: 1, ttl, rdata: ptrHttpServiceTypeRdata },
125
- { name: httpServiceType, rtype: 12, rclass: 1, ttl, rdata: ptrHttpInstanceRdata },
126
- { name: '_services._dns-sd._udp.local', rtype: 12, rclass: 1, ttl, rdata: ptrMatterbridgeServiceTypeRdata },
127
- { name: matterbridgeServiceType, rtype: 12, rclass: 1, ttl, rdata: ptrMatterbridgeInstanceRdata },
128
- { name: httpInstanceName, rtype: 33, rclass: 1 | 32768, ttl, rdata: srvRdata },
129
- { name: matterbridgeInstanceName, rtype: 33, rclass: 1 | 32768, ttl, rdata: srvRdata },
130
- { name: httpInstanceName, rtype: 16, rclass: 1 | 32768, ttl, rdata: txtRdata },
131
- { name: matterbridgeInstanceName, rtype: 16, rclass: 1 | 32768, ttl, rdata: txtRdata },
132
- ];
133
- const interfaces = os.networkInterfaces();
134
- let interfaceInfos = mdns.interfaceName ? interfaces[mdns.interfaceName] : undefined;
135
- if (!interfaceInfos) {
136
- interfaceInfos = [];
137
- for (const name of Object.keys(interfaces)) {
138
- const infos = interfaces[name];
139
- if (infos && infos.length > 0 && !infos[0].internal) {
140
- interfaceInfos.push(...infos);
141
- break;
142
- }
143
- }
144
- }
145
- for (const info of interfaceInfos) {
146
- if (info.family === 'IPv4' && !info.internal) {
147
- const ipv4 = info.address;
148
- answers.push({ name: hostName, rtype: 1, rclass: 1 | 32768, ttl, rdata: mdns.encodeA(ipv4) });
149
- }
150
- else if (info.family === 'IPv6' && !info.internal) {
151
- const ipv6 = info.address;
152
- answers.push({ name: hostName, rtype: 28, rclass: 1 | 32768, ttl, rdata: mdns.encodeAAAA(ipv6) });
153
- }
154
- }
155
- try {
156
- mdns.sendResponse(answers);
157
- }
158
- catch (error) {
159
- mdns.log.error(`Error sending mDNS advertisement: ${error.message}`);
160
- }
161
- };
162
- if (!hasParameter('noIpv4')) {
163
- mdnsIpv4 = new Mdns('mDNS Server udp4', MDNS_MULTICAST_IPV4_ADDRESS, MDNS_MULTICAST_PORT, 'udp4', true, getParameter('interfaceName'), getParameter('ipv4InterfaceAddress') || '0.0.0.0', getParameter('outgoingIpv4InterfaceAddress'));
164
- if (hasParameter('v') || hasParameter('verbose'))
165
- mdnsIpv4.listNetworkInterfaces();
166
- const filters = getStringArrayParameter('filter');
167
- if (filters)
168
- mdnsIpv4.filters.push(...filters);
169
- const ipFilters = getStringArrayParameter('ip-filter');
170
- if (ipFilters)
171
- mdnsIpv4.ipFilters.push(...ipFilters);
172
- mdnsIpv4.on('error', (err) => {
173
- mdnsIpv4?.log.error(`mDNS udp4 Server error: ${err.message}\n${err.stack}`);
174
- });
175
- mdnsIpv4.start();
176
- mdnsIpv4.on('ready', (address) => {
177
- mdnsIpv4?.socket.setMulticastLoopback(false);
178
- mdnsIpv4?.log.info(`mdnsIpv4 server ready on ${address.family} ${address.address}:${address.port}`);
179
- if (hasParameter('advertise')) {
180
- advertise(mdnsIpv4);
181
- mdnsIpv4AdvertiseInterval = setInterval(() => advertise(mdnsIpv4), getIntParameter('advertise') || 10000).unref();
182
- }
183
- if (hasParameter('query')) {
184
- query(mdnsIpv4);
185
- mdnsIpv4QueryInterval = setInterval(() => query(mdnsIpv4), getIntParameter('query') || 10000).unref();
186
- }
187
- });
188
- }
189
- if (!hasParameter('noIpv6')) {
190
- mdnsIpv6 = new Mdns('mDNS Server udp6', MDNS_MULTICAST_IPV6_ADDRESS, MDNS_MULTICAST_PORT, 'udp6', true, getParameter('interfaceName'), getParameter('ipv6InterfaceAddress') || '::', getParameter('outgoingIpv6InterfaceAddress'));
191
- if (hasParameter('v') || hasParameter('verbose'))
192
- mdnsIpv6.listNetworkInterfaces();
193
- const filters = getStringArrayParameter('filter');
194
- if (filters)
195
- mdnsIpv6.filters.push(...filters);
196
- const ipFilters = getStringArrayParameter('ip-filter');
197
- if (ipFilters)
198
- mdnsIpv6.ipFilters.push(...ipFilters);
199
- mdnsIpv6.on('error', (err) => {
200
- mdnsIpv6?.log.error(`mDNS udp6 Server error: ${err.message}\n${err.stack}`);
201
- });
202
- mdnsIpv6.start();
203
- mdnsIpv6.on('ready', (address) => {
204
- mdnsIpv6?.socket.setMulticastLoopback(false);
205
- mdnsIpv6?.log.info(`mdnsIpv6 server ready on ${address.family} ${address.address}:${address.port}`);
206
- if (hasParameter('advertise')) {
207
- advertise(mdnsIpv6);
208
- mdnsIpv6AdvertiseInterval = setInterval(() => advertise(mdnsIpv6), getIntParameter('advertise') || 10000).unref();
209
- }
210
- if (hasParameter('query')) {
211
- query(mdnsIpv6);
212
- mdnsIpv6QueryInterval = setInterval(() => query(mdnsIpv6), getIntParameter('query') || 10000).unref();
213
- }
214
- });
215
- }
216
- process.on('SIGINT', async () => {
217
- await cleanupAndLogAndExit();
218
- });
219
- process.on('SIGTERM', async () => {
220
- await cleanupAndLogAndExit();
221
- });
222
- if (!hasParameter('no-timeout')) {
223
- setTimeout(async () => {
224
- await cleanupAndLogAndExit();
225
- }, 600000).unref();
226
- }
227
- }