iobroker.zigbee2mqtt 2.1.1 → 2.2.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.
@@ -1,80 +1,85 @@
1
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
- }
2
+ constructor(adapter, deviceCache, groupCache, logCustomizations) {
3
+ this.adapter = adapter;
4
+ this.groupCache = groupCache;
5
+ this.deviceCache = deviceCache;
6
+ this.logCustomizations = logCustomizations;
7
+ }
8
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
- }
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
15
 
16
- const ieee_address = splitedID[2];
17
- const stateName = splitedID[3];
16
+ const ieee_address = splitedID[2];
17
+ const stateName = splitedID[3];
18
18
 
19
- const device = this.groupCache.concat(this.deviceCache).find(d => d.ieee_address == ieee_address);
20
- if (!device) {
21
- return;
22
- }
19
+ const device = this.groupCache.concat(this.deviceCache).find(d => d.ieee_address == ieee_address);
20
+ if (!device) {
21
+ return;
22
+ }
23
23
 
24
- const deviceState = device.states.find(s => s.id == stateName);
25
- if (!deviceState) {
26
- return;
27
- }
24
+ const deviceState = device.states.find(s => s.id == stateName);
25
+ if (!deviceState) {
26
+ return;
27
+ }
28
28
 
29
- let stateVal = state.val;
30
- if (deviceState.setter) {
31
- stateVal = deviceState.setter(state.val);
32
- }
29
+ let stateVal = state.val;
30
+ if (deviceState.setter) {
31
+ stateVal = deviceState.setter(state.val);
32
+ }
33
33
 
34
- let stateID = deviceState.id;
35
- if (deviceState.prop) {
36
- stateID = deviceState.prop;
37
- }
34
+ let stateID = deviceState.id;
35
+ if (deviceState.prop) {
36
+ stateID = deviceState.prop;
37
+ }
38
38
 
39
- let topic = `${device.ieee_address}/set`;
40
- if (device.ieee_address.includes('group_')) {
41
- topic = `${device.id}/set`;
42
- }
39
+ let topic = `${device.ieee_address}/set`;
40
+ if (device.ieee_address.includes('group_')) {
41
+ topic = `${device.id}/set`;
42
+ }
43
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
- }
44
+ const controlObj = {
45
+ payload: {
46
+ [stateID]: stateVal
47
+ },
48
+ topic: topic
49
+ };
54
50
 
55
- return controlObj;
56
- }
51
+ // set stats with the mentioned role or ids always immediately to ack = true, because these are not reported back by Zigbee2MQTT
52
+ if (['button'].includes(deviceState.role) || ['brightness_move', 'color_temp_move'].includes(stateID)) {
53
+ this.adapter.setState(id, state, true);
54
+ }
57
55
 
58
- async proxyZ2MLogs(messageObj) {
59
- const logMessage = messageObj.payload.message;
60
- if (this.logCustomizations.logfilter.some(x => logMessage.includes(x))) {
61
- return;
62
- }
56
+ if (this.logCustomizations.debugDevices.includes(device.ieee_address)) {
57
+ this.adapter.log.warn(`<<<--- toZ2M -> ${device.ieee_address} states: ${JSON.stringify(controlObj)}`);
58
+ }
63
59
 
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
- }
60
+ return controlObj;
61
+ }
62
+
63
+ async proxyZ2MLogs(messageObj) {
64
+ const logMessage = messageObj.payload.message;
65
+ if (this.logCustomizations.logfilter.some(x => logMessage.includes(x))) {
66
+ return;
67
+ }
68
+
69
+ const logLevel = messageObj.payload.level;
70
+ switch (logLevel) {
71
+ case 'debug':
72
+ case 'info':
73
+ case 'error':
74
+ this.adapter.log[logLevel](logMessage);
75
+ break;
76
+ case 'warning':
77
+ this.adapter.log.warn(logMessage);
78
+ break;
79
+ }
80
+ }
76
81
  }
77
82
 
78
83
  module.exports = {
79
- Z2mController: Z2mController
84
+ Z2mController: Z2mController
80
85
  };
package/main.js CHANGED
@@ -33,233 +33,233 @@ let mqttServerController;
33
33
 
