iobroker.zigbee 3.0.3 → 3.1.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/lib/utils.js CHANGED
@@ -88,11 +88,13 @@ function decimalToHex(decimal, padding) {
88
88
  }
89
89
 
90
90
  function getZbId(adapterDevId) {
91
- const idx = adapterDevId.indexOf('group');
92
- if (idx > 0) {
93
- return adapterDevId.substr(idx + 6);
91
+ const pieces = adapterDevId.split('.');
92
+ const piece = pieces.length > 2 ? pieces[2] : adapterDevId;
93
+ const idx = piece.indexOf('group_');
94
+ if (idx > -1) {
95
+ return Number(piece.substr(idx + 6));
94
96
  }
95
- return `0x${adapterDevId.split('.')[2]}`;
97
+ return `0x${piece}`;
96
98
  }
97
99
 
98
100
  function getAdId(adapter, id) {
@@ -197,4 +199,4 @@ exports.getDeviceIcon = getDeviceIcon;
197
199
  exports.getEntityInfo = getEntityInfo;
198
200
  exports.getNetAddress = getNetAddress;
199
201
  exports.byteArrayToString = byteArrayToString;
200
- exports.reverseByteString = reverseByteString;
202
+ exports.reverseByteString = reverseByteString;
@@ -24,7 +24,7 @@ const AverageTimeBetweenPings = 45; // on average, plan for 30 seconds between p
24
24
  * This extensions pings devices to check if they are online.
25
25
  */
26
26
  class DeviceAvailability extends BaseExtension {
27
- constructor(zigbee, options) {
27
+ constructor(zigbee, options, config) {
28
28
  super(zigbee, options);
29
29
  this.availability_timeout = 300; // wait 5 min for live check
30
30
  this.timers = {};
@@ -44,12 +44,19 @@ class DeviceAvailability extends BaseExtension {
44
44
  this.startDevicePingQueue = []; // simple fifo array for starting device pings
45
45
  this.startDevicePingTimeout = null; // handle for the timeout which empties the queue
46
46
  this.startDevicePingDelay = 500; // 200 ms delay between starting the ping timeout
47
+ this.startReadDelay = 0;
47
48
  this.name = 'DeviceAvailability';
48
49
  this.elevate_debug = false;
49
50
  this.isStarted = false;
51
+ this.active_ping = !config.disablePing;
52
+ this.forced_ping = false;
53
+ this.max_ping = 5;
54
+ this.availability_timeout = Math.max(60, typeof config.pingTimeout == 'number' ? config.pingTimeout : 300);
55
+ this.startReadDelay = config.readAllAtStart ? Math.max(500, Math.min(10000, config.startReadDelay * 1000)) : 0;
56
+ this.debugDevices = [];
50
57
  }
51
58
 
52
- setOptions(options) {
59
+ /* setOptions(options) {
53
60
  if (typeof options !== 'object') {
54
61
  return false;
55
62
  }
@@ -65,7 +72,26 @@ class DeviceAvailability extends BaseExtension {
65
72
  if (typeof options.pingCount === 'number') {
66
73
  this.max_ping = Math.min(2, options.pingCount);
67
74
  }
75
+ this.startReadDelay = (typeof options.startReadDelay === 'number') ? Math.max(500, Math.min(10000, options.startReadDelay * 1000)) : 0;
76
+ this.warn(`DA: setting options to ${JSON.stringify(options)} - Read Delay is ${this.startReadDelay}`)
68
77
  return true;
78
+ } */
79
+ checkDebugDevice(dev) {
80
+ if (typeof dev != 'string' || dev == '') return false;
81
+ if (this.debugDevices === undefined) return false;
82
+ else
83
+ {
84
+ for (const addressPart of this.debugDevices) {
85
+ if (typeof dev === 'string' && dev.includes(addressPart)) {
86
+ return true;
87
+ }
88
+ }
89
+ }
90
+ return false;
91
+ }
92
+
93
+ setLocalVariable(name, value) {
94
+ this[name] = value;
69
95
  }
70
96
 
71
97
  isPingable(device) {
@@ -132,17 +158,27 @@ class DeviceAvailability extends BaseExtension {
132
158
  }
133
159
  }
134
160
 
161
+ async startNotPingable(device) {
162
+ this.publishAvailability(device, true);
163
+ this.timers[device.ieeeAddr] = setInterval(() =>
164
+ this.handleIntervalNotPingable(device), utils.secondsToMilliseconds(this.availability_timeout));
165
+ }
166
+
135
167
  async onZigbeeStarted() {
136
168
  // As some devices are not checked for availability (e.g. battery powered devices)
137
169
  // we mark these device as online by default.
138
170
  // NOTE: The start of active pings for pingable devices is done separately,
139
171
  // triggered by the 'new device' event to ensure that they are handled
140
172
  // identically on reconnect, disconnect and new pair (as)
141
- const clients = await this.zigbee.getClients();
173
+ const clients = await this.zigbee.getClientIterator();
174
+ const readables = [];
175
+
142
176
  this.isStarted = true;
143
177
  // this.warn('onZigbeeStarted called');
178
+
144
179
  for (const device of clients) {
145
180
  if (this.isPingable(device)) {
181
+ readables.push(device);
146
182
  // this.setTimerPingable(device);
147
183
  } else {
148
184
  // this.warn(`Setting '${device.ieeeAddr}' as available - battery driven`);
@@ -151,17 +187,28 @@ class DeviceAvailability extends BaseExtension {
151
187
  this.handleIntervalNotPingable(device), utils.secondsToMilliseconds(this.availability_timeout));
152
188
  }
153
189
  }
190
+ if (this.startReadDelay > 0 && readables.length > 0) {
191
+ this.warn(`Triggering device_query on ${readables.length} devices in ${this.startReadDelay / 1000} seconds.`)
192
+ setTimeout(() => {
193
+ readables.forEach(device => this.zigbee.doDeviceQuery(device, Date().now, false));
194
+ }, this.startReadDelay)
195
+ }
154
196
  }
155
197
 
156
198
  async handleIntervalPingable(device, entity) {
157
199
  if (!this.isStarted) return;
200
+ const has_elevated_debug = this.checkDebugDevice(device.ieeeAddr)
201
+
158
202
  const ieeeAddr = device.ieeeAddr;
159
203
  const resolvedEntity = entity ? entity : await this.zigbee.resolveEntity(ieeeAddr);
160
204
  if (!resolvedEntity) {
161
- this.warn(`Stop pinging '${ieeeAddr}' ${device.modelID}, device is not known anymore`);
205
+ const msg = `Stop pinging '${ieeeAddr}' ${device.modelID}, device is not known anymore`
206
+ if (has_elevated_debug) this.warn(`ELEVADED: ${msg}`);
207
+ else this.info(msg);
162
208
  return;
163
209
  }
164
210
  if (this.isPingable(device)) {
211
+ const pt = Date.now();
165
212
  let pingCount = this.ping_counters[device.ieeeAddr];
166
213
  if (pingCount === undefined) {
167
214
  this.ping_counters[device.ieeeAddr] = {failed: 0, reported: 0};
@@ -171,22 +218,25 @@ class DeviceAvailability extends BaseExtension {
171
218
  //this.warn(`Pinging '${ieeeAddr}' (${device.modelID})`)
172
219
  await device.ping();
173
220
  this.publishAvailability(device, true);
174
- //this.warn(`Successfully pinged ${ieeeAddr} (${device.modelID})`);
221
+ if (has_elevated_debug) this.warn(`ELEVATED : Successfully pinged ${ieeeAddr} (${device.modelID}) in ${Date.now()-pt} ms`);
175
222
  this.setTimerPingable(device, 1);
176
223
  this.ping_counters[device.ieeeAddr].failed = 0;
177
224
  } catch (error) {
225
+ if (has_elevated_debug) this.warn(`ELEVATED : Failed to ping ${ieeeAddr} (${device.modelID}) after ${Date.now()-pt} ms${error && error.message ? ' - '+error.message : ''}`);
178
226
  this.publishAvailability(device, false);
179
227
  if (pingCount.failed++ <= this.max_ping) {
228
+ const msg = `Failed to ping ${ieeeAddr} ${device.modelID} for ${JSON.stringify(pingCount)} attempts`
180
229
  if (pingCount.failed < 2 && pingCount.reported < this.max_ping) {
181
- this.info(`Failed to ping ${ieeeAddr} ${device.modelID}`);
230
+ if (has_elevated_debug) this.warn(`ELEVATED: ${msg}`); else this.info(msg);
182
231
  pingCount.reported++;
183
232
  } else {
184
- this.debug(`Failed to ping ${ieeeAddr} ${device.modelID} on ${pingCount} consecutive attempts`);
233
+ this.debug(msg);
185
234
  }
186
235
  this.setTimerPingable(device, pingCount.failed);
187
236
  this.ping_counters[device.ieeeAddr] = pingCount;
188
237
  } else {
189
- this.info(`Stopping to ping ${ieeeAddr} ${device.modelID} after ${pingCount.failed} ping attempts`);
238
+ const msg = `Stopping to ping ${ieeeAddr} ${device.modelID} after ${pingCount.failed} ping attempts`;
239
+ if (has_elevated_debug) this.warn(`ELEVATED ${msg}`); else this.info(msg);
190
240
  }
191
241
  }
192
242
  }
@@ -200,11 +250,15 @@ class DeviceAvailability extends BaseExtension {
200
250
  }
201
251
 
202
252
  const ago = Date.now() - entity.device.lastSeen;
203
- this.debug(`Non-pingable device ${entity.device.ieeeAddr} ${entity.device.modelID} was last seen '${ago / 1000}' seconds ago.`);
204
253
 
205
254
  if (ago > Hours25) {
206
255
  this.publishAvailability(entity.device, false);
256
+ const msg = `Non-pingable device ${entity.device.ieeeAddr} ${entity.device.modelID} was last seen over 25 hrs ago - setting it offline.`
257
+ if (this.checkDebugDevice(device.ieeeAddr)) this.warn(`ELEVATED: ${msg}`); else this.debug(msg);
258
+ return;
207
259
  }
260
+ const msg = `Non-pingable device ${entity.device.ieeeAddr} ${entity.device.modelID} was last seen '${ago / 1000}' seconds ago.`
261
+ if (this.checkDebugDevice(device.ieeeAddr)) this.warn(`ELEVATED: ${msg}`); else this.debug(msg);
208
262
  }
209
263
 
210
264
  setTimerPingable(device, factor) {
@@ -236,7 +290,7 @@ class DeviceAvailability extends BaseExtension {
236
290
  for (const key of toZigbeeCandidates) {
237
291
  const converter = entity.mapped.toZigbee.find((tz) => tz.key.includes(key));
238
292
  if (converter && !used.includes(converter)) {
239
- await converter.convertGet(device.endpoints[0], key, {});
293
+ await converter.convertGet(device.endpoints[0], key, {device:entity.device});
240
294
  used.push(converter);
241
295
  }
242
296
  }
@@ -266,7 +320,7 @@ class DeviceAvailability extends BaseExtension {
266
320
  }
267
321
  }
268
322
 
269
- onZigbeeEvent(data) {
323
+ async onZigbeeEvent(data) {
270
324
  const device = data.device;
271
325
  if (!device || this.forcedNonPingable[device.ieeeAddr]) {
272
326
  return;
@@ -287,16 +341,19 @@ class DeviceAvailability extends BaseExtension {
287
341
 
288
342
  const online = this.state.hasOwnProperty(device.ieeeAddr) && this.state[device.ieeeAddr];
289
343
  if (online && data.type === 'deviceAnnounce' && !utils.isIkeaTradfriDevice(device)) {
290
- /**
291
- * In case the device is powered off AND on within the availability timeout,
292
- * zigbee2qmtt does not detect the device as offline (device is still marked online).
293
- * When a device is turned on again the state could be out of sync.
294
- * https://github.com/Koenkk/zigbee2mqtt/issues/1383#issuecomment-489412168
295
- * endDeviceAnnce is typically send when a device comes online.
296
- *
297
- * This isn't needed for TRADFRI devices as they already send the state themself.
298
- */
299
- this.onReconnect(device);
344
+ const entity = await this.zigbee.resolveEntity(device);
345
+ if (entity && entity.mapped) {
346
+ /**
347
+ * In case the device is powered off AND on within the availability timeout,
348
+ * zigbee2qmtt does not detect the device as offline (device is still marked online).
349
+ * When a device is turned on again the state could be out of sync.
350
+ * https://github.com/Koenkk/zigbee2mqtt/issues/1383#issuecomment-489412168
351
+ * endDeviceAnnce is typically send when a device comes online.
352
+ *
353
+ * This isn't needed for TRADFRI devices as they already send the state themself.
354
+ */
355
+ this.onReconnect(device);
356
+ }
300
357
  }
301
358
  }
302
359
  }
@@ -21,7 +21,7 @@ class DeviceEvent extends BaseExtension {
21
21
  }
22
22
 
23
23
  async onZigbeeEvent(data, mappedDevice) {
24
- if (data.device) {
24
+ if (data && data.device && data.type) {
25
25
  this.callOnEvent(data.device, data.type, data, mappedDevice);
26
26
  }
27
27
  }