iobroker.zigbee 3.1.2 → 3.1.4
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 +14 -1
- package/admin/admin.js +369 -191
- package/admin/index_m.html +30 -6
- package/admin/tab_m.html +31 -11
- package/io-package.json +42 -39
- package/lib/DeviceDebug.js +25 -2
- package/lib/binding.js +7 -7
- package/lib/commands.js +279 -249
- package/lib/developer.js +1 -1
- package/lib/devices.js +2 -2
- package/lib/exclude.js +1 -1
- package/lib/exposes.js +54 -24
- package/lib/groups.js +26 -28
- package/lib/localConfig.js +8 -8
- package/lib/networkmap.js +10 -2
- package/lib/statescontroller.js +134 -90
- package/lib/zbDelayedAction.js +4 -4
- package/lib/zbDeviceAvailability.js +32 -33
- package/lib/zbDeviceConfigure.js +7 -0
- package/lib/zbDeviceEvent.js +39 -6
- package/lib/zigbeecontroller.js +91 -43
- package/main.js +31 -38
- package/package.json +5 -8
- package/lib/tools.js +0 -55
package/lib/zigbeecontroller.js
CHANGED
|
@@ -11,7 +11,6 @@ const DeviceAvailabilityExt = require('./zbDeviceAvailability');
|
|
|
11
11
|
const DeviceConfigureExt = require('./zbDeviceConfigure');
|
|
12
12
|
const DeviceEventExt = require('./zbDeviceEvent');
|
|
13
13
|
const DelayedActionExt = require('./zbDelayedAction');
|
|
14
|
-
const Groups = require('./groups');
|
|
15
14
|
const utils = require('./utils');
|
|
16
15
|
|
|
17
16
|
const { access, constants } =require('node:fs/promises');
|
|
@@ -75,7 +74,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
75
74
|
this.herdsmanTimeoutRegexp = new RegExp(/(\d+)ms/);
|
|
76
75
|
this.herdsmanLogSettings = {};
|
|
77
76
|
this.debugActive = true;
|
|
78
|
-
this.ListDevicesAtStart =
|
|
77
|
+
this.ListDevicesAtStart = adapter.config.listDevicesAtStart;
|
|
79
78
|
this.deviceQueryActive = [];
|
|
80
79
|
this.storedOptions = undefined;
|
|
81
80
|
this.isConfigured = false;
|
|
@@ -167,7 +166,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
167
166
|
this.herdsmanLogSettings.extendedPanID = utils.byteArrayToString(herdsmanSettings.network.extendedPanID);
|
|
168
167
|
this.herdsman = new ZigbeeHerdsman.Controller(herdsmanSettings, this.adapter.log);
|
|
169
168
|
this.callExtensionMethod('setOptions', [{
|
|
170
|
-
|
|
169
|
+
pingCluster: this.adapter.config.pingCluster,
|
|
171
170
|
startReadDelay: this.adapter.config.readAllAtStart ? this.adapter.config.startReadDelay : 0,
|
|
172
171
|
disableForcedPing: false,
|
|
173
172
|
pingTimeout: 300,
|
|
@@ -283,8 +282,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
283
282
|
// get the model description for the known devices
|
|
284
283
|
const entity = await this.resolveEntity(device);
|
|
285
284
|
if (!entity) {
|
|
286
|
-
this.
|
|
287
|
-
//this.emit('pairing','failed to resolve Entity for ' + device.ieeeAddr)
|
|
285
|
+
this.debug('failed to resolve Entity for ' + device.ieeeAddr);
|
|
288
286
|
continue;
|
|
289
287
|
}
|
|
290
288
|
//await this.adapter.stController.AddModelFromHerdsman(device, entity.mapped.model);
|
|
@@ -305,7 +303,6 @@ class ZigbeeController extends EventEmitter {
|
|
|
305
303
|
` (addr ${entity.device.networkAddress}): ` +
|
|
306
304
|
(entity.mapped ? `${entity.mapped.model} - ${entity.mapped.vendor} ${entity.mapped.description} ` : `Unsupported (model ${entity.device.modelID})`) +
|
|
307
305
|
`(${entity.device.type})`);
|
|
308
|
-
//this.emit('pairing',msg);
|
|
309
306
|
if (this.ListDevicesAtStart) this.info(msg);
|
|
310
307
|
}
|
|
311
308
|
|
|
@@ -536,9 +533,10 @@ class ZigbeeController extends EventEmitter {
|
|
|
536
533
|
return members;
|
|
537
534
|
}
|
|
538
535
|
|
|
539
|
-
getDevice(key) {
|
|
536
|
+
async getDevice(key) {
|
|
540
537
|
try {
|
|
541
|
-
|
|
538
|
+
const dev = await this.herdsman.getDeviceByIeeeAddr(key);
|
|
539
|
+
return dev;
|
|
542
540
|
}
|
|
543
541
|
catch (error) {
|
|
544
542
|
this.error(`getDeviceByIeeeAddr: ${(error && error.message ? error.message : 'no error message')}`);
|
|
@@ -572,7 +570,9 @@ class ZigbeeController extends EventEmitter {
|
|
|
572
570
|
key: key,
|
|
573
571
|
message: 'success'
|
|
574
572
|
}
|
|
575
|
-
if (typeof key === 'object')
|
|
573
|
+
if (typeof key === 'object') {
|
|
574
|
+
return rv;
|
|
575
|
+
}
|
|
576
576
|
if (typeof key === 'number') {
|
|
577
577
|
rv.kind = 'group';
|
|
578
578
|
return rv;
|
|
@@ -602,8 +602,11 @@ class ZigbeeController extends EventEmitter {
|
|
|
602
602
|
return rv;
|
|
603
603
|
}
|
|
604
604
|
|
|
605
|
+
async getGroup(id) {
|
|
606
|
+
return await this.herdsman.getGroupByID(id);
|
|
607
|
+
}
|
|
608
|
+
|
|
605
609
|
async resolveEntity(key, ep) {
|
|
606
|
-
// this.warn('resolve entity with key of tyoe ' + typeof (key));
|
|
607
610
|
try {
|
|
608
611
|
const _key = await this.analyzeKey(key);
|
|
609
612
|
if (_key.message !== 'success') return undefined;
|
|
@@ -615,6 +618,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
615
618
|
device: coordinator,
|
|
616
619
|
endpoint: coordinator.getEndpoint(1),
|
|
617
620
|
name: 'Coordinator',
|
|
621
|
+
options:{}
|
|
618
622
|
};
|
|
619
623
|
}
|
|
620
624
|
if (_key.kind === 'group') {
|
|
@@ -626,18 +630,38 @@ class ZigbeeController extends EventEmitter {
|
|
|
626
630
|
type: 'group',
|
|
627
631
|
mapped: group,
|
|
628
632
|
device: group,
|
|
633
|
+
endpoint: group,
|
|
629
634
|
//group,
|
|
630
635
|
name: `Group ${_key.key}`,
|
|
636
|
+
options: {},
|
|
631
637
|
};
|
|
632
638
|
|
|
633
639
|
}
|
|
634
640
|
//if (_key.kind === 'ieee')
|
|
635
641
|
const device = (_key.kind === 'ieee' ? this.herdsman.getDeviceByIeeeAddr(_key.key) : key);
|
|
642
|
+
if (device && device.model === 'group') {
|
|
643
|
+
return {
|
|
644
|
+
type: 'group',
|
|
645
|
+
mapped: device,
|
|
646
|
+
device,
|
|
647
|
+
endpoint: device,
|
|
648
|
+
name: `Group ${device.groupID}`,
|
|
649
|
+
options:{}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
636
652
|
if (device) {
|
|
637
653
|
const t = Date.now();
|
|
638
654
|
const mapped = await zigbeeHerdsmanConverters.findByDevice(device, false);
|
|
639
|
-
if (!mapped)
|
|
640
|
-
|
|
655
|
+
if (!mapped) {
|
|
656
|
+
if (device.type === 'Coordinator')
|
|
657
|
+
return {
|
|
658
|
+
type: 'device',
|
|
659
|
+
device: device,
|
|
660
|
+
endpoint: device.getEndpoint(1),
|
|
661
|
+
name: 'Coordinator',
|
|
662
|
+
};
|
|
663
|
+
this.warn(`Resolve Entity did not manage to find a mapped device for ${device.ieeeAddr} of type ${device.modelID}`);
|
|
664
|
+
}
|
|
641
665
|
const endpoints = mapped && mapped.endpoint ? mapped.endpoint(device) : null;
|
|
642
666
|
let endpoint;
|
|
643
667
|
if (endpoints && ep != undefined && endpoints[ep]) {
|
|
@@ -652,6 +676,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
652
676
|
endpoint = device.endpoints[0];
|
|
653
677
|
}
|
|
654
678
|
}
|
|
679
|
+
const options = this.adapter.stController.localConfig.getOptions(device.ieeeAddr, mapped.model);
|
|
655
680
|
return {
|
|
656
681
|
type: 'device',
|
|
657
682
|
device,
|
|
@@ -659,10 +684,11 @@ class ZigbeeController extends EventEmitter {
|
|
|
659
684
|
endpoint,
|
|
660
685
|
endpoints: device.endpoints,
|
|
661
686
|
name: device._ieeeAddr,
|
|
687
|
+
options:options
|
|
662
688
|
};
|
|
663
689
|
}
|
|
664
690
|
else {
|
|
665
|
-
this.
|
|
691
|
+
this.debug(`resolve_entity failed for ${JSON.stringify(_key)}`);
|
|
666
692
|
}
|
|
667
693
|
}
|
|
668
694
|
catch (error)
|
|
@@ -705,10 +731,13 @@ class ZigbeeController extends EventEmitter {
|
|
|
705
731
|
}
|
|
706
732
|
|
|
707
733
|
try {
|
|
708
|
-
if (this.HerdsmanStarted)
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
734
|
+
if (this.HerdsmanStarted) {
|
|
735
|
+
await this.permitJoin(0);
|
|
736
|
+
await this.herdsman.stop();
|
|
737
|
+
this.HerdsmanStarted = false;
|
|
738
|
+
this.info('zigbecontroller stopped successfully');
|
|
739
|
+
}
|
|
740
|
+
this.info('zigbecontroller stopped successfully - ZH was not running');
|
|
712
741
|
} catch (error) {
|
|
713
742
|
this.sendError(error);
|
|
714
743
|
if (this.herdsmanStarted) {
|
|
@@ -860,25 +889,26 @@ class ZigbeeController extends EventEmitter {
|
|
|
860
889
|
this.info(`Device '${friendlyName}' announced itself${this.readAtAnnounce ? ', trying to read its status' : ''}`);
|
|
861
890
|
}
|
|
862
891
|
|
|
863
|
-
if (entity.device && entity.device.
|
|
864
|
-
this.
|
|
892
|
+
if (entity.device && entity.device.modelID && entity.device.interviewState != 'SUCCESSFUL') {
|
|
893
|
+
this.info(`ignoring device announcement for ${entity.device.modelID} due to interview state ${entity.device.interviewState}`);
|
|
865
894
|
this.emit('pairing', `device interview state is ${entity.device.interviewState}`)
|
|
866
895
|
return;
|
|
867
896
|
}
|
|
868
897
|
|
|
869
898
|
const networkOpen = this.herdsman.getPermitJoin();
|
|
870
|
-
|
|
899
|
+
/*
|
|
900
|
+
if (networkOpen && entity.device && entity.device.modelID && entity.device.interviewState != 'IN_PROGRESS')
|
|
871
901
|
{
|
|
872
902
|
//entity.device.modelID = entity.device._modelID;
|
|
873
|
-
this.emit('new', entity);
|
|
903
|
+
//this.emit('new', entity);
|
|
874
904
|
return;
|
|
875
905
|
}
|
|
876
|
-
|
|
906
|
+
*/
|
|
877
907
|
try {
|
|
878
908
|
if (entity && entity.mapped) {
|
|
879
909
|
this.callExtensionMethod(
|
|
880
910
|
'onZigbeeEvent',
|
|
881
|
-
[{'device': message.device, 'type': 'deviceAnnounce'}, entity ? entity.mapped : null]);
|
|
911
|
+
[{'device': message.device, 'type': 'deviceAnnounce', options: entity.options || {}}, entity ? entity.mapped : null]);
|
|
882
912
|
this.callExtensionMethod('registerDevicePing', [message.device, entity]);
|
|
883
913
|
if (this.readAtAnnounce) await this.doDeviceQuery(message.device || message.ieeeAddr, Date.now(), false);
|
|
884
914
|
}
|
|
@@ -891,7 +921,8 @@ class ZigbeeController extends EventEmitter {
|
|
|
891
921
|
async handleDeviceJoined(message) {
|
|
892
922
|
if (this.debugActive) this.debug('handleDeviceJoined', message);
|
|
893
923
|
//const entity = await this.resolveEntity(message.device || message.ieeeAddr);
|
|
894
|
-
//this.emit('new', entity);
|
|
924
|
+
// this.emit('new', entity);
|
|
925
|
+
//if (entity && entity.mapped) this.callExtensionMethod([message, entity.mapped]);
|
|
895
926
|
}
|
|
896
927
|
|
|
897
928
|
async handleDeviceInterview(message) {
|
|
@@ -916,12 +947,12 @@ class ZigbeeController extends EventEmitter {
|
|
|
916
947
|
const log = {friendly_name: friendlyName, model, vendor, description, supported: true};
|
|
917
948
|
this.emit('pairing', 'Interview successful', JSON.stringify(log));
|
|
918
949
|
//entity.device.modelID = entity.device._modelID;
|
|
919
|
-
this.emit('new', entity);
|
|
920
|
-
// send to extensions again (for configure)
|
|
921
950
|
this.callExtensionMethod(
|
|
922
951
|
'onZigbeeEvent',
|
|
923
|
-
[message, entity
|
|
952
|
+
[{...message,type:'deviceInterview', options: entity.options || {}}, entity.mapped],
|
|
924
953
|
);
|
|
954
|
+
this.emit('new', entity);
|
|
955
|
+
// send to extensions again (for configure)
|
|
925
956
|
} else {
|
|
926
957
|
if (this.debugActive) this.debug(
|
|
927
958
|
`Device '${friendlyName}' with Zigbee model '${message.device.modelID}' is NOT supported, ` +
|
|
@@ -936,8 +967,16 @@ class ZigbeeController extends EventEmitter {
|
|
|
936
967
|
this.error(`Failed to interview '${friendlyName}', device has not successfully been paired. Try again !!!!!!!!!! `);
|
|
937
968
|
//this.error(`Failed to interview '${friendlyName}', device has not successfully been paired. Try again !!!!!!!!!! ${message.error}`);
|
|
938
969
|
this.emit('pairing', 'Interview failed', friendlyName);
|
|
970
|
+
this.callExtensionMethod(
|
|
971
|
+
'onZigbeeEvent',
|
|
972
|
+
[message, entity ? entity.mapped : null],
|
|
973
|
+
);
|
|
939
974
|
} else {
|
|
940
975
|
if (message.status === 'started') {
|
|
976
|
+
this.callExtensionMethod(
|
|
977
|
+
'onZigbeeEvent',
|
|
978
|
+
[message, entity ? entity.mapped : null],
|
|
979
|
+
);
|
|
941
980
|
this.info(`Starting interview of '${friendlyName}'`);
|
|
942
981
|
this.emit('pairing', 'Interview started', friendlyName);
|
|
943
982
|
}
|
|
@@ -953,7 +992,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
953
992
|
|
|
954
993
|
const is = data.device.interviewState;
|
|
955
994
|
if (is != 'SUCCESSFUL' && is != 'FAILED') {
|
|
956
|
-
this.
|
|
995
|
+
this.info(`message ${JSON.stringify(data)} received during interview.`)
|
|
957
996
|
}
|
|
958
997
|
const entity = await this.resolveEntity(data.device || data.ieeeAddr);
|
|
959
998
|
const name = (entity && entity._modelID) ? entity._modelID : data.device.ieeeAddr;
|
|
@@ -963,11 +1002,10 @@ class ZigbeeController extends EventEmitter {
|
|
|
963
1002
|
(data.hasOwnProperty('groupID') ? ` with groupID ${data.groupID}` : ``)
|
|
964
1003
|
);
|
|
965
1004
|
this.event(data.type, entity, data);
|
|
966
|
-
|
|
967
1005
|
// Call extensions
|
|
968
1006
|
this.callExtensionMethod(
|
|
969
1007
|
'onZigbeeEvent',
|
|
970
|
-
[data, entity ? entity.mapped : null],
|
|
1008
|
+
[{...data, options:entity.options || {}}, entity ? entity.mapped : null],
|
|
971
1009
|
);
|
|
972
1010
|
}
|
|
973
1011
|
|
|
@@ -1240,7 +1278,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
1240
1278
|
|
|
1241
1279
|
const epName = stateDesc.epname !== undefined ? stateDesc.epname : (stateDesc.prop || stateDesc.id);
|
|
1242
1280
|
const key = stateDesc.setattr || stateDesc.prop || stateDesc.id;
|
|
1243
|
-
const message = `convert ${key}
|
|
1281
|
+
const message = `convert ${key} with value ${safeJsonStringify(preparedValue)} and options ${safeJsonStringify(preparedOptions)} for device ${deviceId} with Endpoint ${epName}`;
|
|
1244
1282
|
if (has_elevated_debug) {
|
|
1245
1283
|
this.emit('device_debug', { ID:debugID, data: { flag: '04', payload: {key:key, ep: stateDesc.epname, value:preparedValue, options:preparedOptions}, IO:false }, message:message});
|
|
1246
1284
|
}
|
|
@@ -1288,7 +1326,8 @@ class ZigbeeController extends EventEmitter {
|
|
|
1288
1326
|
try {
|
|
1289
1327
|
const result = await converter.convertSet(target, key, preparedValue, meta);
|
|
1290
1328
|
const message = `convert result ${safeJsonStringify(result)} for device ${deviceId}`;
|
|
1291
|
-
|
|
1329
|
+
if (isGroup)
|
|
1330
|
+
this.emit('published', deviceId, model, stateModel, stateList, options, debugID, has_elevated_debug );
|
|
1292
1331
|
if (has_elevated_debug) {
|
|
1293
1332
|
this.emit('device_debug', { ID:debugID, data: { flag: 'SUCCESS' , IO:false }, message:message});
|
|
1294
1333
|
}
|
|
@@ -1309,7 +1348,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
1309
1348
|
}
|
|
1310
1349
|
} catch (error) {
|
|
1311
1350
|
if (has_elevated_debug) {
|
|
1312
|
-
const message = `caught error ${
|
|
1351
|
+
const message = `caught error ${error && error.message ? error.message : 'no reason given'} when setting value for device ${deviceId}.`;
|
|
1313
1352
|
this.emit('device_debug', { ID:debugID, data: { error: 'EXSET' , IO:false },message:message});
|
|
1314
1353
|
}
|
|
1315
1354
|
this.adapter.filterError(`Error ${error.code} on send command to ${deviceId}.` +
|
|
@@ -1348,7 +1387,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
1348
1387
|
// payload: The data to send to the device as JSON object (key/Value pairs)
|
|
1349
1388
|
// endpoint: optional: the endpoint to send the data to, if supported.
|
|
1350
1389
|
//
|
|
1351
|
-
async publishPayload(payload, debugID) {
|
|
1390
|
+
async publishPayload(payload, debugID, has_elevated_debug) {
|
|
1352
1391
|
let payloadObj = {};
|
|
1353
1392
|
if (typeof payload === 'string') {
|
|
1354
1393
|
try {
|
|
@@ -1410,7 +1449,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
1410
1449
|
}
|
|
1411
1450
|
}
|
|
1412
1451
|
try {
|
|
1413
|
-
await this.publishFromState(`0x${payload.device}`, payload.model, payload.stateModel, stateList, payload.options, debugID);
|
|
1452
|
+
await this.publishFromState(`0x${payload.device}`, payload.model, payload.stateModel, stateList, payload.options, debugID, has_elevated_debug);
|
|
1414
1453
|
return {success: true};
|
|
1415
1454
|
} catch (error) {
|
|
1416
1455
|
this.log.error(`Error ${error.code} on send command to ${payload.device}.` + ` Error: ${error.stack} ` + `Send command to ${payload.device} failed with ` + error);
|
|
@@ -1430,8 +1469,9 @@ class ZigbeeController extends EventEmitter {
|
|
|
1430
1469
|
if (this.debugActive) this.debug(`doDeviceQuery: resolveEntity for entity: ${deviceId} is ${safeJsonStringify(entity)}`);
|
|
1431
1470
|
const mappedModel = entity ? entity.mapped : undefined;
|
|
1432
1471
|
if (mappedModel) {
|
|
1472
|
+
const epmap = mappedModel.endpoint ? mappedModel.endpoint() : [];
|
|
1433
1473
|
if (elevated) {
|
|
1434
|
-
const message = `Device query for '${entity.device.ieeeAddr}
|
|
1474
|
+
const message = `Device query for '${entity.device.ieeeAddr}' triggered`;
|
|
1435
1475
|
this.emit('device_debug', { ID:debugID, data: { flag: 'qs' ,states:[{id:'device_query', value:true, payload:'device_query'}], IO:false }, message:message});
|
|
1436
1476
|
}
|
|
1437
1477
|
else
|
|
@@ -1440,24 +1480,32 @@ class ZigbeeController extends EventEmitter {
|
|
|
1440
1480
|
|
|
1441
1481
|
for (const converter of mappedModel.toZigbee) {
|
|
1442
1482
|
if (converter.hasOwnProperty('convertGet')) {
|
|
1443
|
-
|
|
1483
|
+
const sources = [];
|
|
1484
|
+
if (converter.endpoints && epmap) {
|
|
1485
|
+
for (const epname of converter.endpoints) {
|
|
1486
|
+
const source = entity.device.endpoints.find((id) => id.ID == epmap[epname]);
|
|
1487
|
+
if (source) sources.push(source);
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
if (sources.length == 0) sources.push(entity.device.endpoints[0]);
|
|
1491
|
+
for (const source of sources) {
|
|
1444
1492
|
try {
|
|
1445
|
-
await converter.convertGet(
|
|
1446
|
-
this.debug(`read state ${
|
|
1493
|
+
await converter.convertGet(source, '', {device:entity.device});
|
|
1494
|
+
this.debug(`read for state${converter.key.length ? '' : 's'} '${converter.key.join(',')}' of '${entity.device.ieeeAddr}/${source.ID}' after device query`);
|
|
1447
1495
|
} catch (error) {
|
|
1448
1496
|
if (elevated) {
|
|
1449
|
-
const message = `Failed to read state '${
|
|
1497
|
+
const message = `Failed to read for state${converter.key.length ? '' : 's'} '${converter.key.join(',')}' of '${source.ID}' from query with '${error && error.message ? error.message : 'no error message'}`;
|
|
1450
1498
|
this.warn(`ELEVATED OE02.1 ${message}`);
|
|
1451
1499
|
this.emit('device_debug', { ID:debugID, data: { error: 'NOTREAD' , IO:false }, message:message });
|
|
1452
1500
|
}
|
|
1453
1501
|
else
|
|
1454
|
-
this.debug(`failed to read state ${
|
|
1502
|
+
this.debug(`failed to read for state${converter.key.length ? '' : 's'} '${converter.key.join(',')}' of '${source.ID}'after device query`);
|
|
1455
1503
|
}
|
|
1456
1504
|
}
|
|
1457
1505
|
}
|
|
1458
1506
|
}
|
|
1459
1507
|
if (elevated) {
|
|
1460
|
-
const message = `ELEVATED O07: Device query for '${entity.device.ieeeAddr}
|
|
1508
|
+
const message = `ELEVATED O07: Device query for '${entity.device.ieeeAddr}}' complete`;
|
|
1461
1509
|
this.emit('device_debug', { ID:debugID, data: { flag: 'qe' , IO:false }, message:message});
|
|
1462
1510
|
}
|
|
1463
1511
|
else
|
|
@@ -1467,7 +1515,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
1467
1515
|
|
|
1468
1516
|
async deviceQuery(deviceId, debugID, elevated, callback) {
|
|
1469
1517
|
if (this.deviceQueryActive.includes (deviceId)) {
|
|
1470
|
-
this.
|
|
1518
|
+
this.info(`Device query for ${deviceId} is still active.`);
|
|
1471
1519
|
return;
|
|
1472
1520
|
}
|
|
1473
1521
|
this.deviceQueryActive.push(deviceId);
|
package/main.js
CHANGED
|
@@ -13,8 +13,6 @@ try {
|
|
|
13
13
|
}
|
|
14
14
|
const originalLogMethod = debug.log;
|
|
15
15
|
|
|
16
|
-
const zigbeeHerdsmanConvertersUtils = require('zigbee-herdsman-converters/lib/utils');
|
|
17
|
-
|
|
18
16
|
const safeJsonStringify = require('./lib/json');
|
|
19
17
|
const fs = require('fs');
|
|
20
18
|
const path = require('path');
|
|
@@ -37,7 +35,6 @@ const vm = require('vm');
|
|
|
37
35
|
const util = require('util');
|
|
38
36
|
const dmZigbee = require('./lib/devicemgmt.js');
|
|
39
37
|
const DeviceDebug = require('./lib/DeviceDebug');
|
|
40
|
-
const { regexpCode } = require('ajv/dist/compile/codegen');
|
|
41
38
|
const dns = require('dns');
|
|
42
39
|
const net = require('net');
|
|
43
40
|
const { getNetAddress } = require('./lib/utils')
|
|
@@ -416,10 +413,11 @@ class Zigbee extends utils.Adapter {
|
|
|
416
413
|
}
|
|
417
414
|
this.zbController.configure(this.getZigbeeOptions(message.zigbeeOptions));
|
|
418
415
|
response.status = await this.doConnect(true);
|
|
416
|
+
if (!response.status) response.error = { message: 'Unable to start the Zigbee Network. Please check the previous messages.'}
|
|
419
417
|
this.sendTo(from, command, response, callback);
|
|
420
418
|
}
|
|
421
419
|
catch (error) {
|
|
422
|
-
this.sendTo(from, command, { status:false }, callback);
|
|
420
|
+
this.sendTo(from, command, { status:false, error }, callback);
|
|
423
421
|
}
|
|
424
422
|
}
|
|
425
423
|
else try {
|
|
@@ -427,7 +425,7 @@ class Zigbee extends utils.Adapter {
|
|
|
427
425
|
//this.logToPairing('herdsman stopped !');
|
|
428
426
|
this.sendTo(from, command, { status:true }, callback);
|
|
429
427
|
} catch (error) {
|
|
430
|
-
this.sendTo(from, command, { status:true }, callback);
|
|
428
|
+
this.sendTo(from, command, { status:true, error }, callback);
|
|
431
429
|
}
|
|
432
430
|
}
|
|
433
431
|
|
|
@@ -630,50 +628,43 @@ class Zigbee extends utils.Adapter {
|
|
|
630
628
|
|
|
631
629
|
await this.setState('info.connection', true, true);
|
|
632
630
|
this.stController.CleanupRequired(false);
|
|
631
|
+
const devicesFromObjects = (await this.getDevicesAsync()).filter(item => item.native.id.length ==16).map((item) => `0x${item.native.id}`);
|
|
633
632
|
const devicesFromDB = this.zbController.getClientIterator(false);
|
|
634
633
|
for (const device of devicesFromDB) {
|
|
635
634
|
const entity = await this.zbController.resolveEntity(device);
|
|
636
635
|
if (entity) {
|
|
637
636
|
const model = entity.mapped ? entity.mapped.model : entity.device.modelID;
|
|
637
|
+
const idx = devicesFromObjects.indexOf(device.ieeeAddr);
|
|
638
|
+
if (idx > -1) devicesFromObjects.splice(idx, 1);
|
|
638
639
|
this.stController.updateDev(device.ieeeAddr.substr(2), model, model, () =>
|
|
639
640
|
this.stController.syncDevStates(device, model));
|
|
640
641
|
}
|
|
641
642
|
else (this.log.warn('resolveEntity returned no entity'));
|
|
642
643
|
}
|
|
644
|
+
for (const id of devicesFromObjects) {
|
|
645
|
+
try {
|
|
646
|
+
this.log.warn(`removing object for device ${id} - it is no longer in the zigbee database`);
|
|
647
|
+
await this.delObjectAsync(id.substring(2), { recursive:true })
|
|
648
|
+
}
|
|
649
|
+
catch {
|
|
650
|
+
this.log.warn(`error removing ${id}`)
|
|
651
|
+
}
|
|
652
|
+
}
|
|
643
653
|
await this.callPluginMethod('start', [this.zbController, this.stController]);
|
|
644
654
|
}
|
|
645
655
|
|
|
656
|
+
|
|
646
657
|
async checkIfModelUpdate(entity) {
|
|
647
658
|
const model = entity.mapped ? entity.mapped.model : entity.device.modelID;
|
|
648
659
|
const device = entity.device;
|
|
649
660
|
const devId = device.ieeeAddr.substr(2);
|
|
650
661
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
const chain = [];
|
|
658
|
-
states.forEach((state) =>
|
|
659
|
-
chain.push(this.deleteStateAsync(devId, null, state._id)));
|
|
660
|
-
|
|
661
|
-
Promise.all(chain)
|
|
662
|
-
.then(() =>
|
|
663
|
-
this.stController.deleteObj(devId, () =>
|
|
664
|
-
this.stController.updateDev(devId, model, model, async () => {
|
|
665
|
-
await this.stController.syncDevStates(device, model);
|
|
666
|
-
resolve();
|
|
667
|
-
})));
|
|
668
|
-
} else {
|
|
669
|
-
resolve();
|
|
670
|
-
}
|
|
671
|
-
});
|
|
672
|
-
} else {
|
|
673
|
-
resolve();
|
|
674
|
-
}
|
|
675
|
-
});
|
|
676
|
-
});
|
|
662
|
+
const obj = await this.getObjectAsync(devId);
|
|
663
|
+
if (obj && obj.common.type !== model) {
|
|
664
|
+
await this.stController.deleteObj(devId);
|
|
665
|
+
await this.stController.updateDev(devId, model, model);
|
|
666
|
+
await this.stController.syncDevStates(device, model);
|
|
667
|
+
}
|
|
677
668
|
}
|
|
678
669
|
|
|
679
670
|
acknowledgeState(deviceId, model, stateDesc, value) {
|
|
@@ -754,17 +745,19 @@ class Zigbee extends utils.Adapter {
|
|
|
754
745
|
async onUnload(callback) {
|
|
755
746
|
try {
|
|
756
747
|
this.log.info(`Halting zigbee adapter. Restart delay is at least ${this.ioPack.common.stopTimeout / 1000} seconds.`)
|
|
748
|
+
this.setState('info.connection', false, true);
|
|
749
|
+
const chain = [];
|
|
757
750
|
if (this.config.debugHerdsman) {
|
|
758
751
|
debug.disable();
|
|
759
752
|
debug.log = originalLogMethod;
|
|
760
753
|
}
|
|
761
|
-
|
|
762
|
-
this.log.info('cleaning everything up...');
|
|
754
|
+
this.log.info('cleaning everything up');
|
|
763
755
|
await this.callPluginMethod('stop');
|
|
764
|
-
|
|
756
|
+
if (this.stController) chain.push(this.stController.stop());
|
|
765
757
|
if (this.zbController) {
|
|
766
|
-
|
|
758
|
+
chain.push(this.zbController.stop());
|
|
767
759
|
}
|
|
760
|
+
Promise.all(chain);
|
|
768
761
|
this.log.info('cleanup successful');
|
|
769
762
|
callback();
|
|
770
763
|
} catch (error) {
|
|
@@ -776,8 +769,8 @@ class Zigbee extends utils.Adapter {
|
|
|
776
769
|
}
|
|
777
770
|
}
|
|
778
771
|
|
|
779
|
-
getZigbeeOptions(
|
|
780
|
-
const override = (
|
|
772
|
+
getZigbeeOptions(overrideOptions) {
|
|
773
|
+
const override = (overrideOptions ? overrideOptions:{});
|
|
781
774
|
// file path for db
|
|
782
775
|
const dbDir = this.expandFileName('');
|
|
783
776
|
|
|
@@ -823,7 +816,7 @@ class Zigbee extends utils.Adapter {
|
|
|
823
816
|
dbPath: 'shepherd.db',
|
|
824
817
|
backupPath: 'nvbackup.json',
|
|
825
818
|
disableLed: this.config.disableLed,
|
|
826
|
-
disablePing: this.config.
|
|
819
|
+
disablePing: (this.config.pingCluster=='off'),
|
|
827
820
|
transmitPower: this.config.transmitPower,
|
|
828
821
|
disableBackup: this.config.disableBackup,
|
|
829
822
|
extPanIdFix: extPanIdFix,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.zigbee",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.4",
|
|
4
4
|
"author": {
|
|
5
5
|
"name": "Kirov Ilya",
|
|
6
6
|
"email": "kirovilya@gmail.com"
|
|
@@ -21,15 +21,15 @@
|
|
|
21
21
|
"serialport": "^13.0.0"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@iobroker/adapter-core": "^3.2
|
|
24
|
+
"@iobroker/adapter-core": "^3.3.2",
|
|
25
25
|
"@iobroker/dm-utils": "^1.0.10",
|
|
26
26
|
"humanize-duration": "^3.33.0",
|
|
27
27
|
"tar": "^7.4.3",
|
|
28
28
|
"ajv": "^8.17.1",
|
|
29
29
|
"uri-js": "^4.4.1",
|
|
30
|
-
"typescript": "^5.
|
|
30
|
+
"typescript": "^5.9.2",
|
|
31
31
|
"zigbee-herdsman": "^6.0.0",
|
|
32
|
-
"zigbee-herdsman-converters": "
|
|
32
|
+
"zigbee-herdsman-converters": "25.31.0"
|
|
33
33
|
},
|
|
34
34
|
"description": "Zigbee devices",
|
|
35
35
|
"devDependencies": {
|
|
@@ -37,15 +37,12 @@
|
|
|
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.0
|
|
40
|
+
"@iobroker/testing": "^5.1.0",
|
|
41
41
|
"chai": "^5.2.1",
|
|
42
42
|
"chai-as-promised": "^7.1.1",
|
|
43
43
|
"eslint": "^9.30.0",
|
|
44
44
|
"eslint-config-prettier": "^9.1.0",
|
|
45
45
|
"eslint-plugin-prettier": "^5.5.4",
|
|
46
|
-
"gulp": "^4.0.2",
|
|
47
|
-
"gulp-jsdoc3": "^3.0.0",
|
|
48
|
-
"gulp-replace": "^1.1.4",
|
|
49
46
|
"mixin-deep": "^2.0.1",
|
|
50
47
|
"mocha": "^11.7.1",
|
|
51
48
|
"@iobroker/dev-server": "^0.7.8"
|
package/lib/tools.js
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const axios = require('axios');
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Tests whether the given variable is a real object and not an Array
|
|
7
|
-
* @param {any} it The variable to test
|
|
8
|
-
* @returns {it is Record<string, any>}
|
|
9
|
-
*/
|
|
10
|
-
function isObject(it) {
|
|
11
|
-
// This is necessary because:
|
|
12
|
-
// typeof null === 'object'
|
|
13
|
-
// typeof [] === 'object'
|
|
14
|
-
// [] instanceof Object === true
|
|
15
|
-
return Object.prototype.toString.call(it) === '[object Object]';
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Tests whether the given variable is really an Array
|
|
20
|
-
* @param {any} it The variable to test
|
|
21
|
-
* @returns {it is any[]}
|
|
22
|
-
*/
|
|
23
|
-
function isArray(it) {
|
|
24
|
-
if (Array.isArray != null)
|
|
25
|
-
return Array.isArray(it);
|
|
26
|
-
return Object.prototype.toString.call(it) === '[object Array]';
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Translates text using the Google Translate API
|
|
31
|
-
* @param {string} text The text to translate
|
|
32
|
-
* @param {string} targetLang The target languate
|
|
33
|
-
* @returns {Promise<string>}
|
|
34
|
-
*/
|
|
35
|
-
async function translateText(text, targetLang) {
|
|
36
|
-
if (targetLang === 'en')
|
|
37
|
-
return text;
|
|
38
|
-
try {
|
|
39
|
-
const url = `http://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=${targetLang}&dt=t&q=${encodeURIComponent(text)}&ie=UTF-8&oe=UTF-8`;
|
|
40
|
-
const response = await axios({url, timeout: 5000});
|
|
41
|
-
if (isArray(response.data)) {
|
|
42
|
-
// we got a valid response
|
|
43
|
-
return response.data[0][0][0];
|
|
44
|
-
}
|
|
45
|
-
throw new Error('Invalid response for translate request');
|
|
46
|
-
} catch (e) {
|
|
47
|
-
throw new Error(`Could not translate to "${targetLang}": ${e}`);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
module.exports = {
|
|
52
|
-
isArray,
|
|
53
|
-
isObject,
|
|
54
|
-
translateText
|
|
55
|
-
};
|