iobroker.zigbee2mqtt 0.2.0 → 2.1.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/README.md +35 -0
- package/admin/i18n/de/translations.json +22 -6
- package/admin/i18n/en/translations.json +20 -4
- package/admin/i18n/es/translations.json +20 -4
- package/admin/i18n/fr/translations.json +20 -4
- package/admin/i18n/it/translations.json +20 -4
- package/admin/i18n/nl/translations.json +20 -4
- package/admin/i18n/pl/translations.json +20 -4
- package/admin/i18n/pt/translations.json +20 -4
- package/admin/i18n/ru/translations.json +20 -4
- package/admin/i18n/zh-cn/translations.json +20 -4
- package/admin/jsonConfig.json +131 -9
- package/io-package.json +59 -7
- package/lib/check.js +36 -0
- package/lib/colors.js +5 -3
- package/lib/deviceController.js +234 -0
- package/lib/exposes.js +90 -45
- package/lib/messages.js +39 -0
- package/lib/mqttServerController.js +42 -0
- package/lib/rgb.js +42 -17
- package/lib/states.js +41 -19
- package/lib/statesController.js +138 -0
- package/lib/utils.js +19 -17
- package/lib/websocketController.js +84 -0
- package/lib/z2mController.js +80 -0
- package/main.js +130 -387
- package/package.json +11 -7
- package/lib/groups.js +0 -68
package/lib/rgb.js
CHANGED
|
@@ -73,14 +73,12 @@ function cie_to_rgb(x, y, brightness) {
|
|
|
73
73
|
green = green / red;
|
|
74
74
|
blue = blue / red;
|
|
75
75
|
red = 1.0;
|
|
76
|
-
}
|
|
77
|
-
else if (green > blue && green > red && green > 1.0) {
|
|
76
|
+
} else if (green > blue && green > red && green > 1.0) {
|
|
78
77
|
|
|
79
78
|
red = red / green;
|
|
80
79
|
blue = blue / green;
|
|
81
80
|
green = 1.0;
|
|
82
|
-
}
|
|
83
|
-
else if (blue > red && blue > green && blue > 1.0) {
|
|
81
|
+
} else if (blue > red && blue > green && blue > 1.0) {
|
|
84
82
|
|
|
85
83
|
red = red / blue;
|
|
86
84
|
green = green / blue;
|
|
@@ -157,7 +155,9 @@ function hsvToRGB(h, s, v) {
|
|
|
157
155
|
s = s / 100;
|
|
158
156
|
v = v / 100;
|
|
159
157
|
|
|
160
|
-
let r;
|
|
158
|
+
let r;
|
|
159
|
+
let g;
|
|
160
|
+
let b;
|
|
161
161
|
if (arguments.length === 1) {
|
|
162
162
|
s = h.s, v = h.v, h = h.h;
|
|
163
163
|
}
|
|
@@ -167,12 +167,24 @@ function hsvToRGB(h, s, v) {
|
|
|
167
167
|
const q = v * (1 - f * s);
|
|
168
168
|
const t = v * (1 - (1 - f) * s);
|
|
169
169
|
switch (i % 6) {
|
|
170
|
-
case 0:
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
case
|
|
174
|
-
|
|
175
|
-
|
|
170
|
+
case 0:
|
|
171
|
+
r = v, g = t, b = p;
|
|
172
|
+
break;
|
|
173
|
+
case 1:
|
|
174
|
+
r = q, g = v, b = p;
|
|
175
|
+
break;
|
|
176
|
+
case 2:
|
|
177
|
+
r = p, g = v, b = t;
|
|
178
|
+
break;
|
|
179
|
+
case 3:
|
|
180
|
+
r = p, g = q, b = v;
|
|
181
|
+
break;
|
|
182
|
+
case 4:
|
|
183
|
+
r = t, g = p, b = v;
|
|
184
|
+
break;
|
|
185
|
+
case 5:
|
|
186
|
+
r = v, g = p, b = q;
|
|
187
|
+
break;
|
|
176
188
|
}
|
|
177
189
|
return {
|
|
178
190
|
r: Math.round(r * 255),
|
|
@@ -185,17 +197,29 @@ function rgbToHSV(r, g, b, numeric) {
|
|
|
185
197
|
if (arguments.length === 1) {
|
|
186
198
|
g = r.g, b = r.b, r = r.r;
|
|
187
199
|
}
|
|
188
|
-
const max = Math.max(r, g, b);
|
|
200
|
+
const max = Math.max(r, g, b);
|
|
201
|
+
const min = Math.min(r, g, b);
|
|
189
202
|
const d = max - min;
|
|
190
203
|
let h;
|
|
191
204
|
const s = (max === 0 ? 0 : d / max);
|
|
192
205
|
const v = max / 255;
|
|
193
206
|
|
|
194
207
|
switch (max) {
|
|
195
|
-
case min:
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
case
|
|
208
|
+
case min:
|
|
209
|
+
h = 0;
|
|
210
|
+
break;
|
|
211
|
+
case r:
|
|
212
|
+
h = (g - b) + d * (g < b ? 6 : 0);
|
|
213
|
+
h /= 6 * d;
|
|
214
|
+
break;
|
|
215
|
+
case g:
|
|
216
|
+
h = (b - r) + d * 2;
|
|
217
|
+
h /= 6 * d;
|
|
218
|
+
break;
|
|
219
|
+
case b:
|
|
220
|
+
h = (r - g) + d * 4;
|
|
221
|
+
h /= 6 * d;
|
|
222
|
+
break;
|
|
199
223
|
}
|
|
200
224
|
if (numeric) return {
|
|
201
225
|
// @ts-ignore
|
|
@@ -210,6 +234,7 @@ function rgbToHSV(r, g, b, numeric) {
|
|
|
210
234
|
v: (v * 100).toFixed(3),
|
|
211
235
|
};
|
|
212
236
|
}
|
|
237
|
+
|
|
213
238
|
function colorArrayFromString(value) {
|
|
214
239
|
if (typeof (value) === 'string') {
|
|
215
240
|
const rv = [];
|
|
@@ -260,4 +285,4 @@ exports.hsvToRGB = hsvToRGB;
|
|
|
260
285
|
exports.rgbToHSV = rgbToHSV;
|
|
261
286
|
exports.colorArrayFromString = colorArrayFromString;
|
|
262
287
|
exports.colorStringFromRGBArray = colorStringFromRGBArray;
|
|
263
|
-
exports.hsvToRGBString = hsvToRGBString;
|
|
288
|
+
exports.hsvToRGBString = hsvToRGBString;
|
package/lib/states.js
CHANGED
|
@@ -274,17 +274,16 @@ const states = {
|
|
|
274
274
|
},
|
|
275
275
|
voltage: {
|
|
276
276
|
id: 'voltage',
|
|
277
|
-
name: '
|
|
277
|
+
name: 'Voltage',
|
|
278
278
|
icon: undefined,
|
|
279
|
-
role: '
|
|
279
|
+
role: 'value.voltage',
|
|
280
280
|
write: false,
|
|
281
281
|
read: true,
|
|
282
282
|
type: 'number',
|
|
283
283
|
unit: 'V',
|
|
284
|
-
getter: payload => payload.voltage / 1000,
|
|
285
284
|
def: 0,
|
|
286
285
|
},
|
|
287
|
-
|
|
286
|
+
battery_voltage: {
|
|
288
287
|
id: 'voltage',
|
|
289
288
|
name: 'Battery voltage',
|
|
290
289
|
icon: undefined,
|
|
@@ -293,9 +292,18 @@ const states = {
|
|
|
293
292
|
read: true,
|
|
294
293
|
type: 'number',
|
|
295
294
|
unit: 'V',
|
|
296
|
-
getter: payload => payload.voltage * 10,
|
|
297
295
|
def: 0,
|
|
298
296
|
},
|
|
297
|
+
energy: {
|
|
298
|
+
id: 'energy',
|
|
299
|
+
name: 'Sum of consumed energy',
|
|
300
|
+
icon: undefined,
|
|
301
|
+
role: 'value.power.consumption',
|
|
302
|
+
write: false,
|
|
303
|
+
read: true,
|
|
304
|
+
type: 'number',
|
|
305
|
+
unit: 'kWh'
|
|
306
|
+
},
|
|
299
307
|
battery: {
|
|
300
308
|
id: 'battery',
|
|
301
309
|
prop: 'battery',
|
|
@@ -546,6 +554,17 @@ const states = {
|
|
|
546
554
|
type: 'boolean',
|
|
547
555
|
getter: payload => (payload.button_right === 'hold'),
|
|
548
556
|
},
|
|
557
|
+
device_temperature: {
|
|
558
|
+
id: 'device_temperature',
|
|
559
|
+
name: 'Temperature of the device',
|
|
560
|
+
icon: undefined,
|
|
561
|
+
role: 'value.temperature',
|
|
562
|
+
write: false,
|
|
563
|
+
read: true,
|
|
564
|
+
type: 'number',
|
|
565
|
+
unit: '°C',
|
|
566
|
+
def: 0,
|
|
567
|
+
},
|
|
549
568
|
temperature: {
|
|
550
569
|
id: 'temperature',
|
|
551
570
|
name: 'Temperature',
|
|
@@ -736,7 +755,7 @@ const states = {
|
|
|
736
755
|
type: 'boolean',
|
|
737
756
|
def: false,
|
|
738
757
|
},
|
|
739
|
-
smoke_detected2: {
|
|
758
|
+
smoke_detected2: { // for Heiman
|
|
740
759
|
id: 'smoke',
|
|
741
760
|
prop: 'smoke',
|
|
742
761
|
name: 'Smoke leak detected',
|
|
@@ -747,7 +766,7 @@ const states = {
|
|
|
747
766
|
type: 'boolean',
|
|
748
767
|
def: false,
|
|
749
768
|
},
|
|
750
|
-
co_detected: {
|
|
769
|
+
co_detected: { // for Heiman
|
|
751
770
|
id: 'carbon_monoxide',
|
|
752
771
|
prop: 'carbon_monoxide',
|
|
753
772
|
name: 'CO leak detected',
|
|
@@ -1074,11 +1093,11 @@ const states = {
|
|
|
1074
1093
|
unit: 'V'
|
|
1075
1094
|
},
|
|
1076
1095
|
load_current: {
|
|
1077
|
-
id: '
|
|
1096
|
+
id: 'load_current',
|
|
1078
1097
|
prop: 'current',
|
|
1079
1098
|
name: 'Load current',
|
|
1080
1099
|
icon: undefined,
|
|
1081
|
-
role: 'value',
|
|
1100
|
+
role: 'value.current',
|
|
1082
1101
|
write: false,
|
|
1083
1102
|
read: true,
|
|
1084
1103
|
type: 'number',
|
|
@@ -1232,12 +1251,15 @@ const states = {
|
|
|
1232
1251
|
y: xy[1]
|
|
1233
1252
|
};
|
|
1234
1253
|
},
|
|
1235
|
-
setterOpt: (value, options) => {
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
},
|
|
1254
|
+
// setterOpt: (value, options) => {
|
|
1255
|
+
// const hasTransitionTime = options && options.hasOwnProperty('transition_time');
|
|
1256
|
+
// const transitionTime = hasTransitionTime ? options.transition_time : 0;
|
|
1257
|
+
// return { ...options, transition: transitionTime };
|
|
1258
|
+
// },
|
|
1240
1259
|
getter: payload => {
|
|
1260
|
+
if (payload.color_mode != 'xy') {
|
|
1261
|
+
return undefined;
|
|
1262
|
+
}
|
|
1241
1263
|
if (payload.color && payload.color.hasOwnProperty('x') && payload.color.hasOwnProperty('y')) {
|
|
1242
1264
|
const colorval = rgb.cie_to_rgb(payload.color.x, payload.color.y);
|
|
1243
1265
|
return '#' + utils.decimalToHex(colorval[0]) + utils.decimalToHex(colorval[1]) + utils.decimalToHex(colorval[2]);
|
|
@@ -2625,8 +2647,7 @@ const states = {
|
|
|
2625
2647
|
max: 1,
|
|
2626
2648
|
type: 'number',
|
|
2627
2649
|
},
|
|
2628
|
-
thermostat_keypad_lockout:
|
|
2629
|
-
{
|
|
2650
|
+
thermostat_keypad_lockout: {
|
|
2630
2651
|
id: 'keypad_lockout',
|
|
2631
2652
|
name: 'Keypad Lockout',
|
|
2632
2653
|
prop: 'keypad_lockout',
|
|
@@ -5447,6 +5468,7 @@ const states = {
|
|
|
5447
5468
|
write: false,
|
|
5448
5469
|
read: true,
|
|
5449
5470
|
type: 'boolean',
|
|
5471
|
+
def: false,
|
|
5450
5472
|
isEvent: true,
|
|
5451
5473
|
getter: payload => (payload.action === 'brightness_move_up') ? true : undefined,
|
|
5452
5474
|
},
|
|
@@ -5471,6 +5493,7 @@ const states = {
|
|
|
5471
5493
|
write: false,
|
|
5472
5494
|
read: true,
|
|
5473
5495
|
type: 'boolean',
|
|
5496
|
+
def: false,
|
|
5474
5497
|
isEvent: true,
|
|
5475
5498
|
getter: payload => (payload.action === 'brightness_move_down') ? true : undefined,
|
|
5476
5499
|
},
|
|
@@ -5842,8 +5865,7 @@ const states = {
|
|
|
5842
5865
|
setter: (value, options) => {
|
|
5843
5866
|
try {
|
|
5844
5867
|
return JSON.parse(value);
|
|
5845
|
-
}
|
|
5846
|
-
catch (err) {
|
|
5868
|
+
} catch (err) {
|
|
5847
5869
|
const effectjson = {
|
|
5848
5870
|
colors: [{ r: 255, g: 0, b: 0 }, { r: 0, g: 255, b: 0 }, { r: 0, g: 0, b: 255 }],
|
|
5849
5871
|
speed: 10,
|
|
@@ -6417,4 +6439,4 @@ module.exports = {
|
|
|
6417
6439
|
states: states,
|
|
6418
6440
|
unitLookup: unitLookup,
|
|
6419
6441
|
nameLookup: nameLookup,
|
|
6420
|
-
};
|
|
6442
|
+
};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
const utils = require('./utils');
|
|
2
|
+
const incStatsQueue = [];
|
|
3
|
+
const timeOutCache = {};
|
|
4
|
+
|
|
5
|
+
class StatesController {
|
|
6
|
+
constructor(adapter, deviceCache, groupCache, logCustomizations) {
|
|
7
|
+
this.adapter = adapter;
|
|
8
|
+
this.groupCache = groupCache;
|
|
9
|
+
this.deviceCache = deviceCache;
|
|
10
|
+
this.logCustomizations = logCustomizations;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async processDeviceMessage(messageObj) {
|
|
14
|
+
// Is payload present?
|
|
15
|
+
if (messageObj.payload == '') {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const device = this.groupCache.concat(this.deviceCache).find(x => x.id == messageObj.topic);
|
|
20
|
+
if (device) {
|
|
21
|
+
try {
|
|
22
|
+
this.setDeviceState(messageObj, device);
|
|
23
|
+
} catch (error) {
|
|
24
|
+
this.adapter.log.error(error);
|
|
25
|
+
}
|
|
26
|
+
} else {
|
|
27
|
+
incStatsQueue[incStatsQueue.length] = messageObj;
|
|
28
|
+
this.adapter.log.debug(`Device: ${messageObj.topic} not found, queue state in incStatsQueue!`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async setDeviceState(messageObj, device) {
|
|
33
|
+
if (this.logCustomizations.debugDevices.includes(device.ieee_address)) {
|
|
34
|
+
this.adapter.log.warn(`--->>> fromZ2M -> ${device.ieee_address} states: ${JSON.stringify(messageObj)}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
for (const [key, value] of Object.entries(messageObj.payload)) {
|
|
38
|
+
let states;
|
|
39
|
+
if (key == 'action') {
|
|
40
|
+
states = device.states.filter(x => (x.prop && x.prop == key));
|
|
41
|
+
} else {
|
|
42
|
+
states = device.states.filter(x => (x.prop && x.prop == key) || x.id == key);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
for (const state of states) {
|
|
46
|
+
if (!state) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const stateName = `${device.ieee_address}.${state.id}`;
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
if (state.isEvent) {
|
|
54
|
+
if (state.getter) {
|
|
55
|
+
await this.setStateWithTimeoutAsync(stateName, state.getter(messageObj.payload), 300);
|
|
56
|
+
} else {
|
|
57
|
+
await this.setStateWithTimeoutAsync(stateName, value, 300);
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
if (state.getter) {
|
|
61
|
+
await this.setStateChangedAsync(stateName, state.getter(messageObj.payload));
|
|
62
|
+
} else {
|
|
63
|
+
await this.setStateChangedAsync(stateName, value);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
} catch (err) {
|
|
67
|
+
incStatsQueue[incStatsQueue.length] = messageObj;
|
|
68
|
+
this.adapter.log.debug(`Can not set ${stateName}, queue state in incStatsQueue!`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async setStateAsync(stateName, value) {
|
|
75
|
+
if (value !== undefined) {
|
|
76
|
+
await this.adapter.setStateAsync(stateName, value, true);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async setStateChangedAsync(stateName, value) {
|
|
81
|
+
if (value !== undefined) {
|
|
82
|
+
await this.adapter.setStateChangedAsync(stateName, value, true);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async setStateWithTimeoutAsync(stateName, value, timeout) {
|
|
87
|
+
if (value !== undefined) {
|
|
88
|
+
await this.adapter.setStateAsync(stateName, value, true);
|
|
89
|
+
if (timeOutCache[stateName]) {
|
|
90
|
+
clearTimeout(timeOutCache[stateName]);
|
|
91
|
+
}
|
|
92
|
+
timeOutCache[stateName] = setTimeout(() => {
|
|
93
|
+
this.adapter.setStateAsync(stateName, !value, true);
|
|
94
|
+
}, timeout);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async processQueue() {
|
|
99
|
+
const oldIncStatsQueue = [];
|
|
100
|
+
utils.moveArray(incStatsQueue, oldIncStatsQueue);
|
|
101
|
+
while (oldIncStatsQueue.length > 0) {
|
|
102
|
+
this.processDeviceMessage(oldIncStatsQueue.shift());
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async subscribeWritableStates() {
|
|
107
|
+
await this.adapter.unsubscribeObjectsAsync('*');
|
|
108
|
+
for (const device of this.groupCache.concat(this.deviceCache)) {
|
|
109
|
+
for (const state of device.states) {
|
|
110
|
+
if (state.write == true) {
|
|
111
|
+
this.adapter.subscribeStatesAsync(`${device.ieee_address}.${state.id}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
this.adapter.subscribeStatesAsync('info.debugmessages');
|
|
116
|
+
this.adapter.subscribeStatesAsync('info.logfilter');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async setAllAvailableToFalse() {
|
|
120
|
+
for (const device of this.deviceCache) {
|
|
121
|
+
for (const state of device.states) {
|
|
122
|
+
if (state.id == 'available') {
|
|
123
|
+
await this.adapter.setStateChangedAsync(`${device.ieee_address}.${state.id}`, false, true);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async allTimerClear() {
|
|
130
|
+
for (const timer in timeOutCache) {
|
|
131
|
+
clearTimeout(timeOutCache[timer]);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
module.exports = {
|
|
137
|
+
StatesController
|
|
138
|
+
};
|
package/lib/utils.js
CHANGED
|
@@ -106,26 +106,28 @@ function getDeviceIcon(definition) {
|
|
|
106
106
|
return icon;
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
function removeDeviceByIeee(devices, ieee_address) {
|
|
110
|
-
const idx = devices.findIndex(x => x.ieee_address == ieee_address);
|
|
111
|
-
if (idx > -1) {
|
|
112
|
-
devices.splice(idx, 1);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
109
|
function clearArray(array) {
|
|
116
110
|
while (array.length > 0) {
|
|
117
111
|
array.pop();
|
|
118
112
|
}
|
|
119
113
|
}
|
|
120
114
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
exports
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
115
|
+
function moveArray(source, target) {
|
|
116
|
+
while (source.length > 0) {
|
|
117
|
+
target.push(source.shift());
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
module.exports = {
|
|
122
|
+
bulbLevelToAdapterLevel,
|
|
123
|
+
adapterLevelToBulbLevel,
|
|
124
|
+
bytesArrayToWordArray,
|
|
125
|
+
toMired,
|
|
126
|
+
miredKelvinConversion,
|
|
127
|
+
decimalToHex,
|
|
128
|
+
getZbId,
|
|
129
|
+
getAdId,
|
|
130
|
+
getDeviceIcon,
|
|
131
|
+
clearArray,
|
|
132
|
+
moveArray,
|
|
133
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
const WebSocket = require('ws');
|
|
2
|
+
let wsClient;
|
|
3
|
+
const wsHeartbeatIntervall = 5000;
|
|
4
|
+
const restartTimeout = 1000;
|
|
5
|
+
let ping;
|
|
6
|
+
let pingTimeout;
|
|
7
|
+
let autoRestartTimeout;
|
|
8
|
+
|
|
9
|
+
class WebsocketController {
|
|
10
|
+
constructor(adapter) {
|
|
11
|
+
this.adapter = adapter;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async initWsClient(server, port) {
|
|
15
|
+
try {
|
|
16
|
+
wsClient = new WebSocket(`ws://${server}:${port}/api`);
|
|
17
|
+
|
|
18
|
+
wsClient.on('open', () => {
|
|
19
|
+
// Send ping to server
|
|
20
|
+
this.sendPingToServer();
|
|
21
|
+
// Start Heartbeat
|
|
22
|
+
this.wsHeartbeat();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
wsClient.on('pong', () => {
|
|
26
|
+
this.wsHeartbeat();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
wsClient.on('close', async () => {
|
|
30
|
+
clearTimeout(pingTimeout);
|
|
31
|
+
clearTimeout(ping);
|
|
32
|
+
|
|
33
|
+
if (wsClient.readyState === WebSocket.CLOSED) {
|
|
34
|
+
this.autoRestart();
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
wsClient.on('message', () => { });
|
|
39
|
+
|
|
40
|
+
wsClient.on('error', (err) => { this.adapter.log.debug(err); });
|
|
41
|
+
|
|
42
|
+
return wsClient;
|
|
43
|
+
} catch (err) {
|
|
44
|
+
this.adapter.log.error(err);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async send(message) {
|
|
49
|
+
wsClient.send(message);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async sendPingToServer() {
|
|
53
|
+
//this.logDebug('Send ping to server');
|
|
54
|
+
wsClient.ping();
|
|
55
|
+
ping = setTimeout(() => {
|
|
56
|
+
this.sendPingToServer();
|
|
57
|
+
}, wsHeartbeatIntervall);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async wsHeartbeat() {
|
|
61
|
+
clearTimeout(pingTimeout);
|
|
62
|
+
pingTimeout = setTimeout(() => {
|
|
63
|
+
this.adapter.log.warn('Websocked connection timed out');
|
|
64
|
+
wsClient.terminate();
|
|
65
|
+
}, wsHeartbeatIntervall + 1000);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async autoRestart() {
|
|
69
|
+
this.adapter.log.warn(`Start try again in ${restartTimeout / 1000} seconds...`);
|
|
70
|
+
autoRestartTimeout = setTimeout(() => {
|
|
71
|
+
this.adapter.onReady();
|
|
72
|
+
}, restartTimeout);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async allTimerClear() {
|
|
76
|
+
clearTimeout(pingTimeout);
|
|
77
|
+
clearTimeout(ping);
|
|
78
|
+
clearTimeout(autoRestartTimeout);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
module.exports = {
|
|
83
|
+
WebsocketController
|
|
84
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
class Z2mController {
|
|
2
|
+
constructor(adapter, deviceCache, groupCache, logCustomizations) {
|
|
3
|
+
this.adapter = adapter;
|
|
4
|
+
this.groupCache = groupCache;
|
|
5
|
+
this.deviceCache = deviceCache;
|
|
6
|
+
this.logCustomizations = logCustomizations;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
async createZ2MMessage(id, state) {
|
|
10
|
+
const splitedID = id.split('.');
|
|
11
|
+
if (splitedID.length < 4) {
|
|
12
|
+
this.adapter.log.warn(`state ${id} not valid`);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const ieee_address = splitedID[2];
|
|
17
|
+
const stateName = splitedID[3];
|
|
18
|
+
|
|
19
|
+
const device = this.groupCache.concat(this.deviceCache).find(d => d.ieee_address == ieee_address);
|
|
20
|
+
if (!device) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const deviceState = device.states.find(s => s.id == stateName);
|
|
25
|
+
if (!deviceState) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let stateVal = state.val;
|
|
30
|
+
if (deviceState.setter) {
|
|
31
|
+
stateVal = deviceState.setter(state.val);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let stateID = deviceState.id;
|
|
35
|
+
if (deviceState.prop) {
|
|
36
|
+
stateID = deviceState.prop;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let topic = `${device.ieee_address}/set`;
|
|
40
|
+
if (device.ieee_address.includes('group_')) {
|
|
41
|
+
topic = `${device.id}/set`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const controlObj = {
|
|
45
|
+
payload: {
|
|
46
|
+
[stateID]: stateVal
|
|
47
|
+
},
|
|
48
|
+
topic: topic
|
|
49
|
+
};
|
|
50
|
+
// set stats with the mentioned role or ids always immediately to ack = true, because these are not reported back by Zigbee2MQTT
|
|
51
|
+
if (['button'].includes(deviceState.role) || ['brightness_move', 'color_temp_move'].includes(stateID)) {
|
|
52
|
+
this.adapter.setState(id, state, true);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return controlObj;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async proxyZ2MLogs(messageObj) {
|
|
59
|
+
const logMessage = messageObj.payload.message;
|
|
60
|
+
if (this.logCustomizations.logfilter.some(x => logMessage.includes(x))) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const logLevel = messageObj.payload.level;
|
|
65
|
+
switch (logLevel) {
|
|
66
|
+
case 'debug':
|
|
67
|
+
case 'info':
|
|
68
|
+
case 'error':
|
|
69
|
+
this.adapter.log[logLevel](logMessage);
|
|
70
|
+
break;
|
|
71
|
+
case 'warning':
|
|
72
|
+
this.adapter.log.warn(logMessage);
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = {
|
|
79
|
+
Z2mController: Z2mController
|
|
80
|
+
};
|