iobroker.zigbee 3.1.2 → 3.1.4

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/exposes.js CHANGED
@@ -7,7 +7,7 @@ const utils = require('./utils');
7
7
  const colors = require('./colors');
8
8
  const ea = require('zigbee-herdsman-converters/lib/exposes').access;
9
9
 
10
-
10
+ const LocalData = { options:{} };
11
11
  const __logger = undefined;
12
12
 
13
13
  function genState(expose, role, name, desc) {
@@ -819,9 +819,28 @@ function createFromExposes(model, def, device, log) {
819
819
  break;
820
820
 
821
821
  case 'composite':
822
+ {
823
+ const stname = (expose.property);
824
+ const stateId = stname.replace(/\*/g, '');
825
+ const stateName = (expose.description || expose.name);
826
+ const channelID = LocalData.options.newCompositeMethod ? `${stateId}.` : '';
827
+ if (LocalData.options.newCompositeMethod) {
828
+
829
+ if (typeof stname !== 'string') break;
830
+ pushToStates({
831
+ id: stateId,
832
+ name: stateName,
833
+ icon: undefined,
834
+ role: 'state',
835
+ write: expose.access & ea.SET,
836
+ read: expose.access & ea.GET,
837
+ type: 'string',
838
+ }, expose.access)
839
+
840
+ }
822
841
  for (const prop of expose.features) {
823
842
  if (prop.type == 'numeric') {
824
- const st = genState(prop);
843
+ const st = genState(prop, 'state', `${channelID}${prop.name}`);
825
844
  st.prop = expose.property;
826
845
  st.inOptions = true;
827
846
  // I'm not fully sure, as it really needed, but
@@ -831,27 +850,34 @@ function createFromExposes(model, def, device, log) {
831
850
  result[expose.property] = options;
832
851
  return result;
833
852
  };
834
- // if we have a composite expose, the value have to be an object {expose.property : {prop.property: value}}
835
- if (prop.access & ea.SET) {
836
- st.setter = (value, options) => {
837
- const result = {};
838
- options[prop.property] = value;
839
- result[expose.property] = options;
840
- return result;
841
- };
842
- st.setattr = expose.property;
843
- }
844
- // if we have a composite expose, the payload will be an object {expose.property : {prop.property: value}}
845
- if (prop.access & ea.STATE) {
846
- st.getter = payload => {
847
- if ((payload.hasOwnProperty(expose.property)) && (payload[expose.property] !== null) && payload[expose.property].hasOwnProperty(prop.property)) {
848
- return !isNaN(payload[expose.property][prop.property]) ? payload[expose.property][prop.property] : undefined;
849
- } else {
850
- return undefined;
851
- }
852
- };
853
- } else {
854
- st.getter = payload => undefined;
853
+ if (LocalData.options.newCompositeMethod) {
854
+ st.compositeKey = stateId;
855
+ st.compositeTimeout = 250;
856
+ st.compositeState = stateId;
857
+ } else
858
+ {
859
+ // if we have a composite expose, the value have to be an object {expose.property : {prop.property: value}}
860
+ if (prop.access & ea.SET) {
861
+ st.setter = (value, options) => {
862
+ const result = {};
863
+ options[prop.property] = value;
864
+ result[expose.property] = options;
865
+ return result;
866
+ };
867
+ st.setattr = expose.property;
868
+ }
869
+ // if we have a composite expose, the payload will be an object {expose.property : {prop.property: value}}
870
+ if (prop.access & ea.STATE) {
871
+ st.getter = payload => {
872
+ if ((payload.hasOwnProperty(expose.property)) && (payload[expose.property] !== null) && payload[expose.property].hasOwnProperty(prop.property)) {
873
+ return !isNaN(payload[expose.property][prop.property]) ? payload[expose.property][prop.property] : undefined;
874
+ } else {
875
+ return undefined;
876
+ }
877
+ };
878
+ } else {
879
+ st.getter = payload => undefined;
880
+ }
855
881
  }
856
882
  pushToStates(st, prop.access);
857
883
  }
@@ -894,6 +920,7 @@ function createFromExposes(model, def, device, log) {
894
920
  }
895
921
  }
896
922
  break;
923
+ }
897
924
  case 'list':
898
925
  // is not mapped
899
926
  // for (const prop of expose) {
@@ -909,7 +936,10 @@ function createFromExposes(model, def, device, log) {
909
936
 
910
937
  }
911
938
 
912
- async function applyExposeForDevice(mappedDevices, byModel, device) {
939
+ async function applyExposeForDevice(mappedDevices, byModel, device, options) {
940
+ if (options && typeof options == 'object') {
941
+ LocalData.options = options;
942
+ }
913
943
  const deviceDef = await zigbeeHerdsmanConverters.findByDevice(device);
914
944
  if (!deviceDef) {
915
945
  return undefined;
package/lib/groups.js CHANGED
@@ -216,7 +216,6 @@ class Groups {
216
216
  break;
217
217
  }
218
218
  }
219
-
220
219
  }
221
220
  }
222
221
  }
@@ -240,12 +239,9 @@ class Groups {
240
239
  }
241
240
  }
242
241
  }
