iobroker.zigbee 1.6.15 → 1.6.16

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.
Files changed (52) hide show
  1. package/.eslintignore +1 -1
  2. package/.eslintrc.json +36 -36
  3. package/.github/FUNDING.yml +3 -3
  4. package/.github/stale.yml +13 -13
  5. package/.github/workflows/test-and-release.yml +151 -151
  6. package/.travis/wiki.sh +27 -27
  7. package/LICENSE +21 -21
  8. package/README.md +424 -444
  9. package/admin/adapter-settings.js +244 -244
  10. package/admin/admin.js +2926 -2926
  11. package/admin/img/philips_hue_lom001.png +0 -0
  12. package/admin/index.html +159 -159
  13. package/admin/index_m.html +1158 -1158
  14. package/admin/moment.min.js +1 -1
  15. package/admin/shuffle.min.js +2 -2
  16. package/admin/tab_m.html +944 -944
  17. package/admin/vis-network.min.js +26 -26
  18. package/admin/words.js +108 -108
  19. package/docs/de/readme.md +27 -27
  20. package/docs/en/readme.md +30 -30
  21. package/docs/flashing_via_arduino_(en).md +110 -110
  22. package/docs/ru/readme.md +28 -28
  23. package/docs/tutorial/groups-1.png +0 -0
  24. package/docs/tutorial/groups-2.png +0 -0
  25. package/docs/tutorial/tab-dev-1.png +0 -0
  26. package/io-package.json +355 -348
  27. package/lib/backup.js +132 -132
  28. package/lib/binding.js +325 -325
  29. package/lib/colors.js +460 -460
  30. package/lib/commands.js +501 -501
  31. package/lib/developer.js +148 -148
  32. package/lib/devices.js +3144 -3144
  33. package/lib/exclude.js +168 -168
  34. package/lib/exposes.js +795 -795
  35. package/lib/groups.js +342 -342
  36. package/lib/json.js +60 -60
  37. package/lib/networkmap.js +56 -56
  38. package/lib/ota.js +179 -179
  39. package/lib/rgb.js +255 -255
  40. package/lib/seriallist.js +37 -37
  41. package/lib/states.js +6407 -6407
  42. package/lib/statescontroller.js +627 -627
  43. package/lib/tools.js +54 -54
  44. package/lib/utils.js +151 -151
  45. package/lib/zbBaseExtension.js +32 -32
  46. package/lib/zbDelayedAction.js +152 -152
  47. package/lib/zbDeviceAvailability.js +318 -318
  48. package/lib/zbDeviceConfigure.js +152 -152
  49. package/lib/zbDeviceEvent.js +49 -49
  50. package/lib/zigbeecontroller.js +946 -946
  51. package/package.json +77 -77
  52. package/support/docgen.js +93 -93
