iobroker.zigbee 2.0.4 → 2.0.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 +83 -56
- package/admin/admin.js +217 -19
- package/admin/img/philips_hue_lom001.png +0 -0
- package/admin/tab_m.html +13 -8
- package/docs/tutorial/groups-1.png +0 -0
- package/docs/tutorial/groups-2.png +0 -0
- package/docs/tutorial/tab-dev-1.png +0 -0
- package/io-package.json +15 -49
- package/lib/DeviceDebug.js +4 -2
- package/lib/commands.js +21 -2
- package/lib/developer.js +0 -0
- package/lib/exposes.js +8 -3
- package/lib/groups.js +6 -8
- package/lib/localConfig.js +4 -5
- package/lib/ota.js +6 -6
- package/lib/statescontroller.js +276 -101
- package/lib/zbDeviceAvailability.js +2 -2
- package/lib/zbDeviceConfigure.js +92 -58
- package/lib/zigbeecontroller.js +44 -46
- package/main.js +143 -217
- package/package.json +5 -5
package/main.js
CHANGED
|
@@ -36,6 +36,7 @@ const zigbeeHerdsmanPackage = require('zigbee-herdsman/package.json')
|
|
|
36
36
|
const vm = require('vm');
|
|
37
37
|
const util = require('util');
|
|
38
38
|
const dmZigbee = require('./lib/devicemgmt.js');
|
|
39
|
+
const DeviceDebug = require('./lib/DeviceDebug');
|
|
39
40
|
|
|
40
41
|
const createByteArray = function (hexString) {
|
|
41
42
|
const bytes = [];
|
|
@@ -79,6 +80,9 @@ class Zigbee extends utils.Adapter {
|
|
|
79
80
|
this.stController.on('changed', this.publishFromState.bind(this));
|
|
80
81
|
|
|
81
82
|
this.deviceManagement = new dmZigbee(this);
|
|
83
|
+
this.deviceDebug = new DeviceDebug(this),
|
|
84
|
+
this.deviceDebug.on('log', this.onLog.bind(this));
|
|
85
|
+
|
|
82
86
|
|
|
83
87
|
this.plugins = [
|
|
84
88
|
new SerialListPlugin(this),
|
|
@@ -115,6 +119,19 @@ class Zigbee extends utils.Adapter {
|
|
|
115
119
|
}
|
|
116
120
|
}
|
|
117
121
|
|
|
122
|
+
warn(message) {
|
|
123
|
+
this.log.warn(message);
|
|
124
|
+
}
|
|
125
|
+
debug(message) {
|
|
126
|
+
this.log.debug(message);
|
|
127
|
+
}
|
|
128
|
+
error(message) {
|
|
129
|
+
this.log.error(message);
|
|
130
|
+
}
|
|
131
|
+
info(message) {
|
|
132
|
+
this.log.info(message);
|
|
133
|
+
}
|
|
134
|
+
|
|
118
135
|
sendError(error, message) {
|
|
119
136
|
try {
|
|
120
137
|
if (this.supportsFeature && this.supportsFeature('PLUGINS')) {
|
|
@@ -210,12 +227,15 @@ class Zigbee extends utils.Adapter {
|
|
|
210
227
|
this.zbController.on('new', this.newDevice.bind(this));
|
|
211
228
|
this.zbController.on('leave', this.leaveDevice.bind(this));
|
|
212
229
|
this.zbController.on('pairing', this.onPairing.bind(this));
|
|
213
|
-
this.zbController.on('event', this.onZigbeeEvent.bind(this));
|
|
214
|
-
this.zbController.on('msg', this.onZigbeeEvent.bind(this));
|
|
230
|
+
this.zbController.on('event', this.stController.onZigbeeEvent.bind(this.stController));
|
|
231
|
+
this.zbController.on('msg', this.stController.onZigbeeEvent.bind(this.stController));
|
|
215
232
|
this.zbController.on('publish', this.publishToState.bind(this));
|
|
216
233
|
this.zbController.configure(zigbeeOptions);
|
|
217
234
|
await this.callPluginMethod('configure', [zigbeeOptions]);
|
|
218
235
|
|
|
236
|
+
// elevated debug handling
|
|
237
|
+
this.deviceDebug.start(this.stController, this.zbController);
|
|
238
|
+
|
|
219
239
|
this.reconnectCounter = 1;
|
|
220
240
|
this.doConnect();
|
|
221
241
|
}
|
|
@@ -287,7 +307,7 @@ class Zigbee extends utils.Adapter {
|
|
|
287
307
|
require,
|
|
288
308
|
module: {},
|
|
289
309
|
};
|
|
290
|
-
const mN = (fs.existsSync(moduleName) ? moduleName : this.expandFileName(moduleName).replace('.', '
|
|
310
|
+
const mN = (fs.existsSync(moduleName) ? moduleName : this.expandFileName(moduleName).replace('zigbee.', 'zigbee_'));
|
|
291
311
|
if (!fs.existsSync(mN)) {
|
|
292
312
|
this.log.warn(`External converter not loaded - neither ${moduleName} nor ${mN} exist.`);
|
|
293
313
|
}
|
|
@@ -353,25 +373,15 @@ class Zigbee extends utils.Adapter {
|
|
|
353
373
|
const toAdd = {...definition};
|
|
354
374
|
delete toAdd['homeassistant'];
|
|
355
375
|
try {
|
|
376
|
+
const t = Date.now();
|
|
356
377
|
if (zigbeeHerdsmanConverters.hasOwnProperty('addExternalDefinition')) {
|
|
357
378
|
zigbeeHerdsmanConverters.addExternalDefinition(toAdd);
|
|
358
|
-
this.log.info(
|
|
379
|
+
this.log.info(`added external converter using addExternalDefinition (${Date.now()-t} ms)`)
|
|
359
380
|
}
|
|
360
381
|
else if (zigbeeHerdsmanConverters.hasOwnProperty('addDefinition')) {
|
|
361
382
|
zigbeeHerdsmanConverters.addDefinition(toAdd);
|
|
362
|
-
this.log.info(
|
|
383
|
+
this.log.info(`added external converter using addDefinition (${Date.now()-t} ms)`);
|
|
363
384
|
}
|
|
364
|
-
|
|
365
|
-
/*
|
|
366
|
-
for (const zigbeeModel of toAdd.zigbeeModel)
|
|
367
|
-
{
|
|
368
|
-
try {
|
|
369
|
-
zigbeeHerdsmanConverters.addToExternalDefinitionsLookup(zigbeeModel, toAdd.toAdd);
|
|
370
|
-
} catch (e) {
|
|
371
|
-
this.log.error(`unable to apply external converter ${JSON.stringify(toAdd)} for device ${zigbeeModel}: ${e && e.message ? e.message : 'no error message available'}`);
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
*/
|
|
375
385
|
} catch (e) {
|
|
376
386
|
this.log.error(`unable to apply external converter for ${JSON.stringify(toAdd.model)}: ${e && e.message ? e.message : 'no error message available'}`);
|
|
377
387
|
}
|
|
@@ -510,7 +520,6 @@ class Zigbee extends utils.Adapter {
|
|
|
510
520
|
for (const device of devicesFromDB) {
|
|
511
521
|
const entity = await this.zbController.resolveEntity(device);
|
|
512
522
|
if (entity) {
|
|
513
|
-
// this.log.warn('sync dev states for ' + (entity.mapped ? entity.mapped.model : entity.device.modelID));
|
|
514
523
|
const model = entity.mapped ? entity.mapped.model : entity.device.modelID;
|
|
515
524
|
this.stController.updateDev(device.ieeeAddr.substr(2), model, model, () =>
|
|
516
525
|
this.stController.syncDevStates(device, model));
|
|
@@ -553,154 +562,6 @@ class Zigbee extends utils.Adapter {
|
|
|
553
562
|
});
|
|
554
563
|
}
|
|
555
564
|
|
|
556
|
-
async onZigbeeEvent(type, entity, message) {
|
|
557
|
-
this.log.debug(`Type ${type} device ${safeJsonStringify(entity)} incoming event: ${safeJsonStringify(message)}`);
|
|
558
|
-
|
|
559
|
-
const device = entity.device;
|
|
560
|
-
const mappedModel = entity.mapped;
|
|
561
|
-
const model = entity.mapped ? entity.mapped.model : entity.device.modelID;
|
|
562
|
-
const cluster = message.cluster;
|
|
563
|
-
const devId = device.ieeeAddr.substr(2);
|
|
564
|
-
const meta = {device};
|
|
565
|
-
|
|
566
|
-
const has_elevated_debug = this.stController.checkDebugDevice(devId);
|
|
567
|
-
|
|
568
|
-
if (has_elevated_debug) {
|
|
569
|
-
const shortMessage = {};
|
|
570
|
-
for(const propertyName in message) {
|
|
571
|
-
shortMessage[propertyName] = message[propertyName];
|
|
572
|
-
}
|
|
573
|
-
shortMessage.device = device.ieeeAddr;
|
|
574
|
-
shortMessage.meta = undefined;
|
|
575
|
-
shortMessage.endpoint = (message.endpoint.ID ? message.endpoint.ID: -1);
|
|
576
|
-
this.log.warn(`ELEVATED I00: Zigbee Event of Type ${type} from device ${safeJsonStringify(device.ieeeAddr)}, incoming event: ${safeJsonStringify(shortMessage)}`);
|
|
577
|
-
}
|
|
578
|
-
// this assigment give possibility to use iobroker logger in code of the converters, via meta.logger
|
|
579
|
-
meta.logger = this.log;
|
|
580
|
-
|
|
581
|
-
await this.checkIfModelUpdate(entity);
|
|
582
|
-
|
|
583
|
-
let _voltage = 0;
|
|
584
|
-
let _temperature = 0;
|
|
585
|
-
let _humidity = 0;
|
|
586
|
-
|
|
587
|
-
let isMessure = false;
|
|
588
|
-
let isBattKey = false;
|
|
589
|
-
|
|
590
|
-
if (mappedModel && mappedModel.meta && mappedModel.meta.battery) {
|
|
591
|
-
const isVoltage = mappedModel.meta.battery.hasOwnProperty('voltageToPercentage');
|
|
592
|
-
|
|
593
|
-
if (isVoltage) {
|
|
594
|
-
const keys = Object.keys(message.data);
|
|
595
|
-
|
|
596
|
-
for (const key of keys) {
|
|
597
|
-
const value = message.data[key];
|
|
598
|
-
|
|
599
|
-
if (value && value[1]) {
|
|
600
|
-
if (key == 65282 && value[1][1]) {
|
|
601
|
-
_voltage = value[1][1].elmVal;
|
|
602
|
-
isBattKey = true;
|
|
603
|
-
break;
|
|
604
|
-
}
|
|
605
|
-
if (key == 65281) {
|
|
606
|
-
_voltage = value[1];
|
|
607
|
-
isBattKey = true;
|
|
608
|
-
_temperature = value[100];
|
|
609
|
-
_temperature = _temperature /100;
|
|
610
|
-
_humidity = value[101];
|
|
611
|
-
_humidity = _humidity / 100;
|
|
612
|
-
isMessure = true;
|
|
613
|
-
break;
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
// always publish link_quality and battery
|
|
621
|
-
if (message.linkquality) { // send battery with
|
|
622
|
-
this.publishToState(devId, model, {linkquality: message.linkquality});
|
|
623
|
-
if (isBattKey) {
|
|
624
|
-
this.publishToState(devId, model, {voltage: _voltage});
|
|
625
|
-
const battProz = zigbeeHerdsmanConvertersUtils.batteryVoltageToPercentage(_voltage,entity.mapped.meta.battery.voltageToPercentage);
|
|
626
|
-
this.publishToState(devId, model, {battery: battProz});
|
|
627
|
-
}
|
|
628
|
-
if (isMessure) {
|
|
629
|
-
this.publishToState(devId, model, {temperature: _temperature});
|
|
630
|
-
this.publishToState(devId, model, {humidity: _humidity});
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
// publish raw event to "from_zigbee"
|
|
635
|
-
// some cleanup
|
|
636
|
-
const msgForState = Object.assign({}, message);
|
|
637
|
-
delete msgForState['device'];
|
|
638
|
-
delete msgForState['endpoint'];
|
|
639
|
-
|
|
640
|
-
msgForState['endpoint_id'] = message.endpoint.ID;
|
|
641
|
-
this.publishToState(devId, model, {msg_from_zigbee: safeJsonStringify(msgForState)});
|
|
642
|
-
|
|
643
|
-
if (!entity.mapped) {
|
|
644
|
-
return;
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
let converters = mappedModel.fromZigbee.filter(c => c && c.cluster === cluster && (
|
|
648
|
-
Array.isArray(c.type) ? c.type.includes(type) : c.type === type));
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
if (!converters.length && type === 'readResponse') {
|
|
652
|
-
converters = mappedModel.fromZigbee.filter(c => c.cluster === cluster && (
|
|
653
|
-
Array.isArray(c.type) ? c.type.includes('attributeReport') : c.type === 'attributeReport'));
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
if (!converters.length) {
|
|
657
|
-
if (type !== 'readResponse') {
|
|
658
|
-
this.log.debug(`No converter available for '${mappedModel.model}' '${devId}' with cluster '${cluster}' and type '${type}'`);
|
|
659
|
-
if (has_elevated_debug)
|
|
660
|
-
this.log.warn(`ELEVATED IE00: No converter available for '${mappedModel.model}' '${devId}' with cluster '${cluster}' and type '${type}'`);
|
|
661
|
-
}
|
|
662
|
-
return;
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
meta.state = { state: '' }; // for tuya
|
|
666
|
-
|
|
667
|
-
this.processConverters(converters, devId, model, mappedModel, message, meta)
|
|
668
|
-
.catch((error) => {
|
|
669
|
-
// 'Error: Expected one of: 0, 1, got: 'undefined''
|
|
670
|
-
if (cluster !== '64529') {
|
|
671
|
-
this.log.error(`Error while processing converters DEVICE_ID: '${devId}' cluster '${cluster}' type '${type}'`);
|
|
672
|
-
}
|
|
673
|
-
});
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
async processConverters(converters, devId, model, mappedModel, message, meta) {
|
|
677
|
-
for (const converter of converters) {
|
|
678
|
-
const publish = (payload) => {
|
|
679
|
-
this.log.debug(`Publish ${safeJsonStringify(payload)} to ${safeJsonStringify(devId)}`);
|
|
680
|
-
if (typeof payload === 'object') {
|
|
681
|
-
this.publishToState(devId, model, payload);
|
|
682
|
-
}
|
|
683
|
-
};
|
|
684
|
-
|
|
685
|
-
const options = await new Promise((resolve, reject) => {
|
|
686
|
-
this.stController.collectOptions(devId, model, (options) => {
|
|
687
|
-
resolve(options);
|
|
688
|
-
});
|
|
689
|
-
});
|
|
690
|
-
|
|
691
|
-
const payload = await new Promise((resolve, reject) => {
|
|
692
|
-
const payloadConv = converter.convert(mappedModel, message, publish, options, meta);
|
|
693
|
-
if (typeof payloadConv === 'object') {
|
|
694
|
-
resolve(payloadConv);
|
|
695
|
-
}
|
|
696
|
-
});
|
|
697
|
-
|
|
698
|
-
publish(payload);
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
565
|
publishToState(devId, model, payload) {
|
|
705
566
|
this.stController.publishToState(devId, model, payload);
|
|
706
567
|
}
|
|
@@ -721,15 +582,18 @@ class Zigbee extends utils.Adapter {
|
|
|
721
582
|
});
|
|
722
583
|
}
|
|
723
584
|
|
|
724
|
-
async publishFromState(deviceId, model, stateModel, stateList, options) {
|
|
585
|
+
async publishFromState(deviceId, model, stateModel, stateList, options, debugID) {
|
|
725
586
|
let isGroup = false;
|
|
726
587
|
const has_elevated_debug = this.stController.checkDebugDevice(deviceId)
|
|
727
588
|
|
|
728
589
|
if (has_elevated_debug)
|
|
729
590
|
{
|
|
730
591
|
const stateNames = [];
|
|
731
|
-
|
|
732
|
-
|
|
592
|
+
for (const state of stateList) {
|
|
593
|
+
stateNames.push(state.stateDesc.id);
|
|
594
|
+
}
|
|
595
|
+
const message = `Publishing to ${deviceId} of model ${model} with ${stateNames.join(', ')}`;
|
|
596
|
+
this.emit('device_debug', { ID:debugID, data: { ID: deviceId, flag: '03', IO:false }, message: message});
|
|
733
597
|
}
|
|
734
598
|
else
|
|
735
599
|
this.log.debug(`publishFromState : ${deviceId} ${model} ${safeJsonStringify(stateList)}`);
|
|
@@ -744,7 +608,10 @@ class Zigbee extends utils.Adapter {
|
|
|
744
608
|
|
|
745
609
|
if (!mappedModel) {
|
|
746
610
|
this.log.debug(`No mapped model for ${model}`);
|
|
747
|
-
if (has_elevated_debug)
|
|
611
|
+
if (has_elevated_debug) {
|
|
612
|
+
const message=`No mapped model ${deviceId} (model ${model})`;
|
|
613
|
+
this.emit('device_debug', { ID:debugID, data: { error: 'NOMODEL' , IO:false }, message: message});
|
|
614
|
+
}
|
|
748
615
|
return;
|
|
749
616
|
}
|
|
750
617
|
|
|
@@ -761,13 +628,20 @@ class Zigbee extends utils.Adapter {
|
|
|
761
628
|
if (stateDesc.id === 'send_payload') {
|
|
762
629
|
try {
|
|
763
630
|
const json_value = JSON.parse(value);
|
|
764
|
-
const payload = {device: deviceId.replace('0x', ''), payload: json_value};
|
|
631
|
+
const payload = {device: deviceId.replace('0x', ''), payload: json_value, model:model, stateModel:stateModel};
|
|
632
|
+
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 }});
|
|
633
|
+
|
|
765
634
|
const result = await this.sendPayload(payload);
|
|
766
635
|
if (result.hasOwnProperty('success') && result.success) {
|
|
767
636
|
this.acknowledgeState(deviceId, model, stateDesc, value);
|
|
768
637
|
}
|
|
638
|
+
else {
|
|
639
|
+
this.error('Error in SendPayload: '+result.error.message);
|
|
640
|
+
}
|
|
769
641
|
} catch (error) {
|
|
770
|
-
|
|
642
|
+
const message = `send_payload: ${value} does not parse as JSON Object : ${error.message}`;
|
|
643
|
+
if (has_elevated_debug) this.emit('device_debug', { ID:debugID, data: { error: 'EXSEND' ,states:[{id:stateDesc.id, value:value, payload:error.message}], IO:false }, message:message});
|
|
644
|
+
else this.error(message);
|
|
771
645
|
return;
|
|
772
646
|
}
|
|
773
647
|
return;
|
|
@@ -775,8 +649,10 @@ class Zigbee extends utils.Adapter {
|
|
|
775
649
|
|
|
776
650
|
if (stateDesc.isOption || stateDesc.compositeState) {
|
|
777
651
|
// acknowledge state with given value
|
|
778
|
-
if (has_elevated_debug)
|
|
779
|
-
|
|
652
|
+
if (has_elevated_debug) {
|
|
653
|
+
const message = 'changed state: ' + JSON.stringify(changedState);
|
|
654
|
+
this.emit('device_debug', { ID:debugID, data: { flag: 'cc', states:[{id:stateDesc.id, value:value, payload:'none (OC State)'}] , IO:false }, message:message});
|
|
655
|
+
}
|
|
780
656
|
else
|
|
781
657
|
this.log.debug('changed composite state: ' + JSON.stringify(changedState));
|
|
782
658
|
|
|
@@ -784,20 +660,18 @@ class Zigbee extends utils.Adapter {
|
|
|
784
660
|
if (stateDesc.compositeState && stateDesc.compositeTimeout) {
|
|
785
661
|
this.stController.triggerComposite(deviceId, model, stateDesc, changedState.source.includes('.admin.'));
|
|
786
662
|
}
|
|
787
|
-
// process sync state list
|
|
788
|
-
//this.processSyncStatesList(deviceId, modelId, syncStateList);
|
|
789
|
-
// if this is the device query state => trigger the device query
|
|
790
|
-
|
|
791
663
|
// on activation of the 'device_query' state trigger hardware query where possible
|
|
792
664
|
if (stateDesc.id === 'device_query') {
|
|
793
665
|
if (this.query_device_block.indexOf(deviceId) > -1) {
|
|
794
|
-
this.log.
|
|
666
|
+
this.log.info(`Device query for '${entity.device.ieeeAddr}' blocked`);
|
|
795
667
|
return;
|
|
796
668
|
}
|
|
797
669
|
if (mappedModel) {
|
|
798
670
|
this.query_device_block.push(deviceId);
|
|
799
|
-
if (has_elevated_debug)
|
|
800
|
-
|
|
671
|
+
if (has_elevated_debug) {
|
|
672
|
+
const message = `Device query for '${entity.device.ieeeAddr}/${entity.device.endpoints[0].ID}' triggered`;
|
|
673
|
+
this.emit('device_debug', { ID:debugID, data: { flag: 'qs' ,states:[{id:stateDesc.id, value:value, payload:'none for device query'}], IO:false }, message:message});
|
|
674
|
+
}
|
|
801
675
|
else
|
|
802
676
|
this.log.debug(`Device query for '${entity.device.ieeeAddr}' started`);
|
|
803
677
|
for (const converter of mappedModel.toZigbee) {
|
|
@@ -807,7 +681,9 @@ class Zigbee extends utils.Adapter {
|
|
|
807
681
|
await converter.convertGet(entity.device.endpoints[0], ckey, {});
|
|
808
682
|
} catch (error) {
|
|
809
683
|
if (has_elevated_debug) {
|
|
810
|
-
|
|
684
|
+
const message = `Failed to read state '${JSON.stringify(ckey)}'of '${entity.device.ieeeAddr}/${entity.device.endpoints[0].ID}' from query with '${error && error.message ? error.message : 'no error message'}`;
|
|
685
|
+
this.log.warn(`ELEVATED OE02.1 ${message}`);
|
|
686
|
+
this.emit('device_debug', { ID:debugID, data: { error: 'NOTREAD' , IO:false }, message:message });
|
|
811
687
|
}
|
|
812
688
|
else
|
|
813
689
|
this.log.info(`failed to read state ${JSON.stringify(ckey)} of ${entity.device.ieeeAddr}/${entity.device.endpoints[0].ID} after device query`);
|
|
@@ -815,8 +691,10 @@ class Zigbee extends utils.Adapter {
|
|
|
815
691
|
}
|
|
816
692
|
}
|
|
817
693
|
}
|
|
818
|
-
if (has_elevated_debug)
|
|
819
|
-
|
|
694
|
+
if (has_elevated_debug) {
|
|
695
|
+
const message = `ELEVATED O07: Device query for '${entity.device.ieeeAddr}/${entity.device.endpoints[0].ID}' complete`;
|
|
696
|
+
this.emit('device_debug', { ID:debugID, data: { flag: 'qe' , IO:false }, message:message});
|
|
697
|
+
}
|
|
820
698
|
else
|
|
821
699
|
this.log.info(`Device query for '${entity.device.ieeeAddr}' done`);
|
|
822
700
|
const idToRemove = deviceId;
|
|
@@ -840,38 +718,51 @@ class Zigbee extends utils.Adapter {
|
|
|
840
718
|
this.log.debug(`Type of toZigbee is '${typeof c}', Contains key ${(c.hasOwnProperty('key')?JSON.stringify(c.key):'false ')}`)
|
|
841
719
|
if (!c.hasOwnProperty('key'))
|
|
842
720
|
{
|
|
843
|
-
if (
|
|
721
|
+
if (converter === undefined)
|
|
844
722
|
{
|
|
845
723
|
converter = c;
|
|
846
|
-
if (has_elevated_debug)
|
|
847
|
-
|
|
724
|
+
if (has_elevated_debug) {
|
|
725
|
+
const message = `Setting converter to keyless converter for ${deviceId} of type ${model}`;
|
|
726
|
+
this.emit('device_debug', { ID:debugID, data: { flag: `s4.${msg_counter}` , IO:false }, message:message});
|
|
727
|
+
}
|
|
848
728
|
else
|
|
849
|
-
this.log.debug(`Setting converter to keyless converter for ${deviceId} of type ${model}`)
|
|
729
|
+
this.log.debug(`Setting converter to keyless converter for ${deviceId} of type ${model}`);
|
|
850
730
|
msg_counter++;
|
|
851
731
|
}
|
|
852
732
|
else
|
|
853
733
|
{
|
|
854
734
|
if (has_elevated_debug)
|
|
855
|
-
|
|
735
|
+
{
|
|
736
|
+
const message = `ignoring keyless converter for ${deviceId} of type ${model}`;
|
|
737
|
+
this.emit('device_debug', { ID:debugID, data: { flag: `i4.${msg_counter}` , IO:false} , message:message});
|
|
738
|
+
}
|
|
856
739
|
else
|
|
857
|
-
this.log.debug(`ignoring keyless converter for ${deviceId} of type ${model}`)
|
|
740
|
+
this.log.debug(`ignoring keyless converter for ${deviceId} of type ${model}`);
|
|
858
741
|
msg_counter++;
|
|
859
742
|
}
|
|
860
743
|
continue;
|
|
861
744
|
}
|
|
862
745
|
if (c.key.includes(stateDesc.prop) || c.key.includes(stateDesc.setattr) || c.key.includes(stateDesc.id))
|
|
863
746
|
{
|
|
864
|
-
|
|
865
|
-
|
|
747
|
+
const message = `${(converter===undefined?'Setting':'Overriding')}' converter to converter with key(s)'${JSON.stringify(c.key)}}`;
|
|
748
|
+
if (has_elevated_debug) {
|
|
749
|
+
this.emit('device_debugug', { ID:debugID, data: { flag: `${converter===undefined ? 's' : 'o'}4.${msg_counter}` , IO:false }, message:message});
|
|
750
|
+
|
|
751
|
+
}
|
|
866
752
|
else
|
|
867
|
-
this.log.debug(
|
|
753
|
+
this.log.debug(message);
|
|
868
754
|
converter = c;
|
|
869
755
|
msg_counter++;
|
|
870
756
|
}
|
|
871
757
|
}
|
|
872
758
|
if (converter === undefined) {
|
|
873
|
-
|
|
874
|
-
|
|
759
|
+
const message = `No converter available for '${model}' with key '${stateDesc.id}' `;
|
|
760
|
+
if (has_elevated_debug) {
|
|
761
|
+
this.emit('device_debug', { ID:debugID, data: { error: 'NOCONV',states:[{id:stateDesc.id, value:value, payload:'no converter'}] , IO:false }, message:message});
|
|
762
|
+
}
|
|
763
|
+
else {
|
|
764
|
+
this.log.warn(message);
|
|
765
|
+
}
|
|
875
766
|
return;
|
|
876
767
|
}
|
|
877
768
|
|
|
@@ -890,10 +781,12 @@ class Zigbee extends utils.Adapter {
|
|
|
890
781
|
|
|
891
782
|
const epName = stateDesc.epname !== undefined ? stateDesc.epname : (stateDesc.prop || stateDesc.id);
|
|
892
783
|
const key = stateDesc.setattr || stateDesc.prop || stateDesc.id;
|
|
893
|
-
|
|
894
|
-
|
|
784
|
+
const message = `convert ${key}, ${safeJsonStringify(preparedValue)}, ${safeJsonStringify(preparedOptions)} for device ${deviceId} with Endpoint ${epName}`;
|
|
785
|
+
if (has_elevated_debug) {
|
|
786
|
+
this.emit('device_debug', { ID:debugID, data: { flag: '04', payload: {key:key, ep: stateDesc.epname, value:preparedValue, options:preparedOptions}, IO:false }, message:message});
|
|
787
|
+
}
|
|
895
788
|
else
|
|
896
|
-
this.log.debug(
|
|
789
|
+
this.log.debug(message);
|
|
897
790
|
|
|
898
791
|
let target;
|
|
899
792
|
if (model === 'group') {
|
|
@@ -923,6 +816,9 @@ class Zigbee extends utils.Adapter {
|
|
|
923
816
|
meta.message.state = preparedValue;
|
|
924
817
|
}
|
|
925
818
|
}
|
|
819
|
+
if (has_elevated_debug) {
|
|
820
|
+
this.emit('device_debug', { ID:debugID, data: { states:[{id:stateDesc.id, value:value, payload:preparedValue, ep:stateDesc.epname}] , IO:false }});
|
|
821
|
+
}
|
|
926
822
|
|
|
927
823
|
if (preparedOptions !== undefined) {
|
|
928
824
|
if (preparedOptions.hasOwnProperty('state')) {
|
|
@@ -932,33 +828,52 @@ class Zigbee extends utils.Adapter {
|
|
|
932
828
|
|
|
933
829
|
try {
|
|
934
830
|
const result = await converter.convertSet(target, key, preparedValue, meta);
|
|
935
|
-
|
|
936
|
-
|
|
831
|
+
const message = `convert result ${safeJsonStringify(result)} for device ${deviceId}`;
|
|
832
|
+
if (has_elevated_debug) {
|
|
833
|
+
this.emit('device_debug', { ID:debugID, data: { flag: 'SUCCESS' , IO:false }, message:message});
|
|
834
|
+
}
|
|
937
835
|
else
|
|
938
|
-
this.log.debug(
|
|
836
|
+
this.log.debug(message);
|
|
939
837
|
if (result !== undefined) {
|
|
940
|
-
if (stateModel && !isGroup) {
|
|
838
|
+
if (stateModel && !isGroup && !stateDesc.noack) {
|
|
941
839
|
this.acknowledgeState(deviceId, model, stateDesc, value);
|
|
942
840
|
}
|
|
943
841
|
// process sync state list
|
|
944
842
|
this.processSyncStatesList(deviceId, model, syncStateList);
|
|
945
843
|
}
|
|
946
|
-
else
|
|
947
|
-
if (has_elevated_debug)
|
|
948
|
-
|
|
949
|
-
|
|
844
|
+
else {
|
|
845
|
+
if (has_elevated_debug) {
|
|
846
|
+
const message = `Convert does not return a result result for ${key} with ${safeJsonStringify(preparedValue)} on device ${deviceId}.`;
|
|
847
|
+
this.emit('device_debug', { ID:debugID, data: { flag: '06' , IO:false }, message:message});
|
|
848
|
+
}
|
|
849
|
+
}
|
|
950
850
|
} catch (error) {
|
|
951
|
-
if (has_elevated_debug)
|
|
952
|
-
|
|
851
|
+
if (has_elevated_debug) {
|
|
852
|
+
const message = `caught error ${safeJsonStringify(error)} when setting value for device ${deviceId}.`;
|
|
853
|
+
this.emit('device_debug', { ID:debugID, data: { error: 'EXSET' , IO:false },message:message});
|
|
854
|
+
}
|
|
953
855
|
this.filterError(`Error ${error.code} on send command to ${deviceId}.` +
|
|
954
856
|
` Error: ${error.stack}`, `Send command to ${deviceId} failed with`, error);
|
|
955
857
|
}
|
|
956
858
|
});
|
|
957
859
|
} catch (err) {
|
|
958
|
-
|
|
860
|
+
const message = `No entity for ${deviceId} : ${err && err.message ? err.message : 'no error message'}`;
|
|
861
|
+
this.emit('device_debug', { ID:debugID, data: { error: 'EXPUB' , IO:false }, message:message});
|
|
959
862
|
}
|
|
960
863
|
}
|
|
961
864
|
|
|
865
|
+
|
|
866
|
+
extractEP(key, endpoints) {
|
|
867
|
+
try {
|
|
868
|
+
if (endpoints) for (const ep of Object.keys(endpoints)) {
|
|
869
|
+
if (key.endsWith('_'+ep)) return { setattr: key.replace('_'+ep, ''), epname:ep }
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
catch {
|
|
873
|
+
return {};
|
|
874
|
+
}
|
|
875
|
+
return {};
|
|
876
|
+
}
|
|
962
877
|
// This function is introduced to explicitly allow user level scripts to send Commands
|
|
963
878
|
// directly to the zigbee device. It utilizes the zigbee-herdsman-converters to generate
|
|
964
879
|
// the exact zigbee message to be sent and can be used to set device options which are
|
|
@@ -972,7 +887,6 @@ class Zigbee extends utils.Adapter {
|
|
|
972
887
|
// endpoint: optional: the endpoint to send the data to, if supported.
|
|
973
888
|
//
|
|
974
889
|
async sendPayload(payload) {
|
|
975
|
-
this.log.debug(`publishToDevice called with ${safeJsonStringify(payload)}`);
|
|
976
890
|
let payloadObj = {};
|
|
977
891
|
if (typeof payload === 'string') {
|
|
978
892
|
try {
|
|
@@ -987,7 +901,7 @@ class Zigbee extends utils.Adapter {
|
|
|
987
901
|
}
|
|
988
902
|
} else if (typeof payload === 'object') {
|
|
989
903
|
payloadObj = payload;
|
|
990
|
-
}
|
|
904
|
+
} else return { success: false, error: 'illegal type of payload: ' + typeof payload};
|
|
991
905
|
|
|
992
906
|
if (payloadObj.hasOwnProperty('device') && payloadObj.hasOwnProperty('payload')) {
|
|
993
907
|
try {
|
|
@@ -1012,16 +926,24 @@ class Zigbee extends utils.Adapter {
|
|
|
1012
926
|
this.sendError(`Illegal payload type for ${safeJsonStringify(payloadObj.device)}`);
|
|
1013
927
|
return {success: false, error: `Illegal payload type for ${safeJsonStringify(payloadObj.device)}`};
|
|
1014
928
|
}
|
|
929
|
+
const endpoints = mappedModel && mappedModel.endpoint ? mappedModel.endpoint(entity.device) : null;
|
|
1015
930
|
for (const key in payloadObj.payload) {
|
|
1016
931
|
if (payloadObj.payload[key] != undefined) {
|
|
1017
932
|
const datatype = typeof payloadObj.payload[key];
|
|
933
|
+
const epobj = this.extractEP(key, endpoints);
|
|
934
|
+
if (payloadObj.endpoint) {
|
|
935
|
+
epobj.epname = payloadObj.endpoint;
|
|
936
|
+
delete epobj.setattr;
|
|
937
|
+
}
|
|
1018
938
|
stateList.push({
|
|
1019
939
|
stateDesc: {
|
|
1020
940
|
id: key,
|
|
1021
941
|
prop: key,
|
|
1022
942
|
role: 'state',
|
|
1023
943
|
type: datatype,
|
|
1024
|
-
|
|
944
|
+
noack:true,
|
|
945
|
+
epname: epobj.epname,
|
|
946
|
+
setattr: epobj.setattr,
|
|
1025
947
|
},
|
|
1026
948
|
value: payloadObj.payload[key],
|
|
1027
949
|
index: 0,
|
|
@@ -1030,8 +952,7 @@ class Zigbee extends utils.Adapter {
|
|
|
1030
952
|
}
|
|
1031
953
|
}
|
|
1032
954
|
try {
|
|
1033
|
-
this.
|
|
1034
|
-
await this.publishFromState(`0x${payload.device}`, '', undefined, stateList, payload.options);
|
|
955
|
+
await this.publishFromState(`0x${payload.device}`, payload.model, payload.stateModel, stateList, payload.options, Date.now());
|
|
1035
956
|
return {success: true};
|
|
1036
957
|
} catch (error) {
|
|
1037
958
|
this.log.error(`Error ${error.code} on send command to ${payload.device}.` + ` Error: ${error.stack} ` + `Send command to ${payload.device} failed with ` + error);
|
|
@@ -1048,13 +969,18 @@ class Zigbee extends utils.Adapter {
|
|
|
1048
969
|
|
|
1049
970
|
|
|
1050
971
|
newDevice(entity) {
|
|
1051
|
-
this.log.debug(`New device event: ${safeJsonStringify(entity)}`);
|
|
1052
|
-
this.stController.AddModelFromHerdsman(entity.device, entity.mapped.model)
|
|
1053
972
|
const dev = entity.device;
|
|
973
|
+
const model = (entity.mapped) ? entity.mapped.model : dev.modelID;
|
|
974
|
+
this.log.debug(`New device event: ${safeJsonStringify(entity)}`);
|
|
975
|
+
if (!entity.mapped && !entity.device.interviewing) {
|
|
976
|
+
const msg = `New device: '${dev.ieeeAddr}' does not have a known model. please provide an external converter for '${dev.modelID}'.`;
|
|
977
|
+
this.log.warn(msg);
|
|
978
|
+
this.logToPairing(msg, true);
|
|
979
|
+
}
|
|
980
|
+
this.stController.AddModelFromHerdsman(entity.device, model)
|
|
1054
981
|
if (dev) {
|
|
1055
982
|
this.getObject(dev.ieeeAddr.substr(2), (err, obj) => {
|
|
1056
983
|
if (!obj) {
|
|
1057
|
-
const model = (entity.mapped) ? entity.mapped.model : entity.device.modelID;
|
|
1058
984
|
this.log.debug(`new device ${dev.ieeeAddr} ${dev.networkAddress} ${model} `);
|
|
1059
985
|
this.logToPairing(`New device joined '${dev.ieeeAddr}' model ${model}`, true);
|
|
1060
986
|
this.stController.updateDev(dev.ieeeAddr.substr(2), model, model, () =>
|
|
@@ -1123,7 +1049,7 @@ class Zigbee extends utils.Adapter {
|
|
|
1123
1049
|
getZigbeeOptions() {
|
|
1124
1050
|
// file path for db
|
|
1125
1051
|
let dbDir = path.join(utils.getAbsoluteInstanceDataDir(this), '');
|
|
1126
|
-
dbDir = dbDir.replace('.', '
|
|
1052
|
+
dbDir = dbDir.replace('zigbee.', 'zigbee_');
|
|
1127
1053
|
|
|
1128
1054
|
if (this.systemConfig && !fs.existsSync(dbDir)) {
|
|
1129
1055
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.zigbee",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"author": {
|
|
5
5
|
"name": "Kirov Ilya",
|
|
6
6
|
"email": "kirovilya@gmail.com"
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"ajv": "^8.17.1",
|
|
29
29
|
"uri-js": "^4.4.1",
|
|
30
30
|
"typescript": "^5.6.3",
|
|
31
|
-
"zigbee-herdsman": "3.
|
|
32
|
-
"zigbee-herdsman-converters": "
|
|
31
|
+
"zigbee-herdsman": "^3.3.0",
|
|
32
|
+
"zigbee-herdsman-converters": "23.6.0"
|
|
33
33
|
},
|
|
34
34
|
"description": "Zigbee devices",
|
|
35
35
|
"devDependencies": {
|
|
@@ -47,7 +47,8 @@
|
|
|
47
47
|
"gulp-jsdoc3": "^3.0.0",
|
|
48
48
|
"gulp-replace": "^1.1.4",
|
|
49
49
|
"mixin-deep": "^2.0.1",
|
|
50
|
-
"mocha": "^11.1.0"
|
|
50
|
+
"mocha": "^11.1.0",
|
|
51
|
+
"@iobroker/dev-server": "^0.7.8"
|
|
51
52
|
},
|
|
52
53
|
"homepage": "https://github.com/ioBroker/ioBroker.zigbee",
|
|
53
54
|
"keywords": [
|
|
@@ -85,7 +86,6 @@
|
|
|
85
86
|
"test:package": "mocha test/package --exit",
|
|
86
87
|
"test:unit": "mocha test/unit --exit",
|
|
87
88
|
"test:integration": "mocha test/integration --exit",
|
|
88
|
-
"watch:parcel": "parcel admin/src/index.tsx -d admin/build --hmr-port 1235",
|
|
89
89
|
"test:js": "mocha --opts test/mocha.custom.opts",
|
|
90
90
|
"lint": "eslint .",
|
|
91
91
|
"release": "release-script",
|