This repository has been archived on 2026-05-31. You can view files and clone it, but cannot push or open issues or pull requests.
powersync-zigbee/libnymea-zigbee/zdo/zigbeedeviceobject.cpp
Michael Zanetti 806065ff5c Timeout ZDO replies
I've been observing devices that won't reply to ZDO node descriptor requests,
at least not on the first attempt as well as out-of-spec devices which claim
to have an endpoint x but then won't reply on getting the endpoint descriptor.

I have a Lumi (lumi.sensor_switch.aq2) button here which allows to reproduce
both of those. It will never reply on the first attemt.

Anyhow, it happens that the ZDO, waiting on the data indication never finishes
because there does not seem to be a timeout connected to it and it fails initialisation.
Adding a timeout, the code retries and it seems to succeed on the second attempt.
2022-03-07 12:07:40 +01:00

747 lines
31 KiB
C++

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea-zigbee.
* This project including source code and documentation is protected by copyright law, and
* remains the property of nymea GmbH. All rights, including reproduction, publication,
* editing and translation, are reserved. The use of this project is subject to the terms of a
* license agreement to be concluded with nymea GmbH in accordance with the terms
* of use of nymea GmbH, available under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation; version 3.
* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this project.
* If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under contact@nymea.io
* or see our FAQ/Licensing Information on https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "zigbeedeviceobject.h"
#include "zigbeenetwork.h"
#include "loggingcategory.h"
#include "zigbeedeviceprofile.h"
#include "zigbeeutils.h"
#include <QDataStream>
#include <QPointer>
ZigbeeDeviceObject::ZigbeeDeviceObject(ZigbeeNetwork *network, ZigbeeNode *node, QObject *parent) :
QObject(parent),
m_network(network),
m_node(node)
{
}
ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestNetworkAddress()
{
qCDebug(dcZigbeeDeviceObject()) << "Request network address from" << m_node;
// Build APS request and make ieee address request
ZigbeeNetworkRequest request;
request.setRequestId(m_network->generateSequenceNumber());
request.setDestinationAddressMode(Zigbee::DestinationAddressModeIeeeAddress);
request.setDestinationIeeeAddress(m_node->extendedAddress());
request.setDestinationEndpoint(0); // ZDO
request.setProfileId(Zigbee::ZigbeeProfileDevice); // ZDP
request.setClusterId(ZigbeeDeviceProfile::NetworkAddressRequest);
request.setSourceEndpoint(0); // ZDO
// Generate a new transaction sequence number for this device object
quint8 transactionSequenceNumber = m_transactionSequenceNumber++;
// Build ZDO frame
QByteArray asdu;
QDataStream stream(&asdu, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << transactionSequenceNumber;
stream << m_node->extendedAddress().toUInt64();
stream << static_cast<quint8>(0); // Note: 0 = single device response, 1 = extended request
stream << static_cast<quint8>(0); // Start index
// Set the ZDO frame as APS request payload
request.setAsdu(asdu);
// Create the device object reply and wait for the response indication
ZigbeeDeviceObjectReply *zdoReply = createZigbeeDeviceObjectReply(request, transactionSequenceNumber);
// Send the request, on finished read the confirm information
ZigbeeNetworkReply *networkReply = m_network->sendRequest(request);
connect(networkReply, &ZigbeeNetworkReply::finished, zdoReply, [this, networkReply, zdoReply](){
if (!verifyNetworkError(zdoReply, networkReply)) {
finishZdoReply(zdoReply);
return;
}
// The request was successfully sent to the device
// Now check if the expected indication response received already
if (zdoReply->isComplete()) {
finishZdoReply(zdoReply);
return;
}
// We received the confirmation but not yet the indication
});
return zdoReply;
}
ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestIeeeAddress()
{
qCDebug(dcZigbeeDeviceObject()) << "Request IEEE address from" << m_node;
// Build APS request
ZigbeeNetworkRequest request = buildZdoRequest(ZigbeeDeviceProfile::IeeeAddressRequest);
// Generate a new transaction sequence number for this device object
quint8 transactionSequenceNumber = m_transactionSequenceNumber++;
// Build ZDO frame
QByteArray asdu;
QDataStream stream(&asdu, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << transactionSequenceNumber << m_node->shortAddress();
stream << static_cast<quint8>(0); // Note: 0 = single device response, 1 = extended request
stream << static_cast<quint8>(0); // Start index
// Set the ZDO frame as APS request payload
request.setAsdu(asdu);
// Create the device object reply and wait for the response indication
ZigbeeDeviceObjectReply *zdoReply = createZigbeeDeviceObjectReply(request, transactionSequenceNumber);
// Send the request, on finished read the confirm information
ZigbeeNetworkReply *networkReply = m_network->sendRequest(request);
connect(networkReply, &ZigbeeNetworkReply::finished, zdoReply, [this, networkReply, zdoReply](){
if (!verifyNetworkError(zdoReply, networkReply)) {
finishZdoReply(zdoReply);
return;
}
// The request was successfully sent to the device
// Now check if the expected indication response received already
if (zdoReply->isComplete()) {
finishZdoReply(zdoReply);
return;
}
// We received the confirmation but not yet the indication
});
return zdoReply;
}
ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestNodeDescriptor()
{
qCDebug(dcZigbeeDeviceObject()) << "Request node descriptor from" << m_node;
// Build APS request
ZigbeeNetworkRequest request = buildZdoRequest(ZigbeeDeviceProfile::NodeDescriptorRequest);
// Generate a new transaction sequence number for this device object
quint8 transactionSequenceNumber = m_transactionSequenceNumber++;
// Build ZDO frame
QByteArray asdu;
QDataStream stream(&asdu, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << transactionSequenceNumber << m_node->shortAddress();
// Set the ZDO frame as APS request payload
request.setAsdu(asdu);
// Create the device object reply and wait for the response indication
ZigbeeDeviceObjectReply *zdoReply = createZigbeeDeviceObjectReply(request, transactionSequenceNumber);
// Send the request, on finished read the confirm information
ZigbeeNetworkReply *networkReply = m_network->sendRequest(request);
connect(networkReply, &ZigbeeNetworkReply::finished, zdoReply, [this, networkReply, zdoReply](){
if (!verifyNetworkError(zdoReply, networkReply)) {
finishZdoReply(zdoReply);
return;
}
// The request was successfully sent to the device
// Now check if the expected indication response received already
if (zdoReply->isComplete()) {
finishZdoReply(zdoReply);
return;
}
// We received the confirmation but not yet the indication
});
return zdoReply;
}
ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestPowerDescriptor()
{
qCDebug(dcZigbeeDeviceObject()) << "Request power descriptor from" << m_node;
// Build APS request
ZigbeeNetworkRequest request = buildZdoRequest(ZigbeeDeviceProfile::PowerDescriptorRequest);
// Generate a new transaction sequence number for this device object
quint8 transactionSequenceNumber = m_transactionSequenceNumber++;
// Build ZDO frame
QByteArray asdu;
QDataStream stream(&asdu, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << transactionSequenceNumber << m_node->shortAddress();
// Set the ZDO frame as APS request payload
request.setAsdu(asdu);
// Create the device object reply and wait for the response indication
ZigbeeDeviceObjectReply *zdoReply = createZigbeeDeviceObjectReply(request, transactionSequenceNumber);
// Send the request, on finished read the confirm information
ZigbeeNetworkReply *networkReply = m_network->sendRequest(request);
connect(networkReply, &ZigbeeNetworkReply::finished, zdoReply, [this, networkReply, zdoReply](){
if (!verifyNetworkError(zdoReply, networkReply)) {
finishZdoReply(zdoReply);
return;
}
// The request was successfully sent to the device
// Now check if the expected indication response received already
if (zdoReply->isComplete()) {
finishZdoReply(zdoReply);
return;
}
// We received the confirmation but not yet the indication
});
return zdoReply;
}
ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestActiveEndpoints()
{
qCDebug(dcZigbeeDeviceObject()) << "Request active endpoints from" << m_node;
// Build APS request
ZigbeeNetworkRequest request = buildZdoRequest(ZigbeeDeviceProfile::ActiveEndpointsRequest);
// Generate a new transaction sequence number for this device object
quint8 transactionSequenceNumber = m_transactionSequenceNumber++;
// Build ZDO frame
QByteArray asdu;
QDataStream stream(&asdu, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << transactionSequenceNumber << m_node->shortAddress();
// Set the ZDO frame as APS request payload
request.setAsdu(asdu);
// Create the device object reply and wait for the response indication
ZigbeeDeviceObjectReply *zdoReply = createZigbeeDeviceObjectReply(request, transactionSequenceNumber);
// Send the request, on finished read the confirm information
ZigbeeNetworkReply *networkReply = m_network->sendRequest(request);
connect(networkReply, &ZigbeeNetworkReply::finished, zdoReply, [this, networkReply, zdoReply](){
if (!verifyNetworkError(zdoReply, networkReply)) {
finishZdoReply(zdoReply);
return;
}
// The request was successfully sent to the device
// Now check if the expected indication response received already
if (zdoReply->isComplete()) {
finishZdoReply(zdoReply);
return;
}
// We received the confirmation but not yet the indication
});
return zdoReply;
}
ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestSimpleDescriptor(quint8 endpointId)
{
qCDebug(dcZigbeeDeviceObject()) << "Request simple descriptor from" << m_node << "endpoint" << endpointId;
// Build APS request
ZigbeeNetworkRequest request = buildZdoRequest(ZigbeeDeviceProfile::SimpleDescriptorRequest);
// Generate a new transaction sequence number for this device object
quint8 transactionSequenceNumber = m_transactionSequenceNumber++;
// Build ZDO frame
QByteArray asdu;
QDataStream stream(&asdu, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << transactionSequenceNumber << request.destinationShortAddress() << endpointId;
// Set the ZDO frame as APS request payload
request.setAsdu(asdu);
// Create the device object reply and wait for the response indication
ZigbeeDeviceObjectReply *zdoReply = createZigbeeDeviceObjectReply(request, transactionSequenceNumber);
// Send the request, on finished read the confirm information
ZigbeeNetworkReply *networkReply = m_network->sendRequest(request);
connect(networkReply, &ZigbeeNetworkReply::finished, zdoReply, [this, networkReply, zdoReply](){
if (!verifyNetworkError(zdoReply, networkReply)) {
finishZdoReply(zdoReply);
return;
}
// The request was successfully sent to the device
// Now check if the expected indication response received already
if (zdoReply->isComplete()) {
finishZdoReply(zdoReply);
return;
}
// We received the confirmation but not yet the indication
});
return zdoReply;
}
ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestBindGroupAddress(quint8 sourceEndpointId, quint16 clusterId, quint16 destinationAddress)
{
qCDebug(dcZigbeeDeviceObject()) << "Requesting bind group address from" << m_node << "endpoint" << ZigbeeUtils::convertByteToHexString(sourceEndpointId)
<< static_cast<ZigbeeClusterLibrary::ClusterId>(clusterId) << "to group" << ZigbeeUtils::convertUint16ToHexString(destinationAddress);
// Build APS request
ZigbeeNetworkRequest request = buildZdoRequest(ZigbeeDeviceProfile::BindRequest);
// Generate a new transaction sequence number for this device object
quint8 transactionSequenceNumber = m_transactionSequenceNumber++;
// Build ZDO frame
QByteArray asdu;
QDataStream stream(&asdu, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << transactionSequenceNumber;
stream << m_node->extendedAddress().toUInt64();
stream << sourceEndpointId;
stream << clusterId;
stream << static_cast<quint8>(Zigbee::DestinationAddressModeGroup);
stream << destinationAddress;
// Set the ZDO frame as APS request payload
request.setAsdu(asdu);
// Create the device object reply and wait for the response indication
ZigbeeDeviceObjectReply *zdoReply = createZigbeeDeviceObjectReply(request, transactionSequenceNumber);
// Send the request, on finished read the confirm information
ZigbeeNetworkReply *networkReply = m_network->sendRequest(request);
connect(networkReply, &ZigbeeNetworkReply::finished, zdoReply, [this, networkReply, zdoReply](){
if (!verifyNetworkError(zdoReply, networkReply)) {
finishZdoReply(zdoReply);
return;
}
// The request was successfully sent to the device
// Now check if the expected indication response received already
if (zdoReply->isComplete()) {
finishZdoReply(zdoReply);
return;
}
// We received the confirmation but not yet the indication
});
return zdoReply;
}
ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestBindIeeeAddress(quint8 sourceEndpointId, quint16 clusterId, const ZigbeeAddress &destinationIeeeAddress, quint8 destinationEndpointId)
{
qCDebug(dcZigbeeDeviceObject()) << "Request bind IEEE address from" << m_node << "endpoint" << ZigbeeUtils::convertByteToHexString(sourceEndpointId)
<< static_cast<ZigbeeClusterLibrary::ClusterId>(clusterId) << "to" << destinationIeeeAddress.toString() << "endpoint"
<< ZigbeeUtils::convertByteToHexString(destinationEndpointId);
// Build APS request
ZigbeeNetworkRequest request = buildZdoRequest(ZigbeeDeviceProfile::BindRequest);
// Generate a new transaction sequence number for this device object
quint8 transactionSequenceNumber = m_transactionSequenceNumber++;
// Build ZDO frame
QByteArray asdu;
QDataStream stream(&asdu, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << transactionSequenceNumber;
stream << m_node->extendedAddress().toUInt64();
stream << sourceEndpointId;
stream << clusterId;
stream << static_cast<quint8>(Zigbee::DestinationAddressModeIeeeAddress);
stream << destinationIeeeAddress.toUInt64();
stream << destinationEndpointId;
// Set the ZDO frame as APS request payload
request.setAsdu(asdu);
// Create the device object reply and wait for the response indication
ZigbeeDeviceObjectReply *zdoReply = createZigbeeDeviceObjectReply(request, transactionSequenceNumber);
// Send the request, on finished read the confirm information
ZigbeeNetworkReply *networkReply = m_network->sendRequest(request);
connect(networkReply, &ZigbeeNetworkReply::finished, zdoReply, [this, networkReply, zdoReply](){
if (!verifyNetworkError(zdoReply, networkReply)) {
finishZdoReply(zdoReply);
return;
}
// The request was successfully sent to the device
// Now check if the expected indication response received already
if (zdoReply->isComplete()) {
finishZdoReply(zdoReply);
return;
}
// We received the confirmation but not yet the indication
});
return zdoReply;
}
ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestUnbind(const ZigbeeDeviceProfile::BindingTableListRecord &bindingRecord)
{
qCDebug(dcZigbeeDeviceObject()) << "Request unbind" << m_node << bindingRecord;
// Build APS request
ZigbeeNetworkRequest request = buildZdoRequest(ZigbeeDeviceProfile::UnbindRequest);
// Generate a new transaction sequence number for this device object
quint8 transactionSequenceNumber = m_transactionSequenceNumber++;
// Build ZDO frame
QByteArray asdu;
QDataStream stream(&asdu, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << transactionSequenceNumber;
stream << bindingRecord.sourceAddress.toUInt64();
stream << bindingRecord.sourceEndpoint;
stream << bindingRecord.clusterId;
stream << static_cast<quint8>(bindingRecord.destinationAddressMode);
if (bindingRecord.destinationAddressMode == Zigbee::DestinationAddressModeGroup) {
stream << bindingRecord.destinationAddressShort;
} else {
stream << bindingRecord.destinationAddress.toUInt64();
stream << bindingRecord.destinationEndpoint;
}
// Set the ZDO frame as APS request payload
request.setAsdu(asdu);
// Create the device object reply and wait for the response indication
ZigbeeDeviceObjectReply *zdoReply = createZigbeeDeviceObjectReply(request, transactionSequenceNumber);
// Send the request, on finished read the confirm information
ZigbeeNetworkReply *networkReply = m_network->sendRequest(request);
connect(networkReply, &ZigbeeNetworkReply::finished, zdoReply, [this, networkReply, zdoReply](){
if (!verifyNetworkError(zdoReply, networkReply)) {
finishZdoReply(zdoReply);
return;
}
// The request was successfully sent to the device
// Now check if the expected indication response received already
if (zdoReply->isComplete()) {
finishZdoReply(zdoReply);
return;
}
// We received the confirmation but not yet the indication
});
return zdoReply;
}
ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestBindRegister(const ZigbeeAddress &ieeeAddress)
{
qCDebug(dcZigbeeDeviceObject()) << "Request bind register" << m_node << ieeeAddress.toString();
// Build APS request
ZigbeeNetworkRequest request = buildZdoRequest(ZigbeeDeviceProfile::BindRegisterRequest);
// Generate a new transaction sequence number for this device object
quint8 transactionSequenceNumber = m_transactionSequenceNumber++;
// Build ZDO frame
QByteArray asdu;
QDataStream stream(&asdu, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << transactionSequenceNumber;
stream << ieeeAddress.toUInt64();
// Set the ZDO frame as APS request payload
request.setAsdu(asdu);
// Create the device object reply and wait for the response indication
ZigbeeDeviceObjectReply *zdoReply = createZigbeeDeviceObjectReply(request, transactionSequenceNumber);
// Send the request, on finished read the confirm information
ZigbeeNetworkReply *networkReply = m_network->sendRequest(request);
connect(networkReply, &ZigbeeNetworkReply::finished, zdoReply, [this, networkReply, zdoReply](){
if (!verifyNetworkError(zdoReply, networkReply)) {
finishZdoReply(zdoReply);
return;
}
// The request was successfully sent to the device
// Now check if the expected indication response received already
if (zdoReply->isComplete()) {
finishZdoReply(zdoReply);
return;
}
// We received the confirmation but not yet the indication
});
return zdoReply;
}
ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestMgmtLeaveNetwork(bool rejoin, bool removeChildren)
{
qCDebug(dcZigbeeDeviceObject()) << "Request management leave network from" << m_node << "rejoin" << rejoin << "remove children" << removeChildren;
// Build APS request
ZigbeeNetworkRequest request = buildZdoRequest(ZigbeeDeviceProfile::MgmtLeaveRequest);
// Generate a new transaction sequence number for this device object
quint8 transactionSequenceNumber = m_transactionSequenceNumber++;
// Build ZDO frame
quint8 leaveFlag = 0;
if (rejoin) {
leaveFlag |= 0x01;
}
if (removeChildren) {
leaveFlag |= 0x02;
}
QByteArray asdu;
QDataStream stream(&asdu, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << transactionSequenceNumber << m_node->extendedAddress().toUInt64() << leaveFlag;
// Set the ZDO frame as APS request payload
request.setAsdu(asdu);
// Create the device object reply and wait for the response indication
ZigbeeDeviceObjectReply *zdoReply = createZigbeeDeviceObjectReply(request, transactionSequenceNumber);
// Send the request, on finished read the confirm information
ZigbeeNetworkReply *networkReply = m_network->sendRequest(request);
connect(networkReply, &ZigbeeNetworkReply::finished, zdoReply, [this, networkReply, zdoReply](){
if (!verifyNetworkError(zdoReply, networkReply)) {
finishZdoReply(zdoReply);
return;
}
// The request was successfully sent to the device
// Now check if the expected indication response received already
if (zdoReply->isComplete()) {
finishZdoReply(zdoReply);
return;
}
// We received the confirmation but not yet the indication
});
return zdoReply;
}
ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestMgmtLqi(quint8 startIndex)
{
qCDebug(dcZigbeeDeviceObject()) << "Request lqi table from" << m_node << "start index" << startIndex;
// Build APS request
ZigbeeNetworkRequest request = buildZdoRequest(ZigbeeDeviceProfile::MgmtLqiRequest);
// Generate a new transaction sequence number for this device object
quint8 transactionSequenceNumber = m_transactionSequenceNumber++;
QByteArray asdu;
QDataStream stream(&asdu, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << transactionSequenceNumber << startIndex;
// Set the ZDO frame as APS request payload
request.setAsdu(asdu);
// Create the device object reply and wait for the response indication
ZigbeeDeviceObjectReply *zdoReply = createZigbeeDeviceObjectReply(request, transactionSequenceNumber);
// Send the request, on finished read the confirm information
ZigbeeNetworkReply *networkReply = m_network->sendRequest(request);
connect(networkReply, &ZigbeeNetworkReply::finished, zdoReply, [this, networkReply, zdoReply](){
if (!verifyNetworkError(zdoReply, networkReply)) {
finishZdoReply(zdoReply);
return;
}
// The request was successfully sent to the device
// Now check if the expected indication response received already
if (zdoReply->isComplete()) {
qCDebug(dcZigbeeDeviceObject()) << "Successfully received response for" << static_cast<ZigbeeDeviceProfile::ZdoCommand>(networkReply->request().clusterId());
finishZdoReply(zdoReply);
return;
}
// We received the confirmation but not yet the indication
});
return zdoReply;
}
ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestMgmtBind(quint8 startIndex)
{
qCDebug(dcZigbeeDeviceObject()) << "Request management bind table from" << m_node << "start index" << startIndex;
// Build APS request
ZigbeeNetworkRequest request = buildZdoRequest(ZigbeeDeviceProfile::MgmtBindRequest);
// Generate a new transaction sequence number for this device object
quint8 transactionSequenceNumber = m_transactionSequenceNumber++;
QByteArray asdu;
QDataStream stream(&asdu, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << transactionSequenceNumber << startIndex;
// Set the ZDO frame as APS request payload
request.setAsdu(asdu);
// Create the device object reply and wait for the response indication
ZigbeeDeviceObjectReply *zdoReply = createZigbeeDeviceObjectReply(request, transactionSequenceNumber);
// Send the request, on finished read the confirm information
ZigbeeNetworkReply *networkReply = m_network->sendRequest(request);
connect(networkReply, &ZigbeeNetworkReply::finished, zdoReply, [this, networkReply, zdoReply](){
if (!verifyNetworkError(zdoReply, networkReply)) {
finishZdoReply(zdoReply);
return;
}
// The request was successfully sent to the device
// Now check if the expected indication response received already
if (zdoReply->isComplete()) {
qCDebug(dcZigbeeDeviceObject()) << "Successfully received response for" << static_cast<ZigbeeDeviceProfile::ZdoCommand>(networkReply->request().clusterId());
finishZdoReply(zdoReply);
return;
}
// We received the confirmation but not yet the indication
});
return zdoReply;
}
ZigbeeNetworkRequest ZigbeeDeviceObject::buildZdoRequest(quint16 zdoRequest)
{
ZigbeeNetworkRequest request;
request.setRequestId(m_network->generateSequenceNumber());
request.setDestinationAddressMode(Zigbee::DestinationAddressModeShortAddress);
request.setDestinationShortAddress(m_node->shortAddress());
request.setDestinationEndpoint(0); // ZDO
request.setProfileId(Zigbee::ZigbeeProfileDevice); // ZDP
request.setClusterId(zdoRequest);
request.setSourceEndpoint(0); // ZDO
return request;
}
ZigbeeDeviceObjectReply *ZigbeeDeviceObject::createZigbeeDeviceObjectReply(const ZigbeeNetworkRequest &request, quint8 transactionSequenceNumber)
{
ZigbeeDeviceObjectReply *zdoReply = new ZigbeeDeviceObjectReply(request, this);
connect(zdoReply, &ZigbeeDeviceObjectReply::finished, zdoReply, &ZigbeeDeviceObjectReply::deleteLater, Qt::QueuedConnection);
zdoReply->m_expectedResponse = static_cast<ZigbeeDeviceProfile::ZdoCommand>(request.clusterId() | 0x8000);
zdoReply->m_transactionSequenceNumber = transactionSequenceNumber;
m_pendingReplies.insert(transactionSequenceNumber, zdoReply);
return zdoReply;
}
bool ZigbeeDeviceObject::verifyNetworkError(ZigbeeDeviceObjectReply *zdoReply, ZigbeeNetworkReply *networkReply)
{
bool success = false;
switch (networkReply->error()) {
case ZigbeeNetworkReply::ErrorNoError:
// The request has been transported successfully to he destination, now
// wait for the expected indication or check if we already recieved it
zdoReply->m_apsConfirmReceived = true;
if (!zdoReply->m_zdpIndicationReceived) {
zdoReply->m_timeoutTimer.start();
}
success = true;
break;
case ZigbeeNetworkReply::ErrorInterfaceError:
zdoReply->m_error = ZigbeeDeviceObjectReply::ErrorInterfaceError;
qCWarning(dcZigbeeDeviceObject()) << "Failed to send request" << static_cast<ZigbeeDeviceProfile::ZdoCommand>(networkReply->request().clusterId()) << m_node << networkReply->error();
break;
case ZigbeeNetworkReply::ErrorTimeout:
zdoReply->m_error = ZigbeeDeviceObjectReply::ErrorTimeout;
qCWarning(dcZigbeeDeviceObject()) << "Failed to send request" << static_cast<ZigbeeDeviceProfile::ZdoCommand>(networkReply->request().clusterId()) << m_node << networkReply->error();
break;
case ZigbeeNetworkReply::ErrorNetworkOffline:
zdoReply->m_error = ZigbeeDeviceObjectReply::ErrorNetworkOffline;
qCWarning(dcZigbeeDeviceObject()) << "Failed to send request" << static_cast<ZigbeeDeviceProfile::ZdoCommand>(networkReply->request().clusterId()) << m_node << networkReply->error();
break;
case ZigbeeNetworkReply::ErrorZigbeeMacStatusError:
zdoReply->setZigbeeMacLayerStatus(networkReply->zigbeeMacStatus());
qCWarning(dcZigbeeDeviceObject()) << "Failed to send request" << static_cast<ZigbeeDeviceProfile::ZdoCommand>(networkReply->request().clusterId()) << m_node << networkReply->zigbeeMacStatus();
zdoReply->m_apsConfirmReceived = true;
break;
case ZigbeeNetworkReply::ErrorZigbeeApsStatusError:
zdoReply->m_apsConfirmReceived = true;
zdoReply->setZigbeeApsStatus(networkReply->zigbeeApsStatus());
qCWarning(dcZigbeeDeviceObject()) << "Failed to send request" << static_cast<ZigbeeDeviceProfile::ZdoCommand>(networkReply->request().clusterId()) << m_node << networkReply->zigbeeApsStatus();
zdoReply->m_apsConfirmReceived = true;
break;
case ZigbeeNetworkReply::ErrorZigbeeNwkStatusError:
zdoReply->m_apsConfirmReceived = true;
zdoReply->setZigbeeNwkLayerStatus(networkReply->zigbeeNwkStatus());
qCWarning(dcZigbeeDeviceObject()) << "Failed to send request" << static_cast<ZigbeeDeviceProfile::ZdoCommand>(networkReply->request().clusterId()) << m_node << networkReply->zigbeeNwkStatus();
zdoReply->m_apsConfirmReceived = true;
break;
}
return success;
}
void ZigbeeDeviceObject::finishZdoReply(ZigbeeDeviceObjectReply *zdoReply)
{
// Note: here all layer errors have already been set
switch(zdoReply->error()) {
case ZigbeeDeviceObjectReply::ErrorNoError:
qCDebug(dcZigbeeDeviceObject()) << "Reply finished successfully" << zdoReply->request();
break;
case ZigbeeDeviceObjectReply::ErrorZigbeeDeviceObjectStatusError:
qCWarning(dcZigbeeDeviceObject()) << "Reply finished with error" << zdoReply->request() << zdoReply->zigbeeDeviceObjectStatus();
break;
default:
break;
}
m_pendingReplies.remove(zdoReply->transactionSequenceNumber());
zdoReply->m_timeoutTimer.stop();
zdoReply->finished();
}
void ZigbeeDeviceObject::processApsDataIndication(const Zigbee::ApsdeDataIndication &indication)
{
// Check if we have a waiting ZDO reply for this data
ZigbeeDeviceProfile::Adpu asdu = ZigbeeDeviceProfile::parseAdpu(indication.asdu);
ZigbeeDeviceObjectReply *zdoReply = m_pendingReplies.value(asdu.transactionSequenceNumber);
if (zdoReply && indication.clusterId == (zdoReply->request().clusterId() | 0x8000)) {
zdoReply->m_responseData = indication.asdu;
zdoReply->m_responseAdpu = asdu;
zdoReply->setZigbeeDeviceObjectStatus(asdu.status);
zdoReply->m_zdpIndicationReceived = true;
if (zdoReply->isComplete()) {
finishZdoReply(zdoReply);
}
return;
}
qCWarning(dcZigbeeDeviceObject()) << "Unhandled ZDO indication" << m_node << indication << asdu;
}