iobroker.zigbee 2.0.1 → 2.0.3

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.
@@ -3,20 +3,19 @@
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';
17
+
18
+ this.configureKeys = {};
20
19
  }
21
20
 
22
21
  setOptions(options) {
@@ -30,14 +29,42 @@ class DeviceConfigure extends BaseExtension {
30
29
  if (!mappedDevice || !mappedDevice.configure) {
31
30
  return false;
32
31
  }
33
- if (device.meta.hasOwnProperty('configured') &&
34
- zigbeeHerdsmanConverters.getConfigureKey(mappedDevice)) {
35
- return false;
32
+ const delayedAttempts = this.checkDelayedConfigure(device, 0);
33
+ if (!this.configureKeys.hasOwnProperty(device.ieeeAddr)) this.configureKeys[device.ieeeAddr] = zigbeeHerdsmanConverters.getConfigureKey(mappedDevice);
34
+ if (delayedAttempts > 0 && !device.interviewing) return true;
35
+ if (device.meta.hasOwnProperty('configured') && device.meta.configured !== this.configureKeys[device.ieeeAddr]) return true;
36
+ return (device.interviewing !== true && this.checkDelayedConfigure(device)>0);
37
+ }
38
+
39
+ checkDelayedConfigure(device, num) {
40
+ if (!this.delayedConfigure.hasOwnProperty(device.ieeeAddr)) {
41
+ if (num && num > 0) {
42
+ // this.warn('adding dev ' + device.ieeeAddr + ' to delayedConfigure with ' + num + ' attempts');
43
+ this.delayedConfigure[device.ieeeAddr] = { maxAttempts:num };
44
+ return num;
45
+ }
46
+ return 0;
36
47
  }
48
+ const dc = this.delayedConfigure[device.ieeeAddr];
49
+ // this.warn('checkDelayedConfigure for ' + device.ieeeAddr + ' with ' + JSON.stringify(dc));
50
+ dc.maxAttempts--;
51
+ if (dc.maxAttempts > 0) return dc.maxAttempts;
52
+ if (num && num > 0) {
53
+ dc.maxAttempts = num;
54
+ return num;
55
+ }
56
+ return 0;
57
+ }
37
58
 
38
- return device.interviewing !== true;
59
+ delayedConfigureAttempt(device, status) {
60
+ if (status) {
61
+ delete this.delayedConfigure[device.ieeeAddr];
62
+ return 0;
63
+ }
64
+ return this.checkDelayedConfigure(device, 10);
39
65
  }
40
66
 
67
+
41
68
  async onZigbeeStarted() {
42
69
  try {
43
70
  this.coordinatorEndpoint = await this.zigbee.getDevicesByType('Coordinator')[0].endpoints[0];
@@ -58,7 +85,7 @@ class DeviceConfigure extends BaseExtension {
58
85
  }
59
86
  } catch (error) {
60
87
  this.sendError(error);
61
- this.error(`Failed to DeviceConfigure.onZigbeeStarted (${error.stack})`);
88
+ this.error(`Failed to DeviceConfigure.onZigbeeStarted (${error && error.message ? error.message : 'no error message'})`);
62
89
  }
63
90
  }
64
91
 
@@ -66,14 +93,16 @@ class DeviceConfigure extends BaseExtension {
66
93
  try {
67
94
  const device = data.device;
68
95
  if (this.shouldConfigure(device, mappedDevice)) {
96
+ this.debug('ShouldConfigure ' + device.ieeeAddr);
69
97
  this.configure(device, mappedDevice);
70
98
  }
71
99
  } catch (error) {
72
100
  this.sendError(error);
73
- this.error(`Failed to DeviceConfigure.onZigbeeEvent (${error.stack})`);
101
+ this.error(`Failed to DeviceConfigure.onZigbeeEvent (${error && error.message ? error.message : 'no error message'})`);
74
102
  }
75
103
  }
76
104
 
105
+
77
106
  onDeviceRemove(device) {
78
107
  try {
79
108
  if (this.configuring.has(device.ieeeAddr)) {
@@ -85,7 +114,7 @@ class DeviceConfigure extends BaseExtension {
85
114
  }
86
115
  } catch (error) {
87
116
  this.sendError(error);
88
- this.error(`Failed to DeviceConfigure.onDeviceRemove (${error.stack})`);
117
+ this.error(`Failed to DeviceConfigure.onDeviceRemove (${error && error.message ? error.message : 'no error message'})`);
89
118
  }
90
119
  }
91
120
 
@@ -100,23 +129,11 @@ class DeviceConfigure extends BaseExtension {
100
129
  async configure(device, mappedDevice) {
101
130
  try {
102
131
  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
132
  try {
114
- await this.doConfigure(device, mappedDevice);
115
- // this.configuring.delete(device.ieeeAddr);
133
+ await this.doConfigure(device, mappedDevice)
116
134
  } catch (error) {
117
135
  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]++;
136
+ this.warn(`DeviceConfigure failed ${device.ieeeAddr} ${device.modelID}`);
120
137
  }
121
138
  }
122
139
  } catch (error) {
@@ -129,24 +146,41 @@ class DeviceConfigure extends BaseExtension {
129
146
  const coordinatorEndpoint = await this.zigbee.getDevicesByType('Coordinator')[0].endpoints[0];
130
147
  try {
131
148
  if (mappedDevice) {
132
- this.info(`-> Configuring ${device.ieeeAddr} ${device.modelID}`);
133
-
134
- await mappedDevice.configure(device, coordinatorEndpoint, this);
135
-
149
+ if (mappedDevice.configure === undefined) return `No configure available for ${device.ieeeAddr} ${device.modelID}.`;
150
+ this.info(`Configuring ${device.ieeeAddr} ${device.modelID}`);
151
+ if (typeof mappedDevice.configure === 'function') await mappedDevice.configure(device, coordinatorEndpoint, this);
152
+ else {
153
+ const promises = [];
154
+ promises.push(...mappedDevice.configure);
155
+ await Promise.all(promises.map(callback => callback(device, coordinatorEndpoint, mappedDevice)))
156
+ }
136
157
  device.meta.configured = zigbeeHerdsmanConverters.getConfigureKey(mappedDevice);
158
+ this.configureKeys[device.ieeeAddr] = device.meta.configured;
137
159
  device.save();
138
160
  this.info(`DeviceConfigure successful ${device.ieeeAddr} ${device.modelID}`);
161
+ this.delayedConfigureAttempt(device, true);
162
+ return '';
139
163
  }
140
164
  } catch (error) {
141
165
  // https://github.com/Koenkk/zigbee2mqtt/issues/14857
142
166
  if (error.stack.includes('UNSUPPORTED_ATTRIBUTE')) {
143
167
  // do nothing
144
168
  } 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} `);
169
+ if (error && error.message && error.message.match(/(\d+)ms/gm)) {
170
+ // timeout message - we do want to start the configure chain
171
+ const num = this.delayedConfigureAttempt(device, false);
172
+ this.warn(`Timeout trying to configure ${device.ieeeAddr} ${device.modelID}: ${num} attempts remaining`)
173
+ 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.`;
174
+ } else {
175
+ this.sendError(error);
176
+ this.warn(`${device.ieeeAddr} ${device.modelID} Failed to configure. --> ${error && error.message ? error.message : ' no error message given'} `);
177
+ return `${device.ieeeAddr} ${device.modelID} Failed to configure. --> ${error && error.message ? error.message : ' no error message given'} `
178
+ }
179
+
180
+
148
181
  }
149
182
  }
183
+ return 'no return value specified';
150
184
  }
151
185
  }
