iobroker.zigbee 1.5.6 → 1.6.8

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