iobroker.sonoff 3.1.2 → 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 +29 -2
- 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 +146 -28
- package/admin/words.js +36 -24
- package/io-package.json +36 -29
- package/lib/datapoints.js +368 -207
- package/lib/server.js +561 -244
- package/main.js +10 -7
- 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 {
|
|
@@ -815,14 +947,56 @@ function MQTTServer(adapter) {
|
|
|
815
947
|
}
|
|
816
948
|
}
|
|
817
949
|
|
|
818
|
-
function updateClients() {
|
|
950
|
+
async function updateClients() {
|
|
951
|
+
const clientIds = [];
|
|
952
|
+
if (clients) {
|
|
953
|
+
for (const id in clients) {
|
|
954
|
+
const oid = `info.clients.${id.replace(/[.\s]+/g, '_').replace(FORBIDDEN_CHARS, '_')}`;
|
|
955
|
+
clientIds.push(oid);
|
|
956
|
+
const clientObj = await adapter.getObjectAsync(oid);
|
|
957
|
+
if (!clientObj?.native) {
|
|
958
|
+
await adapter.setObjectAsync(oid, {
|
|
959
|
+
type: 'state',
|
|
960
|
+
common: {
|
|
961
|
+
name: id,
|
|
962
|
+
role: 'indicator.reachable',
|
|
963
|
+
type: 'boolean',
|
|
964
|
+
read: true,
|
|
965
|
+
write: false,
|
|
966
|
+
},
|
|
967
|
+
native: {
|
|
968
|
+
ip: clients[id].stream.remoteAddress,
|
|
969
|
+
port: clients[id].stream.remotePort,
|
|
970
|
+
},
|
|
971
|
+
});
|
|
972
|
+
} else {
|
|
973
|
+
if (
|
|
974
|
+
clients[id] &&
|
|
975
|
+
(clientObj.native.port !== clients[id].stream.remotePort ||
|
|
976
|
+
clientObj.native.ip !== clients[id].stream.remoteAddress)
|
|
977
|
+
) {
|
|
978
|
+
clientObj.native.port = clients[id].stream.remotePort;
|
|
979
|
+
clientObj.native.ip = clients[id].stream.remoteAddress;
|
|
980
|
+
adapter.setObject(clientObj._id, clientObj);
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
await adapter.setStateAsync(oid, true, true);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
// read all other states and set alive to false
|
|
987
|
+
const allStates = await adapter.getStatesAsync('info.clients.*');
|
|
988
|
+
for (const id in allStates) {
|
|
989
|
+
if (!clientIds.includes(id.replace(`${adapter.namespace}.`, ''))) {
|
|
990
|
+
await adapter.setStateAsync(id, { val: false, ack: true });
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
|
|
819
994
|
let text = '';
|
|
820
995
|
if (clients) {
|
|
821
996
|
for (let id in clients) {
|
|
822
997
|
text += `${text ? ',' : ''}${id}`;
|
|
823
998
|
}
|
|
824
999
|
}
|
|
825
|
-
|
|
826
1000
|
adapter.setState('info.connection', text, true);
|
|
827
1001
|
}
|
|
828
1002
|
|
|
@@ -846,8 +1020,8 @@ function MQTTServer(adapter) {
|
|
|
846
1020
|
retain = undefined;
|
|
847
1021
|
}
|
|
848
1022
|
adapter.log.debug(`Send to "${client.id}": ${topic} = ${state}`);
|
|
849
|
-
client.publish({topic: topic, payload: state, qos: qos, retain: retain, messageId: messageId++}, cb);
|
|
850
|
-
messageId &=
|
|
1023
|
+
client.publish({ topic: topic, payload: state, qos: qos, retain: retain, messageId: messageId++ }, cb);
|
|
1024
|
+
messageId &= 0xffffffff;
|
|
851
1025
|
}
|
|
852
1026
|
|
|
853
1027
|
function resendMessages2Client(client, messages, i) {
|
|
@@ -856,7 +1030,9 @@ function MQTTServer(adapter) {
|
|
|
856
1030
|
try {
|
|
857
1031
|
messages[i].ts = Date.now();
|
|
858
1032
|
messages[i].count++;
|
|
859
|
-
adapter.log.debug(
|
|
1033
|
+
adapter.log.debug(
|
|
1034
|
+
`Client [${client.id}] resend messages on connect: ${messages[i].topic} = ${messages[i].payload}`,
|
|
1035
|
+
);
|
|
860
1036
|
if (messages[i].cmd === 'publish') {
|
|
861
1037
|
client.publish(messages[i]);
|
|
862
1038
|
}
|
|
@@ -874,18 +1050,20 @@ function MQTTServer(adapter) {
|
|
|
874
1050
|
}
|
|
875
1051
|
}
|
|
876
1052
|
|
|
877
|
-
function addObject(
|
|
878
|
-
|
|
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;
|
|
879
1057
|
let id = `${adapter.namespace}.${client.iobId}.${prefix ? `${prefix}.` : ''}${path.length ? `${path.join('_')}_` : ''}${replaceAttr.replace(FORBIDDEN_CHARS, '_')}`;
|
|
880
1058
|
let obj = {
|
|
881
1059
|
type: 'addObject',
|
|
882
1060
|
id: id,
|
|
883
1061
|
data: {
|
|
884
1062
|
_id: id,
|
|
885
|
-
common: Object.assign({}, types[
|
|
1063
|
+
common: Object.assign({}, types[typeKey]),
|
|
886
1064
|
native: {},
|
|
887
|
-
type: 'state'
|
|
888
|
-
}
|
|
1065
|
+
type: 'state',
|
|
1066
|
+
},
|
|
889
1067
|
};
|
|
890
1068
|
obj.data.common.name = `${client.id} ${prefix ? `${prefix} ` : ''}${path.length ? `${path.join(' ')} ` : ''} ${replaceAttr}`;
|
|
891
1069
|
return obj;
|
|
@@ -905,26 +1083,35 @@ function MQTTServer(adapter) {
|
|
|
905
1083
|
});
|
|
906
1084
|
}
|
|
907
1085
|
|
|
908
|
-
path
|
|
1086
|
+
path = path || [];
|
|
909
1087
|
prefix = prefix || '';
|
|
910
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
|
+
|
|
911
1093
|
for (let attr in data) {
|
|
912
|
-
if (!
|
|
1094
|
+
if (!Object.prototype.hasOwnProperty.call(data, attr)) {
|
|
913
1095
|
adapter.log.warn(`[${client.id}] attr error: ${attr}${data[attr]}`);
|
|
914
1096
|
continue;
|
|
915
1097
|
}
|
|
916
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
|
+
|
|
917
1104
|
if (typeof data[attr] === 'object') {
|
|
918
1105
|
// check for arrays
|
|
919
1106
|
if (types[attr]) {
|
|
920
1107
|
if (types[attr].type === 'array') {
|
|
921
|
-
// transform to array of attributes
|
|
1108
|
+
// transform to an array of attributes
|
|
922
1109
|
for (let i = 1; i <= 10; i++) {
|
|
923
1110
|
let val = data[attr][i - 1];
|
|
924
1111
|
if (typeof val === 'undefined') {
|
|
925
1112
|
break;
|
|
926
1113
|
}
|
|
927
|
-
// define new object
|
|
1114
|
+
// define a new object
|
|
928
1115
|
let replaceAttr = attr.replace(FORBIDDEN_CHARS, '_') + i.toString();
|
|
929
1116
|
let id = `${adapter.namespace}.${client.iobId}.${prefix ? `${prefix}.` : ''}${path.length ? `${path.join('_')}_` : ''}${replaceAttr}`;
|
|
930
1117
|
let obj = {
|
|
@@ -934,8 +1121,8 @@ function MQTTServer(adapter) {
|
|
|
934
1121
|
_id: id,
|
|
935
1122
|
common: Object.assign({}, types[attr]),
|
|
936
1123
|
native: {},
|
|
937
|
-
type: 'state'
|
|
938
|
-
}
|
|
1124
|
+
type: 'state',
|
|
1125
|
+
},
|
|
939
1126
|
};
|
|
940
1127
|
obj.data.common.name = `${client.id} ${prefix ? `${prefix} ` : ''}${path.length ? `${path.join(' ')} ` : ''} ${replaceAttr}`;
|
|
941
1128
|
obj.data.common.type = typeof val;
|
|
@@ -947,29 +1134,45 @@ function MQTTServer(adapter) {
|
|
|
947
1134
|
} else {
|
|
948
1135
|
let nPath = Object.assign([], path);
|
|
949
1136
|
nPath.push(attr.replace(FORBIDDEN_CHARS, '_'));
|
|
950
|
-
|
|
1137
|
+
const nestedUnits = pressureUnit || tempUnit ? { pressureUnit, tempUnit } : unit;
|
|
1138
|
+
checkData(client, topic, prefix, data[attr], nestedUnits, nPath);
|
|
951
1139
|
nPath = undefined;
|
|
952
1140
|
}
|
|
953
1141
|
} else {
|
|
954
1142
|
let nPath = Object.assign([], path);
|
|
955
1143
|
nPath.push(attr.replace(FORBIDDEN_CHARS, '_'));
|
|
1144
|
+
const nestedUnits = pressureUnit || tempUnit ? { pressureUnit, tempUnit } : unit;
|
|
956
1145
|
if (adapter.config.OBJ_TREE) {
|
|
957
|
-
checkData(client, topic, `${prefix}.${nPath}`, data[attr],
|
|
1146
|
+
checkData(client, topic, `${prefix}.${nPath}`, data[attr], nestedUnits);
|
|
958
1147
|
} else {
|
|
959
|
-
checkData(client, topic, prefix, data[attr],
|
|
1148
|
+
checkData(client, topic, prefix, data[attr], nestedUnits, nPath);
|
|
960
1149
|
}
|
|
961
1150
|
nPath = undefined;
|
|
962
1151
|
}
|
|
963
|
-
} else if (types[attr]) {
|
|
1152
|
+
} else if (types[attr] || (path.length > 0 && types[`${path.join('_')}_${attr}`])) {
|
|
964
1153
|
let allowReadColors;
|
|
965
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
|
+
|
|
966
1159
|
// create object
|
|
967
|
-
const obj = addObject(
|
|
968
|
-
let replaceAttr = types[
|
|
1160
|
+
const obj = addObject(typeKey, client, prefix, path);
|
|
1161
|
+
let replaceAttr = types[typeKey].replace || attr;
|
|
969
1162
|
|
|
970
1163
|
if (obj.data.common.storeMap) {
|
|
971
1164
|
delete obj.data.common.storeMap;
|
|
972
|
-
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;
|
|
973
1176
|
}
|
|
974
1177
|
|
|
975
1178
|
// adaptions for magichome tasmota
|
|
@@ -979,7 +1182,7 @@ function MQTTServer(adapter) {
|
|
|
979
1182
|
|
|
980
1183
|
// if ledFlags bit 2, read color from tasmota, else ignore
|
|
981
1184
|
if (data[attr].length === 10) {
|
|
982
|
-
obj.data.common.role = 'level.color.rgbcwww';
|
|
1185
|
+
obj.data.common.role = 'level.color.rgbcwww'; // RGB + cold white + white???
|
|
983
1186
|
} else if (data[attr].length === 8) {
|
|
984
1187
|
obj.data.common.role = 'level.color.rgbww'; // RGB + White
|
|
985
1188
|
} else {
|
|
@@ -1002,15 +1205,24 @@ function MQTTServer(adapter) {
|
|
|
1002
1205
|
|
|
1003
1206
|
xObj = addObject('Red', client, prefix, path);
|
|
1004
1207
|
xObj.data.common.read = allowReadColors;
|
|
1005
|
-
updateState(
|
|
1208
|
+
updateState(
|
|
1209
|
+
xObj,
|
|
1210
|
+
allowReadColors ? (100 * parseInt(data[attr].substring(0, 2), 16)) / 255 : undefined,
|
|
1211
|
+
);
|
|
1006
1212
|
|
|
1007
1213
|
xObj = addObject('Green', client, prefix, path);
|
|
1008
1214
|
xObj.data.common.read = allowReadColors;
|
|
1009
|
-
updateState(
|
|
1215
|
+
updateState(
|
|
1216
|
+
xObj,
|
|
1217
|
+
allowReadColors ? (100 * parseInt(data[attr].substring(2, 4), 16)) / 255 : undefined,
|
|
1218
|
+
);
|
|
1010
1219
|
|
|
1011
1220
|
xObj = addObject('Blue', client, prefix, path);
|
|
1012
1221
|
xObj.data.common.read = allowReadColors;
|
|
1013
|
-
updateState(
|
|
1222
|
+
updateState(
|
|
1223
|
+
xObj,
|
|
1224
|
+
allowReadColors ? (100 * parseInt(data[attr].substring(4, 6), 16)) / 255 : undefined,
|
|
1225
|
+
);
|
|
1014
1226
|
|
|
1015
1227
|
xObj = addObject('RGB_POWER', client, prefix, path);
|
|
1016
1228
|
xObj.data.common.read = allowReadColors;
|
|
@@ -1021,30 +1233,38 @@ function MQTTServer(adapter) {
|
|
|
1021
1233
|
// rgbww
|
|
1022
1234
|
xObj = addObject('WW', client, prefix, path);
|
|
1023
1235
|
xObj.data.common.read = allowReadColors;
|
|
1024
|
-
updateState(
|
|
1236
|
+
updateState(
|
|
1237
|
+
xObj,
|
|
1238
|
+
allowReadColors ? (100 * parseInt(data[attr].substring(6, 8), 16)) / 255 : undefined,
|
|
1239
|
+
);
|
|
1025
1240
|
|
|
1026
1241
|
xObj = addObject('WW_POWER', client, prefix, path);
|
|
1027
1242
|
xObj.data.common.read = allowReadColors;
|
|
1028
|
-
updateState(xObj, allowReadColors ?
|
|
1029
|
-
} else
|
|
1030
|
-
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') {
|
|
1031
1245
|
// rgbcwww
|
|
1032
1246
|
xObj = addObject('CW', client, prefix, path);
|
|
1033
1247
|
xObj.data.common.read = allowReadColors;
|
|
1034
|
-
updateState(
|
|
1248
|
+
updateState(
|
|
1249
|
+
xObj,
|
|
1250
|
+
allowReadColors ? (100 * parseInt(data[attr].substring(6, 8), 16)) / 255 : undefined,
|
|
1251
|
+
);
|
|
1035
1252
|
|
|
1036
1253
|
xObj = addObject('CW_POWER', client, prefix, path);
|
|
1037
1254
|
xObj.data.common.read = allowReadColors;
|
|
1038
|
-
updateState(xObj, allowReadColors ?
|
|
1255
|
+
updateState(xObj, allowReadColors ? val > 0 : undefined);
|
|
1039
1256
|
|
|
1040
1257
|
xObj = addObject('WW', client, prefix, path);
|
|
1041
1258
|
xObj.data.common.read = allowReadColors;
|
|
1042
|
-
updateState(
|
|
1259
|
+
updateState(
|
|
1260
|
+
xObj,
|
|
1261
|
+
allowReadColors ? (100 * parseInt(data[attr].substring(8, 10), 16)) / 255 : undefined,
|
|
1262
|
+
);
|
|
1043
1263
|
|
|
1044
1264
|
xObj = addObject('WW_POWER', client, prefix, path);
|
|
1045
1265
|
xObj.data.common.read = allowReadColors;
|
|
1046
1266
|
val = parseInt(data[attr].substring(8, 10), 16);
|
|
1047
|
-
updateState(xObj, allowReadColors ?
|
|
1267
|
+
updateState(xObj, allowReadColors ? val > 0 : undefined);
|
|
1048
1268
|
}
|
|
1049
1269
|
}
|
|
1050
1270
|
}
|
|
@@ -1075,6 +1295,42 @@ function MQTTServer(adapter) {
|
|
|
1075
1295
|
|
|
1076
1296
|
updateState(obj, val);
|
|
1077
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
|
+
|
|
1078
1334
|
if (powerStateFound) {
|
|
1079
1335
|
adapter.log.debug(`Got .STATE.POWER update, auto syncing .POWER (src: ${obj.id})`);
|
|
1080
1336
|
let powerObj = Object.assign({}, obj);
|
|
@@ -1089,22 +1345,23 @@ function MQTTServer(adapter) {
|
|
|
1089
1345
|
// tele/sonoff/SENSOR tele/sonoff/STATE => read only
|
|
1090
1346
|
// stat/sonoff/RESULT => read,write
|
|
1091
1347
|
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
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
|
|
1105
1362
|
attributes.write = true;
|
|
1106
1363
|
attributes.role = 'level';
|
|
1107
|
-
|
|
1364
|
+
}
|
|
1108
1365
|
|
|
1109
1366
|
attributes.type = typeof val;
|
|
1110
1367
|
if (!['number', 'string', 'boolean'].includes(attributes.type)) {
|
|
@@ -1112,19 +1369,19 @@ function MQTTServer(adapter) {
|
|
|
1112
1369
|
val = (val || '').toString();
|
|
1113
1370
|
}
|
|
1114
1371
|
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
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}`;
|
|
1128
1385
|
// due to some (for ex. NSPanel) versions do not automatically send .POWER switch data
|
|
1129
1386
|
// but this is generally a default for all devices (state cannot exist without switch) we now add
|
|
1130
1387
|
// POWER switches automatically if we get POWER STATE
|
|
@@ -1144,7 +1401,7 @@ function MQTTServer(adapter) {
|
|
|
1144
1401
|
powerObj.data.common.name = powerName;
|
|
1145
1402
|
updateState(powerObj, val);
|
|
1146
1403
|
}
|
|
1147
|
-
|
|
1404
|
+
}
|
|
1148
1405
|
}
|
|
1149
1406
|
}
|
|
1150
1407
|
}
|
|
@@ -1158,7 +1415,7 @@ function MQTTServer(adapter) {
|
|
|
1158
1415
|
client.states[packet.topic] = {
|
|
1159
1416
|
message: packet.payload,
|
|
1160
1417
|
retain: packet.retain,
|
|
1161
|
-
qos: packet.qos
|
|
1418
|
+
qos: packet.qos,
|
|
1162
1419
|
};
|
|
1163
1420
|
|
|
1164
1421
|
// update alive state
|
|
@@ -1235,8 +1492,12 @@ function MQTTServer(adapter) {
|
|
|
1235
1492
|
// ignore: stat/Sonoff/RESULT = {"POWER":"ON"}
|
|
1236
1493
|
// testserver.js reports error, so reject above cmd
|
|
1237
1494
|
const str = val.replace(/\s+/g, '');
|
|
1238
|
-
if (str.startsWith('{"POWER":"ON"}'))
|
|
1239
|
-
|
|
1495
|
+
if (str.startsWith('{"POWER":"ON"}')) {
|
|
1496
|
+
return;
|
|
1497
|
+
}
|
|
1498
|
+
if (str.startsWith('{"POWER":"OFF"}')) {
|
|
1499
|
+
return;
|
|
1500
|
+
}
|
|
1240
1501
|
|
|
1241
1502
|
if (parts[0] === 'stat' || parts[0] === 'tele') {
|
|
1242
1503
|
try {
|
|
@@ -1266,7 +1527,7 @@ function MQTTServer(adapter) {
|
|
|
1266
1527
|
} else if (stateId.match(/^(WAKEUP)\d?$/)) {
|
|
1267
1528
|
checkData(client, packet.topic, 'WAKEUP', JSON.parse(val));
|
|
1268
1529
|
} else if (stateId.match(/^(RESULT)\d?$/)) {
|
|
1269
|
-
checkData(client, packet.topic, '
|
|
1530
|
+
checkData(client, packet.topic, 'RESULT', JSON.parse(val));
|
|
1270
1531
|
}
|
|
1271
1532
|
} else {
|
|
1272
1533
|
checkData(client, packet.topic, NO_PREFIX, JSON.parse(val));
|
|
@@ -1291,6 +1552,13 @@ function MQTTServer(adapter) {
|
|
|
1291
1552
|
} catch (e) {
|
|
1292
1553
|
adapter.log.warn(`Client [${client.id}] cannot parse data"${stateId}": _${val}_ - ${e}`);
|
|
1293
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
|
+
}
|
|
1294
1562
|
} else if (types[stateId]) {
|
|
1295
1563
|
// /ESP_BOX/BM280/Pressure = 1010.09
|
|
1296
1564
|
// /ESP_BOX/BM280/Humidity = 42.39
|
|
@@ -1301,7 +1569,7 @@ function MQTTServer(adapter) {
|
|
|
1301
1569
|
// stat/sonoff/POWER
|
|
1302
1570
|
|
|
1303
1571
|
if (types[stateId]) {
|
|
1304
|
-
let id = `${adapter.namespace}.${client.iobId}.${stateId.replace(/[-.+\s]+/g, '_')}`;
|
|
1572
|
+
let id = `${adapter.namespace}.${client.iobId}.${stateId.replace(/[-.+\s]+/g, '_').replace(FORBIDDEN_CHARS, '_')}`;
|
|
1305
1573
|
let obj = {
|
|
1306
1574
|
type: 'addObject',
|
|
1307
1575
|
id: id,
|
|
@@ -1309,15 +1577,14 @@ function MQTTServer(adapter) {
|
|
|
1309
1577
|
_id: id,
|
|
1310
1578
|
common: JSON.parse(JSON.stringify(types[stateId])),
|
|
1311
1579
|
native: {},
|
|
1312
|
-
type: 'state'
|
|
1313
|
-
}
|
|
1580
|
+
type: 'state',
|
|
1581
|
+
},
|
|
1314
1582
|
};
|
|
1315
1583
|
obj.data.common.name = `${client.id} ${stateId}`;
|
|
1316
1584
|
|
|
1317
1585
|
// push only new objects
|
|
1318
1586
|
updateState(obj, () => {
|
|
1319
1587
|
if (parts[0] === 'cmnd') {
|
|
1320
|
-
|
|
1321
1588
|
// Set Object fix
|
|
1322
1589
|
if (obj.data.common.type === 'number') {
|
|
1323
1590
|
adapter.setState(id, parseFloat(val), true);
|
|
@@ -1354,7 +1621,9 @@ function MQTTServer(adapter) {
|
|
|
1354
1621
|
}
|
|
1355
1622
|
|
|
1356
1623
|
function clientClose(client, reason) {
|
|
1357
|
-
if (!client)
|
|
1624
|
+
if (!client) {
|
|
1625
|
+
return;
|
|
1626
|
+
}
|
|
1358
1627
|
|
|
1359
1628
|
if (persistentSessions[client.id]) {
|
|
1360
1629
|
persistentSessions[client.id].connected = false;
|
|
@@ -1366,7 +1635,7 @@ function MQTTServer(adapter) {
|
|
|
1366
1635
|
}
|
|
1367
1636
|
|
|
1368
1637
|
try {
|
|
1369
|
-
if (clients[client.id] &&
|
|
1638
|
+
if (clients[client.id] && client.__secret === clients[client.id].__secret) {
|
|
1370
1639
|
adapter.log.info(`Client [${client.id}] connection closed: ${reason}`);
|
|
1371
1640
|
updateAlive(client, false);
|
|
1372
1641
|
delete clients[client.id];
|
|
@@ -1388,12 +1657,18 @@ function MQTTServer(adapter) {
|
|
|
1388
1657
|
const now = Date.now();
|
|
1389
1658
|
resending = true;
|
|
1390
1659
|
for (const clientId in clients) {
|
|
1391
|
-
if (
|
|
1660
|
+
if (
|
|
1661
|
+
Object.prototype.hasOwnProperty.call(clients, clientId) &&
|
|
1662
|
+
clients[clientId] &&
|
|
1663
|
+
clients[clientId]._messages
|
|
1664
|
+
) {
|
|
1392
1665
|
for (let m = clients[clientId]._messages.length - 1; m >= 0; m--) {
|
|
1393
1666
|
const message = clients[clientId]._messages[m];
|
|
1394
1667
|
if (now - message.ts >= adapter.config.retransmitInterval) {
|
|
1395
1668
|
if (message.count > adapter.config.retransmitCount) {
|
|
1396
|
-
adapter.log.warn(
|
|
1669
|
+
adapter.log.warn(
|
|
1670
|
+
`Client [${clientId}] message ${message.messageId} deleted after ${message.count} retries`,
|
|
1671
|
+
);
|
|
1397
1672
|
clients[clientId]._messages.splice(m, 1);
|
|
1398
1673
|
continue;
|
|
1399
1674
|
}
|
|
@@ -1402,7 +1677,9 @@ function MQTTServer(adapter) {
|
|
|
1402
1677
|
message.count++;
|
|
1403
1678
|
message.ts = now;
|
|
1404
1679
|
try {
|
|
1405
|
-
adapter.log.debug(
|
|
1680
|
+
adapter.log.debug(
|
|
1681
|
+
`Client [${clientId}] resend message topic: ${message.topic}, payload: ${message.payload}`,
|
|
1682
|
+
);
|
|
1406
1683
|
if (message.cmd === 'publish') {
|
|
1407
1684
|
clients[clientId].publish(message);
|
|
1408
1685
|
}
|
|
@@ -1424,7 +1701,7 @@ function MQTTServer(adapter) {
|
|
|
1424
1701
|
// delete old sessions
|
|
1425
1702
|
if (adapter.config.storeClientsTime !== -1) {
|
|
1426
1703
|
for (const id in persistentSessions) {
|
|
1427
|
-
if (
|
|
1704
|
+
if (Object.prototype.hasOwnProperty.call(persistentSessions, id)) {
|
|
1428
1705
|
if (now - persistentSessions[id].lastSeen > adapter.config.storeClientsTime * 60000) {
|
|
1429
1706
|
delete persistentSessions[id];
|
|
1430
1707
|
}
|
|
@@ -1449,22 +1726,21 @@ function MQTTServer(adapter) {
|
|
|
1449
1726
|
|
|
1450
1727
|
// client connected
|
|
1451
1728
|
client.on('connect', options => {
|
|
1452
|
-
// acknowledge the connect packet
|
|
1729
|
+
// acknowledge the "connect" packet
|
|
1453
1730
|
client.id = options.clientId;
|
|
1454
1731
|
client.iobId = client.id.replace(FORBIDDEN_CHARS, '_');
|
|
1455
1732
|
mappingClients[client.iobId] = client.id;
|
|
1456
1733
|
|
|
1457
|
-
// get possible old client
|
|
1734
|
+
// get possible an old client
|
|
1458
1735
|
let oldClient = clients[client.id];
|
|
1459
1736
|
|
|
1460
1737
|
if (config.user) {
|
|
1461
1738
|
if (options.password) {
|
|
1462
1739
|
options.password = options.password.toString();
|
|
1463
1740
|
}
|
|
1464
|
-
if (config.user !== options.username ||
|
|
1465
|
-
config.password !== options.password) {
|
|
1741
|
+
if (config.user !== options.username || config.password !== options.password) {
|
|
1466
1742
|
adapter.log.warn(`Client [${client.id}] has invalid password or username`);
|
|
1467
|
-
client.connack({returnCode: 4});
|
|
1743
|
+
client.connack({ returnCode: 4 });
|
|
1468
1744
|
if (oldClient) {
|
|
1469
1745
|
// delete existing client
|
|
1470
1746
|
delete clients[client.id];
|
|
@@ -1478,12 +1754,13 @@ function MQTTServer(adapter) {
|
|
|
1478
1754
|
}
|
|
1479
1755
|
|
|
1480
1756
|
if (oldClient) {
|
|
1481
|
-
adapter.log.info(
|
|
1757
|
+
adapter.log.info(
|
|
1758
|
+
`Client [${client.id}] reconnected. Old secret ${clients[client.id].__secret} ==> New secret ${client.__secret}`,
|
|
1759
|
+
);
|
|
1482
1760
|
// need to destroy the old client
|
|
1483
1761
|
|
|
1484
1762
|
if (client.__secret !== clients[client.id].__secret) {
|
|
1485
1763
|
// it is another socket!!
|
|
1486
|
-
|
|
1487
1764
|
// It was following situation:
|
|
1488
1765
|
// - old connection was active
|
|
1489
1766
|
// - new connection is on the same TCP
|
|
@@ -1505,7 +1782,7 @@ function MQTTServer(adapter) {
|
|
|
1505
1782
|
_subsID: {},
|
|
1506
1783
|
_subs: {},
|
|
1507
1784
|
messages: [],
|
|
1508
|
-
lastSeen: Date.now()
|
|
1785
|
+
lastSeen: Date.now(),
|
|
1509
1786
|
};
|
|
1510
1787
|
}
|
|
1511
1788
|
client._messages = persistentSessions[client.id].messages;
|
|
@@ -1515,7 +1792,7 @@ function MQTTServer(adapter) {
|
|
|
1515
1792
|
}
|
|
1516
1793
|
client._messages = client._messages || [];
|
|
1517
1794
|
|
|
1518
|
-
client.connack({returnCode: 0, sessionPresent});
|
|
1795
|
+
client.connack({ returnCode: 0, sessionPresent });
|
|
1519
1796
|
clients[client.id] = client;
|
|
1520
1797
|
updateClients();
|
|
1521
1798
|
|
|
@@ -1526,10 +1803,14 @@ function MQTTServer(adapter) {
|
|
|
1526
1803
|
client._subs = persistentSessions[client.id]._subs;
|
|
1527
1804
|
if (persistentSessions[client.id].messages.length) {
|
|
1528
1805
|
// give to the client a little bit time
|
|
1529
|
-
client._resendonStart = setTimeout(
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1806
|
+
client._resendonStart = setTimeout(
|
|
1807
|
+
clientId => {
|
|
1808
|
+
client._resendonStart = null;
|
|
1809
|
+
resendMessages2Client(client, persistentSessions[clientId].messages);
|
|
1810
|
+
},
|
|
1811
|
+
100,
|
|
1812
|
+
client.id,
|
|
1813
|
+
);
|
|
1533
1814
|
}
|
|
1534
1815
|
}
|
|
1535
1816
|
});
|
|
@@ -1549,7 +1830,10 @@ function MQTTServer(adapter) {
|
|
|
1549
1830
|
|
|
1550
1831
|
client.on('publish', packet => {
|
|
1551
1832
|
if (clients[client.id] && client.__secret !== clients[client.id].__secret) {
|
|
1552
|
-
!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
|
+
);
|
|
1553
1837
|
return;
|
|
1554
1838
|
}
|
|
1555
1839
|
|
|
@@ -1558,33 +1842,36 @@ function MQTTServer(adapter) {
|
|
|
1558
1842
|
}
|
|
1559
1843
|
|
|
1560
1844
|
if (packet.qos === 1) {
|
|
1561
|
-
// send PUBACK to client
|
|
1562
|
-
client.puback({messageId: packet.messageId});
|
|
1845
|
+
// send PUBACK to a client
|
|
1846
|
+
client.puback({ messageId: packet.messageId });
|
|
1563
1847
|
} else if (packet.qos === 2) {
|
|
1564
1848
|
const pack = client._messages && client._messages.find(e => e.messageId === packet.messageId);
|
|
1565
1849
|
if (pack) {
|
|
1566
1850
|
// duplicate message => ignore
|
|
1567
|
-
adapter.log.warn(
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
packet.ts = Date.now();
|
|
1571
|
-
packet.cmd = 'pubrel';
|
|
1572
|
-
packet.count = 0;
|
|
1573
|
-
client._messages.push(packet);
|
|
1574
|
-
|
|
1575
|
-
client.pubrec({messageId: packet.messageId});
|
|
1851
|
+
adapter.log.warn(
|
|
1852
|
+
`Client [${client.id}] ignored duplicate message with ID: ${packet.messageId}`,
|
|
1853
|
+
);
|
|
1576
1854
|
return;
|
|
1577
1855
|
}
|
|
1578
|
-
|
|
1856
|
+
packet.ts = Date.now();
|
|
1857
|
+
packet.cmd = 'pubrel';
|
|
1858
|
+
packet.count = 0;
|
|
1859
|
+
client._messages.push(packet);
|
|
1579
1860
|
|
|
1861
|
+
client.pubrec({ messageId: packet.messageId });
|
|
1862
|
+
return;
|
|
1863
|
+
}
|
|
1580
1864
|
|
|
1581
|
-
receivedTopic(packet, client)
|
|
1865
|
+
receivedTopic(packet, client);
|
|
1582
1866
|
});
|
|
1583
1867
|
|
|
1584
1868
|
// response for QoS2
|
|
1585
1869
|
client.on('pubrec', packet => {
|
|
1586
1870
|
if (clients[client.id] && client.__secret !== clients[client.id].__secret) {
|
|
1587
|
-
!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
|
+
);
|
|
1588
1875
|
return;
|
|
1589
1876
|
}
|
|
1590
1877
|
|
|
@@ -1594,23 +1881,29 @@ function MQTTServer(adapter) {
|
|
|
1594
1881
|
|
|
1595
1882
|
let pos = null;
|
|
1596
1883
|
// remove this message from queue
|
|
1597
|
-
client._messages &&
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1884
|
+
client._messages &&
|
|
1885
|
+
client._messages.forEach((e, i) => {
|
|
1886
|
+
if (e.messageId === packet.messageId) {
|
|
1887
|
+
pos = i;
|
|
1888
|
+
return false;
|
|
1889
|
+
}
|
|
1890
|
+
});
|
|
1603
1891
|
if (pos !== null) {
|
|
1604
|
-
client.pubrel({messageId: packet.messageId});
|
|
1892
|
+
client.pubrel({ messageId: packet.messageId });
|
|
1605
1893
|
} else {
|
|
1606
|
-
adapter.log.warn(
|
|
1894
|
+
adapter.log.warn(
|
|
1895
|
+
`Client [${client.id}] received pubrec on ${client.id} for unknown message ID: ${packet.messageId}`,
|
|
1896
|
+
);
|
|
1607
1897
|
}
|
|
1608
1898
|
});
|
|
1609
1899
|
|
|
1610
1900
|
// response for QoS2
|
|
1611
1901
|
client.on('pubcomp', packet => {
|
|
1612
1902
|
if (clients[client.id] && client.__secret !== clients[client.id].__secret) {
|
|
1613
|
-
!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
|
+
);
|
|
1614
1907
|
return;
|
|
1615
1908
|
}
|
|
1616
1909
|
|
|
@@ -1620,16 +1913,19 @@ function MQTTServer(adapter) {
|
|
|
1620
1913
|
|
|
1621
1914
|
let pos = null;
|
|
1622
1915
|
// remove this message from queue
|
|
1623
|
-
client._messages &&
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1916
|
+
client._messages &&
|
|
1917
|
+
client._messages.forEach((e, i) => {
|
|
1918
|
+
if (e.messageId === packet.messageId) {
|
|
1919
|
+
pos = i;
|
|
1920
|
+
return false;
|
|
1921
|
+
}
|
|
1922
|
+
});
|
|
1629
1923
|
if (pos !== null) {
|
|
1630
1924
|
client._messages.splice(pos, 1);
|
|
1631
1925
|
} else {
|
|
1632
|
-
adapter.log.warn(
|
|
1926
|
+
adapter.log.warn(
|
|
1927
|
+
`Client [${client.id}] received pubcomp for unknown message ID: ${packet.messageId}`,
|
|
1928
|
+
);
|
|
1633
1929
|
}
|
|
1634
1930
|
});
|
|
1635
1931
|
|
|
@@ -1639,7 +1935,10 @@ function MQTTServer(adapter) {
|
|
|
1639
1935
|
return;
|
|
1640
1936
|
}
|
|
1641
1937
|
if (clients[client.id] && client.__secret !== clients[client.id].__secret) {
|
|
1642
|
-
!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
|
+
);
|
|
1643
1942
|
return;
|
|
1644
1943
|
}
|
|
1645
1944
|
|
|
@@ -1649,27 +1948,33 @@ function MQTTServer(adapter) {
|
|
|
1649
1948
|
|
|
1650
1949
|
let pos = null;
|
|
1651
1950
|
// remove this message from queue
|
|
1652
|
-
client._messages &&
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1951
|
+
client._messages &&
|
|
1952
|
+
client._messages.forEach((e, i) => {
|
|
1953
|
+
if (e.messageId === packet.messageId) {
|
|
1954
|
+
pos = i;
|
|
1955
|
+
return false;
|
|
1956
|
+
}
|
|
1957
|
+
});
|
|
1658
1958
|
|
|
1659
1959
|
if (pos !== null) {
|
|
1660
|
-
client.pubcomp({messageId: packet.messageId});
|
|
1960
|
+
client.pubcomp({ messageId: packet.messageId });
|
|
1661
1961
|
|
|
1662
1962
|
receivedTopic(client._messages[pos], client);
|
|
1663
1963
|
client._messages && client._messages.splice(pos, 1);
|
|
1664
1964
|
} else {
|
|
1665
|
-
adapter.log.warn(
|
|
1965
|
+
adapter.log.warn(
|
|
1966
|
+
`Client [${client.id}] received pubrel on ${client.id} for unknown messageId ${packet.messageId}`,
|
|
1967
|
+
);
|
|
1666
1968
|
}
|
|
1667
1969
|
});
|
|
1668
1970
|
|
|
1669
1971
|
// response for QoS1
|
|
1670
1972
|
client.on('puback', packet => {
|
|
1671
1973
|
if (clients[client.id] && client.__secret !== clients[client.id].__secret) {
|
|
1672
|
-
!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
|
+
);
|
|
1673
1978
|
return;
|
|
1674
1979
|
}
|
|
1675
1980
|
|
|
@@ -1680,24 +1985,32 @@ function MQTTServer(adapter) {
|
|
|
1680
1985
|
// remove this message from queue
|
|
1681
1986
|
let pos = null;
|
|
1682
1987
|
// remove this message from queue
|
|
1683
|
-
client._messages &&
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1988
|
+
client._messages &&
|
|
1989
|
+
client._messages.forEach((e, i) => {
|
|
1990
|
+
if (e.messageId === packet.messageId) {
|
|
1991
|
+
pos = i;
|
|
1992
|
+
return false;
|
|
1993
|
+
}
|
|
1994
|
+
});
|
|
1689
1995
|
|
|
1690
1996
|
if (pos !== null) {
|
|
1691
|
-
adapter.log.debug(
|
|
1997
|
+
adapter.log.debug(
|
|
1998
|
+
`Client [${client.id}] received puback for ${client.id} message ID: ${packet.messageId}`,
|
|
1999
|
+
);
|
|
1692
2000
|
client._messages && client._messages.splice(pos, 1);
|
|
1693
2001
|
} else {
|
|
1694
|
-
adapter.log.warn(
|
|
2002
|
+
adapter.log.warn(
|
|
2003
|
+
`Client [${client.id}] received puback for unknown message ID: ${packet.messageId}`,
|
|
2004
|
+
);
|
|
1695
2005
|
}
|
|
1696
2006
|
});
|
|
1697
2007
|
|
|
1698
2008
|
client.on('unsubscribe', packet => {
|
|
1699
2009
|
if (clients[client.id] && client.__secret !== clients[client.id].__secret) {
|
|
1700
|
-
!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
|
+
);
|
|
1701
2014
|
return;
|
|
1702
2015
|
}
|
|
1703
2016
|
|
|
@@ -1705,12 +2018,15 @@ function MQTTServer(adapter) {
|
|
|
1705
2018
|
persistentSessions[client.id].lastSeen = Date.now();
|
|
1706
2019
|
}
|
|
1707
2020
|
|
|
1708
|
-
client.unsuback({messageId: packet.messageId});
|
|
2021
|
+
client.unsuback({ messageId: packet.messageId });
|
|
1709
2022
|
});
|
|
1710
2023
|
|
|
1711
2024
|
client.on('subscribe', packet => {
|
|
1712
2025
|
if (clients[client.id] && client.__secret !== clients[client.id].__secret) {
|
|
1713
|
-
!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
|
+
);
|
|
1714
2030
|
return;
|
|
1715
2031
|
}
|
|
1716
2032
|
|
|
@@ -1722,12 +2038,15 @@ function MQTTServer(adapter) {
|
|
|
1722
2038
|
// we expect subscribe for 'cmnd.sonoff.#'
|
|
1723
2039
|
let granted = packet.subscriptions.map(subs => subs.qos);
|
|
1724
2040
|
|
|
1725
|
-
client.suback({granted: granted, messageId: packet.messageId});
|
|
2041
|
+
client.suback({ granted: granted, messageId: packet.messageId });
|
|
1726
2042
|
});
|
|
1727
2043
|
|
|
1728
2044
|
client.on('pingreq', (/*packet*/) => {
|
|
1729
2045
|
if (clients[client.id] && client.__secret !== clients[client.id].__secret) {
|
|
1730
|
-
!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
|
+
);
|
|
1731
2050
|
return;
|
|
1732
2051
|
}
|
|
1733
2052
|
|
|
@@ -1740,15 +2059,15 @@ function MQTTServer(adapter) {
|
|
|
1740
2059
|
});
|
|
1741
2060
|
});
|
|
1742
2061
|
|
|
1743
|
-
server.on('error', err =>
|
|
1744
|
-
adapter.log.error(`Can not start Server ${err}`));
|
|
2062
|
+
server.on('error', err => adapter.log.error(`Can not start Server ${err}`));
|
|
1745
2063
|
|
|
1746
2064
|
config.port = parseInt(config.port, 10) || 1883;
|
|
1747
2065
|
|
|
1748
2066
|
config.retransmitInterval = config.retransmitInterval || 2000;
|
|
1749
|
-
config.retransmitCount
|
|
1750
|
-
config.storeClientsTime
|
|
1751
|
-
|
|
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;
|
|
1752
2071
|
|
|
1753
2072
|
// Update connection state
|
|
1754
2073
|
updateClients();
|
|
@@ -1757,9 +2076,7 @@ function MQTTServer(adapter) {
|
|
|
1757
2076
|
server.listen(config.port, config.bind, () => {
|
|
1758
2077
|
adapter.log.info(`Starting MQTT ${config.user ? 'authenticated ' : ''} server on port ${config.port}`);
|
|
1759
2078
|
|
|
1760
|
-
resendTimer = setInterval(() =>
|
|
1761
|
-
!resending && checkResends(),
|
|
1762
|
-
adapter.config.retransmitInterval || 2000);
|
|
2079
|
+
resendTimer = setInterval(() => !resending && checkResends(), adapter.config.retransmitInterval || 2000);
|
|
1763
2080
|
});
|
|
1764
2081
|
})(adapter.config);
|
|
1765
2082
|
|