34
34
  class Zigbee2mqtt extends core.Adapter {
35
35
 
36
- constructor(options) {
37
- super({
38
- ...options,
39
- name: 'zigbee2mqtt',
40
- });
41
- this.on('ready', this.onReady.bind(this));
42
- this.on('stateChange', this.onStateChange.bind(this));
43
- this.on('unload', this.onUnload.bind(this));
44
- }
36
+ constructor(options) {
37
+ super({
38
+ ...options,
39
+ name: 'zigbee2mqtt',
40
+ });
41
+ this.on('ready', this.onReady.bind(this));
42
+ this.on('stateChange', this.onStateChange.bind(this));
43
+ this.on('unload', this.onUnload.bind(this));
44
+ }
45
45
 
46
- async onReady() {
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);
46
+ async onReady() {
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);
50
50
 
51
- // Initialize your adapter here
52
- adapterInfo(this.config, this.log);
51
+ // Initialize your adapter here
52
+ adapterInfo(this.config, this.log);
53
53
 
54
- this.setStateAsync('info.connection', false, true);
54
+ this.setStateAsync('info.connection', false, true);
55
55
 
56
- const debugDevicesState = await this.getStateAsync('info.debugmessages');
57
- if (debugDevicesState && debugDevicesState.val) {
58
- logCustomizations.debugDevices = String(debugDevicesState.val);
59
- }
56
+ const debugDevicesState = await this.getStateAsync('info.debugmessages');
57
+ if (debugDevicesState && debugDevicesState.val) {
58
+ logCustomizations.debugDevices = String(debugDevicesState.val);
59
+ }
60
60
 
61
- const logfilterState = await this.getStateAsync('info.logfilter');
62
- if (logfilterState && logfilterState.val) {
63
- // @ts-ignore
64
- logCustomizations.logfilter = String(logfilterState.val).split(';').filter(x => x); // filter removes empty strings here
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 });
61
+ const logfilterState = await this.getStateAsync('info.logfilter');
62
+ if (logfilterState && logfilterState.val) {
63
+ // @ts-ignore
64
+ logCustomizations.logfilter = String(logfilterState.val).split(';').filter(x => x); // filter removes empty strings here
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 });
75
75
 
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
- }
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
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
- });
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
89
 
90
- mqttClient.subscribe('zigbee2mqtt/#');
90
+ mqttClient.subscribe('zigbee2mqtt/#');
91
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);
95
- });
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
- }
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);
95
+ });
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
+ }
103
103
 
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
- }
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
110
 
111
- this.startWebsocket();
112
- }
113
- }
111
+ this.startWebsocket();
112
+ }
113
+ }
114
114
 
115
- startWebsocket() {
116
- websocketController = new WebsocketController(this);
117
- const wsClient = websocketController.initWsClient();
115
+ startWebsocket() {
116
+ websocketController = new WebsocketController(this);
117
+ const wsClient = websocketController.initWsClient();
118
118
 
119
- wsClient.on('open', () => {
120
- this.log.info('Connect to Zigbee2MQTT over websocket connection.');
121
- });
119
+ wsClient.on('open', () => {
120
+ this.log.info('Connect to Zigbee2MQTT over websocket connection.');
121
+ });
122
122
 
123
- wsClient.on('message', (message) => {
124
- this.messageParse(message);
125
- });
123
+ wsClient.on('message', (message) => {
124
+ this.messageParse(message);
125
+ });
126
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
- }
127
+ wsClient.on('close', async () => {
128
+ this.setStateChangedAsync('info.connection', false, true);
129
+ await statesController.setAllAvailableToFalse();
130
+ this.log.warn('Websocket disconnectet');
131
+ });
132
+ }
133
133
 
