iobroker.sonoff 3.3.0 → 4.0.0

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,485 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ const node_net_1 = require("node:net");
40
+ // @ts-expect-error no types
41
+ const mqtt_connection_1 = __importDefault(require("mqtt-connection"));
42
+ const mqttBase_1 = __importStar(require("./mqttBase"));
43
+ /**
44
+ * MQTT Server constructor
45
+ */
46
+ class MQTTServer extends mqttBase_1.default {
47
+ server;
48
+ messageId = 1;
49
+ persistentSessions = {};
50
+ resending = false;
51
+ resendTimer = null;
52
+ constructor(adapter) {
53
+ super(adapter);
54
+ this.server = new node_net_1.Server();
55
+ if (this.config.timeout === undefined) {
56
+ this.config.timeout = 300;
57
+ }
58
+ else {
59
+ this.config.timeout = parseInt(this.config.timeout, 10);
60
+ }
61
+ this.server.on('connection', stream => {
62
+ const client = (0, mqtt_connection_1.default)(stream);
63
+ // Store unique connection identifier
64
+ client.__secret = `${Date.now()}_${Math.round(Math.random() * 10000)}`;
65
+ // client connected
66
+ client.on('connect', async (options) => {
67
+ // acknowledge the "connect" packet
68
+ client.id = options.clientId;
69
+ client.iobId = client.id.replace(mqttBase_1.FORBIDDEN_CHARS, '_');
70
+ this.mappingClients[client.iobId] = client.id;
71
+ // get possible an old client
72
+ const oldClient = this.clients[client.id];
73
+ if (this.config.user) {
74
+ if (options.password) {
75
+ options.password = options.password.toString();
76
+ }
77
+ if (this.config.user !== options.username || this.config.password !== options.password) {
78
+ this.adapter.log.warn(`Client [${client.id}] has invalid password or username`);
79
+ client.connack({ returnCode: 4 });
80
+ if (oldClient) {
81
+ // delete existing client
82
+ delete this.clients[client.id];
83
+ await this.updateAlive(oldClient, false);
84
+ await this.updateClients();
85
+ oldClient.destroy();
86
+ }
87
+ client.destroy();
88
+ return;
89
+ }
90
+ }
91
+ if (oldClient) {
92
+ this.adapter.log.info(`Client [${client.id}] reconnected. Old secret ${this.clients[client.id].__secret} ==> New secret ${client.__secret}`);
93
+ // need to destroy the old client
94
+ if (client.__secret !== this.clients[client.id].__secret) {
95
+ // it is another socket!!
96
+ // It was following situation:
97
+ // - old connection was active
98
+ // - new connection is on the same TCP
99
+ // Just forget him
100
+ // oldClient.destroy();
101
+ }
102
+ }
103
+ else {
104
+ this.adapter.log.info(`Client [${client.id}] connected with secret ${client.__secret}`);
105
+ }
106
+ let sessionPresent = false;
107
+ if (!client.cleanSession && this.config.storeClientsTime !== 0) {
108
+ if (this.persistentSessions[client.id]) {
109
+ sessionPresent = true;
110
+ this.persistentSessions[client.id].lastSeen = Date.now();
111
+ }
112
+ else {
113
+ this.persistentSessions[client.id] = {
114
+ _subsID: {},
115
+ _subs: {},
116
+ messages: [],
117
+ lastSeen: Date.now(),
118
+ };
119
+ }
120
+ client._messages = this.persistentSessions[client.id].messages;
121
+ this.persistentSessions[client.id].connected = true;
122
+ }
123
+ else if (client.cleanSession && this.persistentSessions[client.id]) {
124
+ delete this.persistentSessions[client.id];
125
+ }
126
+ client._messages ||= [];
127
+ client.connack({ returnCode: 0, sessionPresent });
128
+ this.clients[client.id] = client;
129
+ await this.updateClients();
130
+ client._will = options.will;
131
+ this.createClient(client, () => {
132
+ if (this.persistentSessions[client.id]) {
133
+ client._subsID = this.persistentSessions[client.id]._subsID;
134
+ client._subs = this.persistentSessions[client.id]._subs;
135
+ if (this.persistentSessions[client.id].messages.length) {
136
+ // give to the client a little bit time
137
+ client._resendonStart = setTimeout(clientId => {
138
+ client._resendonStart = null;
139
+ this.resendMessages2Client(client, this.persistentSessions[clientId].messages);
140
+ }, 100, client.id);
141
+ }
142
+ }
143
+ });
144
+ });
145
+ // timeout idle streams after 5 minutes
146
+ if (this.config.timeout) {
147
+ stream.setTimeout(this.config.timeout * 1000);
148
+ }
149
+ // connection error handling
150
+ client.on('close', hadError => this.clientClose(client, hadError ? 'closed because of error' : 'closed'));
151
+ client.on('error', e => this.clientClose(client, e));
152
+ client.on('disconnect', () => this.clientClose(client, 'disconnected'));
153
+ // stream timeout
154
+ stream.on('timeout', () => this.clientClose(client, 'timeout'));
155
+ client.on('publish', async (packet) => {
156
+ if (this.clients[client.id] && client.__secret !== this.clients[client.id].__secret) {
157
+ !this.config.ignorePings &&
158
+ this.adapter.log.warn(`Old client ${client.id} with secret ${client.__secret} sends publish. Ignore! Actual secret is ${this.clients[client.id].__secret}`);
159
+ return;
160
+ }
161
+ if (this.persistentSessions[client.id]) {
162
+ this.persistentSessions[client.id].lastSeen = Date.now();
163
+ }
164
+ const packetStored = packet;
165
+ if (packetStored.qos === 1) {
166
+ // send PUBACK to a client
167
+ client.puback({ messageId: packetStored.messageId });
168
+ }
169
+ else if (packet.qos === 2) {
170
+ const pack = client._messages?.find(e => e.messageId === packetStored.messageId);
171
+ if (pack) {
172
+ // duplicate message => ignore
173
+ this.adapter.log.warn(`Client [${client.id}] ignored duplicate message with ID: ${packetStored.messageId}`);
174
+ return;
175
+ }
176
+ packetStored.ts = Date.now();
177
+ packetStored.cmd = 'pubrel';
178
+ packetStored.count = 0;
179
+ client._messages.push(packetStored);
180
+ client.pubrec({ messageId: packetStored.messageId });
181
+ return;
182
+ }
183
+ await this.receivedTopic(packetStored, client);
184
+ });
185
+ // response for QoS2
186
+ client.on('pubrec', packet => {
187
+ if (this.clients[client.id] && client.__secret !== this.clients[client.id].__secret) {
188
+ !this.config.ignorePings &&
189
+ this.adapter.log.warn(`Old client ${client.id} with secret ${client.__secret} sends pubrec. Ignore! Actual secret is ${this.clients[client.id].__secret}`);
190
+ return;
191
+ }
192
+ if (this.persistentSessions[client.id]) {
193
+ this.persistentSessions[client.id].lastSeen = Date.now();
194
+ }
195
+ let pos = null;
196
+ // remove this message from queue
197
+ client._messages?.forEach((e, i) => {
198
+ if (e.messageId === packet.messageId) {
199
+ pos = i;
200
+ return false;
201
+ }
202
+ });
203
+ if (pos !== null) {
204
+ client.pubrel({ messageId: packet.messageId });
205
+ }
206
+ else {
207
+ this.adapter.log.warn(`Client [${client.id}] received pubrec on ${client.id} for unknown message ID: ${packet.messageId}`);
208
+ }
209
+ });
210
+ // response for QoS2
211
+ client.on('pubcomp', packet => {
212
+ if (this.clients[client.id] && client.__secret !== this.clients[client.id].__secret) {
213
+ !this.config.ignorePings &&
214
+ this.adapter.log.warn(`Old client ${client.id} with secret ${client.__secret} sends pubcomp. Ignore! Actual secret is ${this.clients[client.id].__secret}`);
215
+ return;
216
+ }
217
+ if (this.persistentSessions[client.id]) {
218
+ this.persistentSessions[client.id].lastSeen = Date.now();
219
+ }
220
+ let pos = null;
221
+ // remove this message from queue
222
+ client._messages?.forEach((e, i) => {
223
+ if (e.messageId === packet.messageId) {
224
+ pos = i;
225
+ return false;
226
+ }
227
+ });
228
+ if (pos !== null) {
229
+ client._messages?.splice(pos, 1);
230
+ }
231
+ else {
232
+ this.adapter.log.warn(`Client [${client.id}] received pubcomp for unknown message ID: ${packet.messageId}`);
233
+ }
234
+ });
235
+ // response for QoS2
236
+ client.on('pubrel', async (packet) => {
237
+ if (!client._messages) {
238
+ return;
239
+ }
240
+ if (this.clients[client.id] && client.__secret !== this.clients[client.id].__secret) {
241
+ !this.config.ignorePings &&
242
+ this.adapter.log.warn(`Old client ${client.id} with secret ${client.__secret} sends pubrel. Ignore! Actual secret is ${this.clients[client.id].__secret}`);
243
+ return;
244
+ }
245
+ if (this.persistentSessions[client.id]) {
246
+ this.persistentSessions[client.id].lastSeen = Date.now();
247
+ }
248
+ let pos = null;
249
+ // remove this message from queue
250
+ client._messages?.forEach((e, i) => {
251
+ if (e.messageId === packet.messageId) {
252
+ pos = i;
253
+ return false;
254
+ }
255
+ });
256
+ if (pos !== null) {
257
+ client.pubcomp({ messageId: packet.messageId });
258
+ await this.receivedTopic(client._messages[pos], client);
259
+ client._messages?.splice(pos, 1);
260
+ }
261
+ else {
262
+ this.adapter.log.warn(`Client [${client.id}] received pubrel on ${client.id} for unknown messageId ${packet.messageId}`);
263
+ }
264
+ });
265
+ // response for QoS1
266
+ client.on('puback', (packet) => {
267
+ if (this.clients[client.id] && client.__secret !== this.clients[client.id].__secret) {
268
+ !this.config.ignorePings &&
269
+ this.adapter.log.warn(`Old client ${client.id} with secret ${client.__secret} sends puback. Ignore! Actual secret is ${this.clients[client.id].__secret}`);
270
+ return;
271
+ }
272
+ if (this.persistentSessions[client.id]) {
273
+ this.persistentSessions[client.id].lastSeen = Date.now();
274
+ }
275
+ // remove this message from queue
276
+ let pos = null;
277
+ // remove this message from queue
278
+ client._messages?.forEach((e, i) => {
279
+ if (e.messageId === packet.messageId) {
280
+ pos = i;
281
+ return false;
282
+ }
283
+ });
284
+ if (pos !== null) {
285
+ this.adapter.log.debug(`Client [${client.id}] received puback for ${client.id} message ID: ${packet.messageId}`);
286
+ client._messages?.splice(pos, 1);
287
+ }
288
+ else {
289
+ this.adapter.log.warn(`Client [${client.id}] received puback for unknown message ID: ${packet.messageId}`);
290
+ }
291
+ });
292
+ client.on('unsubscribe', (packet) => {
293
+ if (this.clients[client.id] && client.__secret !== this.clients[client.id].__secret) {
294
+ !this.config.ignorePings &&
295
+ this.adapter.log.warn(`Old client ${client.id} with secret ${client.__secret} sends unsubscribe. Ignore! Actual secret is ${this.clients[client.id].__secret}`);
296
+ return;
297
+ }
298
+ if (this.persistentSessions[client.id]) {
299
+ this.persistentSessions[client.id].lastSeen = Date.now();
300
+ }
301
+ client.unsuback({ messageId: packet.messageId });
302
+ });
303
+ client.on('subscribe', (packet) => {
304
+ if (this.clients[client.id] && client.__secret !== this.clients[client.id].__secret) {
305
+ !this.config.ignorePings &&
306
+ this.adapter.log.warn(`Old client ${client.id} with secret ${client.__secret} sends subscribe. Ignore! Actual secret is ${this.clients[client.id].__secret}`);
307
+ return;
308
+ }
309
+ if (this.persistentSessions[client.id]) {
310
+ this.persistentSessions[client.id].lastSeen = Date.now();
311
+ }
312
+ // just confirm the request.
313
+ // we expect subscribe for 'cmnd.sonoff.#'
314
+ const granted = packet.subscriptions.map(subs => subs.qos);
315
+ client.suback({ granted: granted, messageId: packet.messageId });
316
+ });
317
+ client.on('pingreq', ( /*packet*/) => {
318
+ if (this.clients[client.id] && client.__secret !== this.clients[client.id].__secret) {
319
+ !this.config.ignorePings &&
320
+ this.adapter.log.warn(`Old client ${client.id} with secret ${client.__secret} sends pingreq. Ignore! Actual secret is ${this.clients[client.id].__secret}`);
321
+ return;
322
+ }
323
+ if (this.persistentSessions[client.id]) {
324
+ this.persistentSessions[client.id].lastSeen = Date.now();
325
+ }
326
+ this.adapter.log.debug(`Client [${client.id}] pingreq`);
327
+ client.pingresp();
328
+ });
329
+ });
330
+ this.server.on('error', err => this.adapter.log.error(`Can not start Server ${err}`));
331
+ this.config.port = parseInt(this.config.port, 10) || 1883;
332
+ this.config.retransmitInterval = this.config.retransmitInterval || 2000;
333
+ this.config.retransmitCount = this.config.retransmitCount || 10;
334
+ this.config.storeClientsTime =
335
+ this.config.storeClientsTime === undefined
336
+ ? 1440
337
+ : parseInt(this.config.storeClientsTime, 10) || 0;
338
+ // to start
339
+ this.server.listen(this.config.port, this.config.bind, () => {
340
+ this.adapter.log.info(`Starting MQTT ${this.config.user ? 'authenticated ' : ''} server on port ${this.config.port}`);
341
+ // info.connection is filled with the list of the connected clients
342
+ this.updateClients().catch(err => this.adapter.log.error(`Cannot update clients: ${err}`));
343
+ this.resendTimer = setInterval(() => !this.resending && this.checkResends(), this.config.retransmitInterval || 2000);
344
+ });
345
+ }
346
+ async destroy() {
347
+ if (this.resendTimer) {
348
+ clearInterval(this.resendTimer);
349
+ this.resendTimer = null;
350
+ }
351
+ if (this.server) {
352
+ for (const id in this.clients) {
353
+ await this.adapter.setForeignStateAsync(`${this.adapter.namespace}.${this.clients[id].iobId}.alive`, false, true);
354
+ }
355
+ await this.adapter.setStateAsync('info.connection', '', true);
356
+ // to release all resources
357
+ return new Promise(resolve => this.server.close(() => resolve()));
358
+ }
359
+ }
360
+ async receivedTopic(packet, client) {
361
+ await super.receivedTopic(packet, client);
362
+ // Tasmota sends INFO1 - INFO3 only while booting, so the adapter does not know the MAC
363
+ // address, the IP address or the firmware version of a device which is already running.
364
+ // Ask for them once per connection, the answers are processed as STATUS5 and STATUS2
365
+ if (!client._infoRequested) {
366
+ const parts = packet.topic.split('/');
367
+ if ((parts[0] === 'tele' || parts[0] === 'stat') && parts.length > 2) {
368
+ client._infoRequested = true;
369
+ const topic = `cmnd/${parts.slice(1, -1).join('/')}/Status`;
370
+ this.sendState2Client(client, topic, 5, this.config.defaultQoS);
371
+ this.sendState2Client(client, topic, 2, this.config.defaultQoS);
372
+ }
373
+ }
374
+ }
375
+ sendState2Client(client, topic, state, qos, retain, cb) {
376
+ this.adapter.log.debug(`Send to "${client.id}": ${topic} = ${state}`);
377
+ client.publish({
378
+ topic,
379
+ payload: state === null ? 'null' : state.toString(),
380
+ qos,
381
+ retain,
382
+ messageId: this.messageId++,
383
+ }, cb);
384
+ this.messageId &= 0xffffffff;
385
+ }
386
+ resendMessages2Client(client, messages, i) {
387
+ i = i || 0;
388
+ if (messages && i < messages.length) {
389
+ try {
390
+ messages[i].ts = Date.now();
391
+ messages[i].count++;
392
+ this.adapter.log.debug(`Client [${client.id}] resend messages on connect: ${messages[i].topic} = ${messages[i].payload.toString()}`);
393
+ if (messages[i].cmd === 'publish') {
394
+ client.publish(messages[i]);
395
+ }
396
+ }
397
+ catch (e) {
398
+ this.adapter.log.warn(`Client [${client.id}] cannot resend message: ${e}`);
399
+ }
400
+ if (this.config.sendInterval) {
401
+ setTimeout(() => this.resendMessages2Client(client, messages, i + 1), this.config.sendInterval);
402
+ }
403
+ else {
404
+ setImmediate(() => this.resendMessages2Client(client, messages, i + 1));
405
+ }
406
+ }
407
+ }
408
+ async clientClose(client, reason) {
409
+ if (!client) {
410
+ return;
411
+ }
412
+ if (this.persistentSessions[client.id]) {
413
+ this.persistentSessions[client.id].connected = false;
414
+ }
415
+ if (client._resendonStart) {
416
+ clearTimeout(client._resendonStart);
417
+ client._resendonStart = null;
418
+ }
419
+ try {
420
+ if (this.clients[client.id] && client.__secret === this.clients[client.id].__secret) {
421
+ this.adapter.log.info(`Client [${client.id}] connection closed: ${reason}`);
422
+ await this.updateAlive(client, false);
423
+ delete this.clients[client.id];
424
+ await this.updateClients();
425
+ if (client._will) {
426
+ await this.receivedTopic(client._will, client);
427
+ }
428
+ }
429
+ client.destroy();
430
+ }
431
+ catch (e) {
432
+ this.adapter.log.warn(`Client [${client.id}] cannot close client: ${e}`);
433
+ }
434
+ }
435
+ checkResends() {
436
+ const now = Date.now();
437
+ this.resending = true;
438
+ for (const clientId in this.clients) {
439
+ if (Object.prototype.hasOwnProperty.call(this.clients, clientId) && this.clients[clientId]?._messages) {
440
+ for (let m = this.clients[clientId]._messages.length - 1; m >= 0; m--) {
441
+ const message = this.clients[clientId]._messages[m];
442
+ if (now - message.ts >= this.config.retransmitInterval) {
443
+ if (message.count > this.config.retransmitCount) {
444
+ this.adapter.log.warn(`Client [${clientId}] message ${message.messageId} deleted after ${message.count} retries`);
445
+ this.clients[clientId]._messages.splice(m, 1);
446
+ continue;
447
+ }
448
+ // resend this message
449
+ message.count++;
450
+ message.ts = now;
451
+ try {
452
+ this.adapter.log.debug(`Client [${clientId}] resend message topic: ${message.topic}, payload: ${message.payload.toString()}`);
453
+ if (message.cmd === 'publish') {
454
+ this.clients[clientId].publish(message);
455
+ }
456
+ }
457
+ catch (e) {
458
+ this.adapter.log.warn(`Client [${clientId}] cannot publish message: ${e}`);
459
+ }
460
+ if (this.config.sendInterval) {
461
+ setTimeout(() => this.checkResends(), this.config.sendInterval);
462
+ }
463
+ else {
464
+ setImmediate(() => this.checkResends());
465
+ }
466
+ return;
467
+ }
468
+ }
469
+ }
470
+ }
471
+ // delete old sessions
472
+ if (this.config.storeClientsTime !== -1) {
473
+ for (const id in this.persistentSessions) {
474
+ if (Object.prototype.hasOwnProperty.call(this.persistentSessions, id)) {
475
+ if (now - this.persistentSessions[id].lastSeen > this.config.storeClientsTime * 60000) {
476
+ delete this.persistentSessions[id];
477
+ }
478
+ }
479
+ }
480
+ }
481
+ this.resending = false;
482
+ }
483
+ }
484
+ exports.default = MQTTServer;
485
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/lib/server.ts"],"names":[],"mappings":"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","sourcesContent":["import { Server } from 'node:net';\n// @ts-expect-error no types\nimport mqtt from 'mqtt-connection';\nimport MQTTBase, { FORBIDDEN_CHARS, type MQTTClient, type MQTTPacket, type MQTTPacketStored } from './mqttBase';\n\n/**\n * MQTT Server constructor\n */\nexport default class MQTTServer extends MQTTBase {\n private readonly server: Server;\n private messageId = 1;\n private persistentSessions: {\n [id: string]: {\n connected?: boolean;\n lastSeen: number;\n messages: [];\n _subsID: { [key: string]: number };\n _subs: { [key: string]: string[] };\n };\n } = {};\n private resending = false;\n private resendTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(adapter: ioBroker.Adapter) {\n super(adapter);\n this.server = new Server();\n\n if (this.config.timeout === undefined) {\n this.config.timeout = 300;\n } else {\n this.config.timeout = parseInt(this.config.timeout as string, 10);\n }\n\n this.server.on('connection', stream => {\n const client: MQTTClient = mqtt(stream);\n // Store unique connection identifier\n client.__secret = `${Date.now()}_${Math.round(Math.random() * 10000)}`;\n\n // client connected\n client.on(\n 'connect',\n async (options: {\n clientId: string;\n password?: string;\n username?: string;\n will?: MQTTPacket;\n }): Promise<void> => {\n // acknowledge the \"connect\" packet\n client.id = options.clientId;\n client.iobId = client.id.replace(FORBIDDEN_CHARS, '_');\n this.mappingClients[client.iobId] = client.id;\n\n // get possible an old client\n const oldClient = this.clients[client.id];\n\n if (this.config.user) {\n if (options.password) {\n options.password = options.password.toString();\n }\n if (this.config.user !== options.username || this.config.password !== options.password) {\n this.adapter.log.warn(`Client [${client.id}] has invalid password or username`);\n client.connack({ returnCode: 4 });\n if (oldClient) {\n // delete existing client\n delete this.clients[client.id];\n await this.updateAlive(oldClient, false);\n await this.updateClients();\n oldClient.destroy();\n }\n client.destroy();\n return;\n }\n }\n\n if (oldClient) {\n this.adapter.log.info(\n `Client [${client.id}] reconnected. Old secret ${this.clients[client.id].__secret} ==> New secret ${client.__secret}`,\n );\n // need to destroy the old client\n\n if (client.__secret !== this.clients[client.id].__secret) {\n // it is another socket!!\n // It was following situation:\n // - old connection was active\n // - new connection is on the same TCP\n // Just forget him\n // oldClient.destroy();\n }\n } else {\n this.adapter.log.info(`Client [${client.id}] connected with secret ${client.__secret}`);\n }\n\n let sessionPresent = false;\n\n if (!client.cleanSession && this.config.storeClientsTime !== 0) {\n if (this.persistentSessions[client.id]) {\n sessionPresent = true;\n this.persistentSessions[client.id].lastSeen = Date.now();\n } else {\n this.persistentSessions[client.id] = {\n _subsID: {},\n _subs: {},\n messages: [],\n lastSeen: Date.now(),\n };\n }\n client._messages = this.persistentSessions[client.id].messages;\n this.persistentSessions[client.id].connected = true;\n } else if (client.cleanSession && this.persistentSessions[client.id]) {\n delete this.persistentSessions[client.id];\n }\n client._messages ||= [];\n\n client.connack({ returnCode: 0, sessionPresent });\n this.clients[client.id] = client;\n await this.updateClients();\n\n client._will = options.will;\n this.createClient(client, () => {\n if (this.persistentSessions[client.id]) {\n client._subsID = this.persistentSessions[client.id]._subsID;\n client._subs = this.persistentSessions[client.id]._subs;\n if (this.persistentSessions[client.id].messages.length) {\n // give to the client a little bit time\n client._resendonStart = setTimeout(\n clientId => {\n client._resendonStart = null;\n this.resendMessages2Client(client, this.persistentSessions[clientId].messages);\n },\n 100,\n client.id,\n );\n }\n }\n });\n },\n );\n\n // timeout idle streams after 5 minutes\n if (this.config.timeout) {\n stream.setTimeout((this.config.timeout as number) * 1000);\n }\n\n // connection error handling\n client.on('close', hadError => this.clientClose(client, hadError ? 'closed because of error' : 'closed'));\n client.on('error', e => this.clientClose(client, e));\n client.on('disconnect', () => this.clientClose(client, 'disconnected'));\n // stream timeout\n stream.on('timeout', () => this.clientClose(client, 'timeout'));\n\n client.on('publish', async (packet: MQTTPacket): Promise<void> => {\n if (this.clients[client.id] && client.__secret !== this.clients[client.id].__secret) {\n !this.config.ignorePings &&\n this.adapter.log.warn(\n `Old client ${client.id} with secret ${client.__secret} sends publish. Ignore! Actual secret is ${this.clients[client.id].__secret}`,\n );\n return;\n }\n\n if (this.persistentSessions[client.id]) {\n this.persistentSessions[client.id].lastSeen = Date.now();\n }\n const packetStored = packet as MQTTPacketStored;\n\n if (packetStored.qos === 1) {\n // send PUBACK to a client\n client.puback({ messageId: packetStored.messageId });\n } else if (packet.qos === 2) {\n const pack = client._messages?.find(e => e.messageId === packetStored.messageId);\n if (pack) {\n // duplicate message => ignore\n this.adapter.log.warn(\n `Client [${client.id}] ignored duplicate message with ID: ${packetStored.messageId}`,\n );\n return;\n }\n packetStored.ts = Date.now();\n packetStored.cmd = 'pubrel';\n packetStored.count = 0;\n client._messages.push(packetStored);\n\n client.pubrec({ messageId: packetStored.messageId });\n return;\n }\n\n await this.receivedTopic(packetStored, client);\n });\n\n // response for QoS2\n client.on('pubrec', packet => {\n if (this.clients[client.id] && client.__secret !== this.clients[client.id].__secret) {\n !this.config.ignorePings &&\n this.adapter.log.warn(\n `Old client ${client.id} with secret ${client.__secret} sends pubrec. Ignore! Actual secret is ${this.clients[client.id].__secret}`,\n );\n return;\n }\n\n if (this.persistentSessions[client.id]) {\n this.persistentSessions[client.id].lastSeen = Date.now();\n }\n\n let pos = null;\n // remove this message from queue\n client._messages?.forEach((e, i) => {\n if (e.messageId === packet.messageId) {\n pos = i;\n return false;\n }\n });\n if (pos !== null) {\n client.pubrel({ messageId: packet.messageId });\n } else {\n this.adapter.log.warn(\n `Client [${client.id}] received pubrec on ${client.id} for unknown message ID: ${packet.messageId}`,\n );\n }\n });\n\n // response for QoS2\n client.on('pubcomp', packet => {\n if (this.clients[client.id] && client.__secret !== this.clients[client.id].__secret) {\n !this.config.ignorePings &&\n this.adapter.log.warn(\n `Old client ${client.id} with secret ${client.__secret} sends pubcomp. Ignore! Actual secret is ${this.clients[client.id].__secret}`,\n );\n return;\n }\n\n if (this.persistentSessions[client.id]) {\n this.persistentSessions[client.id].lastSeen = Date.now();\n }\n\n let pos = null;\n // remove this message from queue\n client._messages?.forEach((e, i) => {\n if (e.messageId === packet.messageId) {\n pos = i;\n return false;\n }\n });\n if (pos !== null) {\n client._messages?.splice(pos, 1);\n } else {\n this.adapter.log.warn(\n `Client [${client.id}] received pubcomp for unknown message ID: ${packet.messageId}`,\n );\n }\n });\n\n // response for QoS2\n client.on('pubrel', async (packet: MQTTPacket): Promise<void> => {\n if (!client._messages) {\n return;\n }\n if (this.clients[client.id] && client.__secret !== this.clients[client.id].__secret) {\n !this.config.ignorePings &&\n this.adapter.log.warn(\n `Old client ${client.id} with secret ${client.__secret} sends pubrel. Ignore! Actual secret is ${this.clients[client.id].__secret}`,\n );\n return;\n }\n\n if (this.persistentSessions[client.id]) {\n this.persistentSessions[client.id].lastSeen = Date.now();\n }\n\n let pos = null;\n // remove this message from queue\n client._messages?.forEach((e, i) => {\n if (e.messageId === packet.messageId) {\n pos = i;\n return false;\n }\n });\n\n if (pos !== null) {\n client.pubcomp({ messageId: packet.messageId });\n\n await this.receivedTopic(client._messages[pos], client);\n client._messages?.splice(pos, 1);\n } else {\n this.adapter.log.warn(\n `Client [${client.id}] received pubrel on ${client.id} for unknown messageId ${packet.messageId}`,\n );\n }\n });\n\n // response for QoS1\n client.on('puback', (packet: MQTTPacket): void => {\n if (this.clients[client.id] && client.__secret !== this.clients[client.id].__secret) {\n !this.config.ignorePings &&\n this.adapter.log.warn(\n `Old client ${client.id} with secret ${client.__secret} sends puback. Ignore! Actual secret is ${this.clients[client.id].__secret}`,\n );\n return;\n }\n\n if (this.persistentSessions[client.id]) {\n this.persistentSessions[client.id].lastSeen = Date.now();\n }\n\n // remove this message from queue\n let pos = null;\n // remove this message from queue\n client._messages?.forEach((e, i) => {\n if (e.messageId === packet.messageId) {\n pos = i;\n return false;\n }\n });\n\n if (pos !== null) {\n this.adapter.log.debug(\n `Client [${client.id}] received puback for ${client.id} message ID: ${packet.messageId}`,\n );\n client._messages?.splice(pos, 1);\n } else {\n this.adapter.log.warn(\n `Client [${client.id}] received puback for unknown message ID: ${packet.messageId}`,\n );\n }\n });\n\n client.on('unsubscribe', (packet: MQTTPacket): void => {\n if (this.clients[client.id] && client.__secret !== this.clients[client.id].__secret) {\n !this.config.ignorePings &&\n this.adapter.log.warn(\n `Old client ${client.id} with secret ${client.__secret} sends unsubscribe. Ignore! Actual secret is ${this.clients[client.id].__secret}`,\n );\n return;\n }\n\n if (this.persistentSessions[client.id]) {\n this.persistentSessions[client.id].lastSeen = Date.now();\n }\n\n client.unsuback({ messageId: packet.messageId });\n });\n\n client.on('subscribe', (packet: { subscriptions: { qos: 0 | 1 | 2 }[]; messageId: number }): void => {\n if (this.clients[client.id] && client.__secret !== this.clients[client.id].__secret) {\n !this.config.ignorePings &&\n this.adapter.log.warn(\n `Old client ${client.id} with secret ${client.__secret} sends subscribe. Ignore! Actual secret is ${this.clients[client.id].__secret}`,\n );\n return;\n }\n\n if (this.persistentSessions[client.id]) {\n this.persistentSessions[client.id].lastSeen = Date.now();\n }\n\n // just confirm the request.\n // we expect subscribe for 'cmnd.sonoff.#'\n const granted = packet.subscriptions.map(subs => subs.qos);\n\n client.suback({ granted: granted, messageId: packet.messageId });\n });\n\n client.on('pingreq', (/*packet*/) => {\n if (this.clients[client.id] && client.__secret !== this.clients[client.id].__secret) {\n !this.config.ignorePings &&\n this.adapter.log.warn(\n `Old client ${client.id} with secret ${client.__secret} sends pingreq. Ignore! Actual secret is ${this.clients[client.id].__secret}`,\n );\n return;\n }\n\n if (this.persistentSessions[client.id]) {\n this.persistentSessions[client.id].lastSeen = Date.now();\n }\n\n this.adapter.log.debug(`Client [${client.id}] pingreq`);\n client.pingresp();\n });\n });\n\n this.server.on('error', err => this.adapter.log.error(`Can not start Server ${err}`));\n\n this.config.port = parseInt(this.config.port as string, 10) || 1883;\n\n this.config.retransmitInterval = this.config.retransmitInterval || 2000;\n this.config.retransmitCount = this.config.retransmitCount || 10;\n this.config.storeClientsTime =\n this.config.storeClientsTime === undefined\n ? 1440\n : parseInt(this.config.storeClientsTime as string, 10) || 0;\n\n // to start\n this.server.listen(this.config.port, this.config.bind, () => {\n this.adapter.log.info(\n `Starting MQTT ${this.config.user ? 'authenticated ' : ''} server on port ${this.config.port}`,\n );\n // info.connection is filled with the list of the connected clients\n this.updateClients().catch(err => this.adapter.log.error(`Cannot update clients: ${err}`));\n\n this.resendTimer = setInterval(\n () => !this.resending && this.checkResends(),\n this.config.retransmitInterval || 2000,\n );\n });\n }\n\n async destroy(): Promise<void> {\n if (this.resendTimer) {\n clearInterval(this.resendTimer);\n this.resendTimer = null;\n }\n\n if (this.server) {\n for (const id in this.clients) {\n await this.adapter.setForeignStateAsync(\n `${this.adapter.namespace}.${this.clients[id].iobId}.alive`,\n false,\n true,\n );\n }\n await this.adapter.setStateAsync('info.connection', '', true);\n\n // to release all resources\n return new Promise(resolve => this.server.close(() => resolve()));\n }\n }\n\n protected async receivedTopic(packet: MQTTPacket, client: MQTTClient): Promise<void> {\n await super.receivedTopic(packet, client);\n\n // Tasmota sends INFO1 - INFO3 only while booting, so the adapter does not know the MAC\n // address, the IP address or the firmware version of a device which is already running.\n // Ask for them once per connection, the answers are processed as STATUS5 and STATUS2\n if (!client._infoRequested) {\n const parts = packet.topic.split('/');\n if ((parts[0] === 'tele' || parts[0] === 'stat') && parts.length > 2) {\n client._infoRequested = true;\n const topic = `cmnd/${parts.slice(1, -1).join('/')}/Status`;\n this.sendState2Client(client, topic, 5, this.config.defaultQoS);\n this.sendState2Client(client, topic, 2, this.config.defaultQoS);\n }\n }\n }\n\n protected sendState2Client(\n client: MQTTClient,\n topic: string,\n state: ioBroker.StateValue,\n qos: 0 | 1 | 2,\n retain?: boolean,\n cb?: () => void,\n ): void {\n this.adapter.log.debug(`Send to \"${client.id}\": ${topic} = ${state}`);\n client.publish(\n {\n topic,\n payload: state === null ? 'null' : state.toString(),\n qos,\n retain,\n messageId: this.messageId++,\n },\n cb,\n );\n this.messageId &= 0xffffffff;\n }\n\n private resendMessages2Client(client: MQTTClient, messages: MQTTPacketStored[], i?: number): void {\n i = i || 0;\n if (messages && i < messages.length) {\n try {\n messages[i].ts = Date.now();\n messages[i].count++;\n this.adapter.log.debug(\n `Client [${client.id}] resend messages on connect: ${messages[i].topic} = ${messages[i].payload.toString()}`,\n );\n if (messages[i].cmd === 'publish') {\n client.publish(messages[i]);\n }\n } catch (e) {\n this.adapter.log.warn(`Client [${client.id}] cannot resend message: ${e}`);\n }\n\n if (this.config.sendInterval) {\n setTimeout(() => this.resendMessages2Client(client, messages, i + 1), this.config.sendInterval);\n } else {\n setImmediate(() => this.resendMessages2Client(client, messages, i + 1));\n }\n }\n }\n\n private async clientClose(client: MQTTClient, reason: string): Promise<void> {\n if (!client) {\n return;\n }\n\n if (this.persistentSessions[client.id]) {\n this.persistentSessions[client.id].connected = false;\n }\n\n if (client._resendonStart) {\n clearTimeout(client._resendonStart);\n client._resendonStart = null;\n }\n\n try {\n if (this.clients[client.id] && client.__secret === this.clients[client.id].__secret) {\n this.adapter.log.info(`Client [${client.id}] connection closed: ${reason}`);\n await this.updateAlive(client, false);\n delete this.clients[client.id];\n await this.updateClients();\n if (client._will) {\n await this.receivedTopic(client._will, client);\n }\n }\n client.destroy();\n } catch (e) {\n this.adapter.log.warn(`Client [${client.id}] cannot close client: ${e}`);\n }\n }\n\n private checkResends(): void {\n const now = Date.now();\n this.resending = true;\n for (const clientId in this.clients) {\n if (Object.prototype.hasOwnProperty.call(this.clients, clientId) && this.clients[clientId]?._messages) {\n for (let m = this.clients[clientId]._messages.length - 1; m >= 0; m--) {\n const message = this.clients[clientId]._messages[m];\n if (now - message.ts >= this.config.retransmitInterval) {\n if (message.count > this.config.retransmitCount) {\n this.adapter.log.warn(\n `Client [${clientId}] message ${message.messageId} deleted after ${message.count} retries`,\n );\n this.clients[clientId]._messages.splice(m, 1);\n continue;\n }\n\n // resend this message\n message.count++;\n message.ts = now;\n try {\n this.adapter.log.debug(\n `Client [${clientId}] resend message topic: ${message.topic}, payload: ${message.payload.toString()}`,\n );\n if (message.cmd === 'publish') {\n this.clients[clientId].publish(message);\n }\n } catch (e) {\n this.adapter.log.warn(`Client [${clientId}] cannot publish message: ${e}`);\n }\n\n if (this.config.sendInterval) {\n setTimeout(() => this.checkResends(), this.config.sendInterval);\n } else {\n setImmediate(() => this.checkResends());\n }\n return;\n }\n }\n }\n }\n\n // delete old sessions\n if (this.config.storeClientsTime !== -1) {\n for (const id in this.persistentSessions) {\n if (Object.prototype.hasOwnProperty.call(this.persistentSessions, id)) {\n if (now - this.persistentSessions[id].lastSeen > (this.config.storeClientsTime as number) * 60000) {\n delete this.persistentSessions[id];\n }\n }\n }\n }\n\n this.resending = false;\n }\n}\n"]}