iobroker.zigbee 2.0.5 → 3.0.0

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/commands.js CHANGED
@@ -1,12 +1,15 @@
1
1
  'use strict';
2
2
 
3
3
  const getZbId = require('./utils').getZbId;
4
+ const getNetAddress = require('./utils').getNetAddress;
5
+ const reverseByteString = require('./utils').reverseByteString;
4
6
  const fs = require('fs');
5
- const pathLib = require('path');
6
7
  const statesMapping = require('./devices');
7
8
  const utils = require('@iobroker/adapter-core'); // Get common adapter utils
8
9
  const colors = require('./colors.js');
9
- const { exec } = require('child_process');
10
+ const dns = require('dns');
11
+ const net = require('net');
12
+ const { access, constants } =require('fs');
10
13
 
11
14
  const disallowedDashStates = [
12
15
  'link_quality', 'available', 'battery', 'groups', 'device_query',
@@ -50,13 +53,14 @@ class Commands {
50
53
  * @param {ioBroker.Message} obj
51
54
  */
52
55
  onMessage(obj) {
53
- if (obj) {
54
- if (typeof obj === 'object' && obj.command) {
56
+ if (typeof obj === 'object' && obj.command) {
57
+ if (obj) {
55
58
  switch (obj.command) {
56
- case 'reset':
57
- if (obj.message && obj.message.mode == 'delNvbackup') {
58
- this.delNvBackup(obj.from, obj.command, obj.message, obj.callback);
59
- }
59
+ case 'testConnect':
60
+ this.adapter.testConnect(obj.from, obj.command, obj.message, obj.callback);
61
+ break;
62
+ case 'deleteNVBackup':
63
+ this.delNvBackup(obj.from, obj.command, {}, obj.callback);
60
64
  break;
61
65
  case 'letsPairing':
62
66
  if (obj.message && typeof obj.message === 'object') {
@@ -89,7 +93,7 @@ class Commands {
89
93
  }
90
94
  break;
91
95
  case 'getCoordinatorInfo':
92
- if (obj && obj.message && typeof obj.message === 'object') {
96
+ if (obj.message && typeof obj.message === 'object') {
93
97
  this.getCoordinatorInfo(obj.from, obj.command, obj.callback);
94
98
  }
95
99
  break;
@@ -151,25 +155,81 @@ class Commands {
151
155
  }
152
156
  break;
153
157
  case 'getDeviceCleanupRequired':
154
- if (obj) {
155
- this.adapter.sendTo(obj.from, obj.command, {clean:this.stController.CleanupRequired(), errors:this.stController.getStashedErrors()}, obj.callback);
156
- }
158
+ if (this.stController) this.adapter.sendTo(obj.from, obj.command, {clean:this.stController.CleanupRequired(), errors:this.stController.getStashedErrors()}, obj.callback);
159
+ // NO Break - returning the debug-data as well is intentional
157
160
  case 'getDebugMessages':
158
- if (obj) {
159
- this.adapter.sendTo(obj.from, obj.command, {byId:this.adapter.deviceDebug.collectDebugData( obj.message.inlog)},obj.callback);
160
- }
161
+ this.adapter.sendTo(obj.from, obj.command, {byId:this.adapter.deviceDebug.collectDebugData( obj.message.inlog)},obj.callback);
162
+ break;
163
+ case 'testConnection':
164
+ this.testConnection(obj.from, obj.command, obj.message, obj.callback);
165
+ break;
166
+ case 'readNVRam':
167
+ this.readNvBackup(obj.from, obj.command, obj.message, obj.callback);
168
+ default:
169
+ //this.warn(`Commands: Command ${obj.command} is unknown`);
170
+ //this.adapter.sendTo(obj.from, obj.command, obj.message, obj.callback);
171
+ break;
161
172
  }
162
173
  }
163
174
  }
164
175
  }
165
176
 
166
- delNvBackup(from, command, msg, callback) {
177
+ async readNvBackup(from, command, msg, callback) {
178
+ this.warn('readNvBackup called')
179
+ try {
180
+ const zo = this.adapter.getZigbeeOptions();
181
+ const name = require('path').join(zo.dbDir, zo.backupPath);
182
+ const nvbackup = fs.readFileSync(name, {encoding: 'utf8'}).toString();
183
+ const nvBackupJson = JSON.parse(nvbackup);
184
+ const rv = {};
185
+ rv.channel = nvBackupJson.channel;
186
+ rv.precfgkey = (nvBackupJson.network_key ? nvBackupJson.network_key.key : undefined);
187
+ rv.extPanID = nvBackupJson.extended_pan_id ? reverseByteString(nvBackupJson.extended_pan_id) : undefined;
188
+ rv.panID = parseInt('0x'+nvBackupJson.pan_id);
189
+ this.warn('readNvBackup returns ' + JSON.stringify(rv))
190
+ this.adapter.sendTo(from, command, rv, callback)
191
+ }
192
+ catch (error) {
193
+ const msg = `Unable to read nvBackup ${error && error.message ? error.message : 'no message given'}`;
194
+ this.error(msg);
195
+ this.adapter.sendTo(from, command, {error:msg}, callback)
196
+ }
197
+ }
198
+
199
+ async delNvBackup(from, command, msg, callback) {
167
200
  try {
168
- if (this.zbController) {
201
+ if (this.zbController)
202
+ {
203
+ // stop the herdsman if needed
204
+ const wasRunning = this.zbController.herdsmanStarted;
205
+ if (wasRunning) await this.zbController.stop();
169
206
  const name = this.zbController.herdsman.adapter.backupPath;
170
- require('fs').unlinkSync(name);
207
+ fs.unlink(name, async (err) => {
208
+ const rv={};
209
+ if (err) {
210
+ this.error(`Unable to remove ${name}: ${err}`);
211
+ rv.error = `Unable to remove ${name}: ${err}`;
212
+ }
213
+ // start the herdsman again if it was stopped before
214
+ if (wasRunning) await this.zbController.start();
215
+ this.adapter.sendTo(from, command, rv, callback)
216
+ });
217
+ }
218
+ else {
219
+ const zo = this.adapter.getZigbeeOptions();
220
+ const name = require('path').join(zo.dbDir, zo.backupPath);
221
+ fs.unlink(name, async (err) => {
222
+ const rv={};
223
+ if (err) {
224
+ this.error(`Unable to remove ${name}: ${err}`);
225
+ rv.error = `Unable to remove ${name}: ${err}`;
226
+ }
227
+ // start the herdsman again if it was stopped before
228
+ this.adapter.sendTo(from, command, rv, callback)
229
+ });
171
230
  }
172
231
  } catch (error) {
232
+ this.adapter.sendTo(from, command, {error: error.message}, callback)
173
233
  this.error(error);
174
234
  }
175
235
  }
@@ -231,7 +291,7 @@ class Commands {
231
291
  }
232
292
 
233
293
  touchlinkReset(from, command, message, callback) {
234
- if (this.zbController) {
294
+ if (this.zbController && this.zbController.herdsmanStarted) {
235
295
  // allow devices to join the network within 60 secs
236
296
  this.adapter.logToPairing('Touchlink reset started ', true);
237
297
 
@@ -245,14 +305,14 @@ class Commands {
245
305
  } else {
246
306
  this.adapter.sendTo(
247
307
  from, command,
248
- {error: 'You need to setup serial port and start the adapter before pairing!'},
308
+ {error: 'No active connection to Zigbee Hardware!'},
249
309
  callback
250
310
  );
251
311
  }
252
312
  }
253
313
 
254
314
  async getDevices(from, command, id, callback) {
255
- if (this.zbController) {
315
+ if (this.zbController && this.zbController.herdsmanStarted) {
256
316
  this.debug(`getDevices called from ${from} with command ${JSON.stringify(command)} and id ${JSON.stringify(id)}`);
257
317
  const pairedDevices = await this.zbController.getClients(true);
258
318
  const groups = {};
@@ -421,18 +481,27 @@ class Commands {
421
481
  return devices;
422
482
  })
423
483
  .then(devices => {
424
- this.debug(`getDevices result: ${JSON.stringify(devices)}`);
425
- this.adapter.sendTo(from, command, devices, callback);
484
+ this.debug(`getDevices contains ${devices.length} Devices`);
485
+ const rv = { devices:devices, inLog:this.adapter.deviceDebug.logStatus, byId:this.adapter.deviceDebug.collectDebugData() }
486
+ if (this.stController) {
487
+ rv.clean = this.stController.CleanupRequired();
488
+ rv.errors = this.stController.getStashedErrors();
489
+ rv.debugDevices = this.stController.debugDevices;
490
+ }
491
+ this.adapter.sendTo(from, command, rv, callback);
426
492
  })
427
- .catch(err => this.error(`getDevices error: ${err.stack}`));
493
+ .catch(err => {
494
+ this.error(`getDevices error: ${err.stack}`);
495
+ this.adapter.sendTo(from, command, {error: `Error enumerating devices : ${err && err.message ? err.message : ''}`}, callback);
496
+ });
428
497
  } else {
429
- this.adapter.sendTo(from, command, {error: 'You need save and run adapter before pairing!'}, callback);
498
+ this.adapter.sendTo(from, command, {error: 'No active connection to Zigbee Hardware!'}, callback);
430
499
  }
431
500
  }
432
501
 
433
502
 
434
503
  async getCoordinatorInfo(from, command, callback) {
435
- if (this.zbController) {
504
+ if (this.zbController && this.zbController.herdsmanStarted) {
436
505
  const coordinatorinfo = {
437
506
  installSource: 'IADefault_1',
438
507
  channel: '-1',
@@ -500,7 +569,7 @@ class Commands {
500
569
  this.adapter.sendTo(from, command, coordinatorinfo, callback);
501
570
  });
502
571
  } else {
503
- this.adapter.sendTo(from, command, {error: 'You need save and run adapter before pairing!'}, callback);
572
+ this.adapter.sendTo(from, command, {error: 'No active connection to Zigbee Hardware!'}, callback);
504
573
  }
505
574
  }
506
575
 
@@ -515,7 +584,7 @@ class Commands {
515
584
  }
516
585
 
517
586
  deleteDevice(from, command, msg, callback) {
518
- if (this.zbController && this.stController) {
587
+ if (this.zbController && this.zbController.herdsmanStarted && this.stController) {
519
588
  this.debug(`deleteDevice message: ${JSON.stringify(msg)}`);
520
589
  const id = msg.id;
521
590
  const force = msg.force;
@@ -540,7 +609,7 @@ class Commands {
540
609
  }
541
610
  });
542
611
  } else {
543
- this.adapter.sendTo(from, command, {error: 'You need to save and start the adapter!'}, callback);
612
+ this.adapter.sendTo(from, command, {error: 'No active connection to Zigbee Hardware!'}, callback);
544
613
  }
545
614
  }
546
615
 
@@ -564,14 +633,14 @@ class Commands {
564
633
  }
565
634
 
566
635
  async getChannels(from, command, message, callback) {
567
- if (this.zbController) {
636
+ if (this.zbController && this.zbController.herdsmanStarted) {
568
637
  const result = await this.zbController.getChannelsEnergy();
569
638
  this.debug(`getChannels result: ${JSON.stringify(result)}`);
570
639
  this.adapter.sendTo(from, command, result, callback);
571
640
  } else {
572
641
  this.adapter.sendTo(
573
642
  from, command,
574
- {error: 'You need to setup serial port and start the adapter before pairing!'},
643
+ {error: 'No active connection to Zigbee Hardware!'},
575
644
  callback
576
645
  );
577
646
  }
@@ -604,7 +673,7 @@ class Commands {
604
673
  async getLocalImages(from, command, msg, callback) {
605
674
  if (this.stController) {
606
675
  const id = msg.id;
607
- const result = await this.stController.localConfig.enumerateImages(utils.getAbsoluteInstanceDataDir(this.adapter).replace('zigbee.','zigbee_'));
676
+ const result = await this.stController.localConfig.enumerateImages(this.adapter.getDataFolder());
608
677
  this.adapter.sendTo(from, command, {imageData:result}, callback)
609
678
  }
610
679
  }
@@ -663,7 +732,7 @@ class Commands {
663
732
 
664
733
 
665
734
  async reconfigure(from, command, msg, callback) {
666
- if (this.zbController) {
735
+ if (this.zbController && this.zbController.herdsmanStarted) {
667
736
  const devid = getZbId(msg.id);
668
737
  this.debug(`Reconfigure ${devid}`);
669
738
  const entity = await this.zbController.resolveEntity(devid);
@@ -688,6 +757,69 @@ class Commands {
688
757
  this.adapter.sendTo(from, command, {error: 'No device'}, callback);
689
758
  }
690
759
  }
760
+ else {
761
+ this.adapter.sendTo(from, command, {error: 'No active connection to Zigbee Hardware!'}, callback);
762
+ }
763
+ }
764
+
765
+ async testConnection(from, command, msg, callback) {
766
+ this.debug(`TestConnection with ${JSON.stringify(msg)}`);
767
+ if (msg && msg.address) {
768
+ const netAddress = getNetAddress(msg.address);
769
+ if (netAddress && netAddress.host) {
770
+ this.adapter.logToPairing(`attempting dns lookup for ${netAddress.host}`);
771
+ this.debug(`attempting dns lookup for ${netAddress.host}`);
772
+ dns.lookup(netAddress.host, (err, ip, _) => {
773
+ if (err) {
774
+ const msg = `Unable to resolve name: ${err && err.message ? err.message : 'no message'}`;
775
+ this.error(msg);
776
+ this.adapter.logToPairing(`Error: ${msg}`);
777
+ this.adapter.sendTo(from, command, {error:msg}, callback);
778
+ return;
779
+ }
780
+ this.debug(`dns lookup for ${msg.address} produced ${ip}`);
781
+ this.adapter.logToPairing(`dns lookup for ${msg.address} produced ${ip}`);
782
+ const client = new net.Socket();
783
+ this.debug(`attempting to connect to ${ip} port ${netAddress.port ? netAddress.port : 80}`);
784
+ client.connect(netAddress.port, ip, () => {
785
+ client.destroy()
786
+ this.adapter.logToPairing(`connected successfully to connect to ${ip} port ${netAddress.port ? netAddress.port : 80}`);
787
+ this.debug(`connected successfully to connect to ${ip} port ${netAddress.port ? netAddress.port : 80}`);
788
+ this.adapter.sendTo(from, command, {}, callback)
789
+ })
790
+ client.on('error', (error) => {
791
+ const msg = `unable to connect to ${ip} port ${netAddress.port ? netAddress.port : 80} : ${error && error.message ? error.message : 'no message given'}`
792
+ this.error(msg);
793
+ this.adapter.logToPairing(`Error: ${msg}`);
794
+ this.adapter.sendTo(from, command, {error:msg}, callback);
795
+ });
796
+ })
797
+ }
798
+ else
799
+ {
800
+ try {
801
+ const port = msg.address.trim();
802
+ this.adapter.logToPairing(`reading access rights for ${port}`);
803
+ access(port, constants.R_OK | constants.W_OK, (error) => {
804
+ if (error) {
805
+ const msg = `unable to access ${port} : ${error && error.message ? error.message : 'no message given'}`;
806
+ this.error(msg);
807
+ this.adapter.logToPairing(`Error: ${msg}`);
808
+ this.adapter.sendTo(from, command, {error:msg}, callback);
809
+ return;
810
+ }
811
+ this.adapter.logToPairing(`read and write access available for ${port}`);
812
+ this.adapter.sendTo(from, command, {}, callback);
813
+ });
814
+ }
815
+ catch (error) {
816
+ const msg = `File access error: ${error && error.message ? error.message : 'no message given'}`;
817
+ this.error(msg);
818
+ this.adapter.logToPairing(`Error: ${msg}`);
819
+ this.adapter.sendTo(from, command, {error:msg}, callback);
820
+ }
821
+ }
822
+ }
691
823
  }
692
824
  }
693
825
 
package/lib/devices.js CHANGED
@@ -3104,9 +3104,9 @@ function fillStatesWithExposes(logger) {
3104
3104
  async function addExposeToDevices(device, logger, model) {
3105
3105
  const s = DevicesByModel.size;
3106
3106
  if (s < 1) getByModel();
3107
- await applyExposeForDevice(devices, DevicesByModel, device, logger, model);
3107
+ const rv = await applyExposeForDevice(devices, DevicesByModel, device, logger, model);
3108
3108
  removeEmptyStates(devices);
3109
- return (DevicesByModel.size != s);
3109
+ return rv;
3110
3110
  }
3111
3111
 
3112
3112
  // remove empty states
package/lib/exposes.js CHANGED
@@ -251,8 +251,6 @@ function createFromExposes(model, def, device, log) {
251
251
  }
252
252
  }
253
253
 
254
- const icon = utils.getDeviceIcon(def);
255
-
256
254
  if (typeof def.exposes == 'object') {
257
255
  for (const expose of def.exposes) {
258
256
  genStateFromExpose(expose);
@@ -266,11 +264,12 @@ function createFromExposes(model, def, device, log) {
266
264
  genStateFromExpose(expose);
267
265
  }
268
266
  }
267
+ const icon = utils.getDeviceIcon(def);
269
268
 
270
269
  const newDev = {
271
270
  models: [model],
272
- icon,
273
271
  states,
272
+ icon,
274
273
  exposed: true,
275
274
  };
276
275
 
@@ -867,34 +866,12 @@ function createFromExposes(model, def, device, log) {
867
866
 
868
867
  }
869
868
 
870
- function applyExposes(mappedDevices, byModel) {
871
- // create or device from exposes
872
- for (const deviceDef of zigbeeHerdsmanConverters.definitions) {
873
- applyDeviceDef(mappedDevices, byModel, deviceDef);
874
-
875
- if (deviceDef.hasOwnProperty('whiteLabel')) {
876
- for (const deviceWhiteLabel of deviceDef.whiteLabel) {
877
- applyDeviceDef(mappedDevices, byModel, {
878
- ...deviceDef,
879
- model: deviceWhiteLabel.model,
880
- vendor: deviceWhiteLabel.vendor,
881
- description: deviceWhiteLabel.description || deviceDef.description,
882
- });
883
- }
884
- }
885
- }
886
- }
887
-
888
- async function applyExposeForDevice(mappedDevices, byModel, device, logger) {
889
- const t = Date.now();
869
+ async function applyExposeForDevice(mappedDevices, byModel, device) {
890
870
  const deviceDef = await zigbeeHerdsmanConverters.findByDevice(device);
891
871
  if (!deviceDef) {
892
- //logger.warn(`ZHC02,${Date.now()},${Date.now()-t},${device && device.ieeeAddr ? device.ieeeAddr : '0x0'}, failed`);
893
- return false;
872
+ return undefined;
894
873
  }
895
- //logger.warn(`ZHC02,${Date.now()},${Date.now()-t},${device && device.ieeeAddr ? device.ieeeAddr : '0x0'}, passed`);
896
- applyDeviceDef(mappedDevices, byModel, deviceDef, device);
897
- return true;
874
+ return applyDeviceDef(mappedDevices, byModel, deviceDef, device);
898
875
  }
899
876
 
900
877
  function applyDeviceDef(mappedDevices, byModel, deviceDef, device) {
@@ -905,14 +882,15 @@ function applyDeviceDef(mappedDevices, byModel, deviceDef, device) {
905
882
  const newDevice = createFromExposes(stripModel, deviceDef, device);
906
883
  mappedDevices.push(newDevice);
907
884
  byModel.set(stripModel, newDevice);
885
+ return newDevice;
908
886
 
909
887
  } catch (e) {
910
- //this.debug('empty catch in exposes');
888
+ return undefined;
911
889
  }
912
890
  }
891
+ return existsMap;
913
892
  }
914
893
 
915
894
  module.exports = {
916
- applyExposes: applyExposes,
917
895
  applyExposeForDevice: applyExposeForDevice,
918
896
  };
@@ -138,7 +138,7 @@ class localConfig extends EventEmitter {
138
138
  try {
139
139
  this.adapter.fileExists(namespace, rv, (err, result) => {
140
140
  if (result) return;
141
- const src = this.adapter.expandFileName(iconPath).replace('zigbee.','zigbee_');
141
+ const src = this.adapter.expandFileName(iconPath);
142
142
  fs.readFile(src, (err, data) => {
143
143
  if (err) {
144
144
  this.error('unable to read ' + src + ' : '+ (err && err.message? err.message:' no message given'))
@@ -220,7 +220,7 @@ class localConfig extends EventEmitter {
220
220
  }
221
221
 
222
222
  async updateFromDeviceNames() {
223
- const fn = this.adapter.expandFileName('dev_names').replace('zigbee.', 'zigbee_').concat('.json');
223
+ const fn = this.adapter.expandFileName('dev_names.json');
224
224
  fs.readFile(fn, (err, content) => {
225
225
  if (!err) {
226
226
  let data_js = {};
package/lib/seriallist.js CHANGED
@@ -22,10 +22,17 @@ class SerialList {
22
22
  SerialPort.list()
23
23
  .then(ports => {
24
24
  this.adapter.log.info(`List of port: ${JSON.stringify(ports)}`);
25
- this.adapter.sendTo(obj.from, obj.command, ports.map(item => ({
25
+ const candidates = ports.map(item => ({
26
26
  label: item.friendlyName || item.pnpId || item.manufacturer,
27
27
  comName: item.path
28
- })), obj.callback);
28
+ }));
29
+ require('fs').readdir('/dev/serial/by-id', (err, files) => {
30
+ if (!err) {
31
+ for (const item of files)
32
+ candidates.push({comName: `/dev/serial/by-id/${item}`});
33
+ }
34
+ this.adapter.sendTo(obj.from, obj.command, candidates.reverse(), obj.callback);
35
+ })
29
36
  })
30
37
  .catch(e => {
31
38
  this.adapter.sendTo(obj.from, obj.command, [], obj.callback);