iobroker.zigbee 2.0.0 → 2.0.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
@@ -106,11 +106,13 @@ function flatten(arr) {
106
106
  flat.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten), []);
107
107
  }
108
108
 
109
- const forceEndDevice = flatten(
109
+ const forceEndDevice = ['QBKG03LM', 'QBKG04LM', 'ZNMS13LM', 'ZNMS12LM'];
110
+
111
+ /*flatten(
110
112
  ['QBKG03LM', 'QBKG04LM', 'ZNMS13LM', 'ZNMS12LM']
111
113
  .map(model => zigbeeHerdsmanConverters.findByModel(model))
112
114
  .map(mappedModel => mappedModel.zigbeeModel));
113
-
115
+ */
114
116
  // Xiaomi uses 4151 and 4447 (lumi.plug) as manufacturer ID.
115
117
  const xiaomiManufacturerID = [4151, 4447];
116
118
  const ikeaTradfriManufacturerID = [4476];
@@ -157,7 +159,7 @@ exports.decimalToHex = decimalToHex;
157
159
  exports.getZbId = getZbId;
158
160
  exports.getAdId = getAdId;
159
161
  exports.getModelRegEx = getModelRegEx;
160
- exports.isRouter = device => device.type === 'Router' && !forceEndDevice.includes(device.modelID);
162
+ exports.isRouter = device => (device.type === 'Router' || (typeof device.powerSource == 'string' && device.powerSource.startsWith('Mains'))) && !forceEndDevice.includes(device.modelID);
161
163
  exports.isBatteryPowered = device => device.powerSource && device.powerSource === 'Battery';
162
164
  exports.isXiaomiDevice = device =>
163
165
  device.modelID !== 'lumi.router' &&
@@ -6,13 +6,7 @@ const utils = require('./utils');
6
6
 
7
7
  // Some EndDevices should be pinged
8
8
  // e.g. E11-G13 https://github.com/Koenkk/zigbee2mqtt/issues/775#issuecomment-453683846
9
- const forcedPingable = [
10
- zigbeeHerdsmanConverters.findByModel('E11-G13'),
11
- zigbeeHerdsmanConverters.findByModel('53170161'),
12
- zigbeeHerdsmanConverters.findByModel('V3-BTZB'),
13
- zigbeeHerdsmanConverters.findByModel('SPZB0001'),
14
- zigbeeHerdsmanConverters.findByModel('014G2461')
15
- ];
9
+ const forcedPingable = ['E11-G13','53170161','V3-BTZB','SPZB0001','014G2461'];
16
10
 
17
11
  // asgothian: 29.12.2020: Removed color and color_temp from readable
18
12
  // state candidates as most states do not provide a getter to ikea_transform
@@ -49,9 +43,10 @@ class DeviceAvailability extends BaseExtension {
49
43
  });
50
44
  this.startDevicePingQueue = []; // simple fifo array for starting device pings
51
45
  this.startDevicePingTimeout = null; // handle for the timeout which empties the queue
52
- this.startDevicePingDelay = 200; // 200 ms delay between starting the ping timeout
46
+ this.startDevicePingDelay = 500; // 200 ms delay between starting the ping timeout
53
47
  this.name = 'DeviceAvailability';
54
48
  this.elevate_debug = false;
49
+ this.isStarted = false;
55
50
  }
56
51
 
