iobroker.sonoff 3.2.1 → 3.3.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.
package/lib/server.js CHANGED
@@ -7,18 +7,18 @@ const types = require('./datapoints');
7
7
  // const heapdump = require('heapdump');
8
8
  const hueCalc = true;
9
9
  // var heapTest = 0;
10
- const FORBIDDEN_CHARS = /[\]\[*,;'"`<>\\?]/g;
10
+ const FORBIDDEN_CHARS = /[\]\\[*,;'"`<>\\?]/g;
11
11
  const mappingClients = {};
12
12
 
13
13
  /*
14
- * HSV to RGB color conversion
15
- *
16
- * H runs from 0 to 360 degrees
17
- * S and V run from 0 to 100
18
- *
19
- * Ported from the excellent java algorithm by Eugene Vishnevsky at:
20
- * http://www.cs.rit.edu/~ncs/color/t_convert.html
21
- */
14
+ * HSV to RGB color conversion
15
+ *
16
+ * H runs from 0 to 360 degrees
17
+ * S and V run from 0 to 100
18
+ *
19
+ * Ported from the excellent java algorithm by Eugene Vishnevsky at:
20
+ * http://www.cs.rit.edu/~ncs/color/t_convert.html
21
+ */
22
22
  function hsvToRgb(h, s, v) {
23
23
  let r, g, b;
24
24
  let i;
@@ -39,11 +39,7 @@ function hsvToRgb(h, s, v) {
39
39
  if (s === 0) {
40
40
  // Achromatic (grey)
41
41
  r = g = b = v;
42
- return [
43
- Math.round(r * 255),
44
- Math.round(g * 255),
45
- Math.round(b * 255)
46
- ];
42
+ return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
47
43
  }
48
44
 
49
45
  h /= 60; // sector 0 to 5
@@ -90,11 +86,7 @@ function hsvToRgb(h, s, v) {
90
86
  b = q;
91
87
  }
92
88
 
93
- return [
94
- Math.round(r * 255),
95
- Math.round(g * 255),
96
- Math.round(b * 255)
97
- ];
89
+ return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
98
90
  }
99
91
 
100
92
  function componentToHex(c) {
@@ -109,10 +101,17 @@ function toPaddedHexString(num, len) {
109
101
  }
110
102
  }
111
103
  const str = num.toString(16);
112
- if (str.length >= len) return str;
104
+ if (str.length >= len) {
105
+ return str;
106
+ }
113
107
  return '0'.repeat(len - str.length) + str;
114
108
  }
115
109
 
110
+ /**
111
+ * MQTT Server constructor
112
+ *
113
+ * @param {object} adapter - The ioBroker adapter instance
114
+ */
116
115
  function MQTTServer(adapter) {
117
116
  if (!(this instanceof MQTTServer)) {
118
117
  return new MQTTServer(adapter);
@@ -126,7 +125,7 @@ function MQTTServer(adapter) {
126
125
  let taskCallbacks = [];
127
126
  let messageId = 1;
128
127
  let persistentSessions = {};
129
- let resending = false;
128
+ let resending = false;
130
129
  let resendTimer = null;
131
130
 
132
131
  const cacheAddedObjects = {};
@@ -162,7 +161,7 @@ function MQTTServer(adapter) {
162
161
  }
163
162
  };
164
163
 
165
- /*
164
+ /*
166
165
  memwatch.on('leak', (info) => {
167
166
  //console.error('Memory leak detected:\n', info);
168
167
  adapter.log.info('Memory leak detected:\n', info);
@@ -175,7 +174,7 @@ function MQTTServer(adapter) {
175
174
  });
176
175
  });
177
176
  */
178
- /*
177
+ /*
179
178
  memwatch.on('stats', function(stats) {
180
179
  adapter.log.info("stats:",stats);
181
180
  });
@@ -185,9 +184,21 @@ function MQTTServer(adapter) {
185
184
  //adapter.log.info('color write: '+ val);
186
185
  const stateId = 'Color';
187
186
  if (clients[channelId] && clients[channelId]._map && clients[channelId]._map[stateId]) {
188
- setImmediate(sendState2Client, clients[channelId], clients[channelId]._map[stateId] || 'cmnd/sonoff/Color', val, adapter.config.defaultQoS);
187
+ setImmediate(
188
+ sendState2Client,
189
+ clients[channelId],
190
+ clients[channelId]._map[stateId] || 'cmnd/sonoff/Color',
191
+ val,
192
+ adapter.config.defaultQoS,
193
+ );
189
194
  } else if (clients[channelId] && clients[channelId]._fallBackName) {
190
- setImmediate(sendState2Client, clients[channelId], `cmnd/${clients[channelId]._fallBackName}/${stateId}`, val, adapter.config.defaultQoS);
195
+ setImmediate(
196
+ sendState2Client,
197
+ clients[channelId],
198
+ `cmnd/${clients[channelId]._fallBackName}/${stateId}`,
199
+ val,
200
+ adapter.config.defaultQoS,
201
+ );
191
202
  } else {
192
203
  adapter.log.warn(`Unknown mapping for "${stateId}"`);
193
204
  }
@@ -199,19 +210,86 @@ function MQTTServer(adapter) {
199
210
  return adapter.log.debug('Empty power was ignored');
200
211
  }
201
212
  if (clients[channelId]._map && clients[channelId]._map[stateId]) {
202
- setImmediate(sendState2Client, clients[channelId], clients[channelId]._map[stateId] || 'cmnd/sonoff/POWER', val ? 'ON' : 'OFF', adapter.config.defaultQoS);
213
+ setImmediate(
214
+ sendState2Client,
215
+ clients[channelId],
216
+ clients[channelId]._map[stateId] || 'cmnd/sonoff/POWER',
217
+ val ? 'ON' : 'OFF',
218
+ adapter.config.defaultQoS,
219
+ );
203
220
  } else if (clients[channelId]._fallBackName) {
204
- setImmediate(sendState2Client, clients[channelId], `cmnd/${clients[channelId]._fallBackName}/${stateId}`, val, adapter.config.defaultQoS);
221
+ setImmediate(
222
+ sendState2Client,
223
+ clients[channelId],
224
+ `cmnd/${clients[channelId]._fallBackName}/${stateId}`,
225
+ val,
226
+ adapter.config.defaultQoS,
227
+ );
205
228
  } else {
206
229
  adapter.log.warn(`Unknown mapping for "${stateId}"`);
207
230
  }
208
231
  }
209
232
 
233
+ function setZbSendCommand(channelId, deviceId, attribute, val) {
234
+ // Send Zigbee command via ZbSend
235
+ const zbSendCommand = JSON.stringify({
236
+ device: deviceId,
237
+ send: {
238
+ [attribute]: val,
239
+ },
240
+ });
241
+
242
+ if (clients[channelId] && clients[channelId]._fallBackName) {
243
+ setImmediate(
244
+ sendState2Client,
245
+ clients[channelId],
246
+ `cmnd/${clients[channelId]._fallBackName}/ZbSend`,
247
+ zbSendCommand,
248
+ adapter.config.defaultQoS,
249
+ );
250
+ } else {
251
+ adapter.log.warn(`Cannot send ZbSend command for device ${deviceId}: client not found`);
252
+ }
253
+ }
254
+
255
+ /**
256
+ * Transform shutter state names to correct Tasmota command format
257
+ * e.g., "Shutter1_Position" -> "ShutterPosition1"
258
+ * e.g., "Shutter1_Tilt" -> "ShutterTilt1"
259
+ *
260
+ * @param {string} stateId - The original state ID to transform
261
+ * @returns {string} The transformed state ID for Tasmota commands
262
+ */
263
+ function transformShutterStateId(stateId) {
264
+ const shutterMatch = stateId.match(/^Shutter(\d+)_(Position|Direction|Target|Tilt)$/);
265
+ if (shutterMatch) {
266
+ const shutterNumber = shutterMatch[1];
267
+ const command = shutterMatch[2];
268
+ return `Shutter${command}${shutterNumber}`;
269
+ }
270
+ return stateId;
271
+ }
272
+
210
273
  function setStateImmediate(channelId, stateId, val) {
274
+ // Transform shutter state names to correct Tasmota command format
275
+ const transformedStateId = transformShutterStateId(stateId);
276
+
211
277
  if (clients[channelId] && clients[channelId]._map && clients[channelId]._map[stateId]) {
212
- setImmediate(sendState2Client, clients[channelId], clients[channelId]._map[stateId] || (`cmnd/sonoff/${stateId}`), val, adapter.config.defaultQoS);
278
+ setImmediate(
279
+ sendState2Client,
280
+ clients[channelId],
281
+ clients[channelId]._map[stateId] || `cmnd/sonoff/${transformedStateId}`,
282
+ val,
283
+ adapter.config.defaultQoS,
284
+ );
213
285
  } else if (clients[channelId] && clients[channelId]._fallBackName) {
214
- setImmediate(sendState2Client, clients[channelId], `cmnd/${clients[channelId]._fallBackName}/${stateId}`, val, adapter.config.defaultQoS);
286
+ setImmediate(
287
+ sendState2Client,
288
+ clients[channelId],
289
+ `cmnd/${clients[channelId]._fallBackName}/${transformedStateId}`,
290
+ val,
291
+ adapter.config.defaultQoS,
292
+ );
215
293
  } else {
216
294
  adapter.log.warn(`Unknown mapping for "${stateId}"`);
217
295
  }
@@ -228,7 +306,7 @@ function MQTTServer(adapter) {
228
306
  val = undefined;
229
307
  }
230
308
  if (val !== undefined) {
231
- task.cb = _setState;
309
+ task.cb = _setState;
232
310
  task.cbArg = val;
233
311
  }
234
312
  tasks.push(task);
@@ -240,19 +318,37 @@ function MQTTServer(adapter) {
240
318
  if (tasks.length === 1) {
241
319
  processTasks();
242
320
  }
243
- }
321
+ }
244
322
 
245
- const specVars = ['Red', 'Green', 'Blue', 'WW', 'CW', 'Color', 'RGB_POWER', 'WW_POWER', 'CW_POWER', 'Hue', 'Saturation'];
323
+ const specVars = [
324
+ 'Red',
325
+ 'Green',
326
+ 'Blue',
327
+ 'WW',
328
+ 'CW',
329
+ 'Color',
330
+ 'RGB_POWER',
331
+ 'WW_POWER',
332
+ 'CW_POWER',
333
+ 'Hue',
334
+ 'Saturation',
335
+ ];
246
336
  function onStateChangedColors(id, state, channelId, stateId) {
247
337
  if (!channelId) {
248
- let parts = id.split('.');
249
- stateId = parts.pop();
250
-
251
- if (stateId === 'level' || stateId === 'state' || stateId === 'red' || stateId === 'blue' || stateId === 'green') {
338
+ let parts = id.split('.');
339
+ stateId = parts.pop();
340
+
341
+ if (
342
+ stateId === 'level' ||
343
+ stateId === 'state' ||
344
+ stateId === 'red' ||
345
+ stateId === 'blue' ||
346
+ stateId === 'green'
347
+ ) {
252
348
  stateId = `${parts.pop()}.${stateId}`;
253
349
  }
254
350
 
255
- channelId = parts.splice(2, parts.length).join('.');
351
+ channelId = parts.splice(2, parts.length).join('.');
256
352
  }
257
353
  const ledModeIdExor = `${adapter.namespace}.${channelId}.modeLedExor`;
258
354
  if (cachedModeExor[ledModeIdExor] === undefined) {
@@ -278,7 +374,9 @@ function MQTTServer(adapter) {
278
374
  if (!obj) {
279
375
  adapter.log.warn(`Invalid rgbww obj for ${id}`);
280
376
  } else if (typeof state.val !== 'string') {
281
- adapter.log.warn(`Invalid rgbww state value for ${id} : ${JSON.stringify(state.val)} needs to be a string`);
377
+ adapter.log.warn(
378
+ `Invalid rgbww state value for ${id} : ${JSON.stringify(state.val)} needs to be a string`,
379
+ );
282
380
  } else {
283
381
  const role = obj.common.role;
284
382
  let color;
@@ -311,13 +409,13 @@ function MQTTServer(adapter) {
311
409
  const hidE = id.split('.');
312
410
  const deviceDesc = `${hidE[0]}.${hidE[1]}.${hidE[2]}`;
313
411
  let idAlive = `${deviceDesc}.Red`;
314
- adapter.setState(idAlive, 100 * parseInt(color.substring(0, 2), 16) / 255, true);
412
+ adapter.setState(idAlive, (100 * parseInt(color.substring(0, 2), 16)) / 255, true);
315
413
 
316
414
  idAlive = `${deviceDesc}.Green`;
317
- adapter.setState(idAlive, 100 * parseInt(color.substring(2, 4), 16) / 255, true);
415
+ adapter.setState(idAlive, (100 * parseInt(color.substring(2, 4), 16)) / 255, true);
318
416
 
319
417
  idAlive = `${deviceDesc}.Blue`;
320
- adapter.setState(idAlive, 100 * parseInt(color.substring(4, 6), 16) / 255, true);
418
+ adapter.setState(idAlive, (100 * parseInt(color.substring(4, 6), 16)) / 255, true);
321
419
  }
322
420
  });
323
421
  } else {
@@ -325,8 +423,10 @@ function MQTTServer(adapter) {
325
423
  const deviceDesc = `${hidE[0]}.${hidE[1]}.${hidE[2]}`;
326
424
  if (stateId.match(/Red\d?/)) {
327
425
  // set red component
328
- if (state.val > 100) state.val = 100;
329
- const red = toPaddedHexString(Math.floor(255 * state.val / 100), 2);
426
+ if (state.val > 100) {
427
+ state.val = 100;
428
+ }
429
+ const red = toPaddedHexString(Math.floor((255 * state.val) / 100), 2);
330
430
  const idAlive = `${deviceDesc}.Color`;
331
431
  adapter.getForeignState(idAlive, (err, state) => {
332
432
  if (!state) {
@@ -339,11 +439,12 @@ function MQTTServer(adapter) {
339
439
  adapter.setState(idAlive, `#${out}`, false);
340
440
  setColor(channelId, out);
341
441
  });
342
- } else
343
- if (stateId.match(/Green\d?/)) {
442
+ } else if (stateId.match(/Green\d?/)) {
344
443
  // set green component
345
- if (state.val > 100) state.val = 100;
346
- const green = toPaddedHexString(Math.floor(255 * state.val / 100), 2);
444
+ if (state.val > 100) {
445
+ state.val = 100;
446
+ }
447
+ const green = toPaddedHexString(Math.floor((255 * state.val) / 100), 2);
347
448
  const idAlive = `${deviceDesc}.Color`;
348
449
  adapter.getForeignState(idAlive, (err, state) => {
349
450
  if (!state) {
@@ -356,11 +457,12 @@ function MQTTServer(adapter) {
356
457
  adapter.setState(idAlive, `#${out}`, false);
357
458
  setColor(channelId, out);
358
459
  });
359
- } else
360
- if (stateId.match(/Blue\d?/)) {
460
+ } else if (stateId.match(/Blue\d?/)) {
361
461
  // set blue component
362
- if (state.val > 100) state.val = 100;
363
- const blue = toPaddedHexString(Math.floor(255 * state.val / 100), 2);
462
+ if (state.val > 100) {
463
+ state.val = 100;
464
+ }
465
+ const blue = toPaddedHexString(Math.floor((255 * state.val) / 100), 2);
364
466
  const idAlive = `${deviceDesc}.Color`;
365
467
  adapter.getForeignState(idAlive, (err, state) => {
366
468
  if (!state) {
@@ -373,8 +475,7 @@ function MQTTServer(adapter) {
373
475
  adapter.setState(idAlive, `#${out}`, false);
374
476
  setColor(channelId, out);
375
477
  });
376
- } else
377
- if (stateId.match(/RGB_POWER\d?/)) {
478
+ } else if (stateId.match(/RGB_POWER\d?/)) {
378
479
  // set ww component
379
480
  const rgbpow = state.val === 'true' || state.val === true || state.val === 1 || state.val === '1';
380
481
  const idAlive = `${deviceDesc}.Color`;
@@ -407,16 +508,16 @@ function MQTTServer(adapter) {
407
508
  setColor(channelId, out);
408
509
  adapter.setState(idAlive, `#${out}`, false);
409
510
  if (rgbpow) {
410
- setPower(channelId, true)
511
+ setPower(channelId, true);
411
512
  }
412
513
  // if led_mode&1, exor white leds
413
-
414
514
  });
415
- } else
416
- // calc hue + saturation params to rgb
417
- if (hueCalc && stateId.match(/Hue\d?/)) {
515
+ } else if (hueCalc && stateId.match(/Hue\d?/)) {
516
+ // calc hue + saturation params to rgb
418
517
  let hue = state.val;
419
- if (hue > 359) hue = 359;
518
+ if (hue > 359) {
519
+ hue = 359;
520
+ }
420
521
  // recalc color by hue
421
522
  const idAlive = `${deviceDesc}.Dimmer`;
422
523
  adapter.getForeignState(idAlive, (err, state) => {
@@ -441,10 +542,11 @@ function MQTTServer(adapter) {
441
542
  });
442
543
  }
443
544
  });
444
- } else
445
- if (hueCalc && stateId.match(/Saturation\d?/)) {
545
+ } else if (hueCalc && stateId.match(/Saturation\d?/)) {
446
546
  let sat = state.val;
447
- if (sat > 100) sat = 100;
547
+ if (sat > 100) {
548
+ sat = 100;
549
+ }
448
550
  // recalc color by saturation
449
551
  const idAlive = `${deviceDesc}.Dimmer`;
450
552
  adapter.getForeignState(idAlive, (err, state) => {
@@ -488,7 +590,8 @@ function MQTTServer(adapter) {
488
590
 
489
591
  if (stateId.match(/WW_POWER\d?/)) {
490
592
  // set ww component
491
- const wwpow = state.val === 'true' || state.val === true || state.val === 1 || state.val === '1';
593
+ const wwpow =
594
+ state.val === 'true' || state.val === true || state.val === 1 || state.val === '1';
492
595
  const idAlive = `${deviceDesc}.Color`;
493
596
  adapter.getForeignState(idAlive, (err, state) => {
494
597
  if (!state) {
@@ -506,24 +609,24 @@ function MQTTServer(adapter) {
506
609
  //adapter.log.info('reset white');
507
610
  out = `000000${ww}`;
508
611
  let idAlive = `${deviceDesc}.RGB_POWER`;
509
- adapter.setState(idAlive,false, false);
612
+ adapter.setState(idAlive, false, false);
510
613
  }
511
614
 
512
615
  let idAlive = `${deviceDesc}.Color`;
513
- adapter.setState(idAlive,`#${out}`, false);
616
+ adapter.setState(idAlive, `#${out}`, false);
514
617
  setColor(channelId, out);
515
618
 
516
619
  // set ww channel
517
620
  idAlive = `${deviceDesc}.WW`;
518
- adapter.setState(idAlive, 100 * parseInt(out.substring(6, 8), 16) / 255, true);
621
+ adapter.setState(idAlive, (100 * parseInt(out.substring(6, 8), 16)) / 255, true);
519
622
 
520
623
  // in case POWER is off, switch it on
521
- wwpow && setPower(channelId, true)
624
+ wwpow && setPower(channelId, true);
522
625
  });
523
- } else
524
- if (stateId.match(/CW_POWER\d?/)) {
626
+ } else if (stateId.match(/CW_POWER\d?/)) {
525
627
  // set ww component
526
- const cwpow = state.val === 'true' || state.val === true || state.val === 1 || state.val === '1';
628
+ const cwpow =
629
+ state.val === 'true' || state.val === true || state.val === 1 || state.val === '1';
527
630
  const idAlive = `${deviceDesc}.Color`;
528
631
  adapter.getForeignState(idAlive, (err, state) => {
529
632
  if (!state) {
@@ -550,7 +653,7 @@ function MQTTServer(adapter) {
550
653
 
551
654
  // set cw channel
552
655
  idAlive = `${deviceDesc}.CW`;
553
- adapter.setState(idAlive, 100 * parseInt(out.substring(6, 8), 16) / 255, true);
656
+ adapter.setState(idAlive, (100 * parseInt(out.substring(6, 8), 16)) / 255, true);
554
657
 
555
658
  // in case POWER is off, switch it on
556
659
  if (cwpow) {
@@ -558,10 +661,9 @@ function MQTTServer(adapter) {
558
661
  adapter.setState(idAlive, true, false);
559
662
  }
560
663
  });
561
- } else
562
- if (stateId.match(/WW\d?/)) {
664
+ } else if (stateId.match(/WW\d?/)) {
563
665
  // set ww component
564
- const ww = toPaddedHexString(Math.floor(255 * state.val / 100), 2);
666
+ const ww = toPaddedHexString(Math.floor((255 * state.val) / 100), 2);
565
667
  const idAlive = `${deviceDesc}.Color`;
566
668
  adapter.getForeignState(idAlive, (err, state) => {
567
669
  if (!state) {
@@ -573,10 +675,9 @@ function MQTTServer(adapter) {
573
675
  const out = color.substring(0, wwindex) + ww;
574
676
  setColor(channelId, out);
575
677
  });
576
- } else
577
- if (stateId.match(/CW\d?/)) {
678
+ } else if (stateId.match(/CW\d?/)) {
578
679
  // set ww component
579
- const cw = toPaddedHexString(Math.floor(255 * state.val / 100), 2);
680
+ const cw = toPaddedHexString(Math.floor((255 * state.val) / 100), 2);
580
681
  const idAlive = `${deviceDesc}.Color`;
581
682
  adapter.getForeignState(idAlive, (err, state) => {
582
683
  if (!state) {
@@ -599,9 +700,30 @@ function MQTTServer(adapter) {
599
700
  adapter.log.debug(`onStateChange ${id}: ${JSON.stringify(state)}`);
600
701
  if (server && state && !state.ack) {
601
702
  // find client.id
602
- let parts = id.split('.');
603
- const stateId = parts.pop();
703
+ let parts = id.split('.');
704
+ const stateId = parts.pop();
604
705
  const channelId = parts.splice(2, parts.length).join('.');
706
+
707
+ // Check if this is a Zigbee device state change
708
+ // Pattern: ZbReceived_DEVICEID_ATTRIBUTE (e.g., ZbReceived_0x0856_Power)
709
+ const zbMatch = stateId.match(/^ZbReceived_([^_]+)_(Power|Dimmer)$/);
710
+ if (zbMatch && clients[mappingClients[channelId]]) {
711
+ const deviceId = zbMatch[1];
712
+ const attribute = zbMatch[2];
713
+ let zbValue;
714
+
715
+ // Convert values for Zigbee commands
716
+ if (attribute === 'Power') {
717
+ zbValue = state.val ? '1' : '0';
718
+ } else if (attribute === 'Dimmer') {
719
+ zbValue = state.val;
720
+ }
721
+
722
+ adapter.log.debug(`Sending ZbSend command for device ${deviceId}: ${attribute}=${zbValue}`);
723
+ setZbSendCommand(mappingClients[channelId], deviceId, attribute, zbValue);
724
+ return;
725
+ }
726
+
605
727
  if (clients[mappingClients[channelId]]) {
606
728
  // check for special led-stripe vars
607
729
  if (!specVars.includes(stateId)) {
@@ -616,10 +738,18 @@ function MQTTServer(adapter) {
616
738
  setStateImmediate(mappingClients[channelId], stateId, state.val ? 'ON' : 'OFF');
617
739
  break;
618
740
  case 'number':
619
- setStateImmediate(mappingClients[channelId], stateId, state.val === null ? '' : state.val.toString());
741
+ setStateImmediate(
742
+ mappingClients[channelId],
743
+ stateId,
744
+ state.val === null ? '' : state.val.toString(),
745
+ );
620
746
  break;
621
747
  case 'string':
622
- setStateImmediate(mappingClients[channelId], stateId, (state.val === null) ? '' : state.val.toString());
748
+ setStateImmediate(
749
+ mappingClients[channelId],
750
+ stateId,
751
+ state.val === null ? '' : state.val.toString(),
752
+ );
623
753
  break;
624
754
  }
625
755
  }
@@ -634,7 +764,9 @@ function MQTTServer(adapter) {
634
764
  // or state = false
635
765
  // or state.ack = true
636
766
  // or clients[channelId] = false
637
- adapter.log.warn(`Client "${channelId}" not connected`);
767
+ if (!adapter.config.ignoreNotConnectedWarnings) {
768
+ adapter.log.info(`Client "${channelId}" not connected`);
769
+ }
638
770
 
639
771
  /*
640
772
  if (!clients[channelId]) {
@@ -679,9 +811,9 @@ function MQTTServer(adapter) {
679
811
  task.data = JSON.parse(JSON.stringify(task.data)); // copy
680
812
  delete task.data.common.storeMap;
681
813
  }
682
- adapter.setForeignObject(task.id, task.data, (err) => {
814
+ adapter.setForeignObject(task.id, task.data, err => {
683
815
  if (err) {
684
- adapter.log.warn(`New object creation error: ${err.message}`)
816
+ adapter.log.warn(`New object creation error: ${err.message}`);
685
817
  } else {
686
818
  adapter.log.info(`New object created: ${task.id}`);
687
819
  }
@@ -702,7 +834,7 @@ function MQTTServer(adapter) {
702
834
  }
703
835
  adapter.setForeignObject(task.id, obj, err => {
704
836
  if (err) {
705
- adapter.log.warn(`Object update error: ${err.message}`)
837
+ adapter.log.warn(`Object update error: ${err.message}`);
706
838
  } else {
707
839
  adapter.log.info(`Object updated: ${task.id}`);
708
840
  }
@@ -787,14 +919,14 @@ function MQTTServer(adapter) {
787
919
  _id: id,
788
920
  common: {
789
921
  name: client.id,
790
- desc: ''
922
+ desc: '',
791
923
  },
792
924
  native: {
793
- clientId: client.id
925
+ clientId: client.id,
794
926
  },
795
- type: 'channel'
927
+ type: 'channel',
796
928
  };
797
- tasks.push({type: 'addObject', id: obj._id, data: obj});
929
+ tasks.push({ type: 'addObject', id: obj._id, data: obj });
798
930
 
799
931
  obj = {
800
932
  _id: `${id}.alive`,
@@ -803,11 +935,11 @@ function MQTTServer(adapter) {
803
935
  role: 'indicator.reachable',
804
936
  read: true,
805
937
  write: false,
806
- name: `${client.id} alive`
938
+ name: `${client.id} alive`,
807
939
  },
808
- type: 'state'
940
+ type: 'state',
809
941
  };
810
- tasks.push({type: 'addObject', id: obj._id, data: obj});
942
+ tasks.push({ type: 'addObject', id: obj._id, data: obj });
811
943
  if (isStart) {
812
944
  processTasks(callback);
813
945
  } else {
@@ -830,7 +962,7 @@ function MQTTServer(adapter) {
830
962
  role: 'indicator.reachable',
831
963
  type: 'boolean',
832
964
  read: true,
833
- write: false
965
+ write: false,
834
966
  },
835
967
  native: {
836
968
  ip: clients[id].stream.remoteAddress,
@@ -838,9 +970,10 @@ function MQTTServer(adapter) {
838
970
  },
839
971
  });
840
972
  } else {
841
- if (clients[id] &&
973
+ if (
974
+ clients[id] &&
842
975
  (clientObj.native.port !== clients[id].stream.remotePort ||
843
- clientObj.native.ip !== clients[id].stream.remoteAddress)
976
+ clientObj.native.ip !== clients[id].stream.remoteAddress)
844
977
  ) {
845
978
  clientObj.native.port = clients[id].stream.remotePort;
846
979
  clientObj.native.ip = clients[id].stream.remoteAddress;
@@ -887,8 +1020,8 @@ function MQTTServer(adapter) {
887
1020
  retain = undefined;
888
1021
  }
889
1022
  adapter.log.debug(`Send to "${client.id}": ${topic} = ${state}`);
890
- client.publish({topic: topic, payload: state, qos: qos, retain: retain, messageId: messageId++}, cb);
891
- messageId &= 0xFFFFFFFF;
1023
+ client.publish({ topic: topic, payload: state, qos: qos, retain: retain, messageId: messageId++ }, cb);
1024
+ messageId &= 0xffffffff;
892
1025
  }
893
1026
 
894
1027
  function resendMessages2Client(client, messages, i) {
@@ -897,7 +1030,9 @@ function MQTTServer(adapter) {
897
1030
  try {
898
1031
  messages[i].ts = Date.now();
899
1032
  messages[i].count++;
900
- adapter.log.debug(`Client [${client.id}] resend messages on connect: ${messages[i].topic} = ${messages[i].payload}`);
1033
+ adapter.log.debug(
1034
+ `Client [${client.id}] resend messages on connect: ${messages[i].topic} = ${messages[i].payload}`,
1035
+ );
901
1036
  if (messages[i].cmd === 'publish') {
902
1037
  client.publish(messages[i]);
903
1038
  }
@@ -915,18 +1050,20 @@ function MQTTServer(adapter) {
915
1050
  }
916
1051
  }
917
1052
 
918
- function addObject(attr, client, prefix, path) {
919
- let replaceAttr = types[attr].replace || attr;
1053
+ function addObject(typeKey, client, prefix, path) {
1054
+ // Extract the actual attribute name for the state ID construction
1055
+ const attr = typeKey.includes('_') && path.length > 0 ? typeKey.split('_').pop() : typeKey;
1056
+ let replaceAttr = types[typeKey].replace || attr;
920
1057
  let id = `${adapter.namespace}.${client.iobId}.${prefix ? `${prefix}.` : ''}${path.length ? `${path.join('_')}_` : ''}${replaceAttr.replace(FORBIDDEN_CHARS, '_')}`;
921
1058
  let obj = {
922
1059
  type: 'addObject',
923
1060
  id: id,
924
1061
  data: {
925
1062
  _id: id,
926
- common: Object.assign({}, types[attr]),
1063
+ common: Object.assign({}, types[typeKey]),
927
1064
  native: {},
928
- type: 'state'
929
- }
1065
+ type: 'state',
1066
+ },
930
1067
  };
931
1068
  obj.data.common.name = `${client.id} ${prefix ? `${prefix} ` : ''}${path.length ? `${path.join(' ')} ` : ''} ${replaceAttr}`;
932
1069
  return obj;
@@ -946,15 +1083,24 @@ function MQTTServer(adapter) {
946
1083
  });
947
1084
  }
948
1085
 
949
- path = path || [];
1086
+ path = path || [];
950
1087
  prefix = prefix || '';
951
1088
 
1089
+ // Extract pressure and temperature units if available at this level for nested data
1090
+ const pressureUnit = data.PressureUnit || (unit && typeof unit === 'object' ? unit.pressureUnit : undefined);
1091
+ const tempUnit = data.TempUnit || (unit && typeof unit === 'object' ? unit.tempUnit : undefined);
1092
+
952
1093
  for (let attr in data) {
953
- if (!data.hasOwnProperty(attr)) {
1094
+ if (!Object.prototype.hasOwnProperty.call(data, attr)) {
954
1095
  adapter.log.warn(`[${client.id}] attr error: ${attr}${data[attr]}`);
955
1096
  continue;
956
1097
  }
957
1098
 
1099
+ // Skip unit fields - they are used for dynamic unit override, not as states
1100
+ if (attr === 'TempUnit' || attr === 'PressureUnit') {
1101
+ continue;
1102
+ }
1103
+
958
1104
  if (typeof data[attr] === 'object') {
959
1105
  // check for arrays
960
1106
  if (types[attr]) {
@@ -975,8 +1121,8 @@ function MQTTServer(adapter) {
975
1121
  _id: id,
976
1122
  common: Object.assign({}, types[attr]),
977
1123
  native: {},
978
- type: 'state'
979
- }
1124
+ type: 'state',
1125
+ },
980
1126
  };
981
1127
  obj.data.common.name = `${client.id} ${prefix ? `${prefix} ` : ''}${path.length ? `${path.join(' ')} ` : ''} ${replaceAttr}`;
982
1128
  obj.data.common.type = typeof val;
@@ -988,29 +1134,45 @@ function MQTTServer(adapter) {
988
1134
  } else {
989
1135
  let nPath = Object.assign([], path);
990
1136
  nPath.push(attr.replace(FORBIDDEN_CHARS, '_'));
991
- checkData(client, topic, prefix, data[attr], unit, nPath);
1137
+ const nestedUnits = pressureUnit || tempUnit ? { pressureUnit, tempUnit } : unit;
1138
+ checkData(client, topic, prefix, data[attr], nestedUnits, nPath);
992
1139
  nPath = undefined;
993
1140
  }
994
1141
  } else {
995
1142
  let nPath = Object.assign([], path);
996
1143
  nPath.push(attr.replace(FORBIDDEN_CHARS, '_'));
1144
+ const nestedUnits = pressureUnit || tempUnit ? { pressureUnit, tempUnit } : unit;
997
1145
  if (adapter.config.OBJ_TREE) {
998
- checkData(client, topic, `${prefix}.${nPath}`, data[attr], unit);
1146
+ checkData(client, topic, `${prefix}.${nPath}`, data[attr], nestedUnits);
999
1147
  } else {
1000
- checkData(client, topic, prefix, data[attr], unit, nPath);
1148
+ checkData(client, topic, prefix, data[attr], nestedUnits, nPath);
1001
1149
  }
1002
1150
  nPath = undefined;
1003
1151
  }
1004
- } else if (types[attr]) {
1152
+ } else if (types[attr] || (path.length > 0 && types[`${path.join('_')}_${attr}`])) {
1005
1153
  let allowReadColors;
1006
1154
 
1155
+ // Check for path-based type definition first, then fallback to simple attr type
1156
+ const typeKey =
1157
+ path.length > 0 && types[`${path.join('_')}_${attr}`] ? `${path.join('_')}_${attr}` : attr;
1158
+
1007
1159
  // create object
1008
- const obj = addObject(attr, client, prefix, path);
1009
- let replaceAttr = types[attr].replace || attr;
1160
+ const obj = addObject(typeKey, client, prefix, path);
1161
+ let replaceAttr = types[typeKey].replace || attr;
1010
1162
 
1011
1163
  if (obj.data.common.storeMap) {
1012
1164
  delete obj.data.common.storeMap;
1013
- client._map[replaceAttr] = topic.replace(/$\w+\//, 'cmnd/').replace(/\/\w+$/, `/${replaceAttr}`);
1165
+ client._map[replaceAttr] = topic.replace(/^\w+\//, 'cmnd/').replace(/\/\w+$/, `/${replaceAttr}`);
1166
+ }
1167
+
1168
+ // handle dynamic pressure unit override
1169
+ if (attr === 'Pressure' && pressureUnit && typeof pressureUnit === 'string') {
1170
+ obj.data.common.unit = pressureUnit;
1171
+ }
1172
+
1173
+ // handle dynamic temperature unit override
1174
+ if (attr === 'Temperature' && tempUnit && typeof tempUnit === 'string') {
1175
+ obj.data.common.unit = tempUnit;
1014
1176
  }
1015
1177
 
1016
1178
  // adaptions for magichome tasmota
@@ -1020,7 +1182,7 @@ function MQTTServer(adapter) {
1020
1182
 
1021
1183
  // if ledFlags bit 2, read color from tasmota, else ignore
1022
1184
  if (data[attr].length === 10) {
1023
- obj.data.common.role = 'level.color.rgbcwww'; // RGB + cold white + white???
1185
+ obj.data.common.role = 'level.color.rgbcwww'; // RGB + cold white + white???
1024
1186
  } else if (data[attr].length === 8) {
1025
1187
  obj.data.common.role = 'level.color.rgbww'; // RGB + White
1026
1188
  } else {
@@ -1043,15 +1205,24 @@ function MQTTServer(adapter) {
1043
1205
 
1044
1206
  xObj = addObject('Red', client, prefix, path);
1045
1207
  xObj.data.common.read = allowReadColors;
1046
- updateState(xObj, allowReadColors ? 100 * parseInt(data[attr].substring(0, 2), 16) / 255 : undefined);
1208
+ updateState(
1209
+ xObj,
1210
+ allowReadColors ? (100 * parseInt(data[attr].substring(0, 2), 16)) / 255 : undefined,
1211
+ );
1047
1212
 
1048
1213
  xObj = addObject('Green', client, prefix, path);
1049
1214
  xObj.data.common.read = allowReadColors;
1050
- updateState(xObj, allowReadColors ? 100 * parseInt(data[attr].substring(2, 4), 16) / 255 : undefined);
1215
+ updateState(
1216
+ xObj,
1217
+ allowReadColors ? (100 * parseInt(data[attr].substring(2, 4), 16)) / 255 : undefined,
1218
+ );
1051
1219
 
1052
1220
  xObj = addObject('Blue', client, prefix, path);
1053
1221
  xObj.data.common.read = allowReadColors;
1054
- updateState(xObj, allowReadColors ? 100 * parseInt(data[attr].substring(4, 6), 16) / 255 : undefined);
1222
+ updateState(
1223
+ xObj,
1224
+ allowReadColors ? (100 * parseInt(data[attr].substring(4, 6), 16)) / 255 : undefined,
1225
+ );
1055
1226
 
1056
1227
  xObj = addObject('RGB_POWER', client, prefix, path);
1057
1228
  xObj.data.common.read = allowReadColors;
@@ -1062,30 +1233,38 @@ function MQTTServer(adapter) {
1062
1233
  // rgbww
1063
1234
  xObj = addObject('WW', client, prefix, path);
1064
1235
  xObj.data.common.read = allowReadColors;
1065
- updateState(xObj, allowReadColors ? 100 * parseInt(data[attr].substring(6, 8), 16) / 255 : undefined);
1236
+ updateState(
1237
+ xObj,
1238
+ allowReadColors ? (100 * parseInt(data[attr].substring(6, 8), 16)) / 255 : undefined,
1239
+ );
1066
1240
 
1067
1241
  xObj = addObject('WW_POWER', client, prefix, path);
1068
1242
  xObj.data.common.read = allowReadColors;
1069
- updateState(xObj, allowReadColors ? (val > 0) : undefined);
1070
- } else
1071
- if (obj.data.common.role === 'level.color.rgbcwww') {
1243
+ updateState(xObj, allowReadColors ? val > 0 : undefined);
1244
+ } else if (obj.data.common.role === 'level.color.rgbcwww') {
1072
1245
  // rgbcwww
1073
1246
  xObj = addObject('CW', client, prefix, path);
1074
1247
  xObj.data.common.read = allowReadColors;
1075
- updateState(xObj, allowReadColors ? 100 * parseInt(data[attr].substring(6, 8), 16) / 255 : undefined);
1248
+ updateState(
1249
+ xObj,
1250
+ allowReadColors ? (100 * parseInt(data[attr].substring(6, 8), 16)) / 255 : undefined,
1251
+ );
1076
1252
 
1077
1253
  xObj = addObject('CW_POWER', client, prefix, path);
1078
1254
  xObj.data.common.read = allowReadColors;
1079
- updateState(xObj, allowReadColors ? (val > 0) : undefined);
1255
+ updateState(xObj, allowReadColors ? val > 0 : undefined);
1080
1256
 
1081
1257
  xObj = addObject('WW', client, prefix, path);
1082
1258
  xObj.data.common.read = allowReadColors;
1083
- updateState(xObj, allowReadColors ? 100 * parseInt(data[attr].substring(8, 10), 16) / 255 : undefined);
1259
+ updateState(
1260
+ xObj,
1261
+ allowReadColors ? (100 * parseInt(data[attr].substring(8, 10), 16)) / 255 : undefined,
1262
+ );
1084
1263
 
1085
1264
  xObj = addObject('WW_POWER', client, prefix, path);
1086
1265
  xObj.data.common.read = allowReadColors;
1087
1266
  val = parseInt(data[attr].substring(8, 10), 16);
1088
- updateState(xObj, allowReadColors ? (val > 0) : undefined);
1267
+ updateState(xObj, allowReadColors ? val > 0 : undefined);
1089
1268
  }
1090
1269
  }
1091
1270
  }
@@ -1116,6 +1295,42 @@ function MQTTServer(adapter) {
1116
1295
 
1117
1296
  updateState(obj, val);
1118
1297
 
1298
+ // Special handling for Zigbee devices: create writable control states for Power and Dimmer
1299
+ if (
1300
+ (attr === 'Power' || attr === 'Dimmer') &&
1301
+ prefix === 'SENSOR' &&
1302
+ path.length >= 2 &&
1303
+ path[0] === 'ZbReceived'
1304
+ ) {
1305
+ // Extract device ID from path (e.g., ZbReceived_0x0856_Power -> 0x0856)
1306
+ const deviceId = path[1];
1307
+
1308
+ // Create a writable control state for Zigbee devices
1309
+ let controlObj = Object.assign({}, obj);
1310
+
1311
+ // Modify object properties for control state
1312
+ if (attr === 'Power') {
1313
+ // For Zigbee Power: boolean switch, not power consumption in watts
1314
+ controlObj.data.common = {
1315
+ type: 'boolean',
1316
+ role: 'switch',
1317
+ read: true,
1318
+ write: true,
1319
+ name: `${client.id} ${prefix ? `${prefix} ` : ''}${path.join(' ')} ${attr} Control`,
1320
+ };
1321
+ // Convert numeric power value (0/1) to boolean for control state
1322
+ const controlVal = Boolean(val);
1323
+ updateState(controlObj, controlVal);
1324
+ } else if (attr === 'Dimmer') {
1325
+ // For Zigbee Dimmer: ensure it's writable
1326
+ controlObj.data.common.write = true;
1327
+ controlObj.data.common.name = `${client.id} ${prefix ? `${prefix} ` : ''}${path.join(' ')} ${attr} Control`;
1328
+ updateState(controlObj, val);
1329
+ }
1330
+
1331
+ adapter.log.debug(`Created Zigbee control state for device ${deviceId}: ${controlObj.id}`);
1332
+ }
1333
+
1119
1334
  if (powerStateFound) {
1120
1335
  adapter.log.debug(`Got .STATE.POWER update, auto syncing .POWER (src: ${obj.id})`);
1121
1336
  let powerObj = Object.assign({}, obj);
@@ -1130,22 +1345,23 @@ function MQTTServer(adapter) {
1130
1345
  // tele/sonoff/SENSOR tele/sonoff/STATE => read only
1131
1346
  // stat/sonoff/RESULT => read,write
1132
1347
 
1133
- let parts = topic.split('/');
1134
- // auto generate objects
1135
- if ((parts[0] === 'tele' && (
1136
- (adapter.config.TELE_SENSOR && parts[2] === 'SENSOR') ||
1137
- (adapter.config.STAT_RESULT && parts[2] === 'RESULT') ||
1138
- (adapter.config.TELE_STATE && parts[2] === 'STATE'))) ||
1139
- (parts[0] === 'stat' &&
1140
- (adapter.config.STAT_RESULT && parts[2] === 'RESULT'))
1141
- ) {
1142
- let val = data[attr];
1143
- let attributes = {role: 'value', read: true, write: false};
1144
- if (parts[2] === 'RESULT') {
1145
- // control
1348
+ let parts = topic.split('/');
1349
+ // auto generate objects
1350
+ if (
1351
+ (parts[0] === 'tele' &&
1352
+ ((adapter.config.TELE_SENSOR && parts[2] === 'SENSOR') ||
1353
+ (adapter.config.STAT_RESULT && parts[2] === 'RESULT') ||
1354
+ (adapter.config.TELE_STATE && parts[2] === 'STATE') ||
1355
+ (adapter.config.TELE_MARGINS && parts[2] === 'MARGINS'))) ||
1356
+ (parts[0] === 'stat' && adapter.config.STAT_RESULT && parts[2] === 'RESULT')
1357
+ ) {
1358
+ let val = data[attr];
1359
+ let attributes = { role: 'value', read: true, write: false };
1360
+ if (parts[2] === 'RESULT') {
1361
+ // control
1146
1362
  attributes.write = true;
1147
1363
  attributes.role = 'level';
1148
- }
1364
+ }
1149
1365
 
1150
1366
  attributes.type = typeof val;
1151
1367
  if (!['number', 'string', 'boolean'].includes(attributes.type)) {
@@ -1153,19 +1369,19 @@ function MQTTServer(adapter) {
1153
1369
  val = (val || '').toString();
1154
1370
  }
1155
1371
 
1156
- let replaceAttr = attr ;
1157
- let id = `${adapter.namespace}.${client.iobId}.${prefix ? `${prefix}.` : ''}${path.length ? `${path.join('_')}_` : ''}${replaceAttr}`;
1158
- let obj = {
1159
- type: 'addObject',
1160
- id: id,
1161
- data: {
1162
- _id: id,
1163
- common: attributes,
1164
- native: {},
1165
- type: 'state'
1166
- }
1167
- };
1168
- obj.data.common.name = `${client.id} ${prefix ? `${prefix} ` : ''}${path.length ? `${path.join(' ')} ` : ''} ${replaceAttr}`;
1372
+ let replaceAttr = attr;
1373
+ let id = `${adapter.namespace}.${client.iobId}.${prefix ? `${prefix}.` : ''}${path.length ? `${path.join('_')}_` : ''}${replaceAttr}`;
1374
+ let obj = {
1375
+ type: 'addObject',
1376
+ id: id,
1377
+ data: {
1378
+ _id: id,
1379
+ common: attributes,
1380
+ native: {},
1381
+ type: 'state',
1382
+ },
1383
+ };
1384
+ obj.data.common.name = `${client.id} ${prefix ? `${prefix} ` : ''}${path.length ? `${path.join(' ')} ` : ''} ${replaceAttr}`;
1169
1385
  // due to some (for ex. NSPanel) versions do not automatically send .POWER switch data
1170
1386
  // but this is generally a default for all devices (state cannot exist without switch) we now add
1171
1387
  // POWER switches automatically if we get POWER STATE
@@ -1185,7 +1401,7 @@ function MQTTServer(adapter) {
1185
1401
  powerObj.data.common.name = powerName;
1186
1402
  updateState(powerObj, val);
1187
1403
  }
1188
- }
1404
+ }
1189
1405
  }
1190
1406
  }
1191
1407
  }
@@ -1199,7 +1415,7 @@ function MQTTServer(adapter) {
1199
1415
  client.states[packet.topic] = {
1200
1416
  message: packet.payload,
1201
1417
  retain: packet.retain,
1202
- qos: packet.qos
1418
+ qos: packet.qos,
1203
1419
  };
1204
1420
 
1205
1421
  // update alive state
@@ -1276,8 +1492,12 @@ function MQTTServer(adapter) {
1276
1492
  // ignore: stat/Sonoff/RESULT = {"POWER":"ON"}
1277
1493
  // testserver.js reports error, so reject above cmd
1278
1494
  const str = val.replace(/\s+/g, '');
1279
- if (str.startsWith('{"POWER":"ON"}')) return;
1280
- if (str.startsWith('{"POWER":"OFF"}')) return;
1495
+ if (str.startsWith('{"POWER":"ON"}')) {
1496
+ return;
1497
+ }
1498
+ if (str.startsWith('{"POWER":"OFF"}')) {
1499
+ return;
1500
+ }
1281
1501
 
1282
1502
  if (parts[0] === 'stat' || parts[0] === 'tele') {
1283
1503
  try {
@@ -1307,7 +1527,7 @@ function MQTTServer(adapter) {
1307
1527
  } else if (stateId.match(/^(WAKEUP)\d?$/)) {
1308
1528
  checkData(client, packet.topic, 'WAKEUP', JSON.parse(val));
1309
1529
  } else if (stateId.match(/^(RESULT)\d?$/)) {
1310
- checkData(client, packet.topic, 'WAKEUP', JSON.parse(val));
1530
+ checkData(client, packet.topic, 'RESULT', JSON.parse(val));
1311
1531
  }
1312
1532
  } else {
1313
1533
  checkData(client, packet.topic, NO_PREFIX, JSON.parse(val));
@@ -1332,6 +1552,13 @@ function MQTTServer(adapter) {
1332
1552
  } catch (e) {
1333
1553
  adapter.log.warn(`Client [${client.id}] cannot parse data"${stateId}": _${val}_ - ${e}`);
1334
1554
  }
1555
+ } else if (parts[0] === 'tele' && stateId.match(/^(MARGINS)\d?$/)) {
1556
+ // tele/sonoffPOW/MARGINS = {"Time":"2020-04-23T10:15:00","PowerLow":100,"PowerHigh":2000,"PowerDelta":50}
1557
+ try {
1558
+ checkData(client, packet.topic, 'MARGINS', JSON.parse(val));
1559
+ } catch (e) {
1560
+ adapter.log.warn(`Client [${client.id}] cannot parse data"${stateId}": _${val}_ - ${e}`);
1561
+ }
1335
1562
  } else if (types[stateId]) {
1336
1563
  // /ESP_BOX/BM280/Pressure = 1010.09
1337
1564
  // /ESP_BOX/BM280/Humidity = 42.39
@@ -1350,15 +1577,14 @@ function MQTTServer(adapter) {
1350
1577
  _id: id,
1351
1578
  common: JSON.parse(JSON.stringify(types[stateId])),
1352
1579
  native: {},
1353
- type: 'state'
1354
- }
1580
+ type: 'state',
1581
+ },
1355
1582
  };
1356
1583
  obj.data.common.name = `${client.id} ${stateId}`;
1357
1584
 
1358
1585
  // push only new objects
1359
1586
  updateState(obj, () => {
1360
1587
  if (parts[0] === 'cmnd') {
1361
-
1362
1588
  // Set Object fix
1363
1589
  if (obj.data.common.type === 'number') {
1364
1590
  adapter.setState(id, parseFloat(val), true);
@@ -1395,7 +1621,9 @@ function MQTTServer(adapter) {
1395
1621
  }
1396
1622
 
1397
1623
  function clientClose(client, reason) {
1398
- if (!client) return;
1624
+ if (!client) {
1625
+ return;
1626
+ }
1399
1627
 
1400
1628
  if (persistentSessions[client.id]) {
1401
1629
  persistentSessions[client.id].connected = false;
@@ -1407,7 +1635,7 @@ function MQTTServer(adapter) {
1407
1635
  }
1408
1636
 
1409
1637
  try {
1410
- if (clients[client.id] && (client.__secret === clients[client.id].__secret)) {
1638
+ if (clients[client.id] && client.__secret === clients[client.id].__secret) {
1411
1639
  adapter.log.info(`Client [${client.id}] connection closed: ${reason}`);
1412
1640
  updateAlive(client, false);
1413
1641
  delete clients[client.id];
@@ -1429,12 +1657,18 @@ function MQTTServer(adapter) {
1429
1657
  const now = Date.now();
1430
1658
  resending = true;
1431
1659
  for (const clientId in clients) {
1432
- if (clients.hasOwnProperty(clientId) && clients[clientId] && clients[clientId]._messages) {
1660
+ if (
1661
+ Object.prototype.hasOwnProperty.call(clients, clientId) &&
1662
+ clients[clientId] &&
1663
+ clients[clientId]._messages
1664
+ ) {
1433
1665
  for (let m = clients[clientId]._messages.length - 1; m >= 0; m--) {
1434
1666
  const message = clients[clientId]._messages[m];
1435
1667
  if (now - message.ts >= adapter.config.retransmitInterval) {
1436
1668
  if (message.count > adapter.config.retransmitCount) {
1437
- adapter.log.warn(`Client [${clientId}] message ${message.messageId} deleted after ${message.count} retries`);
1669
+ adapter.log.warn(
1670
+ `Client [${clientId}] message ${message.messageId} deleted after ${message.count} retries`,
1671
+ );
1438
1672
  clients[clientId]._messages.splice(m, 1);
1439
1673
  continue;
1440
1674
  }
@@ -1443,7 +1677,9 @@ function MQTTServer(adapter) {
1443
1677
  message.count++;
1444
1678
  message.ts = now;
1445
1679
  try {
1446
- adapter.log.debug(`Client [${clientId}] resend message topic: ${message.topic}, payload: ${message.payload}`);
1680
+ adapter.log.debug(
1681
+ `Client [${clientId}] resend message topic: ${message.topic}, payload: ${message.payload}`,
1682
+ );
1447
1683
  if (message.cmd === 'publish') {
1448
1684
  clients[clientId].publish(message);
1449
1685
  }
@@ -1465,7 +1701,7 @@ function MQTTServer(adapter) {
1465
1701
  // delete old sessions
1466
1702
  if (adapter.config.storeClientsTime !== -1) {
1467
1703
  for (const id in persistentSessions) {
1468
- if (persistentSessions.hasOwnProperty(id)) {
1704
+ if (Object.prototype.hasOwnProperty.call(persistentSessions, id)) {
1469
1705
  if (now - persistentSessions[id].lastSeen > adapter.config.storeClientsTime * 60000) {
1470
1706
  delete persistentSessions[id];
1471
1707
  }
@@ -1502,10 +1738,9 @@ function MQTTServer(adapter) {
1502
1738
  if (options.password) {
1503
1739
  options.password = options.password.toString();
1504
1740
  }
1505
- if (config.user !== options.username ||
1506
- config.password !== options.password) {
1741
+ if (config.user !== options.username || config.password !== options.password) {
1507
1742
  adapter.log.warn(`Client [${client.id}] has invalid password or username`);
1508
- client.connack({returnCode: 4});
1743
+ client.connack({ returnCode: 4 });
1509
1744
  if (oldClient) {
1510
1745
  // delete existing client
1511
1746
  delete clients[client.id];
@@ -1519,12 +1754,13 @@ function MQTTServer(adapter) {
1519
1754
  }
1520
1755
 
1521
1756
  if (oldClient) {
1522
- adapter.log.info(`Client [${client.id}] reconnected. Old secret ${clients[client.id].__secret} ==> New secret ${client.__secret}`);
1757
+ adapter.log.info(
1758
+ `Client [${client.id}] reconnected. Old secret ${clients[client.id].__secret} ==> New secret ${client.__secret}`,
1759
+ );
1523
1760
  // need to destroy the old client
1524
1761
 
1525
1762
  if (client.__secret !== clients[client.id].__secret) {
1526
1763
  // it is another socket!!
1527
-
1528
1764
  // It was following situation:
1529
1765
  // - old connection was active
1530
1766
  // - new connection is on the same TCP
@@ -1546,7 +1782,7 @@ function MQTTServer(adapter) {
1546
1782
  _subsID: {},
1547
1783
  _subs: {},
1548
1784
  messages: [],
1549
- lastSeen: Date.now()
1785
+ lastSeen: Date.now(),
1550
1786
  };
1551
1787
  }
1552
1788
  client._messages = persistentSessions[client.id].messages;
@@ -1556,7 +1792,7 @@ function MQTTServer(adapter) {
1556
1792
  }
1557
1793
  client._messages = client._messages || [];
1558
1794
 
1559
- client.connack({returnCode: 0, sessionPresent});
1795
+ client.connack({ returnCode: 0, sessionPresent });
1560
1796
  clients[client.id] = client;
1561
1797
  updateClients();
1562
1798
 
@@ -1567,10 +1803,14 @@ function MQTTServer(adapter) {
1567
1803
  client._subs = persistentSessions[client.id]._subs;
1568
1804
  if (persistentSessions[client.id].messages.length) {
1569
1805
  // give to the client a little bit time
1570
- client._resendonStart = setTimeout(clientId => {
1571
- client._resendonStart = null;
1572
- resendMessages2Client(client, persistentSessions[clientId].messages);
1573
- }, 100, client.id);
1806
+ client._resendonStart = setTimeout(
1807
+ clientId => {
1808
+ client._resendonStart = null;
1809
+ resendMessages2Client(client, persistentSessions[clientId].messages);
1810
+ },
1811
+ 100,
1812
+ client.id,
1813
+ );
1574
1814
  }
1575
1815
  }
1576
1816
  });
@@ -1590,7 +1830,10 @@ function MQTTServer(adapter) {
1590
1830
 
1591
1831
  client.on('publish', packet => {
1592
1832
  if (clients[client.id] && client.__secret !== clients[client.id].__secret) {
1593
- !adapter.config.ignorePings && adapter.log.warn(`Old client ${client.id} with secret ${client.__secret} sends publish. Ignore! Actual secret is ${clients[client.id].__secret}`);
1833
+ !adapter.config.ignorePings &&
1834
+ adapter.log.warn(
1835
+ `Old client ${client.id} with secret ${client.__secret} sends publish. Ignore! Actual secret is ${clients[client.id].__secret}`,
1836
+ );
1594
1837
  return;
1595
1838
  }
1596
1839
 
@@ -1600,32 +1843,35 @@ function MQTTServer(adapter) {
1600
1843
 
1601
1844
  if (packet.qos === 1) {
1602
1845
  // send PUBACK to a client
1603
- client.puback({messageId: packet.messageId});
1846
+ client.puback({ messageId: packet.messageId });
1604
1847
  } else if (packet.qos === 2) {
1605
1848
  const pack = client._messages && client._messages.find(e => e.messageId === packet.messageId);
1606
1849
  if (pack) {
1607
1850
  // duplicate message => ignore
1608
- adapter.log.warn(`Client [${client.id}] ignored duplicate message with ID: ${packet.messageId}`);
1609
- return;
1610
- } else {
1611
- packet.ts = Date.now();
1612
- packet.cmd = 'pubrel';
1613
- packet.count = 0;
1614
- client._messages.push(packet);
1615
-
1616
- client.pubrec({messageId: packet.messageId});
1851
+ adapter.log.warn(
1852
+ `Client [${client.id}] ignored duplicate message with ID: ${packet.messageId}`,
1853
+ );
1617
1854
  return;
1618
1855
  }
1619
- }
1856
+ packet.ts = Date.now();
1857
+ packet.cmd = 'pubrel';
1858
+ packet.count = 0;
1859
+ client._messages.push(packet);
1620
1860
 
1861
+ client.pubrec({ messageId: packet.messageId });
1862
+ return;
1863
+ }
1621
1864
 
1622
- receivedTopic(packet, client)
1865
+ receivedTopic(packet, client);
1623
1866
  });
1624
1867
 
1625
1868
  // response for QoS2
1626
1869
  client.on('pubrec', packet => {
1627
1870
  if (clients[client.id] && client.__secret !== clients[client.id].__secret) {
1628
- !adapter.config.ignorePings && adapter.log.warn(`Old client ${client.id} with secret ${client.__secret} sends pubrec. Ignore! Actual secret is ${clients[client.id].__secret}`);
1871
+ !adapter.config.ignorePings &&
1872
+ adapter.log.warn(
1873
+ `Old client ${client.id} with secret ${client.__secret} sends pubrec. Ignore! Actual secret is ${clients[client.id].__secret}`,
1874
+ );
1629
1875
  return;
1630
1876
  }
1631
1877
 
@@ -1635,23 +1881,29 @@ function MQTTServer(adapter) {
1635
1881
 
1636
1882
  let pos = null;
1637
1883
  // remove this message from queue
1638
- client._messages && client._messages.forEach((e, i) => {
1639
- if (e.messageId === packet.messageId) {
1640
- pos = i;
1641
- return false;
1642
- }
1643
- });
1884
+ client._messages &&
1885
+ client._messages.forEach((e, i) => {
1886
+ if (e.messageId === packet.messageId) {
1887
+ pos = i;
1888
+ return false;
1889
+ }
1890
+ });
1644
1891
  if (pos !== null) {
1645
- client.pubrel({messageId: packet.messageId});
1892
+ client.pubrel({ messageId: packet.messageId });
1646
1893
  } else {
1647
- adapter.log.warn(`Client [${client.id}] received pubrec on ${client.id} for unknown message ID: ${packet.messageId}`);
1894
+ adapter.log.warn(
1895
+ `Client [${client.id}] received pubrec on ${client.id} for unknown message ID: ${packet.messageId}`,
1896
+ );
1648
1897
  }
1649
1898
  });
1650
1899
 
1651
1900
  // response for QoS2
1652
1901
  client.on('pubcomp', packet => {
1653
1902
  if (clients[client.id] && client.__secret !== clients[client.id].__secret) {
1654
- !adapter.config.ignorePings && adapter.log.warn(`Old client ${client.id} with secret ${client.__secret} sends pubcomp. Ignore! Actual secret is ${clients[client.id].__secret}`);
1903
+ !adapter.config.ignorePings &&
1904
+ adapter.log.warn(
1905
+ `Old client ${client.id} with secret ${client.__secret} sends pubcomp. Ignore! Actual secret is ${clients[client.id].__secret}`,
1906
+ );
1655
1907
  return;
1656
1908
  }
1657
1909
 
@@ -1661,16 +1913,19 @@ function MQTTServer(adapter) {
1661
1913
 
1662
1914
  let pos = null;
1663
1915
  // remove this message from queue
1664
- client._messages && client._messages.forEach((e, i) => {
1665
- if (e.messageId === packet.messageId) {
1666
- pos = i;
1667
- return false;
1668
- }
1669
- });
1916
+ client._messages &&
1917
+ client._messages.forEach((e, i) => {
1918
+ if (e.messageId === packet.messageId) {
1919
+ pos = i;
1920
+ return false;
1921
+ }
1922
+ });
1670
1923
  if (pos !== null) {
1671
1924
  client._messages.splice(pos, 1);
1672
1925
  } else {
1673
- adapter.log.warn(`Client [${client.id}] received pubcomp for unknown message ID: ${packet.messageId}`);
1926
+ adapter.log.warn(
1927
+ `Client [${client.id}] received pubcomp for unknown message ID: ${packet.messageId}`,
1928
+ );
1674
1929
  }
1675
1930
  });
1676
1931
 
@@ -1680,7 +1935,10 @@ function MQTTServer(adapter) {
1680
1935
  return;
1681
1936
  }
1682
1937
  if (clients[client.id] && client.__secret !== clients[client.id].__secret) {
1683
- !adapter.config.ignorePings && adapter.log.warn(`Old client ${client.id} with secret ${client.__secret} sends pubrel. Ignore! Actual secret is ${clients[client.id].__secret}`);
1938
+ !adapter.config.ignorePings &&
1939
+ adapter.log.warn(
1940
+ `Old client ${client.id} with secret ${client.__secret} sends pubrel. Ignore! Actual secret is ${clients[client.id].__secret}`,
1941
+ );
1684
1942
  return;
1685
1943
  }
1686
1944
 
@@ -1690,27 +1948,33 @@ function MQTTServer(adapter) {
1690
1948
 
1691
1949
  let pos = null;
1692
1950
  // remove this message from queue
1693
- client._messages && client._messages.forEach((e, i) => {
1694
- if (e.messageId === packet.messageId) {
1695
- pos = i;
1696
- return false;
1697
- }
1698
- });
1951
+ client._messages &&
1952
+ client._messages.forEach((e, i) => {
1953
+ if (e.messageId === packet.messageId) {
1954
+ pos = i;
1955
+ return false;
1956
+ }
1957
+ });
1699
1958
 
1700
1959
  if (pos !== null) {
1701
- client.pubcomp({messageId: packet.messageId});
1960
+ client.pubcomp({ messageId: packet.messageId });
1702
1961
 
1703
1962
  receivedTopic(client._messages[pos], client);
1704
1963
  client._messages && client._messages.splice(pos, 1);
1705
1964
  } else {
1706
- adapter.log.warn(`Client [${client.id}] received pubrel on ${client.id} for unknown messageId ${packet.messageId}`);
1965
+ adapter.log.warn(
1966
+ `Client [${client.id}] received pubrel on ${client.id} for unknown messageId ${packet.messageId}`,
1967
+ );
1707
1968
  }
1708
1969
  });
1709
1970
 
1710
1971
  // response for QoS1
1711
1972
  client.on('puback', packet => {
1712
1973
  if (clients[client.id] && client.__secret !== clients[client.id].__secret) {
1713
- !adapter.config.ignorePings && adapter.log.warn(`Old client ${client.id} with secret ${client.__secret} sends puback. Ignore! Actual secret is ${clients[client.id].__secret}`);
1974
+ !adapter.config.ignorePings &&
1975
+ adapter.log.warn(
1976
+ `Old client ${client.id} with secret ${client.__secret} sends puback. Ignore! Actual secret is ${clients[client.id].__secret}`,
1977
+ );
1714
1978
  return;
1715
1979
  }
1716
1980
 
@@ -1721,24 +1985,32 @@ function MQTTServer(adapter) {
1721
1985
  // remove this message from queue
1722
1986
  let pos = null;
1723
1987
  // remove this message from queue
1724
- client._messages && client._messages.forEach((e, i) => {
1725
- if (e.messageId === packet.messageId) {
1726
- pos = i;
1727
- return false;
1728
- }
1729
- });
1988
+ client._messages &&
1989
+ client._messages.forEach((e, i) => {
1990
+ if (e.messageId === packet.messageId) {
1991
+ pos = i;
1992
+ return false;
1993
+ }
1994
+ });
1730
1995
 
1731
1996
  if (pos !== null) {
1732
- adapter.log.debug(`Client [${client.id}] received puback for ${client.id} message ID: ${packet.messageId}`);
1997
+ adapter.log.debug(
1998
+ `Client [${client.id}] received puback for ${client.id} message ID: ${packet.messageId}`,
1999
+ );
1733
2000
  client._messages && client._messages.splice(pos, 1);
1734
2001
  } else {
1735
- adapter.log.warn(`Client [${client.id}] received puback for unknown message ID: ${packet.messageId}`);
2002
+ adapter.log.warn(
2003
+ `Client [${client.id}] received puback for unknown message ID: ${packet.messageId}`,
2004
+ );
1736
2005
  }
1737
2006
  });
1738
2007
 
1739
2008
  client.on('unsubscribe', packet => {
1740
2009
  if (clients[client.id] && client.__secret !== clients[client.id].__secret) {
1741
- !adapter.config.ignorePings && adapter.log.warn(`Old client ${client.id} with secret ${client.__secret} sends unsubscribe. Ignore! Actual secret is ${clients[client.id].__secret}`);
2010
+ !adapter.config.ignorePings &&
2011
+ adapter.log.warn(
2012
+ `Old client ${client.id} with secret ${client.__secret} sends unsubscribe. Ignore! Actual secret is ${clients[client.id].__secret}`,
2013
+ );
1742
2014
  return;
1743
2015
  }
1744
2016
 
@@ -1746,12 +2018,15 @@ function MQTTServer(adapter) {
1746
2018
  persistentSessions[client.id].lastSeen = Date.now();
1747
2019
  }
1748
2020
 
1749
- client.unsuback({messageId: packet.messageId});
2021
+ client.unsuback({ messageId: packet.messageId });
1750
2022
  });
1751
2023
 
1752
2024
  client.on('subscribe', packet => {
1753
2025
  if (clients[client.id] && client.__secret !== clients[client.id].__secret) {
1754
- !adapter.config.ignorePings && adapter.log.warn(`Old client ${client.id} with secret ${client.__secret} sends subscribe. Ignore! Actual secret is ${clients[client.id].__secret}`);
2026
+ !adapter.config.ignorePings &&
2027
+ adapter.log.warn(
2028
+ `Old client ${client.id} with secret ${client.__secret} sends subscribe. Ignore! Actual secret is ${clients[client.id].__secret}`,
2029
+ );
1755
2030
  return;
1756
2031
  }
1757
2032
 
@@ -1763,12 +2038,15 @@ function MQTTServer(adapter) {
1763
2038
  // we expect subscribe for 'cmnd.sonoff.#'
1764
2039
  let granted = packet.subscriptions.map(subs => subs.qos);
1765
2040
 
1766
- client.suback({granted: granted, messageId: packet.messageId});
2041
+ client.suback({ granted: granted, messageId: packet.messageId });
1767
2042
  });
1768
2043
 
1769
2044
  client.on('pingreq', (/*packet*/) => {
1770
2045
  if (clients[client.id] && client.__secret !== clients[client.id].__secret) {
1771
- !adapter.config.ignorePings && adapter.log.warn(`Old client ${client.id} with secret ${client.__secret} sends pingreq. Ignore! Actual secret is ${clients[client.id].__secret}`);
2046
+ !adapter.config.ignorePings &&
2047
+ adapter.log.warn(
2048
+ `Old client ${client.id} with secret ${client.__secret} sends pingreq. Ignore! Actual secret is ${clients[client.id].__secret}`,
2049
+ );
1772
2050
  return;
1773
2051
  }
1774
2052
 
@@ -1781,15 +2059,15 @@ function MQTTServer(adapter) {
1781
2059
  });
1782
2060
  });
1783
2061
 
1784
- server.on('error', err =>
1785
- adapter.log.error(`Can not start Server ${err}`));
2062
+ server.on('error', err => adapter.log.error(`Can not start Server ${err}`));
1786
2063
 
1787
2064
  config.port = parseInt(config.port, 10) || 1883;
1788
2065
 
1789
2066
  config.retransmitInterval = config.retransmitInterval || 2000;
1790
- config.retransmitCount = config.retransmitCount || 10;
1791
- config.storeClientsTime = config.storeClientsTime === undefined ? 1440 : parseInt(config.storeClientsTime, 10) || 0;
1792
- config.defaultQoS = parseInt(config.defaultQoS, 10) || 0;
2067
+ config.retransmitCount = config.retransmitCount || 10;
2068
+ config.storeClientsTime =
2069
+ config.storeClientsTime === undefined ? 1440 : parseInt(config.storeClientsTime, 10) || 0;
2070
+ config.defaultQoS = parseInt(config.defaultQoS, 10) || 0;
1793
2071
 
1794
2072
  // Update connection state
1795
2073
  updateClients();
@@ -1798,9 +2076,7 @@ function MQTTServer(adapter) {
1798
2076
  server.listen(config.port, config.bind, () => {
1799
2077
  adapter.log.info(`Starting MQTT ${config.user ? 'authenticated ' : ''} server on port ${config.port}`);
1800
2078
 
1801
- resendTimer = setInterval(() =>
1802
- !resending && checkResends(),
1803
- adapter.config.retransmitInterval || 2000);
2079
+ resendTimer = setInterval(() => !resending && checkResends(), adapter.config.retransmitInterval || 2000);
1804
2080
  });
1805
2081
  })(adapter.config);
1806
2082