134
- async messageParse(message) {
135
- const messageObj = JSON.parse(message);
134
+ async messageParse(message) {
135
+ const messageObj = JSON.parse(message);
136
136
 
137
- switch (messageObj.topic) {
138
- case 'bridge/config':
139
- break;
140
- case 'bridge/info':
141
- if (showInfo) {
142
- zigbee2mqttInfo(messageObj.payload, this.log);
143
- checkConfig(messageObj.payload.config, this.log);
144
- showInfo = false;
145
- }
146
- break;
147
- case 'bridge/state':
148
- if (messageObj.payload.state != 'online') {
149
- statesController.setAllAvailableToFalse();
150
- }
151
- this.setStateChangedAsync('info.connection', messageObj.payload.state == 'online', true);
152
- break;
153
- case 'bridge/devices':
154
- await deviceController.createDeviceDefinitions(messageObj.payload);
155
- await deviceController.createOrUpdateDevices();
156
- await statesController.subscribeWritableStates();
157
- statesController.processQueue();
158
- break;
159
- case 'bridge/groups':
160
- await deviceController.createGroupDefinitions(messageObj.payload);
161
- await deviceController.createOrUpdateDevices();
162
- await statesController.subscribeWritableStates();
163
- statesController.processQueue();
164
- break;
165
- case 'bridge/event':
166
- console.log(JSON.stringify(messageObj));
167
- deviceController.processRemoveEvent(messageObj);
168
- break;
169
- case 'bridge/response/device/remove':
170
- deviceController.processRemoveEvent(messageObj);
171
- break;
172
- case 'bridge/extensions':
173
- break;
174
- case 'bridge/logging':
175
- if (this.config.proxyZ2MLogs == true) {
176
- z2mController.proxyZ2MLogs(messageObj);
177
- }
178
- break;
179
- case 'bridge/response/device/rename':
180
- await deviceController.renameDeviceInCache(messageObj);
181
- await deviceController.createOrUpdateDevices();
182
- statesController.processQueue();
183
- break;
184
- case 'bridge/response/networkmap':
185
- break;
186
- case 'bridge/response/touchlink/scan':
187
- break;
188
- case 'bridge/response/touchlink/identify':
189
- break;
190
- case 'bridge/response/touchlink/factory_reset':
191
- break;
192
- default:
193
- {
194
- // {"payload":{"state":"online"},"topic":"FL.Licht.Links/availability"} ----> {"payload":{"available":true},"topic":"FL.Licht.Links"}
195
- if (messageObj.topic.endsWith('/availability')) {
196
- const topicSplit = messageObj.topic.split('/');
137
+ switch (messageObj.topic) {
138
+ case 'bridge/config':
139
+ break;
140
+ case 'bridge/info':
141
+ if (showInfo) {
142
+ zigbee2mqttInfo(messageObj.payload, this.log);
143
+ checkConfig(messageObj.payload.config, this.log);
144
+ showInfo = false;
145
+ }
146
+ break;
147
+ case 'bridge/state':
148
+ if (messageObj.payload.state != 'online') {
149
+ statesController.setAllAvailableToFalse();
150
+ }
151
+ this.setStateChangedAsync('info.connection', messageObj.payload.state == 'online', true);
152
+ break;
153
+ case 'bridge/devices':
154
+ await deviceController.createDeviceDefinitions(messageObj.payload);
155
+ await deviceController.createOrUpdateDevices();
156
+ await statesController.subscribeWritableStates();
157
+ statesController.processQueue();
158
+ break;
159
+ case 'bridge/groups':
160
+ await deviceController.createGroupDefinitions(messageObj.payload);
161
+ await deviceController.createOrUpdateDevices();
162
+ await statesController.subscribeWritableStates();
163
+ statesController.processQueue();
164
+ break;
165
+ case 'bridge/event':
166
+ deviceController.processRemoveEvent(messageObj);
167
+ break;
168
+ case 'bridge/response/device/remove':
169
+ deviceController.processRemoveEvent(messageObj);
170
+ break;
171
+ case 'bridge/extensions':
172
+ break;
173
+ case 'bridge/logging':
174
+ if (this.config.proxyZ2MLogs == true) {
175
+ z2mController.proxyZ2MLogs(messageObj);
176
+ }
177
+ break;
178
+ case 'bridge/response/device/rename':
179
+ await deviceController.renameDeviceInCache(messageObj);
180
+ await deviceController.createOrUpdateDevices();
181
+ statesController.processQueue();
182
+ break;
183
+ case 'bridge/response/networkmap':
184
+ break;
185
+ case 'bridge/response/touchlink/scan':
186
+ break;
187
+ case 'bridge/response/touchlink/identify':
188
+ break;
189
+ case 'bridge/response/touchlink/factory_reset':
190
+ break;
191
+ default:
192
+ {
193
+ // {"payload":{"state":"online"},"topic":"FL.Licht.Links/availability"} ----> {"payload":{"available":true},"topic":"FL.Licht.Links"}
194
+ if (messageObj.topic.endsWith('/availability')) {
195
+ const topicSplit = messageObj.topic.split('/');
197
196
 
198
- // If an availability message for an old device ID comes with a payload of NULL, this is the indicator that a device has been unnamed.
199
- // If this is then still available in the cache, the messages must first be cached.
200
- if (messageObj.payload == 'null') {
201
- break;
202
- }
197
+ // If an availability message for an old device ID comes with a payload of NULL, this is the indicator that a device has been unnamed.
198
+ // If this is then still available in the cache, the messages must first be cached.
199
+ if (messageObj.payload == 'null') {
200
+ break;
201
+ }
203
202
 
204
- if (topicSplit.length == 2 && messageObj.payload && messageObj.payload.state) {
205
- const newMessage = {
206
- payload: { available: messageObj.payload.state == 'online' },
207
- topic: topicSplit[0]
208
- };
209
- statesController.processDeviceMessage(newMessage);
210
- }
211
- // States
212
- } else if (!messageObj.topic.includes('/')) {
213
- statesController.processDeviceMessage(messageObj);
214
- }
215
- }
216
- break;
217
- }
218
- }
203
+ if (topicSplit.length == 2 && messageObj.payload && messageObj.payload.state) {
204
+ const newMessage = {
205
+ payload: { available: messageObj.payload.state == 'online' },
206
+ topic: topicSplit[0]
207
+ };
208
+ statesController.processDeviceMessage(newMessage);
209
+ }
210
+ // States
211
+ } else if (!messageObj.topic.includes('/')) {
212
+ statesController.processDeviceMessage(messageObj);
213
+ //console.log(JSON.stringify(messageObj));
214
+ }
215
+ }
216
+ break;
217
+ }
218
+ }
219
219
 
