iobroker.zigbee 2.0.4 → 2.0.5

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.
@@ -12,72 +12,75 @@ class DeviceConfigure extends BaseExtension {
12
12
  this.delayedConfigure = {};
13
13
 
14
14
  this.configuring = new Set();
15
- this.attempts = {};
15
+ this.configureOnMessage = new Set();
16
+ this.configureOnMessageAttempts = {};
16
17
  this.name = 'DeviceConfigure';
18
+
19
+ this.MessageStash = [];
20
+ this.deviceConfigureQueue = [];
21
+ this.configureIntervall = null;
17
22
  }
18
23
 
19
24
  setOptions(options) {
20
25
  return typeof options === 'object';
21
26
  }
22
27
 
23
- shouldConfigure(device, mappedDevice) {
24
- if (!device || !mappedDevice) {
25
- return false;
26
- }
27
- if (!mappedDevice || !mappedDevice.configure) {
28
- return false;
28
+ async handleConfigureQueue() {
29
+ if (this.deviceConfigureQueue.length < 1) {
30
+ this.info('Handled all devices Queued for configuration.')
31
+ clearInterval(this.configureIntervall);
32
+ this.configureIntervall == null;
33
+ return;
29
34
  }
30
- if (device.meta.hasOwnProperty('configured') &&
31
- zigbeeHerdsmanConverters.getConfigureKey(mappedDevice)) {
32
- return false;
33
- }
34
-
35
- return (device.interviewing !== true && this.checkDelayedConfigure(device.ieeeAddr)>0);
35
+ const configureItem = this.deviceConfigureQueue.shift();
36
+ this.info(`DeviceConfigureQueue configuring ${configureItem.dev.ieeeAddr} ${configureItem.dev.modelID}`)
37
+ this.doConfigure(configureItem.dev, configureItem.mapped);
36
38
  }
37
39
 
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;
40
+ PushDeviceToQueue(device, mappedDevice) {
41
+ const id = device.ieeeAddr;
42
+ for (const candidate of this.deviceConfigureQueue) {
43
+ if (candidate.id == id) {
44
+ this.debug('no duplicate entry in queue');
45
+ return;
43
46
  }
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
47
  }
53
- return 0;
48
+ this.deviceConfigureQueue.push({ id: device.ieeeAddr, dev:device, mapped:mappedDevice });
49
+ if (this.configureIntervall) return;
50
+ this.configureIntervall = setInterval(async () => await this.handleConfigureQueue(), 5000);
54
51
  }
55
52
 
56
- delayedConfigureAttempt(device, status) {
57
- if (status) {
58
- delete this.delayedConfigure[device.ieeeAddr];
59
- return 0;
53
+ shouldConfigure(device, mappedDevice) {
54
+ if (!device || !mappedDevice) {
55
+ return false;
60
56
  }
61
- return this.checkDelayedConfigure(device, 10);
57
+ if (!mappedDevice || !mappedDevice.configure) {
58
+ return false;
59
+ }
60
+ // no configuration if we are interviewing or configuring
61
+ if (this.configuring.has(device.ieeeAddr) || device.interviewing) return false;
62
+ const t = Date.now();
63
+ const cfgkey = zigbeeHerdsmanConverters.getConfigureKey(mappedDevice);
64
+ const result = device.meta.hasOwnProperty('configured') && device.meta.configured !== cfgkey;
65
+ this.debug(`should configure for device ${device.ieeeAddr} (${mappedDevice.model}: ${device.meta.hasOwnProperty('configured') ? device.meta.configured: 'none'} - ${cfgkey} (query took ${Date.now()- t} ms)`);
66
+ return result;
62
67
  }
63
68
 
64
-
65
69
  async onZigbeeStarted() {
66
70
  try {
67
71
  this.coordinatorEndpoint = await this.zigbee.getDevicesByType('Coordinator')[0].endpoints[0];
68
72
 
69
73
  for (const device of await this.zigbee.getClients()) {
70
74
  const mappedDevice = await zigbeeHerdsmanConverters.findByDevice(device);
71
-
72
75
  if (forcedConfigureOnEachStart.find((d) => d && d.hasOwnProperty('zigbeeModel') && d.zigbeeModel.includes(device.modelID))) {
73
- this.debug(`DeviceConfigure ${device.ieeeAddr} ${device.modelID} forced by adapter config`);
76
+ this.debug(`DeviceConfigure ${device.ieeeAddr} ${mappedDevice ? mappedDevice.model : device.modelID} forced by adapter config`);
74
77
  device.meta.configured = -1; // Force a reconfiguration for this device
75
78
  }
76
79
  if (this.shouldConfigure(device, mappedDevice)) {
77
- this.debug(`DeviceConfigure ${device.ieeeAddr} ${device.modelID} needed`);
78
- await this.configure(device, mappedDevice);
80
+ this.info(`DeviceConfigure ${device.ieeeAddr} ${mappedDevice ? mappedDevice.model : device.modelID} needed - Device added to Configuration Queue`);
81
+ await this.PushDeviceToQueue(device, mappedDevice);
79
82
  } else {
80
- this.debug(`DeviceConfigure ${device.ieeeAddr} ${device.modelID} not needed`);
83
+ this.debug(`DeviceConfigure ${device.ieeeAddr} ${mappedDevice ? mappedDevice.model : device.modelID} not needed`);
81
84
  }
82
85
  }
83
86
  } catch (error) {
@@ -89,8 +92,14 @@ class DeviceConfigure extends BaseExtension {
89
92
  onZigbeeEvent(data, mappedDevice) {
90
93
  try {
91
94
  const device = data.device;
92
- if (this.shouldConfigure(device, mappedDevice)) {
93
- this.configure(device, mappedDevice);
95
+ const com = this.configureOnMessageAttempts[device.ieeeAddr];
96
+ if (com) {
97
+ this.info(`checking configure on message : next attempt in ${30000 - (Date.now() - com.timestamp)} seconds`);
98
+ if (Date.now() - com.timestamp > 30000 && !this.configuring.has(device.ieeeAddr)) {
99
+ com.timestamp = Date.now();
100
+ this.info('Configure on Message for ' + device.ieeeAddr);
101
+ this.doConfigure(device, mappedDevice);
102
+ }
94
103
  }
95
104
  } catch (error) {
96
105
  this.sendError(error);
@@ -98,14 +107,15 @@ class DeviceConfigure extends BaseExtension {
98
107
  }
99
108
  }
100
109
 
110
+
101
111
  onDeviceRemove(device) {
102
112
  try {
103
113
  if (this.configuring.has(device.ieeeAddr)) {
104
114
  this.configuring.delete(device.ieeeAddr);
105
115
  }
106
116
 
107
- if (this.attempts.hasOwnProperty(device.ieeeAddr)) {
108
- delete this.attempts[device.ieeeAddr];
117
+ if (this.configureOnMessageAttempts && this.configureOnMessageAttempts.hasOwnProperty(device.ieeeAddr)) {
118
+ delete this.configureOnMessageAttempts[device.ieeeAddr];
109
119
  }
110
120
  } catch (error) {
111
121
  this.sendError(error);
@@ -124,16 +134,17 @@ class DeviceConfigure extends BaseExtension {
124
134
  async configure(device, mappedDevice) {
125
135
  try {
126
136
  if (mappedDevice !== undefined && device !== undefined) {
137
+
127
138
  try {
128
139
  await this.doConfigure(device, mappedDevice)
129
140
  } catch (error) {
130
141
  this.sendError(error);
131
- this.warn(`DeviceConfigure failed ${device.ieeeAddr} ${device.modelID}`);
142
+ this.warn(`DeviceConfigure failed ${device.ieeeAddr} ${mappedDevice.model}`);
132
143
  }
133
144
  }
134
145
  } catch (error) {
135
146
  this.sendError(error);
136
- this.error(`Failed to DeviceConfigure.configure ${device.ieeeAddr} ${device.modelID} stack: (${error.stack})`);
147
+ this.error(`Failed to DeviceConfigure.configure ${device.ieeeAddr} ${mappedDevice.model}: ${error && error.message ? error.message : 'no error message'})`);
137
148
  }
138
149
  }
139
150
 
@@ -141,33 +152,51 @@ class DeviceConfigure extends BaseExtension {
141
152
  const coordinatorEndpoint = await this.zigbee.getDevicesByType('Coordinator')[0].endpoints[0];
142
153
  try {
143
154
  if (mappedDevice) {
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)))
148
-
149
- //await mappedDevice.configure(device, coordinatorEndpoint, this);
150
-
155
+ if (mappedDevice.configure === undefined) return `No configure available for ${device.ieeeAddr} ${mappedDevice.model}.`;
156
+ this.info(`Configuring ${device.ieeeAddr} ${mappedDevice.model}`);
157
+ this.configuring.add(device.ieeeAddr);
158
+ if (typeof mappedDevice.configure === 'function') await mappedDevice.configure(device, coordinatorEndpoint, this);
159
+ else {
160
+ const promises = [];
161
+ promises.push(...mappedDevice.configure);
162
+ await Promise.all(promises.map(callback => callback(device, coordinatorEndpoint, mappedDevice)))
163
+ }
151
164
  device.meta.configured = zigbeeHerdsmanConverters.getConfigureKey(mappedDevice);
165
+ this.configuring.delete(device.ieeeAddr);
166
+ delete this.configureOnMessageAttempts[device.ieeeAddr];
152
167
  device.save();
153
- this.info(`DeviceConfigure successful ${device.ieeeAddr} ${device.modelID}`);
154
- this.delayedConfigureAttempt(device, true);
168
+ this.info(`DeviceConfigure successful ${device.ieeeAddr} ${mappedDevice.model}`);
155
169
  return '';
156
170
  }
157
171
  } catch (error) {
172
+ this.configuring.delete(device.ieeeAddr);
158
173
  // https://github.com/Koenkk/zigbee2mqtt/issues/14857
159
174
  if (error.stack.includes('UNSUPPORTED_ATTRIBUTE')) {
175
+ this.debug(`Configuration attempt on ${device.ieeeAddr} ${mappedDevice.model} with unsupported Attribute(s) : ${error.message}`)
160
176
  // do nothing
161
177
  } else {
162
178
  if (error && error.message && error.message.match(/(\d+)ms/gm)) {
163
179
  // 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`;
180
+ if (this.configureOnMessageAttempts.hasOwnProperty(device.ieeeAddr)) {
181
+ const com = this.configureOnMessageAttempts[device.ieeeAddr];
182
+ com.count--;
183
+ com.timestamp = Date.now();
184
+ this.info(`Timeout trying to configure ${device.ieeeAddr} ${mappedDevice.model} (${com.count}).`)
185
+ if ( com.count < 0) delete this.configureOnMessage[device.ieeeAddr];
186
+ }
187
+ else {
188
+ this.info(`Timeout trying to configure ${device.ieeeAddr} ${mappedDevice.model} (starting CoM).`)
189
+ this.configureOnMessageAttempts[device.ieeeAddr] = {
190
+ count: 5,
191
+ timestamp: 0,
192
+ };
193
+ }
194
+ return `Configuration timed out ${device.ieeeAddr} ${device.modelID}. The device did not repond in time to the configuration request. Another attempt will be made when the device is awake.`;
167
195
  } else {
168
196
  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'} `
197
+ const msg = `${device.ieeeAddr} ${device.modelID} Failed to configure. --> ${error && error.message ? error.message : ' no error message given'}`
198
+ this.warn(msg);
199
+ return msg;
171
200
  }
172
201
 
173
202
 
@@ -175,6 +204,11 @@ class DeviceConfigure extends BaseExtension {
175
204
  }
176
205
  return 'no return value specified';
177
206
  }
207
+
208
+ async stop() {
209
+ clearInterval(this.configureIntervall);
210
+ this.configureOnMessageAttempts = {};
211
+ }
178
212
  }
179
213
 
180
214
  module.exports = DeviceConfigure;
@@ -65,6 +65,7 @@ class ZigbeeController extends EventEmitter {
65
65
  super();
66
66
  this.adapter = adapter;
67
67
  this._permitJoinTime = 0;
68
+ this.transmitPower = 0;
68
69
  this.herdsmanStarted = false;
69
70
  this.extensions = [
70
71
  new DeviceAvailabilityExt(this, {}),
@@ -74,19 +75,41 @@ class ZigbeeController extends EventEmitter {
74
75
  ];
75
76
  this.herdsmanTimeoutRegexp = new RegExp(/(\d+)ms/);
76
77
  this.herdsmanLogSettings = {}
78
+
77
79
  }
78
80
 
79
81
 
80
82
  ByteArrayToString(data) {
81
- return data.map(function (x) {
82
- x = x + 0x100 + 1; // twos complement
83
- x = x.toString(16); // to hex
84
- x = ('00'+x).substr(-2); // zero-pad to 8-digits
85
- return x
86
- }).join('');
83
+ if (data) {
84
+ return data.map(function (x) {
85
+ x = x + 0x100 + 1; // twos complement
86
+ x = x.toString(16); // to hex
87
+ x = ('00'+x).substr(-2); // zero-pad to 8-digits
88
+ return x
89
+ }).join('');
90
+ }
91
+ else return '';
87
92
  }
88
93
 
89
94
  configure(options) {
95
+
96
+ if (options.transmitPower != undefined) {
97
+ this.transmitPower = options.transmitPower;
98
+ }
99
+
100
+ this.powerText = '';
101
+ if (this.transmitPower !== '0') {
102
+ const powerLevels = {
103
+ '-22': 'low',
104
+ '19': 'high',
105
+ '20': 'high+'
106
+ };
107
+
108
+ this.powerText = powerLevels[this.transmitPower] || 'normal';
109
+ }
110
+
111
+ this.info(` --> transmitPower : ${this.powerText}`);
112
+
90
113
  const herdsmanSettings = {
91
114
  network: {
92
115
  panID: options.net.panId,
@@ -102,6 +125,7 @@ class ZigbeeController extends EventEmitter {
102
125
  path: options.sp.port,
103
126
  adapter: options.sp.adapter,
104
127
  },
128
+ transmitpower: this.transmitPower,
105
129
  adapter: {
106
130
  forceStartWithInconsistentAdapterConfiguration: options.startWithInconsistent
107
131
  },
@@ -114,11 +138,6 @@ class ZigbeeController extends EventEmitter {
114
138
  herdsmanSettings.network.extenedPanID = options.net.extPanId;
115
139
  }
116
140
 
117
- if (options.transmitPower == undefined) {
118
- this.transmitPower = 0;
119
- } else {
120
- this.transmitPower = options.transmitPower;
121
- }
122
141
  this.disableLed = options.disableLed;
123
142
  this.warnOnDeviceAnnouncement = options.warnOnDeviceAnnouncement;
124
143
  this.herdsmanLogSettings.panID = herdsmanSettings.network.panID;
@@ -179,7 +198,7 @@ class ZigbeeController extends EventEmitter {
179
198
  this.warn(`Network parameters on Coordinator: panID=${debNetworkParam.panID} channel=${debNetworkParam.channel} extendedPanID=${extPanIDDebug}`);
180
199
  }
181
200
  catch (error) {
182
- this.warn(`Unable to obtain herdsman settings`)
201
+ this.info(`Unable to obtain herdsman settings`)
183
202
  }
184
203
  try {
185
204
  await this.herdsman.stop();
@@ -204,35 +223,6 @@ class ZigbeeController extends EventEmitter {
204
223
  this.sendError(e);
205
224
  }
206
225
 
207
- // only for CC1352P and CC26X2R1 transmit power
208
- let powerText = 'normal';
209
-
210
- if (this.transmitPower != '0') {
211
- switch (this.transmitPower) {
212
- case '-22':
213
- powerText = 'low';
214
- break;
215
- case '19':
216
- powerText = 'high';
217
- break;
218
- case '20':
219
- powerText = 'high+';
220
- break;
221
- default:
222
- powerText = 'normal';
223
- }
224
- }
225
-
226
-
227
- this.info(` --> transmitPower : ${powerText}`);
228
- try {
229
- await this.herdsman.setTransmitPower(this.transmitPower);
230
- } catch (e) {
231
- this.sendError(e);
232
- this.info('Unable to set transmit power, unsupported function.');
233
- }
234
-
235
- // Call extensions
236
226
 
237
227
  const deviceIterator = this.getClientIterator();
238
228
  let deviceCount = 0;
@@ -575,6 +565,7 @@ class ZigbeeController extends EventEmitter {
575
565
  if (_key.kind === 'ieee') _key.key = await this.herdsman.getDeviceByIeeeAddr(_key.key);
576
566
  const device = _key.key;
577
567
  if (device) {
568
+ const t = Date.now();
578
569
  const mapped = await zigbeeHerdsmanConverters.findByDevice(device);
579
570
  const endpoints = mapped && mapped.endpoint ? mapped.endpoint(device) : null;
580
571
  let endpoint;
@@ -747,7 +738,11 @@ class ZigbeeController extends EventEmitter {
747
738
  this.debug('handleDeviceLeave', message);
748
739
  const entity = await this.resolveEntity(message.device || message.ieeeAddr);
749
740
  const friendlyName = entity ? entity.name : message.ieeeAddr;
750
- this.debug(`Device '${friendlyName}' left the network`);
741
+ if (this.adapter.stController.checkDebugDevice(friendlyName)) {
742
+ this.emit('device_debug', {ID: Date.now(), data: {flag:'dl', states:[{id: '--', value:'--', payload:message}], IO:true},message:`Device '${friendlyName}' has left the network`});
743
+ }
744
+ else
745
+ this.info(`Device '${friendlyName}' left the network`);
751
746
  this.emit('leave', message.ieeeAddr);
752
747
  // Call extensions
753
748
  this.callExtensionMethod(
@@ -764,7 +759,10 @@ class ZigbeeController extends EventEmitter {
764
759
  this.debug('handleDeviceAnnounce', message);
765
760
  const entity = await this.resolveEntity(message.device || message.ieeeAddr);
766
761
  const friendlyName = entity.name;
767
- if (this.warnOnDeviceAnnouncement) {
762
+ if (this.adapter.stController.checkDebugDevice(friendlyName)) {
763
+ this.emit('device_debug', {ID: Date.now(), data: {flag:'da', states:[{id: '--', value:'--', payload:message}] , IO:true} ,message:`Device '${friendlyName}' announced itself`});
764
+ }
765
+ else if (this.warnOnDeviceAnnouncement) {
768
766
  this.warn(`Device '${friendlyName}' announced itself`);
769
767
  } else {
770
768
  this.info(`Device '${friendlyName}' announced itself`);
@@ -883,7 +881,7 @@ class ZigbeeController extends EventEmitter {
883
881
  let resolved = await this.resolveEntity(device, 0);
884
882
  if (!resolved) {
885
883
  resolved = { name:'unresolved device', device:device }
886
- this.warn('resolve Entity failed for ' + device.ieeeAddr)
884
+ this.debug('resolve Entity failed for ' + device.ieeeAddr)
887
885
  }
888
886
  let result;
889
887
 
@@ -943,9 +941,9 @@ class ZigbeeController extends EventEmitter {
943
941
 
944
942
  callback && callback({lqis, routing, errors});
945
943
  if (errors.length) {
946
- this.warn(`Map Data collection complete with ${errors.length} issues:`);
944
+ this.debug(`Map Data collection complete with ${errors.length} issues:`);
947
945
  for (const msg of errors)
948
- this.warn(msg);
946
+ this.debug(msg);
949
947
  }
950
948
  else
951
949
  this.info('Map data collection complete');