iobroker.zigbee 1.8.23 → 1.8.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +422 -415
- package/admin/adapter-settings.js +244 -244
- package/admin/admin.js +2991 -2981
- package/admin/i18n/de/translations.json +108 -108
- package/admin/i18n/en/translations.json +108 -108
- package/admin/i18n/es/translations.json +102 -102
- package/admin/i18n/fr/translations.json +108 -108
- package/admin/i18n/it/translations.json +102 -102
- package/admin/i18n/nl/translations.json +108 -108
- package/admin/i18n/pl/translations.json +108 -108
- package/admin/i18n/pt/translations.json +102 -102
- package/admin/i18n/ru/translations.json +108 -108
- package/admin/i18n/uk/translations.json +108 -108
- package/admin/i18n/zh-cn/translations.json +102 -102
- package/admin/img/LED1923R5.png +0 -0
- package/admin/img/philips_hue_lom001.png +0 -0
- package/admin/index.html +163 -159
- package/admin/index_m.html +1360 -1356
- package/admin/moment.min.js +1 -1
- package/admin/shuffle.min.js +2 -2
- package/admin/tab_m.html +1021 -1009
- package/admin/vis-network.min.css +1 -1
- package/admin/vis-network.min.js +26 -27
- package/admin/words.js +110 -110
- package/docs/de/basedocu.md +19 -19
- package/docs/de/readme.md +126 -126
- package/docs/en/readme.md +128 -128
- package/docs/flashing_via_arduino_(en).md +110 -110
- package/docs/ru/readme.md +28 -28
- package/docs/tutorial/groups-1.png +0 -0
- package/docs/tutorial/groups-2.png +0 -0
- package/docs/tutorial/tab-dev-1.png +0 -0
- package/io-package.json +41 -40
- package/lib/backup.js +171 -171
- package/lib/binding.js +319 -319
- package/lib/colors.js +465 -465
- package/lib/commands.js +534 -534
- package/lib/developer.js +151 -145
- package/lib/devices.js +3139 -3135
- package/lib/exclude.js +162 -162
- package/lib/exposes.js +913 -913
- package/lib/groups.js +345 -345
- package/lib/json.js +59 -59
- package/lib/networkmap.js +55 -55
- package/lib/ota.js +198 -198
- package/lib/rgb.js +297 -297
- package/lib/seriallist.js +48 -48
- package/lib/states.js +6420 -6420
- package/lib/statescontroller.js +672 -672
- package/lib/tools.js +54 -54
- package/lib/utils.js +165 -165
- package/lib/zbBaseExtension.js +36 -36
- package/lib/zbDelayedAction.js +144 -144
- package/lib/zbDeviceAvailability.js +319 -319
- package/lib/zbDeviceConfigure.js +151 -147
- package/lib/zbDeviceEvent.js +48 -48
- package/lib/zigbeecontroller.js +1014 -989
- package/main.js +2 -2
- package/package.json +14 -14
- package/support/docgen.js +93 -93
package/lib/commands.js
CHANGED
|
@@ -1,534 +1,534 @@
|
|
|
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', obj => this.onMessage(obj));
|
|
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
|
-
async letsPairing(from, command, message, callback) {
|
|
116
|
-
if (this.zbController) {
|
|
117
|
-
let devId = '';
|
|
118
|
-
if (message) {
|
|
119
|
-
if (message.id) devId = getZbId(message.id);
|
|
120
|
-
if (message.code) {
|
|
121
|
-
try {
|
|
122
|
-
this.debug(`letsPairing called with code ${message.code}`);
|
|
123
|
-
const success = await this.zbController.addPairingCode(message.code);
|
|
124
|
-
if (!success) {
|
|
125
|
-
this.adapter.sendTo(
|
|
126
|
-
from, command,
|
|
127
|
-
{error: 'Pairing code rejected by Coordinator!'},
|
|
128
|
-
callback
|
|
129
|
-
);
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
catch (e) {
|
|
134
|
-
this.error(JSON.stringify(e));
|
|
135
|
-
this.adapter.sendTo(
|
|
136
|
-
from, command,
|
|
137
|
-
{error: 'Exception when trying to add QR code'},
|
|
138
|
-
callback
|
|
139
|
-
);
|
|
140
|
-
return;
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
// allow devices to join the network within 60 secs
|
|
146
|
-
this.adapter.logToPairing('Pairing started ' + devId, true);
|
|
147
|
-
|
|
148
|
-
let cTimer = Number(this.adapter.config.countDown);
|
|
149
|
-
if (!this.adapter.config.countDown || !cTimer) {
|
|
150
|
-
cTimer = 60;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
this.zbController.permitJoin(cTimer, devId, err => {
|
|
154
|
-
if (!err) {
|
|
155
|
-
// set pairing mode on
|
|
156
|
-
this.adapter.setState('info.pairingMode', true);
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
this.adapter.sendTo(from, command, 'Start pairing!', callback);
|
|
160
|
-
} else {
|
|
161
|
-
this.adapter.sendTo(
|
|
162
|
-
from, command,
|
|
163
|
-
{error: 'You need to setup serial port and start the adapter before pairing!'},
|
|
164
|
-
callback
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
touchlinkReset(from, command, message, callback) {
|
|
170
|
-
if (this.zbController) {
|
|
171
|
-
// allow devices to join the network within 60 secs
|
|
172
|
-
this.adapter.logToPairing('Touchlink reset started ', true);
|
|
173
|
-
|
|
174
|
-
let cTimer = Number(this.adapter.config.countDown);
|
|
175
|
-
if (!this.adapter.config.countDown || !cTimer) {
|
|
176
|
-
cTimer = 60;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
this.zbController.touchlinkReset(cTimer);
|
|
180
|
-
this.adapter.sendTo(from, command, 'Start touchlink reset and pairing!', callback);
|
|
181
|
-
} else {
|
|
182
|
-
this.adapter.sendTo(
|
|
183
|
-
from, command,
|
|
184
|
-
{error: 'You need to setup serial port and start the adapter before pairing!'},
|
|
185
|
-
callback
|
|
186
|
-
);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
async getDevices(from, command, id, callback) {
|
|
191
|
-
if (this.zbController) {
|
|
192
|
-
const pairedDevices = await this.zbController.getClients(true);
|
|
193
|
-
const groups = {};
|
|
194
|
-
let rooms;
|
|
195
|
-
this.adapter.getEnumsAsync('enum.rooms')
|
|
196
|
-
.then(enums => {
|
|
197
|
-
// rooms
|
|
198
|
-
rooms = enums['enum.rooms'];
|
|
199
|
-
})
|
|
200
|
-
// get all adapter devices
|
|
201
|
-
.then(() => this.adapter.getDevicesAsync())
|
|
202
|
-
.then(async result => {
|
|
203
|
-
const alls = id ? await this.adapter.getStatesAsync(id + '.*') : await this.adapter.getStatesAsync('*');
|
|
204
|
-
const allst = id ? await this.adapter.getStatesOfAsync(id) : await this.adapter.getStatesOfAsync();
|
|
205
|
-
result = result.filter(item => !id || id === item._id);
|
|
206
|
-
// get device states and groups
|
|
207
|
-
result.forEach(async devInfo => {
|
|
208
|
-
if (devInfo._id) {
|
|
209
|
-
// groups
|
|
210
|
-
// const grState = alls[`${devInfo._id}.groups`];
|
|
211
|
-
// if (grState && grState.val) {
|
|
212
|
-
// groups[devInfo._id] = JSON.parse(grState.val);
|
|
213
|
-
// }
|
|
214
|
-
// battery and link_quality
|
|
215
|
-
const lqState = alls[`${devInfo._id}.link_quality`];
|
|
216
|
-
devInfo.link_quality = lqState ? lqState.val : undefined;
|
|
217
|
-
devInfo.link_quality_lc = lqState ? lqState.lc : undefined;
|
|
218
|
-
const batState = alls[`${devInfo._id}.battery`];
|
|
219
|
-
devInfo.battery = batState ? batState.val : undefined;
|
|
220
|
-
// devInfo.states = states || {};
|
|
221
|
-
|
|
222
|
-
const states = allst.filter(item => item._id.startsWith(devInfo._id));
|
|
223
|
-
|
|
224
|
-
// put only allowed states
|
|
225
|
-
devInfo.statesDef = (states || []).filter(stateDef => {
|
|
226
|
-
const sid = stateDef._id;
|
|
227
|
-
const name = sid.split('.').pop();
|
|
228
|
-
return !disallowedDashStates.includes(name);
|
|
229
|
-
|
|
230
|
-
}).map(stateDef => {
|
|
231
|
-
const name = stateDef.common.name;
|
|
232
|
-
const devname = devInfo.common.name;
|
|
233
|
-
// replace state
|
|
234
|
-
return {
|
|
235
|
-
id: stateDef._id,
|
|
236
|
-
name: typeof name === 'string' ? name.replace(devname, '') : name,
|
|
237
|
-
type: stateDef.common.type,
|
|
238
|
-
read: stateDef.common.read,
|
|
239
|
-
write: stateDef.common.write,
|
|
240
|
-
val: alls[stateDef._id] ? alls[stateDef._id].val : undefined,
|
|
241
|
-
role: stateDef.common.role,
|
|
242
|
-
unit: stateDef.common.unit,
|
|
243
|
-
states: stateDef.common.states,
|
|
244
|
-
};
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
});
|
|
248
|
-
return result;
|
|
249
|
-
})
|
|
250
|
-
.then(async result => {
|
|
251
|
-
// combine info
|
|
252
|
-
const devices = [];
|
|
253
|
-
for (const devInfo of result) {
|
|
254
|
-
if (devInfo._id.indexOf('group') > 0) {
|
|
255
|
-
devInfo.icon = 'img/group.png';
|
|
256
|
-
devInfo.vendor = 'ioBroker';
|
|
257
|
-
// get group members and store them
|
|
258
|
-
const match = /zigbee.\d.group_([0-9]+)/.exec(devInfo._id);
|
|
259
|
-
if (match && match.length > 1) {
|
|
260
|
-
const groupmembers = await this.zbController.getGroupMembersFromController(match[1]);
|
|
261
|
-
this.debug(`group members: ${JSON.stringify(groupmembers)}`);
|
|
262
|
-
if (groupmembers && groupmembers.length > 0) {
|
|
263
|
-
const memberinfo = [];
|
|
264
|
-
for (const member of groupmembers) {
|
|
265
|
-
if (groups) {
|
|
266
|
-
const grouparray = groups[member.ieee];
|
|
267
|
-
if (grouparray) {
|
|
268
|
-
if (!grouparray.includes(match[1])) {
|
|
269
|
-
groups[member.ieee].push(match[1]);
|
|
270
|
-
}
|
|
271
|
-
} else {
|
|
272
|
-
groups[member.ieee] = [match[1]];
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
const device = await this.adapter.getObjectAsync(`${this.adapter.namespace}.${member.ieee.substr(2)}`);
|
|
276
|
-
if (device) {
|
|
277
|
-
member.device = device.common.name;
|
|
278
|
-
} else {
|
|
279
|
-
member.device = 'unknown';
|
|
280
|
-
}
|
|
281
|
-
memberinfo.push(member);
|
|
282
|
-
}
|
|
283
|
-
devInfo.memberinfo = memberinfo;
|
|
284
|
-
this.debug(`memberinfo for ${match[1]}: ${JSON.stringify(devInfo.memberinfo)}`);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
} else {
|
|
288
|
-
const modelDesc = statesMapping.findModel(devInfo.common.type);
|
|
289
|
-
devInfo.icon = (modelDesc && modelDesc.icon) ? modelDesc.icon : 'img/unknown.png';
|
|
290
|
-
devInfo.vendor = modelDesc ? modelDesc.vendor : '';
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
const id = getZbId(devInfo._id);
|
|
294
|
-
devInfo.info = await this.zbController.resolveEntity(id);
|
|
295
|
-
|
|
296
|
-
devInfo.rooms = [];
|
|
297
|
-
for (const room in rooms) {
|
|
298
|
-
if (!rooms.hasOwnProperty(room) ||
|
|
299
|
-
!rooms[room] ||
|
|
300
|
-
!rooms[room].common ||
|
|
301
|
-
!rooms[room].common.members
|
|
302
|
-
) {
|
|
303
|
-
continue;
|
|
304
|
-
}
|
|
305
|
-
if (rooms[room].common.members.includes(devInfo._id)) {
|
|
306
|
-
devInfo.rooms.push(rooms[room].common.name);
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
devInfo.paired = !!devInfo.info;
|
|
310
|
-
// devInfo.groups = groups[devInfo._id];
|
|
311
|
-
devices.push(devInfo);
|
|
312
|
-
}
|
|
313
|
-
return devices;
|
|
314
|
-
})
|
|
315
|
-
.then(async (devices) => {
|
|
316
|
-
// fill group info
|
|
317
|
-
for (const groupdev in groups) {
|
|
318
|
-
//this.debug(`GetDevices scanning group ${groupdev} ${JSON.stringify(groups[groupdev])}`);
|
|
319
|
-
const device = devices.find(dev => (groupdev === getZbId(dev._id)));
|
|
320
|
-
if (device) {
|
|
321
|
-
device.groups = groups[groupdev];
|
|
322
|
-
//this.debug(`adding group info to device ${groupdev}`);
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
// append devices that paired but not created
|
|
326
|
-
if (!id) {
|
|
327
|
-
for (const d of pairedDevices) {
|
|
328
|
-
const device = await this.zbController.resolveEntity(d.ieeeAddr);
|
|
329
|
-
if (!device) {
|
|
330
|
-
continue;
|
|
331
|
-
}
|
|
332
|
-
const exists = devices.find((dev) => (dev._id && device.device.ieeeAddr === getZbId(dev._id)));
|
|
333
|
-
if (!exists) {
|
|
334
|
-
devices.push({
|
|
335
|
-
_id: device.device.ieeeAddr,
|
|
336
|
-
icon: 'img/unknown.png',
|
|
337
|
-
paired: true,
|
|
338
|
-
info: device,
|
|
339
|
-
common: {
|
|
340
|
-
name: undefined,
|
|
341
|
-
type: undefined,
|
|
342
|
-
},
|
|
343
|
-
native: {}
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
return devices;
|
|
349
|
-
})
|
|
350
|
-
.then(devices => {
|
|
351
|
-
this.debug(`getDevices result: ${JSON.stringify(devices)}`);
|
|
352
|
-
this.adapter.sendTo(from, command, devices, callback);
|
|
353
|
-
})
|
|
354
|
-
.catch(err => this.error(`getDevices error: ${err.stack}`));
|
|
355
|
-
} else {
|
|
356
|
-
this.adapter.sendTo(from, command, {error: 'You need save and run adapter before pairing!'}, callback);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
async getCoordinatorInfo(from, command, callback) {
|
|
362
|
-
if (this.zbController) {
|
|
363
|
-
const coordinatorinfo = {
|
|
364
|
-
installSource: 'IADefault_1',
|
|
365
|
-
channel: '-1',
|
|
366
|
-
port: 'Default_1',
|
|
367
|
-
installedVersion: 'Default_1',
|
|
368
|
-
type: 'Default_1',
|
|
369
|
-
revision: 'Default_1',
|
|
370
|
-
version: '9-9.9.9.9'
|
|
371
|
-
};
|
|
372
|
-
|
|
373
|
-
const coordinatorVersion = await this.adapter.zbController.herdsman.getCoordinatorVersion();
|
|
374
|
-
|
|
375
|
-
await this.adapter.getForeignObject(`system.adapter.${this.adapter.namespace}`, (err, obj) => {
|
|
376
|
-
if (!err && obj) {
|
|
377
|
-
if (obj.common.installedFrom && obj.common.installedFrom.includes('://')) {
|
|
378
|
-
const instFrom = obj.common.installedFrom;
|
|
379
|
-
coordinatorinfo.installSource = instFrom.replace('tarball', 'commit');
|
|
380
|
-
} else {
|
|
381
|
-
coordinatorinfo.installSource = obj.common.installedFrom;
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
try {
|
|
385
|
-
coordinatorinfo.port = obj.native.port;
|
|
386
|
-
coordinatorinfo.channel = obj.native.channel;
|
|
387
|
-
coordinatorinfo.installedVersion = obj.native.version;
|
|
388
|
-
if (coordinatorVersion && coordinatorVersion.type && coordinatorVersion.meta) {
|
|
389
|
-
coordinatorinfo.type = coordinatorVersion.type;
|
|
390
|
-
const meta = coordinatorVersion.meta;
|
|
391
|
-
if (meta) {
|
|
392
|
-
if (meta.hasOwnProperty('revision')) {
|
|
393
|
-
coordinatorinfo.revision = meta.revision;
|
|
394
|
-
}
|
|
395
|
-
let vt = 'x-';
|
|
396
|
-
if (meta.hasOwnProperty('transportrev')) {
|
|
397
|
-
vt = meta.transportrev + '-';
|
|
398
|
-
}
|
|
399
|
-
if (meta.hasOwnProperty('product')) {
|
|
400
|
-
vt = vt + meta.product + '.';
|
|
401
|
-
} else {
|
|
402
|
-
vt = vt + 'x.';
|
|
403
|
-
}
|
|
404
|
-
if (meta.hasOwnProperty('majorrel')) {
|
|
405
|
-
vt = vt + meta.majorrel + '.';
|
|
406
|
-
} else {
|
|
407
|
-
vt = vt + 'x.';
|
|
408
|
-
}
|
|
409
|
-
if (meta.hasOwnProperty('minorrel')) {
|
|
410
|
-
vt = vt + meta.minorrel + '.';
|
|
411
|
-
} else {
|
|
412
|
-
vt = vt + 'x.';
|
|
413
|
-
}
|
|
414
|
-
if (meta.hasOwnProperty('maintrel')) {
|
|
415
|
-
vt = vt + meta.maintrel + '.';
|
|
416
|
-
} else {
|
|
417
|
-
vt = vt + 'x.';
|
|
418
|
-
}
|
|
419
|
-
coordinatorinfo.version = vt;
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
} catch {
|
|
423
|
-
this.warn('exception raised in getCoordinatorInfo');
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
this.debug(`getCoordinatorInfo result: ${JSON.stringify(coordinatorinfo)}`);
|
|
427
|
-
this.adapter.sendTo(from, command, coordinatorinfo, callback);
|
|
428
|
-
});
|
|
429
|
-
} else {
|
|
430
|
-
this.adapter.sendTo(from, command, {error: 'You need save and run adapter before pairing!'}, callback);
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
renameDevice(from, command, msg, callback) {
|
|
436
|
-
if (this.stController) {
|
|
437
|
-
const id = msg.id;
|
|
438
|
-
const newName = msg.name;
|
|
439
|
-
this.stController.renameDevice(id, newName);
|
|
440
|
-
this.adapter.sendTo(from, command, {}, callback);
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
deleteDevice(from, command, msg, callback) {
|
|
445
|
-
if (this.zbController && this.stController) {
|
|
446
|
-
this.debug(`deleteDevice message: ${JSON.stringify(msg)}`);
|
|
447
|
-
const id = msg.id;
|
|
448
|
-
const force = msg.force;
|
|
449
|
-
const sysid = id.replace(this.adapter.namespace + '.', '0x');
|
|
450
|
-
const devId = id.replace(this.adapter.namespace + '.', '');
|
|
451
|
-
this.debug(`deleteDevice sysid: ${sysid}`);
|
|
452
|
-
const dev = this.zbController.getDevice(sysid);
|
|
453
|
-
if (!dev) {
|
|
454
|
-
this.debug('Not found!');
|
|
455
|
-
this.debug(`Try delete dev ${devId} from iobroker.`);
|
|
456
|
-
this.stController.deleteDeviceStates(devId, () =>
|
|
457
|
-
this.adapter.sendTo(from, command, {}, callback));
|
|
458
|
-
return;
|
|
459
|
-
}
|
|
460
|
-
this.zbController.remove(sysid, force, err => {
|
|
461
|
-
if (!err) {
|
|
462
|
-
this.stController.deleteDeviceStates(devId, () =>
|
|
463
|
-
this.adapter.sendTo(from, command, {}, callback));
|
|
464
|
-
} else {
|
|
465
|
-
this.debug(`Error on remove! ${err}`);
|
|
466
|
-
this.adapter.sendTo(from, command, {error: err}, callback);
|
|
467
|
-
}
|
|
468
|
-
});
|
|
469
|
-
} else {
|
|
470
|
-
this.adapter.sendTo(from, command, {error: 'You need to save and start the adapter!'}, callback);
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
async cleanDeviceStates(from, command, msg, callback) {
|
|
475
|
-
this.info(`State cleanup with ${JSON.stringify(msg)}`);
|
|
476
|
-
const devicesFromDB = await this.zbController.getClients(false);
|
|
477
|
-
for (const device of devicesFromDB) {
|
|
478
|
-
const entity = await this.zbController.resolveEntity(device);
|
|
479
|
-
if (entity) {
|
|
480
|
-
const model = (entity.mapped) ? entity.mapped.model : entity.device.modelID;
|
|
481
|
-
await this.stController.deleteOrphanedDeviceStates(device.ieeeAddr, model, msg.force);
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
this.adapter.sendTo(from, command, {}, callback);
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
async getChannels(from, command, message, callback) {
|
|
488
|
-
if (this.zbController) {
|
|
489
|
-
const result = await this.zbController.getChannelsEnergy();
|
|
490
|
-
this.debug(`getChannels result: ${JSON.stringify(result)}`);
|
|
491
|
-
this.adapter.sendTo(from, command, result, callback);
|
|
492
|
-
} else {
|
|
493
|
-
this.adapter.sendTo(
|
|
494
|
-
from, command,
|
|
495
|
-
{error: 'You need to setup serial port and start the adapter before pairing!'},
|
|
496
|
-
callback
|
|
497
|
-
);
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
async setDeviceActivated(from, command, msg, callback) {
|
|
502
|
-
if (this.stController) {
|
|
503
|
-
const id = msg.id;
|
|
504
|
-
const targetstate = msg.deactivated;
|
|
505
|
-
this.stController.setDeviceActivated(id, targetstate);
|
|
506
|
-
this.adapter.sendTo(from, command, {}, callback);
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
async reconfigure(from, command, msg, callback) {
|
|
511
|
-
if (this.zbController) {
|
|
512
|
-
const devid = getZbId(msg.id);
|
|
513
|
-
this.debug(`Reconfigure ${devid}`);
|
|
514
|
-
const entity = await this.zbController.resolveEntity(devid);
|
|
515
|
-
if (entity) {
|
|
516
|
-
try {
|
|
517
|
-
await this.zbController.callExtensionMethod(
|
|
518
|
-
'doConfigure',
|
|
519
|
-
[entity.device, entity.mapped],
|
|
520
|
-
);
|
|
521
|
-
this.adapter.sendTo(from, command, {}, callback);
|
|
522
|
-
} catch (error) {
|
|
523
|
-
const errmsg = `Reconfigure failed ${entity.device.ieeeAddr} ${entity.device.modelID}, (${error.stack})`;
|
|
524
|
-
this.error(errmsg);
|
|
525
|
-
this.adapter.sendTo(from, command, {error: errmsg}, callback);
|
|
526
|
-
}
|
|
527
|
-
} else {
|
|
528
|
-
this.adapter.sendTo(from, command, {error: 'No device'}, callback);
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
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', obj => this.onMessage(obj));
|
|
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
|
+
async letsPairing(from, command, message, callback) {
|
|
116
|
+
if (this.zbController) {
|
|
117
|
+
let devId = '';
|
|
118
|
+
if (message) {
|
|
119
|
+
if (message.id) devId = getZbId(message.id);
|
|
120
|
+
if (message.code) {
|
|
121
|
+
try {
|
|
122
|
+
this.debug(`letsPairing called with code ${message.code}`);
|
|
123
|
+
const success = await this.zbController.addPairingCode(message.code);
|
|
124
|
+
if (!success) {
|
|
125
|
+
this.adapter.sendTo(
|
|
126
|
+
from, command,
|
|
127
|
+
{error: 'Pairing code rejected by Coordinator!'},
|
|
128
|
+
callback
|
|
129
|
+
);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch (e) {
|
|
134
|
+
this.error(JSON.stringify(e));
|
|
135
|
+
this.adapter.sendTo(
|
|
136
|
+
from, command,
|
|
137
|
+
{error: 'Exception when trying to add QR code'},
|
|
138
|
+
callback
|
|
139
|
+
);
|
|
140
|
+
return;
|
|
141
|
+
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// allow devices to join the network within 60 secs
|
|
146
|
+
this.adapter.logToPairing('Pairing started ' + devId, true);
|
|
147
|
+
|
|
148
|
+
let cTimer = Number(this.adapter.config.countDown);
|
|
149
|
+
if (!this.adapter.config.countDown || !cTimer) {
|
|
150
|
+
cTimer = 60;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
this.zbController.permitJoin(cTimer, devId, err => {
|
|
154
|
+
if (!err) {
|
|
155
|
+
// set pairing mode on
|
|
156
|
+
this.adapter.setState('info.pairingMode', true);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
this.adapter.sendTo(from, command, 'Start pairing!', callback);
|
|
160
|
+
} else {
|
|
161
|
+
this.adapter.sendTo(
|
|
162
|
+
from, command,
|
|
163
|
+
{error: 'You need to setup serial port and start the adapter before pairing!'},
|
|
164
|
+
callback
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
touchlinkReset(from, command, message, callback) {
|
|
170
|
+
if (this.zbController) {
|
|
171
|
+
// allow devices to join the network within 60 secs
|
|
172
|
+
this.adapter.logToPairing('Touchlink reset started ', true);
|
|
173
|
+
|
|
174
|
+
let cTimer = Number(this.adapter.config.countDown);
|
|
175
|
+
if (!this.adapter.config.countDown || !cTimer) {
|
|
176
|
+
cTimer = 60;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
this.zbController.touchlinkReset(cTimer);
|
|
180
|
+
this.adapter.sendTo(from, command, 'Start touchlink reset and pairing!', callback);
|
|
181
|
+
} else {
|
|
182
|
+
this.adapter.sendTo(
|
|
183
|
+
from, command,
|
|
184
|
+
{error: 'You need to setup serial port and start the adapter before pairing!'},
|
|
185
|
+
callback
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async getDevices(from, command, id, callback) {
|
|
191
|
+
if (this.zbController) {
|
|
192
|
+
const pairedDevices = await this.zbController.getClients(true);
|
|
193
|
+
const groups = {};
|
|
194
|
+
let rooms;
|
|
195
|
+
this.adapter.getEnumsAsync('enum.rooms')
|
|
196
|
+
.then(enums => {
|
|
197
|
+
// rooms
|
|
198
|
+
rooms = enums['enum.rooms'];
|
|
199
|
+
})
|
|
200
|
+
// get all adapter devices
|
|
201
|
+
.then(() => this.adapter.getDevicesAsync())
|
|
202
|
+
.then(async result => {
|
|
203
|
+
const alls = id ? await this.adapter.getStatesAsync(id + '.*') : await this.adapter.getStatesAsync('*');
|
|
204
|
+
const allst = id ? await this.adapter.getStatesOfAsync(id) : await this.adapter.getStatesOfAsync();
|
|
205
|
+
result = result.filter(item => !id || id === item._id);
|
|
206
|
+
// get device states and groups
|
|
207
|
+
result.forEach(async devInfo => {
|
|
208
|
+
if (devInfo._id) {
|
|
209
|
+
// groups
|
|
210
|
+
// const grState = alls[`${devInfo._id}.groups`];
|
|
211
|
+
// if (grState && grState.val) {
|
|
212
|
+
// groups[devInfo._id] = JSON.parse(grState.val);
|
|
213
|
+
// }
|
|
214
|
+
// battery and link_quality
|
|
215
|
+
const lqState = alls[`${devInfo._id}.link_quality`];
|
|
216
|
+
devInfo.link_quality = lqState ? lqState.val : undefined;
|
|
217
|
+
devInfo.link_quality_lc = lqState ? lqState.lc : undefined;
|
|
218
|
+
const batState = alls[`${devInfo._id}.battery`];
|
|
219
|
+
devInfo.battery = batState ? batState.val : undefined;
|
|
220
|
+
// devInfo.states = states || {};
|
|
221
|
+
|
|
222
|
+
const states = allst.filter(item => item._id.startsWith(devInfo._id));
|
|
223
|
+
|
|
224
|
+
// put only allowed states
|
|
225
|
+
devInfo.statesDef = (states || []).filter(stateDef => {
|
|
226
|
+
const sid = stateDef._id;
|
|
227
|
+
const name = sid.split('.').pop();
|
|
228
|
+
return !disallowedDashStates.includes(name);
|
|
229
|
+
|
|
230
|
+
}).map(stateDef => {
|
|
231
|
+
const name = stateDef.common.name;
|
|
232
|
+
const devname = devInfo.common.name;
|
|
233
|
+
// replace state
|
|
234
|
+
return {
|
|
235
|
+
id: stateDef._id,
|
|
236
|
+
name: typeof name === 'string' ? name.replace(devname, '') : name,
|
|
237
|
+
type: stateDef.common.type,
|
|
238
|
+
read: stateDef.common.read,
|
|
239
|
+
write: stateDef.common.write,
|
|
240
|
+
val: alls[stateDef._id] ? alls[stateDef._id].val : undefined,
|
|
241
|
+
role: stateDef.common.role,
|
|
242
|
+
unit: stateDef.common.unit,
|
|
243
|
+
states: stateDef.common.states,
|
|
244
|
+
};
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
return result;
|
|
249
|
+
})
|
|
250
|
+
.then(async result => {
|
|
251
|
+
// combine info
|
|
252
|
+
const devices = [];
|
|
253
|
+
for (const devInfo of result) {
|
|
254
|
+
if (devInfo._id.indexOf('group') > 0) {
|
|
255
|
+
devInfo.icon = 'img/group.png';
|
|
256
|
+
devInfo.vendor = 'ioBroker';
|
|
257
|
+
// get group members and store them
|
|
258
|
+
const match = /zigbee.\d.group_([0-9]+)/.exec(devInfo._id);
|
|
259
|
+
if (match && match.length > 1) {
|
|
260
|
+
const groupmembers = await this.zbController.getGroupMembersFromController(match[1]);
|
|
261
|
+
this.debug(`group members: ${JSON.stringify(groupmembers)}`);
|
|
262
|
+
if (groupmembers && groupmembers.length > 0) {
|
|
263
|
+
const memberinfo = [];
|
|
264
|
+
for (const member of groupmembers) {
|
|
265
|
+
if (groups) {
|
|
266
|
+
const grouparray = groups[member.ieee];
|
|
267
|
+
if (grouparray) {
|
|
268
|
+
if (!grouparray.includes(match[1])) {
|
|
269
|
+
groups[member.ieee].push(match[1]);
|
|
270
|
+
}
|
|
271
|
+
} else {
|
|
272
|
+
groups[member.ieee] = [match[1]];
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
const device = await this.adapter.getObjectAsync(`${this.adapter.namespace}.${member.ieee.substr(2)}`);
|
|
276
|
+
if (device) {
|
|
277
|
+
member.device = device.common.name;
|
|
278
|
+
} else {
|
|
279
|
+
member.device = 'unknown';
|
|
280
|
+
}
|
|
281
|
+
memberinfo.push(member);
|
|
282
|
+
}
|
|
283
|
+
devInfo.memberinfo = memberinfo;
|
|
284
|
+
this.debug(`memberinfo for ${match[1]}: ${JSON.stringify(devInfo.memberinfo)}`);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
} else {
|
|
288
|
+
const modelDesc = statesMapping.findModel(devInfo.common.type);
|
|
289
|
+
devInfo.icon = (modelDesc && modelDesc.icon) ? modelDesc.icon : 'img/unknown.png';
|
|
290
|
+
devInfo.vendor = modelDesc ? modelDesc.vendor : '';
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const id = getZbId(devInfo._id);
|
|
294
|
+
devInfo.info = await this.zbController.resolveEntity(id);
|
|
295
|
+
|
|
296
|
+
devInfo.rooms = [];
|
|
297
|
+
for (const room in rooms) {
|
|
298
|
+
if (!rooms.hasOwnProperty(room) ||
|
|
299
|
+
!rooms[room] ||
|
|
300
|
+
!rooms[room].common ||
|
|
301
|
+
!rooms[room].common.members
|
|
302
|
+
) {
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
if (rooms[room].common.members.includes(devInfo._id)) {
|
|
306
|
+
devInfo.rooms.push(rooms[room].common.name);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
devInfo.paired = !!devInfo.info;
|
|
310
|
+
// devInfo.groups = groups[devInfo._id];
|
|
311
|
+
devices.push(devInfo);
|
|
312
|
+
}
|
|
313
|
+
return devices;
|
|
314
|
+
})
|
|
315
|
+
.then(async (devices) => {
|
|
316
|
+
// fill group info
|
|
317
|
+
for (const groupdev in groups) {
|
|
318
|
+
//this.debug(`GetDevices scanning group ${groupdev} ${JSON.stringify(groups[groupdev])}`);
|
|
319
|
+
const device = devices.find(dev => (groupdev === getZbId(dev._id)));
|
|
320
|
+
if (device) {
|
|
321
|
+
device.groups = groups[groupdev];
|
|
322
|
+
//this.debug(`adding group info to device ${groupdev}`);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
// append devices that paired but not created
|
|
326
|
+
if (!id) {
|
|
327
|
+
for (const d of pairedDevices) {
|
|
328
|
+
const device = await this.zbController.resolveEntity(d.ieeeAddr);
|
|
329
|
+
if (!device) {
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
const exists = devices.find((dev) => (dev._id && device.device.ieeeAddr === getZbId(dev._id)));
|
|
333
|
+
if (!exists) {
|
|
334
|
+
devices.push({
|
|
335
|
+
_id: device.device.ieeeAddr,
|
|
336
|
+
icon: 'img/unknown.png',
|
|
337
|
+
paired: true,
|
|
338
|
+
info: device,
|
|
339
|
+
common: {
|
|
340
|
+
name: undefined,
|
|
341
|
+
type: undefined,
|
|
342
|
+
},
|
|
343
|
+
native: {}
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return devices;
|
|
349
|
+
})
|
|
350
|
+
.then(devices => {
|
|
351
|
+
this.debug(`getDevices result: ${JSON.stringify(devices)}`);
|
|
352
|
+
this.adapter.sendTo(from, command, devices, callback);
|
|
353
|
+
})
|
|
354
|
+
.catch(err => this.error(`getDevices error: ${err.stack}`));
|
|
355
|
+
} else {
|
|
356
|
+
this.adapter.sendTo(from, command, {error: 'You need save and run adapter before pairing!'}, callback);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
async getCoordinatorInfo(from, command, callback) {
|
|
362
|
+
if (this.zbController) {
|
|
363
|
+
const coordinatorinfo = {
|
|
364
|
+
installSource: 'IADefault_1',
|
|
365
|
+
channel: '-1',
|
|
366
|
+
port: 'Default_1',
|
|
367
|
+
installedVersion: 'Default_1',
|
|
368
|
+
type: 'Default_1',
|
|
369
|
+
revision: 'Default_1',
|
|
370
|
+
version: '9-9.9.9.9'
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
const coordinatorVersion = await this.adapter.zbController.herdsman.getCoordinatorVersion();
|
|
374
|
+
|
|
375
|
+
await this.adapter.getForeignObject(`system.adapter.${this.adapter.namespace}`, (err, obj) => {
|
|
376
|
+
if (!err && obj) {
|
|
377
|
+
if (obj.common.installedFrom && obj.common.installedFrom.includes('://')) {
|
|
378
|
+
const instFrom = obj.common.installedFrom;
|
|
379
|
+
coordinatorinfo.installSource = instFrom.replace('tarball', 'commit');
|
|
380
|
+
} else {
|
|
381
|
+
coordinatorinfo.installSource = obj.common.installedFrom;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
try {
|
|
385
|
+
coordinatorinfo.port = obj.native.port;
|
|
386
|
+
coordinatorinfo.channel = obj.native.channel;
|
|
387
|
+
coordinatorinfo.installedVersion = obj.native.version;
|
|
388
|
+
if (coordinatorVersion && coordinatorVersion.type && coordinatorVersion.meta) {
|
|
389
|
+
coordinatorinfo.type = coordinatorVersion.type;
|
|
390
|
+
const meta = coordinatorVersion.meta;
|
|
391
|
+
if (meta) {
|
|
392
|
+
if (meta.hasOwnProperty('revision')) {
|
|
393
|
+
coordinatorinfo.revision = meta.revision;
|
|
394
|
+
}
|
|
395
|
+
let vt = 'x-';
|
|
396
|
+
if (meta.hasOwnProperty('transportrev')) {
|
|
397
|
+
vt = meta.transportrev + '-';
|
|
398
|
+
}
|
|
399
|
+
if (meta.hasOwnProperty('product')) {
|
|
400
|
+
vt = vt + meta.product + '.';
|
|
401
|
+
} else {
|
|
402
|
+
vt = vt + 'x.';
|
|
403
|
+
}
|
|
404
|
+
if (meta.hasOwnProperty('majorrel')) {
|
|
405
|
+
vt = vt + meta.majorrel + '.';
|
|
406
|
+
} else {
|
|
407
|
+
vt = vt + 'x.';
|
|
408
|
+
}
|
|
409
|
+
if (meta.hasOwnProperty('minorrel')) {
|
|
410
|
+
vt = vt + meta.minorrel + '.';
|
|
411
|
+
} else {
|
|
412
|
+
vt = vt + 'x.';
|
|
413
|
+
}
|
|
414
|
+
if (meta.hasOwnProperty('maintrel')) {
|
|
415
|
+
vt = vt + meta.maintrel + '.';
|
|
416
|
+
} else {
|
|
417
|
+
vt = vt + 'x.';
|
|
418
|
+
}
|
|
419
|
+
coordinatorinfo.version = vt;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
} catch {
|
|
423
|
+
this.warn('exception raised in getCoordinatorInfo');
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
this.debug(`getCoordinatorInfo result: ${JSON.stringify(coordinatorinfo)}`);
|
|
427
|
+
this.adapter.sendTo(from, command, coordinatorinfo, callback);
|
|
428
|
+
});
|
|
429
|
+
} else {
|
|
430
|
+
this.adapter.sendTo(from, command, {error: 'You need save and run adapter before pairing!'}, callback);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
renameDevice(from, command, msg, callback) {
|
|
436
|
+
if (this.stController) {
|
|
437
|
+
const id = msg.id;
|
|
438
|
+
const newName = msg.name;
|
|
439
|
+
this.stController.renameDevice(id, newName);
|
|
440
|
+
this.adapter.sendTo(from, command, {}, callback);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
deleteDevice(from, command, msg, callback) {
|
|
445
|
+
if (this.zbController && this.stController) {
|
|
446
|
+
this.debug(`deleteDevice message: ${JSON.stringify(msg)}`);
|
|
447
|
+
const id = msg.id;
|
|
448
|
+
const force = msg.force;
|
|
449
|
+
const sysid = id.replace(this.adapter.namespace + '.', '0x');
|
|
450
|
+
const devId = id.replace(this.adapter.namespace + '.', '');
|
|
451
|
+
this.debug(`deleteDevice sysid: ${sysid}`);
|
|
452
|
+
const dev = this.zbController.getDevice(sysid);
|
|
453
|
+
if (!dev) {
|
|
454
|
+
this.debug('Not found!');
|
|
455
|
+
this.debug(`Try delete dev ${devId} from iobroker.`);
|
|
456
|
+
this.stController.deleteDeviceStates(devId, () =>
|
|
457
|
+
this.adapter.sendTo(from, command, {}, callback));
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
this.zbController.remove(sysid, force, err => {
|
|
461
|
+
if (!err) {
|
|
462
|
+
this.stController.deleteDeviceStates(devId, () =>
|
|
463
|
+
this.adapter.sendTo(from, command, {}, callback));
|
|
464
|
+
} else {
|
|
465
|
+
this.debug(`Error on remove! ${err}`);
|
|
466
|
+
this.adapter.sendTo(from, command, {error: err}, callback);
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
} else {
|
|
470
|
+
this.adapter.sendTo(from, command, {error: 'You need to save and start the adapter!'}, callback);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
async cleanDeviceStates(from, command, msg, callback) {
|
|
475
|
+
this.info(`State cleanup with ${JSON.stringify(msg)}`);
|
|
476
|
+
const devicesFromDB = await this.zbController.getClients(false);
|
|
477
|
+
for (const device of devicesFromDB) {
|
|
478
|
+
const entity = await this.zbController.resolveEntity(device);
|
|
479
|
+
if (entity) {
|
|
480
|
+
const model = (entity.mapped) ? entity.mapped.model : entity.device.modelID;
|
|
481
|
+
await this.stController.deleteOrphanedDeviceStates(device.ieeeAddr, model, msg.force);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
this.adapter.sendTo(from, command, {}, callback);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
async getChannels(from, command, message, callback) {
|
|
488
|
+
if (this.zbController) {
|
|
489
|
+
const result = await this.zbController.getChannelsEnergy();
|
|
490
|
+
this.debug(`getChannels result: ${JSON.stringify(result)}`);
|
|
491
|
+
this.adapter.sendTo(from, command, result, callback);
|
|
492
|
+
} else {
|
|
493
|
+
this.adapter.sendTo(
|
|
494
|
+
from, command,
|
|
495
|
+
{error: 'You need to setup serial port and start the adapter before pairing!'},
|
|
496
|
+
callback
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
async setDeviceActivated(from, command, msg, callback) {
|
|
502
|
+
if (this.stController) {
|
|
503
|
+
const id = msg.id;
|
|
504
|
+
const targetstate = msg.deactivated;
|
|
505
|
+
this.stController.setDeviceActivated(id, targetstate);
|
|
506
|
+
this.adapter.sendTo(from, command, {}, callback);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
async reconfigure(from, command, msg, callback) {
|
|
511
|
+
if (this.zbController) {
|
|
512
|
+
const devid = getZbId(msg.id);
|
|
513
|
+
this.debug(`Reconfigure ${devid}`);
|
|
514
|
+
const entity = await this.zbController.resolveEntity(devid);
|
|
515
|
+
if (entity) {
|
|
516
|
+
try {
|
|
517
|
+
await this.zbController.callExtensionMethod(
|
|
518
|
+
'doConfigure',
|
|
519
|
+
[entity.device, entity.mapped],
|
|
520
|
+
);
|
|
521
|
+
this.adapter.sendTo(from, command, {}, callback);
|
|
522
|
+
} catch (error) {
|
|
523
|
+
const errmsg = `Reconfigure failed ${entity.device.ieeeAddr} ${entity.device.modelID}, (${error.stack})`;
|
|
524
|
+
this.error(errmsg);
|
|
525
|
+
this.adapter.sendTo(from, command, {error: errmsg}, callback);
|
|
526
|
+
}
|
|
527
|
+
} else {
|
|
528
|
+
this.adapter.sendTo(from, command, {error: 'No device'}, callback);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
module.exports = Commands;
|