iobroker.zigbee 3.1.2 → 3.1.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.
package/lib/commands.js CHANGED
@@ -7,10 +7,12 @@ const fs = require('fs');
7
7
  const statesMapping = require('./devices');
8
8
  const utils = require('@iobroker/adapter-core'); // Get common adapter utils
9
9
  const colors = require('./colors.js');
10
+ /* currently not needed, kept for referencce
10
11
  const dns = require('dns');
11
12
  const net = require('net');
12
- const { access, constants } =require('fs');
13
-
13
+ const access = fs.access;
14
+ const constants = fs.constants;
15
+ */
14
16
  const disallowedDashStates = [
15
17
  'link_quality', 'available', 'battery', 'groups', 'device_query',
16
18
  'hue_move', 'color_temp_move', 'satuation_move', 'brightness_move', 'brightness_step', 'hue_calibration',
@@ -82,9 +84,9 @@ class Commands {
82
84
  this.renameDevice(obj.from, obj.command, obj.message, obj.callback);
83
85
  }
84
86
  break;
85
- case 'deleteDevice':
87
+ case 'deleteZigbeeDevice':
86
88
  if (obj.message && typeof obj.message === 'object') {
87
- this.deleteDevice(obj.from, obj.command, obj.message, obj.callback);
89
+ this.deleteZigbeeDevice(obj.from, obj.command, obj.message, obj.callback);
88
90
  }
89
91
  break;
90
92
  case 'getChannels':
@@ -163,15 +165,22 @@ class Commands {
163
165
  if (this.stController) this.adapter.sendTo(obj.from, obj.command, {clean:this.stController.CleanupRequired(), errors:this.stController.getStashedErrors()}, obj.callback);
164
166
  // NO Break - returning the debug-data as well is intentional
165
167
  case 'getDebugMessages':
166
- this.adapter.sendTo(obj.from, obj.command, {byId:this.adapter.deviceDebug.collectDebugData( obj.message.inlog)},obj.callback);
168
+ this.adapter.sendTo(obj.from, obj.command, {byId:this.adapter.deviceDebug.collectDebugData( obj.message.inlog, obj.message.del )},obj.callback);
167
169
  break;
168
170
  case 'testConnection':
169
171
  this.testConnection(obj.from, obj.command, obj.message, obj.callback);
170
172
  break;
171
173
  case 'readNVRam':
172
174
  this.readNvBackup(obj.from, obj.command, obj.message, obj.callback);
175
+ break;
176
+ case 'downloadIcons':
177
+ this.triggerIconDownload(obj);
178
+ break;
179
+ case 'aliveCheck':
180
+ this.adapter.sendTo(obj.from, obj.command, {msg:'success'}, obj.callback);
181
+ break;
173
182
  default:
174
- //this.warn(`Commands: Command ${obj.command} is unknown`);
183
+ this.debug(`Commands: Command ${obj.command} is unknown`);
175
184
  //this.adapter.sendTo(obj.from, obj.command, obj.message, obj.callback);
176
185
  break;
177
186
  }
@@ -289,6 +298,7 @@ class Commands {
289
298
 
290
299
  if (await this.zbController.permitJoin(cTimer, devId)) {
291
300
  this.adapter.setState('info.pairingMode', cTimer > 0, true);
301
+ if (devId) this.zbController.emit(`Pairing started for ${devId}`);
292
302
  this.adapter.sendTo(from, command, cTimer ? 'Start pairing!':'Stop pairing!', callback);
293
303
  }
294
304
  else {
@@ -329,196 +339,256 @@ class Commands {
329
339
  }
330
340
  }
331
341
 
332
- async getDevices(from, command, id, callback) {
333
- if (this.zbController && this.zbController.herdsmanStarted) {
334
- this.debug(`getDevices called from ${from} with command ${JSON.stringify(command)} and id ${JSON.stringify(id)}`);
335
- const pairedDevices = await this.zbController.getClients(true);
336
- const groups = {};
337
- let rooms;
338
- this.adapter.getEnumsAsync('enum.rooms')
339
- .then(enums => {
340
- // rooms
341
- rooms = enums['enum.rooms'];
342
- })
343
- // get all adapter devices
344
- .then(() => this.adapter.getDevicesAsync())
345
- .then(async result => {
346
- const alls = id ? await this.adapter.getStatesAsync(id + '.*') : await this.adapter.getStatesAsync('*');
347
- const allst = id ? await this.adapter.getStatesOfAsync(id) : await this.adapter.getStatesOfAsync();
348
- result = result.filter(item => !id || id === item._id);
349
- // get device states and groups
350
- result.forEach(async devInfo => {
351
- if (devInfo._id) {
352
- // battery and link_quality
353
- const lqState = alls[`${devInfo._id}.link_quality`];
354
- devInfo.link_quality = lqState ? lqState.val : undefined;
355
- devInfo.link_quality_lc = lqState ? lqState.lc : undefined;
356
- const batState = alls[`${devInfo._id}.battery`];
357
- devInfo.battery = batState ? batState.val : undefined;
358
- // devInfo.states = states || {};
359
-
360
- const states = allst.filter(item => item._id.startsWith(devInfo._id));
361
-
362
- // put only allowed states
363
- devInfo.statesDef = (states || []).filter(stateDef => {
364
- const sid = stateDef._id.replace(this.adapter.namespace + '.', '');
365
- const names = sid.split('.');
366
- if (stateDef.common.color || names.length > 2) return false;
367
- return !disallowedDashStates.includes(names.pop());
368
-
369
- }).map(stateDef => {
370
- const name = stateDef.common.name;
371
- const devname = devInfo.common.name;
372
- // replace state
373
- return {
374
- id: stateDef._id,
375
- name: typeof name === 'string' ? name.replace(devname, '') : name,
376
- type: stateDef.common.type,
377
- read: stateDef.common.read,
378
- write: stateDef.common.write,
379
- val: alls[stateDef._id] ? alls[stateDef._id].val : undefined,
380
- role: stateDef.common.role,
381
- unit: stateDef.common.unit,
382
- states: stateDef.common.states,
383
- };
384
- });
385
- }
386
- });
387
- return result;
388
- })
389
- .then(async result => {
390
- // combine info
391
- const devices = [];
392
- for (const devInfo of result) {
393
- if (devInfo._id.indexOf('group') > 0) {
394
- devInfo.icon = 'img/group.png';
395
- devInfo.vendor = 'ioBroker';
396
- // get group members and store them
397
- const match = /zigbee.\d.group_([0-9]+)/.exec(devInfo._id);
398
- if (match && match.length > 1) {
399
- const groupID = Number(match[1]);
400
- const groupmembers = await this.zbController.getGroupMembersFromController(groupID);
401
- this.debug(`group members for group ${groupID}: ${JSON.stringify(groupmembers)}`);
402
- if (groupmembers && groupmembers.length > 0) {
403
- const memberinfo = [];
404
- for (const member of groupmembers) {
405
- if (member && typeof member.ieee === 'string') {
406
- if (groups) {
407
- const grouparray = groups[member.ieee];
408
- if (grouparray) {
409
- if (!grouparray.includes(groupID)) {
410
- groups[member.ieee].push(groupID);
411
- }
412
- } else {
413
- groups[member.ieee] = [groupID];
414
- }
415
- }
416
- const device = await this.adapter.getObjectAsync(`${this.adapter.namespace}.${member.ieee.substr(2)}`);
417
- if (device) {
418
- member.device = device.common.name;
419
- } else {
420
- member.device = 'unknown';
421
- }
422
- memberinfo.push(member);
423
- }
424
- }
425
- devInfo.memberinfo = memberinfo;
426
- this.debug(`memberinfo for ${match[1]}: ${JSON.stringify(devInfo.memberinfo)}`);
427
- }
428
- }
429
- } else {
430
- const modelDesc = statesMapping.findModel(devInfo.common.type);
431
- devInfo.icon = (modelDesc && modelDesc.icon) ? modelDesc.icon : 'img/unknown.png';
432
- devInfo.vendor = modelDesc ? modelDesc.vendor : '';
433
- const legacyDesc = statesMapping.findModel(devInfo.common.type, true);
434
- devInfo.legacyIcon = (legacyDesc && legacyDesc.icon) ? legacyDesc.icon : undefined;
435
- }
436
342
 
437
- const id = getZbId(devInfo._id);
438
- devInfo.info = await this.zbController.resolveEntity(id);
439
- // check configuration
440
- try {
441
- if (devInfo.info) {
442
- const result = await this.zbController.callExtensionMethod(
443
- 'shouldConfigure',
444
- [devInfo.info.device, devInfo.info.mapped],
445
- );
446
- if (result.length > 0) devInfo.isConfigured = !result[0];
447
- }
448
- } catch (error) {
449
- this.warn('error calling shouldConfigure: ' + error && error.message ? error.message : 'no error message');
450
- }
343
+ async handleDeviceforInfo(device, states) {
344
+ }
451
345
 
452
- devInfo.rooms = [];
453
- for (const room in rooms) {
454
- if (!rooms.hasOwnProperty(room) ||
455
- !rooms[room] ||
456
- !rooms[room].common ||
457
- !rooms[room].common.members
458
- ) {
459
- continue;
460
- }
461
- if (rooms[room].common.members.includes(devInfo._id)) {
462
- devInfo.rooms.push(rooms[room].common.name);
463
- }
464
- }
465
- devInfo.paired = !!devInfo.info;
466
- devices.push(devInfo);
467
- }
468
- return devices;
469
- })
470
- .then(async (devices) => {
471
- // fill group info
472
- for (const groupdev in groups) {
473
- const device = devices.find(dev => (groupdev === getZbId(dev._id)));
346
+ async handleGroupforInfo(group, groups) {
347
+ group.icon = 'img/group.png';
348
+ group.vendor = 'ioBroker';
349
+ // get group members and store them
350
+ const match = /zigbee.\d.group_([0-9]+)/.exec(group._id);
351
+ if (match && match.length > 1) {
352
+ const groupID = Number(match[1]);
353
+ const groupmembers = await this.zbController.getGroupMembersFromController(groupID);
354
+ this.debug(`group members for group ${groupID}: ${JSON.stringify(groupmembers)}`);
355
+ if (groupmembers && groupmembers.length > 0) {
356
+ const memberinfo = [];
357
+ for (const member of groupmembers) {
358
+ if (member && typeof member.ieee === 'string') {
359
+ const memberId = member.ieee.substr(2);
360
+ const device = await this.adapter.getObjectAsync(`${this.adapter.namespace}.${member.ieee.substr(2)}`);
361
+ const item = groups[memberId] || { groups:[], gep: { }};
362
+ const gep = item.gep[member.epid] || [];
363
+
364
+ if (!item.groups.includes(groupID)) item.groups.push(groupID);
365
+ if (!gep.includes(`${groupID}`)) gep.push(`${groupID}`);
366
+ item.gep[member.epid] = gep;
367
+ groups[memberId] = item;
474
368
  if (device) {
475
- device.groups = groups[groupdev];
476
- }
477
- }
478
- // append devices that paired but not created
479
- if (!id) {
480
- for (const d of pairedDevices) {
481
- const device = await this.zbController.resolveEntity(d.ieeeAddr);
482
- if (!device || !device.device) {
483
- continue;
484
- }
485
- const exists = devices.find((dev) => (dev._id && device.device.ieeeAddr === getZbId(dev._id)));
486
- if (!exists) {
487
- devices.push({
488
- _id: device.device.ieeeAddr,
489
- icon: 'img/unknown.png',
490
- paired: true,
491
- info: device,
492
- common: {
493
- name: undefined,
494
- type: undefined,
495
- },
496
- native: {}
497
- });
498
- }
369
+ member.device = device.common.name;
370
+ } else {
371
+ member.device = 'unknown';
499
372
  }
373
+ memberinfo.push(member);
500
374
  }
501
- return devices;
375
+ }
376
+ group.memberinfo = memberinfo;
377
+ this.debug(`memberinfo for ${match[1]}: ${JSON.stringify(group.memberinfo)}`);
378
+ }
379
+ }
380
+ }
381
+
382
+ async fillInfo(device, device_stateDefs, all_states) {
383
+ device.statesDef = (device_stateDefs || []).filter(stateDef => {
384
+ const sid = stateDef._id.replace(this.adapter.namespace + '.', '');
385
+ const names = sid.split('.');
386
+ if (stateDef.common.color || names.length > 2) return false;
387
+ return !disallowedDashStates.includes(names.pop());
388
+ }).map(stateDef => {
389
+ const name = stateDef.common.name;
390
+ const devname = device.common.name;
391
+ // replace state
392
+ return {
393
+ id: stateDef._id,
394
+ name: typeof name === 'string' ? name.replace(devname, '') : name,
395
+ type: stateDef.common.type,
396
+ read: stateDef.common.read,
397
+ write: stateDef.common.write,
398
+ val: all_states[stateDef._id] ? all_states[stateDef._id].val : undefined,
399
+ role: stateDef.common.role,
400
+ unit: stateDef.common.unit,
401
+ states: stateDef.common.states,
402
+ };
403
+ });
404
+
405
+ const id = getZbId(device._id);
406
+ device.info = this.buildDeviceInfo(await this.zbController.resolveEntity(id));
407
+ // check configuration
408
+ try {
409
+ if (device.info) {
410
+ const result = await this.zbController.callExtensionMethod(
411
+ 'shouldConfigure',
412
+ [device.info.device, device.info.mapped],
413
+ );
414
+ if (result.length > 0) device.isConfigured = !result[0];
415
+ }
416
+ } catch (error) {
417
+ this.warn('error calling shouldConfigure: ' + error && error.message ? error.message : 'no error message');
418
+ }
419
+ device.paired = !!device.info;
420
+
421
+
422
+ }
423
+
424
+ buildDeviceInfo(device) {
425
+ const rv = {};
426
+ try {
427
+ rv.device = {
428
+ modelZigbee:device.device.modelID,
429
+ type:device.device.type,
430
+ ieee:device.device.ieeeAddr,
431
+ nwk:device.device.networkAddress,
432
+ manuf_id:device.device.maufacturerID,
433
+ manuf_name:device.device.manufacturerName,
434
+ manufacturer:device.mapped ? device.mapped.vendor : '',
435
+ power:device.device.powerSource,
436
+ app_version:device.device.applicationVersion,
437
+ hard_version:device.device.hardwareVersion,
438
+ zcl_version:device.device.zclVersion,
439
+ stack_version:device.device.stack_version,
440
+ date_code:device.device.dateCode,
441
+ build:device.device.softwareBuildID,
442
+ interviewstate:device.device.interviewState || 'UNKNOWN',
443
+ }
444
+ rv.endpoints = [];
445
+ for (const ep_idx in device.endpoints) {
446
+ const ep = device.endpoints[ep_idx];
447
+ rv.endpoints.push({
448
+ ID:ep.ID,
449
+ profile:ep.profileID,
450
+ input_clusters:ep.inputClusters,
451
+ output_clusters:ep.outputClusters,
502
452
  })
503
- .then(devices => {
504
- this.debug(`getDevices contains ${devices.length} Devices`);
505
- const rv = { devices:devices, inLog:this.adapter.deviceDebug.logStatus, byId:this.adapter.deviceDebug.collectDebugData() }
506
- if (this.stController) {
507
- rv.clean = this.stController.CleanupRequired();
508
- rv.errors = this.stController.getStashedErrors();
509
- rv.debugDevices = this.stController.debugDevices;
453
+ }
454
+ if (device.mapped) {
455
+ rv.mapped = {
456
+ model:device.mapped.model,
457
+ description:device.mapped.description,
458
+ //fingerprint:JSON.stringify(device.mapped.fingerprint),
459
+ vendor:device.mapped.vendor,
460
+ hasOnEvent:device.mapped.onEvent != undefined,
461
+ hasConfigure:device.mapped.configure != undefined,
462
+ options:[],
463
+ }
464
+ if (device.mapped.options && typeof (device.mapped.options == 'object')) {
465
+ const optionDesc = [];
466
+ for (const option of device.mapped.options) {
467
+ if (option.name)
468
+ rv.mapped.options.push(option.name);
510
469
  }
511
- this.adapter.sendTo(from, command, rv, callback);
512
- })
513
- .catch(err => {
514
- this.error(`getDevices error: ${err.stack}`);
515
- this.adapter.sendTo(from, command, {error: `Error enumerating devices : ${err && err.message ? err.message : ''}`}, callback);
516
- });
517
- } else {
518
- this.adapter.sendTo(from, command, {error: 'No active connection to Zigbee Hardware!'}, callback);
470
+ }
471
+ }
472
+ else {
473
+ rv.mapped = {
474
+ model:device.name,
475
+ description:device.name,
476
+ vendor:'not set',
477
+ hasOnEvent: false,
478
+ hasConfigure: false,
479
+ options:[],
480
+ }
481
+ }
482
+ }
483
+ catch (error) {
484
+ if (device && device.name === 'Coordinator') return rv;
485
+ const dev = device ? device.device || {} : {}
486
+ const msg = device ? `device ${device.name} (${dev.ieeeAddr}, NWK ${dev.networkAddres}, ID: ${dev.ID})` : 'undefined device';
487
+ this.warn(`Error ${error && error.message ? error.message + ' ' : ''}building device info for ${msg}`);
488
+ }
489
+ return rv;
490
+ }
491
+
492
+ async appendDevicesWithoutObjects(devices, client) {
493
+ const device = await this.zbController.resolveEntity(client.ieeeAddr);
494
+ if (!device || !device.device) {
495
+ return;
519
496
  }
497
+ const exists = devices.find((dev) => (dev._id && device.device.ieeeAddr === getZbId(dev._id)));
498
+ if (!exists) {
499
+ const coordinatorData = {
500
+ _id : `${this.adapter.namespace}.${device.device.ieeeAddr.substring(2)}`,
501
+ paired: true,
502
+ info: this.buildDeviceInfo(device),
503
+ native: { id: device.device.ieeeAddr.substring(2) },
504
+ mapped : {},
505
+ statesDev: [],
506
+ }
507
+ if (device.device.name === 'Coordinator') {
508
+ coordinatorData.icon = 'zigbee.png';
509
+ coordinatorData.common = { name: undefined, type: undefined };
510
+ } else {
511
+ coordinatorData.common = { name: 'Coordinator', type: 'Coordinator' };
512
+ coordinatorData.icon= 'img/unknown.png';
513
+ }
514
+ devices.push(coordinatorData);
515
+ }
516
+
520
517
  }
521
518
 
519
+ async getDevices(from, command, id, callback) {
520
+ this.debug(`getDevices called from ${from} with command ${JSON.stringify(command)}${id ? ' and id '+JSON.stringify(id) : ' without ID'}`);
521
+ if (!(this.zbController && this.zbController.herdsmanStarted)) {
522
+ this.adapter.sendTo(from, command, {error: 'No active connection to Zigbee Hardware!'}, callback);
523
+ return;
524
+ }
525
+ const rooms = await this.adapter.getEnumsAsync('enum.rooms') || {};
526
+ const deviceObjects = (id ? [await this.adapter.getObjectAsync(id)] : await this.adapter.getDevicesAsync());
527
+ const all_states = id ? await this.adapter.getStatesAsync(id + '.*') : await this.adapter.getStatesAsync('*');
528
+ const all_stateDefs = id ? await this.adapter.getStatesOfAsync(id) : await this.adapter.getStatesOfAsync();
529
+ const illegalDevices = [];
530
+ const groups = {};
531
+ const PromiseChain = [];
532
+ for (const devInfo of deviceObjects) {
533
+ if (devInfo._id.indexOf('group') > -1) {
534
+ PromiseChain.push(this.handleGroupforInfo(devInfo, groups));
535
+ }
536
+ else {
537
+ const modelDesc = statesMapping.findModel(devInfo.common.type);
538
+ devInfo.icon = (modelDesc && modelDesc.icon) ? modelDesc.icon : 'img/unknown.png';
539
+ devInfo.vendor = modelDesc ? modelDesc.vendor : '';
540
+ const legacyDesc = statesMapping.findModel(devInfo.common.type, true);
541
+ devInfo.legacyIcon = (legacyDesc && legacyDesc.icon) ? legacyDesc.icon : undefined;
542
+ const lq_state = all_states[`${devInfo._id}.link_quality`];
543
+ devInfo.link_quality = lq_state ? lq_state.val : -1;
544
+ devInfo.link_quality_lc = lq_state ? lq_state.lc : undefined;
545
+ const battery_state = all_states[`${devInfo._id}.battery`];
546
+ devInfo.battery = battery_state ? battery_state.val : undefined;
547
+ devInfo.rooms = [];
548
+ for (const room in rooms) {
549
+ if (!rooms.hasOwnProperty(room) ||
550
+ !rooms[room] ||
551
+ !rooms[room].common ||
552
+ !rooms[room].common.members
553
+ ) {
554
+ continue;
555
+ }
556
+ if (rooms[room].common.members.includes(devInfo._id)) {
557
+ devInfo.rooms.push(rooms[room].common.name);
558
+ }
559
+ }
560
+
561
+ }
562
+ PromiseChain.push(this.fillInfo(devInfo, all_stateDefs.filter(item => item._id.startsWith(devInfo._id)),all_states));
563
+ }
564
+ if (!id) {
565
+ for (const client of this.zbController.getClientIterator(true)) {
566
+ PromiseChain.push(this.appendDevicesWithoutObjects(deviceObjects,client))
567
+ }
568
+ }
569
+
570
+ await Promise.all(PromiseChain);
571
+
572
+ for (const groupmember in groups) {
573
+ const device = deviceObjects.find(dev => (groupmember === dev.native.id));
574
+ if (device) {
575
+ device.groups = groups[groupmember].groups;
576
+ device.groups_by_ep = groups[groupmember].gep;
577
+ }
578
+ }
579
+
580
+
581
+ this.debug(`getDevices contains ${deviceObjects.length} Devices`);
582
+ const rv = { devices:deviceObjects, inLog:this.adapter.deviceDebug.logStatus, }
583
+ if (!id) rv.byId = this.adapter.deviceDebug.collectDebugData();
584
+ if (this.stController) {
585
+ rv.clean = this.stController.CleanupRequired();
586
+ rv.errors = this.stController.getStashedErrors();
587
+ rv.debugDevices = this.stController.debugDevices;
588
+ }
589
+ this.adapter.sendTo(from, command, rv, callback);
590
+
591
+ }
522
592
 
523
593
  async getCoordinatorInfo(from, command, callback) {
524
594
  const coordinatorinfo = {
@@ -548,6 +618,7 @@ class Commands {
548
618
  coordinatorinfo.port = obj.native.port;
549
619
  coordinatorinfo.type = obj.native.adapterType;
550
620
  coordinatorinfo.channel = obj.native.channel;
621
+ coordinatorinfo.autostart = this.adapter.config.autostart;
551
622
  coordinatorinfo.installedVersion = obj.common.version;
552
623
  if (coordinatorVersion && coordinatorVersion.type && coordinatorVersion.meta) {
553
624
  coordinatorinfo.type = coordinatorVersion.type;
@@ -588,8 +659,8 @@ class Commands {
588
659
  }
589
660
  }
590
661
  else {
591
- coordinatorinfo.version = 'not connected';
592
- coordinatorinfo.revision = 'not connected';
662
+ coordinatorinfo.version = this.adapter.config.autostart ? 'not connected' : 'autostart not set';
663
+ coordinatorinfo.revision = this.adapter.config.autostart ? 'not connected' : 'autostart not set';
593
664
 
594
665
  }
595
666
  } catch {
@@ -611,14 +682,14 @@ class Commands {
611
682
  }
612
683
  }
613
684
 
614
- deleteDevice(from, command, msg, callback) {
685
+ deleteZigbeeDevice(from, command, msg, callback) {
615
686
  if (this.zbController && this.zbController.herdsmanStarted && this.stController) {
616
- this.debug(`deleteDevice message: ${JSON.stringify(msg)}`);
687
+ this.debug(`deleteZigbeeDevice message: ${JSON.stringify(msg)}`);
617
688
  const id = msg.id;
618
689
  const force = msg.force;
619
690
  const sysid = id.replace(this.adapter.namespace + '.', '0x');
620
691
  const devId = id.replace(this.adapter.namespace + '.', '');
621
- this.debug(`deleteDevice sysid: ${sysid}`);
692
+ this.debug(`deleteZigbeeDevice sysid: ${sysid}`);
622
693
  const dev = this.zbController.getDevice(sysid);
623
694
  if (!dev) {
624
695
  this.info(`Attempted to delete device ${devId} - the device is not known to the zigbee controller.`);
@@ -736,7 +807,6 @@ class Commands {
736
807
  this.debug(`updateLocalConfigItems : ${JSON.stringify(msg)}`);
737
808
  const target = msg.target.replace(`${this.adapter.namespace}.`, '');
738
809
  const entity = await this.zbController.resolveEntity(target);
739
- //this.warn('entity for ' + target + ' is '+ JSON.stringify(entity))
740
810
  if (entity && !entity.mapped) {
741
811
  this.warn('unable to set local Override for device whithout mapped model');
742
812
  return;
@@ -744,17 +814,44 @@ class Commands {
744
814
  if (msg.data)
745
815
  {
746
816
  for (const prop in msg.data) {
817
+ if (prop==='options') {
818
+ // we need to trigger the option change
819
+ const changed_from = entity.options;
820
+ const changed_to = msg.data.prop;
821
+ /*
822
+ const allKeys = Object.keys(changed_from);
823
+ for (const nk of Object.keys(changed_to)) {
824
+ if (allKeys.includes(nk)) continue;
825
+ allKeys.push(nk);
826
+ }
827
+ for (const key of allKeys) {
828
+ if (changed_from.hasOwnProperty(key) && changed_to.hasOwnProperty(key) && changed_from[key] == changed_to[key])
829
+ {
830
+ delete changed_from[key];
831
+ delete changed_to[key];
832
+ }
833
+ }
834
+ */
835
+ this.zbController.callExtensionMethod(
836
+ 'onZigbeeEvent',
837
+ [{'device': entity.device, 'type': 'deviceOptionsChanged', from: changed_from, to:changed_to || {}, }, entity ? entity.mapped : null]);
838
+ }
747
839
  this.debug('enumerating data: ' + prop);
748
840
  await this.stController.localConfig.updateLocalOverride(target, (entity ? entity.mapped.model : 'group'), prop, msg.data[prop], msg.global);
749
841
  }
750
842
  }
751
- if (entity) {
752
- this.debug('updateLocalConfigItems with Entity');
753
- this.stController.updateDev(target, entity.mapped.model, entity.mapped.model, () => {this.adapter.sendTo(from, command, {}, callback)});
843
+ try {
844
+ if (entity) {
845
+ this.debug('updateLocalConfigItems with Entity');
846
+ this.stController.updateDev(target, entity.mapped.model, entity.mapped.model, () => {this.adapter.sendTo(from, command, {}, callback)});
847
+ }
848
+ else {
849
+ this.debug('updateLocalConfigItems without Entity');
850
+ this.stController.updateDev(target, undefined, 'group',() => {this.adapter.sendTo(from, command, {}, callback)});
851
+ }
754
852
  }
755
- else {
756
- this.debug('updateLocalConfigItems without Entity');
757
- this.stController.updateDev(target, undefined, 'group',() => {this.adapter.sendTo(from, command, {}, callback)});
853
+ catch (error) {
854
+ this.adapter.sendTo(from, command, {err: error.message}, callback);
758
855
  }
759
856
  }
760
857
  }
@@ -824,65 +921,32 @@ class Commands {
824
921
  this.adapter.logToPairing(`Error: ${result.error}`)
825
922
  }
826
923
  this.adapter.sendTo(from, command, result, callback);
827
- /*
828
- this.debug(`TestConnection with ${JSON.stringify(msg)}`);
829
- if (msg && msg.address) {
830
- const netAddress = getNetAddress(msg.address);
831
- if (netAddress && netAddress.host) {
832
- this.adapter.logToPairing(`attempting dns lookup for ${netAddress.host}`);
833
- this.debug(`attempting dns lookup for ${netAddress.host}`);
834
- dns.lookup(netAddress.host, (err, ip, _) => {
835
- if (err) {
836
- const msg = `Unable to resolve name: ${err && err.message ? err.message : 'no message'}`;
837
- this.error(msg);
838
- this.adapter.logToPairing(`Error: ${msg}`);
839
- this.adapter.sendTo(from, command, {error:msg}, callback);
840
- return;
841
- }
842
- this.debug(`dns lookup for ${msg.address} produced ${ip}`);
843
- this.adapter.logToPairing(`dns lookup for ${msg.address} produced ${ip}`);
844
- const client = new net.Socket();
845
- this.debug(`attempting to connect to ${ip} port ${netAddress.port ? netAddress.port : 80}`);
846
- client.connect(netAddress.port, ip, () => {
847
- client.destroy()
848
- this.adapter.logToPairing(`connected successfully to connect to ${ip} port ${netAddress.port ? netAddress.port : 80}`);
849
- this.debug(`connected successfully to connect to ${ip} port ${netAddress.port ? netAddress.port : 80}`);
850
- this.adapter.sendTo(from, command, {}, callback)
851
- })
852
- client.on('error', (error) => {
853
- const msg = `unable to connect to ${ip} port ${netAddress.port ? netAddress.port : 80} : ${error && error.message ? error.message : 'no message given'}`
854
- this.error(msg);
855
- this.adapter.logToPairing(`Error: ${msg}`);
856
- this.adapter.sendTo(from, command, {error:msg}, callback);
857
- });
858
- })
859
- }
860
- else
861
- {
862
- try {
863
- const port = msg.address.trim();
864
- this.adapter.logToPairing(`reading access rights for ${port}`);
865
- access(port, constants.R_OK | constants.W_OK, (error) => {
866
- if (error) {
867
- const msg = `unable to access ${port} : ${error && error.message ? error.message : 'no message given'}`;
868
- this.error(msg);
869
- this.adapter.logToPairing(`Error: ${msg}`);
870
- this.adapter.sendTo(from, command, {error:msg}, callback);
871
- return;
872
- }
873
- this.adapter.logToPairing(`read and write access available for ${port}`);
874
- this.adapter.sendTo(from, command, {}, callback);
875
- });
876
- }
877
- catch (error) {
878
- const msg = `File access error: ${error && error.message ? error.message : 'no message given'}`;
879
- this.error(msg);
880
- this.adapter.logToPairing(`Error: ${msg}`);
881
- this.adapter.sendTo(from, command, {error:msg}, callback);
882
- }
924
+ }
925
+
926
+ async triggerIconDownload(obj) {
927
+ if (!this.stController) {
928
+ this.adapter.sendTo(obj.from, obj.command, {msg:'No States controller'}, obj.callback);
929
+ return;
930
+ }
931
+ const clients = await this.adapter.getDevicesAsync();
932
+ const Promises = [];
933
+ for (const client of clients) {
934
+ if (client.common.modelIcon && client.common.icon && client.common.modelIcon.startsWith('http')) {
935
+ const filestatus = await this.adapter.fileExistsAsync(this.adapter.namespace, client.common.icon);
936
+ if (!filestatus)
937
+ Promises.push(this.stController.downloadIconToAdmin(client.common.modelIcon, client.common.icon))
883
938
  }
939
+
940
+ }
941
+ const NumDownloads = Promises.length;
942
+ if (NumDownloads) {
943
+ this.adapter.sendTo(obj.from, obj.command, {msg:`${NumDownloads} downloads triggered.`}, obj.callback);
944
+ Promise.all(Promises);
884
945
  }
885
- */
946
+ else {
947
+ this.adapter.sendTo(obj.from, obj.command, {msg:'Nothing to download'}, obj.callback);
948
+ }
949
+
886
950
  }
887
951
  }
888
952