iobroker.zigbee 2.0.4 → 3.0.0
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/README.md +90 -57
- package/admin/admin.js +497 -120
- package/admin/img/philips_hue_lom001.png +0 -0
- package/admin/index_m.html +168 -124
- package/admin/tab_m.html +20 -11
- package/docs/de/img/Bild30.png +0 -0
- package/docs/de/img/Bild38.png +0 -0
- package/docs/de/img/Info.png +0 -0
- package/docs/de/img/Zigbee_config_de.jpg +0 -0
- package/docs/de/img/battery.png +0 -0
- package/docs/de/img/debug.png +0 -0
- package/docs/de/img/delete.png +0 -0
- package/docs/de/img/disconnected.png +0 -0
- package/docs/de/img/edit_grp.png +0 -0
- package/docs/de/img/edit_image.png +0 -0
- package/docs/de/img/grp_nok.png +0 -0
- package/docs/de/img/grp_ok.png +0 -0
- package/docs/de/img/on_off.png +0 -0
- package/docs/de/img/reconfigure.png +0 -0
- package/docs/de/readme.md +52 -43
- package/docs/en/img/Zigbee_config_en.png +0 -0
- package/docs/en/img/Zigbee_pairing_en.png +0 -0
- package/docs/en/readme.md +71 -66
- 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 +31 -65
- package/lib/DeviceDebug.js +5 -2
- package/lib/commands.js +182 -31
- package/lib/developer.js +0 -0
- package/lib/devices.js +2 -2
- package/lib/exposes.js +10 -27
- package/lib/groups.js +6 -8
- package/lib/localConfig.js +4 -5
- package/lib/ota.js +6 -6
- package/lib/seriallist.js +9 -2
- package/lib/statescontroller.js +397 -128
- package/lib/utils.js +41 -11
- package/lib/zbDeviceAvailability.js +2 -2
- package/lib/zbDeviceConfigure.js +99 -58
- package/lib/zigbeecontroller.js +152 -128
- package/main.js +251 -264
- package/package.json +10 -10
- package/docs/en/img/Bild23.png +0 -0
- package/docs/en/img/Bild25.png +0 -0
- package/docs/en/img/Bild26.png +0 -0
- package/docs/en/img/Bild4.png +0 -0
- package/docs/en/img/Bild9.png +0 -0
package/lib/DeviceDebug.js
CHANGED
|
@@ -6,6 +6,7 @@ class DeviceDebug extends EventEmitter {
|
|
|
6
6
|
this.adapter = adapter;
|
|
7
7
|
this.dataByID = { };
|
|
8
8
|
this.dataByDevice = { };
|
|
9
|
+
this.logStatus = true;
|
|
9
10
|
|
|
10
11
|
}
|
|
11
12
|
|
|
@@ -65,14 +66,16 @@ class DeviceDebug extends EventEmitter {
|
|
|
65
66
|
target.push(item);
|
|
66
67
|
this.dataByDevice[item.deviceID] = DevData;
|
|
67
68
|
}
|
|
68
|
-
if (message.hasOwnProperty('message')) {
|
|
69
|
+
if (message.hasOwnProperty('message') && this.logStatus) {
|
|
69
70
|
this.warn(`ELEVATED:${flag} (${dataId.toString(16).slice(-4)}) ${message.message}`)
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
74
|
}
|
|
74
75
|
|
|
75
|
-
collectDebugData() {
|
|
76
|
+
collectDebugData(logStatus) {
|
|
77
|
+
if (logStatus != undefined)
|
|
78
|
+
this.logStatus = logStatus;
|
|
76
79
|
return this.dataByDevice;
|
|
77
80
|
}
|
|
78
81
|
}
|
package/lib/commands.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const getZbId = require('./utils').getZbId;
|
|
4
|
+
const getNetAddress = require('./utils').getNetAddress;
|
|
5
|
+
const reverseByteString = require('./utils').reverseByteString;
|
|
4
6
|
const fs = require('fs');
|
|
5
|
-
const pathLib = require('path');
|
|
6
7
|
const statesMapping = require('./devices');
|
|
7
8
|
const utils = require('@iobroker/adapter-core'); // Get common adapter utils
|
|
8
9
|
const colors = require('./colors.js');
|
|
9
|
-
const
|
|
10
|
+
const dns = require('dns');
|
|
11
|
+
const net = require('net');
|
|
12
|
+
const { access, constants } =require('fs');
|
|
10
13
|
|
|
11
14
|
const disallowedDashStates = [
|
|
12
15
|
'link_quality', 'available', 'battery', 'groups', 'device_query',
|
|
@@ -50,13 +53,14 @@ class Commands {
|
|
|
50
53
|
* @param {ioBroker.Message} obj
|
|
51
54
|
*/
|
|
52
55
|
onMessage(obj) {
|
|
53
|
-
if (obj) {
|
|
54
|
-
if (
|
|
56
|
+
if (typeof obj === 'object' && obj.command) {
|
|
57
|
+
if (obj) {
|
|
55
58
|
switch (obj.command) {
|
|
56
|
-
case '
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
case 'testConnect':
|
|
60
|
+
this.adapter.testConnect(obj.from, obj.command, obj.message, obj.callback);
|
|
61
|
+
break;
|
|
62
|
+
case 'deleteNVBackup':
|
|
63
|
+
this.delNvBackup(obj.from, obj.command, {}, obj.callback);
|
|
60
64
|
break;
|
|
61
65
|
case 'letsPairing':
|
|
62
66
|
if (obj.message && typeof obj.message === 'object') {
|
|
@@ -89,7 +93,7 @@ class Commands {
|
|
|
89
93
|
}
|
|
90
94
|
break;
|
|
91
95
|
case 'getCoordinatorInfo':
|
|
92
|
-
if (obj
|
|
96
|
+
if (obj.message && typeof obj.message === 'object') {
|
|
93
97
|
this.getCoordinatorInfo(obj.from, obj.command, obj.callback);
|
|
94
98
|
}
|
|
95
99
|
break;
|
|
@@ -151,21 +155,81 @@ class Commands {
|
|
|
151
155
|
}
|
|
152
156
|
break;
|
|
153
157
|
case 'getDeviceCleanupRequired':
|
|
154
|
-
if (obj)
|
|
155
|
-
|
|
156
|
-
|
|
158
|
+
if (this.stController) this.adapter.sendTo(obj.from, obj.command, {clean:this.stController.CleanupRequired(), errors:this.stController.getStashedErrors()}, obj.callback);
|
|
159
|
+
// NO Break - returning the debug-data as well is intentional
|
|
160
|
+
case 'getDebugMessages':
|
|
161
|
+
this.adapter.sendTo(obj.from, obj.command, {byId:this.adapter.deviceDebug.collectDebugData( obj.message.inlog)},obj.callback);
|
|
162
|
+
break;
|
|
163
|
+
case 'testConnection':
|
|
164
|
+
this.testConnection(obj.from, obj.command, obj.message, obj.callback);
|
|
165
|
+
break;
|
|
166
|
+
case 'readNVRam':
|
|
167
|
+
this.readNvBackup(obj.from, obj.command, obj.message, obj.callback);
|
|
168
|
+
default:
|
|
169
|
+
//this.warn(`Commands: Command ${obj.command} is unknown`);
|
|
170
|
+
//this.adapter.sendTo(obj.from, obj.command, obj.message, obj.callback);
|
|
171
|
+
break;
|
|
157
172
|
}
|
|
158
173
|
}
|
|
159
174
|
}
|
|
160
175
|
}
|
|
161
176
|
|
|
162
|
-
|
|
177
|
+
async readNvBackup(from, command, msg, callback) {
|
|
178
|
+
this.warn('readNvBackup called')
|
|
179
|
+
try {
|
|
180
|
+
const zo = this.adapter.getZigbeeOptions();
|
|
181
|
+
const name = require('path').join(zo.dbDir, zo.backupPath);
|
|
182
|
+
const nvbackup = fs.readFileSync(name, {encoding: 'utf8'}).toString();
|
|
183
|
+
const nvBackupJson = JSON.parse(nvbackup);
|
|
184
|
+
const rv = {};
|
|
185
|
+
rv.channel = nvBackupJson.channel;
|
|
186
|
+
rv.precfgkey = (nvBackupJson.network_key ? nvBackupJson.network_key.key : undefined);
|
|
187
|
+
rv.extPanID = nvBackupJson.extended_pan_id ? reverseByteString(nvBackupJson.extended_pan_id) : undefined;
|
|
188
|
+
rv.panID = parseInt('0x'+nvBackupJson.pan_id);
|
|
189
|
+
this.warn('readNvBackup returns ' + JSON.stringify(rv))
|
|
190
|
+
this.adapter.sendTo(from, command, rv, callback)
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
const msg = `Unable to read nvBackup ${error && error.message ? error.message : 'no message given'}`;
|
|
194
|
+
this.error(msg);
|
|
195
|
+
this.adapter.sendTo(from, command, {error:msg}, callback)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async delNvBackup(from, command, msg, callback) {
|
|
163
200
|
try {
|
|
164
|
-
if (this.zbController)
|
|
201
|
+
if (this.zbController)
|
|
202
|
+
{
|
|
203
|
+
// stop the herdsman if needed
|
|
204
|
+
const wasRunning = this.zbController.herdsmanStarted;
|
|
205
|
+
if (wasRunning) await this.zbController.stop();
|
|
165
206
|
const name = this.zbController.herdsman.adapter.backupPath;
|
|
166
|
-
|
|
207
|
+
fs.unlink(name, async (err) => {
|
|
208
|
+
const rv={};
|
|
209
|
+
if (err) {
|
|
210
|
+
this.error(`Unable to remove ${name}: ${err}`);
|
|
211
|
+
rv.error = `Unable to remove ${name}: ${err}`;
|
|
212
|
+
}
|
|
213
|
+
// start the herdsman again if it was stopped before
|
|
214
|
+
if (wasRunning) await this.zbController.start();
|
|
215
|
+
this.adapter.sendTo(from, command, rv, callback)
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
const zo = this.adapter.getZigbeeOptions();
|
|
220
|
+
const name = require('path').join(zo.dbDir, zo.backupPath);
|
|
221
|
+
fs.unlink(name, async (err) => {
|
|
222
|
+
const rv={};
|
|
223
|
+
if (err) {
|
|
224
|
+
this.error(`Unable to remove ${name}: ${err}`);
|
|
225
|
+
rv.error = `Unable to remove ${name}: ${err}`;
|
|
226
|
+
}
|
|
227
|
+
// start the herdsman again if it was stopped before
|
|
228
|
+
this.adapter.sendTo(from, command, rv, callback)
|
|
229
|
+
});
|
|
167
230
|
}
|
|
168
231
|
} catch (error) {
|
|
232
|
+
this.adapter.sendTo(from, command, {error: error.message}, callback)
|
|
169
233
|
this.error(error);
|
|
170
234
|
}
|
|
171
235
|
}
|
|
@@ -227,7 +291,7 @@ class Commands {
|
|
|
227
291
|
}
|
|
228
292
|
|
|
229
293
|
touchlinkReset(from, command, message, callback) {
|
|
230
|
-
if (this.zbController) {
|
|
294
|
+
if (this.zbController && this.zbController.herdsmanStarted) {
|
|
231
295
|
// allow devices to join the network within 60 secs
|
|
232
296
|
this.adapter.logToPairing('Touchlink reset started ', true);
|
|
233
297
|
|
|
@@ -241,14 +305,14 @@ class Commands {
|
|
|
241
305
|
} else {
|
|
242
306
|
this.adapter.sendTo(
|
|
243
307
|
from, command,
|
|
244
|
-
{error: '
|
|
308
|
+
{error: 'No active connection to Zigbee Hardware!'},
|
|
245
309
|
callback
|
|
246
310
|
);
|
|
247
311
|
}
|
|
248
312
|
}
|
|
249
313
|
|
|
250
314
|
async getDevices(from, command, id, callback) {
|
|
251
|
-
if (this.zbController) {
|
|
315
|
+
if (this.zbController && this.zbController.herdsmanStarted) {
|
|
252
316
|
this.debug(`getDevices called from ${from} with command ${JSON.stringify(command)} and id ${JSON.stringify(id)}`);
|
|
253
317
|
const pairedDevices = await this.zbController.getClients(true);
|
|
254
318
|
const groups = {};
|
|
@@ -352,6 +416,18 @@ class Commands {
|
|
|
352
416
|
|
|
353
417
|
const id = getZbId(devInfo._id);
|
|
354
418
|
devInfo.info = await this.zbController.resolveEntity(id);
|
|
419
|
+
// check configuration
|
|
420
|
+
try {
|
|
421
|
+
if (devInfo.info) {
|
|
422
|
+
const result = await this.zbController.callExtensionMethod(
|
|
423
|
+
'shouldConfigure',
|
|
424
|
+
[devInfo.info.device, devInfo.info.mapped],
|
|
425
|
+
);
|
|
426
|
+
if (result.length > 0) devInfo.isConfigured = !result[0];
|
|
427
|
+
}
|
|
428
|
+
} catch (error) {
|
|
429
|
+
this.warn('error calling shouldConfigure: ' + error && error.message ? error.message : 'no error message');
|
|
430
|
+
}
|
|
355
431
|
|
|
356
432
|
devInfo.rooms = [];
|
|
357
433
|
for (const room in rooms) {
|
|
@@ -405,18 +481,27 @@ class Commands {
|
|
|
405
481
|
return devices;
|
|
406
482
|
})
|
|
407
483
|
.then(devices => {
|
|
408
|
-
this.debug(`getDevices
|
|
409
|
-
this.adapter.
|
|
484
|
+
this.debug(`getDevices contains ${devices.length} Devices`);
|
|
485
|
+
const rv = { devices:devices, inLog:this.adapter.deviceDebug.logStatus, byId:this.adapter.deviceDebug.collectDebugData() }
|
|
486
|
+
if (this.stController) {
|
|
487
|
+
rv.clean = this.stController.CleanupRequired();
|
|
488
|
+
rv.errors = this.stController.getStashedErrors();
|
|
489
|
+
rv.debugDevices = this.stController.debugDevices;
|
|
490
|
+
}
|
|
491
|
+
this.adapter.sendTo(from, command, rv, callback);
|
|
410
492
|
})
|
|
411
|
-
.catch(err =>
|
|
493
|
+
.catch(err => {
|
|
494
|
+
this.error(`getDevices error: ${err.stack}`);
|
|
495
|
+
this.adapter.sendTo(from, command, {error: `Error enumerating devices : ${err && err.message ? err.message : ''}`}, callback);
|
|
496
|
+
});
|
|
412
497
|
} else {
|
|
413
|
-
this.adapter.sendTo(from, command, {error: '
|
|
498
|
+
this.adapter.sendTo(from, command, {error: 'No active connection to Zigbee Hardware!'}, callback);
|
|
414
499
|
}
|
|
415
500
|
}
|
|
416
501
|
|
|
417
502
|
|
|
418
503
|
async getCoordinatorInfo(from, command, callback) {
|
|
419
|
-
if (this.zbController) {
|
|
504
|
+
if (this.zbController && this.zbController.herdsmanStarted) {
|
|
420
505
|
const coordinatorinfo = {
|
|
421
506
|
installSource: 'IADefault_1',
|
|
422
507
|
channel: '-1',
|
|
@@ -484,7 +569,7 @@ class Commands {
|
|
|
484
569
|
this.adapter.sendTo(from, command, coordinatorinfo, callback);
|
|
485
570
|
});
|
|
486
571
|
} else {
|
|
487
|
-
this.adapter.sendTo(from, command, {error: '
|
|
572
|
+
this.adapter.sendTo(from, command, {error: 'No active connection to Zigbee Hardware!'}, callback);
|
|
488
573
|
}
|
|
489
574
|
}
|
|
490
575
|
|
|
@@ -499,7 +584,7 @@ class Commands {
|
|
|
499
584
|
}
|
|
500
585
|
|
|
501
586
|
deleteDevice(from, command, msg, callback) {
|
|
502
|
-
if (this.zbController && this.stController) {
|
|
587
|
+
if (this.zbController && this.zbController.herdsmanStarted && this.stController) {
|
|
503
588
|
this.debug(`deleteDevice message: ${JSON.stringify(msg)}`);
|
|
504
589
|
const id = msg.id;
|
|
505
590
|
const force = msg.force;
|
|
@@ -524,7 +609,7 @@ class Commands {
|
|
|
524
609
|
}
|
|
525
610
|
});
|
|
526
611
|
} else {
|
|
527
|
-
this.adapter.sendTo(from, command, {error: '
|
|
612
|
+
this.adapter.sendTo(from, command, {error: 'No active connection to Zigbee Hardware!'}, callback);
|
|
528
613
|
}
|
|
529
614
|
}
|
|
530
615
|
|
|
@@ -548,14 +633,14 @@ class Commands {
|
|
|
548
633
|
}
|
|
549
634
|
|
|
550
635
|
async getChannels(from, command, message, callback) {
|
|
551
|
-
if (this.zbController) {
|
|
636
|
+
if (this.zbController && this.zbController.herdsmanStarted) {
|
|
552
637
|
const result = await this.zbController.getChannelsEnergy();
|
|
553
638
|
this.debug(`getChannels result: ${JSON.stringify(result)}`);
|
|
554
639
|
this.adapter.sendTo(from, command, result, callback);
|
|
555
640
|
} else {
|
|
556
641
|
this.adapter.sendTo(
|
|
557
642
|
from, command,
|
|
558
|
-
{error: '
|
|
643
|
+
{error: 'No active connection to Zigbee Hardware!'},
|
|
559
644
|
callback
|
|
560
645
|
);
|
|
561
646
|
}
|
|
@@ -588,7 +673,7 @@ class Commands {
|
|
|
588
673
|
async getLocalImages(from, command, msg, callback) {
|
|
589
674
|
if (this.stController) {
|
|
590
675
|
const id = msg.id;
|
|
591
|
-
const result = await this.stController.localConfig.enumerateImages(
|
|
676
|
+
const result = await this.stController.localConfig.enumerateImages(this.adapter.getDataFolder());
|
|
592
677
|
this.adapter.sendTo(from, command, {imageData:result}, callback)
|
|
593
678
|
}
|
|
594
679
|
}
|
|
@@ -623,6 +708,10 @@ class Commands {
|
|
|
623
708
|
const target = msg.target.replace(`${this.adapter.namespace}.`, '');
|
|
624
709
|
const entity = await this.zbController.resolveEntity(target);
|
|
625
710
|
//this.warn('entity for ' + target + ' is '+ JSON.stringify(entity))
|
|
711
|
+
if (entity && !entity.mapped) {
|
|
712
|
+
this.warn('unable to set local Override for device whithout mapped model');
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
626
715
|
if (msg.data)
|
|
627
716
|
{
|
|
628
717
|
for (const prop in msg.data) {
|
|
@@ -643,7 +732,7 @@ class Commands {
|
|
|
643
732
|
|
|
644
733
|
|
|
645
734
|
async reconfigure(from, command, msg, callback) {
|
|
646
|
-
if (this.zbController) {
|
|
735
|
+
if (this.zbController && this.zbController.herdsmanStarted) {
|
|
647
736
|
const devid = getZbId(msg.id);
|
|
648
737
|
this.debug(`Reconfigure ${devid}`);
|
|
649
738
|
const entity = await this.zbController.resolveEntity(devid);
|
|
@@ -653,7 +742,6 @@ class Commands {
|
|
|
653
742
|
'doConfigure',
|
|
654
743
|
[entity.device, entity.mapped],
|
|
655
744
|
);
|
|
656
|
-
this.warn('do configure result is ' + JSON.stringify(result));
|
|
657
745
|
const msg = result.join(',');
|
|
658
746
|
if (msg.length > 5)
|
|
659
747
|
this.adapter.sendTo(from, command, {error: msg}, callback);
|
|
@@ -669,6 +757,69 @@ class Commands {
|
|
|
669
757
|
this.adapter.sendTo(from, command, {error: 'No device'}, callback);
|
|
670
758
|
}
|
|
671
759
|
}
|
|
760
|
+
else {
|
|
761
|
+
this.adapter.sendTo(from, command, {error: 'No active connection to Zigbee Hardware!'}, callback);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
async testConnection(from, command, msg, callback) {
|
|
766
|
+
this.debug(`TestConnection with ${JSON.stringify(msg)}`);
|
|
767
|
+
if (msg && msg.address) {
|
|
768
|
+
const netAddress = getNetAddress(msg.address);
|
|
769
|
+
if (netAddress && netAddress.host) {
|
|
770
|
+
this.adapter.logToPairing(`attempting dns lookup for ${netAddress.host}`);
|
|
771
|
+
this.debug(`attempting dns lookup for ${netAddress.host}`);
|
|
772
|
+
dns.lookup(netAddress.host, (err, ip, _) => {
|
|
773
|
+
if (err) {
|
|
774
|
+
const msg = `Unable to resolve name: ${err && err.message ? err.message : 'no message'}`;
|
|
775
|
+
this.error(msg);
|
|
776
|
+
this.adapter.logToPairing(`Error: ${msg}`);
|
|
777
|
+
this.adapter.sendTo(from, command, {error:msg}, callback);
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
this.debug(`dns lookup for ${msg.address} produced ${ip}`);
|
|
781
|
+
this.adapter.logToPairing(`dns lookup for ${msg.address} produced ${ip}`);
|
|
782
|
+
const client = new net.Socket();
|
|
783
|
+
this.debug(`attempting to connect to ${ip} port ${netAddress.port ? netAddress.port : 80}`);
|
|
784
|
+
client.connect(netAddress.port, ip, () => {
|
|
785
|
+
client.destroy()
|
|
786
|
+
this.adapter.logToPairing(`connected successfully to connect to ${ip} port ${netAddress.port ? netAddress.port : 80}`);
|
|
787
|
+
this.debug(`connected successfully to connect to ${ip} port ${netAddress.port ? netAddress.port : 80}`);
|
|
788
|
+
this.adapter.sendTo(from, command, {}, callback)
|
|
789
|
+
})
|
|
790
|
+
client.on('error', (error) => {
|
|
791
|
+
const msg = `unable to connect to ${ip} port ${netAddress.port ? netAddress.port : 80} : ${error && error.message ? error.message : 'no message given'}`
|
|
792
|
+
this.error(msg);
|
|
793
|
+
this.adapter.logToPairing(`Error: ${msg}`);
|
|
794
|
+
this.adapter.sendTo(from, command, {error:msg}, callback);
|
|
795
|
+
});
|
|
796
|
+
})
|
|
797
|
+
}
|
|
798
|
+
else
|
|
799
|
+
{
|
|
800
|
+
try {
|
|
801
|
+
const port = msg.address.trim();
|
|
802
|
+
this.adapter.logToPairing(`reading access rights for ${port}`);
|
|
803
|
+
access(port, constants.R_OK | constants.W_OK, (error) => {
|
|
804
|
+
if (error) {
|
|
805
|
+
const msg = `unable to access ${port} : ${error && error.message ? error.message : 'no message given'}`;
|
|
806
|
+
this.error(msg);
|
|
807
|
+
this.adapter.logToPairing(`Error: ${msg}`);
|
|
808
|
+
this.adapter.sendTo(from, command, {error:msg}, callback);
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
811
|
+
this.adapter.logToPairing(`read and write access available for ${port}`);
|
|
812
|
+
this.adapter.sendTo(from, command, {}, callback);
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
catch (error) {
|
|
816
|
+
const msg = `File access error: ${error && error.message ? error.message : 'no message given'}`;
|
|
817
|
+
this.error(msg);
|
|
818
|
+
this.adapter.logToPairing(`Error: ${msg}`);
|
|
819
|
+
this.adapter.sendTo(from, command, {error:msg}, callback);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
672
823
|
}
|
|
673
824
|
}
|
|
674
825
|
|
package/lib/developer.js
CHANGED
|
File without changes
|
package/lib/devices.js
CHANGED
|
@@ -3104,9 +3104,9 @@ function fillStatesWithExposes(logger) {
|
|
|
3104
3104
|
async function addExposeToDevices(device, logger, model) {
|
|
3105
3105
|
const s = DevicesByModel.size;
|
|
3106
3106
|
if (s < 1) getByModel();
|
|
3107
|
-
await applyExposeForDevice(devices, DevicesByModel, device, logger, model);
|
|
3107
|
+
const rv = await applyExposeForDevice(devices, DevicesByModel, device, logger, model);
|
|
3108
3108
|
removeEmptyStates(devices);
|
|
3109
|
-
return
|
|
3109
|
+
return rv;
|
|
3110
3110
|
}
|
|
3111
3111
|
|
|
3112
3112
|
// remove empty states
|
package/lib/exposes.js
CHANGED
|
@@ -251,8 +251,6 @@ function createFromExposes(model, def, device, log) {
|
|
|
251
251
|
}
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
-
const icon = utils.getDeviceIcon(def);
|
|
255
|
-
|
|
256
254
|
if (typeof def.exposes == 'object') {
|
|
257
255
|
for (const expose of def.exposes) {
|
|
258
256
|
genStateFromExpose(expose);
|
|
@@ -266,11 +264,12 @@ function createFromExposes(model, def, device, log) {
|
|
|
266
264
|
genStateFromExpose(expose);
|
|
267
265
|
}
|
|
268
266
|
}
|
|
267
|
+
const icon = utils.getDeviceIcon(def);
|
|
269
268
|
|
|
270
269
|
const newDev = {
|
|
271
270
|
models: [model],
|
|
272
|
-
icon,
|
|
273
271
|
states,
|
|
272
|
+
icon,
|
|
274
273
|
exposed: true,
|
|
275
274
|
};
|
|
276
275
|
|
|
@@ -594,7 +593,7 @@ function createFromExposes(model, def, device, log) {
|
|
|
594
593
|
break;
|
|
595
594
|
|
|
596
595
|
case 'voltage':
|
|
597
|
-
state =
|
|
596
|
+
state = genState(expose);
|
|
598
597
|
break;
|
|
599
598
|
|
|
600
599
|
case 'temperature':
|
|
@@ -867,29 +866,12 @@ function createFromExposes(model, def, device, log) {
|
|
|
867
866
|
|
|
868
867
|
}
|
|
869
868
|
|
|
870
|
-
function applyExposes(mappedDevices, byModel) {
|
|
871
|
-
// create or device from exposes
|
|
872
|
-
for (const deviceDef of zigbeeHerdsmanConverters.definitions) {
|
|
873
|
-
applyDeviceDef(mappedDevices, byModel, deviceDef);
|
|
874
|
-
|
|
875
|
-
if (deviceDef.hasOwnProperty('whiteLabel')) {
|
|
876
|
-
for (const deviceWhiteLabel of deviceDef.whiteLabel) {
|
|
877
|
-
applyDeviceDef(mappedDevices, byModel, {
|
|
878
|
-
...deviceDef,
|
|
879
|
-
model: deviceWhiteLabel.model,
|
|
880
|
-
vendor: deviceWhiteLabel.vendor,
|
|
881
|
-
description: deviceWhiteLabel.description || deviceDef.description,
|
|
882
|
-
});
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
|
|
888
869
|
async function applyExposeForDevice(mappedDevices, byModel, device) {
|
|
889
870
|
const deviceDef = await zigbeeHerdsmanConverters.findByDevice(device);
|
|
890
|
-
if (!deviceDef)
|
|
891
|
-
|
|
892
|
-
|
|
871
|
+
if (!deviceDef) {
|
|
872
|
+
return undefined;
|
|
873
|
+
}
|
|
874
|
+
return applyDeviceDef(mappedDevices, byModel, deviceDef, device);
|
|
893
875
|
}
|
|
894
876
|
|
|
895
877
|
function applyDeviceDef(mappedDevices, byModel, deviceDef, device) {
|
|
@@ -900,14 +882,15 @@ function applyDeviceDef(mappedDevices, byModel, deviceDef, device) {
|
|
|
900
882
|
const newDevice = createFromExposes(stripModel, deviceDef, device);
|
|
901
883
|
mappedDevices.push(newDevice);
|
|
902
884
|
byModel.set(stripModel, newDevice);
|
|
885
|
+
return newDevice;
|
|
903
886
|
|
|
904
887
|
} catch (e) {
|
|
905
|
-
|
|
888
|
+
return undefined;
|
|
906
889
|
}
|
|
907
890
|
}
|
|
891
|
+
return existsMap;
|
|
908
892
|
}
|
|
909
893
|
|
|
910
894
|
module.exports = {
|
|
911
|
-
applyExposes: applyExposes,
|
|
912
895
|
applyExposeForDevice: applyExposeForDevice,
|
|
913
896
|
};
|
package/lib/groups.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
const json = require('iobroker.zigbee/lib/json');
|
|
4
4
|
const statesMapping = require('./devices');
|
|
5
|
-
const { numberWithinRange } = require('zigbee-herdsman-converters/lib/utils');
|
|
6
5
|
const idRegExp = new RegExp(/group_(\d+)/);
|
|
7
6
|
|
|
8
7
|
|
|
@@ -168,7 +167,7 @@ class Groups {
|
|
|
168
167
|
for (const gpid of groups[epid]) {
|
|
169
168
|
const gpidn = parseInt(gpid);
|
|
170
169
|
if (gpidn < 0) {
|
|
171
|
-
this.
|
|
170
|
+
this.debug(`calling removeDevFromGroup with ${sysid}, ${-gpidn}, ${epid}` );
|
|
172
171
|
const response = await this.zbController.removeDevFromGroup(sysid, (-gpidn), epid);
|
|
173
172
|
if (response && response.error) {
|
|
174
173
|
errors.push(response.error);
|
|
@@ -176,7 +175,7 @@ class Groups {
|
|
|
176
175
|
}
|
|
177
176
|
const icon = this.stController.getDefaultGroupIcon(-gpidn)
|
|
178
177
|
} else if (gpidn > 0) {
|
|
179
|
-
this.
|
|
178
|
+
this.debug(`calling addDevToGroup with ${sysid}, ${gpidn}, ${epid}` );
|
|
180
179
|
const response = await this.zbController.addDevToGroup(sysid, (gpidn), epid);
|
|
181
180
|
if (response && response.error) {
|
|
182
181
|
errors.push(response.error);
|
|
@@ -233,7 +232,7 @@ class Groups {
|
|
|
233
232
|
throw error;
|
|
234
233
|
}
|
|
235
234
|
if (result.unread.length > 0) {
|
|
236
|
-
this.
|
|
235
|
+
this.debug(`unread ${stateDesc.id} change for group members ${JSON.stringify(result.unread)}`);
|
|
237
236
|
}
|
|
238
237
|
}
|
|
239
238
|
|
|
@@ -254,11 +253,10 @@ class Groups {
|
|
|
254
253
|
if (message.remove) {
|
|
255
254
|
for (const member of message.remove) {
|
|
256
255
|
const response = await this.zbController.removeDevFromGroup(member.id, id, member.ep);
|
|
257
|
-
this.
|
|
256
|
+
this.debug('trying to remove ' + member.id + (member.ep ? '.'+member.ep : '') + ' ' + ' from group ' + message.id + ' response is '+JSON.stringify(response));
|
|
258
257
|
}
|
|
259
258
|
}
|
|
260
259
|
if (icon.match(/img\/group_\d+\.png/g)) {
|
|
261
|
-
this.warn('.');
|
|
262
260
|
icon = await this.zbController.rebuildGroupIcon(group);
|
|
263
261
|
}
|
|
264
262
|
} catch (e) {
|
|
@@ -270,7 +268,7 @@ class Groups {
|
|
|
270
268
|
this.warn(`rename group name ${name}, id ${id}, icon ${icon} remove ${JSON.stringify(message.removeMembers)}`);
|
|
271
269
|
const group = await this.adapter.getObjectAsync(id);
|
|
272
270
|
if (!group) {
|
|
273
|
-
this.
|
|
271
|
+
this.debug('group object doesnt exist ')
|
|
274
272
|
// assume we have to create the group
|
|
275
273
|
this.adapter.setObjectNotExists(id, {
|
|
276
274
|
type: 'device',
|
|
@@ -301,7 +299,7 @@ class Groups {
|
|
|
301
299
|
});
|
|
302
300
|
}
|
|
303
301
|
else {
|
|
304
|
-
this.
|
|
302
|
+
this.debug('group object exists');
|
|
305
303
|
this.adapter.extendObject(id, {common: {name, type: 'group', icon: icon}});
|
|
306
304
|
}
|
|
307
305
|
}
|
package/lib/localConfig.js
CHANGED
|
@@ -138,7 +138,7 @@ class localConfig extends EventEmitter {
|
|
|
138
138
|
try {
|
|
139
139
|
this.adapter.fileExists(namespace, rv, (err, result) => {
|
|
140
140
|
if (result) return;
|
|
141
|
-
const src = this.adapter.expandFileName(iconPath)
|
|
141
|
+
const src = this.adapter.expandFileName(iconPath);
|
|
142
142
|
fs.readFile(src, (err, data) => {
|
|
143
143
|
if (err) {
|
|
144
144
|
this.error('unable to read ' + src + ' : '+ (err && err.message? err.message:' no message given'))
|
|
@@ -220,8 +220,7 @@ class localConfig extends EventEmitter {
|
|
|
220
220
|
}
|
|
221
221
|
|
|
222
222
|
async updateFromDeviceNames() {
|
|
223
|
-
this.
|
|
224
|
-
const fn = this.adapter.expandFileName('dev_names').replace('.', '_').concat('.json');
|
|
223
|
+
const fn = this.adapter.expandFileName('dev_names.json');
|
|
225
224
|
fs.readFile(fn, (err, content) => {
|
|
226
225
|
if (!err) {
|
|
227
226
|
let data_js = {};
|
|
@@ -250,7 +249,7 @@ class localConfig extends EventEmitter {
|
|
|
250
249
|
|
|
251
250
|
async init() {
|
|
252
251
|
this.info('init localConfig');
|
|
253
|
-
const fn = this.adapter.expandFileName('LocalOverrides').replace('.','
|
|
252
|
+
const fn = this.adapter.expandFileName('LocalOverrides').replace('zigbee.','zigbee_').concat('.json');
|
|
254
253
|
this.filename = fn;
|
|
255
254
|
this.basefolder = path.dirname(fn);
|
|
256
255
|
|
|
@@ -286,7 +285,7 @@ class localConfig extends EventEmitter {
|
|
|
286
285
|
const files= fs.readdirSync(_path, {withFileTypes: true, recursive: true}).filter(item => (!item.isDirectory() && item.name.endsWith('.png')));
|
|
287
286
|
files.forEach((item) => {
|
|
288
287
|
const fn = path.join(item.parentPath, item.name);
|
|
289
|
-
rv.push({file: fn, name: item.name, data: fs.readFileSync(path.join(item.parentPath, item.name), 'base64')})
|
|
288
|
+
rv.push({file: fn, name: item.name, data: fs.readFileSync(path.join(item.parentPath, item.name), 'base64'), isBase64:true});
|
|
290
289
|
});
|
|
291
290
|
//this.warn('enumerateImages for ' + _path + ' is ' + JSON.stringify(rv));
|
|
292
291
|
}
|
package/lib/ota.js
CHANGED
|
@@ -55,7 +55,7 @@ class Ota {
|
|
|
55
55
|
async checkOtaAvail(obj) {
|
|
56
56
|
const device = await this.getDeviceForMessage(obj);
|
|
57
57
|
if (!device) {
|
|
58
|
-
this.
|
|
58
|
+
this.info(`Device ${obj.message.devId} is unavailable`);
|
|
59
59
|
this.adapter.sendTo(obj.from, obj.command, {
|
|
60
60
|
status: 'fail',
|
|
61
61
|
device: getZbId(obj.message.devId),
|
|
@@ -64,13 +64,13 @@ class Ota {
|
|
|
64
64
|
return;
|
|
65
65
|
}
|
|
66
66
|
if (this.inProgress.has(device.device.ieeeAddr)) {
|
|
67
|
-
this.
|
|
67
|
+
this.info(`Update or check already in progress for '${device.name}', skipping...`);
|
|
68
68
|
return;
|
|
69
69
|
}
|
|
70
70
|
// do not attempt update for a device which has been deactivated or is unavailable
|
|
71
71
|
const stateObj = await this.adapter.getObjectAsync(obj.message.devId);
|
|
72
72
|
if (stateObj && stateObj.common && stateObj.common.deactivated) {
|
|
73
|
-
this.
|
|
73
|
+
this.info(`Device ${obj.message.devId} is deactivated, skipping...`);
|
|
74
74
|
this.adapter.sendTo(obj.from, obj.command, {
|
|
75
75
|
status: 'fail',
|
|
76
76
|
device: getZbId(obj.message.devId),
|
|
@@ -81,7 +81,7 @@ class Ota {
|
|
|
81
81
|
const availablestate = await this.adapter.getStateAsync(`${obj.message.devId.replace(this.namespace + '.', '')}.available`);
|
|
82
82
|
const lqi = await this.adapter.getStateAsync(`${obj.message.devId.replace(this.namespace + '.', '')}.link_quality`);
|
|
83
83
|
if ((availablestate && (!availablestate.val)) || (lqi && lqi.val < 1)) {
|
|
84
|
-
this.
|
|
84
|
+
this.info(`Device ${obj.message.devId} is marked unavailable, skipping...`);
|
|
85
85
|
this.adapter.sendTo(obj.from, obj.command, {
|
|
86
86
|
status: 'fail',
|
|
87
87
|
device: getZbId(obj.message.devId),
|
|
@@ -133,13 +133,13 @@ class Ota {
|
|
|
133
133
|
// do not attempt update for a device which has been deactivated or is unavailable
|
|
134
134
|
const stateObj = await this.adapter.getObjectAsync(obj.message.devId);
|
|
135
135
|
if (stateObj && stateObj.common && stateObj.common.deactivated) {
|
|
136
|
-
this.
|
|
136
|
+
this.info(`Device ${obj.message.devId} is deactivated, skipping...`);
|
|
137
137
|
return;
|
|
138
138
|
}
|
|
139
139
|
const availablestate = await this.adapter.getStateAsync(`${obj.message.devId.replace(this.namespace + '.', '')}.available`);
|
|
140
140
|
const lqi = await this.adapter.getStateAsync(`${obj.message.devId.replace(this.namespace + '.', '')}.link_quality`);
|
|
141
141
|
if ((availablestate && (!availablestate.val)) || (lqi && lqi.val < 1)) {
|
|
142
|
-
this.
|
|
142
|
+
this.info(`Device ${obj.message.devId} is marked unavailable, skipping...`);
|
|
143
143
|
return;
|
|
144
144
|
}
|
|
145
145
|
this.inProgress.add(device.device.ieeeAddr);
|
package/lib/seriallist.js
CHANGED
|
@@ -22,10 +22,17 @@ class SerialList {
|
|
|
22
22
|
SerialPort.list()
|
|
23
23
|
.then(ports => {
|
|
24
24
|
this.adapter.log.info(`List of port: ${JSON.stringify(ports)}`);
|
|
25
|
-
|
|
25
|
+
const candidates = ports.map(item => ({
|
|
26
26
|
label: item.friendlyName || item.pnpId || item.manufacturer,
|
|
27
27
|
comName: item.path
|
|
28
|
-
}))
|
|
28
|
+
}));
|
|
29
|
+
require('fs').readdir('/dev/serial/by-id', (err, files) => {
|
|
30
|
+
if (!err) {
|
|
31
|
+
for (const item of files)
|
|
32
|
+
candidates.push({comName: `/dev/serial/by-id/${item}`});
|
|
33
|
+
}
|
|
34
|
+
this.adapter.sendTo(obj.from, obj.command, candidates.reverse(), obj.callback);
|
|
35
|
+
})
|
|
29
36
|
})
|
|
30
37
|
.catch(e => {
|
|
31
38
|
this.adapter.sendTo(obj.from, obj.command, [], obj.callback);
|