iobroker.zigbee2mqtt 1.0.0 → 2.1.1
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 +26 -2
- package/admin/i18n/de/translations.json +20 -8
- package/admin/i18n/en/translations.json +15 -3
- package/admin/i18n/es/translations.json +15 -3
- package/admin/i18n/fr/translations.json +15 -3
- package/admin/i18n/it/translations.json +15 -3
- package/admin/i18n/nl/translations.json +15 -3
- package/admin/i18n/pl/translations.json +15 -3
- package/admin/i18n/pt/translations.json +15 -3
- package/admin/i18n/ru/translations.json +15 -3
- package/admin/i18n/zh-cn/translations.json +15 -3
- package/admin/jsonConfig.json +90 -17
- package/io-package.json +47 -5
- package/lib/check.js +1 -2
- package/lib/colors.js +5 -3
- package/lib/deviceController.js +78 -16
- package/lib/exposes.js +56 -42
- package/lib/messages.js +31 -10
- package/lib/mqttServerController.js +42 -0
- package/lib/rgb.js +42 -17
- package/lib/states.js +39 -19
- package/lib/statesController.js +34 -23
- package/lib/utils.js +12 -19
- package/lib/websocketController.js +84 -0
- package/lib/z2mController.js +3 -3
- package/main.js +91 -42
- package/package.json +5 -5
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',
|
|
@@ -5844,8 +5865,7 @@ const states = {
|
|
|
5844
5865
|
setter: (value, options) => {
|
|
5845
5866
|
try {
|
|
5846
5867
|
return JSON.parse(value);
|
|
5847
|
-
}
|
|
5848
|
-
catch (err) {
|
|
5868
|
+
} catch (err) {
|
|
5849
5869
|
const effectjson = {
|
|
5850
5870
|
colors: [{ r: 255, g: 0, b: 0 }, { r: 0, g: 255, b: 0 }, { r: 0, g: 0, b: 255 }],
|
|
5851
5871
|
speed: 10,
|
|
@@ -6419,4 +6439,4 @@ module.exports = {
|
|
|
6419
6439
|
states: states,
|
|
6420
6440
|
unitLookup: unitLookup,
|
|
6421
6441
|
nameLookup: nameLookup,
|
|
6422
|
-
};
|
|
6442
|
+
};
|
package/lib/statesController.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
const utils = require('./utils');
|
|
2
2
|
const incStatsQueue = [];
|
|
3
|
+
const timeOutCache = {};
|
|
3
4
|
|
|
4
5
|
class StatesController {
|
|
5
|
-
constructor(adapter, deviceCache, groupCache,
|
|
6
|
+
constructor(adapter, deviceCache, groupCache, logCustomizations) {
|
|
6
7
|
this.adapter = adapter;
|
|
7
8
|
this.groupCache = groupCache;
|
|
8
9
|
this.deviceCache = deviceCache;
|
|
9
|
-
this.
|
|
10
|
+
this.logCustomizations = logCustomizations;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
processDeviceMessage(messageObj) {
|
|
13
14
|
// Is payload present?
|
|
14
15
|
if (messageObj.payload == '') {
|
|
15
16
|
return;
|
|
@@ -18,19 +19,18 @@ class StatesController {
|
|
|
18
19
|
const device = this.groupCache.concat(this.deviceCache).find(x => x.id == messageObj.topic);
|
|
19
20
|
if (device) {
|
|
20
21
|
try {
|
|
21
|
-
this.
|
|
22
|
+
this.setDeviceStateSafely(messageObj, device);
|
|
22
23
|
} catch (error) {
|
|
23
24
|
this.adapter.log.error(error);
|
|
24
25
|
}
|
|
25
|
-
}
|
|
26
|
-
else {
|
|
26
|
+
} else {
|
|
27
27
|
incStatsQueue[incStatsQueue.length] = messageObj;
|
|
28
28
|
this.adapter.log.debug(`Device: ${messageObj.topic} not found, queue state in incStatsQueue!`);
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
async
|
|
33
|
-
if (this.debugDevices.includes(device.ieee_address)) {
|
|
32
|
+
async setDeviceStateSafely(messageObj, device) {
|
|
33
|
+
if (this.logCustomizations.debugDevices.includes(device.ieee_address)) {
|
|
34
34
|
this.adapter.log.warn(`--->>> fromZ2M -> ${device.ieee_address} states: ${JSON.stringify(messageObj)}`);
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -53,44 +53,49 @@ class StatesController {
|
|
|
53
53
|
if (state.isEvent) {
|
|
54
54
|
if (state.getter) {
|
|
55
55
|
await this.setStateWithTimeoutAsync(stateName, state.getter(messageObj.payload), 300);
|
|
56
|
-
}
|
|
57
|
-
else {
|
|
56
|
+
} else {
|
|
58
57
|
await this.setStateWithTimeoutAsync(stateName, value, 300);
|
|
59
58
|
}
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
59
|
+
} else {
|
|
62
60
|
if (state.getter) {
|
|
63
|
-
await this.
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
await this.setStateAsync(stateName, value);
|
|
61
|
+
await this.setStateChangedSafelyAsync(stateName, state.getter(messageObj.payload));
|
|
62
|
+
} else {
|
|
63
|
+
await this.setStateChangedSafelyAsync(stateName, value);
|
|
67
64
|
}
|
|
68
65
|
}
|
|
69
66
|
} catch (err) {
|
|
70
|
-
//this.adapter.log.warn(`Can not set ${stateName}`);
|
|
71
67
|
incStatsQueue[incStatsQueue.length] = messageObj;
|
|
72
|
-
this.adapter.log.debug(`Can not set ${stateName}
|
|
68
|
+
this.adapter.log.debug(`Can not set ${stateName}, queue state in incStatsQueue!`);
|
|
73
69
|
}
|
|
74
70
|
}
|
|
75
71
|
}
|
|
76
72
|
}
|
|
77
73
|
|
|
78
|
-
async
|
|
74
|
+
async setStateSafelyAsync(stateName, value) {
|
|
79
75
|
if (value !== undefined) {
|
|
80
76
|
await this.adapter.setStateAsync(stateName, value, true);
|
|
81
77
|
}
|
|
82
78
|
}
|
|
83
79
|
|
|
80
|
+
async setStateChangedSafelyAsync(stateName, value) {
|
|
81
|
+
if (value !== undefined) {
|
|
82
|
+
await this.adapter.setStateChangedAsync(stateName, value, true);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
84
86
|
async setStateWithTimeoutAsync(stateName, value, timeout) {
|
|
85
87
|
if (value !== undefined) {
|
|
86
88
|
await this.adapter.setStateAsync(stateName, value, true);
|
|
87
|
-
|
|
89
|
+
if (timeOutCache[stateName]) {
|
|
90
|
+
clearTimeout(timeOutCache[stateName]);
|
|
91
|
+
}
|
|
92
|
+
timeOutCache[stateName] = setTimeout(() => {
|
|
88
93
|
this.adapter.setStateAsync(stateName, !value, true);
|
|
89
94
|
}, timeout);
|
|
90
95
|
}
|
|
91
96
|
}
|
|
92
97
|
|
|
93
|
-
|
|
98
|
+
processQueue() {
|
|
94
99
|
const oldIncStatsQueue = [];
|
|
95
100
|
utils.moveArray(incStatsQueue, oldIncStatsQueue);
|
|
96
101
|
while (oldIncStatsQueue.length > 0) {
|
|
@@ -115,13 +120,19 @@ class StatesController {
|
|
|
115
120
|
for (const device of this.deviceCache) {
|
|
116
121
|
for (const state of device.states) {
|
|
117
122
|
if (state.id == 'available') {
|
|
118
|
-
await this.adapter.
|
|
123
|
+
await this.adapter.setStateChangedAsync(`${device.ieee_address}.${state.id}`, false, true);
|
|
119
124
|
}
|
|
120
125
|
}
|
|
121
126
|
}
|
|
122
127
|
}
|
|
128
|
+
|
|
129
|
+
async allTimerClear() {
|
|
130
|
+
for (const timer in timeOutCache) {
|
|
131
|
+
clearTimeout(timeOutCache[timer]);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
123
134
|
}
|
|
124
135
|
|
|
125
136
|
module.exports = {
|
|
126
137
|
StatesController
|
|
127
|
-
};
|
|
138
|
+
};
|
package/lib/utils.js
CHANGED
|
@@ -106,12 +106,6 @@ 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();
|
|
@@ -125,16 +119,15 @@ function moveArray(source, target) {
|
|
|
125
119
|
}
|
|
126
120
|
|
|
127
121
|
module.exports = {
|
|
128
|
-
bulbLevelToAdapterLevel
|
|
129
|
-
adapterLevelToBulbLevel
|
|
130
|
-
bytesArrayToWordArray
|
|
131
|
-
toMired
|
|
132
|
-
miredKelvinConversion
|
|
133
|
-
decimalToHex
|
|
134
|
-
getZbId
|
|
135
|
-
getAdId
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
};
|
|
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
|
+
initWsClient() {
|
|
15
|
+
try {
|
|
16
|
+
wsClient = new WebSocket(`ws://${this.adapter.config.wsServerIP}:${this.adapter.config.wsServerPort}/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
|
+
send(message) {
|
|
49
|
+
wsClient.send(message);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
sendPingToServer() {
|
|
53
|
+
//this.logDebug('Send ping to server');
|
|
54
|
+
wsClient.ping();
|
|
55
|
+
ping = setTimeout(() => {
|
|
56
|
+
this.sendPingToServer();
|
|
57
|
+
}, wsHeartbeatIntervall);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
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.startWebsocket();
|
|
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
|
+
};
|
package/lib/z2mController.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
class Z2mController {
|
|
2
|
-
constructor(adapter, deviceCache, groupCache,
|
|
2
|
+
constructor(adapter, deviceCache, groupCache, logCustomizations) {
|
|
3
3
|
this.adapter = adapter;
|
|
4
4
|
this.groupCache = groupCache;
|
|
5
5
|
this.deviceCache = deviceCache;
|
|
6
|
-
this.
|
|
6
|
+
this.logCustomizations = logCustomizations;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
async createZ2MMessage(id, state) {
|
|
@@ -57,7 +57,7 @@ class Z2mController {
|
|
|
57
57
|
|
|
58
58
|
async proxyZ2MLogs(messageObj) {
|
|
59
59
|
const logMessage = messageObj.payload.message;
|
|
60
|
-
if (this.logfilter.some(x => logMessage.includes(x))) {
|
|
60
|
+
if (this.logCustomizations.logfilter.some(x => logMessage.includes(x))) {
|
|
61
61
|
return;
|
|
62
62
|
}
|
|
63
63
|
|
package/main.js
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
// The adapter-core module gives you access to the core ioBroker functions
|
|
8
8
|
// you need to create an adapter
|
|
9
9
|
const core = require('@iobroker/adapter-core');
|
|
10
|
-
const NedbPersistence = require('aedes-persistence-nedb');
|
|
11
10
|
const mqtt = require('mqtt');
|
|
12
11
|
const checkConfig = require('./lib/check').checkConfig;
|
|
13
12
|
const adapterInfo = require('./lib/messages').adapterInfo;
|
|
@@ -15,6 +14,8 @@ const zigbee2mqttInfo = require('./lib/messages').zigbee2mqttInfo;
|
|
|
15
14
|
const Z2mController = require('./lib/z2mController').Z2mController;
|
|
16
15
|
const DeviceController = require('./lib/deviceController').DeviceController;
|
|
17
16
|
const StatesController = require('./lib/statesController').StatesController;
|
|
17
|
+
const WebsocketController = require('./lib/websocketController').WebsocketController;
|
|
18
|
+
const MqttServerController = require('./lib/mqttServerController').MqttServerController;
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
let mqttClient;
|
|
@@ -22,16 +23,13 @@ let mqttClient;
|
|
|
22
23
|
let deviceCache = [];
|
|
23
24
|
// eslint-disable-next-line prefer-const
|
|
24
25
|
let groupCache = [];
|
|
25
|
-
|
|
26
|
-
let pingTimeout;
|
|
27
|
-
let autoRestartTimeout;
|
|
28
|
-
let checkAvailableTimout;
|
|
29
|
-
let debugDevices = '';
|
|
30
|
-
let logfilter = [];
|
|
26
|
+
const logCustomizations = { debugDevices: '', logfilter: [] };
|
|
31
27
|
let showInfo = true;
|
|
32
28
|
let statesController;
|
|
33
29
|
let deviceController;
|
|
34
30
|
let z2mController;
|
|
31
|
+
let websocketController;
|
|
32
|
+
let mqttServerController;
|
|
35
33
|
|
|
36
34
|
class Zigbee2mqtt extends core.Adapter {
|
|
37
35
|
|
|
@@ -46,11 +44,9 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
46
44
|
}
|
|
47
45
|
|
|
48
46
|
async onReady() {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
deviceController = new DeviceController(this, deviceCache, groupCache, this.config.useKelvin);
|
|
53
|
-
z2mController = new Z2mController(this, deviceCache, groupCache, logfilter);
|
|
47
|
+
statesController = new StatesController(this, deviceCache, groupCache, logCustomizations);
|
|
48
|
+
deviceController = new DeviceController(this, deviceCache, groupCache, this.config);
|
|
49
|
+
z2mController = new Z2mController(this, deviceCache, groupCache, logCustomizations);
|
|
54
50
|
|
|
55
51
|
// Initialize your adapter here
|
|
56
52
|
adapterInfo(this.config, this.log);
|
|
@@ -59,39 +55,81 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
59
55
|
|
|
60
56
|
const debugDevicesState = await this.getStateAsync('info.debugmessages');
|
|
61
57
|
if (debugDevicesState && debugDevicesState.val) {
|
|
62
|
-
debugDevices = String(debugDevicesState.val);
|
|
58
|
+
logCustomizations.debugDevices = String(debugDevicesState.val);
|
|
63
59
|
}
|
|
64
60
|
|
|
65
61
|
const logfilterState = await this.getStateAsync('info.logfilter');
|
|
66
62
|
if (logfilterState && logfilterState.val) {
|
|
67
|
-
|
|
63
|
+
// @ts-ignore
|
|
64
|
+
logCustomizations.logfilter = String(logfilterState.val).split(';').filter(x => x); // filter removes empty strings here
|
|
68
65
|
}
|
|
66
|
+
// MQTT
|
|
67
|
+
if (['exmqtt', 'intmqtt'].includes(this.config.connectionType)) {
|
|
68
|
+
// External MQTT-Server
|
|
69
|
+
if (this.config.connectionType == 'exmqtt') {
|
|
70
|
+
if (this.config.externalMqttServerIP == '') {
|
|
71
|
+
this.log.warn('Please configure the External MQTT-Server connection!');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
mqttClient = mqtt.connect(`mqtt://${this.config.externalMqttServerIP}:${this.config.externalMqttServerPort}`, { clientId: `ioBroker.zigbee2mqtt_${Math.random().toString(16).slice(2, 8)}`, clean: true, reconnectPeriod: 500 });
|
|
69
75
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
}
|
|
77
|
+
// Internal MQTT-Server
|
|
78
|
+
else {
|
|
79
|
+
mqttServerController = new MqttServerController(this);
|
|
80
|
+
await mqttServerController.createMQTTServer();
|
|
81
|
+
await this.delay(1500);
|
|
82
|
+
mqttClient = mqtt.connect(`mqtt://${this.config.mqttServerIPBind}:${this.config.mqttServerPort}`, { clientId: `ioBroker.zigbee2mqtt_${Math.random().toString(16).slice(2, 8)}`, clean: true, reconnectPeriod: 500 });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// MQTT Client
|
|
86
|
+
mqttClient.on('connect', () => {
|
|
87
|
+
this.log.info(`Connect to Zigbee2MQTT over ${this.config.connectionType == 'exmqtt' ? 'external mqtt' : 'internal mqtt'} connection.`);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
mqttClient.subscribe('zigbee2mqtt/#');
|
|
91
|
+
|
|
92
|
+
mqttClient.on('message', (topic, payload) => {
|
|
93
|
+
const newMessage = `{"payload":${payload.toString() == '' ? '"null"' : payload.toString()},"topic":"${topic.slice(topic.search('/') + 1)}"}`;
|
|
94
|
+
this.messageParse(newMessage);
|
|
78
95
|
});
|
|
79
|
-
// @ts-ignore
|
|
80
|
-
const aedes = Aedes({ persistence: db });
|
|
81
|
-
const mqttServer = net.createServer(aedes.handle);
|
|
82
|
-
mqttServer.listen(this.config.mqttServerPort, this.config.mqttServerIPBind, () => { });
|
|
83
|
-
mqttClient = mqtt.connect(`mqtt://${this.config.mqttServerIPBind}:${this.config.mqttServerPort}`, { clientId: 'ioBroker.zigbee2mqtt', clean: true, reconnectPeriod: 500 });
|
|
84
96
|
}
|
|
97
|
+
// Websocket
|
|
98
|
+
else if (this.config.connectionType == 'ws') {
|
|
99
|
+
if (this.config.wsServerIP == '') {
|
|
100
|
+
this.log.warn('Please configure the Websoket connection!');
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
85
103
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
104
|
+
// Dummy MQTT-Server
|
|
105
|
+
if (this.config.dummyMqtt == true) {
|
|
106
|
+
mqttServerController = new MqttServerController(this);
|
|
107
|
+
await mqttServerController.createDummyMQTTServer();
|
|
108
|
+
await this.delay(1500);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
this.startWebsocket();
|
|
112
|
+
}
|
|
93
113
|
}
|
|
94
114
|
|
|
115
|
+
startWebsocket() {
|
|
116
|
+
websocketController = new WebsocketController(this);
|
|
117
|
+
const wsClient = websocketController.initWsClient();
|
|
118
|
+
|
|
119
|
+
wsClient.on('open', () => {
|
|
120
|
+
this.log.info('Connect to Zigbee2MQTT over websocket connection.');
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
wsClient.on('message', (message) => {
|
|
124
|
+
this.messageParse(message);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
wsClient.on('close', async () => {
|
|
128
|
+
this.setStateChangedAsync('info.connection', false, true);
|
|
129
|
+
await statesController.setAllAvailableToFalse();
|
|
130
|
+
this.log.warn('Websocket disconnectet');
|
|
131
|
+
});
|
|
132
|
+
}
|
|
95
133
|
|
|
96
134
|
async messageParse(message) {
|
|
97
135
|
const messageObj = JSON.parse(message);
|
|
@@ -107,6 +145,10 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
107
145
|
}
|
|
108
146
|
break;
|
|
109
147
|
case 'bridge/state':
|
|
148
|
+
if (messageObj.payload.state != 'online') {
|
|
149
|
+
statesController.setAllAvailableToFalse();
|
|
150
|
+
}
|
|
151
|
+
this.setStateChangedAsync('info.connection', messageObj.payload.state == 'online', true);
|
|
110
152
|
break;
|
|
111
153
|
case 'bridge/devices':
|
|
112
154
|
await deviceController.createDeviceDefinitions(messageObj.payload);
|
|
@@ -121,6 +163,10 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
121
163
|
statesController.processQueue();
|
|
122
164
|
break;
|
|
123
165
|
case 'bridge/event':
|
|
166
|
+
console.log(JSON.stringify(messageObj));
|
|
167
|
+
deviceController.processRemoveEvent(messageObj);
|
|
168
|
+
break;
|
|
169
|
+
case 'bridge/response/device/remove':
|
|
124
170
|
deviceController.processRemoveEvent(messageObj);
|
|
125
171
|
break;
|
|
126
172
|
case 'bridge/extensions':
|
|
@@ -174,10 +220,8 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
174
220
|
async onUnload(callback) {
|
|
175
221
|
try {
|
|
176
222
|
await statesController.setAllAvailableToFalse();
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
clearTimeout(autoRestartTimeout);
|
|
180
|
-
clearTimeout(checkAvailableTimout);
|
|
223
|
+
await websocketController.allTimerClear();
|
|
224
|
+
await statesController.allTimerClear();
|
|
181
225
|
callback();
|
|
182
226
|
} catch (e) {
|
|
183
227
|
callback();
|
|
@@ -187,18 +231,23 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
187
231
|
async onStateChange(id, state) {
|
|
188
232
|
if (state && state.ack == false) {
|
|
189
233
|
if (id.includes('info.debugmessages')) {
|
|
190
|
-
debugDevices = state.val;
|
|
234
|
+
logCustomizations.debugDevices = state.val;
|
|
191
235
|
this.setState(id, state.val, true);
|
|
192
236
|
return;
|
|
193
237
|
}
|
|
194
238
|
if (id.includes('info.logfilter')) {
|
|
195
|
-
logfilter = state.val.split(';').filter(x => x); // filter removes empty strings here
|
|
239
|
+
logCustomizations.logfilter = state.val.split(';').filter(x => x); // filter removes empty strings here
|
|
196
240
|
this.setState(id, state.val, true);
|
|
197
241
|
return;
|
|
198
242
|
}
|
|
199
243
|
|
|
200
244
|
const message = await z2mController.createZ2MMessage(id, state) || { topic: '', payload: '' };
|
|
201
|
-
|
|
245
|
+
|
|
246
|
+
if (['exmqtt', 'intmqtt'].includes(this.config.connectionType)) {
|
|
247
|
+
mqttClient.publish(`zigbee2mqtt/${message.topic}`, JSON.stringify(message.payload));
|
|
248
|
+
} else if (this.config.connectionType == 'ws') {
|
|
249
|
+
websocketController.send(JSON.stringify(message));
|
|
250
|
+
}
|
|
202
251
|
}
|
|
203
252
|
}
|
|
204
253
|
}
|
|
@@ -213,4 +262,4 @@ if (require.main !== module) {
|
|
|
213
262
|
} else {
|
|
214
263
|
// otherwise start the instance directly
|
|
215
264
|
new Zigbee2mqtt();
|
|
216
|
-
}
|
|
265
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.zigbee2mqtt",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "Zigbee2MQTT adapter for ioBroker",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Dennis Rathjen",
|
|
@@ -19,8 +19,6 @@
|
|
|
19
19
|
"url": "https://github.com/o0shojo0o/ioBroker.zigbee2mqtt.git"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@alcalzone/release-script-plugin-iobroker": "^3.5.9",
|
|
23
|
-
"@alcalzone/release-script-plugin-license": "^3.5.9",
|
|
24
22
|
"@iobroker/adapter-core": "^2.6.7",
|
|
25
23
|
"aedes": "^0.48.0",
|
|
26
24
|
"aedes-persistence-nedb": "^2.0.3",
|
|
@@ -29,6 +27,8 @@
|
|
|
29
27
|
"ws": "^8.9.0"
|
|
30
28
|
},
|
|
31
29
|
"devDependencies": {
|
|
30
|
+
"@alcalzone/release-script-plugin-iobroker": "^3.5.9",
|
|
31
|
+
"@alcalzone/release-script-plugin-license": "^3.5.9",
|
|
32
32
|
"@alcalzone/release-script": "^3.5.9",
|
|
33
33
|
"@iobroker/adapter-dev": "^1.1.0",
|
|
34
34
|
"@iobroker/testing": "^4.1.0",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"@types/chai": "^4.3.3",
|
|
37
37
|
"@types/chai-as-promised": "^7.1.5",
|
|
38
38
|
"@types/mocha": "^10.0.0",
|
|
39
|
-
"@types/node": "^18.
|
|
39
|
+
"@types/node": "^18.11.0",
|
|
40
40
|
"@types/proxyquire": "^1.3.28",
|
|
41
41
|
"@types/sinon": "^10.0.13",
|
|
42
42
|
"@types/sinon-chai": "^3.2.8",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"eslint": "^8.25.0",
|
|
46
46
|
"eslint-config-prettier": "^8.5.0",
|
|
47
47
|
"eslint-plugin-prettier": "^4.2.1",
|
|
48
|
-
"mocha": "^10.
|
|
48
|
+
"mocha": "^10.1.0",
|
|
49
49
|
"prettier": "^2.7.1",
|
|
50
50
|
"proxyquire": "^2.1.3",
|
|
51
51
|
"sinon": "^14.0.1",
|