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/LICENSE +1 -1
- package/README.md +21 -1
- package/admin/i18n/de/translations.json +33 -21
- package/admin/i18n/en/translations.json +14 -2
- package/admin/i18n/es/translations.json +13 -1
- package/admin/i18n/fr/translations.json +13 -1
- package/admin/i18n/it/translations.json +13 -1
- package/admin/i18n/nl/translations.json +13 -1
- package/admin/i18n/pl/translations.json +13 -1
- package/admin/i18n/pt/translations.json +13 -1
- package/admin/i18n/ru/translations.json +13 -1
- package/admin/i18n/uk/translations.json +13 -1
- package/admin/i18n/zh-cn/translations.json +13 -1
- package/admin/jsonConfig.json +132 -29
- package/admin/words.js +36 -24
- package/io-package.json +34 -27
- package/lib/datapoints.js +368 -214
- package/lib/server.js +515 -239
- package/main.js +10 -5
- package/package.json +10 -8
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 = /[\]
|
|
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)
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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 = [
|
|
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
|
|
249
|
-
stateId
|
|
250
|
-
|
|
251
|
-
if (
|
|
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
|
|
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(
|
|
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)
|
|
329
|
-
|
|
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)
|
|
346
|
-
|
|
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)
|
|
363
|
-
|
|
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
|
-
|
|
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)
|
|
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)
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
603
|
-
const stateId
|
|
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(
|
|
741
|
+
setStateImmediate(
|
|
742
|
+
mappingClients[channelId],
|
|
743
|
+
stateId,
|
|
744
|
+
state.val === null ? '' : state.val.toString(),
|
|
745
|
+
);
|
|
620
746
|
break;
|
|
621
747
|
case 'string':
|
|
622
|
-
setStateImmediate(
|
|
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.
|
|
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,
|
|
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 (
|
|
973
|
+
if (
|
|
974
|
+
clients[id] &&
|
|
842
975
|
(clientObj.native.port !== clients[id].stream.remotePort ||
|
|
843
|
-
|
|
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 &=
|
|
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(
|
|
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(
|
|
919
|
-
|
|
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[
|
|
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
|
|
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 (!
|
|
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
|
-
|
|
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],
|
|
1146
|
+
checkData(client, topic, `${prefix}.${nPath}`, data[attr], nestedUnits);
|
|
999
1147
|
} else {
|
|
1000
|
-
checkData(client, topic, prefix, data[attr],
|
|
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(
|
|
1009
|
-
let replaceAttr = types[
|
|
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(
|
|
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';
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 ?
|
|
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(
|
|
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 ?
|
|
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(
|
|
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 ?
|
|
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
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
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
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
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"}'))
|
|
1280
|
-
|
|
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, '
|
|
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)
|
|
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] &&
|
|
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 (
|
|
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(
|
|
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(
|
|
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 (
|
|
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(
|
|
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(
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
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 &&
|
|
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(
|
|
1609
|
-
|
|
1610
|
-
|
|
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 &&
|
|
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 &&
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
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(
|
|
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 &&
|
|
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 &&
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
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(
|
|
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 &&
|
|
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 &&
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
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(
|
|
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 &&
|
|
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 &&
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
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(
|
|
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(
|
|
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 &&
|
|
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 &&
|
|
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 &&
|
|
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
|
|
1791
|
-
config.storeClientsTime
|
|
1792
|
-
|
|
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
|
|