diff --git a/lib/extension/pollLivoloSwitch.js b/lib/extension/pollLivoloSwitch.js index d1518e053..ec775d38f 100644 --- a/lib/extension/pollLivoloSwitch.js +++ b/lib/extension/pollLivoloSwitch.js @@ -18,6 +18,31 @@ class PollLivoloSwitch { this.startTimer(); } + _resetDeviceState(ieeeAddr) { + this.configured[ieeeAddr] = { + stage: 0, + retry: 0, + waitresp: false, + }; + } + + onZigbeeMessage(message, device, mappedDevice) { + if (!device) { + return; + } + + if ((message.type == 'devInterview') || + (message.type == 'devIncoming') || + (message.type == 'endDeviceAnnce') || + (message.type == 'devStatus')) { + if (this.configured.hasOwnProperty(device.ieeeAddr)) { + logger.info(`LIVOLO ${device.ieeeAddr}. (Re)joins in the network (after power off?)`); + this._resetDeviceState(device.ieeeAddr); + } + return; + } + } + startTimer() { this.clearTimer(); this.timer = setInterval(() => this.handleInterval(), interval); @@ -35,21 +60,32 @@ class PollLivoloSwitch { } _handleCommandRespSimple(err, rsp) { + this.ext.configured[this.ieeeAddr].waitresp = false; + if (err) { - logger.error(this.cid + '.' + this.ctype, 'response error:', err); - if (this.cid === 'toggle') { - this.ext.configured[this.ieeeAddr] = 0; + if (this.ctype === 'toggle') { + logger.debug(`LIVOLO ${this.ieeeAddr}. Toggle command error:`, err.message); } } else { - this.ext.configured[this.ieeeAddr] = 2; // sucessfully send command + logger.debug(`LIVOLO ${this.ieeeAddr}. Sucessfully configured`, rsp); + this.ext.configured[this.ieeeAddr].stage = 1; // sucessfully send command + this.ext.configured[this.ieeeAddr].retry = 0; this.device.status = 'online'; } } _handleCommandRespWithData(err, rsp) { + this.ext.configured[this.ieeeAddr].waitresp = false; + if (err) { - logger.info(this.cid + '.' + this.ctype, 'response error:', err); + logger.info(`LIVOLO ${this.ieeeAddr}. ${this.cid}.${this.ctype} response error:`, err.message); + if (this.ext.configured[this.ieeeAddr].retry >= 3) { + // errors in three sequental reads, stop polling, wait for a device message + this.device.status = 'offline'; + logger.info(`LIVOLO ${this.ieeeAddr}. Stopped polling after 3 unsuccessful attempts`); + } } else { + this.ext.configured[this.ieeeAddr].retry = 0; this.device.status = 'online'; if (this.ext.zigbee) { this.ext.zigbee.shepherd.emit('ind:reported', this.ep, this.cid, rsp, this.ep.last_af_msg); @@ -64,6 +100,40 @@ class PollLivoloSwitch { ep.last_af_msg = msg; } + _sendToggle(zdev, ieeeAddr, ep, retry) { + this.zigbee.queue.push((queueCallback) => { + const cfg = {}; + logger.debug(`LIVOLO ${ieeeAddr}. Sending the 'toggle' command. Retry: ${retry}`); + ep.functional('genOnOff', 'toggle', [cfg], foundationCfg, + this._handleCommandRespSimple.bind({ + device: zdev, + ieeeAddr: ieeeAddr, + cid: 'genOnOff', + ctype: 'toggle', + ext: this, + })); + + queueCallback(); + }); + } + + _sendPoll(zdev, ieeeAddr, ep, retry) { + this.zigbee.queue.push((queueCallback) => { + ep.foundation('genOnOff', 'read', [{ + attrId: 0, // onOff + }], this._handleCommandRespWithData.bind({ + device: zdev, + ieeeAddr: ieeeAddr, + ep: ep, + cid: 'genOnOff', + ctype: 'read', + ext: this, + })); + + queueCallback(); + }); + } + handleInterval() { this.zigbee.getAllClients() .filter((d) => d.manufName && d.manufName.startsWith('LIVOLO')) // LIVOLO @@ -84,31 +154,24 @@ class PollLivoloSwitch { ep.onAfIncomingMsg = this._handleAfMessage; if (!this.configured.hasOwnProperty(d.ieeeAddr)) { - this.configured[d.ieeeAddr] = 0; + this._resetDeviceState(d.ieeeAddr); } - if (!this.configured[d.ieeeAddr]) { - const cfg = {}; - // logger.info('=====>Send toggle'); - this.configured[d.ieeeAddr] = 1; // command sent, wait a result - ep.functional('genOnOff', 'toggle', [cfg], foundationCfg, - this._handleCommandRespSimple.bind({ - device: zdev, - ieeeAddr: d.ieeeAddr, - cid: 'genOnOff', - ctype: 'toggle', - ext: this, - })); - } else if (this.configured[d.ieeeAddr] === 2) { - ep.foundation('genOnOff', 'read', [{ - attrId: 0, // onOff - }], this._handleCommandRespWithData.bind({ - device: zdev, - ep: ep, - cid: 'genOnOff', - ctype: 'read', - ext: this, - })); + const state = this.configured[d.ieeeAddr]; + if (state.waitresp) { + return; + } + + if (state.retry < 3) { + if (state.stage === 0) { + state.retry += 1; + state.waitresp = true; + this._sendToggle(zdev, d.ieeeAddr, ep, state.retry); + } else if (state.stage === 1) { + state.retry += 1; + state.waitresp = true; + this._sendPoll(zdev, d.ieeeAddr, ep, state.retry); + } } } }