iobroker.zigbee2mqtt 2.7.1 → 2.7.2

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 CHANGED
@@ -32,6 +32,10 @@ This adapter allows to control the data points of the devices of a Zigbee2MQTT i
32
32
  Placeholder for the next version (at the beginning of the line):
33
33
  ### **WORK IN PROGRESS**
34
34
  -->
35
+ ### 2.7.2 (2023-02-01)
36
+
37
+ - (o0shojo0o) rework of the detection of removed devices
38
+
35
39
  ### 2.7.1 (2023-01-24)
36
40
 
37
41
  - (o0shojo0o) added option for use folder description
package/io-package.json CHANGED
@@ -1,8 +1,21 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "zigbee2mqtt",
4
- "version": "2.7.1",
4
+ "version": "2.7.2",
5
5
  "news": {
6
+ "2.7.2": {
7
+ "en": "rework of the detection of removed devices",
8
+ "de": "nacharbeiten der erkennung von entfernten geräten",
9
+ "ru": "переработка обнаружения удаленных устройств",
10
+ "pt": "retrabalho da detecção de dispositivos removidos",
11
+ "nl": "_",
12
+ "fr": "retravail de la détection des dispositifs enlevés",
13
+ "it": "rielaborazione del rilevamento dei dispositivi rimossi",
14
+ "es": "rework of the detection of removed devices",
15
+ "pl": "przepraszanie detekcji usuniętych urządzeń",
16
+ "uk": "реконструкція виявлення видалених пристроїв",
17
+ "zh-cn": "清除装置的探查工作"
18
+ },
6
19
  "2.7.1": {
7
20
  "en": "added option for use folder description\nuse the iobroker device folder description for device description or events",
8
21
  "de": "zusatzoption für die ordnerbeschreibung\ndie beschreibung des iobroker geräteordners für gerätebeschreibung oder ereignisse",
@@ -80,19 +93,6 @@
80
93
  "pl": "zobacz też: #79 (https:/github.com/o0shojo0o/ioBroker.zigbee2mqtt/issues/79)",
81
94
  "uk": "[79] (https://github.com/o0shojo0o/ioBroker.zigbee2mqtt/issues/79)",
82
95
  "zh-cn": "更好的国家辨认(http://github.com/oshojo0o/ioBroker.zigbee2mqt/issues/79)"
83
- },
84
- "2.4.3": {
85
- "en": "fix availability when `friendly_name` `/` contains",
86
- "de": "verfügbarkeit korrigieren bei friendly_name `/` enthält",
87
- "ru": "исправить доступность когда friendly_name `/` содержит",
88
- "pt": "corrigir disponibilidade quando friendly_name `/` contém",
89
- "nl": "_ _",
90
- "fr": "fixer la disponibilité lorsque friendly_name `/` contient",
91
- "it": "fissare la disponibilità quando amichevole_name `/` contiene",
92
- "es": "fijar disponibilidad cuando friendly_name `/` contiene",
93
- "pl": "ustanowić dostęp do przyjaznej nazwy. zawierać",
94
- "uk": "застосувати доступність при дружній_ім`я `/` в наявності",
95
- "zh-cn": "f 在友好的情况下,可提供“/”一词 内载"
96
96
  }
97
97
  },
