From 882023198af7c950779f42f6492f678c77de23b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Mon, 13 Apr 2020 13:46:11 +0200 Subject: [PATCH] Fix indication matching and rely handling in network instance --- libnymea-zigbee/deconz/interface/deconz.h | 5 +- .../interface/zigbeeinterfacedeconz.cpp | 3 + .../deconz/zigbeebridgecontrollerdeconz.cpp | 69 +++-- .../deconz/zigbeebridgecontrollerdeconz.h | 7 +- .../deconz/zigbeenetworkdeconz.cpp | 129 +++++++- libnymea-zigbee/deconz/zigbeenetworkdeconz.h | 5 + libnymea-zigbee/deconz/zigbeenodedeconz.cpp | 292 ++++++++++++++++-- libnymea-zigbee/deconz/zigbeenodedeconz.h | 8 + libnymea-zigbee/zigbee.h | 7 + libnymea-zigbee/zigbeenetwork.cpp | 54 +++- libnymea-zigbee/zigbeenetwork.h | 3 +- libnymea-zigbee/zigbeenetworkreply.cpp | 20 ++ libnymea-zigbee/zigbeenetworkreply.h | 4 +- libnymea-zigbee/zigbeenetworkrequest.cpp | 50 +++ libnymea-zigbee/zigbeenetworkrequest.h | 14 + libnymea-zigbee/zigbeenode.cpp | 2 +- 16 files changed, 605 insertions(+), 67 deletions(-) diff --git a/libnymea-zigbee/deconz/interface/deconz.h b/libnymea-zigbee/deconz/interface/deconz.h index 3cf9485..3ea785b 100644 --- a/libnymea-zigbee/deconz/interface/deconz.h +++ b/libnymea-zigbee/deconz/interface/deconz.h @@ -64,7 +64,10 @@ public: CommandVersion = 0x0D, CommandApsDataRequest = 0x12, CommandApsDataConfirm = 0x04, - CommandApsDataIndication = 0x17 + CommandApsDataIndication = 0x17, + CommandGreenPower = 0x19, + CommandMacPoll = 0x1C, + CommandSimplifiedBeacon = 0x1F }; Q_ENUM(Command) diff --git a/libnymea-zigbee/deconz/interface/zigbeeinterfacedeconz.cpp b/libnymea-zigbee/deconz/interface/zigbeeinterfacedeconz.cpp index fcc8230..94c6ee0 100644 --- a/libnymea-zigbee/deconz/interface/zigbeeinterfacedeconz.cpp +++ b/libnymea-zigbee/deconz/interface/zigbeeinterfacedeconz.cpp @@ -172,6 +172,7 @@ void ZigbeeInterfaceDeconz::onReadyRead() } // Checksum verified, we got valid data + qCDebug(dcZigbeeInterface()) << "Received frame" << ZigbeeUtils::convertByteArrayToHexString(frame); emit packageReceived(package); } m_dataBuffer.clear(); @@ -206,6 +207,8 @@ void ZigbeeInterfaceDeconz::sendPackage(const QByteArray &package) frameStream.setByteOrder(QDataStream::LittleEndian); frameStream << checksum; + qCDebug(dcZigbeeInterface()) << "Send frame" << ZigbeeUtils::convertByteArrayToHexString(frame); + // Escape data according to SLIP for transfere QByteArray serializedData = escapeData(frame); diff --git a/libnymea-zigbee/deconz/zigbeebridgecontrollerdeconz.cpp b/libnymea-zigbee/deconz/zigbeebridgecontrollerdeconz.cpp index 6163b81..84b5604 100644 --- a/libnymea-zigbee/deconz/zigbeebridgecontrollerdeconz.cpp +++ b/libnymea-zigbee/deconz/zigbeebridgecontrollerdeconz.cpp @@ -25,10 +25,11 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -#include "zigbeebridgecontrollerdeconz.h" -#include "loggingcategory.h" #include "zigbeeutils.h" +#include "loggingcategory.h" +#include "zigbeechannelmask.h" #include "zigbeedeviceprofile.h" +#include "zigbeebridgecontrollerdeconz.h" #include @@ -220,19 +221,19 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestQuerySendDataCo return createReply(Deconz::CommandApsDataConfirm, sequenceNumber, this); } -ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendDataGroup(quint8 requestId, quint16 groupAddress, quint8 destinationEndpoint, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, quint8 radius) +ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendDataGroup(quint8 requestId, quint16 groupAddress, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, Zigbee::ZigbeeTxOptions txOptions, quint8 radius) { quint8 sequenceNumber = generateSequenceNumber(); qCDebug(dcZigbeeController()) << "Request enqueue send data to group" << ZigbeeUtils::convertUint16ToHexString(groupAddress) << "SQN:" << sequenceNumber - << ZigbeeUtils::convertByteToHexString(destinationEndpoint) - << profileId << clusterId + << static_cast(profileId) + << ZigbeeUtils::convertUint16ToHexString(clusterId) << ZigbeeUtils::convertByteToHexString(sourceEndpoint); Q_ASSERT_X(asdu.length() <= 127, "ASDU", "ASDU package length has to <= 127 bytes"); - // Note: 15 protocol bytes + asdu package length - quint16 payloadLength = static_cast(15 + asdu.length()); + // Note: 14 protocol bytes + asdu package length + quint16 payloadLength = static_cast(14 + asdu.length()); QByteArray message; QDataStream stream(&message, QIODevice::WriteOnly); @@ -245,7 +246,7 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendData stream << requestId; stream << static_cast(0); // Flags stream << static_cast(Zigbee::DestinationAddressModeGroup); - stream << groupAddress << destinationEndpoint; + stream << groupAddress; stream << profileId; stream << clusterId; stream << sourceEndpoint; @@ -253,7 +254,7 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendData for (int i = 0; i < asdu.length(); i++) { stream << static_cast(asdu.at(i)); } - stream << static_cast(0x04); // TX Options: Use ASP ACKs + stream << static_cast(txOptions); stream << radius; m_interface->sendPackage(message); @@ -261,13 +262,14 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendData return createReply(Deconz::CommandApsDataRequest, sequenceNumber, this); } -ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendDataShortAddress(quint8 requestId, quint16 shortAddress, quint8 destinationEndpoint, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, quint8 radius) +ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendDataShortAddress(quint8 requestId, quint16 shortAddress, quint8 destinationEndpoint, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, Zigbee::ZigbeeTxOptions txOptions, quint8 radius) { quint8 sequenceNumber = generateSequenceNumber(); qCDebug(dcZigbeeController()) << "Request enqueue send data to short address" << ZigbeeUtils::convertUint16ToHexString(shortAddress) << "SQN:" << sequenceNumber << ZigbeeUtils::convertByteToHexString(destinationEndpoint) - << profileId << clusterId + << static_cast(profileId) + << ZigbeeUtils::convertUint16ToHexString(clusterId) << ZigbeeUtils::convertByteToHexString(sourceEndpoint); Q_ASSERT_X(asdu.length() <= 127, "ASDU", "ASDU package length has to <= 127 bytes"); @@ -294,7 +296,7 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendData for (int i = 0; i < asdu.length(); i++) { stream << static_cast(asdu.at(i)); } - stream << static_cast(0x04); // TX Options: Use ASP ACKs + stream << static_cast(txOptions); // TX Options: Use ASP ACKs stream << radius; m_interface->sendPackage(message); @@ -302,7 +304,7 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendData return createReply(Deconz::CommandApsDataRequest, sequenceNumber, this); } -ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendDataIeeeAddress(quint8 requestId, ZigbeeAddress ieeeAddress, quint8 destinationEndpoint, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, quint8 radius) +ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendDataIeeeAddress(quint8 requestId, ZigbeeAddress ieeeAddress, quint8 destinationEndpoint, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, Zigbee::ZigbeeTxOptions txOptions, quint8 radius) { quint8 sequenceNumber = generateSequenceNumber(); qCDebug(dcZigbeeController()) << "Request enqueue send data to IEEE address" << ieeeAddress.toString() @@ -335,7 +337,7 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendData for (int i = 0; i < asdu.length(); i++) { stream << static_cast(asdu.at(i)); } - stream << static_cast(0x04); // TX Options: Use ASP ACKs + stream << static_cast(txOptions); // TX Options: Use ASP ACKs stream << radius; m_interface->sendPackage(message); @@ -349,18 +351,18 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestSendRequest(con switch (request.destinationAddressMode()) { case Zigbee::DestinationAddressModeGroup: interfaceReply = requestEnqueueSendDataGroup(request.requestId(), request.destinationShortAddress(), - request.destinationEndpoint(), request.profileId(), request.clusterId(), - request.sourceEndpoint(), request.asdu(), request.radius()); + request.profileId(), request.clusterId(),request.sourceEndpoint(), + request.asdu(), request.txOptions(), request.radius()); break; case Zigbee::DestinationAddressModeShortAddress: interfaceReply = requestEnqueueSendDataShortAddress(request.requestId(), request.destinationShortAddress(), - request.destinationEndpoint(), request.profileId(), request.clusterId(), - request.sourceEndpoint(), request.asdu(), request.radius()); + request.destinationEndpoint(), request.profileId(), request.clusterId(), + request.sourceEndpoint(), request.asdu(), request.txOptions(), request.radius()); break; case Zigbee::DestinationAddressModeIeeeAddress: interfaceReply = requestEnqueueSendDataIeeeAddress(request.requestId(), request.destinationIeeeAddress(), - request.destinationEndpoint(), request.profileId(), request.clusterId(), - request.sourceEndpoint(), request.asdu(), request.radius()); + request.destinationEndpoint(), request.profileId(), request.clusterId(), + request.sourceEndpoint(), request.asdu(), request.txOptions(), request.radius()); break; } @@ -936,6 +938,14 @@ void ZigbeeBridgeControllerDeconz::onInterfacePackageReceived(const QByteArray & processDeviceState(parseDeviceStateFlag(deviceStateFlag)); break; } + case Deconz::CommandMacPoll: { + // FIXME: parse the data and print info + break; + } + case Deconz::CommandSimplifiedBeacon: { + // FIXME: parse the data and print info + break; + } default: qCWarning(dcZigbeeController()) << "Unhandled interface package received" << command << "SQN:" << sequenceNumber << status << "Frame length:" << frameLength << ZigbeeUtils::convertByteArrayToHexString(data); @@ -1050,3 +1060,22 @@ QDebug operator<<(QDebug debug, const DeconzApsDataIndication &indication) debug.nospace() << "RSSI: " << indication.rssi << "dBm)"; return debug.space(); } + +QDebug operator<<(QDebug debug, const DeconzNetworkConfiguration &configuration) +{ + debug.nospace() << "Network configuration:" << endl; + debug.nospace() << " - Node type:" << configuration.nodeType << endl; + debug.nospace() << " - IEEE address:" << configuration.ieeeAddress.toString() << endl; + debug.nospace() << " - NWK address:" << ZigbeeUtils::convertUint16ToHexString(configuration.shortAddress) << endl; + debug.nospace() << " - PAN ID:" << ZigbeeUtils::convertUint16ToHexString(configuration.panId) << endl; + debug.nospace() << " - Extended PAN ID:" << ZigbeeUtils::convertUint64ToHexString(configuration.extendedPanId) << endl; + debug.nospace() << " - ASP Extended PAN ID:" << ZigbeeUtils::convertUint64ToHexString(configuration.apsExtendedPanId) << endl; + debug.nospace() << " - Trust center IEEE address:" << configuration.trustCenterAddress.toString() << endl; + debug.nospace() << " - Channel mask:" << ZigbeeChannelMask(configuration.channelMask) << endl; + debug.nospace() << " - Channel:" << configuration.currentChannel << endl; + debug.nospace() << " - Security mode:" << configuration.securityMode << endl; + debug.nospace() << " - Protocol version:" << ZigbeeUtils::convertUint16ToHexString(configuration.protocolVersion) << endl; + debug.nospace() << " - Network update ID:" << ZigbeeUtils::convertByteToHexString(configuration.networkUpdateId) << endl; + debug.nospace() << " - Watchdog TTL:" << configuration.watchdogTimeout << endl; + return debug.space(); +} diff --git a/libnymea-zigbee/deconz/zigbeebridgecontrollerdeconz.h b/libnymea-zigbee/deconz/zigbeebridgecontrollerdeconz.h index 4ac1a9b..cbf72a8 100644 --- a/libnymea-zigbee/deconz/zigbeebridgecontrollerdeconz.h +++ b/libnymea-zigbee/deconz/zigbeebridgecontrollerdeconz.h @@ -125,9 +125,9 @@ public: ZigbeeInterfaceDeconzReply *requestQuerySendDataConfirm(); // Send data - ZigbeeInterfaceDeconzReply *requestEnqueueSendDataGroup(quint8 requestId, quint16 groupAddress, quint8 destinationEndpoint, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, quint8 radius = 0); - ZigbeeInterfaceDeconzReply *requestEnqueueSendDataShortAddress(quint8 requestId, quint16 shortAddress, quint8 destinationEndpoint, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, quint8 radius = 0); - ZigbeeInterfaceDeconzReply *requestEnqueueSendDataIeeeAddress(quint8 requestId, ZigbeeAddress ieeeAddress, quint8 destinationEndpoint, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, quint8 radius = 0); + ZigbeeInterfaceDeconzReply *requestEnqueueSendDataGroup(quint8 requestId, quint16 groupAddress, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, Zigbee::ZigbeeTxOptions txOptions, quint8 radius = 0); + ZigbeeInterfaceDeconzReply *requestEnqueueSendDataShortAddress(quint8 requestId, quint16 shortAddress, quint8 destinationEndpoint, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, Zigbee::ZigbeeTxOptions txOptions, quint8 radius = 0); + ZigbeeInterfaceDeconzReply *requestEnqueueSendDataIeeeAddress(quint8 requestId, ZigbeeAddress ieeeAddress, quint8 destinationEndpoint, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, Zigbee::ZigbeeTxOptions txOptions, quint8 radius = 0); ZigbeeInterfaceDeconzReply *requestSendRequest(const ZigbeeNetworkRequest &request); @@ -182,6 +182,7 @@ public slots: QDebug operator<<(QDebug debug, const DeconzDeviceState &deviceState); QDebug operator<<(QDebug debug, const DeconzApsDataConfirm &confirm); QDebug operator<<(QDebug debug, const DeconzApsDataIndication &indication); +QDebug operator<<(QDebug debug, const DeconzNetworkConfiguration &configuration); #endif // ZIGBEEBRIDGECONTROLLERDECONZ_H diff --git a/libnymea-zigbee/deconz/zigbeenetworkdeconz.cpp b/libnymea-zigbee/deconz/zigbeenetworkdeconz.cpp index 1d98bd7..dafa300 100644 --- a/libnymea-zigbee/deconz/zigbeenetworkdeconz.cpp +++ b/libnymea-zigbee/deconz/zigbeenetworkdeconz.cpp @@ -45,6 +45,11 @@ ZigbeeNetworkDeconz::ZigbeeNetworkDeconz(QObject *parent) : m_pollNetworkStateTimer->setInterval(1000); m_pollNetworkStateTimer->setSingleShot(false); connect(m_pollNetworkStateTimer, &QTimer::timeout, this, &ZigbeeNetworkDeconz::onPollNetworkStateTimeout); + + m_permitJoinRefreshTimer = new QTimer(this); + m_permitJoinRefreshTimer->setInterval(250 * 1000); + m_permitJoinRefreshTimer->setSingleShot(false); + connect(m_permitJoinRefreshTimer, &QTimer::timeout, this, &ZigbeeNetworkDeconz::onPermitJoinRefreshTimout); } ZigbeeBridgeController *ZigbeeNetworkDeconz::bridgeController() const @@ -58,10 +63,13 @@ ZigbeeBridgeController *ZigbeeNetworkDeconz::bridgeController() const ZigbeeNetworkReply *ZigbeeNetworkDeconz::sendRequest(const ZigbeeNetworkRequest &request) { ZigbeeNetworkReply *reply = createNetworkReply(request); - // Send the request, and keep the reply until transposrt, zigbee trasmission and response arrived m_pendingReplies.insert(request.requestId(), reply); + connect(reply, &ZigbeeNetworkReply::finished, this, [this, request](){ + m_pendingReplies.remove(request.requestId()); + }); + qCDebug(dcZigbeeNetwork()) << "Send request" << request; ZigbeeInterfaceDeconzReply *interfaceReply = m_controller->requestSendRequest(request); connect(interfaceReply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply, interfaceReply](){ if (interfaceReply->statusCode() != Deconz::StatusCodeSuccess) { @@ -74,6 +82,32 @@ ZigbeeNetworkReply *ZigbeeNetworkDeconz::sendRequest(const ZigbeeNetworkRequest return reply; } +ZigbeeNetworkReply *ZigbeeNetworkDeconz::setPermitJoin(quint16 shortAddress, quint8 duration) +{ + // Get the power descriptor + ZigbeeNetworkRequest request; + request.setRequestId(generateSequenceNumber()); + request.setDestinationAddressMode(Zigbee::DestinationAddressModeGroup); + request.setDestinationShortAddress(static_cast(shortAddress)); + request.setProfileId(Zigbee::ZigbeeProfileDevice); // ZDP + request.setClusterId(ZigbeeDeviceProfile::MgmtPermitJoinRequest); + request.setSourceEndpoint(0); // ZDO + request.setRadius(10); + + // Build ASDU + QByteArray asdu; + QDataStream stream(&asdu, QIODevice::WriteOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream << request.requestId(); + stream << duration; + stream << static_cast(0x01); // TrustCenter significance, always force to 1 according to Spec. + request.setTxOptions(Zigbee::ZigbeeTxOptions(nullptr)); // no ACK for broadcasts + request.setAsdu(asdu); + + qCDebug(dcZigbeeNetwork()) << "Send permit join request" << ZigbeeUtils::convertUint16ToHexString(request.destinationShortAddress()) << duration << "s"; + return sendRequest(request); +} + quint8 ZigbeeNetworkDeconz::generateSequenceNumber() { return m_sequenceNumber++; @@ -253,16 +287,26 @@ void ZigbeeNetworkDeconz::setCreateNetworkState(ZigbeeNetworkDeconz::CreateNetwo ZigbeeNodeDeconz *coordinatorNode = qobject_cast(createNode(this)); coordinatorNode->setShortAddress(m_controller->networkConfiguration().shortAddress); coordinatorNode->setExtendedAddress(m_controller->networkConfiguration().ieeeAddress); + m_coordinatorNode = coordinatorNode; + + // Network creation done when coordinator node is initialized + connect(coordinatorNode, &ZigbeeNodeDeconz::stateChanged, this, [this, coordinatorNode](ZigbeeNode::State state){ + if (state == ZigbeeNode::StateInitialized) { + qCDebug(dcZigbeeNetwork()) << "Coordinator initialized successfully." << coordinatorNode; + setState(StateRunning); + return; + } + }); - addUnitializedNode(coordinatorNode); coordinatorNode->startInitialization(); - // TODO: done when when node initialized + addUnitializedNode(coordinatorNode); } } } void ZigbeeNetworkDeconz::handleZigbeeDeviceProfileIndication(const DeconzApsDataIndication &indication) { + // Check if this is a device announcement if (indication.clusterId == ZigbeeDeviceProfile::DeviceAnnounce) { QDataStream stream(indication.asdu); stream.setByteOrder(QDataStream::LittleEndian); @@ -273,15 +317,14 @@ void ZigbeeNetworkDeconz::handleZigbeeDeviceProfileIndication(const DeconzApsDat } // Check if this is a response for a ZDO request - foreach (ZigbeeNetworkReply *reply, m_pendingReplies) { + foreach (ZigbeeNetworkReply *reply, m_pendingReplies.values()) { if (reply->request().profileId() == Zigbee::ZigbeeProfileDevice) { // We have a reply which is waiting for a ZDO response, lets check if they match - // Check if this is the response to the sent request command if (indication.clusterId == (reply->request().clusterId() | 0x8000)) { // Now check if the id matches, if so set the ADPU as response to the reply, otherwise this is not the message for this reply ZigbeeDeviceProfileAdpu deviceAdpu = ZigbeeDeviceProfile::parseAdpu(indication.asdu); - if (deviceAdpu.sequenceNumber == reply->request().requestId() && deviceAdpu.addressOfInterest == reply->request().destinationShortAddress()) { + if (deviceAdpu.sequenceNumber == reply->request().requestId()) { // We found the correct reply // Set the response payload of the @@ -296,6 +339,8 @@ void ZigbeeNetworkDeconz::handleZigbeeDeviceProfileIndication(const DeconzApsDat } } } + + qCWarning(dcZigbeeNetwork()) << "Unhandled ZDO indication" << indication; } ZigbeeNode *ZigbeeNetworkDeconz::createNode(QObject *parent) @@ -305,8 +350,52 @@ ZigbeeNode *ZigbeeNetworkDeconz::createNode(QObject *parent) void ZigbeeNetworkDeconz::setPermitJoiningInternal(bool permitJoining) { - //FIXME - Q_UNUSED(permitJoining) + quint8 duration = 0; + if (permitJoining == true) { + duration = 254; + } + + // Note: since compliance version >= 21 the value 255 is not any more endless. + // we need to refresh the command on timeout + + ZigbeeNetworkReply *reply = setPermitJoin(Zigbee::BroadcastAddressAllRouters, duration); + connect(reply, &ZigbeeNetworkReply::finished, this, [this, reply, permitJoining, duration](){ + if (reply->zigbeeStatus() != Zigbee::ZigbeeStatusSuccess) { + qCDebug(dcZigbeeNetwork()) << "Could not set permit join to" << duration; + m_permitJoining = false; + emit permitJoiningChanged(m_permitJoining); + return; + } + qCDebug(dcZigbeeNetwork()) << "Permit join request finished successfully"; + if (permitJoining) { + m_permitJoinRefreshTimer->start(); + } else { + m_permitJoinRefreshTimer->stop(); + } + + if (m_permitJoining != permitJoining) { + m_permitJoining = permitJoining; + emit permitJoiningChanged(m_permitJoining); + } + + // Set the permit joining timeout network configuration parameter + QByteArray parameterData; + QDataStream stream(¶meterData, QIODevice::WriteOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream << duration; + + ZigbeeInterfaceDeconzReply *reply = m_controller->requestWriteParameter(Deconz::ParameterPermitJoin, parameterData); + connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [reply](){ + if (reply->statusCode() != Deconz::StatusCodeSuccess) { + qCWarning(dcZigbeeController()) << "Request" << reply->command() << "finished with error" << reply->statusCode(); + // FIXME: set an appropriate error + return; + } + + qCDebug(dcZigbeeNetwork()) << "Set permit join configuration request finished" << reply->statusCode(); + }); + + }); } void ZigbeeNetworkDeconz::startNetworkInternally() @@ -370,6 +459,8 @@ void ZigbeeNetworkDeconz::startNetworkInternally() m_controller->setFirmwareVersionString(QString("%1 - %2").arg(firmwareVersion).arg(protocolVersion)); + qCDebug(dcZigbeeNetwork()) << m_controller->networkConfiguration(); + ZigbeeInterfaceDeconzReply *reply = m_controller->requestDeviceState(); connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){ if (reply->statusCode() != Deconz::StatusCodeSuccess) { @@ -496,17 +587,22 @@ void ZigbeeNetworkDeconz::onPollNetworkStateTimeout() } } +void ZigbeeNetworkDeconz::onPermitJoinRefreshTimout() +{ + setPermitJoiningInternal(true); +} + void ZigbeeNetworkDeconz::onAspDataConfirmReceived(const DeconzApsDataConfirm &confirm) { qCDebug(dcZigbeeNetwork()) << confirm; - ZigbeeNetworkReply *reply = m_pendingReplies.take(confirm.requestId); + ZigbeeNetworkReply *reply = m_pendingReplies.value(confirm.requestId); if (!reply) { qCWarning(dcZigbeeNetwork()) << "Received confirmation but could not find any reply. Ignoring the confirmation"; return; } - finishNetworkReply(reply, ZigbeeNetworkReply::ErrorNoError, static_cast(confirm.zigbeeStatusCode)); + setReplyResponseError(reply, static_cast(confirm.zigbeeStatusCode)); } void ZigbeeNetworkDeconz::onAspDataIndicationReceived(const DeconzApsDataIndication &indication) @@ -519,7 +615,7 @@ void ZigbeeNetworkDeconz::onAspDataIndicationReceived(const DeconzApsDataIndicat return; } - // Find reply finish it + // FIXME: handle it qCDebug(dcZigbeeNetwork()) << "Unhandled indication" << indication; } @@ -527,9 +623,18 @@ void ZigbeeNetworkDeconz::onAspDataIndicationReceived(const DeconzApsDataIndicat void ZigbeeNetworkDeconz::onDeviceAnnounced(quint16 shortAddress, ZigbeeAddress ieeeAddress, quint8 macCapabilities) { qCDebug(dcZigbeeNetwork()) << "Device announced" << ZigbeeUtils::convertUint16ToHexString(shortAddress) << ieeeAddress.toString() << ZigbeeUtils::convertByteToHexString(macCapabilities); - // Create node and initialize it + if (hasNode(ieeeAddress)) { + qCWarning(dcZigbeeNetwork()) << "Already known device announced. FIXME: Ignoring announcement" << ieeeAddress.toString(); + return; + } + ZigbeeNodeDeconz *node = static_cast(createNode(this)); + node->setExtendedAddress(ieeeAddress); + node->setShortAddress(shortAddress); + node->setMacCapabilitiesFlag(macCapabilities); + addUnitializedNode(node); + node->startInitialization(); } void ZigbeeNetworkDeconz::startNetwork() diff --git a/libnymea-zigbee/deconz/zigbeenetworkdeconz.h b/libnymea-zigbee/deconz/zigbeenetworkdeconz.h index 3d726b2..8db4e02 100644 --- a/libnymea-zigbee/deconz/zigbeenetworkdeconz.h +++ b/libnymea-zigbee/deconz/zigbeenetworkdeconz.h @@ -55,6 +55,8 @@ public: ZigbeeNetworkReply *sendRequest(const ZigbeeNetworkRequest &request) override; + ZigbeeNetworkReply *setPermitJoin(quint16 shortAddress, quint8 duration); + quint8 generateSequenceNumber(); private: @@ -63,6 +65,8 @@ private: CreateNetworkState m_createState = CreateNetworkStateIdle; bool m_createNewNetwork = false; + QTimer *m_permitJoinRefreshTimer = nullptr; + QHash m_pendingReplies; quint8 m_sequenceNumber = 0; @@ -83,6 +87,7 @@ protected: private slots: void onControllerAvailableChanged(bool available); void onPollNetworkStateTimeout(); + void onPermitJoinRefreshTimout(); void onAspDataConfirmReceived(const DeconzApsDataConfirm &confirm); void onAspDataIndicationReceived(const DeconzApsDataIndication &indication); diff --git a/libnymea-zigbee/deconz/zigbeenodedeconz.cpp b/libnymea-zigbee/deconz/zigbeenodedeconz.cpp index c3374b3..78f6ae2 100644 --- a/libnymea-zigbee/deconz/zigbeenodedeconz.cpp +++ b/libnymea-zigbee/deconz/zigbeenodedeconz.cpp @@ -25,6 +25,7 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#include "zigbeeutils.h" #include "zigbeenodedeconz.h" #include "zigbeedeviceprofile.h" #include "zigbeenetworkdeconz.h" @@ -39,21 +40,8 @@ ZigbeeNodeDeconz::ZigbeeNodeDeconz(ZigbeeNetworkDeconz *network, QObject *parent } -void ZigbeeNodeDeconz::leaveNetworkRequest(bool rejoin, bool removeChildren) +ZigbeeNetworkReply *ZigbeeNodeDeconz::requestNodeDescriptor() { - Q_UNUSED(rejoin) - Q_UNUSED(removeChildren) -} - -void ZigbeeNodeDeconz::setClusterAttributeReport(const ZigbeeClusterAttributeReport &report) -{ - Q_UNUSED(report) -} - -void ZigbeeNodeDeconz::startInitialization() -{ - setState(StateInitializing); - // Get the node descriptor ZigbeeNetworkRequest request; request.setRequestId(m_network->generateSequenceNumber()); @@ -71,20 +59,97 @@ void ZigbeeNodeDeconz::startInitialization() stream << request.requestId() << request.destinationShortAddress(); request.setAsdu(asdu); - ZigbeeNetworkReply *reply = m_network->sendRequest(request); - connect(reply, &ZigbeeNetworkReply::finished, this, [this, reply](){ - // TODO: check reply error + // We expect an indication with the response and the confirmation + request.setExpectIndication(true); + return m_network->sendRequest(request); +} - ZigbeeDeviceProfileAdpu adpu = ZigbeeDeviceProfile::parseAdpu(reply->responseData()); - qCDebug(dcZigbeeNode()) << "Node descriptor request finished" << adpu; - setNodeDescriptorRawData(reply->responseData()); +ZigbeeNetworkReply *ZigbeeNodeDeconz::requestPowerDescriptor() +{ + // Get the power descriptor + ZigbeeNetworkRequest request; + request.setRequestId(m_network->generateSequenceNumber()); + request.setDestinationAddressMode(Zigbee::DestinationAddressModeShortAddress); + request.setDestinationShortAddress(shortAddress()); + request.setDestinationEndpoint(0); // ZDO + request.setProfileId(Zigbee::ZigbeeProfileDevice); // ZDP + request.setClusterId(ZigbeeDeviceProfile::PowerDescriptorRequest); + request.setSourceEndpoint(0); // ZDO - QDataStream stream(adpu.payload); - stream.setByteOrder(QDataStream::LittleEndian); + // Build ASDU + QByteArray asdu; + QDataStream stream(&asdu, QIODevice::WriteOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream << request.requestId() << request.destinationShortAddress(); + request.setAsdu(asdu); + // We expect an indication with the response and the confirmation + request.setExpectIndication(true); + return m_network->sendRequest(request); +} +ZigbeeNetworkReply *ZigbeeNodeDeconz::requestActiveEndpoints() +{ + // Get the power descriptor + ZigbeeNetworkRequest request; + request.setRequestId(m_network->generateSequenceNumber()); + request.setDestinationAddressMode(Zigbee::DestinationAddressModeShortAddress); + request.setDestinationShortAddress(shortAddress()); + request.setDestinationEndpoint(0); // ZDO + request.setProfileId(Zigbee::ZigbeeProfileDevice); // ZDP + request.setClusterId(ZigbeeDeviceProfile::ActiveEndpointsRequest); + request.setSourceEndpoint(0); // ZDO - }); + // Build ASDU + QByteArray asdu; + QDataStream stream(&asdu, QIODevice::WriteOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream << request.requestId() << request.destinationShortAddress(); + request.setAsdu(asdu); + + // We expect an indication with the response and the confirmation + request.setExpectIndication(true); + return m_network->sendRequest(request); +} + +ZigbeeNetworkReply *ZigbeeNodeDeconz::requestSimpleDescriptor(quint8 endpoint) +{ + // Get the power descriptor + ZigbeeNetworkRequest request; + request.setRequestId(m_network->generateSequenceNumber()); + request.setDestinationAddressMode(Zigbee::DestinationAddressModeShortAddress); + request.setDestinationShortAddress(shortAddress()); + request.setDestinationEndpoint(0); // ZDO + request.setProfileId(Zigbee::ZigbeeProfileDevice); // ZDP + request.setClusterId(ZigbeeDeviceProfile::SimpleDescriptorRequest); + request.setSourceEndpoint(0); // ZDO + + // Build ASDU + QByteArray asdu; + QDataStream stream(&asdu, QIODevice::WriteOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream << request.requestId() << request.destinationShortAddress() << endpoint; + request.setAsdu(asdu); + + // We expect an indication with the response and the confirmation + request.setExpectIndication(true); + return m_network->sendRequest(request); +} + +void ZigbeeNodeDeconz::leaveNetworkRequest(bool rejoin, bool removeChildren) +{ + Q_UNUSED(rejoin) + Q_UNUSED(removeChildren) +} + +void ZigbeeNodeDeconz::setClusterAttributeReport(const ZigbeeClusterAttributeReport &report) +{ + Q_UNUSED(report) +} + +void ZigbeeNodeDeconz::startInitialization() +{ + setState(StateInitializing); /* Node initialisation steps (sequentially) * - Node descriptor @@ -97,6 +162,187 @@ void ZigbeeNodeDeconz::startInitialization() */ + ZigbeeNetworkReply *reply = requestNodeDescriptor(); + connect(reply, &ZigbeeNetworkReply::finished, this, [this, reply](){ + // TODO: check reply error + + ZigbeeDeviceProfileAdpu adpu = ZigbeeDeviceProfile::parseAdpu(reply->responseData()); + qCDebug(dcZigbeeNode()) << "Node descriptor request finished" << adpu; + QDataStream stream(adpu.payload); + stream.setByteOrder(QDataStream::LittleEndian); + quint8 typeDescriptorFlag = 0; quint8 frequencyFlag = 0; quint8 macCapabilities = 0; + quint16 serverMask = 0; + quint8 descriptorCapabilities = 0; + + stream >> typeDescriptorFlag >> frequencyFlag >> macCapabilities >> m_manufacturerCode >> m_maximumBufferSize; + stream >> m_maximumRxSize >> serverMask >> m_maximumTxSize >> descriptorCapabilities; + + // 0-2 Bit = logical type, 0 = coordinator, 1 = router, 2 = end device + if (!ZigbeeUtils::checkBitUint8(typeDescriptorFlag, 0) && !ZigbeeUtils::checkBitUint8(typeDescriptorFlag, 1)) { + m_nodeType = NodeTypeCoordinator; + } else if (!ZigbeeUtils::checkBitUint8(typeDescriptorFlag, 0) && ZigbeeUtils::checkBitUint8(typeDescriptorFlag, 1)) { + m_nodeType = NodeTypeRouter; + } else if (ZigbeeUtils::checkBitUint8(typeDescriptorFlag, 0) && !ZigbeeUtils::checkBitUint8(typeDescriptorFlag, 1)) { + m_nodeType = NodeTypeEndDevice; + } + + m_complexDescriptorAvailable = (typeDescriptorFlag >> 3) & 0x0001; + m_userDescriptorAvailable = (typeDescriptorFlag >> 4) & 0x0001; + + // Frequency band, 5 bits + if (ZigbeeUtils::checkBitUint8(frequencyFlag, 3)) { + m_frequencyBand = FrequencyBand868Mhz; + } else if (ZigbeeUtils::checkBitUint8(frequencyFlag, 5)) { + m_frequencyBand = FrequencyBand902Mhz; + } else if (ZigbeeUtils::checkBitUint8(frequencyFlag, 6)) { + m_frequencyBand = FrequencyBand2400Mhz; + } + + setMacCapabilitiesFlag(macCapabilities); + setServerMask(serverMask); + setDescriptorFlag(descriptorCapabilities); + + qCDebug(dcZigbeeNode()) << "Node descriptor:" << ZigbeeUtils::convertUint16ToHexString(shortAddress()) << extendedAddress().toString(); + qCDebug(dcZigbeeNode()) << " Node type:" << nodeType(); + qCDebug(dcZigbeeNode()) << " Complex desciptor available:" << complexDescriptorAvailable(); + qCDebug(dcZigbeeNode()) << " User desciptor available:" << userDescriptorAvailable(); + qCDebug(dcZigbeeNode()) << " Frequency band:" << frequencyBand(); + qCDebug(dcZigbeeNode()) << " Manufacturer code:" << ZigbeeUtils::convertUint16ToHexString(m_manufacturerCode); + qCDebug(dcZigbeeNode()) << " Maximum Rx size:" << ZigbeeUtils::convertUint16ToHexString(m_maximumRxSize) << "(" << m_maximumRxSize << ")"; + qCDebug(dcZigbeeNode()) << " Maximum Tx size:" << ZigbeeUtils::convertUint16ToHexString(m_maximumTxSize) << "(" << m_maximumTxSize << ")"; + qCDebug(dcZigbeeNode()) << " Maximum buffer size:" << ZigbeeUtils::convertByteToHexString(m_maximumBufferSize) << "(" << m_maximumBufferSize << ")"; + qCDebug(dcZigbeeNode()) << " Server mask:" << ZigbeeUtils::convertUint16ToHexString(serverMask); + qCDebug(dcZigbeeNode()) << " Primary Trust center:" << isPrimaryTrustCenter(); + qCDebug(dcZigbeeNode()) << " Backup Trust center:" << isBackupTrustCenter(); + qCDebug(dcZigbeeNode()) << " Primary Binding cache:" << isPrimaryBindingCache(); + qCDebug(dcZigbeeNode()) << " Backup Binding cache:" << isBackupBindingCache(); + qCDebug(dcZigbeeNode()) << " Primary Discovery cache:" << isPrimaryDiscoveryCache(); + qCDebug(dcZigbeeNode()) << " Backup Discovery cache:" << isBackupDiscoveryCache(); + qCDebug(dcZigbeeNode()) << " Network Manager:" << isNetworkManager(); + qCDebug(dcZigbeeNode()) << " Descriptor flag:" << ZigbeeUtils::convertByteToHexString(descriptorCapabilities); + qCDebug(dcZigbeeNode()) << " Extended active endpoint list available:" << extendedActiveEndpointListAvailable(); + qCDebug(dcZigbeeNode()) << " Extended simple descriptor list available:" << extendedSimpleDescriptorListAvailable(); + qCDebug(dcZigbeeNode()) << " MAC flags:" << ZigbeeUtils::convertByteToHexString(macCapabilities); + qCDebug(dcZigbeeNode()) << " Alternate PAN coordinator:" << alternatePanCoordinator(); + qCDebug(dcZigbeeNode()) << " Device type:" << deviceType(); + qCDebug(dcZigbeeNode()) << " Power source flag main power:" << powerSourceFlagMainPower(); + qCDebug(dcZigbeeNode()) << " Receiver on when idle:" << receiverOnWhenIdle(); + qCDebug(dcZigbeeNode()) << " Security capability:" << securityCapability(); + qCDebug(dcZigbeeNode()) << " Allocate address:" << allocateAddress(); + + + // Power descriptor + ZigbeeNetworkReply *reply = requestPowerDescriptor(); + connect(reply, &ZigbeeNetworkReply::finished, this, [this, reply](){ + // TODO: check reply error + + ZigbeeDeviceProfileAdpu adpu = ZigbeeDeviceProfile::parseAdpu(reply->responseData()); + qCDebug(dcZigbeeNode()) << "Power descriptor request finished" << this << adpu; + QDataStream stream(adpu.payload); + stream.setByteOrder(QDataStream::LittleEndian); + quint16 powerDescriptorFlag = 0; + stream >> powerDescriptorFlag; + setPowerDescriptorFlag(powerDescriptorFlag); + + + ZigbeeNetworkReply *reply = requestActiveEndpoints(); + connect(reply, &ZigbeeNetworkReply::finished, this, [this, reply](){ + // TODO: check reply error + + ZigbeeDeviceProfileAdpu adpu = ZigbeeDeviceProfile::parseAdpu(reply->responseData()); + qCDebug(dcZigbeeNode()) << "Active endpoints request finished" << this << adpu; + QDataStream stream(adpu.payload); + stream.setByteOrder(QDataStream::LittleEndian); + quint8 endpointCount = 0; + m_uninitializedEndpoints.clear(); + stream >> endpointCount; + for (int i = 0; i < endpointCount; i++) { + quint8 endpoint = 0; + stream >> endpoint; + m_uninitializedEndpoints.append(endpoint); + } + + qCDebug(dcZigbeeNode()) << "Endpoints" << endpointCount; + for (int i = 0; i < m_uninitializedEndpoints.count(); i++) { + qCDebug(dcZigbeeNode()) << " -" << ZigbeeUtils::convertByteToHexString(m_uninitializedEndpoints.at(i)); + } + + // Read simple descriptor for each endpoint + if (m_uninitializedEndpoints.isEmpty()) { + setState(StateInitialized); + } + + for (int i = 0; i < m_uninitializedEndpoints.count(); i++) { + quint8 endpointId = m_uninitializedEndpoints.at(i); + qCDebug(dcZigbeeNode()) << "Read simple descriptor of endpoint" << ZigbeeUtils::convertByteToHexString(endpointId); + ZigbeeNetworkReply *reply = requestSimpleDescriptor(endpointId); + connect(reply, &ZigbeeNetworkReply::finished, this, [this, reply, endpointId](){ + // TODO: check reply error + ZigbeeDeviceProfileAdpu adpu = ZigbeeDeviceProfile::parseAdpu(reply->responseData()); + qCDebug(dcZigbeeNode()) << "Simple descriptor request finished" << this << endpointId << adpu; + + QDataStream stream(adpu.payload); + stream.setByteOrder(QDataStream::LittleEndian); + quint8 length = 0; + quint8 endpoint = 0; + quint16 profileId = 0; + quint16 deviceId = 0; + quint8 deviceVersion = 0; + quint8 inputClusterCount = 0; + quint8 outputClusterCount = 0; + + QList inputClusters; + QList outputClusters; + + stream >> length >> endpoint >> profileId >> deviceId >> deviceVersion >> inputClusterCount; + + qCDebug(dcZigbeeNode()) << "Node endpoint simple descriptor:"; + qCDebug(dcZigbeeNode()) << " Lenght:" << ZigbeeUtils::convertByteToHexString(length); + qCDebug(dcZigbeeNode()) << " End Point:" << ZigbeeUtils::convertByteToHexString(endpoint); + qCDebug(dcZigbeeNode()) << " Profile:" << ZigbeeUtils::profileIdToString(static_cast(profileId)); + if (profileId == Zigbee::ZigbeeProfileLightLink) { + qCDebug(dcZigbeeNode()) << " Device ID:" << ZigbeeUtils::convertUint16ToHexString(deviceId) << static_cast(deviceId); + } else if (profileId == Zigbee::ZigbeeProfileHomeAutomation) { + qCDebug(dcZigbeeNode()) << " Device ID:" << ZigbeeUtils::convertUint16ToHexString(deviceId) << static_cast(deviceId); + } else if (profileId == Zigbee::ZigbeeProfileGreenPower) { + qCDebug(dcZigbeeNode()) << " Device ID:" << ZigbeeUtils::convertUint16ToHexString(deviceId) << static_cast(deviceId); + } + + qCDebug(dcZigbeeNode()) << " Device version:" << ZigbeeUtils::convertByteToHexString(deviceVersion); + + + qCDebug(dcZigbeeNode()) << " Input clusters: (" << inputClusterCount << ")"; + + for (int i = 0; i < inputClusterCount; i++) { + quint16 clusterId = 0; + stream >> clusterId; + inputClusters.append(clusterId); + qCDebug(dcZigbeeNode()) << " Cluster ID:" << ZigbeeUtils::convertUint16ToHexString(clusterId) << ZigbeeUtils::clusterIdToString(static_cast(clusterId)); + + } + stream >> outputClusterCount; + + qCDebug(dcZigbeeNode()) << " Output clusters: (" << outputClusterCount << ")"; + for (int i = 0; i < outputClusterCount; i++) { + quint16 clusterId = 0; + stream >> clusterId; + outputClusters.append(clusterId); + qCDebug(dcZigbeeNode()) << " Cluster ID:" << ZigbeeUtils::convertUint16ToHexString(clusterId) << ZigbeeUtils::clusterIdToString(static_cast(clusterId)); + } + + m_uninitializedEndpoints.removeAll(endpointId); + + // Create endpoint + + + if (m_uninitializedEndpoints.isEmpty()) { + setState(StateInitialized); + } + }); + } + }); + }); + }); } ZigbeeNodeEndpoint *ZigbeeNodeDeconz::createNodeEndpoint(quint8 endpointId, QObject *parent) diff --git a/libnymea-zigbee/deconz/zigbeenodedeconz.h b/libnymea-zigbee/deconz/zigbeenodedeconz.h index f5a5ccb..704a144 100644 --- a/libnymea-zigbee/deconz/zigbeenodedeconz.h +++ b/libnymea-zigbee/deconz/zigbeenodedeconz.h @@ -44,11 +44,19 @@ class ZigbeeNodeDeconz : public ZigbeeNode public: explicit ZigbeeNodeDeconz(ZigbeeNetworkDeconz *network, QObject *parent = nullptr); + ZigbeeNetworkReply *requestNodeDescriptor(); + ZigbeeNetworkReply *requestPowerDescriptor(); + ZigbeeNetworkReply *requestActiveEndpoints(); + ZigbeeNetworkReply *requestSimpleDescriptor(quint8 endpoint); + void leaveNetworkRequest(bool rejoin = false, bool removeChildren = false) override; private: ZigbeeNetworkDeconz *m_network = nullptr; + QList m_uninitializedEndpoints; + QList m_uninitalizedBasicClusterAttributes; + void setClusterAttributeReport(const ZigbeeClusterAttributeReport &report) override; protected: diff --git a/libnymea-zigbee/zigbee.h b/libnymea-zigbee/zigbee.h index 4174437..5637688 100644 --- a/libnymea-zigbee/zigbee.h +++ b/libnymea-zigbee/zigbee.h @@ -535,6 +535,13 @@ public: }; Q_ENUM(DataType) + enum BroadcastAddress { + BroadcastAddressAllNodes = 0xffff, + BroadcastAddressAllNonSleepingNodes = 0xfffd, + BroadcastAddressAllRouters = 0xfffc + }; + Q_ENUM(BroadcastAddress) + enum DestinationAddressMode { DestinationAddressModeGroup = 0x01, DestinationAddressModeShortAddress = 0x02, diff --git a/libnymea-zigbee/zigbeenetwork.cpp b/libnymea-zigbee/zigbeenetwork.cpp index 586914b..7aad462 100644 --- a/libnymea-zigbee/zigbeenetwork.cpp +++ b/libnymea-zigbee/zigbeenetwork.cpp @@ -295,8 +295,21 @@ void ZigbeeNetwork::loadNetwork() ZigbeeNode *node = createNode(this); node->setExtendedAddress(ZigbeeAddress(ieeeAddressString)); node->setShortAddress(static_cast(settings.value("nwkAddress", 0).toUInt())); + + // Node descriptor + node->m_nodeType = static_cast(settings.value("nodeType", 0).toUInt()); + node->m_complexDescriptorAvailable = settings.value("complexDescriptor", false).toBool(); + node->m_userDescriptorAvailable = settings.value("userDescriptor", false).toBool(); + node->m_frequencyBand = static_cast(settings.value("frequencyBand", 0).toUInt()); node->setMacCapabilitiesFlag(static_cast(settings.value("macCapabilitiesFlag", 0).toUInt())); - node->setNodeDescriptorRawData(settings.value("nodeDescriptorRawData", QByteArray()).toByteArray()); + node->m_manufacturerCode = static_cast(settings.value("manufacturerCode", 0).toUInt()); + node->m_maximumBufferSize = static_cast(settings.value("maximumBufferSize", 0).toUInt()); + node->m_maximumRxSize = static_cast(settings.value("maximumRxSize", 0).toUInt()); + node->m_maximumTxSize = static_cast(settings.value("maximumTxSize", 0).toUInt()); + node->setServerMask(static_cast(settings.value("serverMask", 0).toUInt())); + node->setDescriptorFlag(static_cast(settings.value("descriptorCapabilities", 0).toUInt())); + + // Power descriptor node->setPowerDescriptorFlag(static_cast(settings.value("powerDescriptorFlag", 0).toUInt())); int endpointsCount = settings.beginReadArray("endpoints"); @@ -377,11 +390,21 @@ void ZigbeeNetwork::saveNode(ZigbeeNode *node) // Save this node settings.beginGroup(node->extendedAddress().toString()); settings.setValue("nwkAddress", node->shortAddress()); + + // Node descriptor + settings.setValue("nodeType", node->m_nodeType); + settings.setValue("complexDescriptor", node->complexDescriptorAvailable()); + settings.setValue("userDescriptor", node->userDescriptorAvailable()); + settings.setValue("frequencyBand", node->frequencyBand()); settings.setValue("macCapabilitiesFlag", node->m_macCapabilitiesFlag); settings.setValue("manufacturerCode", node->m_manufacturerCode); + settings.setValue("maximumBufferSize", node->m_maximumBufferSize); + settings.setValue("maximumRxSize", node->m_maximumRxSize); + settings.setValue("maximumTxSize", node->m_maximumTxSize); + settings.setValue("serverMask", node->m_serverMask); + settings.setValue("descriptorCapabilities", node->m_descriptorFlag); - - settings.setValue("nodeDescriptorRawData", node->m_nodeDescriptorRawData); + // Power descriptor settings.setValue("powerDescriptorFlag", node->m_powerDescriptorFlag); settings.beginWriteArray("endpoints"); @@ -505,12 +528,33 @@ ZigbeeNetworkReply *ZigbeeNetwork::createNetworkReply(const ZigbeeNetworkRequest void ZigbeeNetwork::setReplyResponseData(ZigbeeNetworkReply *reply, const QByteArray &responseData) { reply->m_responseData = responseData; + if (reply->isComplete()) { + if (reply->m_zigbeeStatus == Zigbee::ZigbeeStatusSuccess) { + finishNetworkReply(reply); + } else { + finishNetworkReply(reply, ZigbeeNetworkReply::ErrorZigbeeStatusError); + } + } } -void ZigbeeNetwork::finishNetworkReply(ZigbeeNetworkReply *reply, ZigbeeNetworkReply::Error error, Zigbee::ZigbeeStatus zigbeeStatus) +void ZigbeeNetwork::setReplyResponseError(ZigbeeNetworkReply *reply, Zigbee::ZigbeeStatus zigbeeStatus) { - reply->m_error = error; reply->m_zigbeeStatus = zigbeeStatus; + reply->m_zigbeeConfirmArrived = true; + + if (reply->isComplete()) { + if (reply->m_zigbeeStatus == Zigbee::ZigbeeStatusSuccess) { + finishNetworkReply(reply); + } else { + finishNetworkReply(reply, ZigbeeNetworkReply::ErrorZigbeeStatusError); + } + } +} + +void ZigbeeNetwork::finishNetworkReply(ZigbeeNetworkReply *reply, ZigbeeNetworkReply::Error error) +{ + qCDebug(dcZigbeeNetwork()) << "Reply finished" << error << reply->request(); + reply->m_error = error; reply->finished(); } diff --git a/libnymea-zigbee/zigbeenetwork.h b/libnymea-zigbee/zigbeenetwork.h index f707cb3..0da1bdd 100644 --- a/libnymea-zigbee/zigbeenetwork.h +++ b/libnymea-zigbee/zigbeenetwork.h @@ -157,7 +157,8 @@ protected: // Network reply methods ZigbeeNetworkReply *createNetworkReply(const ZigbeeNetworkRequest &request = ZigbeeNetworkRequest()); void setReplyResponseData(ZigbeeNetworkReply *reply, const QByteArray &responseData); - void finishNetworkReply(ZigbeeNetworkReply *reply, ZigbeeNetworkReply::Error error = ZigbeeNetworkReply::ErrorNoError, Zigbee::ZigbeeStatus zigbeeStatus = Zigbee::ZigbeeStatusSuccess); + void setReplyResponseError(ZigbeeNetworkReply *reply, Zigbee::ZigbeeStatus zigbeeStatus = Zigbee::ZigbeeStatusSuccess); + void finishNetworkReply(ZigbeeNetworkReply *reply, ZigbeeNetworkReply::Error error = ZigbeeNetworkReply::ErrorNoError); signals: void settingsFileNameChanged(const QString &settingsFileName); diff --git a/libnymea-zigbee/zigbeenetworkreply.cpp b/libnymea-zigbee/zigbeenetworkreply.cpp index 56699e8..40f0ffb 100644 --- a/libnymea-zigbee/zigbeenetworkreply.cpp +++ b/libnymea-zigbee/zigbeenetworkreply.cpp @@ -47,6 +47,26 @@ QByteArray ZigbeeNetworkReply::responseData() const return m_responseData; } +bool ZigbeeNetworkReply::isComplete() const +{ + // If we expect indication and confirmation + if (m_request.expectConfirmation() && m_request.expectIndication()) { + if (m_zigbeeConfirmArrived && !m_responseData.isEmpty()) { + return true; + } else { + return false; + } + } + + // If we expect only a confirmation + if (m_request.expectConfirmation() && !m_request.expectIndication()) { + return m_zigbeeConfirmArrived; + } + + // If we don't expect any response... + return true; +} + ZigbeeNetworkReply::ZigbeeNetworkReply(const ZigbeeNetworkRequest &request, QObject *parent) : QObject(parent), m_request(request) diff --git a/libnymea-zigbee/zigbeenetworkreply.h b/libnymea-zigbee/zigbeenetworkreply.h index b0bbe13..0489ad2 100644 --- a/libnymea-zigbee/zigbeenetworkreply.h +++ b/libnymea-zigbee/zigbeenetworkreply.h @@ -51,18 +51,20 @@ public: }; Q_ENUM(Error) - Error error() const; ZigbeeNetworkRequest request() const; Zigbee::ZigbeeStatus zigbeeStatus() const; QByteArray responseData() const; + bool isComplete() const; + private: explicit ZigbeeNetworkReply(const ZigbeeNetworkRequest &request, QObject *parent = nullptr); ZigbeeNetworkRequest m_request; bool m_finished = false; Error m_error = ErrorNoError; + bool m_zigbeeConfirmArrived = false; Zigbee::ZigbeeStatus m_zigbeeStatus = Zigbee::ZigbeeStatusSuccess; QByteArray m_responseData; diff --git a/libnymea-zigbee/zigbeenetworkrequest.cpp b/libnymea-zigbee/zigbeenetworkrequest.cpp index 3d4cdcb..9ddd9dd 100644 --- a/libnymea-zigbee/zigbeenetworkrequest.cpp +++ b/libnymea-zigbee/zigbeenetworkrequest.cpp @@ -26,6 +26,8 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "zigbeenetworkrequest.h" +#include "zigbeedeviceprofile.h" +#include "zigbeeutils.h" ZigbeeNetworkRequest::ZigbeeNetworkRequest() { @@ -141,3 +143,51 @@ void ZigbeeNetworkRequest::setRadius(quint8 radius) { m_radius = radius; } + +bool ZigbeeNetworkRequest::expectIndication() const +{ + return m_expectIndication; +} + +void ZigbeeNetworkRequest::setExpectIndication(bool expectIndication) +{ + m_expectIndication = expectIndication; +} + +bool ZigbeeNetworkRequest::expectConfirmation() const +{ + return m_expectConfirmation; +} + +void ZigbeeNetworkRequest::setExpectConfirmation(bool expectConfirmation) +{ + m_expectConfirmation = expectConfirmation; +} + +QDebug operator<<(QDebug debug, const ZigbeeNetworkRequest &request) +{ + debug.nospace() << "Request(ID:" << request.requestId() << ", "; + debug.nospace() << static_cast(request.profileId()) << ", "; + if (request.profileId() == Zigbee::ZigbeeProfileDevice) { + debug.nospace() << static_cast(request.clusterId()) << ", "; + } else { + debug.nospace() << static_cast(request.clusterId()) << ", "; + } + + if (request.destinationAddressMode() == Zigbee::DestinationAddressModeGroup) + debug.nospace() << "Group address:" << ZigbeeUtils::convertUint16ToHexString(request.destinationShortAddress()) << ", "; + + if (request.destinationAddressMode() == Zigbee::DestinationAddressModeShortAddress) + debug.nospace() << "NWK address:" << ZigbeeUtils::convertUint16ToHexString(request.destinationShortAddress()) << ", "; + + if (request.destinationAddressMode() == Zigbee::DestinationAddressModeIeeeAddress) + debug.nospace() << "IEEE address:" << ZigbeeAddress(request.destinationIeeeAddress()).toString() << ", "; + + debug.nospace() << "Destination EP:" << ZigbeeUtils::convertByteToHexString(request.destinationEndpoint()) << ", "; + debug.nospace() << "Source EP:" << ZigbeeUtils::convertByteToHexString(request.sourceEndpoint()) << ", "; + debug.nospace() << "Radius:" << request.radius() << ", "; + debug.nospace() << request.txOptions() << ", "; + debug.nospace() << ZigbeeUtils::convertByteArrayToHexString(request.asdu()) << ", "; + debug.nospace() << ")"; + return debug.space(); +} diff --git a/libnymea-zigbee/zigbeenetworkrequest.h b/libnymea-zigbee/zigbeenetworkrequest.h index 831f5ce..518ea2c 100644 --- a/libnymea-zigbee/zigbeenetworkrequest.h +++ b/libnymea-zigbee/zigbeenetworkrequest.h @@ -28,6 +28,7 @@ #ifndef ZIGBEENETWORKREQUEST_H #define ZIGBEENETWORKREQUEST_H +#include #include #include "zigbee.h" @@ -71,6 +72,13 @@ public: quint8 radius() const; void setRadius(quint8 radius); + // Response expectations + bool expectIndication() const; + void setExpectIndication(bool expectIndication); + + bool expectConfirmation() const; + void setExpectConfirmation(bool expectConfirmation); + private: quint8 m_requestId = 0; Zigbee::DestinationAddressMode m_destinationAddressMode = Zigbee::DestinationAddressModeShortAddress; @@ -83,6 +91,12 @@ private: QByteArray m_asdu; Zigbee::ZigbeeTxOptions m_txOptions = Zigbee::ZigbeeTxOptions(Zigbee::ZigbeeTxOptionAckTransmission); quint8 m_radius = 0; + + bool m_expectIndication = false; + bool m_expectConfirmation = true; }; +QDebug operator<<(QDebug debug, const ZigbeeNetworkRequest &request); + + #endif // ZIGBEENETWORKREQUEST_H diff --git a/libnymea-zigbee/zigbeenode.cpp b/libnymea-zigbee/zigbeenode.cpp index 30dfbea..3e99bb0 100644 --- a/libnymea-zigbee/zigbeenode.cpp +++ b/libnymea-zigbee/zigbeenode.cpp @@ -307,9 +307,9 @@ void ZigbeeNode::setNodeDescriptorRawData(const QByteArray nodeDescriptorRawData m_userDescriptorAvailable = (bitField >> 4) & 0x0001; qCDebug(dcZigbeeNode()) << "Node descriptor:"; - qCDebug(dcZigbeeNode()) << " Node type:" << nodeType(); qCDebug(dcZigbeeNode()) << " Sequence number:" << ZigbeeUtils::convertByteToHexString(sequenceNumber); qCDebug(dcZigbeeNode()) << " Status:" << ZigbeeUtils::convertByteToHexString(status); + qCDebug(dcZigbeeNode()) << " Node type:" << nodeType(); qCDebug(dcZigbeeNode()) << " Short address:" << ZigbeeUtils::convertUint16ToHexString(shortAddress); qCDebug(dcZigbeeNode()) << " Manufacturer code:" << ZigbeeUtils::convertUint16ToHexString(manufacturerCode); qCDebug(dcZigbeeNode()) << " Maximum Rx size:" << ZigbeeUtils::convertUint16ToHexString(maximumRxSize());