iobroker.zigbee 3.1.5 → 3.2.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 +33 -15
- package/admin/admin.js +672 -224
- package/admin/img/group.png +0 -0
- package/admin/img/restore_backup.png +0 -0
- package/admin/index_m.html +161 -9
- package/admin/tab_m.html +7 -8
- package/docs/de/img/edit_grp.png +0 -0
- package/docs/de/img/edit_image.png +0 -0
- package/docs/de/readme.md +2 -2
- package/docs/en/readme.md +2 -2
- package/io-package.json +27 -27
- package/lib/DeviceDebug.js +1 -1
- package/lib/backup.js +55 -26
- package/lib/commands.js +150 -92
- package/lib/devices.js +10 -1
- package/lib/exclude.js +2 -1
- package/lib/exposes.js +20 -4
- package/lib/groups.js +2 -2
- package/lib/localConfig.js +56 -18
- package/lib/statescontroller.js +81 -50
- package/lib/utils.js +41 -0
- package/lib/zbDelayedAction.js +1 -1
- package/lib/zbDeviceAvailability.js +3 -3
- package/lib/zbDeviceEvent.js +4 -2
- package/lib/zigbeecontroller.js +44 -20
- package/main.js +56 -37
- package/package.json +2 -4
package/lib/commands.js
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const getZbId = require('./utils')
|
|
4
|
-
const getNetAddress = require('./utils').getNetAddress;
|
|
5
|
-
const reverseByteString = require('./utils').reverseByteString;
|
|
3
|
+
const { getZbId, getNetAddress, reverseByteString, zbIdorIeeetoAdId, adIdtoZbIdorIeee } = require('./utils');
|
|
6
4
|
const fs = require('fs');
|
|
7
5
|
const statesMapping = require('./devices');
|
|
8
|
-
const utils = require('@iobroker/adapter-core'); // Get common adapter utils
|
|
9
6
|
const colors = require('./colors.js');
|
|
10
7
|
/* currently not needed, kept for referencce
|
|
8
|
+
const utils = require('@iobroker/adapter-core'); // Get common adapter utils
|
|
11
9
|
const dns = require('dns');
|
|
12
10
|
const net = require('net');
|
|
13
11
|
const access = fs.access;
|
|
@@ -54,12 +52,12 @@ class Commands {
|
|
|
54
52
|
/**
|
|
55
53
|
* @param {ioBroker.Message} obj
|
|
56
54
|
*/
|
|
57
|
-
onMessage(obj) {
|
|
55
|
+
async onMessage(obj) {
|
|
58
56
|
if (typeof obj === 'object' && obj.command) {
|
|
59
57
|
if (obj) {
|
|
60
58
|
switch (obj.command) {
|
|
61
59
|
case 'testConnect':
|
|
62
|
-
this.adapter.
|
|
60
|
+
this.adapter.sendTo(obj.from, obj.command, await this.adapter.testConnect(obj.message), obj.callback);
|
|
63
61
|
break;
|
|
64
62
|
case 'deleteNVBackup':
|
|
65
63
|
this.delNvBackup(obj.from, obj.command, {}, obj.callback);
|
|
@@ -153,7 +151,7 @@ class Commands {
|
|
|
153
151
|
break;
|
|
154
152
|
case 'updateLocalConfigItems':
|
|
155
153
|
if (obj.message && typeof obj.message === 'object') {
|
|
156
|
-
this.
|
|
154
|
+
this.updateConfigItems(obj.from, obj.command, obj.message, obj.callback);
|
|
157
155
|
}
|
|
158
156
|
break;
|
|
159
157
|
case 'getLocalConfigItems':
|
|
@@ -298,8 +296,7 @@ class Commands {
|
|
|
298
296
|
|
|
299
297
|
if (await this.zbController.permitJoin(cTimer, devId)) {
|
|
300
298
|
this.adapter.setState('info.pairingMode', cTimer > 0, true);
|
|
301
|
-
|
|
302
|
-
this.adapter.sendTo(from, command, cTimer ? 'Start pairing!':'Stop pairing!', callback);
|
|
299
|
+
//this.adapter.sendTo(from, command, cTimer ? 'Start pairing!':'Stop pairing!', callback);
|
|
303
300
|
}
|
|
304
301
|
else {
|
|
305
302
|
this.adapter.sendTo(
|
|
@@ -339,12 +336,8 @@ class Commands {
|
|
|
339
336
|
}
|
|
340
337
|
}
|
|
341
338
|
|
|
342
|
-
|
|
343
|
-
async handleDeviceforInfo(device, states) {
|
|
344
|
-
}
|
|
345
|
-
|
|
346
339
|
async handleGroupforInfo(group, groups) {
|
|
347
|
-
group.icon = 'img/
|
|
340
|
+
group.icon = 'img/group_1.png';
|
|
348
341
|
group.vendor = 'ioBroker';
|
|
349
342
|
// get group members and store them
|
|
350
343
|
const match = /zigbee.\d.group_([0-9]+)/.exec(group._id);
|
|
@@ -356,8 +349,8 @@ class Commands {
|
|
|
356
349
|
const memberinfo = [];
|
|
357
350
|
for (const member of groupmembers) {
|
|
358
351
|
if (member && typeof member.ieee === 'string') {
|
|
359
|
-
const memberId = member.ieee
|
|
360
|
-
const device = await this.adapter.getObjectAsync(
|
|
352
|
+
const memberId = zbIdorIeeetoAdId(this.adapter, member.ieee, false);
|
|
353
|
+
const device = await this.adapter.getObjectAsync(zbIdorIeeetoAdId(this.adapter, member.ieee, true));
|
|
361
354
|
const item = groups[memberId] || { groups:[], gep: { }};
|
|
362
355
|
const gep = item.gep[member.epid] || [];
|
|
363
356
|
|
|
@@ -365,12 +358,12 @@ class Commands {
|
|
|
365
358
|
if (!gep.includes(`${groupID}`)) gep.push(`${groupID}`);
|
|
366
359
|
item.gep[member.epid] = gep;
|
|
367
360
|
groups[memberId] = item;
|
|
368
|
-
|
|
369
|
-
member.
|
|
370
|
-
|
|
371
|
-
member.
|
|
372
|
-
|
|
373
|
-
|
|
361
|
+
memberinfo.push({
|
|
362
|
+
ieee:member.ieee,
|
|
363
|
+
epid:member.epid,
|
|
364
|
+
model:member.model,
|
|
365
|
+
device:device? device.common.name:'unknown'
|
|
366
|
+
});
|
|
374
367
|
}
|
|
375
368
|
}
|
|
376
369
|
group.memberinfo = memberinfo;
|
|
@@ -379,7 +372,7 @@ class Commands {
|
|
|
379
372
|
}
|
|
380
373
|
}
|
|
381
374
|
|
|
382
|
-
async fillInfo(device, device_stateDefs, all_states) {
|
|
375
|
+
async fillInfo(device, device_stateDefs, all_states, models) {
|
|
383
376
|
device.statesDef = (device_stateDefs || []).filter(stateDef => {
|
|
384
377
|
const sid = stateDef._id.replace(this.adapter.namespace + '.', '');
|
|
385
378
|
const names = sid.split('.');
|
|
@@ -403,7 +396,25 @@ class Commands {
|
|
|
403
396
|
});
|
|
404
397
|
|
|
405
398
|
const id = getZbId(device._id);
|
|
406
|
-
|
|
399
|
+
const entity = await this.zbController.resolveEntity(id)
|
|
400
|
+
|
|
401
|
+
device.info = this.buildDeviceInfo(entity);
|
|
402
|
+
|
|
403
|
+
const UID = models.UIDbyModel[device.info?.mapped?.model || 'unknown'] || `m_${Object.keys(models.UIDbyModel).length}`;
|
|
404
|
+
if (models.byUID.hasOwnProperty(UID)) {
|
|
405
|
+
models.byUID[UID].devices.push(device);
|
|
406
|
+
}
|
|
407
|
+
else {
|
|
408
|
+
models.byUID[UID] = {
|
|
409
|
+
model:device.info.mapped,
|
|
410
|
+
availableOptions : [...device.info?.mapped?.options || [], ...['use_legacy_model']],
|
|
411
|
+
setOptions: this.adapter.stController.localConfig.getByModel(device.info?.mapped?.model || 'unknown') || [],
|
|
412
|
+
devices: [device],
|
|
413
|
+
}
|
|
414
|
+
if (!models.byUID[UID].model.type)
|
|
415
|
+
models.byUID[UID].model.type = 'Group';
|
|
416
|
+
models.UIDbyModel[device.info?.mapped?.model || 'unknown'] = UID;
|
|
417
|
+
}
|
|
407
418
|
// check configuration
|
|
408
419
|
try {
|
|
409
420
|
if (device.info) {
|
|
@@ -412,26 +423,38 @@ class Commands {
|
|
|
412
423
|
[device.info.device, device.info.mapped],
|
|
413
424
|
);
|
|
414
425
|
if (result.length > 0) device.isConfigured = !result[0];
|
|
415
|
-
|
|
426
|
+
device.paired = true;
|
|
427
|
+
} else device.paired = false;
|
|
416
428
|
} catch (error) {
|
|
417
429
|
this.warn('error calling shouldConfigure: ' + error && error.message ? error.message : 'no error message');
|
|
418
430
|
}
|
|
419
|
-
device.paired = !!device.info;
|
|
420
|
-
|
|
421
|
-
|
|
422
431
|
}
|
|
423
432
|
|
|
424
433
|
buildDeviceInfo(device) {
|
|
434
|
+
function getKey(object, value) {
|
|
435
|
+
try {
|
|
436
|
+
for (const key of Object.keys(object)) {
|
|
437
|
+
if (object[key] == value) {
|
|
438
|
+
return key;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
catch {
|
|
443
|
+
return undefined;
|
|
444
|
+
}
|
|
445
|
+
return undefined;
|
|
446
|
+
|
|
447
|
+
}
|
|
425
448
|
const rv = {};
|
|
426
449
|
try {
|
|
427
450
|
rv.device = {
|
|
428
451
|
modelZigbee:device.device.modelID,
|
|
429
452
|
type:device.device.type,
|
|
430
|
-
ieee:device.device.ieeeAddr,
|
|
431
|
-
nwk:device.device.networkAddress,
|
|
453
|
+
ieee:device.device.ieeeAddr || device.device.groupID,
|
|
454
|
+
nwk:device.device.networkAddress || 0,
|
|
432
455
|
manuf_id:device.device.maufacturerID,
|
|
433
456
|
manuf_name:device.device.manufacturerName,
|
|
434
|
-
manufacturer:device.mapped
|
|
457
|
+
manufacturer:device.mapped?.vendor,
|
|
435
458
|
power:device.device.powerSource,
|
|
436
459
|
app_version:device.device.applicationVersion,
|
|
437
460
|
hard_version:device.device.hardwareVersion,
|
|
@@ -446,32 +469,40 @@ class Commands {
|
|
|
446
469
|
const ep = device.endpoints[ep_idx];
|
|
447
470
|
rv.endpoints.push({
|
|
448
471
|
ID:ep.ID,
|
|
472
|
+
epName: device.mapped?.endpoint ? getKey(device.mapped?.endpoint(device), ep.ID) : ep.ID,
|
|
449
473
|
profile:ep.profileID,
|
|
450
474
|
input_clusters:ep.inputClusters,
|
|
451
475
|
output_clusters:ep.outputClusters,
|
|
452
476
|
})
|
|
477
|
+
if (ep.inputClusters.includes(4)) rv.device.isGroupable = true;
|
|
453
478
|
}
|
|
454
479
|
if (device.mapped) {
|
|
455
480
|
rv.mapped = {
|
|
456
481
|
model:device.mapped.model,
|
|
482
|
+
type:device.device.type,
|
|
457
483
|
description:device.mapped.description,
|
|
484
|
+
hasLegacyDef:statesMapping.hasLegacyDevice(device.mapped.model),
|
|
458
485
|
//fingerprint:JSON.stringify(device.mapped.fingerprint),
|
|
459
486
|
vendor:device.mapped.vendor,
|
|
460
487
|
hasOnEvent:device.mapped.onEvent != undefined,
|
|
461
488
|
hasConfigure:device.mapped.configure != undefined,
|
|
489
|
+
icon:`img/${device.mapped.model.replace(/\//g, '-')}.png`,
|
|
490
|
+
legacyIcon: statesMapping.getIconforLegacyModel(device.mapped.model),
|
|
462
491
|
options:[],
|
|
463
492
|
}
|
|
464
493
|
if (device.mapped.options && typeof (device.mapped.options == 'object')) {
|
|
465
|
-
|
|
494
|
+
rv.mapped.optionExposes = device.mapped.options;
|
|
466
495
|
for (const option of device.mapped.options) {
|
|
467
|
-
if (option.name)
|
|
496
|
+
if (option.name) {
|
|
468
497
|
rv.mapped.options.push(option.name);
|
|
498
|
+
}
|
|
469
499
|
}
|
|
470
500
|
}
|
|
471
501
|
}
|
|
472
502
|
else {
|
|
473
503
|
rv.mapped = {
|
|
474
504
|
model:device.name,
|
|
505
|
+
type: device.device.type,
|
|
475
506
|
description:device.name,
|
|
476
507
|
vendor:'not set',
|
|
477
508
|
hasOnEvent: false,
|
|
@@ -501,19 +532,18 @@ class Commands {
|
|
|
501
532
|
paired: true,
|
|
502
533
|
info: this.buildDeviceInfo(device),
|
|
503
534
|
native: { id: device.device.ieeeAddr.substring(2) },
|
|
504
|
-
mapped : {},
|
|
535
|
+
mapped : { model: client.modelID || client.type || 'NotSet' },
|
|
505
536
|
statesDev: [],
|
|
506
537
|
}
|
|
507
|
-
if (device.
|
|
538
|
+
if (device.name === 'Coordinator') {
|
|
508
539
|
coordinatorData.icon = 'zigbee.png';
|
|
509
|
-
coordinatorData.common = { name: undefined, type: undefined };
|
|
510
|
-
} else {
|
|
511
540
|
coordinatorData.common = { name: 'Coordinator', type: 'Coordinator' };
|
|
541
|
+
} else {
|
|
542
|
+
coordinatorData.common = { name: device.mapped?.model || 'unknown', type: device.type };
|
|
512
543
|
coordinatorData.icon= 'img/unknown.png';
|
|
513
544
|
}
|
|
514
545
|
devices.push(coordinatorData);
|
|
515
546
|
}
|
|
516
|
-
|
|
517
547
|
}
|
|
518
548
|
|
|
519
549
|
async getDevices(from, command, id, callback) {
|
|
@@ -522,13 +552,14 @@ class Commands {
|
|
|
522
552
|
this.adapter.sendTo(from, command, {error: 'No active connection to Zigbee Hardware!'}, callback);
|
|
523
553
|
return;
|
|
524
554
|
}
|
|
525
|
-
const
|
|
555
|
+
const roomsEnum = await this.adapter.getEnumsAsync('enum.rooms') || {};
|
|
526
556
|
const deviceObjects = (id ? [await this.adapter.getObjectAsync(id)] : await this.adapter.getDevicesAsync());
|
|
527
557
|
const all_states = id ? await this.adapter.getStatesAsync(id + '.*') : await this.adapter.getStatesAsync('*');
|
|
528
558
|
const all_stateDefs = id ? await this.adapter.getStatesOfAsync(id) : await this.adapter.getStatesOfAsync();
|
|
529
559
|
const illegalDevices = [];
|
|
530
560
|
const groups = {};
|
|
531
561
|
const PromiseChain = [];
|
|
562
|
+
const models = { byUID : {}, UIDbyModel: {} };
|
|
532
563
|
for (const devInfo of deviceObjects) {
|
|
533
564
|
if (devInfo._id.indexOf('group') > -1) {
|
|
534
565
|
PromiseChain.push(this.handleGroupforInfo(devInfo, groups));
|
|
@@ -537,29 +568,29 @@ class Commands {
|
|
|
537
568
|
const modelDesc = statesMapping.findModel(devInfo.common.type);
|
|
538
569
|
devInfo.icon = (modelDesc && modelDesc.icon) ? modelDesc.icon : 'img/unknown.png';
|
|
539
570
|
devInfo.vendor = modelDesc ? modelDesc.vendor : '';
|
|
540
|
-
|
|
541
|
-
devInfo.legacyIcon = (legacyDesc && legacyDesc.icon) ? legacyDesc.icon : undefined;
|
|
571
|
+
devInfo.legacyIcon = statesMapping.getIconforLegacyModel(devInfo.common.type);
|
|
542
572
|
const lq_state = all_states[`${devInfo._id}.link_quality`];
|
|
543
573
|
devInfo.link_quality = lq_state ? lq_state.val : -1;
|
|
544
574
|
devInfo.link_quality_lc = lq_state ? lq_state.lc : undefined;
|
|
545
575
|
const battery_state = all_states[`${devInfo._id}.battery`];
|
|
546
576
|
devInfo.battery = battery_state ? battery_state.val : undefined;
|
|
547
|
-
devInfo.rooms = [];
|
|
548
|
-
for (const room in rooms) {
|
|
549
|
-
if (!rooms.hasOwnProperty(room) ||
|
|
550
|
-
!rooms[room] ||
|
|
551
|
-
!rooms[room].common ||
|
|
552
|
-
!rooms[room].common.members
|
|
553
|
-
) {
|
|
554
|
-
continue;
|
|
555
|
-
}
|
|
556
|
-
if (rooms[room].common.members.includes(devInfo._id)) {
|
|
557
|
-
devInfo.rooms.push(rooms[room].common.name);
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
577
|
|
|
561
578
|
}
|
|
562
|
-
|
|
579
|
+
devInfo.rooms = [];
|
|
580
|
+
const rooms = roomsEnum['enum.rooms'] || {};
|
|
581
|
+
for (const room of Object.keys(rooms)) {
|
|
582
|
+
if (!rooms.hasOwnProperty(room) ||
|
|
583
|
+
!rooms[room] ||
|
|
584
|
+
!rooms[room].common ||
|
|
585
|
+
!rooms[room].common.members
|
|
586
|
+
) {
|
|
587
|
+
continue;
|
|
588
|
+
}
|
|
589
|
+
if (rooms[room].common.members.includes(devInfo._id)) {
|
|
590
|
+
devInfo.rooms.push(rooms[room].common.name);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
PromiseChain.push(this.fillInfo(devInfo, all_stateDefs.filter(item => item._id.startsWith(devInfo._id)),all_states, models));
|
|
563
594
|
}
|
|
564
595
|
if (!id) {
|
|
565
596
|
for (const client of this.zbController.getClientIterator(true)) {
|
|
@@ -580,7 +611,12 @@ class Commands {
|
|
|
580
611
|
|
|
581
612
|
this.debug(`getDevices contains ${deviceObjects.length} Devices`);
|
|
582
613
|
const rv = { devices:deviceObjects, inLog:this.adapter.deviceDebug.logStatus, }
|
|
583
|
-
if (!id)
|
|
614
|
+
if (!id) {
|
|
615
|
+
rv.deviceDebugData = this.adapter.deviceDebug.collectDebugData();
|
|
616
|
+
rv.localOverrides = this.adapter.stController.localConfig.localData;
|
|
617
|
+
rv.models = models.byUID;
|
|
618
|
+
}
|
|
619
|
+
|
|
584
620
|
if (this.stController) {
|
|
585
621
|
rv.clean = this.stController.CleanupRequired();
|
|
586
622
|
rv.errors = this.stController.getStashedErrors();
|
|
@@ -682,32 +718,43 @@ class Commands {
|
|
|
682
718
|
}
|
|
683
719
|
}
|
|
684
720
|
|
|
685
|
-
deleteZigbeeDevice(from, command, msg, callback) {
|
|
721
|
+
async deleteZigbeeDevice(from, command, msg, callback) {
|
|
686
722
|
if (this.zbController && this.zbController.herdsmanStarted && this.stController) {
|
|
687
723
|
this.debug(`deleteZigbeeDevice message: ${JSON.stringify(msg)}`);
|
|
688
724
|
const id = msg.id;
|
|
689
725
|
const force = msg.force;
|
|
690
|
-
const sysid = id.replace(this.adapter.namespace + '.', '0x')
|
|
726
|
+
const sysid = id.startsWith(this.adapter.namespace) ? id.replace(this.adapter.namespace + '.', '0x') : `0x${id}`;
|
|
691
727
|
const devId = id.replace(this.adapter.namespace + '.', '');
|
|
692
728
|
this.debug(`deleteZigbeeDevice sysid: ${sysid}`);
|
|
693
729
|
const dev = this.zbController.getDevice(sysid);
|
|
694
730
|
if (!dev) {
|
|
731
|
+
|
|
695
732
|
this.info(`Attempted to delete device ${devId} - the device is not known to the zigbee controller.`);
|
|
696
|
-
this.stController.deleteObj(devId
|
|
697
|
-
|
|
733
|
+
const err = await this.stController.deleteObj(devId);
|
|
734
|
+
if (err != '') this.adapter.sendTo(from, command, {errror:err}, callback);
|
|
735
|
+
else this.adapter.sendTo(from, command, {}, callback);
|
|
698
736
|
return;
|
|
699
737
|
}
|
|
700
738
|
this.info(`${force ? 'Force removing' : 'Gracefully removing '} device ${devId} from the network.`);
|
|
701
|
-
this.zbController.remove(sysid, force, err => {
|
|
739
|
+
this.zbController.remove(sysid, force, async (err) => {
|
|
702
740
|
if (!err) {
|
|
703
741
|
this.info('Device removed from the network, deleting objects.')
|
|
704
|
-
|
|
705
|
-
this.
|
|
706
|
-
|
|
742
|
+
if (!force) {
|
|
743
|
+
const err = await this.stController.deleteObj(devId);
|
|
744
|
+
if (err == '') this.adapter.sendTo(from, command, {}, callback);
|
|
745
|
+
else this.adapter.sendTo(from, command, {error:err}, callback);
|
|
746
|
+
}
|
|
747
|
+
this.adapter.sendTo(from, command, {}, callback);
|
|
748
|
+
this.adapter.stController.localConfig.removeLocalData()
|
|
707
749
|
} else {
|
|
708
750
|
this.adapter.sendTo(from, command, {error: err}, callback);
|
|
709
751
|
}
|
|
710
752
|
});
|
|
753
|
+
if (force) {
|
|
754
|
+
const err = await this.stController.deleteObj(devId);
|
|
755
|
+
if (err != '') this.adapter.sendTo(from, command, {errror:err}, callback);
|
|
756
|
+
else this.adapter.sendTo(from, command, {}, callback);
|
|
757
|
+
}
|
|
711
758
|
} else {
|
|
712
759
|
this.adapter.sendTo(from, command, {error: 'No active connection to Zigbee Hardware!'}, callback);
|
|
713
760
|
}
|
|
@@ -802,43 +849,47 @@ class Commands {
|
|
|
802
849
|
}
|
|
803
850
|
}
|
|
804
851
|
|
|
805
|
-
async
|
|
852
|
+
async updateConfigItems(from, command, msg, callback) {
|
|
806
853
|
if (this.stController) {
|
|
807
|
-
this.debug(`
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
if (entity && !entity.mapped) {
|
|
811
|
-
this.warn('unable to set local Override for device whithout mapped model');
|
|
854
|
+
this.debug(`updateConfigItems : ${JSON.stringify(msg)}`);
|
|
855
|
+
if (msg == {}) {
|
|
856
|
+
this.adapter.sendTo(from, command, {}, callback);
|
|
812
857
|
return;
|
|
813
858
|
}
|
|
814
|
-
|
|
859
|
+
const target = msg.target ? msg.target.replace(`${this.adapter.namespace}.`, '') : '';
|
|
860
|
+
const entity = await this.zbController.resolveEntity(target);
|
|
861
|
+
if (msg.data);
|
|
815
862
|
{
|
|
816
863
|
for (const prop in msg.data) {
|
|
817
864
|
if (prop==='options') {
|
|
818
865
|
// we need to trigger the option change
|
|
819
|
-
|
|
820
|
-
const
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
}
|
|
827
|
-
for (const key of allKeys) {
|
|
828
|
-
if (changed_from.hasOwnProperty(key) && changed_to.hasOwnProperty(key) && changed_from[key] == changed_to[key])
|
|
829
|
-
{
|
|
830
|
-
delete changed_from[key];
|
|
831
|
-
delete changed_to[key];
|
|
866
|
+
// first: retrieve the global options.
|
|
867
|
+
const newOptions = {};
|
|
868
|
+
const globalOptions = this.stController.localConfig.getLocalOverride(target, entity?.mapped?.model || '', prop, true)?.options;
|
|
869
|
+
if (globalOptions) {
|
|
870
|
+
for (const key of Object.keys(entity.options)) {
|
|
871
|
+
if (globalOptions[key] != undefined)
|
|
872
|
+
newOptions[key] = globalOptions[key];
|
|
832
873
|
}
|
|
833
874
|
}
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
875
|
+
for (const key of Object.keys(msg.data.options)) {
|
|
876
|
+
newOptions[key]= msg.data.options[key];
|
|
877
|
+
}
|
|
878
|
+
if (entity && entity.device) {
|
|
879
|
+
this.zbController.callExtensionMethod(
|
|
880
|
+
'onZigbeeEvent',
|
|
881
|
+
[{'device': entity.device, 'type': 'deviceOptionsChanged', from: entity.options, to:newOptions || {}, }, entity.mapped]);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
this.warn(`enumerating data: ${JSON.stringify(prop)}`);
|
|
885
|
+
let val = msg.data[prop];
|
|
886
|
+
if (typeof val === 'string') {
|
|
887
|
+
val = val.trim();
|
|
888
|
+
if (val.length < 1) val = '##REMOVE##';
|
|
838
889
|
}
|
|
839
|
-
this.
|
|
840
|
-
await this.stController.localConfig.updateLocalOverride(target, (entity ? entity.mapped.model : 'group'), prop, msg.data[prop], msg.global);
|
|
890
|
+
await this.stController.localConfig.updateLocalOverride(target, target, prop, val, msg.global);
|
|
841
891
|
}
|
|
892
|
+
await this.stController.localConfig.retainData();
|
|
842
893
|
}
|
|
843
894
|
try {
|
|
844
895
|
if (entity) {
|
|
@@ -846,8 +897,12 @@ class Commands {
|
|
|
846
897
|
this.stController.updateDev(target, entity.mapped.model, entity.mapped.model, () => {this.adapter.sendTo(from, command, {}, callback)});
|
|
847
898
|
}
|
|
848
899
|
else {
|
|
849
|
-
|
|
850
|
-
this.
|
|
900
|
+
// try to see if it is a model -> find the devices for that model
|
|
901
|
+
const devicesFromObjects = (await this.adapter.getDevicesAsync()).filter(item => item.common.type === target).map((item) => item.native.id);
|
|
902
|
+
for (const device of devicesFromObjects) {
|
|
903
|
+
await this.stController.updateDev(device, target, target);
|
|
904
|
+
}
|
|
905
|
+
|
|
851
906
|
}
|
|
852
907
|
}
|
|
853
908
|
catch (error) {
|
|
@@ -875,6 +930,9 @@ class Commands {
|
|
|
875
930
|
//const targetModel = msg.model ? msg.model : '';
|
|
876
931
|
}
|
|
877
932
|
else {
|
|
933
|
+
if (msg.getAllData) {
|
|
934
|
+
this.adapter.sendTo(from, command, this.stController.localConfig.localData, callback);
|
|
935
|
+
}
|
|
878
936
|
rv.error = `missing data in message ${JSON.stringify(msg)}`;
|
|
879
937
|
}
|
|
880
938
|
}
|
package/lib/devices.js
CHANGED
|
@@ -3165,9 +3165,16 @@ function setLegacyDevices(legacyModels) {
|
|
|
3165
3165
|
}
|
|
3166
3166
|
|
|
3167
3167
|
function getIconforLegacyModel(model) {
|
|
3168
|
+
const mid = findModel(model, true);
|
|
3169
|
+
if (!mid) return undefined;
|
|
3170
|
+
return mid.icon;
|
|
3168
3171
|
|
|
3169
3172
|
}
|
|
3170
3173
|
|
|
3174
|
+
function hasLegacyDevice(model) {
|
|
3175
|
+
return Boolean(findModel(model, true));
|
|
3176
|
+
}
|
|
3177
|
+
|
|
3171
3178
|
module.exports = {
|
|
3172
3179
|
devices,
|
|
3173
3180
|
legacy_devices,
|
|
@@ -3183,5 +3190,7 @@ module.exports = {
|
|
|
3183
3190
|
findModel,
|
|
3184
3191
|
fillDevicesForLegacy,
|
|
3185
3192
|
pairedLegacyDevices,
|
|
3186
|
-
setLegacyDevices
|
|
3193
|
+
setLegacyDevices,
|
|
3194
|
+
hasLegacyDevice,
|
|
3195
|
+
getIconforLegacyModel,
|
|
3187
3196
|
};
|
package/lib/exclude.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
const devicedefinitions = require('./devices');
|
|
3
|
+
const utils = require('./utils');
|
|
3
4
|
|
|
4
5
|
class Exclude {
|
|
5
6
|
constructor(adapter) {
|
|
@@ -126,7 +127,7 @@ class Exclude {
|
|
|
126
127
|
const devices = this.zbController.getClients();
|
|
127
128
|
const excludables = [];
|
|
128
129
|
for (const device of devices) {
|
|
129
|
-
const obj = await this.adapter.getObjectAsync(device.ieeeAddr.substr(2));
|
|
130
|
+
const obj = await this.adapter.getObjectAsync(utils.zbIdorIeeetoAdId(this.adapter, device.ieeeAddr, false));//device.ieeeAddr.substr(2));
|
|
130
131
|
if (obj && obj.common && obj.common.type) {
|
|
131
132
|
excludables.push(obj.common.tyoe);
|
|
132
133
|
}
|
package/lib/exposes.js
CHANGED
|
@@ -259,7 +259,7 @@ function createFromExposes(model, def, device, log) {
|
|
|
259
259
|
|
|
260
260
|
// maybee here check manufacturerName for tuya devices
|
|
261
261
|
if (typeof def.exposes == 'function') {
|
|
262
|
-
const expFunction = def.exposes(device === undefined ? {isDummyDevice: true} : device, {}); // maybee here check manufacturerName for tuya devices
|
|
262
|
+
const expFunction = def.exposes((device === undefined || device === null) ? {isDummyDevice: true} : device, {}); // maybee here check manufacturerName for tuya devices
|
|
263
263
|
for (const expose of expFunction) {
|
|
264
264
|
genStateFromExpose(expose);
|
|
265
265
|
}
|
|
@@ -344,7 +344,22 @@ function createFromExposes(model, def, device, log) {
|
|
|
344
344
|
epname: expose.endpoint,
|
|
345
345
|
setattr: 'brightness',
|
|
346
346
|
}, prop.access);
|
|
347
|
-
pushToStates(statesDefs.brightness_move, prop.access);
|
|
347
|
+
//pushToStates(statesDefs.brightness_move, prop.access);
|
|
348
|
+
pushToStates({
|
|
349
|
+
id: 'brightness_move',
|
|
350
|
+
prop: 'brightness_move',
|
|
351
|
+
name: `Dimming ${expose.endpoint ? expose.endpoint : ''}`.trim(),
|
|
352
|
+
icon: undefined,
|
|
353
|
+
role: 'state',
|
|
354
|
+
write: true,
|
|
355
|
+
read: false,
|
|
356
|
+
type: 'number',
|
|
357
|
+
min: -50, // ignore expose.value_min
|
|
358
|
+
max: 50, // ignore expose.value_max
|
|
359
|
+
epname: expose.endpoint,
|
|
360
|
+
setattr: 'brightness_move',
|
|
361
|
+
}, prop.access);
|
|
362
|
+
//pushToStates(statesDefs.brightness_move, prop.access);
|
|
348
363
|
break;
|
|
349
364
|
}
|
|
350
365
|
case 'color_temp': {
|
|
@@ -801,7 +816,7 @@ function createFromExposes(model, def, device, log) {
|
|
|
801
816
|
|
|
802
817
|
case 'climate':
|
|
803
818
|
for (const prop of expose.features) {
|
|
804
|
-
switch (prop.name) {
|
|
819
|
+
/* switch (prop.name) {
|
|
805
820
|
case 'away_mode':
|
|
806
821
|
pushToStates(statesDefs.climate_away_mode, prop.access);
|
|
807
822
|
break;
|
|
@@ -814,7 +829,8 @@ function createFromExposes(model, def, device, log) {
|
|
|
814
829
|
default:
|
|
815
830
|
pushToStates(genState(prop), prop.access);
|
|
816
831
|
break;
|
|
817
|
-
}
|
|
832
|
+
}*/
|
|
833
|
+
pushToStates(genState(prop), prop.access);
|
|
818
834
|
}
|
|
819
835
|
break;
|
|
820
836
|
|
package/lib/groups.js
CHANGED
|
@@ -629,7 +629,7 @@ class Groups {
|
|
|
629
629
|
const entity = await this.zbController.resolveEntity(member.ieee, member.epid);
|
|
630
630
|
let epname = undefined;
|
|
631
631
|
if (entity && entity.mapped && entity.mapped.endpoint) {
|
|
632
|
-
const epnames = entity.mapped.endpoint();
|
|
632
|
+
const epnames = entity.mapped.endpoint(entity);
|
|
633
633
|
for (const key in epnames) {
|
|
634
634
|
if (epnames[key] == member.epid) {
|
|
635
635
|
epname = key;
|
|
@@ -657,7 +657,7 @@ class Groups {
|
|
|
657
657
|
this.rebuildGroupMemberStateList(j, memberInfo);
|
|
658
658
|
}
|
|
659
659
|
|
|
660
|
-
const name = this.stController.localConfig.NameForId(id, 'group', groups[j]);
|
|
660
|
+
const name = await this.stController.localConfig.NameForId(id, 'group', groups[j]);
|
|
661
661
|
const icon = this.stController.localConfig.IconForId(id, 'group', await this.stController.getDefaultGroupIcon(id));
|
|
662
662
|
chain.push(new Promise(resolve => {
|
|
663
663
|
const isActive = false;
|