243
-
244
242
  }
245
243
  }
246
244
 
247
-
248
-
249
245
  static readables = {
250
246
  state: { cluster:6, attributes:['onOff']},
251
247
  brightness: { cluster:8, attributes:['currentLevel']},
@@ -260,16 +256,21 @@ class Groups {
260
256
  const endpoint = entity.endpoint;
261
257
  const mappedModel = entity.mapped;
262
258
  if (!mappedModel) return;
259
+ const obj = await this.adapter.getObjectAsync((member.ieee.includes('x') ? member.ieee.split('x')[1] : member.ieee));
260
+ if (obj && obj.common.deactivated) {
261
+ this.debug(`omitting reading member state for deactivated device ${member.ieee}`);
262
+ return;
263
+ }
263
264
  const converter = mappedModel.toZigbee.find(c => c && (c.key.includes(item.state)));
264
265
  const canReadViaConverter = converter && converter.hasOwnProperty('convertGet');
265
266
  if (canReadViaConverter) {
266
267
  try {
267
268
  await converter.convertGet(endpoint, item.state, {device:entity.device});
268
269
  } catch (error) {
269
- this.warn(`reading ${item.state} from ${member.id}${member.ieee ? '/' + member.epid : ''} via convertGet failed with ${error && error.message ? error.message : 'no reason given'}`);
270
+ this.debug(`reading ${item.state} from ${member.id}${member.ieee ? '/' + member.epid : ''} via convertGet failed with ${error && error.message ? error.message : 'no reason given'}`);
270
271
  return {unread:member.device};
271
272
  }
272
- this.warn(`reading ${item.state} from ${member.ieee}${member.ep ? '/' + member.epid : ''} via convertGet succeeded` )
273
+ this.debug(`reading ${item.state} from ${member.ieee}${member.ep ? '/' + member.epid : ''} via convertGet succeeded` );
273
274
  return {read:member.device};
274
275
  }
275
276
  if (toRead.cluster) {
@@ -277,25 +278,23 @@ class Groups {
277
278
  if (entity.endpoint.inputClusters.includes(toRead.cluster)) {
278
279
  try {
279
280
  const result = await endpoint.read(toRead.cluster, toRead.attributes,{disableDefaultResponse : true });
280
- this.warn(`readGroupMemberStatus for ${item.state} from ${member.ieee}${member.ep ? '/' + member.epid : ''} resulted in ${JSON.stringify(result)}`);
281
+ this.debug(`readGroupMemberStatus for ${item.state} from ${member.ieee}${member.ep ? '/' + member.epid : ''} resulted in ${JSON.stringify(result)}`);
281
282
  }
282
283
  catch (error) {
283
- this.warn(`reading ${item.state} from ${member.ieee}${member.ep ? '/' + member.epid : ''} via endpoint.read with ${toRead.cluster}, ${JSON.stringify(toRead.attributes)} resulted in ${error && error.message ? error.message : 'an unspecified error'}`);
284
+ this.debug(`reading ${item.state} from ${member.ieee}${member.ep ? '/' + member.epid : ''} via endpoint.read with ${toRead.cluster}, ${JSON.stringify(toRead.attributes)} resulted in ${error && error.message ? error.message : 'an unspecified error'}`);
284
285
  }
285
- this.warn(`reading ${item.state} from ${member.ieee}${member.ep ? '/' + member.epid : ''} via endpoint.read with ${toRead.cluster}, ${JSON.stringify(toRead.attributes)} succeeded`);
286
+ this.debug(`reading ${item.state} from ${member.ieee}${member.ep ? '/' + member.epid : ''} via endpoint.read with ${toRead.cluster}, ${JSON.stringify(toRead.attributes)} succeeded`);
286
287
  }
287
- else this.warn(`omitting cluster ${toRead.cluster} - not supported`);
288
+ else this.debug(`omitting cluster ${toRead.cluster} - not supported`);
288
289
  }
289
290
  else {
290
- this.warn(`unable to read ${item.state} from ${member.id}${member.ep ? '/' + member.epid : ''} - unable to resolve the entity`);
291
+ this.debug(`unable to read ${item.state} from ${member.id}${member.ep ? '/' + member.epid : ''} - unable to resolve the entity`);
291
292
  }
292
293
  return;
293
294
  }
294
295
  }
295
296
 
296
297
  async readGroupMemberStatus(obj, item) {
297
- obj.warn(`rgms with ${JSON.stringify(item)}`);
298
-
299
298
  const toRead = Groups.readables[item.state]
300
299
  if (toRead) {
301
300
  const members = await obj.zbController.getGroupMembersFromController(item.id);
@@ -465,11 +464,10 @@ class Groups {
465
464
 
466
465
  async renameGroup(from, command, message) {
467
466
  this.debug(`rename group called with ${from}, ${command}, ${JSON.stringify(message)}`);
468
- // const groupsEntry = await this.adapter.getStateAsync('info.groups');
469
- // const objGroups = (groupsEntry && groupsEntry.val ? JSON.parse(groupsEntry.val) : {});
470
- const name = message.name;
471
467
  const id = `group_${message.id}`;
472
- let icon = this.stController.localConfig.IconForId(id, 'group', await this.stController.getDefaultGroupIcon(id));
468
+
469
+ this.stController.localConfig.updateDeviceName(id, message.name);
470
+
473
471
  try {
474
472
  const group = await this.zbController.verifyGroupExists(message.id);
475
473
  if (message.remove) {
@@ -478,16 +476,13 @@ class Groups {
478
476
  this.debug('trying to remove ' + member.id + (member.ep ? '.'+member.ep : '') + ' ' + ' from group ' + message.id + ' response is '+JSON.stringify(response));
479
477
  }
480
478
  }
481
- if (icon.match(/img\/group_\d+\.png/g)) {
482
- icon = await this.zbController.rebuildGroupIcon(group);
483
- }
484
479
  } catch (e) {
485
480
  this.warn('renameGroup caught error ' + (e && e.message ? e.message : 'no message'));
486
481
  if (e && e.hasOwnProperty('code')) {
487
482
  this.warn(`renameGroup caught error ${JSON.stringify(e.code)}`);
488
483
  }
489
484
  }
490
- this.debug(`rename group name ${name}, id ${id}, icon ${icon} remove ${JSON.stringify(message.removeMembers)}`);
485
+ this.debug(`rename group name ${message.name}, id ${id}, remove ${JSON.stringify(message.removeMembers)}`);
491
486
  this.syncGroups([parseInt(message.id)]);
492
487
  }
493
488
 
@@ -601,10 +596,13 @@ class Groups {
601
596
  if (!this.GroupData.states[id]) return undefined;
602
597
  const val = this.GroupData.states[id].val;
603
598
  if (val == null || val == undefined) {
604
- const obj = await this.adapter.getStateAsync(id);
605
- if (obj) {
606
- this.GroupData.states[id].val = obj.val;
607
- return obj.val;
599
+ const ai = getAdId(this.adapter, id);
600
+ const obj = await this.adapter.getObjectAsync(ai);
601
+ if (obj && obj.common.deactivated) return undefined;
602
+ const state = await this.adapter.getStateAsync(id);
603
+ if (state) {
604
+ this.GroupData.states[id].val = state.val;
605
+ return state.val;
608
606
  }
609
607
  return undefined;
610
608
  }
@@ -617,7 +615,7 @@ class Groups {
617
615
  // get all group id's from the database and the respective names from the local overrides (if present)
618
616
  const groups = await this.getGroups();
619
617
  const chain = [];
620
- const usedGroupsIds = Object.keys(groups);
618
+ const usedGroupsIds = numericGroupIdArray.length > 0 ? Object.keys(groups) : [];
621
619
  for (const j in groups) {
622
620
  if (numericGroupIdArray.length > 0 && !numericGroupIdArray.includes(Number(j))) continue; // skip groups we didnt ask for
623
621
  this.debug(`Analysing group_${JSON.stringify(j)}`);
@@ -659,7 +657,7 @@ class Groups {
659
657
  this.rebuildGroupMemberStateList(j, memberInfo);
660
658
  }
661
659
 
662
- const name = groups[j];
660
+ const name = this.stController.localConfig.NameForId(id, 'group', groups[j]);
663
661
  const icon = this.stController.localConfig.IconForId(id, 'group', await this.stController.getDefaultGroupIcon(id));
664
662
  chain.push(new Promise(resolve => {
665
663
  const isActive = false;
@@ -668,7 +666,7 @@ class Groups {
668
666
  common: {name: name, type: 'group', icon: icon },
669
667
  native: {id: j}
670
668
  }, () => {
671
- this.adapter.extendObject(id, {common: {type: 'group', icon: icon}});
669
+ this.adapter.extendObject(id, {common: {name: name, type: 'group', icon: icon}});
672
670
  // create writable states for groups from their devices
673
671
  for (const stateInd in statesMapping.groupStates) {
674
672
  if (!statesMapping.groupStates.hasOwnProperty(stateInd)) {
@@ -1,10 +1,8 @@
1
1
  'use strict';
2
2
 
3
- /*eslint no-unused-vars: ['off']*/
4
3
  const fs = require('fs');
5
4
  const path = require('path');
6
5
  const utils = require('@iobroker/adapter-core'); // Get common adapter utils
7
- // const { src } = require('gulp');
8
6
 
9
7
  const EventEmitter = require('events').EventEmitter;
10
8
 
@@ -20,7 +18,6 @@ class localConfig extends EventEmitter {
20
18
  this.adapter.on('ready', () => this.onReady());
21
19
  }
22
20
 
23
-
24
21
  async onReady()
25
22
  {
26
23
  }
@@ -116,9 +113,12 @@ class localConfig extends EventEmitter {
116
113
  this.error(`update called with illegal id data:${JSON.stringify(target)}:${JSON.stringify(key)}`)
117
114
  return false;
118
115
  }
119
- const base = global ? this.localData.by_model[target] || {} : this.localData.by_id[target] || {};
120
116
  const rv = {};
121
- if (base.hasOwnProperty(key)) rv[key] = base[key];
117
+ if ((this.localData.byModel[target] || {}).hasOwnProperty(key))
118
+ rv[key] = this.localData.byModel[target] || {}[key];
119
+ if (global) return rv;
120
+ if ((this.localData.by_id[_target] || {}).hasOwnProperty(key))
121
+ rv[key] = this.localData.by_id[_target] || {}[key];
122
122
  return rv;
123
123
  }
124
124
 
@@ -285,7 +285,7 @@ class localConfig extends EventEmitter {
285
285
  }
286
286
 
287
287
  async retainData() {
288
- //this.warn('retaining local config: ' + JSON.stringify(this.localData));
288
+ this.debug('retaining local config: ' + JSON.stringify(this.localData));
289
289
  try {
290
290
  fs.writeFileSync(this.filename, JSON.stringify(this.localData, null, 2))
291
291
  this.info('Saved local configuration data');
@@ -304,7 +304,6 @@ class localConfig extends EventEmitter {
304
304
  const fn = path.join(item.parentPath, item.name);
305
305
  rv.push({file: fn, name: item.name, data: fs.readFileSync(path.join(item.parentPath, item.name), 'base64'), isBase64:true});
306
306
  });
307
- //this.warn('enumerateImages for ' + _path + ' is ' + JSON.stringify(rv));
308
307
  }
309
308
  catch (error) {
310
309
  this.error(`error in enumerateImages : ${error.message ? error.message : 'undefined error'}`)
@@ -312,13 +311,14 @@ class localConfig extends EventEmitter {
312
311
  return rv;
313
312
  }
314
313
 
315
- getOptions(dev_id, model_id) {
314
+ getOptions(_dev_id, model_id) {
316
315
  function extractOptions(target, options) {
317
316
  if (typeof target != 'object') target = {};
318
317
  if (typeof options != 'object') return target;
319
318
  Object.keys(options).forEach((option) => target[option] = options[option]);
320
319
  return target;
321
320
  }
321
+ const dev_id = _dev_id.startsWith('0x') ? _dev_id.substring(2) : _dev_id
322
322
 
323
323
  const ld = this.localData.by_id[dev_id];
324
324
  const gd = this.localData.by_model[model_id];
package/lib/networkmap.js CHANGED
@@ -4,6 +4,7 @@ class NetworkMap {
4
4
  constructor(adapter) {
5
5
  this.adapter = adapter;
6
6
  this.adapter.on('message', this.onMessage.bind(this));
7
+ this.mapdata = undefined;
7
8
  }
8
9
 
9
10
  start(zbController, stController) {
@@ -37,16 +38,23 @@ class NetworkMap {
37
38
  switch (obj.command) {
38
39
  case 'getMap':
39
40
  if (obj && obj.message && typeof obj.message === 'object') {
40
- this.getMap(obj.from, obj.command, obj.callback);
41
+ this.getMap(obj.from, obj.command, obj.message, obj.callback);
41
42
  }
42
43
  break;
43
44
  }
44
45
  }
45
46
  }
46
47
 
47
- getMap(from, command, callback) {
48
+ getMap(from, command, message, callback) {
49
+ if (message.forcebuild) this.mapdata = undefined;
50
+ if (this.mapdata) {
51
+ this.mapdata.errors = {};
52
+ this.adapter.sendTo(from, command, this.mapdata, callback)
53
+ return;
54
+ }
48
55
  if (this.zbController) {
49
56
  this.zbController.getMap(networkmap => {
57
+ this.mapdata = networkmap;
50
58
  this.adapter.log.debug(`getMap result: ${JSON.stringify(networkmap)}`);
51
59
  this.adapter.sendTo(from, command, networkmap, callback);
52
60
  });