iobroker.zigbee 3.1.5 → 3.2.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,13 +1,11 @@
1
1
  'use strict';
2
2
 
3
- const getZbId = require('./utils').getZbId;
4
- const getNetAddress = require('./utils').getNetAddress;
5
- const reverseByteString = require('./utils').reverseByteString;
3
+ const { getZbId, getNetAddress, reverseByteString, zbIdorIeeetoAdId, adIdtoZbIdorIeee } = require('./utils');
6
4
  const fs = require('fs');
7
5
  const statesMapping = require('./devices');
8
- const utils = require('@iobroker/adapter-core'); // Get common adapter utils
9
6
  const colors = require('./colors.js');
10
7
  /* currently not needed, kept for referencce
8
+ const utils = require('@iobroker/adapter-core'); // Get common adapter utils
11
9
  const dns = require('dns');
12
10
  const net = require('net');
13
11
  const access = fs.access;
@@ -54,12 +52,12 @@ class Commands {
54
52
  /**
55
53
  * @param {ioBroker.Message} obj
56
54
  */
57
- onMessage(obj) {
55
+ async onMessage(obj) {
58
56
  if (typeof obj === 'object' && obj.command) {
59
57
  if (obj) {
60
58
  switch (obj.command) {
61
59
  case 'testConnect':
62
- this.adapter.testConnect(obj.from, obj.command, obj.message, obj.callback);
60
+ this.adapter.sendTo(obj.from, obj.command, await this.adapter.testConnect(obj.message), obj.callback);
63
61
  break;
64
62
  case 'deleteNVBackup':
65
63
  this.delNvBackup(obj.from, obj.command, {}, obj.callback);
@@ -153,7 +151,7 @@ class Commands {
153
151
  break;
154
152
  case 'updateLocalConfigItems':
155
153
  if (obj.message && typeof obj.message === 'object') {
156
- this.updateLocalConfigItems(obj.from, obj.command, obj.message, obj.callback);
154
+ this.updateConfigItems(obj.from, obj.command, obj.message, obj.callback);
157
155
  }
158
156
  break;
159
157
  case 'getLocalConfigItems':
@@ -298,8 +296,7 @@ class Commands {
298
296
 
299
297
  if (await this.zbController.permitJoin(cTimer, devId)) {
300
298
  this.adapter.setState('info.pairingMode', cTimer > 0, true);
301
- if (devId) this.zbController.emit(`Pairing started for ${devId}`);
302
- this.adapter.sendTo(from, command, cTimer ? 'Start pairing!':'Stop pairing!', callback);
299
+ //this.adapter.sendTo(from, command, cTimer ? 'Start pairing!':'Stop pairing!', callback);
303
300
  }
304
301
  else {
305
302
  this.adapter.sendTo(
@@ -339,12 +336,8 @@ class Commands {
339
336
  }
340
337
  }
341
338
 
342
-
343
- async handleDeviceforInfo(device, states) {
344
- }
345
-
346
339
  async handleGroupforInfo(group, groups) {
347
- group.icon = 'img/group.png';
340
+ group.icon = 'img/group_1.png';
348
341
  group.vendor = 'ioBroker';
349
342
  // get group members and store them
350
343
  const match = /zigbee.\d.group_([0-9]+)/.exec(group._id);
@@ -356,8 +349,8 @@ class Commands {
356
349
  const memberinfo = [];
357
350
  for (const member of groupmembers) {
358
351
  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)}`);
352
+ const memberId = zbIdorIeeetoAdId(this.adapter, member.ieee, false);
353
+ const device = await this.adapter.getObjectAsync(zbIdorIeeetoAdId(this.adapter, member.ieee, true));
361
354
  const item = groups[memberId] || { groups:[], gep: { }};
362
355
  const gep = item.gep[member.epid] || [];
363
356
 
@@ -365,12 +358,12 @@ class Commands {
365
358
  if (!gep.includes(`${groupID}`)) gep.push(`${groupID}`);
366
359
  item.gep[member.epid] = gep;
367
360
  groups[memberId] = item;
368
- if (device) {
369
- member.device = device.common.name;
370
- } else {
371
- member.device = 'unknown';
372
- }
373
- memberinfo.push(member);
361
+ memberinfo.push({
362
+ ieee:member.ieee,
363
+ epid:member.epid,
364
+ model:member.model,
365
+ device:device? device.common.name:'unknown'
366
+ });
374
367
  }
375
368
  }
376
369
  group.memberinfo = memberinfo;
@@ -379,7 +372,7 @@ class Commands {
379
372
  }
380
373
  }
381
374
 
382
- async fillInfo(device, device_stateDefs, all_states) {
375
+ async fillInfo(device, device_stateDefs, all_states, models) {
383
376
  device.statesDef = (device_stateDefs || []).filter(stateDef => {
384
377
  const sid = stateDef._id.replace(this.adapter.namespace + '.', '');
385
378
  const names = sid.split('.');
@@ -403,7 +396,25 @@ class Commands {
403
396
  });
404
397
 
405
398
  const id = getZbId(device._id);
406
- device.info = this.buildDeviceInfo(await this.zbController.resolveEntity(id));
399
+ const entity = await this.zbController.resolveEntity(id)
400
+
401
+ device.info = this.buildDeviceInfo(entity);
402
+
403
+ const UID = models.UIDbyModel[device.info?.mapped?.model || 'unknown'] || `m_${Object.keys(models.UIDbyModel).length}`;
404
+ if (models.byUID.hasOwnProperty(UID)) {
405
+ models.byUID[UID].devices.push(device);
406
+ }
407
+ else {
408
+ models.byUID[UID] = {
409
+ model:device.info.mapped,
410
+ availableOptions : [...device.info?.mapped?.options || [], ...['use_legacy_model']],
411
+ setOptions: this.adapter.stController.localConfig.getByModel(device.info?.mapped?.model || 'unknown') || [],
412
+ devices: [device],
413
+ }
414
+ if (!models.byUID[UID].model.type)
415
+ models.byUID[UID].model.type = 'Group';
416
+ models.UIDbyModel[device.info?.mapped?.model || 'unknown'] = UID;
417
+ }
407
418
  // check configuration
408
419
  try {
409
420
  if (device.info) {
@@ -412,26 +423,38 @@ class Commands {
412
423
  [device.info.device, device.info.mapped],
413
424
  );
414
425
  if (result.length > 0) device.isConfigured = !result[0];
415
- }
426
+ device.paired = true;
427
+ } else device.paired = false;
416
428
  } catch (error) {
417
429
  this.warn('error calling shouldConfigure: ' + error && error.message ? error.message : 'no error message');
418
430
  }
419
- device.paired = !!device.info;
420
-
421
-
422
431
  }
423
432
 
424
433
  buildDeviceInfo(device) {
434
+ function getKey(object, value) {
435
+ try {
436
+ for (const key of Object.keys(object)) {
437
+ if (object[key] == value) {
438
+ return key;
439
+ }
440
+ }
441
+ }
442
+ catch {
443
+ return undefined;
444
+ }
445
+ return undefined;
446
+
447
+ }
425
448
  const rv = {};
426
449
  try {
427
450
  rv.device = {
428
451
  modelZigbee:device.device.modelID,
429
452
  type:device.device.type,
430
- ieee:device.device.ieeeAddr,
431
- nwk:device.device.networkAddress,
453
+ ieee:device.device.ieeeAddr || device.device.groupID,
454
+ nwk:device.device.networkAddress || 0,
432
455
  manuf_id:device.device.maufacturerID,
433
456
  manuf_name:device.device.manufacturerName,
434
- manufacturer:device.mapped ? device.mapped.vendor : '',
457
+ manufacturer:device.mapped?.vendor,
435
458
  power:device.device.powerSource,
436
459
  app_version:device.device.applicationVersion,
437
460
  hard_version:device.device.hardwareVersion,
@@ -446,32 +469,40 @@ class Commands {
446
469
  const ep = device.endpoints[ep_idx];
447
470
  rv.endpoints.push({
448
471
  ID:ep.ID,
472
+ epName: device.mapped?.endpoint ? getKey(device.mapped?.endpoint(device), ep.ID) : ep.ID,
449
473
  profile:ep.profileID,
450
474
  input_clusters:ep.inputClusters,
451
475
  output_clusters:ep.outputClusters,
452
476
  })
477
+ if (ep.inputClusters.includes(4)) rv.device.isGroupable = true;
453
478
  }
454
479
  if (device.mapped) {
455
480
  rv.mapped = {
456
481
  model:device.mapped.model,
482
+ type:device.device.type,
457
483
  description:device.mapped.description,
484
+ hasLegacyDef:statesMapping.hasLegacyDevice(device.mapped.model),
458
485
  //fingerprint:JSON.stringify(device.mapped.fingerprint),
459
486
  vendor:device.mapped.vendor,
460
487
  hasOnEvent:device.mapped.onEvent != undefined,
461
488
  hasConfigure:device.mapped.configure != undefined,
489
+ icon:`img/${device.mapped.model.replace(/\//g, '-')}.png`,
490
+ legacyIcon: statesMapping.getIconforLegacyModel(device.mapped.model),
462
491
  options:[],
463
492
  }
464
493
  if (device.mapped.options && typeof (device.mapped.options == 'object')) {
465
- const optionDesc = [];
494
+ rv.mapped.optionExposes = device.mapped.options;
466
495
  for (const option of device.mapped.options) {
467
- if (option.name)
496
+ if (option.name) {
468
497
  rv.mapped.options.push(option.name);
498
+ }
469
499
  }
470
500
  }
471
501
  }
472
502
  else {
473
503
  rv.mapped = {
474
504
  model:device.name,
505
+ type: device.device.type,
475
506
  description:device.name,
476
507
  vendor:'not set',
477
508
  hasOnEvent: false,
@@ -501,19 +532,18 @@ class Commands {
501
532
  paired: true,
502
533
  info: this.buildDeviceInfo(device),
503
534
  native: { id: device.device.ieeeAddr.substring(2) },
504
- mapped : {},
535
+ mapped : { model: client.modelID || client.type || 'NotSet' },
505
536
  statesDev: [],
506
537
  }
507
- if (device.device.name === 'Coordinator') {
538
+ if (device.name === 'Coordinator') {
508
539
  coordinatorData.icon = 'zigbee.png';
509
- coordinatorData.common = { name: undefined, type: undefined };
510
- } else {
511
540
  coordinatorData.common = { name: 'Coordinator', type: 'Coordinator' };
541
+ } else {
542
+ coordinatorData.common = { name: device.mapped?.model || 'unknown', type: device.type };
512
543
  coordinatorData.icon= 'img/unknown.png';
513
544
  }
514
545
  devices.push(coordinatorData);
515
546
  }
516
-
517
547
  }
518
548
 
519
549
  async getDevices(from, command, id, callback) {
@@ -522,13 +552,14 @@ class Commands {
522
552
  this.adapter.sendTo(from, command, {error: 'No active connection to Zigbee Hardware!'}, callback);
523
553
  return;
524
554
  }
525
- const rooms = await this.adapter.getEnumsAsync('enum.rooms') || {};
555
+ const roomsEnum = await this.adapter.getEnumsAsync('enum.rooms') || {};
526
556
  const deviceObjects = (id ? [await this.adapter.getObjectAsync(id)] : await this.adapter.getDevicesAsync());
527
557
  const all_states = id ? await this.adapter.getStatesAsync(id + '.*') : await this.adapter.getStatesAsync('*');
528
558
  const all_stateDefs = id ? await this.adapter.getStatesOfAsync(id) : await this.adapter.getStatesOfAsync();
529
559
  const illegalDevices = [];
530
560
  const groups = {};
531
561
  const PromiseChain = [];
562
+ const models = { byUID : {}, UIDbyModel: {} };
532
563
  for (const devInfo of deviceObjects) {
533
564
  if (devInfo._id.indexOf('group') > -1) {
534
565
  PromiseChain.push(this.handleGroupforInfo(devInfo, groups));
@@ -537,29 +568,29 @@ class Commands {
537
568
  const modelDesc = statesMapping.findModel(devInfo.common.type);
538
569
  devInfo.icon = (modelDesc && modelDesc.icon) ? modelDesc.icon : 'img/unknown.png';
539
570
  devInfo.vendor = modelDesc ? modelDesc.vendor : '';
540
- const legacyDesc = statesMapping.findModel(devInfo.common.type, true);
541
- devInfo.legacyIcon = (legacyDesc && legacyDesc.icon) ? legacyDesc.icon : undefined;
571
+ devInfo.legacyIcon = statesMapping.getIconforLegacyModel(devInfo.common.type);
542
572
  const lq_state = all_states[`${devInfo._id}.link_quality`];
543
573
  devInfo.link_quality = lq_state ? lq_state.val : -1;
544
574
  devInfo.link_quality_lc = lq_state ? lq_state.lc : undefined;
545
575
  const battery_state = all_states[`${devInfo._id}.battery`];
546
576
  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
577
 
561
578
  }
562
- PromiseChain.push(this.fillInfo(devInfo, all_stateDefs.filter(item => item._id.startsWith(devInfo._id)),all_states));
579
+ devInfo.rooms = [];
580
+ const rooms = roomsEnum['enum.rooms'] || {};
581
+ for (const room of Object.keys(rooms)) {
582
+ if (!rooms.hasOwnProperty(room) ||
583
+ !rooms[room] ||
584
+ !rooms[room].common ||
585
+ !rooms[room].common.members
586
+ ) {
587
+ continue;
588
+ }
589
+ if (rooms[room].common.members.includes(devInfo._id)) {
590
+ devInfo.rooms.push(rooms[room].common.name);
591
+ }
592
+ }
593
+ PromiseChain.push(this.fillInfo(devInfo, all_stateDefs.filter(item => item._id.startsWith(devInfo._id)),all_states, models));
563
594
  }
564
595
  if (!id) {
565
596
  for (const client of this.zbController.getClientIterator(true)) {
@@ -580,7 +611,12 @@ class Commands {
580
611
 
581
612
  this.debug(`getDevices contains ${deviceObjects.length} Devices`);
582
613
  const rv = { devices:deviceObjects, inLog:this.adapter.deviceDebug.logStatus, }
583
- if (!id) rv.byId = this.adapter.deviceDebug.collectDebugData();
614
+ if (!id) {
615
+ rv.deviceDebugData = this.adapter.deviceDebug.collectDebugData();
616
+ rv.localOverrides = this.adapter.stController.localConfig.localData;
617
+ rv.models = models.byUID;
618
+ }
619
+
584
620
  if (this.stController) {
585
621
  rv.clean = this.stController.CleanupRequired();
586
622
  rv.errors = this.stController.getStashedErrors();
@@ -682,32 +718,43 @@ class Commands {
682
718
  }
683
719
  }
684
720
 
685
- deleteZigbeeDevice(from, command, msg, callback) {
721
+ async deleteZigbeeDevice(from, command, msg, callback) {
686
722
  if (this.zbController && this.zbController.herdsmanStarted && this.stController) {
687
723
  this.debug(`deleteZigbeeDevice message: ${JSON.stringify(msg)}`);
688
724
  const id = msg.id;
689
725
  const force = msg.force;
690
- const sysid = id.replace(this.adapter.namespace + '.', '0x');
726
+ const sysid = id.startsWith(this.adapter.namespace) ? id.replace(this.adapter.namespace + '.', '0x') : `0x${id}`;
691
727
  const devId = id.replace(this.adapter.namespace + '.', '');
692
728
  this.debug(`deleteZigbeeDevice sysid: ${sysid}`);
693
729
  const dev = this.zbController.getDevice(sysid);
694
730
  if (!dev) {
731
+
695
732
  this.info(`Attempted to delete device ${devId} - the device is not known to the zigbee controller.`);
696
- this.stController.deleteObj(devId, () =>
697
- this.adapter.sendTo(from, command, {}, callback));
733
+ const err = await this.stController.deleteObj(devId);
734
+ if (err != '') this.adapter.sendTo(from, command, {errror:err}, callback);
735
+ else this.adapter.sendTo(from, command, {}, callback);
698
736
  return;
699
737
  }
700
738
  this.info(`${force ? 'Force removing' : 'Gracefully removing '} device ${devId} from the network.`);
701
- this.zbController.remove(sysid, force, err => {
739
+ this.zbController.remove(sysid, force, async (err) => {
702
740
  if (!err) {
703
741
  this.info('Device removed from the network, deleting objects.')
704
- this.stController.deleteObj(devId, () => {
705
- this.adapter.sendTo(from, command, {}, callback);
706
- });
742
+ if (!force) {
743
+ const err = await this.stController.deleteObj(devId);
744
+ if (err == '') this.adapter.sendTo(from, command, {}, callback);
745
+ else this.adapter.sendTo(from, command, {error:err}, callback);
746
+ }
747
+ this.adapter.sendTo(from, command, {}, callback);
748
+ this.adapter.stController.localConfig.removeLocalData()
707
749
  } else {
708
750
  this.adapter.sendTo(from, command, {error: err}, callback);
709
751
  }
710
752
  });
753
+ if (force) {
754
+ const err = await this.stController.deleteObj(devId);
755
+ if (err != '') this.adapter.sendTo(from, command, {errror:err}, callback);
756
+ else this.adapter.sendTo(from, command, {}, callback);
757
+ }
711
758
  } else {
712
759
  this.adapter.sendTo(from, command, {error: 'No active connection to Zigbee Hardware!'}, callback);
713
760
  }
@@ -802,43 +849,47 @@ class Commands {
802
849
  }
803
850
  }
804
851
 
805
- async updateLocalConfigItems(from, command, msg, callback) {
852
+ async updateConfigItems(from, command, msg, callback) {
806
853
  if (this.stController) {
807
- this.debug(`updateLocalConfigItems : ${JSON.stringify(msg)}`);
808
- const target = msg.target.replace(`${this.adapter.namespace}.`, '');
809
- const entity = await this.zbController.resolveEntity(target);
810
- if (entity && !entity.mapped) {
811
- this.warn('unable to set local Override for device whithout mapped model');
854
+ this.debug(`updateConfigItems : ${JSON.stringify(msg)}`);
855
+ if (msg == {}) {
856
+ this.adapter.sendTo(from, command, {}, callback);
812
857
  return;
813
858
  }
814
- if (msg.data)
859
+ const target = msg.target ? msg.target.replace(`${this.adapter.namespace}.`, '') : '';
860
+ const entity = await this.zbController.resolveEntity(target);
861
+ if (msg.data);
815
862
  {
816
863
  for (const prop in msg.data) {
817
864
  if (prop==='options') {
818
865
  // 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];
866
+ // first: retrieve the global options.
867
+ const newOptions = {};
868
+ const globalOptions = this.stController.localConfig.getLocalOverride(target, entity?.mapped?.model || '', prop, true)?.options;
869
+ if (globalOptions) {
870
+ for (const key of Object.keys(entity.options)) {
871
+ if (globalOptions[key] != undefined)
872
+ newOptions[key] = globalOptions[key];
832
873
  }
833
874
  }
834
- */
835
- this.zbController.callExtensionMethod(
836
- 'onZigbeeEvent',
837
- [{'device': entity.device, 'type': 'deviceOptionsChanged', from: changed_from, to:changed_to || {}, }, entity ? entity.mapped : null]);
875
+ for (const key of Object.keys(msg.data.options)) {
876
+ newOptions[key]= msg.data.options[key];
877
+ }
878
+ if (entity && entity.device) {
879
+ this.zbController.callExtensionMethod(
880
+ 'onZigbeeEvent',
881
+ [{'device': entity.device, 'type': 'deviceOptionsChanged', from: entity.options, to:newOptions || {}, }, entity.mapped]);
882
+ }
883
+ }
884
+ this.warn(`enumerating data: ${JSON.stringify(prop)}`);
885
+ let val = msg.data[prop];
886
+ if (typeof val === 'string') {
887
+ val = val.trim();
888
+ if (val.length < 1) val = '##REMOVE##';
838
889
  }
839
- this.debug('enumerating data: ' + prop);
840
- await this.stController.localConfig.updateLocalOverride(target, (entity ? entity.mapped.model : 'group'), prop, msg.data[prop], msg.global);
890
+ await this.stController.localConfig.updateLocalOverride(target, target, prop, val, msg.global);
841
891
  }
892
+ await this.stController.localConfig.retainData();
842
893
  }
843
894
  try {
844
895
  if (entity) {
@@ -846,8 +897,12 @@ class Commands {
846
897
  this.stController.updateDev(target, entity.mapped.model, entity.mapped.model, () => {this.adapter.sendTo(from, command, {}, callback)});
847
898
  }
848
899
  else {
849
- this.debug('updateLocalConfigItems without Entity');
850
- this.stController.updateDev(target, undefined, 'group',() => {this.adapter.sendTo(from, command, {}, callback)});
900
+ // try to see if it is a model -> find the devices for that model
901
+ const devicesFromObjects = (await this.adapter.getDevicesAsync()).filter(item => item.common.type === target).map((item) => item.native.id);
902
+ for (const device of devicesFromObjects) {
903
+ await this.stController.updateDev(device, target, target);
904
+ }
905
+
851
906
  }
852
907
  }
853
908
  catch (error) {
@@ -875,6 +930,9 @@ class Commands {
875
930
  //const targetModel = msg.model ? msg.model : '';
876
931
  }
877
932
  else {
933
+ if (msg.getAllData) {
934
+ this.adapter.sendTo(from, command, this.stController.localConfig.localData, callback);
935
+ }
878
936
  rv.error = `missing data in message ${JSON.stringify(msg)}`;
879
937
  }
880
938
  }
package/lib/devices.js CHANGED
@@ -3165,9 +3165,16 @@ function setLegacyDevices(legacyModels) {
3165
3165
  }
3166
3166
 
3167
3167
  function getIconforLegacyModel(model) {
3168
+ const mid = findModel(model, true);
3169
+ if (!mid) return undefined;
3170
+ return mid.icon;
3168
3171
 
3169
3172
  }
3170
3173
 
3174
+ function hasLegacyDevice(model) {
3175
+ return Boolean(findModel(model, true));
3176
+ }
3177
+
3171
3178
  module.exports = {
3172
3179
  devices,
3173
3180
  legacy_devices,
@@ -3183,5 +3190,7 @@ module.exports = {
3183
3190
  findModel,
3184
3191
  fillDevicesForLegacy,
3185
3192
  pairedLegacyDevices,
3186
- setLegacyDevices
3193
+ setLegacyDevices,
3194
+ hasLegacyDevice,
3195
+ getIconforLegacyModel,
3187
3196
  };
package/lib/exclude.js CHANGED
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
  const devicedefinitions = require('./devices');
3
+ const utils = require('./utils');
3
4
 
4
5
  class Exclude {
5
6
  constructor(adapter) {
@@ -126,7 +127,7 @@ class Exclude {
126
127
  const devices = this.zbController.getClients();
127
128
  const excludables = [];
128
129
  for (const device of devices) {
129
- const obj = await this.adapter.getObjectAsync(device.ieeeAddr.substr(2));
130
+ const obj = await this.adapter.getObjectAsync(utils.zbIdorIeeetoAdId(this.adapter, device.ieeeAddr, false));//device.ieeeAddr.substr(2));
130
131
  if (obj && obj.common && obj.common.type) {
131
132
  excludables.push(obj.common.tyoe);
132
133
  }
package/lib/exposes.js CHANGED
@@ -259,7 +259,7 @@ function createFromExposes(model, def, device, log) {
259
259
 
260
260
  // maybee here check manufacturerName for tuya devices
261
261
  if (typeof def.exposes == 'function') {
262
- const expFunction = def.exposes(device === undefined ? {isDummyDevice: true} : device, {}); // maybee here check manufacturerName for tuya devices
262
+ const expFunction = def.exposes((device === undefined || device === null) ? {isDummyDevice: true} : device, {}); // maybee here check manufacturerName for tuya devices
263
263
  for (const expose of expFunction) {
264
264
  genStateFromExpose(expose);
265
265
  }
@@ -344,7 +344,22 @@ function createFromExposes(model, def, device, log) {
344
344
  epname: expose.endpoint,
345
345
  setattr: 'brightness',
346
346
  }, prop.access);
347
- pushToStates(statesDefs.brightness_move, prop.access);
347
+ //pushToStates(statesDefs.brightness_move, prop.access);
348
+ pushToStates({
349
+ id: 'brightness_move',
350
+ prop: 'brightness_move',
351
+ name: `Dimming ${expose.endpoint ? expose.endpoint : ''}`.trim(),
352
+ icon: undefined,
353
+ role: 'state',
354
+ write: true,
355
+ read: false,
356
+ type: 'number',
357
+ min: -50, // ignore expose.value_min
358
+ max: 50, // ignore expose.value_max
359
+ epname: expose.endpoint,
360
+ setattr: 'brightness_move',
361
+ }, prop.access);
362
+ //pushToStates(statesDefs.brightness_move, prop.access);
348
363
  break;
349
364
  }
350
365
  case 'color_temp': {
@@ -801,7 +816,7 @@ function createFromExposes(model, def, device, log) {
801
816
 
802
817
  case 'climate':
803
818
  for (const prop of expose.features) {
804
- switch (prop.name) {
819
+ /* switch (prop.name) {
805
820
  case 'away_mode':
806
821
  pushToStates(statesDefs.climate_away_mode, prop.access);
807
822
  break;
@@ -814,7 +829,8 @@ function createFromExposes(model, def, device, log) {
814
829
  default:
815
830
  pushToStates(genState(prop), prop.access);
816
831
  break;
817
- }
832
+ }*/
833
+ pushToStates(genState(prop), prop.access);
818
834
  }
819
835
  break;
820
836
 
package/lib/groups.js CHANGED
@@ -629,7 +629,7 @@ class Groups {
629
629
  const entity = await this.zbController.resolveEntity(member.ieee, member.epid);
630
630
  let epname = undefined;
631
631
  if (entity && entity.mapped && entity.mapped.endpoint) {
632
- const epnames = entity.mapped.endpoint();
632
+ const epnames = entity.mapped.endpoint(entity);
633
633
  for (const key in epnames) {
634
634
  if (epnames[key] == member.epid) {
635
635
  epname = key;
@@ -657,7 +657,7 @@ class Groups {
657
657
  this.rebuildGroupMemberStateList(j, memberInfo);
658
658
  }
659
659
 
660
- const name = this.stController.localConfig.NameForId(id, 'group', groups[j]);
660
+ const name = await this.stController.localConfig.NameForId(id, 'group', groups[j]);
661
661
  const icon = this.stController.localConfig.IconForId(id, 'group', await this.stController.getDefaultGroupIcon(id));
662
662
  chain.push(new Promise(resolve => {
663
663
  const isActive = false;