iobroker.zigbee 1.6.8 → 1.6.12

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/groups.js CHANGED
@@ -115,34 +115,44 @@ class Groups {
115
115
  this.adapter.sendTo(obj.from, obj.command, response, obj.callback);
116
116
 
117
117
  }
118
+ return response.groups;
118
119
  }
119
120
 
120
121
  async updateGroupMembership(from, command, message, callback) {
121
122
  try {
122
- const groups = (message && message.groups ? message.groups : []);
123
+ const groups = (message && message.groups ? message.groups : {});
123
124
  const devId = (message && message.id ? message.id : undefined);
124
125
  if (devId === undefined) {
125
126
  this.adapter.sendTo(from, command, {error: 'No device specified'}, callback);
126
127
  }
127
128
  const sysid = devId.replace(this.adapter.namespace + '.', '0x');
128
- const id = `${devId}.groups`;
129
+ // Keeping this for reference. State update or state removal needs to be decided upon
130
+ //const id = `${devId}.groups`;
131
+ // this.adapter.setState(id, JSON.stringify(groups), true);
129
132
 
130
- this.adapter.setState(id, JSON.stringify(groups), true);
131
- let response = await this.zbController.removeDevFromAllGroups(sysid);
132
- if (response && response.error)
133
- {
134
- this.adapter.sendTo(from, command, response, callback);
135
- return;
136
- }
137
- for (const groupId of groups) {
138
- response = await this.zbController.addDevToGroup(sysid, parseInt(groupId));
139
- if (response && response.error)
140
- {
141
- this.adapter.sendTo(from, command, response, callback);
142
- return;
133
+ //const current = await this.zbController.getGroupMembersFromController(sysid);
134
+ const errors = []
135
+ for (const epid in groups) {
136
+ for (const gpid of groups[epid]) {
137
+ const gpidn = parseInt(gpid);
138
+ if (gpidn < 0) {
139
+ let response = await this.zbController.removeDevFromGroup(sysid,(-gpidn), epid);
140
+ if (response && response.error) {
141
+ errors.push(response.error);
142
+ this.error("remove dev from group Error: "+ JSON.stringify(response.error));
143
+ }
144
+ }
145
+ else if (gpidn > 0){
146
+ let response = await this.zbController.addDevToGroup(sysid,(gpidn), epid);
147
+ if (response && response.error) {
148
+ errors.push(response.error);
149
+ this.error("add dev to group Error: "+ JSON.stringify(response.error));
150
+ }
151
+
152
+ }
153
+ else this.warn('illegal group id 0');
143
154
  }
144
155
  }
145
-
146
156
  } catch (e) {
147
157
  this.adapter.sendTo(from, command, {error: e}, callback);
148
158
  return;
@@ -177,7 +187,6 @@ class Groups {
177
187
  }
178
188
  }
179
189
  if (result.unsupported.length>0) {
180
- // this.warn('unsupported ' + stateDesc.id + ' change for group members ' + JSON.stringify(result.unsupported));
181
190
  const error = {
182
191
  code: 134,
183
192
  message: `unsupported ${stateDesc.id} change for group members ${result.unsupported.join()}`
@@ -255,7 +264,8 @@ class Groups {
255
264
  }
256
265
 
257
266
 
258
- syncGroups(groups) {
267
+ async syncGroups(adapterGroups) {
268
+ const groups = await this.getGroups();
259
269
  const chain = [];
260
270
  const usedGroupsIds = [];
261
271
  for (const j in groups) {
package/lib/rgb.js CHANGED
@@ -212,14 +212,44 @@ function colorArrayFromString(value) {
212
212
  return [{r:0,g:128,b:255}];
213
213
  }
214
214
 
215
+ function colorStringFromRGBArray(payload)
216
+ {
217
+ let rv = []
218
+ payload.forEach(element => {
219
+ rv.push(rgb_to_rgbstring(element));
220
+ });
221
+ return rv.toString();
222
+ }
223
+
215
224
  function hsv_to_cie(h,s,v){
216
225
  const rgb = hsvToRGB(h,s,v);
217
226
  return rgb_to_cie(rgb.r, rgb.g, rgb.b);
218
227
  }
219
228
 
229
+ function rgb_to_rgbstring(element) {
230
+ let col = '#';
231
+ if (element && element.hasOwnProperty("r"))
232
+ col = col + element.r.toString(16).padStart(2, '0');
233
+ else col = col + '00';
234
+ if (element && element.hasOwnProperty("g"))
235
+ col = col + element.g.toString(16).padStart(2, '0');
236
+ else col = col + '00';
237
+ if (element && element.hasOwnProperty("b"))
238
+ col = col + element.b.toString(16).padStart(2, '0');
239
+ else col = col + '00';
240
+ return col;
241
+ }
242
+
243
+
244
+ function hsvToRGBString(h,s,v) {
245
+ return rgb_to_rgbstring(hsvToRGB(h,s,v))
246
+ }
247
+
220
248
  exports.hsv_to_cie = hsv_to_cie;
221
249
  exports.rgb_to_cie = rgb_to_cie;
222
250
  exports.cie_to_rgb = cie_to_rgb;
223
251
  exports.hsvToRGB = hsvToRGB;
224
252
  exports.rgbToHSV = rgbToHSV;
225
253
  exports.colorArrayFromString = colorArrayFromString;
254
+ exports.colorStringFromRGBArray = colorStringFromRGBArray;
255
+ exports.hsvToRGBString = hsvToRGBString;
package/lib/states.js CHANGED
@@ -4547,10 +4547,11 @@ const states = {
4547
4547
  name: 'Button ROW 2',
4548
4548
  icon: undefined,
4549
4549
  role: 'button',
4550
- write: false,
4550
+ write: true,
4551
4551
  read: true,
4552
4552
  type: 'boolean',
4553
4553
  getter: payload => (payload.action === 'row_2_on') ? true : (payload.action === 'row_2_off') ? false : undefined,
4554
+ setter: (value) => (value) ? 'row_2_on' : 'row_2_off',
4554
4555
  },
4555
4556
  rm01_row_3: {
4556
4557
  id: 'rm01_row_3',
@@ -4558,10 +4559,11 @@ const states = {
4558
4559
  name: 'Button ROW 3',
4559
4560
  icon: undefined,
4560
4561
  role: 'button',
4561
- write: false,
4562
+ write: true,
4562
4563
  read: true,
4563
4564
  type: 'boolean',
4564
4565
  getter: payload => (payload.action === 'row_3_on') ? true : (payload.action === 'row_3_off') ? false : undefined,
4566
+ setter: (value) => (value) ? 'row_3_on' : 'row_3_off',
4565
4567
  },
4566
4568
  rm01_row_4: {
4567
4569
  id: 'rm01_row_4',
@@ -4569,10 +4571,11 @@ const states = {
4569
4571
  name: 'Button ROW 4',
4570
4572
  icon: undefined,
4571
4573
  role: 'button',
4572
- write: false,
4574
+ write: true,
4573
4575
  read: true,
4574
4576
  type: 'boolean',
4575
4577
  getter: payload => (payload.action === 'row_4_on') ? true : (payload.action === 'row_4_off') ? false : undefined,
4578
+ setter: (value) => (value) ? 'row_4_on' : 'row_4_off',
4576
4579
  },
4577
4580
  left_top_click: {
4578
4581
  id: 'left_top_click',
@@ -5800,16 +5803,21 @@ const states = {
5800
5803
  speed:10,
5801
5804
  effect:'glow',
5802
5805
  };
5803
- if (options.hasOwnProperty('effect_speed'))
5806
+ if (options && options.hasOwnProperty('effect_speed'))
5804
5807
  effectjson.speed = options.effect_speed;
5805
- if (options.hasOwnProperty('effect_colors'))
5808
+ if (options && options.hasOwnProperty('effect_colors'))
5806
5809
  {
5807
5810
  effectjson.colors = rgb.colorArrayFromString(options.effect_colors);
5808
5811
  }
5809
5812
  effectjson.effect = value;
5810
5813
  return effectjson;
5814
+ },
5815
+ getter: (payload) => {
5816
+ if (payload && payload.effect && payload.effect.hasOwnProperty('effect')) return payload.effect.effect;
5817
+ return "steady";
5811
5818
  }
5812
- },
5819
+
5820
+ },
5813
5821
  effect_json: {
5814
5822
  id: 'effect_json',
5815
5823
  name: 'Effect',
@@ -5829,11 +5837,11 @@ const states = {
5829
5837
  speed:10,
5830
5838
  effect:'glow',
5831
5839
  };
5832
- if (options.hasOwnProperty('effect_speed'))
5840
+ if (options && options.hasOwnProperty('effect_speed'))
5833
5841
  effectjson.speed = options.effect_speed;
5834
- if (options.hasOwnProperty('effect_colors'))
5842
+ if (options && options.hasOwnProperty('effect_colors'))
5835
5843
  effectjson.colors = rgb.colorArrayFromString(options.effect_colors);
5836
- if (options.hasOwnProperty('effect_type'))
5844
+ if (options && options.hasOwnProperty('effect_type'))
5837
5845
  effectjson.effect = options.effect_type;
5838
5846
  value = JSON.stringify(effectjson);
5839
5847
  return effectjson;
@@ -5859,12 +5867,16 @@ const states = {
5859
5867
  speed:10,
5860
5868
  effect:'snake',
5861
5869
  };
5862
- if (options.hasOwnProperty('effect_type'))
5870
+ if (options && options.hasOwnProperty('effect_type'))
5863
5871
  effectjson.effect = options.effect_type;
5864
- if (options.hasOwnProperty('effect_colors'))
5872
+ if (options && options.hasOwnProperty('effect_colors'))
5865
5873
  effectjson.colors = rgb.colorArrayFromString(options.effect_colors);
5866
5874
  effectjson.speed = value;
5867
5875
  return effectjson;
5876
+ },
5877
+ getter: (payload) => {
5878
+ if (payload && payload.effect && payload.effect.hasOwnProperty('speed')) return payload.effect.speed;
5879
+ return 1;
5868
5880
  }
5869
5881
  },
5870
5882
 
@@ -5885,12 +5897,16 @@ const states = {
5885
5897
  effect:'sparkle',
5886
5898
  };
5887
5899
 
5888
- if (options.hasOwnProperty('effect_speed'))
5900
+ if (options && options.hasOwnProperty('effect_speed'))
5889
5901
  effectjson.speed = options.effect_speed;
5890
- if (options.hasOwnProperty('effect_type'))
5902
+ if (options && options.hasOwnProperty('effect_type'))
5891
5903
  effectjson.effect = options.effect_type;
5892
5904
  effectjson.colors = rgb.colorArrayFromString(value);
5893
5905
  return effectjson;
5906
+ },
5907
+ getter: (payload) => {
5908
+ if (payload && payload.effect && payload.effect.hasOwnProperty('colors')) return rgb.colorStringFromRGBArray(payload.effect.colors);
5909
+ return '#ffff00,#ff00ff,#00ffff,#0000ff,#00ff00,#ff0000';
5894
5910
  }
5895
5911
  },
5896
5912
 
@@ -5925,6 +5941,15 @@ const states = {
5925
5941
 
5926
5942
  };
5927
5943
  },
5944
+ getter: (payload) => {
5945
+ if (payload.color) {
5946
+ const c = payload.color;
5947
+ if (c.hasOwnProperty("h") && c.hasOwnProperty("s") && c.hasOwnProperty("b"))
5948
+ return rgb.hsvToRGBString(c.h, c.s, c.b * 100 / 255);
5949
+ if (c.hasOwnProperty("h") && c.hasOwnProperty("s") && c.hasOwnProperty("v"))
5950
+ return rgb.hsvToRGBString(c.h, c.s, c.v * 100);
5951
+ }
5952
+ },
5928
5953
  setterOpt: (value, options) => {
5929
5954
  const hasTransitionTime = options && options.hasOwnProperty('transition_time');
5930
5955
  const transitionTime = hasTransitionTime ? options.transition_time : 0;
@@ -4,6 +4,7 @@ const EventEmitter = require('events').EventEmitter;
4
4
  const statesMapping = require('./devices');
5
5
  const getAdId = require('./utils').getAdId;
6
6
  const getZbId = require('./utils').getZbId;
7
+ var knownUndefinedDevices = {};
7
8
 
8
9
 
9
10
  class StatesController extends EventEmitter {
@@ -55,8 +56,20 @@ class StatesController extends EventEmitter {
55
56
  });
56
57
  }
57
58
  });
58
-
59
+ this.adapter.setObject('info.undefinedDevices', {
60
+ 'type': 'state',
61
+ 'common': {
62
+ 'name': 'Recorded undefined devices',
63
+ 'role': '',
64
+ 'type': 'string',
65
+ 'read': true,
66
+ 'write': false,
67
+ },
68
+ 'native': {},
69
+ });
70
+ this.adapter.setStateAsync(`info.undefinedDevices`, JSON.stringify(knownUndefinedDevices), true);
59
71
  }
72
+
60
73
  onStateChange(id, state){
61
74
  if (!this.adapter.zbController || !this.adapter.zbController.connected()) return;
62
75
  if (this.debugDevices === undefined) this.getDebugDevices();
@@ -116,20 +129,25 @@ class StatesController extends EventEmitter {
116
129
  return;
117
130
  }
118
131
  let cnt = 0;
119
- const len = states.length;
120
- states.forEach(statedesc => {
121
- const id = this.adapter.namespace + '.' + devId + '.' + statedesc.id;
122
- this.adapter.getState(id, (err, state) => {
123
- cnt = cnt + 1;
124
- if (!err && state) {
125
- result[statedesc.id] = state.val;
126
- }
127
- if (cnt === len) {
128
- callback(result);
129
- }
132
+ try {
133
+ const len = states.length;
134
+ states.forEach(statedesc => {
135
+ const id = this.adapter.namespace + '.' + devId + '.' + statedesc.id;
136
+ this.adapter.getState(id, (err, state) => {
137
+ cnt = cnt + 1;
138
+ if (!err && state) {
139
+ result[statedesc.id] = state.val;
140
+ }
141
+ if (cnt === len) {
142
+ callback(result);
143
+ }
144
+ });
130
145
  });
131
- });
132
- if (!len) callback(result);
146
+ if (!len) callback(result);
147
+ } catch (error) {
148
+ this.sendError(error);
149
+ this.error(`Error collectOptions for ${devId}. Error: ${error.stack}`);
150
+ }
133
151
  }
134
152
 
135
153
  async getDevStates(deviceId, model) {
@@ -141,7 +159,15 @@ class StatesController extends EventEmitter {
141
159
  } else {
142
160
  stateModel = statesMapping.findModel(model);
143
161
  if (!stateModel) {
144
- this.error('Device ' + deviceId + ' "' + model + '" not described in statesMapping.');
162
+ if (knownUndefinedDevices[deviceId])
163
+ {
164
+ knownUndefinedDevices[deviceId]++;
165
+ }
166
+ else {
167
+ knownUndefinedDevices[deviceId] = 1;
168
+ this.error('Device ' + deviceId + ' "' + model + '" not described in statesMapping.');
169
+ }
170
+ this.adapter.setStateAsync(`info.undefinedDevices`, JSON.stringify(knownUndefinedDevices), true);
145
171
  states = statesMapping.commonStates;
146
172
  } else {
147
173
  states = stateModel.states;
@@ -343,13 +369,10 @@ class StatesController extends EventEmitter {
343
369
  // only change object when any common property has changed
344
370
  if (hasChanges) {
345
371
  this.adapter.extendObject(id, {type: 'state', common: new_common, native: {} }, () => {
346
- value !== undefined && this.adapter.setState(id, value, true);
372
+ value !== undefined && this.setState_typed(id, value, true, (stobj) ? stobj.common.type : new_common.type);
347
373
  });
348
374
  } else if (value !== undefined) {
349
- if (typeof(value) !== 'object') {
350
- this.adapter.setState(id, value, true);
351
- } else this.warn('set state with object for id :' + JSON.stringify(id) + ' '+ JSON.stringify(value));
352
-
375
+ this.setState_typed(id, value, true, stobj.common.type);
353
376
  }
354
377
 
355
378
  });
@@ -359,6 +382,44 @@ class StatesController extends EventEmitter {
359
382
  });
360
383
  }
361
384
 
385
+ setState_typed(id, value, ack, type, callback)
386
+ {
387
+ // never set a null or undefined value
388
+ if (value === null || value === undefined) return;
389
+ if (!type) {
390
+ this.debug("SetState_typed called without type");
391
+ // identify datatype, recursively call this function with set datatype
392
+ this.adapter.getObject(id, (err, obj) => {
393
+ if (obj && obj.common)
394
+ this.setState_typed(id, value, ack, obj.common.type, callback);
395
+ else {
396
+ this.setState_typed(id, value, ack, 'noobj', callback);
397
+ }
398
+ });
399
+ return;
400
+ }
401
+ if (typeof value != type) {
402
+ this.debug("SetState_typed : converting " + JSON.stringify(value) + " for " + id + " from " + typeof value + " to " + type);
403
+ switch (type) {
404
+ case 'number':
405
+ value = parseFloat(value);
406
+ if (isNaN (value)) value = 0;
407
+ break;
408
+ case 'string':
409
+ case 'text': value = JSON.stringify(value); break;
410
+ case 'boolean':
411
+ if (typeof value == 'number') {
412
+ value = (value != 0);
413
+ break;
414
+ }
415
+ const sval = JSON.stringify(value).toLowerCase().trim();
416
+ value = (sval == 'true' || sval == 'yes' || sval == 'on');
417
+ break;
418
+ }
419
+ }
420
+ this.adapter.setState(id, value, ack, callback);
421
+ }
422
+
362
423
  updateDev(dev_id, dev_name, model, callback) {
363
424
  const id = '' + dev_id;
364
425
  const modelDesc = statesMapping.findModel(model);
@@ -432,6 +493,7 @@ class StatesController extends EventEmitter {
432
493
  for (const addressPart of this.debugDevices) {
433
494
  if (typeof(devId) == 'string' && devId.indexOf(addressPart) > -1)
434
495
  {
496
+ if (payload.hasOwnProperty('msg_from_zigbee')) break;
435
497
  this.warn(`ELEVATED publishToState: message received '${JSON.stringify(payload)}' from device ${devId} type '${model}'`);
436
498
  has_debug = true;
437
499
  break;
@@ -454,10 +516,10 @@ class StatesController extends EventEmitter {
454
516
  value = payload[statedesc.prop || statedesc.id];
455
517
  }
456
518
  // checking value
457
- if (value === undefined) continue;
519
+ if (value === undefined || value === null) continue;
458
520
  let stateID = statedesc.id;
459
521
 
460
- if (has_debug) {
522
+ if (has_debug && statedesc.id != 'msg_from_zigbee') {
461
523
  this.warn(`ELEVATED publishToState: value generated '${JSON.stringify(value)}' from device ${devId} for '${statedesc.name}'`);
462
524
  }
463
525
 
@@ -5,6 +5,7 @@
5
5
  class BaseExtension {
6
6
  constructor(zigbee, options) {
7
7
  this.zigbee = zigbee;
8
+ this.name = 'BaseExtension';
8
9
  }
9
10
 
10
11
  info(message, data) {
@@ -12,15 +13,15 @@ class BaseExtension {
12
13
  }
13
14
 
14
15
  error(message, data) {
15
- this.zigbee.error(message, data);
16
+ this.zigbee.error(this.name + ':' + message, data);
16
17
  }
17
18
 
18
19
  warn(message, data) {
19
- this.zigbee.warn(message, data);
20
+ this.zigbee.warn(this.name + ':' + message, data);
20
21
  }
21
22
 
22
23
  debug(message, data) {
23
- this.zigbee.debug(message, data);
24
+ this.zigbee.debug(this.name + ':' + message, data);
24
25
  }
25
26
 
26
27
  sendError(error, message) {
@@ -8,6 +8,7 @@ class DelayedAction extends BaseExtension {
8
8
 
9
9
  this.actions = {};
10
10
  this.zigbee.delayAction = this.delayAction.bind(this);
11
+ this.name = "DelayedAction";
11
12
  }
12
13
 
13
14
  setOptions(options) {
@@ -50,6 +50,7 @@ class DeviceAvailability extends BaseExtension {
50
50
  this.startDevicePingQueue = []; // simple fifo array for starting device pings
51
51
  this.startDevicePingTimeout = null; // handle for the timeout which empties the queue
52
52
  this.startDevicePingDelay = 200; // 200 ms delay between starting the ping timeout
53
+ this.name = "DeviceAvailability";
53
54
  }
54
55
 
55
56
  setOptions(options) {
@@ -265,7 +266,7 @@ class DeviceAvailability extends BaseExtension {
265
266
  // }
266
267
  }
267
268
  }
268
-
269
+
269
270
  onZigbeeEvent(data) {
270
271
  const device = data.device;
271
272
  if (!device) {
@@ -18,6 +18,7 @@ class DeviceConfigure extends BaseExtension {
18
18
 
19
19
  this.configuring = new Set();
20
20
  this.attempts = {};
21
+ this.name = "DeviceConfigure";
21
22
  }
22
23
 
23
24
  setOptions(options) {
@@ -119,13 +120,8 @@ class DeviceConfigure extends BaseExtension {
119
120
  if (!this.attempts.hasOwnProperty(device.ieeeAddr)) {
120
121
  this.attempts[device.ieeeAddr] = 0;
121
122
  }
122
-
123
- this.info(`Configuring ${device.ieeeAddr} ${device.modelID}`);
124
123
  try {
125
- await mappedDevice.configure(device, this.coordinatorEndpoint);
126
- this.info(`DeviceConfigure successful ${device.ieeeAddr} ${device.modelID}`);
127
- device.meta.configured = zigbeeHerdsmanConverters.getConfigureKey(mappedDevice);
128
- device.save();
124
+ await this.doConfigure(device, mappedDevice);
129
125
  } catch (error) {
130
126
  this.sendError(error);
131
127
  this.warn(
@@ -134,7 +130,6 @@ class DeviceConfigure extends BaseExtension {
134
130
  );
135
131
  this.attempts[device.ieeeAddr]++;
136
132
  }
137
-
138
133
  this.configuring.delete(device.ieeeAddr);
139
134
  } catch (error) {
140
135
  this.sendError(error);
@@ -143,6 +138,15 @@ class DeviceConfigure extends BaseExtension {
143
138
  );
144
139
  }
145
140
  }
141
+
142
+ async doConfigure(device, mappedDevice) {
143
+ this.info(`Configuring ${device.ieeeAddr} ${device.modelID}`);
144
+ const coordinatorEndpoint = await this.zigbee.getDevicesByType('Coordinator')[0].endpoints[0];
145
+ await mappedDevice.configure(device, coordinatorEndpoint);
146
+ device.meta.configured = zigbeeHerdsmanConverters.getConfigureKey(mappedDevice);
147
+ device.save();
148
+ this.info(`DeviceConfigure successful ${device.ieeeAddr} ${device.modelID}`);
149
+ }
146
150
  }
147
151
 
148
152
  module.exports = DeviceConfigure;
@@ -4,6 +4,12 @@ const BaseExtension = require('./zbBaseExtension');
4
4
  const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
5
5
 
6
6
  class DeviceEvent extends BaseExtension {
7
+ constructor(zigbee, options) {
8
+ super(zigbee, options);
9
+ this.name = "DeviceEvent";
10
+ }
11
+
12
+
7
13
  async onZigbeeStarted() {
8
14
  for (const device of await this.zigbee.getClients()) {
9
15
  this.callOnEvent(device, 'start', {});