152
186
 
@@ -73,6 +73,17 @@ class ZigbeeController extends EventEmitter {
73
73
  new DelayedActionExt(this, {}),
74
74
  ];
75
75
  this.herdsmanTimeoutRegexp = new RegExp(/(\d+)ms/);
76
+ this.herdsmanLogSettings = {}
77
+ }
78
+
79
+
80
+ 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('');
76
87
  }
77
88
 
78
89
  configure(options) {
@@ -110,8 +121,9 @@ class ZigbeeController extends EventEmitter {
110
121
  }
111
122
  this.disableLed = options.disableLed;
112
123
  this.warnOnDeviceAnnouncement = options.warnOnDeviceAnnouncement;
113
-
114
- this.debug(`Using zigbee-herdsman with settings: ${JSON.stringify(herdsmanSettings)}`);
124
+ this.herdsmanLogSettings.panID = herdsmanSettings.network.panID;
125
+ this.herdsmanLogSettings.channel = herdsmanSettings.network.channelList[0];
126
+ this.herdsmanLogSettings.extendedPanID = this.ByteArrayToString(herdsmanSettings.network.extendedPanID);
115
127
  this.herdsman = new ZigbeeHerdsman.Controller(herdsmanSettings, this.adapter.log);
116
128
  this.callExtensionMethod('setOptions', [{
117
129
  disableActivePing: options.disablePing,
@@ -136,11 +148,10 @@ class ZigbeeController extends EventEmitter {
136
148
  this.herdsman.on('message', this.handleMessage.bind(this));
137
149
  this.herdsman.on('permitJoinChanged', this.handlePermitJoinChanged.bind(this));
138
150
 
151
+ this.info('Starting Zigbee-Herdsman');
139
152
  await this.herdsman.start();
140
-
141
- this.debug('zigbee-herdsman started');
142
153
  this.herdsmanStarted = true;
143
- this.info(`Coordinator firmware version: ${JSON.stringify(await this.herdsman.getCoordinatorVersion())}`);
154
+ this.info(`Zigbee-Herdsman started successfully with Coordinator firmware version: ${JSON.stringify(await this.herdsman.getCoordinatorVersion())}`);
144
155
 
145
156
  // debug info from herdsman getNetworkParameters
146
157
  const debNetworkParam = JSON.parse(JSON.stringify(await this.herdsman.getNetworkParameters()));
@@ -152,14 +163,29 @@ class ZigbeeController extends EventEmitter {
152
163
  extPanIDDebug += extendedPanIDDebug[i];
153
164
  i--;
154
165
  }
155
-
156
- this.info(`Zigbee network parameters: panID=${debNetworkParam.panID} channel=${debNetworkParam.channel} extendedPanID=${extPanIDDebug}`);
166
+ this.debug(`Network parameters: panID=${debNetworkParam.panID} channel=${debNetworkParam.channel} extendedPanID=${extPanIDDebug}`);
157
167
  } catch (e) {
168
+ try {
169
+ const debNetworkParam = JSON.parse(JSON.stringify(await this.herdsman.getNetworkParameters()));
170
+ const extendedPanIDDebug = typeof debNetworkParam.extendedPanID === 'string' ? debNetworkParam.extendedPanID.replace('0x', '') : debNetworkParam.extendedPanID;
171
+
172
+ let extPanIDDebug = '';
173
+ for (let i = extendedPanIDDebug.length - 1; i >= 0; i--) {
174
+ extPanIDDebug += extendedPanIDDebug[i - 1];
175
+ extPanIDDebug += extendedPanIDDebug[i];
176
+ i--;
177
+ }
178
+ this.warn(`Network parameters in Config : panID=${this.herdsmanLogSettings.panID} channel=${this.herdsmanLogSettings.channel} extendedPanID=${this.herdsmanLogSettings.extendedPanID}`)
179
+ this.warn(`Network parameters on Coordinator: panID=${debNetworkParam.panID} channel=${debNetworkParam.channel} extendedPanID=${extPanIDDebug}`);
180
+ }
181
+ catch (error) {
182
+ this.info(`Unable to obtain herdsman settings`)
183
+ }
158
184
  try {
159
185
  await this.herdsman.stop();
160
186
  }
161
187
  catch (error) {
162
- this.warn(`Starting zigbee-herdsman problem : ${error && error.message ? error.message : 'no error message'}`)
188
+ this.warn('unable to stop zigbee-herdsman after failed startup');
163
189
  }
164
190
  this.sendError(e);
165
191
  this.error(`Starting zigbee-herdsman problem : ${(e && e.message ? e.message : 'no error message')}`);
@@ -207,18 +233,20 @@ class ZigbeeController extends EventEmitter {
207
233
  }
208
234
 
209
235
  // Call extensions
210
- this.callExtensionMethod('onZigbeeStarted', []);
211
236
 
212
237
  const deviceIterator = this.getClientIterator();
213
238
  let deviceCount = 0;
214
239
  try {
215
240
  for (const device of deviceIterator) {
216
241
  deviceCount++;
242
+ // get the model description for the known devices
217
243
  const entity = await this.resolveEntity(device);
218
244
  if (!entity) {
219
245
  this.warn('failed to resolve Entity for ' + device.ieeeAddr);
220
246
  continue;
221
247
  }
248
+ //await this.adapter.stController.AddModelFromHerdsman(device, entity.mapped.model);
249
+
222
250
  this.adapter.getObject(device.ieeeAddr.substr(2), (err, obj) => {
223
251
  if (obj && obj.common && obj.common.deactivated) {
224
252
  this.callExtensionMethod('deregisterDevicePing', [device, entity]);
@@ -248,6 +276,7 @@ class ZigbeeController extends EventEmitter {
248
276
  } else {
249
277
  this.info(`Currently no devices.`);
250
278
  }
279
+ this.callExtensionMethod('onZigbeeStarted', []);
251
280
  }
252
281
  catch (error) {
253
282
  this.error('error iterating devices : '+ (error && error.message ? error.message: 'no reason given'));
@@ -718,7 +747,11 @@ class ZigbeeController extends EventEmitter {
718
747
  this.debug('handleDeviceLeave', message);
719
748
  const entity = await this.resolveEntity(message.device || message.ieeeAddr);
720
749
  const friendlyName = entity ? entity.name : message.ieeeAddr;
721
- this.debug(`Device '${friendlyName}' left the network`);
750
+ if (this.adapter.stController.checkDebugDevice(friendlyName)) {
751
+ this.emit('device_debug', {ID: Date.now(), data: {flag:'dl', states:[{id: '--', value:'--', payload:message}], IO:true},message:`Device '${friendlyName}' has left the network`});
752
+ }
753
+ else
754
+ this.info(`Device '${friendlyName}' left the network`);
722
755
  this.emit('leave', message.ieeeAddr);
723
756
  // Call extensions
724
757
  this.callExtensionMethod(
@@ -735,7 +768,10 @@ class ZigbeeController extends EventEmitter {
735
768
  this.debug('handleDeviceAnnounce', message);
736
769
  const entity = await this.resolveEntity(message.device || message.ieeeAddr);
737
770
  const friendlyName = entity.name;
738
- if (this.warnOnDeviceAnnouncement) {
771
+ if (this.adapter.stController.checkDebugDevice(friendlyName)) {
772
+ this.emit('device_debug', {ID: Date.now(), data: {flag:'da', states:[{id: '--', value:'--', payload:message}] , IO:true} ,message:`Device '${friendlyName}' announced itself`});
773
+ }
774
+ else if (this.warnOnDeviceAnnouncement) {
739
775
  this.warn(`Device '${friendlyName}' announced itself`);
740
776
  } else {
741
777
  this.info(`Device '${friendlyName}' announced itself`);
@@ -854,7 +890,7 @@ class ZigbeeController extends EventEmitter {
854
890
  let resolved = await this.resolveEntity(device, 0);
855
891
  if (!resolved) {
856
892
  resolved = { name:'unresolved device', device:device }
857
- this.warn('resolve Entity failed for ' + device.ieeeAddr)
893
+ this.debug('resolve Entity failed for ' + device.ieeeAddr)
858
894
  }
859
895
  let result;
860
896
 
@@ -914,9 +950,9 @@ class ZigbeeController extends EventEmitter {
914
950
 
915
951
  callback && callback({lqis, routing, errors});
916
952
  if (errors.length) {
917
- this.warn(`Map Data collection complete with ${errors.length} issues:`);
953
+ this.debug(`Map Data collection complete with ${errors.length} issues:`);
918
954
  for (const msg of errors)
919
- this.warn(msg);
955
+ this.debug(msg);
920
956
  }
921
957
  else
922
958
  this.info('Map data collection complete');