iobroker.zigbee 1.7.1 → 1.7.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.
Files changed (55) 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 +379 -373
  9. package/admin/adapter-settings.js +244 -244
  10. package/admin/admin.js +2926 -2926
  11. package/admin/img/MLI-404011-MLI-404049.png +0 -0
  12. package/admin/img/philips_hue_lom001.png +0 -0
  13. package/admin/index.html +159 -159
  14. package/admin/index_m.html +1161 -1161
  15. package/admin/moment.min.js +1 -1
  16. package/admin/shuffle.min.js +2 -2
  17. package/admin/tab_m.html +944 -944
  18. package/admin/vis-network.min.css +1 -1
  19. package/admin/vis-network.min.js +27 -27
  20. package/admin/words.js +112 -112
  21. package/docs/de/readme.md +27 -27
  22. package/docs/en/readme.md +30 -30
  23. package/docs/flashing_via_arduino_(en).md +110 -110
  24. package/docs/ru/readme.md +28 -28
  25. package/docs/tutorial/groups-1.png +0 -0
  26. package/docs/tutorial/groups-2.png +0 -0
  27. package/docs/tutorial/tab-dev-1.png +0 -0
  28. package/io-package.json +354 -330
  29. package/lib/backup.js +171 -171
  30. package/lib/binding.js +325 -325
  31. package/lib/colors.js +460 -460
  32. package/lib/commands.js +501 -501
  33. package/lib/developer.js +148 -148
  34. package/lib/devices.js +3145 -3145
  35. package/lib/exclude.js +168 -168
  36. package/lib/exposes.js +804 -804
  37. package/lib/groups.js +342 -342
  38. package/lib/json.js +60 -60
  39. package/lib/networkmap.js +56 -56
  40. package/lib/ota.js +179 -179
  41. package/lib/rgb.js +255 -255
  42. package/lib/seriallist.js +37 -37
  43. package/lib/states.js +6416 -6416
  44. package/lib/statescontroller.js +666 -658
  45. package/lib/tools.js +54 -54
  46. package/lib/utils.js +151 -151
  47. package/lib/zbBaseExtension.js +36 -36
  48. package/lib/zbDelayedAction.js +152 -152
  49. package/lib/zbDeviceAvailability.js +315 -315
  50. package/lib/zbDeviceConfigure.js +152 -152
  51. package/lib/zbDeviceEvent.js +49 -49
  52. package/lib/zigbeecontroller.js +946 -946
  53. package/package.json +2 -2
  54. package/support/docgen.js +93 -93
  55. package/admin/img/zbt_remote.png +0 -0
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','send_payload'
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','send_payload'
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;