iobroker.zigbee 3.1.2 → 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 +19 -1
- package/admin/admin.js +1020 -729
- package/admin/index_m.html +55 -155
- package/admin/tab_m.html +161 -242
- package/io-package.json +42 -39
- package/lib/DeviceDebug.js +24 -2
- package/lib/binding.js +7 -7
- package/lib/commands.js +319 -255
- 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 +135 -91
- package/lib/zbDelayedAction.js +4 -4
- package/lib/zbDeviceAvailability.js +32 -33
- package/lib/zbDeviceConfigure.js +7 -0
- package/lib/zbDeviceEvent.js +38 -6
- package/lib/zigbeecontroller.js +98 -46
- package/main.js +43 -57
- package/package.json +6 -9
- 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 = mapped ? 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.
|
|
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
|
+
else this.info('zigbecontroller stopped successfully - ZH was not running');
|
|
712
741
|
} catch (error) {
|
|
713
742
|
this.sendError(error);
|
|
714
743
|
if (this.herdsmanStarted) {
|
|
@@ -847,10 +876,11 @@ class ZigbeeController extends EventEmitter {
|
|
|
847
876
|
async handleDeviceAnnounce(message) {
|
|
848
877
|
if (this.debugActive) this.debug('handleDeviceAnnounce', message);
|
|
849
878
|
const entity = await this.resolveEntity(message.device || message.ieeeAddr);
|
|
850
|
-
const friendlyName = entity.name;
|
|
851
|
-
|
|
879
|
+
const friendlyName = entity ? entity.name : message.ieeeAddr ? message.ieeeAddr : message.device && message.device.ieeeAddr ? message.device.ieeeAddr : 'without data';
|
|
852
880
|
|
|
853
881
|
this.emit('pairing', `Device '${friendlyName}' announced itself`);
|
|
882
|
+
if (!entity) return;
|
|
883
|
+
|
|
854
884
|
if (this.adapter.stController.checkDebugDevice(friendlyName)) {
|
|
855
885
|
this.emit('device_debug', {ID: Date.now(), data: {flag:'da', states:[{id: '--', value:'--', payload:message}] , IO:true} ,message:`Device '${friendlyName}' announced itself`});
|
|
856
886
|
}
|
|
@@ -860,25 +890,26 @@ class ZigbeeController extends EventEmitter {
|
|
|
860
890
|
this.info(`Device '${friendlyName}' announced itself${this.readAtAnnounce ? ', trying to read its status' : ''}`);
|
|
861
891
|
}
|
|
862
892
|
|
|
863
|
-
if (entity.device && entity.device.
|
|
864
|
-
this.
|
|
893
|
+
if (entity.device && entity.device.modelID && entity.device.interviewState != 'SUCCESSFUL') {
|
|
894
|
+
this.info(`ignoring device announcement for ${entity.device.modelID} due to interview state ${entity.device.interviewState}`);
|
|
865
895
|
this.emit('pairing', `device interview state is ${entity.device.interviewState}`)
|
|
866
896
|
return;
|
|
867
897
|
}
|
|
868
898
|
|
|
869
899
|
const networkOpen = this.herdsman.getPermitJoin();
|
|
870
|
-
|
|
900
|
+
/*
|
|
901
|
+
if (networkOpen && entity.device && entity.device.modelID && entity.device.interviewState != 'IN_PROGRESS')
|
|
871
902
|
{
|
|
872
903
|
//entity.device.modelID = entity.device._modelID;
|
|
873
|
-
this.emit('new', entity);
|
|
904
|
+
//this.emit('new', entity);
|
|
874
905
|
return;
|
|
875
906
|
}
|
|
876
|
-
|
|
907
|
+
*/
|
|
877
908
|
try {
|
|
878
909
|
if (entity && entity.mapped) {
|
|
879
910
|
this.callExtensionMethod(
|
|
880
911
|
'onZigbeeEvent',
|
|
881
|
-
[{'device': message.device, 'type': 'deviceAnnounce'}, entity ? entity.mapped : null]);
|
|
912
|
+
[{'device': message.device, 'type': 'deviceAnnounce', options: entity.options || {}}, entity ? entity.mapped : null]);
|
|
882
913
|
this.callExtensionMethod('registerDevicePing', [message.device, entity]);
|
|
883
914
|
if (this.readAtAnnounce) await this.doDeviceQuery(message.device || message.ieeeAddr, Date.now(), false);
|
|
884
915
|
}
|
|
@@ -891,7 +922,8 @@ class ZigbeeController extends EventEmitter {
|
|
|
891
922
|
async handleDeviceJoined(message) {
|
|
892
923
|
if (this.debugActive) this.debug('handleDeviceJoined', message);
|
|
893
924
|
//const entity = await this.resolveEntity(message.device || message.ieeeAddr);
|
|
894
|
-
//this.emit('new', entity);
|
|
925
|
+
// this.emit('new', entity);
|
|
926
|
+
//if (entity && entity.mapped) this.callExtensionMethod([message, entity.mapped]);
|
|
895
927
|
}
|
|
896
928
|
|
|
897
929
|
async handleDeviceInterview(message) {
|
|
@@ -916,12 +948,12 @@ class ZigbeeController extends EventEmitter {
|
|
|
916
948
|
const log = {friendly_name: friendlyName, model, vendor, description, supported: true};
|
|
917
949
|
this.emit('pairing', 'Interview successful', JSON.stringify(log));
|
|
918
950
|
//entity.device.modelID = entity.device._modelID;
|
|
919
|
-
this.emit('new', entity);
|
|
920
|
-
// send to extensions again (for configure)
|
|
921
951
|
this.callExtensionMethod(
|
|
922
952
|
'onZigbeeEvent',
|
|
923
|
-
[message, entity
|
|
953
|
+
[{...message,type:'deviceInterview', options: entity.options || {}}, entity.mapped],
|
|
924
954
|
);
|
|
955
|
+
this.emit('new', entity);
|
|
956
|
+
// send to extensions again (for configure)
|
|
925
957
|
} else {
|
|
926
958
|
if (this.debugActive) this.debug(
|
|
927
959
|
`Device '${friendlyName}' with Zigbee model '${message.device.modelID}' is NOT supported, ` +
|
|
@@ -936,8 +968,16 @@ class ZigbeeController extends EventEmitter {
|
|
|
936
968
|
this.error(`Failed to interview '${friendlyName}', device has not successfully been paired. Try again !!!!!!!!!! `);
|
|
937
969
|
//this.error(`Failed to interview '${friendlyName}', device has not successfully been paired. Try again !!!!!!!!!! ${message.error}`);
|
|
938
970
|
this.emit('pairing', 'Interview failed', friendlyName);
|
|
971
|
+
this.callExtensionMethod(
|
|
972
|
+
'onZigbeeEvent',
|
|
973
|
+
[message, entity ? entity.mapped : null],
|
|
974
|
+
);
|
|
939
975
|
} else {
|
|
940
976
|
if (message.status === 'started') {
|
|
977
|
+
this.callExtensionMethod(
|
|
978
|
+
'onZigbeeEvent',
|
|
979
|
+
[message, entity ? entity.mapped : null],
|
|
980
|
+
);
|
|
941
981
|
this.info(`Starting interview of '${friendlyName}'`);
|
|
942
982
|
this.emit('pairing', 'Interview started', friendlyName);
|
|
943
983
|
}
|
|
@@ -953,7 +993,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
953
993
|
|
|
954
994
|
const is = data.device.interviewState;
|
|
955
995
|
if (is != 'SUCCESSFUL' && is != 'FAILED') {
|
|
956
|
-
this.
|
|
996
|
+
this.info(`message ${JSON.stringify(data)} received during interview.`)
|
|
957
997
|
}
|
|
958
998
|
const entity = await this.resolveEntity(data.device || data.ieeeAddr);
|
|
959
999
|
const name = (entity && entity._modelID) ? entity._modelID : data.device.ieeeAddr;
|
|
@@ -963,11 +1003,10 @@ class ZigbeeController extends EventEmitter {
|
|
|
963
1003
|
(data.hasOwnProperty('groupID') ? ` with groupID ${data.groupID}` : ``)
|
|
964
1004
|
);
|
|
965
1005
|
this.event(data.type, entity, data);
|
|
966
|
-
|
|
967
1006
|
// Call extensions
|
|
968
1007
|
this.callExtensionMethod(
|
|
969
1008
|
'onZigbeeEvent',
|
|
970
|
-
[data, entity ? entity.mapped : null],
|
|
1009
|
+
[{...data, options:entity.options || {}}, entity ? entity.mapped : null],
|
|
971
1010
|
);
|
|
972
1011
|
}
|
|
973
1012
|
|
|
@@ -1240,7 +1279,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
1240
1279
|
|
|
1241
1280
|
const epName = stateDesc.epname !== undefined ? stateDesc.epname : (stateDesc.prop || stateDesc.id);
|
|
1242
1281
|
const key = stateDesc.setattr || stateDesc.prop || stateDesc.id;
|
|
1243
|
-
const message = `convert ${key}
|
|
1282
|
+
const message = `convert ${key} with value ${safeJsonStringify(preparedValue)} and options ${safeJsonStringify(preparedOptions)} for device ${deviceId} with Endpoint ${epName}`;
|
|
1244
1283
|
if (has_elevated_debug) {
|
|
1245
1284
|
this.emit('device_debug', { ID:debugID, data: { flag: '04', payload: {key:key, ep: stateDesc.epname, value:preparedValue, options:preparedOptions}, IO:false }, message:message});
|
|
1246
1285
|
}
|
|
@@ -1288,7 +1327,8 @@ class ZigbeeController extends EventEmitter {
|
|
|
1288
1327
|
try {
|
|
1289
1328
|
const result = await converter.convertSet(target, key, preparedValue, meta);
|
|
1290
1329
|
const message = `convert result ${safeJsonStringify(result)} for device ${deviceId}`;
|
|
1291
|
-
|
|
1330
|
+
if (isGroup)
|
|
1331
|
+
this.emit('published', deviceId, model, stateModel, stateList, options, debugID, has_elevated_debug );
|
|
1292
1332
|
if (has_elevated_debug) {
|
|
1293
1333
|
this.emit('device_debug', { ID:debugID, data: { flag: 'SUCCESS' , IO:false }, message:message});
|
|
1294
1334
|
}
|
|
@@ -1309,7 +1349,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
1309
1349
|
}
|
|
1310
1350
|
} catch (error) {
|
|
1311
1351
|
if (has_elevated_debug) {
|
|
1312
|
-
const message = `caught error ${
|
|
1352
|
+
const message = `caught error ${error && error.message ? error.message : 'no reason given'} when setting value for device ${deviceId}.`;
|
|
1313
1353
|
this.emit('device_debug', { ID:debugID, data: { error: 'EXSET' , IO:false },message:message});
|
|
1314
1354
|
}
|
|
1315
1355
|
this.adapter.filterError(`Error ${error.code} on send command to ${deviceId}.` +
|
|
@@ -1347,8 +1387,8 @@ class ZigbeeController extends EventEmitter {
|
|
|
1347
1387
|
// device: name of the device. For a device zigbee.0.0011223344556677 this would be 0011223344556677
|
|
1348
1388
|
// payload: The data to send to the device as JSON object (key/Value pairs)
|
|
1349
1389
|
// endpoint: optional: the endpoint to send the data to, if supported.
|
|
1350
|
-
//
|
|
1351
|
-
async publishPayload(payload, debugID) {
|
|
1390
|
+
// acknowledge: optional: if to update the devices 'send_payload' DP (if present) after successful publish
|
|
1391
|
+
async publishPayload(payload, debugID, has_elevated_debug) {
|
|
1352
1392
|
let payloadObj = {};
|
|
1353
1393
|
if (typeof payload === 'string') {
|
|
1354
1394
|
try {
|
|
@@ -1410,7 +1450,10 @@ class ZigbeeController extends EventEmitter {
|
|
|
1410
1450
|
}
|
|
1411
1451
|
}
|
|
1412
1452
|
try {
|
|
1413
|
-
await this.publishFromState(`0x${payload.device}`, payload.model, payload.stateModel, stateList, payload.options, debugID);
|
|
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
|
+
}
|
|
1414
1457
|
return {success: true};
|
|
1415
1458
|
} catch (error) {
|
|
1416
1459
|
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 +1473,9 @@ class ZigbeeController extends EventEmitter {
|
|
|
1430
1473
|
if (this.debugActive) this.debug(`doDeviceQuery: resolveEntity for entity: ${deviceId} is ${safeJsonStringify(entity)}`);
|
|
1431
1474
|
const mappedModel = entity ? entity.mapped : undefined;
|
|
1432
1475
|
if (mappedModel) {
|
|
1476
|
+
const epmap = mappedModel.endpoint ? mappedModel.endpoint() : [];
|
|
1433
1477
|
if (elevated) {
|
|
1434
|
-
const message = `Device query for '${entity.device.ieeeAddr}
|
|
1478
|
+
const message = `Device query for '${entity.device.ieeeAddr}' triggered`;
|
|
1435
1479
|
this.emit('device_debug', { ID:debugID, data: { flag: 'qs' ,states:[{id:'device_query', value:true, payload:'device_query'}], IO:false }, message:message});
|
|
1436
1480
|
}
|
|
1437
1481
|
else
|
|
@@ -1440,24 +1484,32 @@ class ZigbeeController extends EventEmitter {
|
|
|
1440
1484
|
|
|
1441
1485
|
for (const converter of mappedModel.toZigbee) {
|
|
1442
1486
|
if (converter.hasOwnProperty('convertGet')) {
|
|
1443
|
-
|
|
1487
|
+
const sources = [];
|
|
1488
|
+
if (converter.endpoints && epmap) {
|
|
1489
|
+
for (const epname of converter.endpoints) {
|
|
1490
|
+
const source = entity.device.endpoints.find((id) => id.ID == epmap[epname]);
|
|
1491
|
+
if (source) sources.push(source);
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
if (sources.length == 0) sources.push(entity.device.endpoints[0]);
|
|
1495
|
+
for (const source of sources) {
|
|
1444
1496
|
try {
|
|
1445
|
-
await converter.convertGet(
|
|
1446
|
-
this.debug(`read state ${
|
|
1497
|
+
await converter.convertGet(source, '', {device:entity.device});
|
|
1498
|
+
this.debug(`read for state${converter.key.length ? '' : 's'} '${converter.key.join(',')}' of '${entity.device.ieeeAddr}/${source.ID}' after device query`);
|
|
1447
1499
|
} catch (error) {
|
|
1448
1500
|
if (elevated) {
|
|
1449
|
-
const message = `Failed to read state '${
|
|
1501
|
+
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
1502
|
this.warn(`ELEVATED OE02.1 ${message}`);
|
|
1451
1503
|
this.emit('device_debug', { ID:debugID, data: { error: 'NOTREAD' , IO:false }, message:message });
|
|
1452
1504
|
}
|
|
1453
1505
|
else
|
|
1454
|
-
this.debug(`failed to read state ${
|
|
1506
|
+
this.debug(`failed to read for state${converter.key.length ? '' : 's'} '${converter.key.join(',')}' of '${source.ID}'after device query`);
|
|
1455
1507
|
}
|
|
1456
1508
|
}
|
|
1457
1509
|
}
|
|
1458
1510
|
}
|
|
1459
1511
|
if (elevated) {
|
|
1460
|
-
const message = `ELEVATED O07: Device query for '${entity.device.ieeeAddr}
|
|
1512
|
+
const message = `ELEVATED O07: Device query for '${entity.device.ieeeAddr}}' complete`;
|
|
1461
1513
|
this.emit('device_debug', { ID:debugID, data: { flag: 'qe' , IO:false }, message:message});
|
|
1462
1514
|
}
|
|
1463
1515
|
else
|
|
@@ -1467,7 +1519,7 @@ class ZigbeeController extends EventEmitter {
|
|
|
1467
1519
|
|
|
1468
1520
|
async deviceQuery(deviceId, debugID, elevated, callback) {
|
|
1469
1521
|
if (this.deviceQueryActive.includes (deviceId)) {
|
|
1470
|
-
this.
|
|
1522
|
+
this.info(`Device query for ${deviceId} is still active.`);
|
|
1471
1523
|
return;
|
|
1472
1524
|
}
|
|
1473
1525
|
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')
|
|
@@ -277,7 +274,6 @@ class Zigbee extends utils.Adapter {
|
|
|
277
274
|
|
|
278
275
|
SandboxRequire(sandbox, items) {
|
|
279
276
|
if (!items) return true;
|
|
280
|
-
//let converterLoaded = true;
|
|
281
277
|
for (const item of items) {
|
|
282
278
|
const modulePath = item[2].replace(/['"]/gm, '');
|
|
283
279
|
|
|
@@ -353,9 +349,6 @@ class Zigbee extends utils.Adapter {
|
|
|
353
349
|
converterLoaded = false;
|
|
354
350
|
this.log.error(`converter does not export any converter array, please add 'module.exports' statement to ${mN}`);
|
|
355
351
|
}
|
|
356
|
-
|
|
357
|
-
//fs.writeFileSync(mN+'.tmp', modifiedCode)
|
|
358
|
-
|
|
359
352
|
if (converterLoaded) {
|
|
360
353
|
try {
|
|
361
354
|
this.log.warn('Trying to run sandbox for ' + mN);
|
|
@@ -416,18 +409,18 @@ class Zigbee extends utils.Adapter {
|
|
|
416
409
|
}
|
|
417
410
|
this.zbController.configure(this.getZigbeeOptions(message.zigbeeOptions));
|
|
418
411
|
response.status = await this.doConnect(true);
|
|
412
|
+
if (!response.status) response.error = { message: 'Unable to start the Zigbee Network. Please check the previous messages.'}
|
|
419
413
|
this.sendTo(from, command, response, callback);
|
|
420
414
|
}
|
|
421
415
|
catch (error) {
|
|
422
|
-
this.sendTo(from, command, { status:false }, callback);
|
|
416
|
+
this.sendTo(from, command, { status:false, error }, callback);
|
|
423
417
|
}
|
|
424
418
|
}
|
|
425
419
|
else try {
|
|
426
420
|
await this.zbController.stopHerdsman();
|
|
427
|
-
//this.logToPairing('herdsman stopped !');
|
|
428
421
|
this.sendTo(from, command, { status:true }, callback);
|
|
429
422
|
} catch (error) {
|
|
430
|
-
this.sendTo(from, command, { status:true }, callback);
|
|
423
|
+
this.sendTo(from, command, { status:true, error }, callback);
|
|
431
424
|
}
|
|
432
425
|
}
|
|
433
426
|
|
|
@@ -460,12 +453,6 @@ class Zigbee extends utils.Adapter {
|
|
|
460
453
|
this.setState('info.connection', false, true);
|
|
461
454
|
this.logToPairing(`Failed to start Zigbee: ${error && error.message ? error.message : 'no message given'}`)
|
|
462
455
|
this.log.error(`Failed to start Zigbee: ${error && error.message ? error.message : 'no message given'}`);
|
|
463
|
-
/* if (error.stack) {
|
|
464
|
-
this.log.error(error.stack);
|
|
465
|
-
} else {
|
|
466
|
-
this.log.error(error);
|
|
467
|
-
}
|
|
468
|
-
*/
|
|
469
456
|
this.sendError(error, `Failed to start Zigbee`);
|
|
470
457
|
if (noReconnect) return false;
|
|
471
458
|
|
|
@@ -630,59 +617,58 @@ class Zigbee extends utils.Adapter {
|
|
|
630
617
|
|
|
631
618
|
await this.setState('info.connection', true, true);
|
|
632
619
|
this.stController.CleanupRequired(false);
|
|
620
|
+
const devicesFromObjects = (await this.getDevicesAsync()).filter(item => item.native.id.length ==16).map((item) => `0x${item.native.id}`);
|
|
633
621
|
const devicesFromDB = this.zbController.getClientIterator(false);
|
|
634
622
|
for (const device of devicesFromDB) {
|
|
635
623
|
const entity = await this.zbController.resolveEntity(device);
|
|
636
624
|
if (entity) {
|
|
637
625
|
const model = entity.mapped ? entity.mapped.model : entity.device.modelID;
|
|
626
|
+
const idx = devicesFromObjects.indexOf(device.ieeeAddr);
|
|
627
|
+
if (idx > -1) devicesFromObjects.splice(idx, 1);
|
|
638
628
|
this.stController.updateDev(device.ieeeAddr.substr(2), model, model, () =>
|
|
639
629
|
this.stController.syncDevStates(device, model));
|
|
640
630
|
}
|
|
641
631
|
else (this.log.warn('resolveEntity returned no entity'));
|
|
642
632
|
}
|
|
633
|
+
for (const id of devicesFromObjects) {
|
|
634
|
+
try {
|
|
635
|
+
this.log.warn(`removing object for device ${id} - it is no longer in the zigbee database`);
|
|
636
|
+
await this.delObjectAsync(id.substring(2), { recursive:true })
|
|
637
|
+
}
|
|
638
|
+
catch {
|
|
639
|
+
this.log.warn(`error removing ${id}`)
|
|
640
|
+
}
|
|
641
|
+
}
|
|
643
642
|
await this.callPluginMethod('start', [this.zbController, this.stController]);
|
|
644
643
|
}
|
|
645
644
|
|
|
645
|
+
|
|
646
646
|
async checkIfModelUpdate(entity) {
|
|
647
647
|
const model = entity.mapped ? entity.mapped.model : entity.device.modelID;
|
|
648
648
|
const device = entity.device;
|
|
649
649
|
const devId = device.ieeeAddr.substr(2);
|
|
650
650
|
|
|
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
|
-
});
|
|
651
|
+
const obj = await this.getObjectAsync(devId);
|
|
652
|
+
if (obj && obj.common.type !== model) {
|
|
653
|
+
await this.stController.deleteObj(devId);
|
|
654
|
+
await this.stController.updateDev(devId, model, model);
|
|
655
|
+
await this.stController.syncDevStates(device, model);
|
|
656
|
+
}
|
|
677
657
|
}
|
|
678
658
|
|
|
679
659
|
acknowledgeState(deviceId, model, stateDesc, value) {
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
this.
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
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'}`);
|
|
686
672
|
}
|
|
687
673
|
}
|
|
688
674
|
|
|
@@ -754,17 +740,17 @@ class Zigbee extends utils.Adapter {
|
|
|
754
740
|
async onUnload(callback) {
|
|
755
741
|
try {
|
|
756
742
|
this.log.info(`Halting zigbee adapter. Restart delay is at least ${this.ioPack.common.stopTimeout / 1000} seconds.`)
|
|
743
|
+
this.setState('info.connection', false, true);
|
|
744
|
+
const chain = [];
|
|
757
745
|
if (this.config.debugHerdsman) {
|
|
758
746
|
debug.disable();
|
|
759
747
|
debug.log = originalLogMethod;
|
|
760
748
|
}
|
|
761
|
-
|
|
762
|
-
this.log.info('cleaning everything up...');
|
|
749
|
+
this.log.info('cleaning everything up');
|
|
763
750
|
await this.callPluginMethod('stop');
|
|
764
|
-
|
|
765
|
-
if (this.zbController)
|
|
766
|
-
|
|
767
|
-
}
|
|
751
|
+
if (this.stController) chain.push(this.stController.stop());
|
|
752
|
+
if (this.zbController) chain.push(this.zbController.stop());
|
|
753
|
+
await Promise.all(chain);
|
|
768
754
|
this.log.info('cleanup successful');
|
|
769
755
|
callback();
|
|
770
756
|
} catch (error) {
|
|
@@ -776,8 +762,8 @@ class Zigbee extends utils.Adapter {
|
|
|
776
762
|
}
|
|
777
763
|
}
|
|
778
764
|
|
|
779
|
-
getZigbeeOptions(
|
|
780
|
-
const override = (
|
|
765
|
+
getZigbeeOptions(overrideOptions) {
|
|
766
|
+
const override = (overrideOptions ? overrideOptions:{});
|
|
781
767
|
// file path for db
|
|
782
768
|
const dbDir = this.expandFileName('');
|
|
783
769
|
|
|
@@ -823,7 +809,7 @@ class Zigbee extends utils.Adapter {
|
|
|
823
809
|
dbPath: 'shepherd.db',
|
|
824
810
|
backupPath: 'nvbackup.json',
|
|
825
811
|
disableLed: this.config.disableLed,
|
|
826
|
-
disablePing: this.config.
|
|
812
|
+
disablePing: (this.config.pingCluster=='off'),
|
|
827
813
|
transmitPower: this.config.transmitPower,
|
|
828
814
|
disableBackup: this.config.disableBackup,
|
|
829
815
|
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.5",
|
|
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.
|
|
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
|
-
"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"
|