iobroker.zigbee2mqtt 3.0.10 → 3.0.13
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 +14 -245
- package/io-package.json +41 -42
- package/lib/check.js +6 -0
- package/lib/colors.js +26 -6
- package/lib/deviceController.js +69 -17
- package/lib/exposes.js +89 -103
- package/lib/imageController.js +52 -3
- package/lib/messages.js +10 -0
- package/lib/mqttServerController.js +21 -5
- package/lib/nonGenericDevicesExtension.js +6 -2
- package/lib/rgb.js +81 -27
- package/lib/statesController.js +62 -11
- package/lib/utils.js +54 -7
- package/lib/websocketController.js +29 -0
- package/lib/z2mController.js +19 -0
- package/main.js +8 -10
- package/package.json +2 -6
package/lib/rgb.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
2
|
/*
|
|
3
3
|
With these functions you can convert the CIE color space to the RGB color space and vice versa.
|
|
4
4
|
|
|
@@ -44,7 +44,11 @@ const colors = require('./colors.js');
|
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
46
|
* Converts CIE color space to RGB color space
|
|
47
|
-
*
|
|
47
|
+
*
|
|
48
|
+
* @param x
|
|
49
|
+
* @param y
|
|
50
|
+
* @param brightness
|
|
51
|
+
* @returns {Array} Array that contains the color values for red, green and blue
|
|
48
52
|
*/
|
|
49
53
|
function cie_to_rgb(x, y, brightness) {
|
|
50
54
|
//Set to maximum brightness if no custom value was given (Not the slick ECMAScript 6 way for compatibility reasons)
|
|
@@ -54,17 +58,17 @@ function cie_to_rgb(x, y, brightness) {
|
|
|
54
58
|
|
|
55
59
|
const z = 1.0 - x - y;
|
|
56
60
|
const Y = (brightness / 254).toFixed(2);
|
|
57
|
-
|
|
61
|
+
|
|
58
62
|
const X = (Y / y) * x;
|
|
59
|
-
|
|
63
|
+
|
|
60
64
|
const Z = (Y / y) * z;
|
|
61
65
|
|
|
62
66
|
//Convert to RGB using Wide RGB D65 conversion
|
|
63
|
-
|
|
67
|
+
|
|
64
68
|
let red = X * 1.656492 - Y * 0.354851 - Z * 0.255038;
|
|
65
|
-
|
|
69
|
+
|
|
66
70
|
let green = -X * 0.707196 + Y * 1.655397 + Z * 0.036152;
|
|
67
|
-
|
|
71
|
+
|
|
68
72
|
let blue = X * 0.051713 - Y * 0.121364 + Z * 1.01153;
|
|
69
73
|
|
|
70
74
|
//If red, green or blue is larger than 1.0 set it back to the maximum of 1.0
|
|
@@ -109,10 +113,11 @@ function cie_to_rgb(x, y, brightness) {
|
|
|
109
113
|
|
|
110
114
|
/**
|
|
111
115
|
* Converts RGB color space to CIE color space
|
|
112
|
-
*
|
|
113
|
-
* @param {
|
|
114
|
-
* @param {
|
|
115
|
-
* @
|
|
116
|
+
*
|
|
117
|
+
* @param {number} red
|
|
118
|
+
* @param {number} green
|
|
119
|
+
* @param {number} blue
|
|
120
|
+
* @returns {Array} Array that contains the CIE color values for x and y
|
|
116
121
|
*/
|
|
117
122
|
function rgb_to_cie(red, green, blue) {
|
|
118
123
|
// Apply a gamma correction to the RGB values, which makes the color more vivid and more the like the color displayed on the screen of your device
|
|
@@ -129,21 +134,27 @@ function rgb_to_cie(red, green, blue) {
|
|
|
129
134
|
let x = (X / (X + Y + Z)).toFixed(4);
|
|
130
135
|
let y = (Y / (X + Y + Z)).toFixed(4);
|
|
131
136
|
|
|
132
|
-
|
|
137
|
+
|
|
133
138
|
if (isNaN(x)) {
|
|
134
|
-
|
|
139
|
+
|
|
135
140
|
x = 0;
|
|
136
141
|
}
|
|
137
142
|
|
|
138
|
-
|
|
143
|
+
|
|
139
144
|
if (isNaN(y)) {
|
|
140
|
-
|
|
145
|
+
|
|
141
146
|
y = 0;
|
|
142
147
|
}
|
|
143
148
|
|
|
144
149
|
return [x, y];
|
|
145
150
|
}
|
|
146
151
|
|
|
152
|
+
/**
|
|
153
|
+
*
|
|
154
|
+
* @param h
|
|
155
|
+
* @param s
|
|
156
|
+
* @param v
|
|
157
|
+
*/
|
|
147
158
|
function hsvToRGB(h, s, v) {
|
|
148
159
|
h = (h % 360) / 360;
|
|
149
160
|
s = s / 100;
|
|
@@ -187,6 +198,13 @@ function hsvToRGB(h, s, v) {
|
|
|
187
198
|
};
|
|
188
199
|
}
|
|
189
200
|
|
|
201
|
+
/**
|
|
202
|
+
*
|
|
203
|
+
* @param r
|
|
204
|
+
* @param g
|
|
205
|
+
* @param b
|
|
206
|
+
* @param numeric
|
|
207
|
+
*/
|
|
190
208
|
function rgbToHSV(r, g, b, numeric) {
|
|
191
209
|
if (arguments.length === 1) {
|
|
192
210
|
(g = r.g), (b = r.b), (r = r.r);
|
|
@@ -215,21 +233,26 @@ function rgbToHSV(r, g, b, numeric) {
|
|
|
215
233
|
h /= 6 * d;
|
|
216
234
|
break;
|
|
217
235
|
}
|
|
218
|
-
if (numeric)
|
|
219
|
-
|
|
220
|
-
|
|
236
|
+
if (numeric) {
|
|
237
|
+
return {
|
|
238
|
+
|
|
221
239
|
h: Math.round(h * 360),
|
|
222
240
|
s: Math.round(s * 100),
|
|
223
241
|
v: Math.round(v * 100),
|
|
224
242
|
};
|
|
243
|
+
}
|
|
225
244
|
return {
|
|
226
|
-
|
|
245
|
+
|
|
227
246
|
h: (h * 360).toFixed(3),
|
|
228
247
|
s: (s * 100).toFixed(3),
|
|
229
248
|
v: (v * 100).toFixed(3),
|
|
230
249
|
};
|
|
231
250
|
}
|
|
232
251
|
|
|
252
|
+
/**
|
|
253
|
+
*
|
|
254
|
+
* @param value
|
|
255
|
+
*/
|
|
233
256
|
function colorArrayFromString(value) {
|
|
234
257
|
if (typeof value === 'string') {
|
|
235
258
|
const rv = [];
|
|
@@ -241,6 +264,10 @@ function colorArrayFromString(value) {
|
|
|
241
264
|
return [{ r: 0, g: 128, b: 255 }];
|
|
242
265
|
}
|
|
243
266
|
|
|
267
|
+
/**
|
|
268
|
+
*
|
|
269
|
+
* @param payload
|
|
270
|
+
*/
|
|
244
271
|
function colorStringFromRGBArray(payload) {
|
|
245
272
|
const rv = [];
|
|
246
273
|
payload.forEach((element) => {
|
|
@@ -249,6 +276,12 @@ function colorStringFromRGBArray(payload) {
|
|
|
249
276
|
return rv.toString();
|
|
250
277
|
}
|
|
251
278
|
|
|
279
|
+
/**
|
|
280
|
+
*
|
|
281
|
+
* @param h
|
|
282
|
+
* @param s
|
|
283
|
+
* @param v
|
|
284
|
+
*/
|
|
252
285
|
function hsv_to_cie(h, s, v) {
|
|
253
286
|
const rgb = hsvToRGB(h, s, v);
|
|
254
287
|
return rgb_to_cie(rgb.r, rgb.g, rgb.b);
|
|
@@ -256,20 +289,35 @@ function hsv_to_cie(h, s, v) {
|
|
|
256
289
|
|
|
257
290
|
function rgb_to_rgbstring(element) {
|
|
258
291
|
let col = '#';
|
|
259
|
-
if (element && element.hasOwnProperty('r'))
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
292
|
+
if (element && element.hasOwnProperty('r')) {
|
|
293
|
+
col = col + element.r.toString(16).padStart(2, '0');
|
|
294
|
+
} else {
|
|
295
|
+
col = `${col }00`;
|
|
296
|
+
}
|
|
297
|
+
if (element && element.hasOwnProperty('g')) {
|
|
298
|
+
col = col + element.g.toString(16).padStart(2, '0');
|
|
299
|
+
} else {
|
|
300
|
+
col = `${col }00`;
|
|
301
|
+
}
|
|
302
|
+
if (element && element.hasOwnProperty('b')) {
|
|
303
|
+
col = col + element.b.toString(16).padStart(2, '0');
|
|
304
|
+
} else {
|
|
305
|
+
col = `${col }00`;
|
|
306
|
+
}
|
|
265
307
|
return col;
|
|
266
308
|
}
|
|
267
309
|
|
|
310
|
+
/**
|
|
311
|
+
*
|
|
312
|
+
* @param h
|
|
313
|
+
* @param s
|
|
314
|
+
* @param b
|
|
315
|
+
*/
|
|
268
316
|
function hsbToRGB(h, s, b) {
|
|
269
317
|
const br = Math.round(b * 2.55);
|
|
270
318
|
if (s == 0) {
|
|
271
319
|
return [br, br, br];
|
|
272
|
-
}
|
|
320
|
+
}
|
|
273
321
|
const hue = h % 360;
|
|
274
322
|
const f = hue % 60;
|
|
275
323
|
const p = Math.round(b * (100 - s) * 0.0255);
|
|
@@ -289,10 +337,16 @@ function hsbToRGB(h, s, b) {
|
|
|
289
337
|
case 5:
|
|
290
338
|
return [br, p, q];
|
|
291
339
|
}
|
|
292
|
-
|
|
340
|
+
|
|
293
341
|
return false;
|
|
294
342
|
}
|
|
295
343
|
|
|
344
|
+
/**
|
|
345
|
+
*
|
|
346
|
+
* @param h
|
|
347
|
+
* @param s
|
|
348
|
+
* @param v
|
|
349
|
+
*/
|
|
296
350
|
function hsvToRGBString(h, s, v) {
|
|
297
351
|
return rgb_to_rgbstring(hsvToRGB(h, s, v));
|
|
298
352
|
}
|
package/lib/statesController.js
CHANGED
|
@@ -2,7 +2,18 @@ const utils = require('./utils');
|
|
|
2
2
|
const incStatsQueue = [];
|
|
3
3
|
const timeOutCache = {};
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
5
8
|
class StatesController {
|
|
9
|
+
/**
|
|
10
|
+
*
|
|
11
|
+
* @param adapter
|
|
12
|
+
* @param deviceCache
|
|
13
|
+
* @param groupCache
|
|
14
|
+
* @param logCustomizations
|
|
15
|
+
* @param createCache
|
|
16
|
+
*/
|
|
6
17
|
constructor(adapter, deviceCache, groupCache, logCustomizations, createCache) {
|
|
7
18
|
this.adapter = adapter;
|
|
8
19
|
this.groupCache = groupCache;
|
|
@@ -11,6 +22,10 @@ class StatesController {
|
|
|
11
22
|
this.createCache = createCache;
|
|
12
23
|
}
|
|
13
24
|
|
|
25
|
+
/**
|
|
26
|
+
*
|
|
27
|
+
* @param messageObj
|
|
28
|
+
*/
|
|
14
29
|
processDeviceMessage(messageObj) {
|
|
15
30
|
// Is payload present?
|
|
16
31
|
if (messageObj.payload == '') {
|
|
@@ -30,6 +45,11 @@ class StatesController {
|
|
|
30
45
|
}
|
|
31
46
|
}
|
|
32
47
|
|
|
48
|
+
/**
|
|
49
|
+
*
|
|
50
|
+
* @param messageObj
|
|
51
|
+
* @param device
|
|
52
|
+
*/
|
|
33
53
|
async setDeviceStateSafely(messageObj, device) {
|
|
34
54
|
if (this.logCustomizations.debugDevices.includes(device.ieee_address)) {
|
|
35
55
|
this.adapter.log.warn(`--->>> fromZ2M -> ${device.ieee_address} states: ${JSON.stringify(messageObj)}`);
|
|
@@ -103,19 +123,15 @@ class StatesController {
|
|
|
103
123
|
// Is an action
|
|
104
124
|
if (state.prop && state.prop == 'action') {
|
|
105
125
|
actionStates.push(state);
|
|
106
|
-
}
|
|
107
|
-
|
|
126
|
+
} else if (
|
|
127
|
+
// Is not an action
|
|
108
128
|
// check if its a motion sensor (occupancy state) and if configuration is set to update state every time
|
|
109
129
|
// if yes, use setStateSafelyAsync instead of setStateChangedSafelyAsync
|
|
110
|
-
|
|
111
|
-
this.adapter.config.allwaysUpdateOccupancyState === true &&
|
|
112
|
-
state.id === 'occupancy' &&
|
|
113
|
-
value === true
|
|
130
|
+
this.adapter.config.allwaysUpdateOccupancyState === true && state.id === 'occupancy' && value === true
|
|
114
131
|
) {
|
|
115
132
|
await this.setStateSafelyAsync(stateName, value);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
else {
|
|
133
|
+
} else {
|
|
134
|
+
// end section for motion sensor update
|
|
119
135
|
if (state.getter) {
|
|
120
136
|
await this.setStateChangedSafelyAsync(stateName, state.getter(messageObj.payload));
|
|
121
137
|
} else {
|
|
@@ -134,11 +150,17 @@ class StatesController {
|
|
|
134
150
|
|
|
135
151
|
try {
|
|
136
152
|
if (state.isEvent && state.isEvent == true) {
|
|
153
|
+
const gettr = state.getter(messageObj.payload);
|
|
137
154
|
if (state.type == 'boolean') {
|
|
138
|
-
await this.setStateWithTimeoutAsync(stateName,
|
|
155
|
+
await this.setStateWithTimeoutAsync(stateName, gettr, 450);
|
|
139
156
|
} else {
|
|
140
|
-
await this.setStateSafelyAsync(stateName,
|
|
157
|
+
await this.setStateSafelyAsync(stateName, gettr);
|
|
141
158
|
}
|
|
159
|
+
// publish the action into action dp
|
|
160
|
+
if (state.prop && state.prop == 'action') {
|
|
161
|
+
await this.setStateSafelyAsync(`${device.ieee_address}.action`, messageObj.payload.action);
|
|
162
|
+
}
|
|
163
|
+
|
|
142
164
|
} else {
|
|
143
165
|
await this.setStateChangedSafelyAsync(stateName, state.getter(messageObj.payload));
|
|
144
166
|
}
|
|
@@ -149,6 +171,11 @@ class StatesController {
|
|
|
149
171
|
}
|
|
150
172
|
}
|
|
151
173
|
|
|
174
|
+
/**
|
|
175
|
+
*
|
|
176
|
+
* @param stateName
|
|
177
|
+
* @param value
|
|
178
|
+
*/
|
|
152
179
|
async setStateSafelyAsync(stateName, value) {
|
|
153
180
|
if (value === undefined || value === null) {
|
|
154
181
|
return;
|
|
@@ -156,6 +183,11 @@ class StatesController {
|
|
|
156
183
|
await this.adapter.setStateAsync(stateName, value, true);
|
|
157
184
|
}
|
|
158
185
|
|
|
186
|
+
/**
|
|
187
|
+
*
|
|
188
|
+
* @param stateName
|
|
189
|
+
* @param value
|
|
190
|
+
*/
|
|
159
191
|
async setStateChangedSafelyAsync(stateName, value) {
|
|
160
192
|
if (value === undefined || value === null) {
|
|
161
193
|
return;
|
|
@@ -163,6 +195,12 @@ class StatesController {
|
|
|
163
195
|
await this.adapter.setStateChangedAsync(stateName, value, true);
|
|
164
196
|
}
|
|
165
197
|
|
|
198
|
+
/**
|
|
199
|
+
*
|
|
200
|
+
* @param stateName
|
|
201
|
+
* @param value
|
|
202
|
+
* @param timeout
|
|
203
|
+
*/
|
|
166
204
|
async setStateWithTimeoutAsync(stateName, value, timeout) {
|
|
167
205
|
if (value === undefined || value === null) {
|
|
168
206
|
return;
|
|
@@ -174,9 +212,13 @@ class StatesController {
|
|
|
174
212
|
}
|
|
175
213
|
timeOutCache[stateName] = setTimeout(() => {
|
|
176
214
|
this.adapter.setStateAsync(stateName, !value, true);
|
|
215
|
+
|
|
177
216
|
}, timeout);
|
|
178
217
|
}
|
|
179
218
|
|
|
219
|
+
/**
|
|
220
|
+
*
|
|
221
|
+
*/
|
|
180
222
|
processQueue() {
|
|
181
223
|
const oldIncStatsQueue = [];
|
|
182
224
|
utils.moveArray(incStatsQueue, oldIncStatsQueue);
|
|
@@ -185,6 +227,9 @@ class StatesController {
|
|
|
185
227
|
}
|
|
186
228
|
}
|
|
187
229
|
|
|
230
|
+
/**
|
|
231
|
+
*
|
|
232
|
+
*/
|
|
188
233
|
async subscribeWritableStates() {
|
|
189
234
|
await this.adapter.unsubscribeObjectsAsync('*');
|
|
190
235
|
for (const device of this.groupCache.concat(this.deviceCache)) {
|
|
@@ -199,6 +244,9 @@ class StatesController {
|
|
|
199
244
|
this.adapter.subscribeStates('info.coordinator_check');
|
|
200
245
|
}
|
|
201
246
|
|
|
247
|
+
/**
|
|
248
|
+
*
|
|
249
|
+
*/
|
|
202
250
|
async setAllAvailableToFalse() {
|
|
203
251
|
const availableStates = await this.adapter.getStatesAsync('*.available');
|
|
204
252
|
for (const availableState in availableStates) {
|
|
@@ -206,6 +254,9 @@ class StatesController {
|
|
|
206
254
|
}
|
|
207
255
|
}
|
|
208
256
|
|
|
257
|
+
/**
|
|
258
|
+
*
|
|
259
|
+
*/
|
|
209
260
|
async allTimerClear() {
|
|
210
261
|
for (const timer in timeOutCache) {
|
|
211
262
|
clearTimeout(timeOutCache[timer]);
|
package/lib/utils.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Converts a bulb level of range [0...254] to an adapter level of range [0...100]
|
|
3
|
+
*
|
|
4
|
+
* @param bulbLevel
|
|
3
5
|
*/
|
|
4
6
|
function bulbLevelToAdapterLevel(bulbLevel) {
|
|
5
7
|
// Convert from bulb levels [0...254] to adapter levels [0...100]:
|
|
@@ -19,14 +21,16 @@ function bulbLevelToAdapterLevel(bulbLevel) {
|
|
|
19
21
|
if (bulbLevel >= 2) {
|
|
20
22
|
// Perform linear mapping of range [2...254] to [1...100]
|
|
21
23
|
return Math.round(((bulbLevel - 2) * 99) / 252) + 1;
|
|
22
|
-
}
|
|
24
|
+
}
|
|
23
25
|
// The bulb is considered off. Even a bulb level of "1" is considered as off.
|
|
24
26
|
return 0;
|
|
25
|
-
|
|
27
|
+
// else
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
/**
|
|
29
31
|
* Converts an adapter level of range [0...100] to a bulb level of range [0...254]
|
|
32
|
+
*
|
|
33
|
+
* @param adapterLevel
|
|
30
34
|
*/
|
|
31
35
|
function adapterLevelToBulbLevel(adapterLevel) {
|
|
32
36
|
// Convert from adapter levels [0...100] to bulb levels [0...254].
|
|
@@ -35,13 +39,17 @@ function adapterLevelToBulbLevel(adapterLevel) {
|
|
|
35
39
|
if (adapterLevel) {
|
|
36
40
|
// Perform linear mapping of range [1...100] to [2...254]
|
|
37
41
|
return Math.round(((adapterLevel - 1) * 252) / 99) + 2;
|
|
38
|
-
}
|
|
42
|
+
}
|
|
39
43
|
// Switch the bulb off. Some bulbs need "0" (IKEA), others "1" (HUE), and according to the
|
|
40
44
|
// ZigBee docs "1" is the "minimum possible level"... we choose "0" here which seems to work.
|
|
41
45
|
return 0;
|
|
42
|
-
|
|
46
|
+
// else
|
|
43
47
|
}
|
|
44
48
|
|
|
49
|
+
/**
|
|
50
|
+
*
|
|
51
|
+
* @param ba
|
|
52
|
+
*/
|
|
45
53
|
function bytesArrayToWordArray(ba) {
|
|
46
54
|
const wa = [];
|
|
47
55
|
for (let i = 0; i < ba.length; i++) {
|
|
@@ -52,6 +60,10 @@ function bytesArrayToWordArray(ba) {
|
|
|
52
60
|
|
|
53
61
|
// If the value is greater than 1000, kelvin is assumed.
|
|
54
62
|
// If smaller, it is assumed to be mired.
|
|
63
|
+
/**
|
|
64
|
+
*
|
|
65
|
+
* @param t
|
|
66
|
+
*/
|
|
55
67
|
function toMired(t) {
|
|
56
68
|
let miredValue = t;
|
|
57
69
|
if (t > 1000) {
|
|
@@ -60,12 +72,19 @@ function toMired(t) {
|
|
|
60
72
|
return miredValue;
|
|
61
73
|
}
|
|
62
74
|
|
|
75
|
+
/**
|
|
76
|
+
*
|
|
77
|
+
* @param t
|
|
78
|
+
*/
|
|
63
79
|
function miredKelvinConversion(t) {
|
|
64
80
|
return Math.round(1000000 / t);
|
|
65
81
|
}
|
|
66
82
|
|
|
67
83
|
/**
|
|
68
84
|
* Converts a decimal number to a hex string with zero-padding
|
|
85
|
+
*
|
|
86
|
+
* @param decimal
|
|
87
|
+
* @param padding
|
|
69
88
|
*/
|
|
70
89
|
function decimalToHex(decimal, padding) {
|
|
71
90
|
let hex = Number(decimal).toString(16);
|
|
@@ -78,32 +97,60 @@ function decimalToHex(decimal, padding) {
|
|
|
78
97
|
return hex;
|
|
79
98
|
}
|
|
80
99
|
|
|
100
|
+
/**
|
|
101
|
+
*
|
|
102
|
+
* @param adapterDevId
|
|
103
|
+
*/
|
|
81
104
|
function getZbId(adapterDevId) {
|
|
82
105
|
const idx = adapterDevId.indexOf('group');
|
|
83
|
-
if (idx > 0)
|
|
84
|
-
|
|
106
|
+
if (idx > 0) {
|
|
107
|
+
return adapterDevId.substr(idx + 6);
|
|
108
|
+
}
|
|
109
|
+
return `0x${ adapterDevId.split('.')[2]}`;
|
|
85
110
|
}
|
|
86
111
|
|
|
112
|
+
/**
|
|
113
|
+
*
|
|
114
|
+
* @param adapter
|
|
115
|
+
* @param id
|
|
116
|
+
*/
|
|
87
117
|
function getAdId(adapter, id) {
|
|
88
|
-
return adapter.namespace
|
|
118
|
+
return `${adapter.namespace }.${ id.split('.')[2]}`; // iobroker device id
|
|
89
119
|
}
|
|
90
120
|
|
|
121
|
+
/**
|
|
122
|
+
*
|
|
123
|
+
* @param array
|
|
124
|
+
*/
|
|
91
125
|
function clearArray(array) {
|
|
92
126
|
while (array.length > 0) {
|
|
93
127
|
array.pop();
|
|
94
128
|
}
|
|
95
129
|
}
|
|
96
130
|
|
|
131
|
+
/**
|
|
132
|
+
*
|
|
133
|
+
* @param source
|
|
134
|
+
* @param target
|
|
135
|
+
*/
|
|
97
136
|
function moveArray(source, target) {
|
|
98
137
|
while (source.length > 0) {
|
|
99
138
|
target.push(source.shift());
|
|
100
139
|
}
|
|
101
140
|
}
|
|
102
141
|
|
|
142
|
+
/**
|
|
143
|
+
*
|
|
144
|
+
* @param item
|
|
145
|
+
*/
|
|
103
146
|
function isObject(item) {
|
|
104
147
|
return typeof item === 'object' && !Array.isArray(item) && item !== null;
|
|
105
148
|
}
|
|
106
149
|
|
|
150
|
+
/**
|
|
151
|
+
*
|
|
152
|
+
* @param item
|
|
153
|
+
*/
|
|
107
154
|
function isJson(item) {
|
|
108
155
|
let value = typeof item !== 'string' ? JSON.stringify(item) : item;
|
|
109
156
|
try {
|
|
@@ -6,11 +6,21 @@ let ping;
|
|
|
6
6
|
let pingTimeout;
|
|
7
7
|
let autoRestartTimeout;
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
*
|
|
11
|
+
*/
|
|
9
12
|
class WebsocketController {
|
|
13
|
+
/**
|
|
14
|
+
*
|
|
15
|
+
* @param adapter
|
|
16
|
+
*/
|
|
10
17
|
constructor(adapter) {
|
|
11
18
|
this.adapter = adapter;
|
|
12
19
|
}
|
|
13
20
|
|
|
21
|
+
/**
|
|
22
|
+
*
|
|
23
|
+
*/
|
|
14
24
|
initWsClient() {
|
|
15
25
|
try {
|
|
16
26
|
let wsURL = `${this.adapter.config.wsScheme}://${this.adapter.config.wsServerIP}:${this.adapter.config.wsServerPort}/api`;
|
|
@@ -53,6 +63,10 @@ class WebsocketController {
|
|
|
53
63
|
}
|
|
54
64
|
}
|
|
55
65
|
|
|
66
|
+
/**
|
|
67
|
+
*
|
|
68
|
+
* @param message
|
|
69
|
+
*/
|
|
56
70
|
send(message) {
|
|
57
71
|
if (wsClient.readyState !== WebSocket.OPEN) {
|
|
58
72
|
this.adapter.log.warn('Cannot set State, no websocket connection to Zigbee2MQTT!');
|
|
@@ -61,6 +75,9 @@ class WebsocketController {
|
|
|
61
75
|
wsClient.send(message);
|
|
62
76
|
}
|
|
63
77
|
|
|
78
|
+
/**
|
|
79
|
+
*
|
|
80
|
+
*/
|
|
64
81
|
sendPingToServer() {
|
|
65
82
|
//this.logDebug('Send ping to server');
|
|
66
83
|
wsClient.ping();
|
|
@@ -69,6 +86,9 @@ class WebsocketController {
|
|
|
69
86
|
}, wsHeartbeatIntervall);
|
|
70
87
|
}
|
|
71
88
|
|
|
89
|
+
/**
|
|
90
|
+
*
|
|
91
|
+
*/
|
|
72
92
|
wsHeartbeat() {
|
|
73
93
|
clearTimeout(pingTimeout);
|
|
74
94
|
pingTimeout = setTimeout(() => {
|
|
@@ -77,6 +97,9 @@ class WebsocketController {
|
|
|
77
97
|
}, wsHeartbeatIntervall + 3000);
|
|
78
98
|
}
|
|
79
99
|
|
|
100
|
+
/**
|
|
101
|
+
*
|
|
102
|
+
*/
|
|
80
103
|
async autoRestart() {
|
|
81
104
|
this.adapter.log.warn(`Start try again in ${restartTimeout / 1000} seconds...`);
|
|
82
105
|
autoRestartTimeout = setTimeout(() => {
|
|
@@ -84,12 +107,18 @@ class WebsocketController {
|
|
|
84
107
|
}, restartTimeout);
|
|
85
108
|
}
|
|
86
109
|
|
|
110
|
+
/**
|
|
111
|
+
*
|
|
112
|
+
*/
|
|
87
113
|
closeConnection() {
|
|
88
114
|
if (wsClient && wsClient.readyState !== WebSocket.CLOSED) {
|
|
89
115
|
wsClient.close();
|
|
90
116
|
}
|
|
91
117
|
}
|
|
92
118
|
|
|
119
|
+
/**
|
|
120
|
+
*
|
|
121
|
+
*/
|
|
93
122
|
async allTimerClear() {
|
|
94
123
|
clearTimeout(pingTimeout);
|
|
95
124
|
clearTimeout(ping);
|
package/lib/z2mController.js
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
*/
|
|
1
4
|
class Z2mController {
|
|
5
|
+
/**
|
|
6
|
+
*
|
|
7
|
+
* @param adapter
|
|
8
|
+
* @param deviceCache
|
|
9
|
+
* @param groupCache
|
|
10
|
+
* @param logCustomizations
|
|
11
|
+
*/
|
|
2
12
|
constructor(adapter, deviceCache, groupCache, logCustomizations) {
|
|
3
13
|
this.adapter = adapter;
|
|
4
14
|
this.groupCache = groupCache;
|
|
@@ -6,6 +16,11 @@ class Z2mController {
|
|
|
6
16
|
this.logCustomizations = logCustomizations;
|
|
7
17
|
}
|
|
8
18
|
|
|
19
|
+
/**
|
|
20
|
+
*
|
|
21
|
+
* @param id
|
|
22
|
+
* @param state
|
|
23
|
+
*/
|
|
9
24
|
async createZ2MMessage(id, state) {
|
|
10
25
|
const splitedID = id.split('.');
|
|
11
26
|
if (splitedID.length < 4) {
|
|
@@ -114,6 +129,10 @@ class Z2mController {
|
|
|
114
129
|
return controlObj;
|
|
115
130
|
}
|
|
116
131
|
|
|
132
|
+
/**
|
|
133
|
+
*
|
|
134
|
+
* @param messageObj
|
|
135
|
+
*/
|
|
117
136
|
async proxyZ2MLogs(messageObj) {
|
|
118
137
|
const logMessage = messageObj.payload.message;
|
|
119
138
|
if (this.logCustomizations.logfilter.some((x) => logMessage.includes(x))) {
|
package/main.js
CHANGED
|
@@ -68,8 +68,7 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
const logfilterState = await this.getStateAsync('info.logfilter');
|
|
71
|
-
if (logfilterState && logfilterState.val) {
|
|
72
|
-
// @ts-ignore
|
|
71
|
+
if (logfilterState && logfilterState.val) {
|
|
73
72
|
logCustomizations.logfilter = String(logfilterState.val)
|
|
74
73
|
.split(';')
|
|
75
74
|
.filter((x) => x); // filter removes empty strings here
|
|
@@ -111,9 +110,8 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
111
110
|
`mqtt://${this.config.externalMqttServerIP}:${this.config.externalMqttServerPort}`,
|
|
112
111
|
mqttClientOptions
|
|
113
112
|
);
|
|
114
|
-
}
|
|
113
|
+
} else {
|
|
115
114
|
// Internal MQTT-Server
|
|
116
|
-
else {
|
|
117
115
|
mqttServerController = new MqttServerController(this);
|
|
118
116
|
await mqttServerController.createMQTTServer();
|
|
119
117
|
await this.delay(1500);
|
|
@@ -137,9 +135,8 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
137
135
|
const newMessage = `{"payload":${payload.toString() == '' ? '"null"' : payload.toString()},"topic":"${topic.slice(topic.search('/') + 1)}"}`;
|
|
138
136
|
this.messageParse(newMessage);
|
|
139
137
|
});
|
|
140
|
-
}
|
|
138
|
+
} else if (this.config.connectionType == 'ws') {
|
|
141
139
|
// Websocket
|
|
142
|
-
else if (this.config.connectionType == 'ws') {
|
|
143
140
|
if (this.config.wsServerIP == '') {
|
|
144
141
|
this.log.warn('Please configure the Websoket connection!');
|
|
145
142
|
return;
|
|
@@ -316,9 +313,8 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
316
313
|
} catch (e) {
|
|
317
314
|
this.log.error(e);
|
|
318
315
|
}
|
|
319
|
-
}
|
|
316
|
+
} else if (this.config.connectionType == 'ws') {
|
|
320
317
|
// Websocket
|
|
321
|
-
else if (this.config.connectionType == 'ws') {
|
|
322
318
|
try {
|
|
323
319
|
if (websocketController) {
|
|
324
320
|
websocketController.closeConnection();
|
|
@@ -352,6 +348,8 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
352
348
|
this.log.error(e);
|
|
353
349
|
}
|
|
354
350
|
|
|
351
|
+
this.setState('info.connection', false, true);
|
|
352
|
+
|
|
355
353
|
callback();
|
|
356
354
|
}
|
|
357
355
|
|
|
@@ -382,8 +380,8 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
382
380
|
if (require.main !== module) {
|
|
383
381
|
// Export the constructor in compact mode
|
|
384
382
|
/**
|
|
385
|
-
|
|
386
|
-
|
|
383
|
+
* @param {Partial<core.AdapterOptions>} [options]
|
|
384
|
+
*/
|
|
387
385
|
module.exports = (options) => new Zigbee2mqtt(options);
|
|
388
386
|
} else {
|
|
389
387
|
// otherwise start the instance directly
|