57
52
  setOptions(options) {
@@ -74,6 +69,7 @@ class DeviceAvailability extends BaseExtension {
74
69
  }
75
70
 
76
71
  isPingable(device) {
72
+
77
73
  if (this.active_ping) {
78
74
  if (this.forced_ping && forcedPingable.find(d => d && d.hasOwnProperty('zigbeeModel') && d.zigbeeModel.includes(device.modelID))) {
79
75
  return true;
@@ -90,6 +86,7 @@ class DeviceAvailability extends BaseExtension {
90
86
  }
91
87
 
92
88
  async registerDevicePing(device, entity) {
89
+ if (!this.isStarted) return;
93
90
  this.debug(`register device Ping for ${JSON.stringify(device.ieeeAddr)}`);
94
91
  this.forcedNonPingable[device.ieeeAddr] = false;
95
92
  // this.warn(`Called registerDevicePing for '${device}' of '${entity}'`);
@@ -104,6 +101,7 @@ class DeviceAvailability extends BaseExtension {
104
101
  }
105
102
  });
106
103
  this.number_of_registered_devices++;
104
+ this.debug(`registering device Ping (${this.number_of_registered_devices}) for ${device.ieeeAddr} (${device.modelID})`);
107
105
  this.availability_timeout = Math.max(Math.min(this.number_of_registered_devices * AverageTimeBetweenPings, MaxAvailabilityTimeout), MinAvailabilityTimeout);
108
106
  this.startDevicePingQueue.push({device, entity});
109
107
  if (this.startDevicePingTimeout == null) {
@@ -113,7 +111,7 @@ class DeviceAvailability extends BaseExtension {
113
111
  }
114
112
 
115
113
  async deregisterDevicePing(device) {
116
- this.info(`deregister device Ping for deactivated device ${JSON.stringify(device.ieeeAddr)}`);
114
+ this.debug(`deregister device Ping for deactivated device ${JSON.stringify(device.ieeeAddr)}`);
117
115
  this.forcedNonPingable[device.ieeeAddr] = true;
118
116
  if (this.timers[device.ieeeAddr]) {
119
117
  clearTimeout(this.timers[device.ieeeAddr]);
@@ -122,6 +120,7 @@ class DeviceAvailability extends BaseExtension {
122
120
 
123
121
  async startDevicePing() {
124
122
  // this.warn(JSON.stringify(this));
123
+ if (!this.isStarted) return;
125
124
  this.startDevicePingTimeout = null;
126
125
  const item = this.startDevicePingQueue.shift();
127
126
  if (this.startDevicePingQueue.length > 0) {
@@ -140,6 +139,7 @@ class DeviceAvailability extends BaseExtension {
140
139
  // triggered by the 'new device' event to ensure that they are handled
141
140
  // identically on reconnect, disconnect and new pair (as)
142
141
  const clients = await this.zigbee.getClients();
142
+ this.isStarted = true;
143
143
  // this.warn('onZigbeeStarted called');
144
144
  for (const device of clients) {
145
145
  if (this.isPingable(device)) {
@@ -154,10 +154,11 @@ class DeviceAvailability extends BaseExtension {
154
154
  }
155
155
 
156
156
  async handleIntervalPingable(device, entity) {
157
+ if (!this.isStarted) return;
157
158
  const ieeeAddr = device.ieeeAddr;
158
159
  const resolvedEntity = entity ? entity : await this.zigbee.resolveEntity(ieeeAddr);
159
160
  if (!resolvedEntity) {
160
- this.debug(`Stop pinging '${ieeeAddr}' ${device.modelID}, device is not known anymore`);
161
+ this.warn(`Stop pinging '${ieeeAddr}' ${device.modelID}, device is not known anymore`);
161
162
  return;
162
163
  }
163
164
  if (this.isPingable(device)) {
@@ -166,29 +167,11 @@ class DeviceAvailability extends BaseExtension {
166
167
  this.ping_counters[device.ieeeAddr] = {failed: 0, reported: 0};
167
168
  pingCount = {failed: 0, reported: 0};
168
169
  }
169
-
170
- // first see if we can "ping" the device by reading a Status
171
- try {
172
- for (const key of toZigbeeCandidates) {
173
- const converter = resolvedEntity.mapped.toZigbee.find((tz) => tz.key.includes(key));
174
- if (converter) {
175
- await converter.convertGet(device.endpoints[0], key, {});
176
- this.debug(`Successful read state '${key}' of '${device.ieeeAddr}' in stead of pinging`);
177
- this.setTimerPingable(device, 1);
178
- this.ping_counters[device.ieeeAddr].failed = 0;
179
- return;
180
- }
181
- }
182
- } catch (error) {
183
- this.sendError(error);
184
- this.debug(`Exception in readState of '${device.ieeeAddr}' - error : '${(error && error.message ? error.message : 'no error message')}'`);
185
- // intentionally empty: Just present to ensure we cause no harm
186
- // when reading the state fails. => fall back on standard Ping function
187
- }
188
170
  try {
171
+ //this.warn(`Pinging '${ieeeAddr}' (${device.modelID})`)
189
172
  await device.ping();
190
173
  this.publishAvailability(device, true);
191
- this.debug(`Successfully pinged ${ieeeAddr} ${device.modelID}`);
174
+ //this.warn(`Successfully pinged ${ieeeAddr} (${device.modelID})`);
192
175
  this.setTimerPingable(device, 1);
193
176
  this.ping_counters[device.ieeeAddr].failed = 0;
194
177
  } catch (error) {
@@ -210,6 +193,7 @@ class DeviceAvailability extends BaseExtension {
210
193
  }
211
194
 
212
195
  async handleIntervalNotPingable(device) {
196
+ if (!this.isStarted) return;
213
197
  const entity = await this.zigbee.resolveEntity(device.ieeeAddr);
214
198
  if (!entity || !device.lastSeen) {
215
199
  return;
@@ -224,6 +208,7 @@ class DeviceAvailability extends BaseExtension {
224
208
  }
225
209
 
226
210
  setTimerPingable(device, factor) {
211
+ if (!this.isStarted) return;
227
212
  if (factor === undefined || factor < 1) {
228
213
  factor = 1;
229
214
  }
@@ -235,6 +220,7 @@ class DeviceAvailability extends BaseExtension {
235
220
  }
236
221
 
237
222
  async stop() {
223
+ this.isStarted = false;
238
224
  for (const timer of Object.values(this.timers)) {
239
225
  clearTimeout(timer);
240
226
  }
@@ -3,17 +3,14 @@
3
3
  const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
4
4
  const BaseExtension = require('./zbBaseExtension');
5
5
 
6
- const forcedConfigureOnEachStart = [
7
- zigbeeHerdsmanConverters.findByModel('V3-BTZB'),
8
- zigbeeHerdsmanConverters.findByModel('014G2461'),
9
- zigbeeHerdsmanConverters.findByModel('SPZB0001'),
10
- zigbeeHerdsmanConverters.findByModel('ZK03840')
11
- ];
6
+ const forcedConfigureOnEachStart = ['V3-BTZB','014G2461','SPZB0001','ZK03840'];
12
7
 
13
8
  class DeviceConfigure extends BaseExtension {
14
9
  constructor(zigbee, options) {
15
10
  super(zigbee, options);
16
11
 
12
+ this.delayedConfigure = {};
13
+
17
14
  this.configuring = new Set();
18
15
  this.attempts = {};
19
16
  this.name = 'DeviceConfigure';
@@ -35,9 +32,36 @@ class DeviceConfigure extends BaseExtension {
35
32
  return false;
36
33
  }
37
34
 
38
- return device.interviewing !== true;
35
+ return (device.interviewing !== true && this.checkDelayedConfigure(device.ieeeAddr)>0);
36
+ }
37
+
38
+ checkDelayedConfigure(device, num) {
39
+ if (!this.delayedConfigure.hasOwnProperty(device.ieeeAddr)) {
40
+ if (num && num > 0) {
41
+ this.delayedConfigure[device.ieeeAddr] = { maxAttempts:num };
42
+ return num;
43
+ }
44
+ return 0;
45
+ }
46
+ const dc = this.delayedConfigure[device.ieeeAddr];
47
+ dc.maxAttempts--;
48
+ if (dc.maxAttempts > 0) return dc.maxAttempts;
49
+ if (num && num > 0) {
50
+ dc.maxAttempts = num;
51
+ return num;
52
+ }
53
+ return 0;
54
+ }
55
+
56
+ delayedConfigureAttempt(device, status) {
57
+ if (status) {
58
+ delete this.delayedConfigure[device.ieeeAddr];
59
+ return 0;
60
+ }
61
+ return this.checkDelayedConfigure(device, 10);
39
62
  }
40
63
 
64
+
41
65
  async onZigbeeStarted() {
42
66
  try {
43
67
  this.coordinatorEndpoint = await this.zigbee.getDevicesByType('Coordinator')[0].endpoints[0];
@@ -58,7 +82,7 @@ class DeviceConfigure extends BaseExtension {
58
82
  }
59
83
  } catch (error) {
60
84
  this.sendError(error);
61
- this.error(`Failed to DeviceConfigure.onZigbeeStarted (${error.stack})`);
85
+ this.error(`Failed to DeviceConfigure.onZigbeeStarted (${error && error.message ? error.message : 'no error message'})`);
62
86
  }
63
87
  }
64
88
 
@@ -70,7 +94,7 @@ class DeviceConfigure extends BaseExtension {
70
94
  }
71
95
  } catch (error) {
72
96
  this.sendError(error);
73
- this.error(`Failed to DeviceConfigure.onZigbeeEvent (${error.stack})`);
97
+ this.error(`Failed to DeviceConfigure.onZigbeeEvent (${error && error.message ? error.message : 'no error message'})`);
74
98
  }
75
99
  }
76
100
 
@@ -85,7 +109,7 @@ class DeviceConfigure extends BaseExtension {
85
109
  }
86
110
  } catch (error) {
87
111
  this.sendError(error);
88
- this.error(`Failed to DeviceConfigure.onDeviceRemove (${error.stack})`);
112
+ this.error(`Failed to DeviceConfigure.onDeviceRemove (${error && error.message ? error.message : 'no error message'})`);
89
113
  }
90
114
  }
91
115
 
@@ -100,23 +124,11 @@ class DeviceConfigure extends BaseExtension {
100
124
  async configure(device, mappedDevice) {
101
125
  try {
102
126
  if (mappedDevice !== undefined && device !== undefined) {
103
- if (this.configuring.has(device.ieeeAddr) || this.attempts[device.ieeeAddr] >= 5) {
104
- return false;
105
- }
106
-
107
- this.configuring.add(device.ieeeAddr);
108
-
109
- if (!this.attempts.hasOwnProperty(device.ieeeAddr)) {
110
- this.attempts[device.ieeeAddr] = 0;
111
- }
112
-
113
127
  try {
114
- await this.doConfigure(device, mappedDevice);
115
- // this.configuring.delete(device.ieeeAddr);
128
+ await this.doConfigure(device, mappedDevice)
116
129
  } catch (error) {
117
130
  this.sendError(error);
118
- this.warn(`DeviceConfigure failed ${device.ieeeAddr} ${device.modelID} attempt ${this.attempts[device.ieeeAddr] + 1} (${error.stack})`);
119
- this.attempts[device.ieeeAddr]++;
131
+ this.warn(`DeviceConfigure failed ${device.ieeeAddr} ${device.modelID}`);
120
132
  }
121
133
  }
122
134
  } catch (error) {
@@ -130,23 +142,38 @@ class DeviceConfigure extends BaseExtension {
130
142
  try {
131
143
  if (mappedDevice) {
132
144
  this.info(`-> Configuring ${device.ieeeAddr} ${device.modelID}`);
145
+ const promises = [];
146
+ promises.push(mappedDevice.configure);
147
+ await Promise.all(promises.map(callback => callback(device, coordinatorEndpoint, mappedDevice)))
133
148
 
134
- await mappedDevice.configure(device, coordinatorEndpoint, this);
149
+ //await mappedDevice.configure(device, coordinatorEndpoint, this);
135
150
 
136
151
  device.meta.configured = zigbeeHerdsmanConverters.getConfigureKey(mappedDevice);
137
152
  device.save();
138
153
  this.info(`DeviceConfigure successful ${device.ieeeAddr} ${device.modelID}`);
154
+ this.delayedConfigureAttempt(device, true);
155
+ return '';
139
156
  }
140
157
  } catch (error) {
141
158
  // https://github.com/Koenkk/zigbee2mqtt/issues/14857
142
159
  if (error.stack.includes('UNSUPPORTED_ATTRIBUTE')) {
143
160
  // do nothing
144
161
  } else {
145
- this.sendError(error);
146
- this.warn(` ${device.ieeeAddr} ${device.modelID} Failed to configure. When device works is all fine when not wake up the device and check again`);
147
- this.debug(` --> ${error.stack} `);
162
+ if (error && error.message && error.message.match(/(\d+)ms/gm)) {
163
+ // timeout message - we do want to start the configure chain
164
+ const num = this.delayedConfigureAttempt(device, false);
165
+ this.info(`Delayed configure for ${device.ieeeAddr} ${device.modelID}: ${num} attempts remaining`)
166
+ return `Delayed configure for ${device.ieeeAddr} ${device.modelID}: ${num} attempts remaining`;
167
+ } else {
168
+ this.sendError(error);
169
+ this.warn(`${device.ieeeAddr} ${device.modelID} Failed to configure. --> ${error && error.message ? error.message : ' no error message given'} `);
170
+ return `${device.ieeeAddr} ${device.modelID} Failed to configure. --> ${error && error.message ? error.message : ' no error message given'} `
171
+ }
172
+
173
+
148
174
  }
149
175
  }
176
+ return 'no return value specified';
150
177
  }
151
178
  }
152
179
 
@@ -36,22 +36,11 @@ class DeviceEvent extends BaseExtension {
36
36
 
37
37
  async callOnEvent(device, type, data, mappedDevice) {
38
38
  if (!mappedDevice) {
39
- try {
40
- mappedDevice = await zigbeeHerdsmanConverters.findByDevice(device);
41
- }
42
- catch (error) {
43
- this.log.error(`onEvent: unable to find mapped device for ${JSON.stringify(device)} `);
44
- return;
45
- }
39
+ mappedDevice = await zigbeeHerdsmanConverters.findByDevice(device);
46
40
  }
47
41
 
48
42
  if (mappedDevice && mappedDevice.onEvent) {
49
- try {
50
- mappedDevice.onEvent(type, data, device,mappedDevice.options,'{}');
51
- }
52
- catch (error) {
53
- this.log.error(`onEvent for ${JSON.stringify(device)} failed with error ${error.message}`);
54
- }
43
+ mappedDevice.onEvent(type, data, device,mappedDevice.options,'{}');
55
44
  }
56
45
  }
57
46
  }