iobroker.zigbee 1.10.13 → 2.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/devices.js CHANGED
@@ -306,7 +306,8 @@ const generator = {
306
306
  setattr: 'state',
307
307
  });
308
308
  }
309
- if (endpoint.supportsOutputCluster('genMultistateInput')) {
309
+ if (endpoint.supportsOutputCluster('genMultistateInput') || endpoint.clusters.hasOwnProperty('genMultistateInput'))
310
+ {
310
311
  devstates.push({
311
312
  id: `channel_${epID}.click`,
312
313
  prop: 'action',
@@ -343,6 +344,18 @@ const generator = {
343
344
  isEvent: true,
344
345
  getter: payload => payload.action === `tripple_${epName}` ? true : undefined,
345
346
  });
347
+ devstates.push({
348
+ id: `channel_${epID}.hold`,
349
+ prop: 'action',
350
+ name: `long click event`,
351
+ icon: undefined,
352
+ role: 'button',
353
+ write: false,
354
+ read: true,
355
+ type: 'boolean',
356
+ isEvent: true,
357
+ getter: payload => payload.action === `hold_${epName}` ? true : undefined,
358
+ });
346
359
  }
347
360
  if (endpoint.supportsOutputCluster('genLevelCtrl')) {
348
361
  devstates.push({
@@ -487,7 +500,7 @@ const generator = {
487
500
  });
488
501
  return icasa_states;
489
502
  },
490
- ptvo_switch: (entity) => {
503
+ /*ptvo_switch: (entity) => {
491
504
  const devstates = [];
492
505
  for (const endpoint of entity.device.endpoints) {
493
506
  const epID = endpoint.ID;
@@ -566,7 +579,7 @@ const generator = {
566
579
  }
567
580
  }
568
581
  return devstates;
569
- },
582
+ },*/
570
583
  };
571
584
 
572
585
  function states_with_epname(entity, states) {
@@ -3094,6 +3107,10 @@ const devices = [
3094
3107
  models: ['E2204'],
3095
3108
  icon: 'img/E2204.png',
3096
3109
  },
3110
+ {
3111
+ models: ['L1(ZW)'],
3112
+ icon: 'img/TS0502B.png',
3113
+ },
3097
3114
  ];
3098
3115
 
3099
3116
  const commonStates = [
package/lib/exposes.js CHANGED
@@ -681,12 +681,15 @@ function createFromExposes(model, def) {
681
681
  if (!Array.isArray(expose.values)) break;
682
682
  const hasHold = expose.values.find((actionName) => actionName.includes('hold'));
683
683
  const hasRelease = expose.values.find((actionName) => actionName.includes('release'));
684
+ const hasPress = expose.values.find((actionName) => actionName.includes('press'));
685
+ const hasPressRelease = expose.values.find((actionName) => actionName.includes('press_release'));
684
686
  for (const actionName of expose.values) {
685
687
  // is release state ? - skip
686
688
  if (hasHold && hasRelease && actionName.includes('release')) continue;
687
689
  // is hold state ?
688
690
  if (hasHold && hasRelease && actionName.includes('hold')) {
689
691
  const releaseActionName = actionName.replace('hold', 'release');
692
+ const releaseActionName2 = actionName.concat('_release');
690
693
  state = {
691
694
  id: actionName.replace(/\*/g, ''),
692
695
  prop: 'action',
@@ -696,9 +699,11 @@ function createFromExposes(model, def) {
696
699
  write: false,
697
700
  read: true,
698
701
  type: 'boolean',
699
- getter: payload => payload.action === actionName ? true : (payload.action === releaseActionName ? false : undefined),
702
+ getter: payload => payload.action === actionName ? true : (payload.action === releaseActionName || payload.action === releaseActionName2 ? false : undefined),
700
703
  };
701
- } else {
704
+ } else if (hasPress && hasPressRelease && actionName.includes('press')) {
705
+ let getterKey = actionName.concat('_release');
706
+ if (expose.values.indexOf(getterKey) < 0) getterKey = actionName;
702
707
  state = {
703
708
  id: actionName.replace(/\*/g, ''),
704
709
  prop: 'action',
@@ -708,10 +713,23 @@ function createFromExposes(model, def) {
708
713
  write: false,
709
714
  read: true,
710
715
  type: 'boolean',
716
+ getter: payload => payload.action === getterKey ? true : undefined,
717
+ isEvent: true,
718
+ };
719
+ } else {
720
+ state = {
721
+ id: actionName.replace(/\*/g, ''),
722
+ prop: 'action',
723
+ name: actionName,
724
+ icon: undefined,
725
+ role: 'button',
726
+ write: false,
727
+ read: true,
728
+ type: 'boolean',
711
729
  getter: payload => payload.action === actionName ? true : undefined,
712
730
  isEvent: true,
713
731
  };
714
- }
732
+ };
715
733
  pushToStates(state, expose.access);
716
734
  }
717
735
  state = null;
package/lib/groups.js CHANGED
@@ -85,7 +85,7 @@ class Groups {
85
85
  }
86
86
 
87
87
  } catch (error) {
88
- if (error) this.error(`getGroupMembersFromController: error is ${JSON.stringify(error)} ${JSON.stringify(new Error().stack)}`);
88
+ if (error) this.error(`getGroupMembersFromController: error is ${(error && error.message ? error.message : 'no error message')} ${(error && error.stack ? error.stack : 'no call stack')}`);
89
89
  else this.error('unidentifed error in getGroupMembersFromController');
90
90
  }
91
91
  return members;
@@ -93,28 +93,28 @@ class Groups {
93
93
 
94
94
  async getGroups(obj) {
95
95
  const response = {groups: {}};
96
- try {
97
- // const groupsState = await this.adapter.getStateAsync('info.groups');
98
- const herdsmanGroups = await this.zbController.getGroups();
99
-
100
- // const groups = (groupsState && groupsState.val) ? JSON.parse(groupsState.val) : {};
101
96
 
102
- const groups = {};
103
- if (typeof herdsmanGroups === 'object') {
104
- for (const group of herdsmanGroups) {
105
- const gid = group.groupID;
106
- if (gid) {
107
- groups[gid] = this.stController.verifyDeviceName(`group_${gid}`, `Group ${gid}`);
97
+ const isEnable = await this.adapter.getStateAsync('info.connection');
98
+ if (isEnable.val) {
99
+ try {
100
+ const herdsmanGroups = await this.zbController.getGroups();
101
+ const groups = {};
102
+ if (typeof herdsmanGroups === 'object') {
103
+ for (const group of herdsmanGroups) {
104
+ const gid = group.groupID;
105
+ if (gid) {
106
+ groups[gid] = this.stController.verifyDeviceName(`group_${gid}`, `Group ${gid}`);
107
+ }
108
108
  }
109
109
  }
110
+ this.debug(`getGroups result: ${JSON.stringify(groups)}`);
111
+ response.groups = groups;
112
+ } catch (error) {
113
+ response.error = `res getGroups: caught error: ${error}`;
114
+ this.error(`getGroups: caught error: ${error}`);
115
+ } finally {
116
+ obj && this.adapter.sendTo(obj.from, obj.command, response, obj.callback);
110
117
  }
111
- this.debug(`getGroups result: ${JSON.stringify(groups)}`);
112
- response.groups = groups;
113
- } catch (error) {
114
- response.error = `getGroups: caught error: ${error}`;
115
- this.error(`getGroups: caught error: ${error}`);
116
- } finally {
117
- obj && this.adapter.sendTo(obj.from, obj.command, response, obj.callback);
118
118
  }
119
119
  return response.groups;
120
120
  }
@@ -138,7 +138,7 @@ class Groups {
138
138
  for (const gpid of groups[epid]) {
139
139
  const gpidn = parseInt(gpid);
140
140
  if (gpidn < 0) {
141
- this.warn(`calling removeDevFromGroup with ${sysid}, ${-gpidn}, ${epid}` );
141
+ this.debug(`calling removeDevFromGroup with ${sysid}, ${-gpidn}, ${epid}` );
142
142
  const response = await this.zbController.removeDevFromGroup(sysid, (-gpidn), epid);
143
143
  if (response && response.error) {
144
144
  errors.push(response.error);
@@ -146,7 +146,7 @@ class Groups {
146
146
  }
147
147
 
148
148
  } else if (gpidn > 0) {
149
- this.warn(`calling addDevToGroup with ${sysid}, ${gpidn}, ${epid}` );
149
+ this.debug(`calling addDevToGroup with ${sysid}, ${gpidn}, ${epid}` );
150
150
  const response = await this.zbController.addDevToGroup(sysid, (gpidn), epid);
151
151
  if (response && response.error) {
152
152
  errors.push(response.error);
@@ -158,7 +158,7 @@ class Groups {
158
158
  }
159
159
  }
160
160
  } catch (e) {
161
- this.warn('caught error ' + JSON.stringify(e) + ' in updateGroupMembership');
161
+ this.warn('caught error ' + (e && e.message ? e.message : 'no error message') + ' in updateGroupMembership');
162
162
  this.adapter.sendTo(from, command, {error: e}, callback);
163
163
  return;
164
164
  }
@@ -207,7 +207,7 @@ class Groups {
207
207
 
208
208
  async deleteGroup(from, command, message) {
209
209
  await this.zbController.removeGroupById(message);
210
- await this.stController.deleteGroupById(`group_${parseInt(message)}`);
210
+ await this.stController.deleteObj(`group_${parseInt(message)}`);
211
211
  }
212
212
 
213
213
  async renameGroup(from, command, message) {
@@ -223,8 +223,6 @@ class Groups {
223
223
  this.warn(`renameGroup caught error ${JSON.stringify(e.code)}`);
224
224
  }
225
225
  }
226
- // objGroups[message.id.toString()] = message.name;
227
- // await this.adapter.setStateAsync('info.groups', JSON.stringify(objGroups), true);
228
226
 
229
227
  const group = await this.adapter.getStateAsync(id);
230
228
  if (!group) {
@@ -306,7 +304,7 @@ class Groups {
306
304
  if (dev.common.type === 'group') {
307
305
  const groupid = parseInt(dev.native.id);
308
306
  if (!usedGroupsIds.includes(groupid)) {
309
- this.stController.deleteDeviceStates(`group_${groupid}`);
307
+ this.stController.deleteObj(`group_${groupid}`);
310
308
  }
311
309
  }
312
310
  });
@@ -8,7 +8,7 @@ const fs = require('fs');
8
8
  const axios = require('axios');
9
9
 
10
10
  let savedDeviceNamesDB = {};
11
- const knownUndefinedDevices = {};
11
+
12
12
 
13
13
  class StatesController extends EventEmitter {
14
14
  constructor(adapter) {
@@ -54,9 +54,9 @@ class StatesController extends EventEmitter {
54
54
  retainDeviceNames() {
55
55
  clearTimeout(this.retTimeoutHandle);
56
56
  this.retTimeoutHanlde = setTimeout(() => {
57
- fs.writeFile(this.dev_names_fn, JSON.stringify(savedDeviceNamesDB, null, 2), err => {
58
- if (err) {
59
- this.error(`error saving device names: ${JSON.stringify(err)}`);
57
+ fs.writeFile(this.dev_names_fn, JSON.stringify(savedDeviceNamesDB, null, 2), error => {
58
+ if (error) {
59
+ this.error(`error saving device names: ${(error && error.message ? error.message : 'no error message')} ${(error && error.stack ? error.stack : 'no call stack')}`);
60
60
  } else {
61
61
  this.debug('saved device names');
62
62
  }
@@ -75,7 +75,6 @@ class StatesController extends EventEmitter {
75
75
  }
76
76
  });
77
77
 
78
- this.adapter.setStateAsync(`info.undefinedDevices`, JSON.stringify(knownUndefinedDevices), true);
79
78
  }
80
79
 
81
80
  checkDebugDevice(dev) {
@@ -95,9 +94,6 @@ class StatesController extends EventEmitter {
95
94
  if (!this.adapter.zbController || !this.adapter.zbController.connected()) {
96
95
  return;
97
96
  }
98
- if (this.debugDevices === undefined) {
99
- this.getDebugDevices();
100
- }
101
97
  if (state && !state.ack) {
102
98
  if (id.endsWith('pairingCountdown') || id.endsWith('pairingMessage') || id.endsWith('connection')) {
103
99
  return;
@@ -112,7 +108,8 @@ class StatesController extends EventEmitter {
112
108
  return;
113
109
  }
114
110
 
115
- if (this.checkDebugDevice(id)) this.warn(`ELEVATED: User stateChange ${id} ${JSON.stringify(state)}`);
111
+ if (this.checkDebugDevice(id))
112
+ this.warn(`ELEVATED O01: User state change of state ${id} with value ${state.val} (ack: ${state.ack}) from ${state.from}`);
116
113
 
117
114
  this.debug(`User stateChange ${id} ${JSON.stringify(state)}`);
118
115
  const devId = getAdId(this.adapter, id); // iobroker device id
@@ -191,13 +188,7 @@ class StatesController extends EventEmitter {
191
188
  } else {
192
189
  stateModel = statesMapping.findModel(model);
193
190
  if (!stateModel) {
194
- if (knownUndefinedDevices[deviceId]) {
195
- knownUndefinedDevices[deviceId]++;
196
- } else {
197
- knownUndefinedDevices[deviceId] = 1;
198
- this.info(`Device ${deviceId} "${model}" not present in statesMapping - relying on exposes for device definition.`);
199
- }
200
- this.adapter.setStateAsync(`info.undefinedDevices`, JSON.stringify(knownUndefinedDevices), true);
191
+ this.info(`Device ${deviceId} "${model}" not present in statesMapping - relying on exposes for device definition.`);
201
192
  states = statesMapping.commonStates;
202
193
  } else {
203
194
  states = stateModel.states;
@@ -217,15 +208,14 @@ class StatesController extends EventEmitter {
217
208
  }
218
209
 
219
210
  async publishFromState(deviceId, model, stateKey, state, options) {
220
- if (this.debugDevices === undefined) this.getDebugDevices();
221
211
  this.debug(`Change state '${stateKey}' at device ${deviceId} type '${model}'`);
222
212
  const elevated = this.checkDebugDevice(deviceId);
223
213
 
224
- if (elevated) this.warn(`ELEVATED Change state '${stateKey}' at device ${deviceId} type '${model}'`);
214
+ if (elevated) this.warn(`ELEVATED O02: Change state '${stateKey}' at device ${deviceId} type '${model}'`);
225
215
 
226
216
  const devStates = await this.getDevStates(deviceId, model);
227
217
  if (!devStates) {
228
- if (elevated) this.error(`ELEVATED no device states for device ${deviceId} type '${model}'`);
218
+ if (elevated) this.error(`ELEVATED OE1: no device states for device ${deviceId} type '${model}'`);
229
219
  return;
230
220
  }
231
221
  const commonStates = statesMapping.commonStates.find(statedesc => stateKey === statedesc.id);
@@ -238,7 +228,8 @@ class StatesController extends EventEmitter {
238
228
 
239
229
  const value = state.val;
240
230
  if (value === undefined || value === '') {
241
- if (elevated) this.error(`ELEVATED no value for device ${deviceId} type '${model}'`);
231
+ if (elevated)
232
+ this.error(`ELEVATED OE2: no value for device ${deviceId} type '${model}'`);
242
233
  return;
243
234
  }
244
235
  let stateList = [{stateDesc: stateDesc, value: value, index: 0, timeout: 0}];
@@ -308,18 +299,7 @@ class StatesController extends EventEmitter {
308
299
  return savedDeviceNamesDB[savedId];
309
300
  }
310
301
 
311
- deleteDeviceStates(devId, callback) {
312
- this.adapter.getStatesOf(devId, (err, states) => {
313
- if (!err && states) {
314
- states.forEach(state =>
315
- this.adapter.deleteState(devId, null, state._id));
316
- }
317
- this.adapter.delObject(devId, () =>
318
- callback && callback());
319
- });
320
- }
321
-
322
- async deleteGroupById(devId) {
302
+ async deleteObj(devId) {
323
303
  const options = { recursive:true };
324
304
  try {
325
305
  this.adapter.delObject(devId,options), (err) => { }
@@ -329,7 +309,6 @@ class StatesController extends EventEmitter {
329
309
  }
330
310
  }
331
311
 
332
-
333
312
  async deleteOrphanedDeviceStates(ieeeAddr, model, force, callback) {
334
313
  const devStates = await this.getDevStates(ieeeAddr, model);
335
314
  const commonStates = statesMapping.commonStates;
@@ -620,19 +599,20 @@ class StatesController extends EventEmitter {
620
599
  async publishToState(devId, model, payload) {
621
600
  const devStates = await this.getDevStates(`0x${devId}`, model);
622
601
  let has_debug = false;
623
- if (this.debugDevices === undefined) this.getDebugDevices();
624
602
  if (this.checkDebugDevice(devId))
625
603
  {
626
604
  if (!payload.hasOwnProperty('msg_from_zigbee')) {
627
- this.warn(`ELEVATED publishToState: message received '${JSON.stringify(payload)}' from device ${devId} type '${model}'`);
605
+ this.warn(`ELEVATED I1: message received '${JSON.stringify(payload)}' from device ${devId} type '${model}'`);
628
606
  has_debug = true;
629
607
  }
630
608
  }
631
609
  if (!devStates) {
632
- if (has_debug) this.error(`ELEVATED publishToState: no device states for device ${devId} type '${model}'`)
610
+ if (has_debug) this.error(`ELEVATED IE2: no device states for device ${devId} type '${model}'`)
633
611
  return;
634
612
  }
635
613
  // find states for payload
614
+ let has_published = false;
615
+
636
616
  if (devStates.states !== undefined) {
637
617
  try {
638
618
  const states = statesMapping.commonStates.concat(
@@ -655,7 +635,7 @@ class StatesController extends EventEmitter {
655
635
  let stateID = statedesc.id;
656
636
 
657
637
  if (has_debug && statedesc.id !== 'msg_from_zigbee') {
658
- this.warn(`ELEVATED publishToState: value generated '${JSON.stringify(value)}' from device ${devId} for '${statedesc.name}'`);
638
+ this.warn(`ELEVATED I2: value generated '${JSON.stringify(value)}' from device ${devId} for '${statedesc.name}'`);
659
639
  }
660
640
 
661
641
  const common = {
@@ -693,11 +673,22 @@ class StatesController extends EventEmitter {
693
673
  this.updateState(devId, stateID, value, common);
694
674
  }
695
675
  }
676
+ has_published = true;
696
677
  }
697
678
  } catch (e) {
698
679
  this.debug(`No states in device ${devId} : payload ${JSON.stringify(payload)}`);
680
+ if (has_debug)
681
+ this.error(`ELEVATED IE3: error when enumerating states of ${devId} for payload ${JSON.stringify(payload)}, ${(e ? e.name : 'undefined')} (${(e ? e.message : '')}).`);
682
+ }
683
+ if (!has_published && has_debug) {
684
+ this.error(`ELEVATED IE4: No value published for device ${devId}`);
685
+
699
686
  }
700
687
  }
688
+ else {
689
+ if (has_debug)
690
+ this.error(`ELEVATED IE5: No states matching the payload ${JSON.stringify(payload)} for device ${devId}`);
691
+ }
701
692
  }
702
693
  }
703
694
 
@@ -181,7 +181,7 @@ class DeviceAvailability extends BaseExtension {
181
181
  }
182
182
  } catch (error) {
183
183
  this.sendError(error);
184
- this.debug(`Exception in readState of '${device.ieeeAddr}' - error : '${error}'`);
184
+ this.debug(`Exception in readState of '${device.ieeeAddr}' - error : '${(error && error.message ? error.message : 'no error message')}'`);
185
185
  // intentionally empty: Just present to ensure we cause no harm
186
186
  // when reading the state fails. => fall back on standard Ping function
187
187
  }
@@ -36,11 +36,22 @@ class DeviceEvent extends BaseExtension {
36
36
 
37
37
  async callOnEvent(device, type, data, mappedDevice) {
38
38
  if (!mappedDevice) {
39
- mappedDevice = await zigbeeHerdsmanConverters.findByDevice(device);
39
+ try {
40
+ mappedDevice = await zigbeeHerdsmanConverters.findByDevice(device);
41
+ }
42
+ catch (error) {
43
+ this.log.error(`onEvent: unable to find mapped device for ${JSON.stringify(device)} `);
44
+ return;
45
+ }
40
46
  }
41
47
 
42
48
  if (mappedDevice && mappedDevice.onEvent) {
43
- mappedDevice.onEvent(type, data, device,mappedDevice.options,'{}');
49
+ try {
50
+ mappedDevice.onEvent(type, data, device,mappedDevice.options,'{}');
51
+ }
52
+ catch (error) {
53
+ this.log.error(`onEvent for ${JSON.stringify(device)} failed with error ${error.message}`);
54
+ }
44
55
  }
45
56
  }
46
57
  }