package/lib/commands.js CHANGED
@@ -1,501 +1,501 @@
1
- 'use strict';
2
-
3
- const getZbId = require('./utils').getZbId;
4
- const statesMapping = require('./devices');
5
- const disallowedDashStates = [
6
- 'link_quality', 'available', 'battery', 'groups', 'device_query',
7
- 'hue_move', 'color_temp_move', 'satuation_move', 'brightness_move', 'brightness_step', 'hue_calibration',
8
- 'msg_from_zigbee',
9
- ];
10
-
11
- class Commands {
12
- constructor(adapter) {
13
- this.adapter = adapter;
14
- this.adapter.on('message', this.onMessage.bind(this));
15
- }
16
-
17
- start(zbController, stController) {
18
- this.zbController = zbController;
19
- this.stController = stController;
20
- }
21
-
22
- stop() {
23
- delete this.zbController;
24
- delete this.stController;
25
- }
26
-
27
- info(msg) {
28
- this.adapter.log.info(msg);
29
- }
30
-
31
- error(msg) {
32
- this.adapter.log.error(msg);
33
- }
34
-
35
- debug(msg) {
36
- this.adapter.log.debug(msg);
37
- }
38
-
39
- warn(msg) {
40
- this.adapter.log.warn(msg);
41
- }
42
-
43
- /**
44
- * @param {ioBroker.Message} obj
45
- */
46
- onMessage(obj) {
47
- if (typeof obj === 'object' && obj.command) {
48
- switch (obj.command) {
49
- case 'letsPairing':
50
- if (obj && obj.message && typeof obj.message === 'object') {
51
- this.letsPairing(obj.from, obj.command, obj.message, obj.callback);
52
- }
53
- break;
54
- case 'touchlinkReset':
55
- if (obj && obj.message && typeof obj.message === 'object') {
56
- this.touchlinkReset(obj.from, obj.command, obj.message, obj.callback);
57
- }
58
- break;
59
- case 'getDevices':
60
- if (obj && obj.message && typeof obj.message === 'object') {
61
- this.getDevices(obj.from, obj.command, null, obj.callback);
62
- }
63
- break;
64
- case 'renameDevice':
65
- if (obj && obj.message && typeof obj.message === 'object') {
66
- this.renameDevice(obj.from, obj.command, obj.message, obj.callback);
67
- }
68
- break;
69
- case 'deleteDevice':
70
- if (obj && obj.message && typeof obj.message === 'object') {
71
- this.deleteDevice(obj.from, obj.command, obj.message, obj.callback);
72
- }
73
- break;
74
- case 'getChannels':
75
- if (obj && obj.message && typeof obj.message === 'object') {
76
- this.getChannels(obj.from, obj.command, obj.message, obj.callback);
77
- }
78
- break;
79
- case 'getCoordinatorInfo':
80
- if (obj && obj.message && typeof obj.message === 'object') {
81
- this.getCoordinatorInfo(obj.from, obj.command, obj.callback);
82
- }
83
- break;
84
- case 'cleanDeviceStates':
85
- if (obj && obj.message && typeof obj.message === 'object') {
86
- this.cleanDeviceStates(obj.from, obj.command, obj.message, obj.callback);
87
- }
88
- break;
89
- case 'setState':
90
- if (obj && obj.message && typeof obj.message === 'object' && obj.message.id) {
91
- // this.adapter.setState(obj.message.id, obj.message.val, false, obj.callback);
92
- this.stController.setState_typed(obj.message.id, obj.message.val, false, undefined, obj.callback);
93
- }
94
- break;
95
- case 'getDevice':
96
- if (obj && obj.message && typeof obj.message === 'object' && obj.message.id) {
97
- this.getDevices(obj.from, obj.command, obj.message.id, obj.callback);
98
- }
99
- break;
100
- case 'reconfigure':
101
- if (obj && obj.message && typeof obj.message === 'object') {
102
- this.reconfigure(obj.from, obj.command, obj.message, obj.callback);
103
- }
104
- break;
105
- case 'setDeviceActivated':
106
- if (obj && obj.message && typeof obj.message === 'object') {
107
- this.setDeviceActivated(obj.from, obj.command, obj.message, obj.callback);
108
- }
109
- break;
110
-
111
- }
112
- }
113
- }
114
-
115
-
116
- letsPairing(from, command, message, callback) {
117
- if (this.zbController) {
118
- let devId = '';
119
- if (message && message.id) {
120
- devId = getZbId(message.id);
121
- }
122
- // allow devices to join the network within 60 secs
123
- this.adapter.logToPairing('Pairing started ' + devId, true);
124
-
125
- let cTimer = Number(this.adapter.config.countDown);
126
- if (!this.adapter.config.countDown || cTimer == 0) {
127
- cTimer = 60;
128
- }
129
-
130
- this.zbController.permitJoin(cTimer, devId, (err) => {
131
- if (!err) {
132
- // set pairing mode on
133
- this.adapter.setState('info.pairingMode', true);
134
- }
135
- });
136
- this.adapter.sendTo(from, command, 'Start pairing!', callback);
137
- } else {
138
- this.adapter.sendTo(
139
- from, command,
140
- {error: 'You need to setup serial port and start the adapter before pairing!'},
141
- callback
142
- );
143
- }
144
- }
145
-
146
- touchlinkReset(from, command, message, callback) {
147
- if (this.zbController) {
148
- // allow devices to join the network within 60 secs
149
- this.adapter.logToPairing('Touchlink reset started ', true);
150
-
151
- let cTimer = Number(this.adapter.config.countDown);
152
- if (!this.adapter.config.countDown || cTimer == 0) {
153
- cTimer = 60;
154
- }
155
-
156
- this.zbController.touchlinkReset(cTimer);
157
- this.adapter.sendTo(from, command, 'Start touchlink reset and pairing!', callback);
158
- } else {
159
- this.adapter.sendTo(
160
- from, command,
161
- {error: 'You need to setup serial port and start the adapter before pairing!'},
162
- callback
163
- );
164
- }
165
- }
166
-
167
- async getDevices(from, command, id, callback) {
168
- if (this.zbController) {
169
- const pairedDevices = await this.zbController.getClients(true);
170
- const groups = {};
171
- let rooms;
172
- this.adapter.getEnumsAsync('enum.rooms')
173
- .then((enums) => {
174
- // rooms
175
- rooms = enums['enum.rooms'];
176
- })
177
- .then(() => {
178
- // get all adapter devices
179
- return this.adapter.getDevicesAsync();
180
- })
181
- .then(async (result) => {
182
- const alls = (id) ? await this.adapter.getStatesAsync(id+'.*') : await this.adapter.getStatesAsync('*');
183
- const allst = (id) ? await this.adapter.getStatesOfAsync(id) : await this.adapter.getStatesOfAsync();
184
- result = result.filter((item)=>!id || id == item._id);
185
- // get device states and groups
186
- result.forEach(async (devInfo) => {
187
- if (devInfo._id) {
188
- // groups
189
- // const grState = alls[`${devInfo._id}.groups`];
190
- // if (grState && grState.val) {
191
- // groups[devInfo._id] = JSON.parse(grState.val);
192
- // }
193
- // battery and link_quality
194
- const lqState = alls[`${devInfo._id}.link_quality`];
195
- devInfo.link_quality = (lqState) ? lqState.val: undefined;
196
- devInfo.link_quality_lc = (lqState) ? lqState.lc: undefined;
197
- const batState = alls[`${devInfo._id}.battery`];
198
- devInfo.battery = (batState) ? batState.val: undefined;
199
- // devInfo.states = states || {};
200
-
201
- const states = allst.filter((item)=>item._id.startsWith(devInfo._id));
202
-
203
- // put only allowed states
204
- devInfo.statesDef = (states || []).filter((stateDef)=>{
205
- const sid = stateDef._id;
206
- const name = sid.split('.').pop();
207
- if (disallowedDashStates.includes(name)) return false;
208
- return true;
209
- }).map((stateDef)=>{
210
- const name = stateDef.common.name;
211
- const devname = devInfo.common.name;
212
- // replace state
213
- return {
214
- id: stateDef._id,
215
- name: (typeof name === 'string') ? name.replace(devname, '') : name,
216
- type: stateDef.common.type,
217
- read: stateDef.common.read,
218
- write: stateDef.common.write,
219
- val: (alls[stateDef._id]) ? alls[stateDef._id].val : undefined,
220
- role: stateDef.common.role,
221
- unit: stateDef.common.unit,
222
- states: stateDef.common.states,
223
- };
224
- });
225
- }
226
- });
227
- return result;
228
- })
229
- .then(async (result) => {
230
- // combine info
231
- const devices = [];
232
- for (const devInfo of result) {
233
- if (devInfo._id.indexOf('group') > 0) {
234
- devInfo.icon = 'img/group.png';
235
- devInfo.vendor = 'ioBroker';
236
- // get group members and store them
237
- const match = /zigbee.\d.group_([0-9]+)/.exec(devInfo._id);
238
- if (match && match.length > 1) {
239
- const groupmembers = await this.zbController.getGroupMembersFromController(match[1]);
240
- this.debug('group members: ' + JSON.stringify(groupmembers));
241
- if (groupmembers && groupmembers.length > 0) {
242
- const memberinfo = [];
243
- for (const member of groupmembers) {
244
- if (groups) {
245
- const grouparray = groups[member.ieee];
246
- if (grouparray)
247
- {
248
- if (!grouparray.includes(match[1])) {
249
- groups[member.ieee].push(match[1])
250
- }
251
- }
252
- else groups[member.ieee] = [match[1]];
253
- }
254
- const device = await this.adapter.getObjectAsync(this.adapter.namespace + '.' + member.ieee.substr(2));
255
- if (device) {
256
- member.device = device.common.name;
257
- }
258
- else {
259
- member.device = 'unknown';
260
- }
261
- memberinfo.push(member);
262
- }
263
- devInfo.memberinfo = memberinfo;
264
- this.debug('memberinfo: ' + JSON.stringify(devInfo.memberinfo));
265
- }
266
- }
267
- }
268
- else
269
- {
270
- const modelDesc = statesMapping.findModel(devInfo.common.type);
271
- devInfo.icon = (modelDesc && modelDesc.icon) ? modelDesc.icon : 'img/unknown.png';
272
- devInfo.vendor = (modelDesc) ? modelDesc.vendor : '';
273
- }
274
- const id = getZbId(devInfo._id);
275
- devInfo.info = await this.zbController.resolveEntity(id);
276
-
277
- devInfo.rooms = [];
278
- for (const room in rooms) {
279
- if (!rooms.hasOwnProperty(room) ||
280
- !rooms[room] ||
281
- !rooms[room].common ||
282
- !rooms[room].common.members) {
283
- continue;
284
- }
285
- if (rooms[room].common.members.indexOf(devInfo._id) !== -1) {
286
- devInfo.rooms.push(rooms[room].common.name);
287
- }
288
- }
289
- devInfo.paired = !!devInfo.info;
290
- // devInfo.groups = groups[devInfo._id];
291
- devices.push(devInfo);
292
- }
293
- return devices;
294
- })
295
- .then(async (devices) => {
296
- // fill group info
297
- for (const groupdev in groups) {
298
- this.debug('GetDevices scanning group ' + groupdev + ' ' + JSON.stringify(groups[groupdev]))
299
- const device = devices.find((dev) =>( groupdev === getZbId(dev._id) ))
300
- if (device) {
301
- device.groups = groups[groupdev]
302
- this.debug('adding group info to device ' + groupdev);
303
- }
304
- }
305
- // append devices that paired but not created
306
- if (!id) {
307
- for (const d of pairedDevices) {
308
- const device = await this.zbController.resolveEntity(d.ieeeAddr);
309
- if (!device) continue;
310
- const exists = devices.find((dev) => (dev._id && device.device.ieeeAddr === getZbId(dev._id)));
311
- if (!exists) {
312
- devices.push({
313
- _id: device.device.ieeeAddr,
314
- icon: 'img/unknown.png',
315
- paired: true,
316
- info: device,
317
- common: {
318
- name: undefined,
319
- type: undefined,
320
- },
321
- native: {}
322
- });
323
- }
324
- }
325
- }
326
- return devices;
327
- })
328
- .then((devices) => {
329
- this.debug('getDevices result: ' + JSON.stringify(devices));
330
- this.adapter.sendTo(from, command, devices, callback);
331
- })
332
- .catch((err) => {
333
- this.error('getDevices error: ' + err.stack);
334
- });
335
- } else {
336
- this.adapter.sendTo(from, command, {error: 'You need save and run adapter before pairing!'}, callback);
337
- }
338
- }
339
-
340
-
341
- async getCoordinatorInfo(from, command, callback) {
342
- if (this.zbController) {
343
- const coordinatorinfo = {
344
- installSource: 'IADefault_1',
345
- channel: '-1',
346
- port: 'Default_1',
347
- installedVersion: 'Default_1',
348
- type: 'Default_1',
349
- revision: 'Default_1',
350
- version: '9-9.9.9.9'
351
- };
352
-
353
- const coordinatorVersion = await this.adapter.zbController.herdsman.getCoordinatorVersion();
354
-
355
- await this.adapter.getForeignObject('system.adapter.' + this.adapter.namespace, (err, obj) => {
356
- if (!err && obj) {
357
- if (obj.common.installedFrom && obj.common.installedFrom.includes('://')) {
358
- const instFrom = obj.common.installedFrom;
359
- coordinatorinfo.installSource = instFrom.replace('tarball','commit');
360
- } else {
361
- coordinatorinfo.installSource = obj.common.installedFrom;
362
- }
363
- }
364
- try
365
- {
366
- coordinatorinfo.port = obj.native.port;
367
- coordinatorinfo.channel = obj.native.channel;
368
- coordinatorinfo.installedVersion = obj.native.version;
369
- if (coordinatorVersion && coordinatorVersion.type && coordinatorVersion.meta) {
370
- coordinatorinfo.type = coordinatorVersion.type;
371
- const meta = coordinatorVersion.meta;
372
- if (meta) {
373
- if (meta.hasOwnProperty('revision')) coordinatorinfo.revision = meta.revision;
374
- let vt = 'x-';
375
- if (meta.hasOwnProperty('transportrev')) vt = meta.transportrev + '-';
376
- if (meta.hasOwnProperty('product')) vt = vt + meta.product + '.';
377
- else vt = vt + 'x.';
378
- if (meta.hasOwnProperty('majorrel')) vt = vt + meta.majorrel + '.';
379
- else vt = vt + 'x.';
380
- if (meta.hasOwnProperty('minorrel')) vt = vt + meta.minorrel + '.';
381
- else vt = vt + 'x.';
382
- if (meta.hasOwnProperty('maintrel')) vt = vt + meta.maintrel + '.';
383
- else vt = vt + 'x.';
384
- coordinatorinfo.version = vt;
385
- }
386
- }
387
- }
388
- catch { this.warn('exception raised in getCoordinatorInfo');}
389
-
390
- this.debug('getCoorinatorInfo result: ' + JSON.stringify(coordinatorinfo));
391
- this.adapter.sendTo(from, command, coordinatorinfo, callback);
392
- });
393
- } else {
394
- this.adapter.sendTo(from, command, {error: 'You need save and run adapter before pairing!'}, callback);
395
- }
396
- }
397
-
398
-
399
- renameDevice(from, command, msg, callback) {
400
- if (this.stController) {
401
- const id = msg.id;
402
- const newName = msg.name;
403
- this.stController.renameDevice(id, newName);
404
- this.adapter.sendTo(from, command, {}, callback);
405
- }
406
- }
407
-
408
- deleteDevice(from, command, msg, callback) {
409
- if (this.zbController && this.stController) {
410
- this.debug('deleteDevice message: ' + JSON.stringify(msg));
411
- const id = msg.id;
412
- const force = msg.force;
413
- const sysid = id.replace(this.adapter.namespace + '.', '0x');
414
- const devId = id.replace(this.adapter.namespace + '.', '');
415
- this.debug('deleteDevice sysid: ' + sysid);
416
- const dev = this.zbController.getDevice(sysid);
417
- if (!dev) {
418
- this.debug('Not found!');
419
- this.debug('Try delete dev ' + devId + ' from iobroker.');
420
- this.stController.deleteDeviceStates(devId, () => {
421
- this.adapter.sendTo(from, command, {}, callback);
422
- });
423
- return;
424
- }
425
- this.zbController.remove(sysid, force, (err) => {
426
- if (!err) {
427
- this.stController.deleteDeviceStates(devId, () => {
428
- this.adapter.sendTo(from, command, {}, callback);
429
- });
430
- } else {
431
- this.debug('Error on remove! ' + err);
432
- this.adapter.sendTo(from, command, {error: err}, callback);
433
- }
434
- });
435
- } else {
436
- this.adapter.sendTo(from, command, {error: 'You need to save and start the adapter!'}, callback);
437
- }
438
- }
439
-
440
- async cleanDeviceStates(from, command, msg, callback) {
441
- this.info(`State cleanup with ${JSON.stringify(msg)}`);
442
- const devicesFromDB = await this.zbController.getClients(false);
443
- for (const device of devicesFromDB) {
444
- const entity = await this.zbController.resolveEntity(device);
445
- if (entity) {
446
- const model = (entity.mapped) ? entity.mapped.model : entity.device.modelID;
447
- await this.stController.deleteOrphanedDeviceStates(device.ieeeAddr, model, msg.force);
448
- }
449
- }
450
- this.adapter.sendTo(from, command, {}, callback);
451
- }
452
-
453
- async getChannels(from, command, message, callback) {
454
- if (this.zbController) {
455
- const result = await this.zbController.getChannelsEnergy();
456
- this.debug('getChannels result: ' + JSON.stringify(result));
457
- this.adapter.sendTo(from, command, result, callback);
458
- } else {
459
- this.adapter.sendTo(
460
- from, command,
461
- {error: 'You need to setup serial port and start the adapter before pairing!'},
462
- callback
463
- );
464
- }
465
- }
466
-
467
- async setDeviceActivated(from, command, msg, callback) {
468
- if (this.stController) {
469
- const id = msg.id;
470
- const targetstate = msg.deactivated;
471
- this.stController.setDeviceActivated(id, targetstate);
472
- this.adapter.sendTo(from, command, {}, callback);
473
- }
474
- }
475
-
476
- async reconfigure(from, command, msg, callback) {
477
- if (this.zbController) {
478
- const devid = getZbId(msg.id);
479
- this.debug(`Reconfigure ${devid}`);
480
- const entity = await this.zbController.resolveEntity(devid);
481
- if (entity) {
482
- try {
483
- await this.zbController.callExtensionMethod(
484
- 'doConfigure',
485
- [entity.device, entity.mapped],
486
- );
487
- this.adapter.sendTo(from, command, {}, callback);
488
- } catch (error) {
489
- const errmsg = `Reconfigure failed ${entity.device.ieeeAddr} ${entity.device.modelID}, ` +
490
- `(${error.stack})`;
491
- this.error(errmsg);
492
- this.adapter.sendTo(from, command, {error: errmsg}, callback);
493
- }
494
- } else {
495
- this.adapter.sendTo(from, command, {error: 'No device'}, callback);
496
- }
497
- }
498
- }
499
- }
500
-
501
- module.exports = Commands;
1
+ 'use strict';
2
+
3
+ const getZbId = require('./utils').getZbId;
4
+ const statesMapping = require('./devices');
5
+ const disallowedDashStates = [
6
+ 'link_quality', 'available', 'battery', 'groups', 'device_query',
7
+ 'hue_move', 'color_temp_move', 'satuation_move', 'brightness_move', 'brightness_step', 'hue_calibration',
8
+ 'msg_from_zigbee',
9
+ ];
10
+
11
+ class Commands {
12
+ constructor(adapter) {
13
+ this.adapter = adapter;
14
+ this.adapter.on('message', this.onMessage.bind(this));
15
+ }
16
+
17
+ start(zbController, stController) {
18
+ this.zbController = zbController;
19
+ this.stController = stController;
20
+ }
21
+
22
+ stop() {
23
+ delete this.zbController;
24
+ delete this.stController;
25
+ }
26
+
27
+ info(msg) {
28
+ this.adapter.log.info(msg);
29
+ }
30
+
31
+ error(msg) {
32
+ this.adapter.log.error(msg);
33
+ }
34
+
35
+ debug(msg) {
36
+ this.adapter.log.debug(msg);
37
+ }
38
+
39
+ warn(msg) {
40
+ this.adapter.log.warn(msg);
41
+ }
42
+
43
+ /**
44
+ * @param {ioBroker.Message} obj
45
+ */
46
+ onMessage(obj) {
47
+ if (typeof obj === 'object' && obj.command) {
48
+ switch (obj.command) {
49
+ case 'letsPairing':
50
+ if (obj && obj.message && typeof obj.message === 'object') {
51
+ this.letsPairing(obj.from, obj.command, obj.message, obj.callback);
52
+ }
53
+ break;
54
+ case 'touchlinkReset':
55
+ if (obj && obj.message && typeof obj.message === 'object') {
56
+ this.touchlinkReset(obj.from, obj.command, obj.message, obj.callback);
57
+ }
58
+ break;
59
+ case 'getDevices':
60
+ if (obj && obj.message && typeof obj.message === 'object') {
61
+ this.getDevices(obj.from, obj.command, null, obj.callback);
62
+ }
63
+ break;
64
+ case 'renameDevice':
65
+ if (obj && obj.message && typeof obj.message === 'object') {
66
+ this.renameDevice(obj.from, obj.command, obj.message, obj.callback);
67
+ }
68
+ break;
69
+ case 'deleteDevice':
70
+ if (obj && obj.message && typeof obj.message === 'object') {
71
+ this.deleteDevice(obj.from, obj.command, obj.message, obj.callback);
72
+ }
73
+ break;
74
+ case 'getChannels':
75
+ if (obj && obj.message && typeof obj.message === 'object') {
76
+ this.getChannels(obj.from, obj.command, obj.message, obj.callback);
77
+ }
78
+ break;
79
+ case 'getCoordinatorInfo':
80
+ if (obj && obj.message && typeof obj.message === 'object') {
81
+ this.getCoordinatorInfo(obj.from, obj.command, obj.callback);
82
+ }
83
+ break;
84
+ case 'cleanDeviceStates':
85
+ if (obj && obj.message && typeof obj.message === 'object') {
86
+ this.cleanDeviceStates(obj.from, obj.command, obj.message, obj.callback);
87
+ }
88
+ break;
89
+ case 'setState':
90
+ if (obj && obj.message && typeof obj.message === 'object' && obj.message.id) {
91
+ // this.adapter.setState(obj.message.id, obj.message.val, false, obj.callback);
92
+ this.stController.setState_typed(obj.message.id, obj.message.val, false, undefined, obj.callback);
93
+ }
94
+ break;
95
+ case 'getDevice':
96
+ if (obj && obj.message && typeof obj.message === 'object' && obj.message.id) {
97
+ this.getDevices(obj.from, obj.command, obj.message.id, obj.callback);
98
+ }
99
+ break;
100
+ case 'reconfigure':
101
+ if (obj && obj.message && typeof obj.message === 'object') {
102
+ this.reconfigure(obj.from, obj.command, obj.message, obj.callback);
103
+ }
104
+ break;
105
+ case 'setDeviceActivated':
106
+ if (obj && obj.message && typeof obj.message === 'object') {
107
+ this.setDeviceActivated(obj.from, obj.command, obj.message, obj.callback);
108
+ }
109
+ break;
110
+
111
+ }
112
+ }
113
+ }
114
+
115
+
116
+ letsPairing(from, command, message, callback) {
117
+ if (this.zbController) {
118
+ let devId = '';
119
+ if (message && message.id) {
120
+ devId = getZbId(message.id);
121
+ }
122
+ // allow devices to join the network within 60 secs
123
+ this.adapter.logToPairing('Pairing started ' + devId, true);
124
+
125
+ let cTimer = Number(this.adapter.config.countDown);
126
+ if (!this.adapter.config.countDown || cTimer == 0) {
127
+ cTimer = 60;
128
+ }
129
+
130
+ this.zbController.permitJoin(cTimer, devId, (err) => {
131
+ if (!err) {
132
+ // set pairing mode on
133
+ this.adapter.setState('info.pairingMode', true);
134
+ }
135
+ });
136
+ this.adapter.sendTo(from, command, 'Start pairing!', callback);
137
+ } else {
138
+ this.adapter.sendTo(
139
+ from, command,
140
+ {error: 'You need to setup serial port and start the adapter before pairing!'},
141
+ callback
142
+ );
143
+ }
144
+ }
145
+
146
+ touchlinkReset(from, command, message, callback) {
147
+ if (this.zbController) {
148
+ // allow devices to join the network within 60 secs
149
+ this.adapter.logToPairing('Touchlink reset started ', true);
150
+
151
+ let cTimer = Number(this.adapter.config.countDown);
152
+ if (!this.adapter.config.countDown || cTimer == 0) {
153
+ cTimer = 60;
154
+ }
155
+
156
+ this.zbController.touchlinkReset(cTimer);
157
+ this.adapter.sendTo(from, command, 'Start touchlink reset and pairing!', callback);
158
+ } else {
159
+ this.adapter.sendTo(
160
+ from, command,
161
+ {error: 'You need to setup serial port and start the adapter before pairing!'},
162
+ callback
163
+ );
164
+ }
165
+ }
166
+
167
+ async getDevices(from, command, id, callback) {
168
+ if (this.zbController) {
169
+ const pairedDevices = await this.zbController.getClients(true);
170
+ const groups = {};
171
+ let rooms;
172
+ this.adapter.getEnumsAsync('enum.rooms')
173
+ .then((enums) => {
174
+ // rooms
175
+ rooms = enums['enum.rooms'];
176
+ })
177
+ .then(() => {
178
+ // get all adapter devices
179
+ return this.adapter.getDevicesAsync();
180
+ })
181
+ .then(async (result) => {
182
+ const alls = (id) ? await this.adapter.getStatesAsync(id+'.*') : await this.adapter.getStatesAsync('*');
183
+ const allst = (id) ? await this.adapter.getStatesOfAsync(id) : await this.adapter.getStatesOfAsync();
184
+ result = result.filter((item)=>!id || id == item._id);
185
+ // get device states and groups
186
+ result.forEach(async (devInfo) => {
187
+ if (devInfo._id) {
188
+ // groups
189
+ // const grState = alls[`${devInfo._id}.groups`];
190
+ // if (grState && grState.val) {
191
+ // groups[devInfo._id] = JSON.parse(grState.val);
192
+ // }
193
+ // battery and link_quality
194
+ const lqState = alls[`${devInfo._id}.link_quality`];
195
+ devInfo.link_quality = (lqState) ? lqState.val: undefined;
196
+ devInfo.link_quality_lc = (lqState) ? lqState.lc: undefined;
197
+ const batState = alls[`${devInfo._id}.battery`];
198
+ devInfo.battery = (batState) ? batState.val: undefined;
199
+ // devInfo.states = states || {};
200
+
201
+ const states = allst.filter((item)=>item._id.startsWith(devInfo._id));
202
+
203
+ // put only allowed states
204
+ devInfo.statesDef = (states || []).filter((stateDef)=>{
205
+ const sid = stateDef._id;
206
+ const name = sid.split('.').pop();
207
+ if (disallowedDashStates.includes(name)) return false;
208
+ return true;
209
+ }).map((stateDef)=>{
210
+ const name = stateDef.common.name;
211
+ const devname = devInfo.common.name;
212
+ // replace state
213
+ return {
214
+ id: stateDef._id,
215
+ name: (typeof name === 'string') ? name.replace(devname, '') : name,
216
+ type: stateDef.common.type,
217
+ read: stateDef.common.read,
218
+ write: stateDef.common.write,
219
+ val: (alls[stateDef._id]) ? alls[stateDef._id].val : undefined,
220
+ role: stateDef.common.role,
221
+ unit: stateDef.common.unit,
222
+ states: stateDef.common.states,
223
+ };
224
+ });
225
+ }
226
+ });
227
+ return result;
228
+ })
229
+ .then(async (result) => {
230
+ // combine info
231
+ const devices = [];
232
+ for (const devInfo of result) {
233
+ if (devInfo._id.indexOf('group') > 0) {
234
+ devInfo.icon = 'img/group.png';
235
+ devInfo.vendor = 'ioBroker';
236
+ // get group members and store them
237
+ const match = /zigbee.\d.group_([0-9]+)/.exec(devInfo._id);
238
+ if (match && match.length > 1) {
239
+ const groupmembers = await this.zbController.getGroupMembersFromController(match[1]);
240
+ this.debug('group members: ' + JSON.stringify(groupmembers));
241
+ if (groupmembers && groupmembers.length > 0) {
242
+ const memberinfo = [];
243
+ for (const member of groupmembers) {
244
+ if (groups) {
245
+ const grouparray = groups[member.ieee];
246
+ if (grouparray)
247
+ {
248
+ if (!grouparray.includes(match[1])) {
249
+ groups[member.ieee].push(match[1])
250
+ }
251
+ }
252
+ else groups[member.ieee] = [match[1]];
253
+ }
254
+ const device = await this.adapter.getObjectAsync(this.adapter.namespace + '.' + member.ieee.substr(2));
255
+ if (device) {
256
+ member.device = device.common.name;
257
+ }
258
+ else {
259
+ member.device = 'unknown';
260
+ }
261
+ memberinfo.push(member);
262
+ }
263
+ devInfo.memberinfo = memberinfo;
264
+ this.debug('memberinfo: ' + JSON.stringify(devInfo.memberinfo));
265
+ }
266
+ }
267
+ }
268
+ else
269
+ {
270
+ const modelDesc = statesMapping.findModel(devInfo.common.type);
271
+ devInfo.icon = (modelDesc && modelDesc.icon) ? modelDesc.icon : 'img/unknown.png';
272
+ devInfo.vendor = (modelDesc) ? modelDesc.vendor : '';
273
+ }
274
+ const id = getZbId(devInfo._id);
275
+ devInfo.info = await this.zbController.resolveEntity(id);
276
+
277
+ devInfo.rooms = [];
278
+ for (const room in rooms) {
279
+ if (!rooms.hasOwnProperty(room) ||
280
+ !rooms[room] ||
281
+ !rooms[room].common ||
282
+ !rooms[room].common.members) {
283
+ continue;
284
+ }
285
+ if (rooms[room].common.members.indexOf(devInfo._id) !== -1) {
286
+ devInfo.rooms.push(rooms[room].common.name);
287
+ }
288
+ }
289
+ devInfo.paired = !!devInfo.info;
290
+ // devInfo.groups = groups[devInfo._id];
291
+ devices.push(devInfo);
292
+ }
293
+ return devices;
294
+ })
295
+ .then(async (devices) => {
296
+ // fill group info
297
+ for (const groupdev in groups) {
298
+ this.debug('GetDevices scanning group ' + groupdev + ' ' + JSON.stringify(groups[groupdev]))
299
+ const device = devices.find((dev) =>( groupdev === getZbId(dev._id) ))
300
+ if (device) {
301
+ device.groups = groups[groupdev]
302
+ this.debug('adding group info to device ' + groupdev);
303
+ }
304
+ }
305
+ // append devices that paired but not created
306
+ if (!id) {
307
+ for (const d of pairedDevices) {
308
+ const device = await this.zbController.resolveEntity(d.ieeeAddr);
309
+ if (!device) continue;
310
+ const exists = devices.find((dev) => (dev._id && device.device.ieeeAddr === getZbId(dev._id)));
311
+ if (!exists) {
312
+ devices.push({
313
+ _id: device.device.ieeeAddr,
314
+ icon: 'img/unknown.png',
315
+ paired: true,
316
+ info: device,
317
+ common: {
318
+ name: undefined,
319
+ type: undefined,
320
+ },
321
+ native: {}
322
+ });
323
+ }
324
+ }
325
+ }
326
+ return devices;
327
+ })
328
+ .then((devices) => {
329
+ this.debug('getDevices result: ' + JSON.stringify(devices));
330
+ this.adapter.sendTo(from, command, devices, callback);
331
+ })
332
+ .catch((err) => {
333
+ this.error('getDevices error: ' + err.stack);
334
+ });
335
+ } else {
336
+ this.adapter.sendTo(from, command, {error: 'You need save and run adapter before pairing!'}, callback);
337
+ }
338
+ }
339
+
340
+
341
+ async getCoordinatorInfo(from, command, callback) {
342
+ if (this.zbController) {
343
+ const coordinatorinfo = {
344
+ installSource: 'IADefault_1',
345
+ channel: '-1',
346
+ port: 'Default_1',
347
+ installedVersion: 'Default_1',
348
+ type: 'Default_1',
349
+ revision: 'Default_1',
350
+ version: '9-9.9.9.9'
351
+ };
352
+
353
+ const coordinatorVersion = await this.adapter.zbController.herdsman.getCoordinatorVersion();
354
+
355
+ await this.adapter.getForeignObject('system.adapter.' + this.adapter.namespace, (err, obj) => {
356
+ if (!err && obj) {
357
+ if (obj.common.installedFrom && obj.common.installedFrom.includes('://')) {
358
+ const instFrom = obj.common.installedFrom;
359
+ coordinatorinfo.installSource = instFrom.replace('tarball','commit');
360
+ } else {
361
+ coordinatorinfo.installSource = obj.common.installedFrom;
362
+ }
363
+ }
364
+ try
365
+ {
366
+ coordinatorinfo.port = obj.native.port;
367
+ coordinatorinfo.channel = obj.native.channel;
368
+ coordinatorinfo.installedVersion = obj.native.version;
369
+ if (coordinatorVersion && coordinatorVersion.type && coordinatorVersion.meta) {
370
+ coordinatorinfo.type = coordinatorVersion.type;
371
+ const meta = coordinatorVersion.meta;
372
+ if (meta) {
373
+ if (meta.hasOwnProperty('revision')) coordinatorinfo.revision = meta.revision;
374
+ let vt = 'x-';
375
+ if (meta.hasOwnProperty('transportrev')) vt = meta.transportrev + '-';
376
+ if (meta.hasOwnProperty('product')) vt = vt + meta.product + '.';
377
+ else vt = vt + 'x.';
378
+ if (meta.hasOwnProperty('majorrel')) vt = vt + meta.majorrel + '.';
379
+ else vt = vt + 'x.';
380
+ if (meta.hasOwnProperty('minorrel')) vt = vt + meta.minorrel + '.';
381
+ else vt = vt + 'x.';
382
+ if (meta.hasOwnProperty('maintrel')) vt = vt + meta.maintrel + '.';
383
+ else vt = vt + 'x.';
384
+ coordinatorinfo.version = vt;
385
+ }
386
+ }
387
+ }
388
+ catch { this.warn('exception raised in getCoordinatorInfo');}
389
+
390
+ this.debug('getCoorinatorInfo result: ' + JSON.stringify(coordinatorinfo));
391
+ this.adapter.sendTo(from, command, coordinatorinfo, callback);
392
+ });
393
+ } else {
394
+ this.adapter.sendTo(from, command, {error: 'You need save and run adapter before pairing!'}, callback);
395
+ }
396
+ }
397
+
398
+
399
+ renameDevice(from, command, msg, callback) {
400
+ if (this.stController) {
401
+ const id = msg.id;
402
+ const newName = msg.name;
403
+ this.stController.renameDevice(id, newName);
404
+ this.adapter.sendTo(from, command, {}, callback);
405
+ }
406
+ }
407
+
408
+ deleteDevice(from, command, msg, callback) {
409
+ if (this.zbController && this.stController) {
410
+ this.debug('deleteDevice message: ' + JSON.stringify(msg));
411
+ const id = msg.id;
412
+ const force = msg.force;
413
+ const sysid = id.replace(this.adapter.namespace + '.', '0x');
414
+ const devId = id.replace(this.adapter.namespace + '.', '');
415
+ this.debug('deleteDevice sysid: ' + sysid);
416
+ const dev = this.zbController.getDevice(sysid);
417
+ if (!dev) {
418
+ this.debug('Not found!');
419
+ this.debug('Try delete dev ' + devId + ' from iobroker.');
420
+ this.stController.deleteDeviceStates(devId, () => {
421
+ this.adapter.sendTo(from, command, {}, callback);
422
+ });
423
+ return;
424
+ }
425
+ this.zbController.remove(sysid, force, (err) => {
426
+ if (!err) {
427
+ this.stController.deleteDeviceStates(devId, () => {
428
+ this.adapter.sendTo(from, command, {}, callback);
429
+ });
430
+ } else {
431
+ this.debug('Error on remove! ' + err);
432
+ this.adapter.sendTo(from, command, {error: err}, callback);
433
+ }
434
+ });
435
+ } else {
436
+ this.adapter.sendTo(from, command, {error: 'You need to save and start the adapter!'}, callback);
437
+ }
438
+ }
439
+
440
+ async cleanDeviceStates(from, command, msg, callback) {
441
+ this.info(`State cleanup with ${JSON.stringify(msg)}`);
442
+ const devicesFromDB = await this.zbController.getClients(false);
443
+ for (const device of devicesFromDB) {
444
+ const entity = await this.zbController.resolveEntity(device);
445
+ if (entity) {
446
+ const model = (entity.mapped) ? entity.mapped.model : entity.device.modelID;
447
+ await this.stController.deleteOrphanedDeviceStates(device.ieeeAddr, model, msg.force);
448
+ }
449
+ }
450
+ this.adapter.sendTo(from, command, {}, callback);
451
+ }
452
+
453
+ async getChannels(from, command, message, callback) {
454
+ if (this.zbController) {
455
+ const result = await this.zbController.getChannelsEnergy();
456
+ this.debug('getChannels result: ' + JSON.stringify(result));
457
+ this.adapter.sendTo(from, command, result, callback);
458
+ } else {
459
+ this.adapter.sendTo(
460
+ from, command,
461
+ {error: 'You need to setup serial port and start the adapter before pairing!'},
462
+ callback
463
+ );
464
+ }
465
+ }
466
+
467
+ async setDeviceActivated(from, command, msg, callback) {
468
+ if (this.stController) {
469
+ const id = msg.id;
470
+ const targetstate = msg.deactivated;
471
+ this.stController.setDeviceActivated(id, targetstate);
472
+ this.adapter.sendTo(from, command, {}, callback);
473
+ }
474
+ }
475
+
476
+ async reconfigure(from, command, msg, callback) {
477
+ if (this.zbController) {
478
+ const devid = getZbId(msg.id);
479
+ this.debug(`Reconfigure ${devid}`);
480
+ const entity = await this.zbController.resolveEntity(devid);
481
+ if (entity) {
482
+ try {
483
+ await this.zbController.callExtensionMethod(
484
+ 'doConfigure',
485
+ [entity.device, entity.mapped],
486
+ );
487
+ this.adapter.sendTo(from, command, {}, callback);
488
+ } catch (error) {
489
+ const errmsg = `Reconfigure failed ${entity.device.ieeeAddr} ${entity.device.modelID}, ` +
490
+ `(${error.stack})`;
491
+ this.error(errmsg);
492
+ this.adapter.sendTo(from, command, {error: errmsg}, callback);
493
+ }
494
+ } else {
495
+ this.adapter.sendTo(from, command, {error: 'No device'}, callback);
496
+ }
497
+ }
498
+ }
499
+ }
500
+
501
+ module.exports = Commands;