220
- async onUnload(callback) {
221
- try {
222
- await statesController.setAllAvailableToFalse();
223
- await websocketController.allTimerClear();
224
- await statesController.allTimerClear();
225
- callback();
226
- } catch (e) {
227
- callback();
228
- }
229
- }
220
+ async onUnload(callback) {
221
+ try {
222
+ await statesController.setAllAvailableToFalse();
223
+ await websocketController.allTimerClear();
224
+ await statesController.allTimerClear();
225
+ callback();
226
+ } catch (e) {
227
+ callback();
228
+ }
229
+ }
230
230
 
231
- async onStateChange(id, state) {
232
- if (state && state.ack == false) {
233
- if (id.includes('info.debugmessages')) {
234
- logCustomizations.debugDevices = state.val;
235
- this.setState(id, state.val, true);
236
- return;
237
- }
238
- if (id.includes('info.logfilter')) {
239
- logCustomizations.logfilter = state.val.split(';').filter(x => x); // filter removes empty strings here
240
- this.setState(id, state.val, true);
241
- return;
242
- }
231
+ async onStateChange(id, state) {
232
+ if (state && state.ack == false) {
233
+ if (id.includes('info.debugmessages')) {
234
+ logCustomizations.debugDevices = state.val;
235
+ this.setState(id, state.val, true);
236
+ return;
237
+ }
238
+ if (id.includes('info.logfilter')) {
239
+ logCustomizations.logfilter = state.val.split(';').filter(x => x); // filter removes empty strings here
240
+ this.setState(id, state.val, true);
241
+ return;
242
+ }
243
243
 
244
- const message = await z2mController.createZ2MMessage(id, state) || { topic: '', payload: '' };
244
+ const message = await z2mController.createZ2MMessage(id, state) || { topic: '', payload: '' };
245
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
- }
251
- }
252
- }
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
+ }
251
+ }
252
+ }
253
253
  }
254
254
 
255
255
 
256
256
  if (require.main !== module) {
257
- // Export the constructor in compact mode
258
- /**
259
- * @param {Partial<core.AdapterOptions>} [options={}]
260
- */
261
- module.exports = (options) => new Zigbee2mqtt(options);
257
+ // Export the constructor in compact mode
258
+ /**
259
+ * @param {Partial<core.AdapterOptions>} [options={}]
260
+ */
261
+ module.exports = (options) => new Zigbee2mqtt(options);
262
262
  } else {
263
- // otherwise start the instance directly
264
- new Zigbee2mqtt();
263
+ // otherwise start the instance directly
264
+ new Zigbee2mqtt();
265
265
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.zigbee2mqtt",
3
- "version": "2.1.1",
3
+ "version": "2.2.0",
4
4
  "description": "Zigbee2MQTT adapter for ioBroker",
5
5
  "author": {
6
6
  "name": "Dennis Rathjen",