iobroker.zigbee 3.1.4 → 3.1.5
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 +5 -0
- package/admin/admin.js +745 -632
- package/admin/index_m.html +26 -150
- package/admin/tab_m.html +137 -238
- package/io-package.json +15 -15
- package/lib/DeviceDebug.js +0 -1
- package/lib/commands.js +69 -35
- package/lib/statescontroller.js +1 -1
- package/lib/zbDeviceEvent.js +0 -1
- package/lib/zigbeecontroller.js +11 -7
- package/main.js +14 -21
- package/package.json +3 -3
package/lib/commands.js
CHANGED
|
@@ -176,6 +176,9 @@ class Commands {
|
|
|
176
176
|
case 'downloadIcons':
|
|
177
177
|
this.triggerIconDownload(obj);
|
|
178
178
|
break;
|
|
179
|
+
case 'aliveCheck':
|
|
180
|
+
this.adapter.sendTo(obj.from, obj.command, {msg:'success'}, obj.callback);
|
|
181
|
+
break;
|
|
179
182
|
default:
|
|
180
183
|
this.debug(`Commands: Command ${obj.command} is unknown`);
|
|
181
184
|
//this.adapter.sendTo(obj.from, obj.command, obj.message, obj.callback);
|
|
@@ -355,7 +358,13 @@ class Commands {
|
|
|
355
358
|
if (member && typeof member.ieee === 'string') {
|
|
356
359
|
const memberId = member.ieee.substr(2);
|
|
357
360
|
const device = await this.adapter.getObjectAsync(`${this.adapter.namespace}.${member.ieee.substr(2)}`);
|
|
358
|
-
|
|
361
|
+
const item = groups[memberId] || { groups:[], gep: { }};
|
|
362
|
+
const gep = item.gep[member.epid] || [];
|
|
363
|
+
|
|
364
|
+
if (!item.groups.includes(groupID)) item.groups.push(groupID);
|
|
365
|
+
if (!gep.includes(`${groupID}`)) gep.push(`${groupID}`);
|
|
366
|
+
item.gep[member.epid] = gep;
|
|
367
|
+
groups[memberId] = item;
|
|
359
368
|
if (device) {
|
|
360
369
|
member.device = device.common.name;
|
|
361
370
|
} else {
|
|
@@ -422,7 +431,7 @@ class Commands {
|
|
|
422
431
|
nwk:device.device.networkAddress,
|
|
423
432
|
manuf_id:device.device.maufacturerID,
|
|
424
433
|
manuf_name:device.device.manufacturerName,
|
|
425
|
-
manufacturer:device.mapped.vendor,
|
|
434
|
+
manufacturer:device.mapped ? device.mapped.vendor : '',
|
|
426
435
|
power:device.device.powerSource,
|
|
427
436
|
app_version:device.device.applicationVersion,
|
|
428
437
|
hard_version:device.device.hardwareVersion,
|
|
@@ -442,25 +451,40 @@ class Commands {
|
|
|
442
451
|
output_clusters:ep.outputClusters,
|
|
443
452
|
})
|
|
444
453
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
454
|
+
if (device.mapped) {
|
|
455
|
+
rv.mapped = {
|
|
456
|
+
model:device.mapped.model,
|
|
457
|
+
description:device.mapped.description,
|
|
458
|
+
//fingerprint:JSON.stringify(device.mapped.fingerprint),
|
|
459
|
+
vendor:device.mapped.vendor,
|
|
460
|
+
hasOnEvent:device.mapped.onEvent != undefined,
|
|
461
|
+
hasConfigure:device.mapped.configure != undefined,
|
|
462
|
+
options:[],
|
|
463
|
+
}
|
|
464
|
+
if (device.mapped.options && typeof (device.mapped.options == 'object')) {
|
|
465
|
+
const optionDesc = [];
|
|
466
|
+
for (const option of device.mapped.options) {
|
|
467
|
+
if (option.name)
|
|
468
|
+
rv.mapped.options.push(option.name);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
else {
|
|
473
|
+
rv.mapped = {
|
|
474
|
+
model:device.name,
|
|
475
|
+
description:device.name,
|
|
476
|
+
vendor:'not set',
|
|
477
|
+
hasOnEvent: false,
|
|
478
|
+
hasConfigure: false,
|
|
479
|
+
options:[],
|
|
459
480
|
}
|
|
460
481
|
}
|
|
461
482
|
}
|
|
462
483
|
catch (error) {
|
|
463
|
-
|
|
484
|
+
if (device && device.name === 'Coordinator') return rv;
|
|
485
|
+
const dev = device ? device.device || {} : {}
|
|
486
|
+
const msg = device ? `device ${device.name} (${dev.ieeeAddr}, NWK ${dev.networkAddres}, ID: ${dev.ID})` : 'undefined device';
|
|
487
|
+
this.warn(`Error ${error && error.message ? error.message + ' ' : ''}building device info for ${msg}`);
|
|
464
488
|
}
|
|
465
489
|
return rv;
|
|
466
490
|
}
|
|
@@ -472,17 +496,22 @@ class Commands {
|
|
|
472
496
|
}
|
|
473
497
|
const exists = devices.find((dev) => (dev._id && device.device.ieeeAddr === getZbId(dev._id)));
|
|
474
498
|
if (!exists) {
|
|
475
|
-
|
|
476
|
-
_id: device.device.ieeeAddr
|
|
477
|
-
icon: 'img/unknown.png',
|
|
499
|
+
const coordinatorData = {
|
|
500
|
+
_id : `${this.adapter.namespace}.${device.device.ieeeAddr.substring(2)}`,
|
|
478
501
|
paired: true,
|
|
479
502
|
info: this.buildDeviceInfo(device),
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
503
|
+
native: { id: device.device.ieeeAddr.substring(2) },
|
|
504
|
+
mapped : {},
|
|
505
|
+
statesDev: [],
|
|
506
|
+
}
|
|
507
|
+
if (device.device.name === 'Coordinator') {
|
|
508
|
+
coordinatorData.icon = 'zigbee.png';
|
|
509
|
+
coordinatorData.common = { name: undefined, type: undefined };
|
|
510
|
+
} else {
|
|
511
|
+
coordinatorData.common = { name: 'Coordinator', type: 'Coordinator' };
|
|
512
|
+
coordinatorData.icon= 'img/unknown.png';
|
|
513
|
+
}
|
|
514
|
+
devices.push(coordinatorData);
|
|
486
515
|
}
|
|
487
516
|
|
|
488
517
|
}
|
|
@@ -533,7 +562,7 @@ class Commands {
|
|
|
533
562
|
PromiseChain.push(this.fillInfo(devInfo, all_stateDefs.filter(item => item._id.startsWith(devInfo._id)),all_states));
|
|
534
563
|
}
|
|
535
564
|
if (!id) {
|
|
536
|
-
for (const client of this.zbController.getClientIterator(
|
|
565
|
+
for (const client of this.zbController.getClientIterator(true)) {
|
|
537
566
|
PromiseChain.push(this.appendDevicesWithoutObjects(deviceObjects,client))
|
|
538
567
|
}
|
|
539
568
|
}
|
|
@@ -543,7 +572,8 @@ class Commands {
|
|
|
543
572
|
for (const groupmember in groups) {
|
|
544
573
|
const device = deviceObjects.find(dev => (groupmember === dev.native.id));
|
|
545
574
|
if (device) {
|
|
546
|
-
device.groups = groups[groupmember];
|
|
575
|
+
device.groups = groups[groupmember].groups;
|
|
576
|
+
device.groups_by_ep = groups[groupmember].gep;
|
|
547
577
|
}
|
|
548
578
|
}
|
|
549
579
|
|
|
@@ -777,7 +807,6 @@ class Commands {
|
|
|
777
807
|
this.debug(`updateLocalConfigItems : ${JSON.stringify(msg)}`);
|
|
778
808
|
const target = msg.target.replace(`${this.adapter.namespace}.`, '');
|
|
779
809
|
const entity = await this.zbController.resolveEntity(target);
|
|
780
|
-
//this.warn('entity for ' + target + ' is '+ JSON.stringify(entity))
|
|
781
810
|
if (entity && !entity.mapped) {
|
|
782
811
|
this.warn('unable to set local Override for device whithout mapped model');
|
|
783
812
|
return;
|
|
@@ -811,13 +840,18 @@ class Commands {
|
|
|
811
840
|
await this.stController.localConfig.updateLocalOverride(target, (entity ? entity.mapped.model : 'group'), prop, msg.data[prop], msg.global);
|
|
812
841
|
}
|
|
813
842
|
}
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
843
|
+
try {
|
|
844
|
+
if (entity) {
|
|
845
|
+
this.debug('updateLocalConfigItems with Entity');
|
|
846
|
+
this.stController.updateDev(target, entity.mapped.model, entity.mapped.model, () => {this.adapter.sendTo(from, command, {}, callback)});
|
|
847
|
+
}
|
|
848
|
+
else {
|
|
849
|
+
this.debug('updateLocalConfigItems without Entity');
|
|
850
|
+
this.stController.updateDev(target, undefined, 'group',() => {this.adapter.sendTo(from, command, {}, callback)});
|
|
851
|
+
}
|
|
817
852
|
}
|
|
818
|
-
|
|
819
|
-
this.
|
|
820
|
-
this.stController.updateDev(target, undefined, 'group',() => {this.adapter.sendTo(from, command, {}, callback)});
|
|
853
|
+
catch (error) {
|
|
854
|
+
this.adapter.sendTo(from, command, {err: error.message}, callback);
|
|
821
855
|
}
|
|
822
856
|
}
|
|
823
857
|
}
|
package/lib/statescontroller.js
CHANGED
|
@@ -458,7 +458,7 @@ class StatesController extends EventEmitter {
|
|
|
458
458
|
if (stateDesc.id === 'send_payload') {
|
|
459
459
|
try {
|
|
460
460
|
const json_value = JSON.parse(value);
|
|
461
|
-
const payload = {device: deviceId.replace('0x', ''), payload: json_value, model:model, stateModel:stateModel};
|
|
461
|
+
const payload = {device: deviceId.replace('0x', ''), payload: json_value, model:model, stateModel:stateModel, acknowledge:true};
|
|
462
462
|
if (has_elevated_debug) this.emit('device_debug', { ID:debugID, data: { flag: '04' ,payload:value ,states:[{id:stateDesc.id, value:json_value, payload:'none'}], IO:false }});
|
|
463
463
|
|
|
464
464
|
this.emit('send_payload', payload, debugID, has_elevated_debug);
|
package/lib/zbDeviceEvent.js
CHANGED
package/lib/zigbeecontroller.js
CHANGED
|
@@ -676,7 +676,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
676
676
|
endpoint = device.endpoints[0];
|
|
677
677
|
}
|
|
678
678
|
}
|
|
679
|
-
const options = this.adapter.stController.localConfig.getOptions(device.ieeeAddr, mapped.model);
|
|
679
|
+
const options = mapped ? this.adapter.stController.localConfig.getOptions(device.ieeeAddr, mapped.model) : {};
|
|
680
680
|
return {
|
|
681
681
|
type: 'device',
|
|
682
682
|
device,
|
|
@@ -731,13 +731,13 @@ class ZigbeeController extends EventEmitter {
|
|
|
731
731
|
}
|
|
732
732
|
|
|
733
733
|
try {
|
|
734
|
-
if (this.
|
|
734
|
+
if (this.herdsmanStarted) {
|
|
735
735
|
await this.permitJoin(0);
|
|
736
736
|
await this.herdsman.stop();
|
|
737
|
-
this.
|
|
737
|
+
this.herdsmanStarted = false;
|
|
738
738
|
this.info('zigbecontroller stopped successfully');
|
|
739
739
|
}
|
|
740
|
-
this.info('zigbecontroller stopped successfully - ZH was not running');
|
|
740
|
+
else this.info('zigbecontroller stopped successfully - ZH was not running');
|
|
741
741
|
} catch (error) {
|
|
742
742
|
this.sendError(error);
|
|
743
743
|
if (this.herdsmanStarted) {
|
|
@@ -876,10 +876,11 @@ class ZigbeeController extends EventEmitter {
|
|
|
876
876
|
async handleDeviceAnnounce(message) {
|
|
877
877
|
if (this.debugActive) this.debug('handleDeviceAnnounce', message);
|
|
878
878
|
const entity = await this.resolveEntity(message.device || message.ieeeAddr);
|
|
879
|
-
const friendlyName = entity.name;
|
|
880
|
-
|
|
879
|
+
const friendlyName = entity ? entity.name : message.ieeeAddr ? message.ieeeAddr : message.device && message.device.ieeeAddr ? message.device.ieeeAddr : 'without data';
|
|
881
880
|
|
|
882
881
|
this.emit('pairing', `Device '${friendlyName}' announced itself`);
|
|
882
|
+
if (!entity) return;
|
|
883
|
+
|
|
883
884
|
if (this.adapter.stController.checkDebugDevice(friendlyName)) {
|
|
884
885
|
this.emit('device_debug', {ID: Date.now(), data: {flag:'da', states:[{id: '--', value:'--', payload:message}] , IO:true} ,message:`Device '${friendlyName}' announced itself`});
|
|
885
886
|
}
|
|
@@ -1386,7 +1387,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
1386
1387
|
// device: name of the device. For a device zigbee.0.0011223344556677 this would be 0011223344556677
|
|
1387
1388
|
// payload: The data to send to the device as JSON object (key/Value pairs)
|
|
1388
1389
|
// endpoint: optional: the endpoint to send the data to, if supported.
|
|
1389
|
-
//
|
|
1390
|
+
// acknowledge: optional: if to update the devices 'send_payload' DP (if present) after successful publish
|
|
1390
1391
|
async publishPayload(payload, debugID, has_elevated_debug) {
|
|
1391
1392
|
let payloadObj = {};
|
|
1392
1393
|
if (typeof payload === 'string') {
|
|
@@ -1450,6 +1451,9 @@ class ZigbeeController extends EventEmitter {
|
|
|
1450
1451
|
}
|
|
1451
1452
|
try {
|
|
1452
1453
|
await this.publishFromState(`0x${payload.device}`, payload.model, payload.stateModel, stateList, payload.options, debugID, has_elevated_debug);
|
|
1454
|
+
if (payload.acknowledge) {
|
|
1455
|
+
this.emit('acknowledge_state', payload.device, payload.model, { id:'send_payload' }, undefined);
|
|
1456
|
+
}
|
|
1453
1457
|
return {success: true};
|
|
1454
1458
|
} catch (error) {
|
|
1455
1459
|
this.log.error(`Error ${error.code} on send command to ${payload.device}.` + ` Error: ${error.stack} ` + `Send command to ${payload.device} failed with ` + error);
|
package/main.js
CHANGED
|
@@ -274,7 +274,6 @@ class Zigbee extends utils.Adapter {
|
|
|
274
274
|
|
|
275
275
|
SandboxRequire(sandbox, items) {
|
|
276
276
|
if (!items) return true;
|
|
277
|
-
//let converterLoaded = true;
|
|
278
277
|
for (const item of items) {
|
|
279
278
|
const modulePath = item[2].replace(/['"]/gm, '');
|
|
280
279
|
|
|
@@ -350,9 +349,6 @@ class Zigbee extends utils.Adapter {
|
|
|
350
349
|
converterLoaded = false;
|
|
351
350
|
this.log.error(`converter does not export any converter array, please add 'module.exports' statement to ${mN}`);
|
|
352
351
|
}
|
|
353
|
-
|
|
354
|
-
//fs.writeFileSync(mN+'.tmp', modifiedCode)
|
|
355
|
-
|
|
356
352
|
if (converterLoaded) {
|
|
357
353
|
try {
|
|
358
354
|
this.log.warn('Trying to run sandbox for ' + mN);
|
|
@@ -422,7 +418,6 @@ class Zigbee extends utils.Adapter {
|
|
|
422
418
|
}
|
|
423
419
|
else try {
|
|
424
420
|
await this.zbController.stopHerdsman();
|
|
425
|
-
//this.logToPairing('herdsman stopped !');
|
|
426
421
|
this.sendTo(from, command, { status:true }, callback);
|
|
427
422
|
} catch (error) {
|
|
428
423
|
this.sendTo(from, command, { status:true, error }, callback);
|
|
@@ -458,12 +453,6 @@ class Zigbee extends utils.Adapter {
|
|
|
458
453
|
this.setState('info.connection', false, true);
|
|
459
454
|
this.logToPairing(`Failed to start Zigbee: ${error && error.message ? error.message : 'no message given'}`)
|
|
460
455
|
this.log.error(`Failed to start Zigbee: ${error && error.message ? error.message : 'no message given'}`);
|
|
461
|
-
/* if (error.stack) {
|
|
462
|
-
this.log.error(error.stack);
|
|
463
|
-
} else {
|
|
464
|
-
this.log.error(error);
|
|
465
|
-
}
|
|
466
|
-
*/
|
|
467
456
|
this.sendError(error, `Failed to start Zigbee`);
|
|
468
457
|
if (noReconnect) return false;
|
|
469
458
|
|
|
@@ -668,12 +657,18 @@ class Zigbee extends utils.Adapter {
|
|
|
668
657
|
}
|
|
669
658
|
|
|
670
659
|
acknowledgeState(deviceId, model, stateDesc, value) {
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
this.
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
660
|
+
const stateId = (model === 'group' ?
|
|
661
|
+
`${this.namespace}.group_${deviceId}.${stateDesc.id}` :
|
|
662
|
+
`${this.namespace}.${deviceId.replace('0x', '')}.${stateDesc.id}`);
|
|
663
|
+
if (value === undefined) try {
|
|
664
|
+
this.getState(stateId, (err, state) => { if (!err && state.hasOwnProperty('val')) this.setState(stateId, state.val, true)});
|
|
665
|
+
}
|
|
666
|
+
catch (error) {
|
|
667
|
+
this.log.warn(`Error acknowledging ${stateId} without value: ${error && error.message ? error.message : 'no reason given'}`);
|
|
668
|
+
}
|
|
669
|
+
else try { this.setState(stateId, value, true); }
|
|
670
|
+
catch (error) {
|
|
671
|
+
this.log.warn(`Error acknowledging ${stateId} with value ${JSON.stringify(value)}: ${error && error.message ? error.message : 'no reason given'}`);
|
|
677
672
|
}
|
|
678
673
|
}
|
|
679
674
|
|
|
@@ -754,10 +749,8 @@ class Zigbee extends utils.Adapter {
|
|
|
754
749
|
this.log.info('cleaning everything up');
|
|
755
750
|
await this.callPluginMethod('stop');
|
|
756
751
|
if (this.stController) chain.push(this.stController.stop());
|
|
757
|
-
if (this.zbController)
|
|
758
|
-
|
|
759
|
-
}
|
|
760
|
-
Promise.all(chain);
|
|
752
|
+
if (this.zbController) chain.push(this.zbController.stop());
|
|
753
|
+
await Promise.all(chain);
|
|
761
754
|
this.log.info('cleanup successful');
|
|
762
755
|
callback();
|
|
763
756
|
} catch (error) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.zigbee",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.5",
|
|
4
4
|
"author": {
|
|
5
5
|
"name": "Kirov Ilya",
|
|
6
6
|
"email": "kirovilya@gmail.com"
|
|
@@ -37,10 +37,10 @@
|
|
|
37
37
|
"@alcalzone/release-script-plugin-iobroker": "^3.7.2",
|
|
38
38
|
"@alcalzone/release-script-plugin-license": "^3.7.0",
|
|
39
39
|
"@alcalzone/release-script-plugin-manual-review": "^3.7.0",
|
|
40
|
-
"@iobroker/testing": "^5.1.
|
|
40
|
+
"@iobroker/testing": "^5.1.1",
|
|
41
41
|
"chai": "^5.2.1",
|
|
42
42
|
"chai-as-promised": "^7.1.1",
|
|
43
|
-
"eslint": "^9.
|
|
43
|
+
"eslint": "^9.36.0",
|
|
44
44
|
"eslint-config-prettier": "^9.1.0",
|
|
45
45
|
"eslint-plugin-prettier": "^5.5.4",
|
|
46
46
|
"mixin-deep": "^2.0.1",
|