98
98
  "titleLang": {
@@ -143,6 +143,7 @@
143
143
  "mode": "daemon",
144
144
  "type": "hardware",
145
145
  "compact": true,
146
+ "stopTimeout": 2000,
146
147
  "connectionType": "local",
147
148
  "dataSource": "push",
148
149
  "adminUI": {
@@ -198,10 +198,13 @@ class DeviceController {
198
198
  this.groupCache.push(newDevice);
199
199
  }
200
200
 
201
- async createGroupDefinitions(exposes) {
201
+ async createGroupDefinitions(groupsMessage) {
202
202
  utils.clearArray(this.groupCache);
203
- for (const expose of exposes) {
204
- await this.defineGroupDevice(expose.friendly_name, `group_${expose.id}`, expose.scenes);
203
+ for (const groupMessage of groupsMessage) {
204
+ if (this.logCustomizations.debugDevices.includes(groupMessage.id)) {
205
+ this.adapter.log.warn(`--->>> fromZ2M -> ${groupMessage.id} exposes: ${JSON.stringify(groupMessage)}`);
206
+ }
207
+ await this.defineGroupDevice(groupMessage.friendly_name, `group_${groupMessage.id}`, groupMessage.scenes);
205
208
  }
206
209
  }
207
210
 
@@ -231,22 +234,26 @@ class DeviceController {
231
234
  type: 'device',
232
235
  common: {
233
236
  name: deviceName,
234
- desc: description
237
+ desc: description,
238
+ statusStates: { onlineId: '' }
235
239
  },
236
-
237
- native: {}
240
+ native: {
241
+ deviceRemoved: false,
242
+ groupDevice: false
243
+ }
238
244
  };
239
245
 
240
- // Only the onlineId is set if the device is not disabled and is not a group
241
- if (!device.ieee_address.includes('group_') && (!device.disabled || device.disabled == false)) {
242
- deviceObj.common.statusStates = {
243
- onlineId: `${this.adapter.name}.${this.adapter.instance}.${device.ieee_address}.available`
244
- };
246
+ // Group Device
247
+ if (device.ieee_address.includes('group_')) {
248
+ deviceObj.native.groupDevice = true;
249
+ }
250
+ // Disabled Device
251
+ else if (device.disabled || device.disabled == true) {
252
+ // Placeholder for possible later logic
245
253
  }
254
+ // Only the onlineId is set if the device is not disabled and is not a group
246
255
  else {
247
- deviceObj.common.statusStates = {
248
- onlineId: ''
249
- };
256
+ deviceObj.common.statusStates.onlineId = `${this.adapter.name}.${this.adapter.instance}.${device.ieee_address}.available`;
250
257
  }
251
258
 
252
259
  //@ts-ignore
@@ -287,40 +294,41 @@ class DeviceController {
287
294
  }
288
295
  }
289
296
 
290
- processRemoveEvent(messageObj) {
291
- let ieee_address = undefined;
297
+ async checkAndProgressDeviceRemove() {
292
298
  let description = '';
293
299
  let deviceName = '';
294
- if (messageObj.payload && messageObj.payload.type == 'device_leave') {
295
- ieee_address = messageObj.payload.data.ieee_address;
296
- }
300
+ let iobDevices = await this.adapter.getDevicesAsync();
301
+ // Do not consider devices already marked as "deviceRemoved"
302
+ iobDevices = iobDevices.filter(x => x.native.deviceRemoved == false);
303
+ // Do not consider groups
304
+ iobDevices = iobDevices.filter(x => x.native.groupDevice == false);
297
305
 
298
- //{"data":{"block":false,"force":true,"id":"0xa4c138c954baaf54"},"status":"ok","transaction":"zhvjf-5"}
299
- if (messageObj.payload && messageObj.payload.data) {
300
- const device = this.deviceCache.find(x => x.id == messageObj.payload.data.id);
301
- if (device) {
302
- ieee_address = device.ieee_address;
303
- deviceName = this.getDeviceName(device);
304
- }
305
- }
306
+ for (const iobDevice of iobDevices) {
307
+ const ieee_address = iobDevice._id.split('.')[2];
308
+ //Check whether the devices found from the object tree are also available in the DeviceCache
309
+ if (!this.deviceCache.find(x => x.ieee_address == ieee_address)) {
310
+ deviceName = iobDevice.common.name;
306
311
 
307
- if (ieee_address != undefined) {
308
- this.adapter.setState(`${ieee_address}.available`, false, true);
309
- deviceName = this.createCache[ieee_address].name;
312
+ if (this.config.useEventInDesc == true) {
313
+ description = 'Device was removed!';
314
+ }
315
+ else {
316
+ deviceName = `[Removed] ${deviceName}`;
317
+ }
310
318
 
311
- if (this.config.useEventInDesc == true) {
312
- description = 'Device was removed!';
313
- }
314
- else {
315
- deviceName = `[Removed] ${deviceName}`;
319
+ this.adapter.extendObject(`${ieee_address}`, {
320
+ common: {
321
+ name: deviceName,
322
+ desc: description
323
+ },
324
+ native: {
325
+ deviceRemoved: true
326
+ }
327
+ });
328
+ this.adapter.setStateChangedAsync(`${ieee_address}.available`, false, true);
329
+
330
+ delete this.createCache[ieee_address];
316
331
  }
317
- this.adapter.extendObject(`${ieee_address}`, {
318
- common: {
319
- name: deviceName,
320
- desc: description
321
- }
322
- });
323
- delete this.createCache[ieee_address];
324
332
  }
325
333
  }
326
334
 
@@ -133,12 +133,17 @@ class StatesController {
133
133
  }
134
134
 
135
135
  async setAllAvailableToFalse() {
136
- for (const device of this.deviceCache) {
137
- for (const state of device.states) {
138
- if (state.id == 'available') {
139
- await this.adapter.setStateChangedAsync(`${device.ieee_address}.${state.id}`, false, true);
140
- }
141
- }
136
+ // for (const device of this.deviceCache) {
137
+ // for (const state of device.states) {
138
+ // if (state.id == 'available') {
139
+ // await this.adapter.setStateChangedAsync(`${device.ieee_address}.${state.id}`, false, true);
140
+ // }
141
+ // }
142
+ // }
143
+
144
+ const availableStates = await this.adapter.getStatesAsync('*.available');
145
+ for (const availableState in availableStates) {
146
+ await this.adapter.setStateChangedAsync(availableState, false, true);
142
147
  }
143
148
  }
144
149
 
package/main.js CHANGED
@@ -153,6 +153,7 @@ class Zigbee2mqtt extends core.Adapter {
153
153
  case 'bridge/devices':
154
154
  await deviceController.createDeviceDefinitions(messageObj.payload);
155
155
  await deviceController.createOrUpdateDevices();
156
+ await deviceController.checkAndProgressDeviceRemove();
156
157
  await statesController.subscribeWritableStates();
157
158
  statesController.processQueue();
158
159
  break;
@@ -163,13 +164,13 @@ class Zigbee2mqtt extends core.Adapter {
163
164
  statesController.processQueue();
164
165
  break;
165
166
  case 'bridge/event':
166
- deviceController.processRemoveEvent(messageObj);
167
167
  break;
168
168
  case 'bridge/response/device/remove':
169
- deviceController.processRemoveEvent(messageObj);
170
169
  break;
171
170
  case 'bridge/response/device/options':
172
171
  break;
172
+ case 'bridge/response/permit_join':
173
+ break;
173
174
  case 'bridge/extensions':
174
175
  break;
175
176
  case 'bridge/logging':
@@ -177,6 +178,8 @@ class Zigbee2mqtt extends core.Adapter {
177
178
  z2mController.proxyZ2MLogs(messageObj);
178
179
  }
179
180
  break;
181
+ case 'bridge/response/device/configure':
182
+ break;
180
183
  case 'bridge/response/device/rename':
181
184
  await deviceController.renameDeviceInCache(messageObj);
182
185
  await deviceController.createOrUpdateDevices();
@@ -210,7 +213,6 @@ class Zigbee2mqtt extends core.Adapter {
210
213
  }
211
214
  // States
212
215
  } else {
213
- //console.log(JSON.stringify(messageObj));
214
216
  statesController.processDeviceMessage(messageObj);
215
217
  }
216
218
  }
@@ -219,33 +221,45 @@ class Zigbee2mqtt extends core.Adapter {
219
221
  }
220
222
 
221
223
  async onUnload(callback) {
222
- try {
224
+ // Close MQTT connections
225
+ if (['exmqtt', 'intmqtt'].includes(this.config.connectionType)) {
223
226
  if (mqttClient && !mqttClient.closed) {
224
- mqttClient.close();
227
+ try {
228
+ mqttClient.close();
229
+ } catch (e) {
230
+ this.log.error(e);
231
+ }
225
232
  }
226
- websocketController.closeConnection();
227
- } catch (e) {
228
- this.log.error(e);
229
233
  }
230
-
231
- try {
232
- mqttServerController.closeServer();
233
- } catch (e) {
234
- this.log.error(e);
234
+ // Internal or Dummy MQTT-Server
235
+ if (this.config.connectionType == 'intmqtt' || this.config.dummyMqtt == true) {
236
+ try {
237
+ mqttServerController.closeServer();
238
+ } catch (e) {
239
+ this.log.error(e);
240
+ }
235
241
  }
236
-
242
+ // Websocket
243
+ else if (this.config.connectionType == 'ws') {
244
+ try {
245
+ websocketController.closeConnection();
246
+ } catch (e) {
247
+ this.log.error(e);
248
+ }
249
+ }
250
+ // Set all device available states of false
237
251
  try {
238
252
  await statesController.setAllAvailableToFalse();
239
253
  } catch (e) {
240
254
  this.log.error(e);
241
255
  }
242
-
256
+ // Clear all websocket timers
243
257
  try {
244
258
  await websocketController.allTimerClear();
245
259
  } catch (e) {
246
260
  this.log.error(e);
247
261
  }
248
-
262
+ // Clear all state timers
249
263
  try {
250
264
  await statesController.allTimerClear();
251
265
  } catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.zigbee2mqtt",
3
- "version": "2.7.1",
3
+ "version": "2.7.2",
4
4
  "description": "Zigbee2MQTT adapter for ioBroker",
5
5
  "author": {
6
6
  "name": "Dennis Rathjen",
@@ -42,7 +42,7 @@
42
42
  "@types/sinon-chai": "^3.2.9",
43
43
  "chai": "^4.3.7",
44
44
  "chai-as-promised": "^7.1.1",
45
- "eslint": "^8.32.0",
45
+ "eslint": "^8.33.0",
46
46
  "eslint-config-prettier": "^8.6.0",
47
47
  "eslint-plugin-prettier": "^4.2.1",
48
48
  "mocha": "^10.2.0",