matterbridge 3.1.6 → 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 +31 -0
- package/bin/mb_coap.js +2 -0
- package/bin/mb_mdns.js +2 -0
- package/dist/cli.js +2 -91
- package/dist/cliEmitter.js +0 -30
- package/dist/clusters/export.js +0 -2
- package/dist/defaultConfigSchema.js +0 -24
- package/dist/deviceManager.js +1 -94
- package/dist/devices/batteryStorage.js +1 -48
- package/dist/devices/dishwasher.js +3 -78
- package/dist/devices/evse.js +10 -74
- package/dist/devices/export.js +0 -2
- package/dist/devices/extractorHood.js +0 -42
- package/dist/devices/heatPump.js +2 -50
- package/dist/devices/laundryDryer.js +6 -83
- package/dist/devices/laundryWasher.js +7 -91
- package/dist/devices/roboticVacuumCleaner.js +7 -93
- package/dist/devices/solarPower.js +0 -38
- package/dist/devices/waterHeater.js +2 -82
- package/dist/dgram/coap.js +252 -0
- package/dist/dgram/dgram.js +2 -113
- package/dist/dgram/mb_coap.js +52 -0
- package/dist/dgram/mb_mdns.js +60 -0
- package/dist/dgram/mdns.js +135 -380
- package/dist/dgram/multicast.js +7 -64
- package/dist/dgram/unicast.js +0 -54
- package/dist/frontend.js +21 -435
- package/dist/globalMatterbridge.js +0 -47
- package/dist/helpers.js +0 -53
- package/dist/index.js +1 -30
- package/dist/logger/export.js +0 -1
- package/dist/matter/behaviors.js +0 -2
- package/dist/matter/clusters.js +0 -2
- package/dist/matter/devices.js +0 -2
- package/dist/matter/endpoints.js +0 -2
- package/dist/matter/export.js +0 -3
- package/dist/matter/types.js +0 -3
- package/dist/matterbridge.js +50 -804
- package/dist/matterbridgeAccessoryPlatform.js +0 -36
- package/dist/matterbridgeBehaviors.js +5 -65
- package/dist/matterbridgeDeviceTypes.js +15 -579
- package/dist/matterbridgeDynamicPlatform.js +0 -36
- package/dist/matterbridgeEndpoint.js +51 -1191
- package/dist/matterbridgeEndpointHelpers.js +13 -322
- package/dist/matterbridgePlatform.js +0 -233
- package/dist/matterbridgeTypes.js +0 -25
- package/dist/pluginManager.js +3 -249
- package/dist/shelly.js +7 -168
- package/dist/storage/export.js +0 -1
- package/dist/update.js +0 -54
- package/dist/utils/colorUtils.js +2 -263
- package/dist/utils/commandLine.js +0 -54
- package/dist/utils/copyDirectory.js +1 -38
- package/dist/utils/createDirectory.js +0 -33
- package/dist/utils/createZip.js +2 -47
- package/dist/utils/deepCopy.js +0 -39
- package/dist/utils/deepEqual.js +1 -72
- package/dist/utils/error.js +0 -41
- package/dist/utils/export.js +0 -1
- package/dist/utils/hex.js +0 -58
- package/dist/utils/isvalid.js +0 -101
- package/dist/utils/network.js +5 -81
- package/dist/utils/spawn.js +0 -40
- package/dist/utils/wait.js +9 -62
- package/npm-shrinkwrap.json +5 -3
- package/package.json +4 -3
- package/dist/cli.d.ts +0 -26
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cliEmitter.d.ts +0 -34
- package/dist/cliEmitter.d.ts.map +0 -1
- package/dist/cliEmitter.js.map +0 -1
- package/dist/clusters/export.d.ts +0 -2
- package/dist/clusters/export.d.ts.map +0 -1
- package/dist/clusters/export.js.map +0 -1
- package/dist/defaultConfigSchema.d.ts +0 -28
- package/dist/defaultConfigSchema.d.ts.map +0 -1
- package/dist/defaultConfigSchema.js.map +0 -1
- package/dist/deviceManager.d.ts +0 -112
- package/dist/deviceManager.d.ts.map +0 -1
- package/dist/deviceManager.js.map +0 -1
- package/dist/devices/batteryStorage.d.ts +0 -48
- package/dist/devices/batteryStorage.d.ts.map +0 -1
- package/dist/devices/batteryStorage.js.map +0 -1
- package/dist/devices/dishwasher.d.ts +0 -91
- package/dist/devices/dishwasher.d.ts.map +0 -1
- package/dist/devices/dishwasher.js.map +0 -1
- package/dist/devices/evse.d.ts +0 -75
- package/dist/devices/evse.d.ts.map +0 -1
- package/dist/devices/evse.js.map +0 -1
- package/dist/devices/export.d.ts +0 -11
- package/dist/devices/export.d.ts.map +0 -1
- package/dist/devices/export.js.map +0 -1
- package/dist/devices/extractorHood.d.ts +0 -46
- package/dist/devices/extractorHood.d.ts.map +0 -1
- package/dist/devices/extractorHood.js.map +0 -1
- package/dist/devices/heatPump.d.ts +0 -47
- package/dist/devices/heatPump.d.ts.map +0 -1
- package/dist/devices/heatPump.js.map +0 -1
- package/dist/devices/laundryDryer.d.ts +0 -87
- package/dist/devices/laundryDryer.d.ts.map +0 -1
- package/dist/devices/laundryDryer.js.map +0 -1
- package/dist/devices/laundryWasher.d.ts +0 -242
- package/dist/devices/laundryWasher.d.ts.map +0 -1
- package/dist/devices/laundryWasher.js.map +0 -1
- package/dist/devices/roboticVacuumCleaner.d.ts +0 -112
- package/dist/devices/roboticVacuumCleaner.d.ts.map +0 -1
- package/dist/devices/roboticVacuumCleaner.js.map +0 -1
- package/dist/devices/solarPower.d.ts +0 -40
- package/dist/devices/solarPower.d.ts.map +0 -1
- package/dist/devices/solarPower.js.map +0 -1
- package/dist/devices/waterHeater.d.ts +0 -111
- package/dist/devices/waterHeater.d.ts.map +0 -1
- package/dist/devices/waterHeater.js.map +0 -1
- package/dist/dgram/dgram.d.ts +0 -140
- package/dist/dgram/dgram.d.ts.map +0 -1
- package/dist/dgram/dgram.js.map +0 -1
- package/dist/dgram/mdns.d.ts +0 -288
- package/dist/dgram/mdns.d.ts.map +0 -1
- package/dist/dgram/mdns.js.map +0 -1
- package/dist/dgram/multicast.d.ts +0 -65
- package/dist/dgram/multicast.d.ts.map +0 -1
- package/dist/dgram/multicast.js.map +0 -1
- package/dist/dgram/unicast.d.ts +0 -56
- package/dist/dgram/unicast.d.ts.map +0 -1
- package/dist/dgram/unicast.js.map +0 -1
- package/dist/frontend.d.ts +0 -304
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/globalMatterbridge.d.ts +0 -59
- package/dist/globalMatterbridge.d.ts.map +0 -1
- package/dist/globalMatterbridge.js.map +0 -1
- package/dist/helpers.d.ts +0 -48
- package/dist/helpers.d.ts.map +0 -1
- package/dist/helpers.js.map +0 -1
- package/dist/index.d.ts +0 -33
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/logger/export.d.ts +0 -2
- package/dist/logger/export.d.ts.map +0 -1
- package/dist/logger/export.js.map +0 -1
- package/dist/matter/behaviors.d.ts +0 -2
- package/dist/matter/behaviors.d.ts.map +0 -1
- package/dist/matter/behaviors.js.map +0 -1
- package/dist/matter/clusters.d.ts +0 -2
- package/dist/matter/clusters.d.ts.map +0 -1
- package/dist/matter/clusters.js.map +0 -1
- package/dist/matter/devices.d.ts +0 -2
- package/dist/matter/devices.d.ts.map +0 -1
- package/dist/matter/devices.js.map +0 -1
- package/dist/matter/endpoints.d.ts +0 -2
- package/dist/matter/endpoints.d.ts.map +0 -1
- package/dist/matter/endpoints.js.map +0 -1
- package/dist/matter/export.d.ts +0 -5
- package/dist/matter/export.d.ts.map +0 -1
- package/dist/matter/export.js.map +0 -1
- package/dist/matter/types.d.ts +0 -3
- package/dist/matter/types.d.ts.map +0 -1
- package/dist/matter/types.js.map +0 -1
- package/dist/matterbridge.d.ts +0 -460
- package/dist/matterbridge.d.ts.map +0 -1
- package/dist/matterbridge.js.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.d.ts +0 -42
- package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
- package/dist/matterbridgeBehaviors.d.ts +0 -1351
- package/dist/matterbridgeBehaviors.d.ts.map +0 -1
- package/dist/matterbridgeBehaviors.js.map +0 -1
- package/dist/matterbridgeDeviceTypes.d.ts +0 -709
- package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
- package/dist/matterbridgeDeviceTypes.js.map +0 -1
- package/dist/matterbridgeDynamicPlatform.d.ts +0 -42
- package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
- package/dist/matterbridgeDynamicPlatform.js.map +0 -1
- package/dist/matterbridgeEndpoint.d.ts +0 -1328
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -3198
- package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
- package/dist/matterbridgeEndpointHelpers.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts +0 -310
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts +0 -195
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/pluginManager.d.ts +0 -270
- package/dist/pluginManager.d.ts.map +0 -1
- package/dist/pluginManager.js.map +0 -1
- package/dist/shelly.d.ts +0 -174
- package/dist/shelly.d.ts.map +0 -1
- package/dist/shelly.js.map +0 -1
- package/dist/storage/export.d.ts +0 -2
- package/dist/storage/export.d.ts.map +0 -1
- package/dist/storage/export.js.map +0 -1
- package/dist/update.d.ts +0 -59
- package/dist/update.d.ts.map +0 -1
- package/dist/update.js.map +0 -1
- package/dist/utils/colorUtils.d.ts +0 -117
- package/dist/utils/colorUtils.d.ts.map +0 -1
- package/dist/utils/colorUtils.js.map +0 -1
- package/dist/utils/commandLine.d.ts +0 -59
- package/dist/utils/commandLine.d.ts.map +0 -1
- package/dist/utils/commandLine.js.map +0 -1
- package/dist/utils/copyDirectory.d.ts +0 -33
- package/dist/utils/copyDirectory.d.ts.map +0 -1
- package/dist/utils/copyDirectory.js.map +0 -1
- package/dist/utils/createDirectory.d.ts +0 -34
- package/dist/utils/createDirectory.d.ts.map +0 -1
- package/dist/utils/createDirectory.js.map +0 -1
- package/dist/utils/createZip.d.ts +0 -39
- package/dist/utils/createZip.d.ts.map +0 -1
- package/dist/utils/createZip.js.map +0 -1
- package/dist/utils/deepCopy.d.ts +0 -32
- package/dist/utils/deepCopy.d.ts.map +0 -1
- package/dist/utils/deepCopy.js.map +0 -1
- package/dist/utils/deepEqual.d.ts +0 -54
- package/dist/utils/deepEqual.d.ts.map +0 -1
- package/dist/utils/deepEqual.js.map +0 -1
- package/dist/utils/error.d.ts +0 -44
- package/dist/utils/error.d.ts.map +0 -1
- package/dist/utils/error.js.map +0 -1
- package/dist/utils/export.d.ts +0 -12
- package/dist/utils/export.d.ts.map +0 -1
- package/dist/utils/export.js.map +0 -1
- package/dist/utils/hex.d.ts +0 -49
- package/dist/utils/hex.d.ts.map +0 -1
- package/dist/utils/hex.js.map +0 -1
- package/dist/utils/isvalid.d.ts +0 -103
- package/dist/utils/isvalid.d.ts.map +0 -1
- package/dist/utils/isvalid.js.map +0 -1
- package/dist/utils/network.d.ts +0 -74
- package/dist/utils/network.d.ts.map +0 -1
- package/dist/utils/network.js.map +0 -1
- package/dist/utils/spawn.d.ts +0 -33
- package/dist/utils/spawn.d.ts.map +0 -1
- package/dist/utils/spawn.js.map +0 -1
- package/dist/utils/wait.d.ts +0 -56
- package/dist/utils/wait.d.ts.map +0 -1
- package/dist/utils/wait.js.map +0 -1
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { BLUE, db, GREEN, MAGENTA, nf } from 'node-ansi-logger';
|
|
2
|
+
import { COAP_MULTICAST_IPV4_ADDRESS, COAP_MULTICAST_PORT, Multicast } from './multicast.js';
|
|
3
|
+
export const COAP_OPTION_URI_PATH = 11;
|
|
4
|
+
export const COIOT_OPTION_DEVID = 3332;
|
|
5
|
+
export const COIOT_OPTION_VALIDITY = 3412;
|
|
6
|
+
export const COIOT_OPTION_SERIAL = 3420;
|
|
7
|
+
export const COIOT_REQUEST_STATUS_ID = 56831;
|
|
8
|
+
export const COIOT_REQUEST_DESCRIPTION_ID = 56832;
|
|
9
|
+
export class Coap extends Multicast {
|
|
10
|
+
constructor(name, multicastAddress, multicastPort, socketType, reuseAddr = true, interfaceName, interfaceAddress) {
|
|
11
|
+
super(name, multicastAddress, multicastPort, socketType, reuseAddr, interfaceName, interfaceAddress);
|
|
12
|
+
}
|
|
13
|
+
onCoapMessage(message, rinfo) {
|
|
14
|
+
this.log.debug(`Coap message received from ${BLUE}${rinfo.family}${db} ${BLUE}${rinfo.address}${db}:${BLUE}${rinfo.port}${db}`);
|
|
15
|
+
}
|
|
16
|
+
onMessage(msg, rinfo) {
|
|
17
|
+
this.log.info(`Dgram multicast socket received a CoAP message from ${BLUE}${rinfo.family}${nf} ${BLUE}${rinfo.address}${nf}:${BLUE}${rinfo.port}${nf}`);
|
|
18
|
+
try {
|
|
19
|
+
const result = this.decodeCoapMessage(msg);
|
|
20
|
+
this.onCoapMessage(result, rinfo);
|
|
21
|
+
this.logCoapMessage(result);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
this.log.error(`Error decoding CoAP message: ${error instanceof Error ? error.message : error}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
decodeCoapMessage(msg) {
|
|
28
|
+
if (msg.length < 4) {
|
|
29
|
+
throw new Error('Message too short to be a valid CoAP message');
|
|
30
|
+
}
|
|
31
|
+
const version = (msg[0] & 0xc0) >> 6;
|
|
32
|
+
const type = (msg[0] & 0x30) >> 4;
|
|
33
|
+
const tokenLength = msg[0] & 0x0f;
|
|
34
|
+
const code = msg[1];
|
|
35
|
+
const messageId = msg.readUInt16BE(2);
|
|
36
|
+
let offset = 4;
|
|
37
|
+
let token = Buffer.alloc(0);
|
|
38
|
+
if (tokenLength > 0) {
|
|
39
|
+
if (msg.length < offset + tokenLength) {
|
|
40
|
+
throw new Error('Message too short for the token length specified');
|
|
41
|
+
}
|
|
42
|
+
token = msg.slice(offset, offset + tokenLength);
|
|
43
|
+
offset += tokenLength;
|
|
44
|
+
}
|
|
45
|
+
const options = [];
|
|
46
|
+
let currentOptionNumber = 0;
|
|
47
|
+
while (offset < msg.length) {
|
|
48
|
+
if (msg[offset] === 0xff) {
|
|
49
|
+
offset++;
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
const optionHeader = msg[offset++];
|
|
53
|
+
let delta = (optionHeader & 0xf0) >> 4;
|
|
54
|
+
let length = optionHeader & 0x0f;
|
|
55
|
+
if (delta === 13) {
|
|
56
|
+
if (offset >= msg.length) {
|
|
57
|
+
throw new Error('Invalid extended option delta');
|
|
58
|
+
}
|
|
59
|
+
delta = msg[offset++] + 13;
|
|
60
|
+
}
|
|
61
|
+
else if (delta === 14) {
|
|
62
|
+
if (offset + 1 >= msg.length) {
|
|
63
|
+
throw new Error('Invalid extended option delta');
|
|
64
|
+
}
|
|
65
|
+
delta = msg.readUInt16BE(offset) + 269;
|
|
66
|
+
offset += 2;
|
|
67
|
+
}
|
|
68
|
+
else if (delta === 15) {
|
|
69
|
+
throw new Error('Reserved option delta value encountered');
|
|
70
|
+
}
|
|
71
|
+
if (length === 13) {
|
|
72
|
+
if (offset >= msg.length) {
|
|
73
|
+
throw new Error('Invalid extended option length');
|
|
74
|
+
}
|
|
75
|
+
length = msg[offset++] + 13;
|
|
76
|
+
}
|
|
77
|
+
else if (length === 14) {
|
|
78
|
+
if (offset + 1 >= msg.length) {
|
|
79
|
+
throw new Error('Invalid extended option length');
|
|
80
|
+
}
|
|
81
|
+
length = msg.readUInt16BE(offset) + 269;
|
|
82
|
+
offset += 2;
|
|
83
|
+
}
|
|
84
|
+
else if (length === 15) {
|
|
85
|
+
throw new Error('Reserved option length value encountered');
|
|
86
|
+
}
|
|
87
|
+
currentOptionNumber += delta;
|
|
88
|
+
if (offset + length > msg.length) {
|
|
89
|
+
throw new Error('Option length exceeds message length');
|
|
90
|
+
}
|
|
91
|
+
const optionValue = msg.slice(offset, offset + length);
|
|
92
|
+
offset += length;
|
|
93
|
+
options.push({
|
|
94
|
+
number: currentOptionNumber,
|
|
95
|
+
value: optionValue,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
const payload = offset < msg.length ? msg.slice(offset) : undefined;
|
|
99
|
+
return {
|
|
100
|
+
version,
|
|
101
|
+
type,
|
|
102
|
+
tokenLength,
|
|
103
|
+
code,
|
|
104
|
+
messageId,
|
|
105
|
+
token,
|
|
106
|
+
options,
|
|
107
|
+
payload,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
encodeCoapMessage(msg) {
|
|
111
|
+
const parts = [];
|
|
112
|
+
const token = msg.token || Buffer.alloc(0);
|
|
113
|
+
const tokenLength = token.length;
|
|
114
|
+
if (tokenLength > 8) {
|
|
115
|
+
throw new Error('Token length cannot exceed 8 bytes');
|
|
116
|
+
}
|
|
117
|
+
const header = Buffer.alloc(4);
|
|
118
|
+
header[0] = ((msg.version & 0x03) << 6) | ((msg.type & 0x03) << 4) | (tokenLength & 0x0f);
|
|
119
|
+
header[1] = msg.code;
|
|
120
|
+
header.writeUInt16BE(msg.messageId, 2);
|
|
121
|
+
parts.push(header);
|
|
122
|
+
if (tokenLength > 0) {
|
|
123
|
+
parts.push(token);
|
|
124
|
+
}
|
|
125
|
+
const sortedOptions = msg.options.slice().sort((a, b) => a.number - b.number);
|
|
126
|
+
let previousOptionNumber = 0;
|
|
127
|
+
for (const option of sortedOptions) {
|
|
128
|
+
const optionDelta = option.number - previousOptionNumber;
|
|
129
|
+
const optionValueLength = option.value.length;
|
|
130
|
+
let deltaNibble;
|
|
131
|
+
let deltaExtended = null;
|
|
132
|
+
let lengthNibble;
|
|
133
|
+
let lengthExtended = null;
|
|
134
|
+
if (optionDelta < 13) {
|
|
135
|
+
deltaNibble = optionDelta;
|
|
136
|
+
}
|
|
137
|
+
else if (optionDelta < 269) {
|
|
138
|
+
deltaNibble = 13;
|
|
139
|
+
deltaExtended = Buffer.from([optionDelta - 13]);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
deltaNibble = 14;
|
|
143
|
+
deltaExtended = Buffer.alloc(2);
|
|
144
|
+
deltaExtended.writeUInt16BE(optionDelta - 269, 0);
|
|
145
|
+
}
|
|
146
|
+
if (optionValueLength < 13) {
|
|
147
|
+
lengthNibble = optionValueLength;
|
|
148
|
+
}
|
|
149
|
+
else if (optionValueLength < 269) {
|
|
150
|
+
lengthNibble = 13;
|
|
151
|
+
lengthExtended = Buffer.from([optionValueLength - 13]);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
lengthNibble = 14;
|
|
155
|
+
lengthExtended = Buffer.alloc(2);
|
|
156
|
+
lengthExtended.writeUInt16BE(optionValueLength - 269, 0);
|
|
157
|
+
}
|
|
158
|
+
const optionHeader = Buffer.alloc(1);
|
|
159
|
+
optionHeader[0] = (deltaNibble << 4) | (lengthNibble & 0x0f);
|
|
160
|
+
parts.push(optionHeader);
|
|
161
|
+
if (deltaExtended) {
|
|
162
|
+
parts.push(deltaExtended);
|
|
163
|
+
}
|
|
164
|
+
if (lengthExtended) {
|
|
165
|
+
parts.push(lengthExtended);
|
|
166
|
+
}
|
|
167
|
+
parts.push(option.value);
|
|
168
|
+
previousOptionNumber = option.number;
|
|
169
|
+
}
|
|
170
|
+
if (msg.payload && msg.payload.length > 0) {
|
|
171
|
+
parts.push(Buffer.from([0xff]));
|
|
172
|
+
parts.push(msg.payload);
|
|
173
|
+
}
|
|
174
|
+
return Buffer.concat(parts);
|
|
175
|
+
}
|
|
176
|
+
coapTypeToString(type) {
|
|
177
|
+
switch (type) {
|
|
178
|
+
case 0:
|
|
179
|
+
return 'Confirmable (CON)';
|
|
180
|
+
case 1:
|
|
181
|
+
return 'Non-confirmable (NON)';
|
|
182
|
+
case 2:
|
|
183
|
+
return 'Acknowledgement (ACK)';
|
|
184
|
+
case 3:
|
|
185
|
+
return 'Reset (RST)';
|
|
186
|
+
default:
|
|
187
|
+
return `Unknown Type (${type})`;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
coapCodeToString(code) {
|
|
191
|
+
const cls = code >> 5;
|
|
192
|
+
const detail = code & 0x1f;
|
|
193
|
+
const codeStr = `${cls}.${detail.toString().padStart(2, '0')}`;
|
|
194
|
+
return codeStr;
|
|
195
|
+
}
|
|
196
|
+
sendRequest(messageId, options, payload, token, address, port) {
|
|
197
|
+
const coapMessage = {
|
|
198
|
+
version: 1,
|
|
199
|
+
type: 1,
|
|
200
|
+
tokenLength: token ? Buffer.from(token).length : 0,
|
|
201
|
+
code: 1,
|
|
202
|
+
messageId,
|
|
203
|
+
token: token ? Buffer.from(token) : Buffer.alloc(0),
|
|
204
|
+
options,
|
|
205
|
+
payload: payload ? Buffer.from(JSON.stringify(payload)) : undefined,
|
|
206
|
+
};
|
|
207
|
+
const encodedBuffer = this.encodeCoapMessage(coapMessage);
|
|
208
|
+
this.send(encodedBuffer, address ?? COAP_MULTICAST_IPV4_ADDRESS, port ?? COAP_MULTICAST_PORT);
|
|
209
|
+
}
|
|
210
|
+
logCoapMessage(msg) {
|
|
211
|
+
this.log.info(`Decoded CoAP message: version ${MAGENTA}${msg.version}${nf} type ${MAGENTA}${this.coapTypeToString(msg.type)}${nf} ` +
|
|
212
|
+
`code ${MAGENTA}${this.coapCodeToString(msg.code)}${nf} messageId ${MAGENTA}${msg.messageId}${nf} ` +
|
|
213
|
+
`tokenLength ${MAGENTA}${msg.tokenLength}${nf} token ${MAGENTA}${msg.tokenLength == 0 ? 'no token' : msg.token.toString('hex')}${nf} ` +
|
|
214
|
+
`options ${MAGENTA}${msg.options.length}${nf} payload ${MAGENTA}${msg.payload ? msg.payload.length + ' bytes' : undefined}${nf}`);
|
|
215
|
+
msg.options.forEach((option) => {
|
|
216
|
+
if (option.number === COAP_OPTION_URI_PATH) {
|
|
217
|
+
this.log.info(`Option: COAP_OPTION_URI_PATH => ${GREEN}${option.value}${nf}`);
|
|
218
|
+
}
|
|
219
|
+
else if (option.number === COIOT_OPTION_DEVID) {
|
|
220
|
+
const parts = option.value.toString().split('#');
|
|
221
|
+
const deviceModel = parts[0];
|
|
222
|
+
const deviceMac = parts[1];
|
|
223
|
+
const protocolRevision = parts[2];
|
|
224
|
+
this.log.info(`Option: COIOT_OPTION_DEVID => ${option.value} => Model: ${GREEN}${deviceModel}${nf}, MAC: ${GREEN}${deviceMac}${nf}, Protocol: ${GREEN}${protocolRevision}${nf}`);
|
|
225
|
+
}
|
|
226
|
+
else if (option.number === COIOT_OPTION_SERIAL) {
|
|
227
|
+
const serial = option.value.readUInt16BE(0);
|
|
228
|
+
this.log.info(`Option: COIOT_OPTION_SERIAL => 0x${option.value.toString('hex')} => serial: ${GREEN}${serial}${nf}`);
|
|
229
|
+
}
|
|
230
|
+
else if (option.number === COIOT_OPTION_VALIDITY) {
|
|
231
|
+
const validity = option.value.readUInt16BE(0);
|
|
232
|
+
let validFor = 0;
|
|
233
|
+
if ((validity & 0x1) === 0) {
|
|
234
|
+
validFor = Math.floor(validity / 10);
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
validFor = validity * 4;
|
|
238
|
+
}
|
|
239
|
+
this.log.info(`Option: COIOT_OPTION_VALIDITY => 0x${option.value.toString('hex')} => valid for: ${GREEN}${validFor}${nf} seconds`);
|
|
240
|
+
}
|
|
241
|
+
else
|
|
242
|
+
this.log.info(`Option: ${option.number} - ${option.value
|
|
243
|
+
.toString('hex')
|
|
244
|
+
.match(/.{1,2}/g)
|
|
245
|
+
?.join(' ')}`);
|
|
246
|
+
});
|
|
247
|
+
if (msg.payload && msg.payload.length > 0)
|
|
248
|
+
this.log.info(`Payload:`, JSON.parse(msg.payload.toString()), '\n');
|
|
249
|
+
else
|
|
250
|
+
this.log.info(`No payload`, '\n');
|
|
251
|
+
}
|
|
252
|
+
}
|
package/dist/dgram/dgram.js
CHANGED
|
@@ -1,34 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @description This file contains the class Dgram.
|
|
3
|
-
* @file dgram.ts
|
|
4
|
-
* @author Luca Liguori
|
|
5
|
-
* @created 2025-03-22
|
|
6
|
-
* @version 1.0.0
|
|
7
|
-
* @license Apache-2.0
|
|
8
|
-
*
|
|
9
|
-
* Copyright 2025, 2026, 2027 Luca Liguori.
|
|
10
|
-
*
|
|
11
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
12
|
-
* you may not use this file except in compliance with the License.
|
|
13
|
-
* You may obtain a copy of the License at
|
|
14
|
-
*
|
|
15
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
16
|
-
*
|
|
17
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
18
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
19
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
20
|
-
* See the License for the specific language governing permissions and
|
|
21
|
-
* limitations under the License.
|
|
22
|
-
*/
|
|
23
|
-
// Node.js imports
|
|
24
1
|
import dgram from 'node:dgram';
|
|
25
2
|
import EventEmitter from 'node:events';
|
|
26
3
|
import os from 'node:os';
|
|
27
|
-
// AnsiLogger imports
|
|
28
4
|
import { AnsiLogger, BLUE, db, idn, nf, rs } from 'node-ansi-logger';
|
|
29
|
-
/**
|
|
30
|
-
* This class implements a dgram socket.
|
|
31
|
-
*/
|
|
32
5
|
export class Dgram extends EventEmitter {
|
|
33
6
|
log;
|
|
34
7
|
socket;
|
|
@@ -37,18 +10,9 @@ export class Dgram extends EventEmitter {
|
|
|
37
10
|
interfaceName;
|
|
38
11
|
interfaceAddress;
|
|
39
12
|
interfaceNetmask;
|
|
40
|
-
/**
|
|
41
|
-
* Creates an instance of Dgram.
|
|
42
|
-
*
|
|
43
|
-
* @param {string} name - The name of the socket.
|
|
44
|
-
* @param {'udp4' | 'udp6'} socketType - The type of the socket (IPv4 or IPv6).
|
|
45
|
-
* @param {boolean | undefined} reuseAddr - Whether to allow address reuse.
|
|
46
|
-
* @param {string} [interfaceName] - The name of the network interface to bind to.
|
|
47
|
-
* @param {string} [interfaceAddress] - The address of the network interface to bind to.
|
|
48
|
-
*/
|
|
49
13
|
constructor(name, socketType, reuseAddr = true, interfaceName, interfaceAddress) {
|
|
50
14
|
super();
|
|
51
|
-
this.log = new AnsiLogger({ logName: name, logTimestampFormat: 4
|
|
15
|
+
this.log = new AnsiLogger({ logName: name, logTimestampFormat: 4, logLevel: "debug" });
|
|
52
16
|
this.socket = dgram.createSocket({ type: socketType, reuseAddr });
|
|
53
17
|
this.socketType = socketType;
|
|
54
18
|
this.interfaceName = interfaceName;
|
|
@@ -82,13 +46,6 @@ export class Dgram extends EventEmitter {
|
|
|
82
46
|
this.onListening(address);
|
|
83
47
|
});
|
|
84
48
|
}
|
|
85
|
-
/**
|
|
86
|
-
* Sends a message to the specified server.
|
|
87
|
-
*
|
|
88
|
-
* @param {Buffer} msg - The message buffer to send.
|
|
89
|
-
* @param {string} serverAddress - The IPv4 address of the destination server.
|
|
90
|
-
* @param {number} serverPort - The port of the destination server.
|
|
91
|
-
*/
|
|
92
49
|
send(msg, serverAddress, serverPort) {
|
|
93
50
|
this.socket.send(msg, 0, msg.length, serverPort, serverAddress, (error) => {
|
|
94
51
|
if (error) {
|
|
@@ -126,30 +83,18 @@ export class Dgram extends EventEmitter {
|
|
|
126
83
|
this.log.info(`Socket ready on ${BLUE}${address.family}${nf} ${BLUE}${address.address}${nf}:${BLUE}${address.port}${nf}`);
|
|
127
84
|
this.emit('ready', address);
|
|
128
85
|
}
|
|
129
|
-
/**
|
|
130
|
-
* Retrieves the IPv4 address of the specified network interface or the first external IPv4 interface if no interface is specified.
|
|
131
|
-
* Throws an error if no suitable interface or address is found.
|
|
132
|
-
*
|
|
133
|
-
* @param {string} networkInterface - The name of the network interface to retrieve the IPv4 address from. If not specified, the first external IPv4 interface will be used.
|
|
134
|
-
* @returns {string | undefined} The IPv4 address of the specified network interface or the first external IPv4 interface.
|
|
135
|
-
* @throws Error if no suitable interface or address is found.
|
|
136
|
-
*/
|
|
137
86
|
getIpv4InterfaceAddress(networkInterface) {
|
|
138
|
-
// Normalize the interface name: treat an empty string as undefined.
|
|
139
87
|
if (networkInterface === '')
|
|
140
88
|
networkInterface = undefined;
|
|
141
89
|
const interfaces = os.networkInterfaces();
|
|
142
|
-
// If a specific interface is provided but not found, warn and fall back.
|
|
143
90
|
if (networkInterface && !interfaces[networkInterface]) {
|
|
144
91
|
this.log.warn(`Interface "${networkInterface}" not found. Using first external IPv4 interface.`);
|
|
145
92
|
networkInterface = undefined;
|
|
146
93
|
}
|
|
147
|
-
// If no interface was specified or the provided one doesn't exist, find the first external IPv4 interface.
|
|
148
94
|
if (!networkInterface) {
|
|
149
95
|
for (const [interfaceName, interfaceDetails] of Object.entries(interfaces)) {
|
|
150
96
|
if (!interfaceDetails)
|
|
151
97
|
continue;
|
|
152
|
-
// Check if at least one external IPv4 address exists on this interface.
|
|
153
98
|
for (const detail of interfaceDetails) {
|
|
154
99
|
if (detail.family === 'IPv4' && !detail.internal) {
|
|
155
100
|
networkInterface = interfaceName;
|
|
@@ -163,7 +108,6 @@ export class Dgram extends EventEmitter {
|
|
|
163
108
|
if (!networkInterface) {
|
|
164
109
|
throw new Error(`Didn't find an external IPv4 network interface`);
|
|
165
110
|
}
|
|
166
|
-
// Select the first external IPv4 address from the interface.
|
|
167
111
|
const addresses = interfaces[networkInterface];
|
|
168
112
|
const ipv4Address = addresses?.find((addr) => addr.family === 'IPv4' && !addr.internal);
|
|
169
113
|
if (!ipv4Address) {
|
|
@@ -171,25 +115,14 @@ export class Dgram extends EventEmitter {
|
|
|
171
115
|
}
|
|
172
116
|
return ipv4Address.address;
|
|
173
117
|
}
|
|
174
|
-
/**
|
|
175
|
-
* Retrieves the IPv6 address of the specified network interface or the first external IPv6 interface if no interface is specified.
|
|
176
|
-
* Throws an error if no suitable interface or address is found.
|
|
177
|
-
*
|
|
178
|
-
* @param {string} [networkInterface] - The name of the network interface to retrieve the IPv6 address from. If not specified, the first external IPv6 interface will be used.
|
|
179
|
-
* @returns {string | undefined} The IPv6 address of the specified network interface or the first external IPv6 interface.
|
|
180
|
-
* @throws {Error} If no suitable interface or address is found.
|
|
181
|
-
*/
|
|
182
118
|
getIpv6InterfaceAddress(networkInterface) {
|
|
183
|
-
// Normalize the interface name: treat an empty string as undefined.
|
|
184
119
|
if (networkInterface === '')
|
|
185
120
|
networkInterface = undefined;
|
|
186
121
|
const interfaces = os.networkInterfaces();
|
|
187
|
-
// If a specific interface is provided, verify it exists. Otherwise, warn and use the first external IPv6 interface.
|
|
188
122
|
if (networkInterface && !interfaces[networkInterface]) {
|
|
189
123
|
this.log.warn(`Interface "${networkInterface}" not found. Using first external IPv6 interface.`);
|
|
190
124
|
networkInterface = undefined;
|
|
191
125
|
}
|
|
192
|
-
// If no network interface was specified, search for the first external IPv6 interface.
|
|
193
126
|
if (!networkInterface) {
|
|
194
127
|
for (const [interfaceName, interfaceDetails] of Object.entries(interfaces)) {
|
|
195
128
|
if (!interfaceDetails)
|
|
@@ -208,21 +141,18 @@ export class Dgram extends EventEmitter {
|
|
|
208
141
|
throw new Error(`Didn't find an external IPv6 network interface`);
|
|
209
142
|
}
|
|
210
143
|
const addresses = interfaces[networkInterface];
|
|
211
|
-
// Try to find a link-local address and use scopeid
|
|
212
144
|
const linkLocalAddress = addresses?.find((addr) => addr.family === 'IPv6' && !addr.internal && addr.address.startsWith('fe80'));
|
|
213
145
|
if (linkLocalAddress) {
|
|
214
146
|
this.log.debug('Found IPv6 link-local address');
|
|
215
147
|
return linkLocalAddress.scopeid ? `${linkLocalAddress.address}%${process.platform !== 'win32' ? networkInterface : linkLocalAddress.scopeid}` : linkLocalAddress.address;
|
|
216
148
|
}
|
|
217
149
|
this.log.debug('No IPv6 link-local address found');
|
|
218
|
-
// Try to find a unique local address
|
|
219
150
|
const ulaAddress = addresses?.find((addr) => addr.family === 'IPv6' && !addr.internal && addr.address.startsWith('fd') && addr.netmask === 'ffff:ffff:ffff:ffff::');
|
|
220
151
|
if (ulaAddress) {
|
|
221
152
|
this.log.debug('Found IPv6 Unique Local Addresses (ULA) unicast address');
|
|
222
153
|
return ulaAddress.address;
|
|
223
154
|
}
|
|
224
155
|
this.log.debug('No IPv6 Unique Local Addresses (ULA) unicast address found');
|
|
225
|
-
// Try to find a unique local address
|
|
226
156
|
const uniqueLocalAddress = addresses?.find((addr) => addr.family === 'IPv6' && !addr.internal && addr.address.startsWith('fd'));
|
|
227
157
|
if (uniqueLocalAddress) {
|
|
228
158
|
this.log.debug('Found IPv6 Unique Local Addresses (ULA) address');
|
|
@@ -231,11 +161,6 @@ export class Dgram extends EventEmitter {
|
|
|
231
161
|
this.log.debug('No IPv6 Unique Local Addresses (ULA) address found');
|
|
232
162
|
throw new Error(`Interface ${networkInterface} does not have a suitable external IPv6 address`);
|
|
233
163
|
}
|
|
234
|
-
/**
|
|
235
|
-
* Retrieves the names of all available network interfaces.
|
|
236
|
-
*
|
|
237
|
-
* @returns {string[]} An array of network interface names.
|
|
238
|
-
*/
|
|
239
164
|
getInterfacesNames() {
|
|
240
165
|
const interfaces = os.networkInterfaces();
|
|
241
166
|
const interfaceNames = [];
|
|
@@ -246,11 +171,6 @@ export class Dgram extends EventEmitter {
|
|
|
246
171
|
}
|
|
247
172
|
return interfaceNames;
|
|
248
173
|
}
|
|
249
|
-
/**
|
|
250
|
-
* Retrieves the scope ID for all interfaces address '::'.
|
|
251
|
-
*
|
|
252
|
-
* @returns {string} The scope ID of the first found IPv6 address or an empty string.
|
|
253
|
-
*/
|
|
254
174
|
getIpv6ScopeIdForAllInterfacesAddress() {
|
|
255
175
|
const interfaces = os.networkInterfaces();
|
|
256
176
|
for (const name in interfaces) {
|
|
@@ -258,24 +178,17 @@ export class Dgram extends EventEmitter {
|
|
|
258
178
|
if (iface) {
|
|
259
179
|
const ipv6Address = iface.find((addr) => addr.family === 'IPv6' && !addr.internal && addr.scopeid);
|
|
260
180
|
if (ipv6Address) {
|
|
261
|
-
return process.platform === 'win32' ? '%' + String(ipv6Address.scopeid) : '%' + name;
|
|
181
|
+
return process.platform === 'win32' ? '%' + String(ipv6Address.scopeid) : '%' + name;
|
|
262
182
|
}
|
|
263
183
|
}
|
|
264
184
|
}
|
|
265
185
|
return '';
|
|
266
186
|
}
|
|
267
|
-
/**
|
|
268
|
-
* Retrieves the interface name from the scope id of an IPv6 address.
|
|
269
|
-
*
|
|
270
|
-
* @param {number} scopeId - The scope id of the IPv6 address.
|
|
271
|
-
* @returns {string | undefined} The interface name or undefined if not found.
|
|
272
|
-
*/
|
|
273
187
|
getInterfaceNameFromScopeId(scopeId) {
|
|
274
188
|
const nets = os.networkInterfaces();
|
|
275
189
|
for (const ifaceName in nets) {
|
|
276
190
|
const addresses = nets[ifaceName] || [];
|
|
277
191
|
for (const addr of addresses) {
|
|
278
|
-
// Check for IPv6 addresses with a matching scope id.
|
|
279
192
|
if (addr.family === 'IPv6' && addr.scopeid === scopeId) {
|
|
280
193
|
return ifaceName;
|
|
281
194
|
}
|
|
@@ -283,16 +196,8 @@ export class Dgram extends EventEmitter {
|
|
|
283
196
|
}
|
|
284
197
|
return undefined;
|
|
285
198
|
}
|
|
286
|
-
/**
|
|
287
|
-
* Retrieves the netmask of the specified interface address.
|
|
288
|
-
*
|
|
289
|
-
* @param {string} interfaceAddress - The interface address for which to retrieve the netmask.
|
|
290
|
-
* @returns {string | undefined} The netmask of the specified interface address or undefined if not found.
|
|
291
|
-
*/
|
|
292
199
|
getNetmask(interfaceAddress) {
|
|
293
|
-
// Remove zone index if present (e.g. for IPv6 "fe80::1%eth0")
|
|
294
200
|
const cleanedAddress = interfaceAddress.includes('%') ? interfaceAddress.split('%')[0] : interfaceAddress;
|
|
295
|
-
// Iterate over all interfaces.
|
|
296
201
|
const nets = os.networkInterfaces();
|
|
297
202
|
for (const ifaceName in nets) {
|
|
298
203
|
const ifaceAddresses = nets[ifaceName];
|
|
@@ -306,13 +211,6 @@ export class Dgram extends EventEmitter {
|
|
|
306
211
|
}
|
|
307
212
|
return undefined;
|
|
308
213
|
}
|
|
309
|
-
/**
|
|
310
|
-
* Computes the broadcast address given an IPv4 address and netmask.
|
|
311
|
-
*
|
|
312
|
-
* @param {string | undefined} [ipAddress] - The IPv4 address e.g. "192.168.1.20"
|
|
313
|
-
* @param {string | undefined} [netmask] - The IPv4 netmask e.g. "255.255.255.0"
|
|
314
|
-
* @returns {string | undefined} The computed broadcast address, e.g. "192.168.1.255"
|
|
315
|
-
*/
|
|
316
214
|
getIpv4BroadcastAddress(ipAddress, netmask) {
|
|
317
215
|
if (!ipAddress || !netmask) {
|
|
318
216
|
return undefined;
|
|
@@ -322,17 +220,9 @@ export class Dgram extends EventEmitter {
|
|
|
322
220
|
const broadcastParts = ipParts.map((octet, i) => (octet & maskParts[i]) | (255 - maskParts[i]));
|
|
323
221
|
return broadcastParts.join('.');
|
|
324
222
|
}
|
|
325
|
-
/**
|
|
326
|
-
* Returns the broadcast IPv6 address.
|
|
327
|
-
*
|
|
328
|
-
* @returns {string} The broadcast IPv6 address, e.g. "ff02::1"
|
|
329
|
-
*/
|
|
330
223
|
getIpv6BroadcastAddress() {
|
|
331
224
|
return 'ff02::1';
|
|
332
225
|
}
|
|
333
|
-
/**
|
|
334
|
-
* Logs all available network interfaces and their details.
|
|
335
|
-
*/
|
|
336
226
|
listNetworkInterfaces() {
|
|
337
227
|
const interfaces = os.networkInterfaces();
|
|
338
228
|
for (const [name, addresses] of Object.entries(interfaces)) {
|
|
@@ -345,4 +235,3 @@ export class Dgram extends EventEmitter {
|
|
|
345
235
|
}
|
|
346
236
|
}
|
|
347
237
|
}
|
|
348
|
-
//# sourceMappingURL=dgram.js.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { COAP_MULTICAST_IPV4_ADDRESS, COAP_MULTICAST_IPV6_ADDRESS, COAP_MULTICAST_PORT } from './multicast.js';
|
|
2
|
+
import { Coap, COAP_OPTION_URI_PATH } from './coap.js';
|
|
3
|
+
{
|
|
4
|
+
const coapIpv4 = new Coap('CoAP Server udp4', COAP_MULTICAST_IPV4_ADDRESS, COAP_MULTICAST_PORT, 'udp4', true);
|
|
5
|
+
const coapIpv6 = new Coap('CoAP Server udp6', COAP_MULTICAST_IPV6_ADDRESS, COAP_MULTICAST_PORT, 'udp6', true);
|
|
6
|
+
coapIpv4.listNetworkInterfaces();
|
|
7
|
+
function cleanupAndLogAndExit() {
|
|
8
|
+
if (process.argv.includes('--coap-udp4'))
|
|
9
|
+
coapIpv4.stop();
|
|
10
|
+
if (process.argv.includes('--coap-udp6'))
|
|
11
|
+
coapIpv6.stop();
|
|
12
|
+
process.exit(0);
|
|
13
|
+
}
|
|
14
|
+
const requestUdp4 = () => {
|
|
15
|
+
coapIpv4.sendRequest(32000, [
|
|
16
|
+
{ number: COAP_OPTION_URI_PATH, value: Buffer.from('cit') },
|
|
17
|
+
{ number: COAP_OPTION_URI_PATH, value: Buffer.from('d') },
|
|
18
|
+
], {}, undefined, COAP_MULTICAST_IPV4_ADDRESS, COAP_MULTICAST_PORT);
|
|
19
|
+
};
|
|
20
|
+
const requestUdp6 = () => {
|
|
21
|
+
coapIpv6.sendRequest(32000, [
|
|
22
|
+
{ number: COAP_OPTION_URI_PATH, value: Buffer.from('cit') },
|
|
23
|
+
{ number: COAP_OPTION_URI_PATH, value: Buffer.from('d') },
|
|
24
|
+
], {}, undefined, COAP_MULTICAST_IPV6_ADDRESS, COAP_MULTICAST_PORT);
|
|
25
|
+
};
|
|
26
|
+
process.on('SIGINT', () => {
|
|
27
|
+
cleanupAndLogAndExit();
|
|
28
|
+
});
|
|
29
|
+
coapIpv4.start();
|
|
30
|
+
coapIpv4.on('ready', (address) => {
|
|
31
|
+
coapIpv4.log.info(`coapIpv4 server ready on ${address.family} ${address.address}:${address.port}`);
|
|
32
|
+
if (!process.argv.includes('--coap-request'))
|
|
33
|
+
return;
|
|
34
|
+
requestUdp4();
|
|
35
|
+
setInterval(() => {
|
|
36
|
+
requestUdp4();
|
|
37
|
+
}, 10000).unref();
|
|
38
|
+
});
|
|
39
|
+
coapIpv6.start();
|
|
40
|
+
coapIpv6.on('ready', (address) => {
|
|
41
|
+
coapIpv6.log.info(`coapIpv6 server ready on ${address.family} ${address.address}:${address.port}`);
|
|
42
|
+
if (!process.argv.includes('--coap-request'))
|
|
43
|
+
return;
|
|
44
|
+
requestUdp6();
|
|
45
|
+
setInterval(() => {
|
|
46
|
+
requestUdp6();
|
|
47
|
+
}, 10000).unref();
|
|
48
|
+
});
|
|
49
|
+
setTimeout(() => {
|
|
50
|
+
cleanupAndLogAndExit();
|
|
51
|
+
}, 600000);
|
|
52
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { MDNS_MULTICAST_IPV4_ADDRESS, MDNS_MULTICAST_IPV6_ADDRESS, MDNS_MULTICAST_PORT } from './multicast.js';
|
|
2
|
+
import { Mdns } from './mdns.js';
|
|
3
|
+
{
|
|
4
|
+
const mdnsIpv4 = new Mdns('mDNS Server udp4', MDNS_MULTICAST_IPV4_ADDRESS, MDNS_MULTICAST_PORT, 'udp4', true, undefined, '0.0.0.0');
|
|
5
|
+
const mdnsIpv6 = new Mdns('mDNS Server udp6', MDNS_MULTICAST_IPV6_ADDRESS, MDNS_MULTICAST_PORT, 'udp6', true, undefined, '::');
|
|
6
|
+
mdnsIpv4.listNetworkInterfaces();
|
|
7
|
+
function cleanupAndLogAndExit() {
|
|
8
|
+
mdnsIpv4.stop();
|
|
9
|
+
mdnsIpv6.stop();
|
|
10
|
+
mdnsIpv4.logDevices();
|
|
11
|
+
mdnsIpv6.logDevices();
|
|
12
|
+
process.exit(0);
|
|
13
|
+
}
|
|
14
|
+
const queryUdp4 = () => {
|
|
15
|
+
mdnsIpv4.sendQuery([
|
|
16
|
+
{ name: '_matter._tcp.local', type: 12, class: 1, unicastResponse: false },
|
|
17
|
+
{ name: '_shelly._tcp.local', type: 12, class: 1, unicastResponse: false },
|
|
18
|
+
{ name: '_http._tcp.local', type: 12, class: 1, unicastResponse: false },
|
|
19
|
+
{ name: '_services._dns-sd._udp.local', type: 12, class: 1, unicastResponse: false },
|
|
20
|
+
]);
|
|
21
|
+
const ptrRdata = mdnsIpv4.encodeDnsName('matterbridge._http._tcp.local');
|
|
22
|
+
mdnsIpv4.sendResponse('_http._tcp.local', 12, 1, 120, ptrRdata);
|
|
23
|
+
};
|
|
24
|
+
const queryUdp6 = () => {
|
|
25
|
+
mdnsIpv6.sendQuery([
|
|
26
|
+
{ name: '_matter._tcp.local', type: 12, class: 1, unicastResponse: true },
|
|
27
|
+
{ name: '_shelly._tcp.local', type: 12, class: 1, unicastResponse: true },
|
|
28
|
+
{ name: '_http._tcp.local', type: 12, class: 1, unicastResponse: true },
|
|
29
|
+
{ name: '_services._dns-sd._udp.local', type: 12, class: 1, unicastResponse: true },
|
|
30
|
+
]);
|
|
31
|
+
const ptrRdata = mdnsIpv6.encodeDnsName('matterbridge._http._tcp.local');
|
|
32
|
+
mdnsIpv6.sendResponse('_http._tcp.local', 12, 1, 120, ptrRdata);
|
|
33
|
+
};
|
|
34
|
+
process.on('SIGINT', () => {
|
|
35
|
+
cleanupAndLogAndExit();
|
|
36
|
+
});
|
|
37
|
+
mdnsIpv4.start();
|
|
38
|
+
mdnsIpv4.on('ready', (address) => {
|
|
39
|
+
mdnsIpv4.log.info(`mdnsIpv4 server ready on ${address.family} ${address.address}:${address.port}`);
|
|
40
|
+
if (!process.argv.includes('--query'))
|
|
41
|
+
return;
|
|
42
|
+
queryUdp4();
|
|
43
|
+
setInterval(() => {
|
|
44
|
+
queryUdp4();
|
|
45
|
+
}, 10000).unref();
|
|
46
|
+
});
|
|
47
|
+
mdnsIpv6.start();
|
|
48
|
+
mdnsIpv6.on('ready', (address) => {
|
|
49
|
+
mdnsIpv6.log.info(`mdnsIpv6 server ready on ${address.family} ${address.address}:${address.port}`);
|
|
50
|
+
if (!process.argv.includes('--query'))
|
|
51
|
+
return;
|
|
52
|
+
queryUdp6();
|
|
53
|
+
setInterval(() => {
|
|
54
|
+
queryUdp6();
|
|
55
|
+
}, 10000).unref();
|
|
56
|
+
});
|
|
57
|
+
setTimeout(() => {
|
|
58
|
+
cleanupAndLogAndExit();
|
|
59
|
+
}, 600000);
|
|
60
|
+
}
|