* Copyright (c) 2016 dresden elektronik ingenieurtechnik gmbh.
* All rights reserved.
*
* The software in this package is published under the terms of the BSD
* style license a copy of which has been included with this distribution in
* the LICENSE.txt file.
*
*/
#include "de_web_plugin_private.h"
#include "utils/utils.h"
#define CHECK_RESET_DEVICES 3000
#define WAIT_CONFIRM 2000
#define WAIT_INDICATION 5000
*/
void DeRestPluginPrivate::initResetDeviceApi()
{
resetDeviceTimer = new QTimer(this);
resetDeviceTimer->setSingleShot(true);
connect(resetDeviceTimer, SIGNAL(timeout()),
this, SLOT(resetDeviceTimerFired()));
zdpResetSeq = 0;
lastNodeAddressExt = 0;
resetDeviceState = ResetIdle;
resetDeviceTimer->start(CHECK_RESET_DEVICES);
}
*/
void DeRestPluginPrivate::checkResetState()
{
if (!apsCtrl || !isInNetwork() || searchSensorsState == SearchSensorsActive || searchLightsState == SearchLightsActive)
{
resetDeviceTimer->start(CHECK_RESET_DEVICES);
return;
}
std::vector<LightNode>::iterator i = nodes.begin();
std::vector<LightNode>::iterator end = nodes.end();
for (; i != end; ++i)
{
if ( i->state() == LightNode::StateDeleted && i->resetRetryCount() > 0)
{
uint8_t retryCount = i->resetRetryCount();
retryCount--;
i->setResetRetryCount(retryCount);
for (const LightNode &l : nodes)
{
if (l.address().ext() == i->address().ext() && l.state() == LightNode::StateNormal)
{
i->setResetRetryCount(0);
retryCount = 0;
break;
}
}
if (retryCount > 0 && i->address().ext() != lastNodeAddressExt)
{
DBG_Printf(DBG_INFO, "reset device retries: %i\n", retryCount);
lastNodeAddressExt = i->address().ext();
zdpResetSeq += 1;
i->setZdpResetSeq(zdpResetSeq);
deCONZ::ApsDataRequest req;
#if QT_VERSION < QT_VERSION_CHECK(5,15,0)
req.setTxOptions(0);
#endif
req.setDstEndpoint(ZDO_ENDPOINT);
req.setDstAddressMode(deCONZ::ApsExtAddress);
req.dstAddress().setExt(i->address().ext());
req.setProfileId(ZDP_PROFILE_ID);
req.setClusterId(ZDP_MGMT_LEAVE_REQ_CLID);
req.setSrcEndpoint(ZDO_ENDPOINT);
req.setRadius(0);
QDataStream stream(&req.asdu(), QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << zdpResetSeq;
stream << (quint64)i->address().ext();
uint8_t flags = 0;
stream << flags;
if (apsCtrlWrapper.apsdeDataRequest(req) == deCONZ::Success)
{
resetDeviceApsRequestId = req.id();
resetDeviceState = ResetWaitConfirm;
resetDeviceTimer->start(WAIT_CONFIRM);
DBG_Printf(DBG_INFO, "reset device apsdeDataRequest success\n");
return;
}
else
{
DBG_Printf(DBG_ERROR, "can't send reset device apsdeDataRequest\n");
}
}
}
lastNodeAddressExt = 0;
}
const auto now = QDateTime::currentDateTime();
std::vector<Sensor>::iterator si = sensors.begin();
std::vector<Sensor>::iterator si_end = sensors.end();
for (; si != si_end; ++si)
{
if (si->isAvailable() && si->resetRetryCount() > 0 && si->node())
{
if (!si->node()->nodeDescriptor().receiverOnWhenIdle() && si->lastRx().secsTo(now) > 6)
{
continue;
}
uint8_t retryCount = si->resetRetryCount();
retryCount--;
si->setResetRetryCount(retryCount);
DBG_Printf(DBG_INFO, "reset device retries: %i\n", retryCount);
if (retryCount > 0 && si->address().ext() != lastNodeAddressExt)
{
lastNodeAddressExt = si->address().ext();
zdpResetSeq += 1;
si->setZdpResetSeq(zdpResetSeq);
deCONZ::ApsDataRequest req;
#if QT_VERSION < QT_VERSION_CHECK(5,15,0)
req.setTxOptions(0);
#endif
req.setDstEndpoint(ZDO_ENDPOINT);
req.setDstAddressMode(deCONZ::ApsExtAddress);
req.dstAddress().setExt(si->address().ext());
req.setProfileId(ZDP_PROFILE_ID);
req.setClusterId(ZDP_MGMT_LEAVE_REQ_CLID);
req.setSrcEndpoint(ZDO_ENDPOINT);
req.setRadius(0);
QDataStream stream(&req.asdu(), QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << zdpResetSeq;
stream << (quint64)si->address().ext();
uint8_t flags = 0;
stream << flags;
if (apsCtrlWrapper.apsdeDataRequest(req) == deCONZ::Success)
{
resetDeviceApsRequestId = req.id();
resetDeviceState = ResetWaitConfirm;
resetDeviceTimer->start(WAIT_CONFIRM);
DBG_Printf(DBG_INFO, "reset device apsdeDataRequest success\n");
return;
}
else
{
DBG_Printf(DBG_ERROR, "can't send reset device apsdeDataRequest\n");
}
}
}
lastNodeAddressExt = 0;
}
resetDeviceState = ResetIdle;
resetDeviceTimer->start(CHECK_RESET_DEVICES);
}
\param success true on success
*/
void DeRestPluginPrivate::resetDeviceSendConfirm(bool success)
{
if (resetDeviceState == ResetWaitConfirm)
{
resetDeviceTimer->stop();
if (success)
{
resetDeviceState = ResetWaitIndication;
resetDeviceTimer->start(WAIT_INDICATION);
}
else
{
resetDeviceState = ResetIdle;
DBG_Printf(DBG_INFO, "reset device apsdeDataConfirm fail\n");
resetDeviceTimer->start(CHECK_RESET_DEVICES);
}
}
}
\param ind a ZDP MgmtLeave_rsp
*/
void DeRestPluginPrivate::handleMgmtLeaveRspIndication(const deCONZ::ApsDataIndication &ind)
{
if (resetDeviceState == ResetWaitIndication)
{
if (ind.asdu().size() < 2)
{
return;
}
resetDeviceTimer->stop();
QDataStream stream(ind.asdu());
stream.setByteOrder(QDataStream::LittleEndian);
quint8 seqNo;
quint8 status;
stream >> seqNo;
stream >> status;
DBG_Printf(DBG_INFO, "MgmtLeave_rsp " FMT_MAC " seq: %u, status 0x%02X \n", (unsigned long long)ind.srcAddress().ext(), seqNo, status);
if (status == deCONZ::ZdpSuccess || status == deCONZ::ZdpNotSupported)
{
std::vector<LightNode>::iterator i;
std::vector<LightNode>::iterator end = nodes.end();
for (i = nodes.begin(); i != end; ++i)
{
if (isSameAddress(ind.srcAddress(), i->address()))
{
i->setResetRetryCount(0);
if (i->state() == LightNode::StateDeleted)
{
i->item(RStateReachable)->setValue(false);
}
}
}
std::vector<Sensor>::iterator s;
std::vector<Sensor>::iterator send = sensors.end();
for (s = sensors.begin(); s != send; ++s)
{
if (isSameAddress(ind.srcAddress(), s->address()))
{
s->setResetRetryCount(0);
s->item(RConfigReachable)->setValue(false);
}
}
}
resetDeviceState = ResetIdle;
resetDeviceTimer->start(CHECK_RESET_DEVICES);
}
}
*/
void DeRestPluginPrivate::resetDeviceTimerFired()
{
switch (resetDeviceState)
{
case ResetIdle:
{
checkResetState();
}
break;
case ResetWaitConfirm:
{
DBG_Printf(DBG_INFO, "reset device wait for confirm timeout.\n");
resetDeviceState = ResetIdle;
resetDeviceTimer->start(CHECK_RESET_DEVICES);
}
break;
case ResetWaitIndication:
{
DBG_Printf(DBG_INFO, "reset device wait for indication timeout.\n");
resetDeviceState = ResetIdle;
resetDeviceTimer->start(CHECK_RESET_DEVICES);
}
break;
default:
DBG_Printf(DBG_INFO, "deleteDeviceTimerFired() unhandled state %d\n", resetDeviceState);
break;
}
}