iobroker.zigbee 2.0.5 → 3.0.1
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 +22 -3
- package/admin/admin.js +420 -115
- package/admin/index_m.html +285 -229
- package/admin/tab_m.html +108 -91
- 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 +66 -66
- package/io-package.json +32 -31
- package/lib/DeviceDebug.js +2 -1
- package/lib/commands.js +203 -40
- package/lib/devices.js +2 -2
- package/lib/exposes.js +8 -30
- package/lib/groups.js +1 -1
- package/lib/localConfig.js +33 -10
- package/lib/networkmap.js +2 -1
- package/lib/seriallist.js +9 -2
- package/lib/statescontroller.js +185 -91
- package/lib/utils.js +41 -11
- package/lib/zbDeviceConfigure.js +10 -3
- package/lib/zigbeecontroller.js +121 -94
- package/main.js +135 -73
- package/package.json +8 -8
- 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/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;
|
|
@@ -145,31 +149,92 @@ class Commands {
|
|
|
145
149
|
this.updateDeviceImage(obj.from, obj.command, obj.message, obj.callback);
|
|
146
150
|
}
|
|
147
151
|
break;
|
|
148
|
-
case '
|
|
152
|
+
case 'updateLocalConfigItems':
|
|
149
153
|
if (obj.message && typeof obj.message === 'object') {
|
|
150
|
-
this.
|
|
154
|
+
this.updateLocalConfigItems(obj.from, obj.command, obj.message, obj.callback);
|
|
151
155
|
}
|
|
152
156
|
break;
|
|
153
|
-
case '
|
|
154
|
-
if (obj) {
|
|
155
|
-
this.
|
|
157
|
+
case 'getLocalConfigItems':
|
|
158
|
+
if (obj.message && typeof obj.message === 'object') {
|
|
159
|
+
this.getLocalConfigItems(obj.from, obj.command, obj.message, obj.callback);
|
|
156
160
|
}
|
|
161
|
+
break;
|
|
162
|
+
case 'getDeviceCleanupRequired':
|
|
163
|
+
if (this.stController) this.adapter.sendTo(obj.from, obj.command, {clean:this.stController.CleanupRequired(), errors:this.stController.getStashedErrors()}, obj.callback);
|
|
164
|
+
// NO Break - returning the debug-data as well is intentional
|
|
157
165
|
case 'getDebugMessages':
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
166
|
+
this.adapter.sendTo(obj.from, obj.command, {byId:this.adapter.deviceDebug.collectDebugData( obj.message.inlog)},obj.callback);
|
|
167
|
+
break;
|
|
168
|
+
case 'testConnection':
|
|
169
|
+
this.testConnection(obj.from, obj.command, obj.message, obj.callback);
|
|
170
|
+
break;
|
|
171
|
+
case 'readNVRam':
|
|
172
|
+
this.readNvBackup(obj.from, obj.command, obj.message, obj.callback);
|
|
173
|
+
default:
|
|
174
|
+
//this.warn(`Commands: Command ${obj.command} is unknown`);
|
|
175
|
+
//this.adapter.sendTo(obj.from, obj.command, obj.message, obj.callback);
|
|
176
|
+
break;
|
|
161
177
|
}
|
|
162
178
|
}
|
|
163
179
|
}
|
|
164
180
|
}
|
|
165
181
|
|
|
166
|
-
|
|
182
|
+
async readNvBackup(from, command, msg, callback) {
|
|
183
|
+
this.debug('readNvBackup called')
|
|
167
184
|
try {
|
|
168
|
-
|
|
185
|
+
const zo = this.adapter.getZigbeeOptions();
|
|
186
|
+
const name = require('path').join(zo.dbDir, zo.backupPath);
|
|
187
|
+
const nvbackup = fs.readFileSync(name, {encoding: 'utf8'}).toString();
|
|
188
|
+
const nvBackupJson = JSON.parse(nvbackup);
|
|
189
|
+
const rv = {};
|
|
190
|
+
rv.channel = nvBackupJson.channel;
|
|
191
|
+
rv.precfgkey = (nvBackupJson.network_key ? nvBackupJson.network_key.key : undefined);
|
|
192
|
+
rv.extPanID = nvBackupJson.extended_pan_id ? reverseByteString(nvBackupJson.extended_pan_id) : undefined;
|
|
193
|
+
rv.panID = parseInt('0x'+nvBackupJson.pan_id);
|
|
194
|
+
this.debug('readNvBackup returns ' + JSON.stringify(rv))
|
|
195
|
+
this.adapter.sendTo(from, command, rv, callback)
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
const msg = `Unable to read nvBackup ${error && error.message ? error.message : 'no message given'}`;
|
|
199
|
+
this.error(msg);
|
|
200
|
+
this.adapter.sendTo(from, command, {error:msg}, callback)
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async delNvBackup(from, command, msg, callback) {
|
|
205
|
+
try {
|
|
206
|
+
if (this.zbController)
|
|
207
|
+
{
|
|
208
|
+
// stop the herdsman if needed
|
|
209
|
+
const wasRunning = this.zbController.herdsmanStarted;
|
|
210
|
+
if (wasRunning) await this.zbController.stop();
|
|
169
211
|
const name = this.zbController.herdsman.adapter.backupPath;
|
|
170
|
-
|
|
212
|
+
fs.unlink(name, async (err) => {
|
|
213
|
+
const rv={};
|
|
214
|
+
if (err) {
|
|
215
|
+
this.error(`Unable to remove ${name}: ${err}`);
|
|
216
|
+
rv.error = `Unable to remove ${name}: ${err}`;
|
|
217
|
+
}
|
|
218
|
+
// start the herdsman again if it was stopped before
|
|
219
|
+
if (wasRunning) await this.zbController.start();
|
|
220
|
+
this.adapter.sendTo(from, command, rv, callback)
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
const zo = this.adapter.getZigbeeOptions();
|
|
225
|
+
const name = require('path').join(zo.dbDir, zo.backupPath);
|
|
226
|
+
fs.unlink(name, async (err) => {
|
|
227
|
+
const rv={};
|
|
228
|
+
if (err) {
|
|
229
|
+
this.error(`Unable to remove ${name}: ${err}`);
|
|
230
|
+
rv.error = `Unable to remove ${name}: ${err}`;
|
|
231
|
+
}
|
|
232
|
+
// start the herdsman again if it was stopped before
|
|
233
|
+
this.adapter.sendTo(from, command, rv, callback)
|
|
234
|
+
});
|
|
171
235
|
}
|
|
172
236
|
} catch (error) {
|
|
237
|
+
this.adapter.sendTo(from, command, {error: error.message}, callback)
|
|
173
238
|
this.error(error);
|
|
174
239
|
}
|
|
175
240
|
}
|
|
@@ -231,7 +296,7 @@ class Commands {
|
|
|
231
296
|
}
|
|
232
297
|
|
|
233
298
|
touchlinkReset(from, command, message, callback) {
|
|
234
|
-
if (this.zbController) {
|
|
299
|
+
if (this.zbController && this.zbController.herdsmanStarted) {
|
|
235
300
|
// allow devices to join the network within 60 secs
|
|
236
301
|
this.adapter.logToPairing('Touchlink reset started ', true);
|
|
237
302
|
|
|
@@ -245,14 +310,14 @@ class Commands {
|
|
|
245
310
|
} else {
|
|
246
311
|
this.adapter.sendTo(
|
|
247
312
|
from, command,
|
|
248
|
-
{error: '
|
|
313
|
+
{error: 'No active connection to Zigbee Hardware!'},
|
|
249
314
|
callback
|
|
250
315
|
);
|
|
251
316
|
}
|
|
252
317
|
}
|
|
253
318
|
|
|
254
319
|
async getDevices(from, command, id, callback) {
|
|
255
|
-
if (this.zbController) {
|
|
320
|
+
if (this.zbController && this.zbController.herdsmanStarted) {
|
|
256
321
|
this.debug(`getDevices called from ${from} with command ${JSON.stringify(command)} and id ${JSON.stringify(id)}`);
|
|
257
322
|
const pairedDevices = await this.zbController.getClients(true);
|
|
258
323
|
const groups = {};
|
|
@@ -421,18 +486,27 @@ class Commands {
|
|
|
421
486
|
return devices;
|
|
422
487
|
})
|
|
423
488
|
.then(devices => {
|
|
424
|
-
this.debug(`getDevices
|
|
425
|
-
this.adapter.
|
|
489
|
+
this.debug(`getDevices contains ${devices.length} Devices`);
|
|
490
|
+
const rv = { devices:devices, inLog:this.adapter.deviceDebug.logStatus, byId:this.adapter.deviceDebug.collectDebugData() }
|
|
491
|
+
if (this.stController) {
|
|
492
|
+
rv.clean = this.stController.CleanupRequired();
|
|
493
|
+
rv.errors = this.stController.getStashedErrors();
|
|
494
|
+
rv.debugDevices = this.stController.debugDevices;
|
|
495
|
+
}
|
|
496
|
+
this.adapter.sendTo(from, command, rv, callback);
|
|
426
497
|
})
|
|
427
|
-
.catch(err =>
|
|
498
|
+
.catch(err => {
|
|
499
|
+
this.error(`getDevices error: ${err.stack}`);
|
|
500
|
+
this.adapter.sendTo(from, command, {error: `Error enumerating devices : ${err && err.message ? err.message : ''}`}, callback);
|
|
501
|
+
});
|
|
428
502
|
} else {
|
|
429
|
-
this.adapter.sendTo(from, command, {error: '
|
|
503
|
+
this.adapter.sendTo(from, command, {error: 'No active connection to Zigbee Hardware!'}, callback);
|
|
430
504
|
}
|
|
431
505
|
}
|
|
432
506
|
|
|
433
507
|
|
|
434
508
|
async getCoordinatorInfo(from, command, callback) {
|
|
435
|
-
if (this.zbController) {
|
|
509
|
+
if (this.zbController && this.zbController.herdsmanStarted) {
|
|
436
510
|
const coordinatorinfo = {
|
|
437
511
|
installSource: 'IADefault_1',
|
|
438
512
|
channel: '-1',
|
|
@@ -500,7 +574,7 @@ class Commands {
|
|
|
500
574
|
this.adapter.sendTo(from, command, coordinatorinfo, callback);
|
|
501
575
|
});
|
|
502
576
|
} else {
|
|
503
|
-
this.adapter.sendTo(from, command, {error: '
|
|
577
|
+
this.adapter.sendTo(from, command, {error: 'No active connection to Zigbee Hardware!'}, callback);
|
|
504
578
|
}
|
|
505
579
|
}
|
|
506
580
|
|
|
@@ -515,7 +589,7 @@ class Commands {
|
|
|
515
589
|
}
|
|
516
590
|
|
|
517
591
|
deleteDevice(from, command, msg, callback) {
|
|
518
|
-
if (this.zbController && this.stController) {
|
|
592
|
+
if (this.zbController && this.zbController.herdsmanStarted && this.stController) {
|
|
519
593
|
this.debug(`deleteDevice message: ${JSON.stringify(msg)}`);
|
|
520
594
|
const id = msg.id;
|
|
521
595
|
const force = msg.force;
|
|
@@ -540,7 +614,7 @@ class Commands {
|
|
|
540
614
|
}
|
|
541
615
|
});
|
|
542
616
|
} else {
|
|
543
|
-
this.adapter.sendTo(from, command, {error: '
|
|
617
|
+
this.adapter.sendTo(from, command, {error: 'No active connection to Zigbee Hardware!'}, callback);
|
|
544
618
|
}
|
|
545
619
|
}
|
|
546
620
|
|
|
@@ -564,14 +638,14 @@ class Commands {
|
|
|
564
638
|
}
|
|
565
639
|
|
|
566
640
|
async getChannels(from, command, message, callback) {
|
|
567
|
-
if (this.zbController) {
|
|
641
|
+
if (this.zbController && this.zbController.herdsmanStarted) {
|
|
568
642
|
const result = await this.zbController.getChannelsEnergy();
|
|
569
643
|
this.debug(`getChannels result: ${JSON.stringify(result)}`);
|
|
570
644
|
this.adapter.sendTo(from, command, result, callback);
|
|
571
645
|
} else {
|
|
572
646
|
this.adapter.sendTo(
|
|
573
647
|
from, command,
|
|
574
|
-
{error: '
|
|
648
|
+
{error: 'No active connection to Zigbee Hardware!'},
|
|
575
649
|
callback
|
|
576
650
|
);
|
|
577
651
|
}
|
|
@@ -604,7 +678,7 @@ class Commands {
|
|
|
604
678
|
async getLocalImages(from, command, msg, callback) {
|
|
605
679
|
if (this.stController) {
|
|
606
680
|
const id = msg.id;
|
|
607
|
-
const result = await this.stController.localConfig.enumerateImages(
|
|
681
|
+
const result = await this.stController.localConfig.enumerateImages(this.adapter.getDataFolder());
|
|
608
682
|
this.adapter.sendTo(from, command, {imageData:result}, callback)
|
|
609
683
|
}
|
|
610
684
|
}
|
|
@@ -633,10 +707,10 @@ class Commands {
|
|
|
633
707
|
}
|
|
634
708
|
}
|
|
635
709
|
|
|
636
|
-
async
|
|
710
|
+
async updateLocalConfigItems(from, command, msg, callback) {
|
|
637
711
|
if (this.stController) {
|
|
638
|
-
this.debug(`
|
|
639
|
-
const target = msg.target.replace(`${this.adapter.namespace}.`, '');
|
|
712
|
+
this.debug(`updateLocalConfigItems : ${JSON.stringify(msg)}`);
|
|
713
|
+
const target = msg.global ? msg.target : msg.target.replace(`${this.adapter.namespace}.`, '');
|
|
640
714
|
const entity = await this.zbController.resolveEntity(target);
|
|
641
715
|
//this.warn('entity for ' + target + ' is '+ JSON.stringify(entity))
|
|
642
716
|
if (entity && !entity.mapped) {
|
|
@@ -651,19 +725,45 @@ class Commands {
|
|
|
651
725
|
}
|
|
652
726
|
}
|
|
653
727
|
if (entity) {
|
|
654
|
-
|
|
728
|
+
this.debug('updateLocalConfigItems with Entity');
|
|
655
729
|
this.stController.updateDev(target, entity.mapped.model, entity.mapped.model, () => {this.adapter.sendTo(from, command, {}, callback)});
|
|
656
730
|
}
|
|
657
731
|
else {
|
|
658
|
-
|
|
732
|
+
this.debug('updateLocalConfigItems without Entity');
|
|
659
733
|
this.stController.updateDev(target, undefined, 'group',() => {this.adapter.sendTo(from, command, {}, callback)});
|
|
660
734
|
}
|
|
661
735
|
}
|
|
662
736
|
}
|
|
663
737
|
|
|
738
|
+
async getLocalConfigItems(from, command, msg, callback)
|
|
739
|
+
{
|
|
740
|
+
const rv = {};
|
|
741
|
+
if (this.stController) {
|
|
742
|
+
this.debug(`getLocalConfigItems : ${JSON.stringify(msg)}`)
|
|
743
|
+
|
|
744
|
+
if (msg.hasOwnProperty('global') && msg.hasOwnProperty('target') && (msg.hasOwnProperty('keys') || msg.hasOwnProperty('key')))
|
|
745
|
+
{
|
|
746
|
+
const target = msg.global ? msg.target : msg.target.replace(`${this.adapter.namespace}.`, '');
|
|
747
|
+
const keys = msg.hasOwnProperty('keys') ? msg.keys : [msg.key];
|
|
748
|
+
for (const key of keys) {
|
|
749
|
+
const ld = this.stController.localConfig.getOverrideWithTargetAndKey(target, key, msg.global);
|
|
750
|
+
if (ld != undefined) rv[key] = ld;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
//const targetId = msg.id ? msg.id.replace(`${this.adapter.namespace}.`, '') : '';
|
|
754
|
+
//const targetModel = msg.model ? msg.model : '';
|
|
755
|
+
}
|
|
756
|
+
else {
|
|
757
|
+
rv.error = `missing data in message ${JSON.stringify(msg)}`;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
else rv.error = 'stController not initialized - no Data sent'
|
|
761
|
+
|
|
762
|
+
this.adapter.sendTo(from, command, rv, callback);
|
|
763
|
+
}
|
|
664
764
|
|
|
665
765
|
async reconfigure(from, command, msg, callback) {
|
|
666
|
-
if (this.zbController) {
|
|
766
|
+
if (this.zbController && this.zbController.herdsmanStarted) {
|
|
667
767
|
const devid = getZbId(msg.id);
|
|
668
768
|
this.debug(`Reconfigure ${devid}`);
|
|
669
769
|
const entity = await this.zbController.resolveEntity(devid);
|
|
@@ -688,6 +788,69 @@ class Commands {
|
|
|
688
788
|
this.adapter.sendTo(from, command, {error: 'No device'}, callback);
|
|
689
789
|
}
|
|
690
790
|
}
|
|
791
|
+
else {
|
|
792
|
+
this.adapter.sendTo(from, command, {error: 'No active connection to Zigbee Hardware!'}, callback);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
async testConnection(from, command, msg, callback) {
|
|
797
|
+
this.debug(`TestConnection with ${JSON.stringify(msg)}`);
|
|
798
|
+
if (msg && msg.address) {
|
|
799
|
+
const netAddress = getNetAddress(msg.address);
|
|
800
|
+
if (netAddress && netAddress.host) {
|
|
801
|
+
this.adapter.logToPairing(`attempting dns lookup for ${netAddress.host}`);
|
|
802
|
+
this.debug(`attempting dns lookup for ${netAddress.host}`);
|
|
803
|
+
dns.lookup(netAddress.host, (err, ip, _) => {
|
|
804
|
+
if (err) {
|
|
805
|
+
const msg = `Unable to resolve name: ${err && err.message ? err.message : 'no message'}`;
|
|
806
|
+
this.error(msg);
|
|
807
|
+
this.adapter.logToPairing(`Error: ${msg}`);
|
|
808
|
+
this.adapter.sendTo(from, command, {error:msg}, callback);
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
811
|
+
this.debug(`dns lookup for ${msg.address} produced ${ip}`);
|
|
812
|
+
this.adapter.logToPairing(`dns lookup for ${msg.address} produced ${ip}`);
|
|
813
|
+
const client = new net.Socket();
|
|
814
|
+
this.debug(`attempting to connect to ${ip} port ${netAddress.port ? netAddress.port : 80}`);
|
|
815
|
+
client.connect(netAddress.port, ip, () => {
|
|
816
|
+
client.destroy()
|
|
817
|
+
this.adapter.logToPairing(`connected successfully to connect to ${ip} port ${netAddress.port ? netAddress.port : 80}`);
|
|
818
|
+
this.debug(`connected successfully to connect to ${ip} port ${netAddress.port ? netAddress.port : 80}`);
|
|
819
|
+
this.adapter.sendTo(from, command, {}, callback)
|
|
820
|
+
})
|
|
821
|
+
client.on('error', (error) => {
|
|
822
|
+
const msg = `unable to connect to ${ip} port ${netAddress.port ? netAddress.port : 80} : ${error && error.message ? error.message : 'no message given'}`
|
|
823
|
+
this.error(msg);
|
|
824
|
+
this.adapter.logToPairing(`Error: ${msg}`);
|
|
825
|
+
this.adapter.sendTo(from, command, {error:msg}, callback);
|
|
826
|
+
});
|
|
827
|
+
})
|
|
828
|
+
}
|
|
829
|
+
else
|
|
830
|
+
{
|
|
831
|
+
try {
|
|
832
|
+
const port = msg.address.trim();
|
|
833
|
+
this.adapter.logToPairing(`reading access rights for ${port}`);
|
|
834
|
+
access(port, constants.R_OK | constants.W_OK, (error) => {
|
|
835
|
+
if (error) {
|
|
836
|
+
const msg = `unable to access ${port} : ${error && error.message ? error.message : 'no message given'}`;
|
|
837
|
+
this.error(msg);
|
|
838
|
+
this.adapter.logToPairing(`Error: ${msg}`);
|
|
839
|
+
this.adapter.sendTo(from, command, {error:msg}, callback);
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
this.adapter.logToPairing(`read and write access available for ${port}`);
|
|
843
|
+
this.adapter.sendTo(from, command, {}, callback);
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
catch (error) {
|
|
847
|
+
const msg = `File access error: ${error && error.message ? error.message : 'no message given'}`;
|
|
848
|
+
this.error(msg);
|
|
849
|
+
this.adapter.logToPairing(`Error: ${msg}`);
|
|
850
|
+
this.adapter.sendTo(from, command, {error:msg}, callback);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
}
|
|
691
854
|
}
|
|
692
855
|
}
|
|
693
856
|
|
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
|
|
|
@@ -867,34 +866,12 @@ function createFromExposes(model, def, device, log) {
|
|
|
867
866
|
|
|
868
867
|
}
|
|
869
868
|
|
|
870
|
-
function
|
|
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
|
-
async function applyExposeForDevice(mappedDevices, byModel, device, logger) {
|
|
889
|
-
const t = Date.now();
|
|
869
|
+
async function applyExposeForDevice(mappedDevices, byModel, device) {
|
|
890
870
|
const deviceDef = await zigbeeHerdsmanConverters.findByDevice(device);
|
|
891
871
|
if (!deviceDef) {
|
|
892
|
-
|
|
893
|
-
return false;
|
|
872
|
+
return undefined;
|
|
894
873
|
}
|
|
895
|
-
|
|
896
|
-
applyDeviceDef(mappedDevices, byModel, deviceDef, device);
|
|
897
|
-
return true;
|
|
874
|
+
return applyDeviceDef(mappedDevices, byModel, deviceDef, device);
|
|
898
875
|
}
|
|
899
876
|
|
|
900
877
|
function applyDeviceDef(mappedDevices, byModel, deviceDef, device) {
|
|
@@ -905,14 +882,15 @@ function applyDeviceDef(mappedDevices, byModel, deviceDef, device) {
|
|
|
905
882
|
const newDevice = createFromExposes(stripModel, deviceDef, device);
|
|
906
883
|
mappedDevices.push(newDevice);
|
|
907
884
|
byModel.set(stripModel, newDevice);
|
|
885
|
+
return newDevice;
|
|
908
886
|
|
|
909
887
|
} catch (e) {
|
|
910
|
-
|
|
888
|
+
return undefined;
|
|
911
889
|
}
|
|
912
890
|
}
|
|
891
|
+
return existsMap;
|
|
913
892
|
}
|
|
914
893
|
|
|
915
894
|
module.exports = {
|
|
916
|
-
applyExposes: applyExposes,
|
|
917
895
|
applyExposeForDevice: applyExposeForDevice,
|
|
918
896
|
};
|
package/lib/groups.js
CHANGED
|
@@ -265,7 +265,7 @@ class Groups {
|
|
|
265
265
|
this.warn(`renameGroup caught error ${JSON.stringify(e.code)}`);
|
|
266
266
|
}
|
|
267
267
|
}
|
|
268
|
-
this.
|
|
268
|
+
this.debug(`rename group name ${name}, id ${id}, icon ${icon} remove ${JSON.stringify(message.removeMembers)}`);
|
|
269
269
|
const group = await this.adapter.getObjectAsync(id);
|
|
270
270
|
if (!group) {
|
|
271
271
|
this.debug('group object doesnt exist ')
|
package/lib/localConfig.js
CHANGED
|
@@ -18,7 +18,6 @@ class localConfig extends EventEmitter {
|
|
|
18
18
|
this.basefolder = undefined;
|
|
19
19
|
this.retTimeoutHanlde = undefined;
|
|
20
20
|
this.adapter.on('ready', () => this.onReady());
|
|
21
|
-
this.adapter.on('unload', callback => this.onUnload(callback));
|
|
22
21
|
}
|
|
23
22
|
|
|
24
23
|
|
|
@@ -28,9 +27,9 @@ class localConfig extends EventEmitter {
|
|
|
28
27
|
|
|
29
28
|
async onUnload(callback)
|
|
30
29
|
{
|
|
31
|
-
this.info('local config saved');
|
|
32
30
|
this.retainData();
|
|
33
|
-
callback
|
|
31
|
+
// No callback here -
|
|
32
|
+
//callback();
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
info(message) {
|
|
@@ -76,14 +75,14 @@ class localConfig extends EventEmitter {
|
|
|
76
75
|
async updateLocalOverride(_target, model, key, data, global)
|
|
77
76
|
{
|
|
78
77
|
const target = (global ? model : _target);
|
|
79
|
-
this.info(`updating local data: (${global ? 'global':'local'}) : ${target}:${key}:${data}`);
|
|
78
|
+
this.info(`updating local data: (${global ? 'global':'local'}) : ${target}:${key}:${JSON.stringify(data)}`);
|
|
80
79
|
|
|
81
80
|
if (typeof target != 'string' || typeof key != 'string') {
|
|
82
81
|
this.error(`update called with illegal id data:${JSON.stringify(target)}:${JSON.stringify(key)}:${JSON.stringify(data)}`)
|
|
83
82
|
return false;
|
|
84
83
|
}
|
|
85
84
|
const base = global ? this.localData.by_model[target] || {} : this.localData.by_id[target] || {};
|
|
86
|
-
if (data && data.length > 0 && data != 'none') {
|
|
85
|
+
if (data && Object.keys(data).length > 0 && data != 'none') {
|
|
87
86
|
if (key == 'icon')
|
|
88
87
|
base[key] = data.replace(this.basefolder, '.');
|
|
89
88
|
else
|
|
@@ -103,10 +102,24 @@ class localConfig extends EventEmitter {
|
|
|
103
102
|
if (base == {}) delete this.localData.by_id[target];
|
|
104
103
|
else this.localData.by_id[target] = base;
|
|
105
104
|
}
|
|
106
|
-
this.info(`Local Data for ${target} is ${JSON.stringify(base)}`);
|
|
105
|
+
this.info(`Local Data for ${target} is ${JSON.stringify(base)} after update`);
|
|
107
106
|
return true;
|
|
108
107
|
}
|
|
109
108
|
|
|
109
|
+
async getLocalOverride(_target, model, key, global)
|
|
110
|
+
{
|
|
111
|
+
const target = (global ? model : _target);
|
|
112
|
+
this.info(`getting local data: (${global ? 'global':'local'}) : ${target}:${key}`);
|
|
113
|
+
|
|
114
|
+
if (typeof target != 'string' || typeof key != 'string') {
|
|
115
|
+
this.error(`update called with illegal id data:${JSON.stringify(target)}:${JSON.stringify(key)}`)
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
const base = global ? this.localData.by_model[target] || {} : this.localData.by_id[target] || {};
|
|
119
|
+
const rv = {};
|
|
120
|
+
if (base.hasOwnProperty(key)) rv[key] = base[key];
|
|
121
|
+
return rv;
|
|
122
|
+
}
|
|
110
123
|
|
|
111
124
|
NameForId(id, model, defaultName) {
|
|
112
125
|
this.debug('name for id with ' + id + ' and ' + defaultName + ' from ' + JSON.stringify(this.localData));
|
|
@@ -138,7 +151,7 @@ class localConfig extends EventEmitter {
|
|
|
138
151
|
try {
|
|
139
152
|
this.adapter.fileExists(namespace, rv, (err, result) => {
|
|
140
153
|
if (result) return;
|
|
141
|
-
const src = this.adapter.expandFileName(iconPath)
|
|
154
|
+
const src = this.adapter.expandFileName(iconPath);
|
|
142
155
|
fs.readFile(src, (err, data) => {
|
|
143
156
|
if (err) {
|
|
144
157
|
this.error('unable to read ' + src + ' : '+ (err && err.message? err.message:' no message given'))
|
|
@@ -213,14 +226,15 @@ class localConfig extends EventEmitter {
|
|
|
213
226
|
return rv;
|
|
214
227
|
}
|
|
215
228
|
|
|
216
|
-
|
|
229
|
+
getOverrideWithTargetAndKey(target, key, isGlobal) {
|
|
217
230
|
const targetdata = this.getOverrideData(target, isGlobal);
|
|
218
231
|
if (targetdata && targetdata.hasOwnProperty(key)) return targetdata[key];
|
|
219
232
|
return undefined;
|
|
220
233
|
}
|
|
221
234
|
|
|
222
235
|
async updateFromDeviceNames() {
|
|
223
|
-
const fn = this.adapter.expandFileName('dev_names
|
|
236
|
+
const fn = this.adapter.expandFileName('dev_names.json');
|
|
237
|
+
this.info('Initializing localConfig from dev_names.json')
|
|
224
238
|
fs.readFile(fn, (err, content) => {
|
|
225
239
|
if (!err) {
|
|
226
240
|
let data_js = {};
|
|
@@ -233,8 +247,10 @@ class localConfig extends EventEmitter {
|
|
|
233
247
|
}
|
|
234
248
|
try {
|
|
235
249
|
for (const prop in data_js) {
|
|
236
|
-
if (data_js[prop] != 'undefined')
|
|
250
|
+
if (data_js[prop] != 'undefined') {
|
|
251
|
+
this.info(`updating device name for ${prop} as ${data_js[prop]}`);
|
|
237
252
|
this.updateDeviceName(prop, data_js[prop]);
|
|
253
|
+
}
|
|
238
254
|
}
|
|
239
255
|
}
|
|
240
256
|
catch (error) {
|
|
@@ -295,6 +311,13 @@ class localConfig extends EventEmitter {
|
|
|
295
311
|
return rv;
|
|
296
312
|
}
|
|
297
313
|
|
|
314
|
+
getOptions(dev_id) {
|
|
315
|
+
const ld = this.localData.by_id[dev_id];
|
|
316
|
+
if (ld === undefined || ld.options === undefined) return {};
|
|
317
|
+
this.debug(`getOptions for ${dev_id} : ${JSON.stringify(ld.options)}`);
|
|
318
|
+
return ld.options;
|
|
319
|
+
}
|
|
320
|
+
|
|
298
321
|
}
|
|
299
322
|
|
|
300
323
|
module.exports = localConfig;
|
package/lib/networkmap.js
CHANGED
|
@@ -9,6 +9,7 @@ class NetworkMap {
|
|
|
9
9
|
start(zbController, stController) {
|
|
10
10
|
this.zbController = zbController;
|
|
11
11
|
this.stController = stController;
|
|
12
|
+
return this.stop;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
stop() {
|
|
@@ -53,4 +54,4 @@ class NetworkMap {
|
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
module.exports = NetworkMap;
|
|
57
|
+
module.exports = NetworkMap;
|
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);
|