diff --git a/libnymea-zigbee/backends/deconz/interface/zigbeeinterfacedeconz.h b/libnymea-zigbee/backends/deconz/interface/zigbeeinterfacedeconz.h index fdef08e..699051a 100644 --- a/libnymea-zigbee/backends/deconz/interface/zigbeeinterfacedeconz.h +++ b/libnymea-zigbee/backends/deconz/interface/zigbeeinterfacedeconz.h @@ -73,8 +73,7 @@ private slots: public slots: void sendPackage(const QByteArray &package); - - bool enable(const QString &serialPort = "/dev/ttyS0", qint32 baudrate = 115200); + bool enable(const QString &serialPort = "/dev/ttyS0", qint32 baudrate = 38400); void reconnectController(); void disable(); diff --git a/libnymea-zigbee/backends/deconz/interface/zigbeeinterfacedeconzreply.cpp b/libnymea-zigbee/backends/deconz/interface/zigbeeinterfacedeconzreply.cpp index 3274938..0506fd1 100644 --- a/libnymea-zigbee/backends/deconz/interface/zigbeeinterfacedeconzreply.cpp +++ b/libnymea-zigbee/backends/deconz/interface/zigbeeinterfacedeconzreply.cpp @@ -27,6 +27,11 @@ #include "zigbeeinterfacedeconzreply.h" +ZigbeeNetworkRequest ZigbeeInterfaceDeconzReply::networkRequest() const +{ + return m_networkRequest; +} + Deconz::Command ZigbeeInterfaceDeconzReply::command() const { return m_command; @@ -47,6 +52,11 @@ Deconz::StatusCode ZigbeeInterfaceDeconzReply::statusCode() const return m_statusCode; } +bool ZigbeeInterfaceDeconzReply::timendOut() const +{ + return m_timeout; +} + bool ZigbeeInterfaceDeconzReply::aborted() const { return m_aborted; @@ -64,7 +74,7 @@ ZigbeeInterfaceDeconzReply::ZigbeeInterfaceDeconzReply(Deconz::Command command, m_command(command), m_sequenceNumber(sequenceNumber) { - m_timer->setInterval(2000); + m_timer->setInterval(5000); m_timer->setSingleShot(true); connect(m_timer, &QTimer::timeout, this, &ZigbeeInterfaceDeconzReply::onTimeout); } diff --git a/libnymea-zigbee/backends/deconz/interface/zigbeeinterfacedeconzreply.h b/libnymea-zigbee/backends/deconz/interface/zigbeeinterfacedeconzreply.h index 204fbe7..db9ab39 100644 --- a/libnymea-zigbee/backends/deconz/interface/zigbeeinterfacedeconzreply.h +++ b/libnymea-zigbee/backends/deconz/interface/zigbeeinterfacedeconzreply.h @@ -32,6 +32,7 @@ #include #include "deconz.h" +#include "zigbeenetworkrequest.h" class ZigbeeInterfaceDeconzReply : public QObject { @@ -41,6 +42,7 @@ class ZigbeeInterfaceDeconzReply : public QObject public: // Request content + ZigbeeNetworkRequest networkRequest() const; Deconz::Command command() const; quint8 sequenceNumber() const; QByteArray responseData() const; @@ -48,12 +50,13 @@ public: // Response content Deconz::StatusCode statusCode() const; - bool timeout() const; + bool timendOut() const; bool aborted() const; void abort(); private: explicit ZigbeeInterfaceDeconzReply(Deconz::Command command, quint8 sequenceNumber, QObject *parent = nullptr); + ZigbeeNetworkRequest m_networkRequest; QTimer *m_timer = nullptr; bool m_timeout = false; bool m_aborted = false; diff --git a/libnymea-zigbee/backends/deconz/zigbeebridgecontrollerdeconz.cpp b/libnymea-zigbee/backends/deconz/zigbeebridgecontrollerdeconz.cpp index 7d3eca3..7bf4ce6 100644 --- a/libnymea-zigbee/backends/deconz/zigbeebridgecontrollerdeconz.cpp +++ b/libnymea-zigbee/backends/deconz/zigbeebridgecontrollerdeconz.cpp @@ -221,6 +221,66 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestQuerySendDataCo return createReply(Deconz::CommandApsDataConfirm, sequenceNumber, this); } +ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestSendRequest(const ZigbeeNetworkRequest &request) +{ + // Send the request only if there are free slots on the device, otherwise enque request +// if (m_apsFreeSlotsAvailable) { + +// } + + qCDebug(dcZigbeeAps()) << "APSDE-DATA.request" << request; + ZigbeeInterfaceDeconzReply *interfaceReply = nullptr; + switch (request.destinationAddressMode()) { + case Zigbee::DestinationAddressModeGroup: + interfaceReply = requestEnqueueSendDataGroup(request.requestId(), request.destinationShortAddress(), + 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.txOptions(), request.radius()); + break; + case Zigbee::DestinationAddressModeIeeeAddress: + interfaceReply = requestEnqueueSendDataIeeeAddress(request.requestId(), request.destinationIeeeAddress(), + request.destinationEndpoint(), request.profileId(), request.clusterId(), + request.sourceEndpoint(), request.asdu(), request.txOptions(), request.radius()); + break; + } + + return interfaceReply; +} + +quint8 ZigbeeBridgeControllerDeconz::generateSequenceNumber() +{ + return m_sequenceNumber++; +} + +ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::createReply(Deconz::Command command, quint8 sequenceNumber, QObject *parent) +{ + // Create the reply + ZigbeeInterfaceDeconzReply *reply = new ZigbeeInterfaceDeconzReply(command, sequenceNumber, parent); + + connect(reply, &ZigbeeInterfaceDeconzReply::timeout, this, [this, reply](){ + qCWarning(dcZigbeeController()) << "Reply timeout" << reply->command() << "SQN:" << reply->sequenceNumber(); + if (m_pendingReplies.contains(reply->sequenceNumber())) { + m_pendingReplies.remove(reply->sequenceNumber()); + // Note: will be deleted with the finished signal + } + }); + + // Auto delete the object on finished + connect(reply, &ZigbeeInterfaceDeconzReply::finished, reply, &ZigbeeInterfaceDeconzReply::deleteLater, Qt::QueuedConnection); + + // Add it to the pending list + m_pendingReplies.insert(sequenceNumber, reply); + + // Fixme: start the timer once actually sent to the interface + reply->m_timer->start(); + + return reply; +} + ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendDataGroup(quint8 requestId, quint16 groupAddress, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, Zigbee::ZigbeeTxOptions txOptions, quint8 radius) { quint8 sequenceNumber = generateSequenceNumber(); @@ -313,7 +373,7 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendData << profileId << clusterId << ZigbeeUtils::convertByteToHexString(sourceEndpoint); - Q_ASSERT_X(asdu.length() <= 127, "ASDU", "ASDU package length has to <= 127 bytes"); + Q_ASSERT_X(asdu.length() <= 127, "ZigbeeController", "ASDU package length has to be <= 127 bytes"); // Note: 21 protocol bytes + asdu package length quint16 payloadLength = static_cast(21 + asdu.length()); @@ -345,53 +405,6 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendData return createReply(Deconz::CommandApsDataRequest, sequenceNumber, this); } -ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestSendRequest(const ZigbeeNetworkRequest &request) -{ - qCDebug(dcZigbeeAps()) << "APSDE-DATA.request" << request; - ZigbeeInterfaceDeconzReply *interfaceReply = nullptr; - switch (request.destinationAddressMode()) { - case Zigbee::DestinationAddressModeGroup: - interfaceReply = requestEnqueueSendDataGroup(request.requestId(), request.destinationShortAddress(), - 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.txOptions(), request.radius()); - break; - case Zigbee::DestinationAddressModeIeeeAddress: - interfaceReply = requestEnqueueSendDataIeeeAddress(request.requestId(), request.destinationIeeeAddress(), - request.destinationEndpoint(), request.profileId(), request.clusterId(), - request.sourceEndpoint(), request.asdu(), request.txOptions(), request.radius()); - break; - } - - return interfaceReply; -} - -quint8 ZigbeeBridgeControllerDeconz::generateSequenceNumber() -{ - return m_sequenceNumber++; -} - -ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::createReply(Deconz::Command command, quint8 sequenceNumber, QObject *parent) -{ - // Create the reply - ZigbeeInterfaceDeconzReply *reply = new ZigbeeInterfaceDeconzReply(command, sequenceNumber, parent); - - // Auto delete the object on finished - connect(reply, &ZigbeeInterfaceDeconzReply::finished, reply, &ZigbeeInterfaceDeconzReply::deleteLater, Qt::QueuedConnection); - - // Add it to the pending list - m_pendingReplies.insert(sequenceNumber, reply); - - // Fixme: start the timer once actually sent to the interface - reply->m_timer->start(); - - return reply; -} - ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters() { qCDebug(dcZigbeeController()) << "Start reading network parameters"; @@ -408,8 +421,8 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters( ZigbeeInterfaceDeconzReply *replyMacAddress = requestReadParameter(Deconz::ParameterMacAddress); connect(replyMacAddress, &ZigbeeInterfaceDeconzReply::finished, this, [this, readNetworkParametersReply, replyMacAddress](){ if (replyMacAddress->statusCode() != Deconz::StatusCodeSuccess) { - qCWarning(dcZigbeeController()) << "Request" << replyMacAddress->command() << Deconz::ParameterMacAddress - << "finished with error" << replyMacAddress->statusCode(); + qCWarning(dcZigbeeController()) << "Request" << "SQN:" << replyMacAddress->sequenceNumber() << replyMacAddress->command() + << Deconz::ParameterMacAddress << "finished with error" << replyMacAddress->statusCode(); readNetworkParametersReply->m_statusCode = replyMacAddress->statusCode(); readNetworkParametersReply->finished(); @@ -421,16 +434,16 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters( stream >> payloadLenght >> parameter >> macAddress; m_networkConfiguration.ieeeAddress = ZigbeeAddress(macAddress); - qCDebug(dcZigbeeController()) << "Request" << replyMacAddress->command() << static_cast(parameter) - << "finished successfully"; + qCDebug(dcZigbeeController()) << "Request" << "SQN:" << replyMacAddress->sequenceNumber() << replyMacAddress->command() + << static_cast(parameter) << "finished successfully"; qCDebug(dcZigbeeController()) << "IEEE address:" << m_networkConfiguration.ieeeAddress.toString(); // Read PAN ID ZigbeeInterfaceDeconzReply *replyPanId = requestReadParameter(Deconz::ParameterPanId); connect(replyPanId, &ZigbeeInterfaceDeconzReply::finished, this, [this, readNetworkParametersReply, replyPanId](){ if (replyPanId->statusCode() != Deconz::StatusCodeSuccess) { - qCWarning(dcZigbeeController()) << "Request" << replyPanId->command() << Deconz::ParameterPanId - << "finished with error" << replyPanId->statusCode(); + qCWarning(dcZigbeeController()) << "Request" << "SQN:" << replyPanId->sequenceNumber() << replyPanId->command() + << Deconz::ParameterPanId << "finished with error" << replyPanId->statusCode(); readNetworkParametersReply->m_statusCode = replyPanId->statusCode(); readNetworkParametersReply->finished(); return; @@ -441,15 +454,15 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters( stream >> payloadLenght >> parameter >> panId; m_networkConfiguration.panId = panId; - qCDebug(dcZigbeeController()) << "Request" << replyPanId->command() << static_cast(parameter) - << "finished successfully"; + qCDebug(dcZigbeeController()) << "Request" << "SQN:" << replyPanId->sequenceNumber() << replyPanId->command() + << static_cast(parameter) << "finished successfully"; qCDebug(dcZigbeeController()) << "PAN ID:" << ZigbeeUtils::convertUint16ToHexString(m_networkConfiguration.panId); // Read short address ZigbeeInterfaceDeconzReply *replyShortAddress = requestReadParameter(Deconz::ParameterNetworkAddress); connect(replyShortAddress, &ZigbeeInterfaceDeconzReply::finished, this, [this, readNetworkParametersReply, replyShortAddress](){ if (replyShortAddress->statusCode() != Deconz::StatusCodeSuccess) { - qCWarning(dcZigbeeController()) << "Request" << replyShortAddress->command() << Deconz::ParameterNetworkAddress + qCWarning(dcZigbeeController()) << "Request" << "SQN:" << replyShortAddress->sequenceNumber() << replyShortAddress->command() << Deconz::ParameterNetworkAddress << "finished with error" << replyShortAddress->statusCode(); readNetworkParametersReply->m_statusCode = replyShortAddress->statusCode(); readNetworkParametersReply->finished(); @@ -461,16 +474,16 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters( quint16 payloadLenght = 0; quint8 parameter = 0; quint16 shortAddress = 0; stream >> payloadLenght >> parameter >> shortAddress; m_networkConfiguration.shortAddress = shortAddress; - qCDebug(dcZigbeeController()) << "Request" << replyShortAddress->command() << static_cast(parameter) - << "finished successfully"; + qCDebug(dcZigbeeController()) << "Request" << "SQN:" << replyShortAddress->sequenceNumber() + << replyShortAddress->command() << static_cast(parameter) << "finished successfully"; qCDebug(dcZigbeeController()) << ZigbeeUtils::convertUint16ToHexString(m_networkConfiguration.shortAddress); // Read extended PAN ID ZigbeeInterfaceDeconzReply *replyExtendedPanId = requestReadParameter(Deconz::ParameterNetworkExtendedPanId); connect(replyExtendedPanId, &ZigbeeInterfaceDeconzReply::finished, this, [this, readNetworkParametersReply, replyExtendedPanId](){ if (replyExtendedPanId->statusCode() != Deconz::StatusCodeSuccess) { - qCWarning(dcZigbeeController()) << "Request" << replyExtendedPanId->command() << Deconz::ParameterNetworkExtendedPanId - << "finished with error" << replyExtendedPanId->statusCode(); + qCWarning(dcZigbeeController()) << "Request" << "SQN:" << replyExtendedPanId->sequenceNumber() << replyExtendedPanId->command() + << Deconz::ParameterNetworkExtendedPanId << "finished with error" << replyExtendedPanId->statusCode(); readNetworkParametersReply->m_statusCode = replyExtendedPanId->statusCode(); readNetworkParametersReply->finished(); return; @@ -481,7 +494,7 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters( quint16 payloadLenght = 0; quint8 parameter = 0; quint64 networkExtendedPanId = 0; stream >> payloadLenght >> parameter >> networkExtendedPanId; m_networkConfiguration.extendedPanId = networkExtendedPanId; - qCDebug(dcZigbeeController()) << "Request" << replyExtendedPanId->command() << static_cast(parameter) + qCDebug(dcZigbeeController()) << "Request" << "SQN:" << replyExtendedPanId->sequenceNumber() << replyExtendedPanId->command() << static_cast(parameter) << "finished successfully"; qCDebug(dcZigbeeController()) << ZigbeeUtils::convertUint64ToHexString(m_networkConfiguration.extendedPanId); @@ -489,8 +502,8 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters( ZigbeeInterfaceDeconzReply *replyNodeType = requestReadParameter(Deconz::ParameterNodeType); connect(replyNodeType, &ZigbeeInterfaceDeconzReply::finished, this, [this, readNetworkParametersReply, replyNodeType](){ if (replyNodeType->statusCode() != Deconz::StatusCodeSuccess) { - qCWarning(dcZigbeeController()) << "Request" << replyNodeType->command() << Deconz::ParameterNodeType - << "finished with error" << replyNodeType->statusCode(); + qCWarning(dcZigbeeController()) << "Request" << "SQN:" << replyNodeType->sequenceNumber() << replyNodeType->command() + << Deconz::ParameterNodeType << "finished with error" << replyNodeType->statusCode(); readNetworkParametersReply->m_statusCode = replyNodeType->statusCode(); readNetworkParametersReply->finished(); return; @@ -502,7 +515,7 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters( stream >> payloadLenght >> parameter >> nodeType; m_networkConfiguration.nodeType = static_cast(nodeType); - qCDebug(dcZigbeeController()) << "Request" << replyNodeType->command() << static_cast(parameter) + qCDebug(dcZigbeeController()) << "Request" << "SQN:" << replyNodeType->sequenceNumber() << replyNodeType->command() << static_cast(parameter) << "finished successfully"; qCDebug(dcZigbeeController()) << m_networkConfiguration.nodeType; @@ -510,7 +523,7 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters( ZigbeeInterfaceDeconzReply *replyChannelMask = requestReadParameter(Deconz::ParameterChannelMask); connect(replyChannelMask, &ZigbeeInterfaceDeconzReply::finished, this, [this, readNetworkParametersReply, replyChannelMask](){ if (replyChannelMask->statusCode() != Deconz::StatusCodeSuccess) { - qCWarning(dcZigbeeController()) << "Request" << replyChannelMask->command() << Deconz::ParameterChannelMask + qCWarning(dcZigbeeController()) << "Request" << "SQN:" << replyChannelMask->sequenceNumber() << replyChannelMask->command() << Deconz::ParameterChannelMask << "finished with error" << replyChannelMask->statusCode(); readNetworkParametersReply->m_statusCode = replyChannelMask->statusCode(); readNetworkParametersReply->finished(); @@ -523,16 +536,16 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters( stream >> payloadLenght >> parameter >> channelMask; m_networkConfiguration.channelMask = channelMask; - qCDebug(dcZigbeeController()) << "Request" << replyChannelMask->command() << static_cast(parameter) - << "finished successfully"; + qCDebug(dcZigbeeController()) << "Request" << "SQN:" << replyChannelMask->sequenceNumber() << replyChannelMask->command() + << static_cast(parameter) << "finished successfully"; qCDebug(dcZigbeeController()) << ZigbeeUtils::convertUint32ToHexString(m_networkConfiguration.channelMask); // Read APS extended PAN ID ZigbeeInterfaceDeconzReply *replyApsExtendedPanId = requestReadParameter(Deconz::ParameterApsExtendedPanId); connect(replyApsExtendedPanId, &ZigbeeInterfaceDeconzReply::finished, this, [this, readNetworkParametersReply, replyApsExtendedPanId](){ if (replyApsExtendedPanId->statusCode() != Deconz::StatusCodeSuccess) { - qCWarning(dcZigbeeController()) << "Request" << replyApsExtendedPanId->command() << Deconz::ParameterApsExtendedPanId - << "finished with error" << replyApsExtendedPanId->statusCode(); + qCWarning(dcZigbeeController()) << "Request" << "SQN:" << replyApsExtendedPanId->sequenceNumber() << replyApsExtendedPanId->command() + << Deconz::ParameterApsExtendedPanId << "finished with error" << replyApsExtendedPanId->statusCode(); readNetworkParametersReply->m_statusCode = replyApsExtendedPanId->statusCode(); readNetworkParametersReply->finished(); return; @@ -544,16 +557,16 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters( stream >> payloadLenght >> parameter >> apsExtendedPanId; m_networkConfiguration.apsExtendedPanId = apsExtendedPanId; - qCDebug(dcZigbeeController()) << "Request" << replyApsExtendedPanId->command() << static_cast(parameter) - << "finished successfully"; + qCDebug(dcZigbeeController()) << "Request" << "SQN:" << replyApsExtendedPanId->sequenceNumber() << replyApsExtendedPanId->command() + << static_cast(parameter) << "finished successfully"; qCDebug(dcZigbeeController()) << ZigbeeUtils::convertUint64ToHexString(m_networkConfiguration.apsExtendedPanId); // Read trust center address ZigbeeInterfaceDeconzReply *replyTrustCenterAddress = requestReadParameter(Deconz::ParameterTrustCenterAddress); connect(replyTrustCenterAddress, &ZigbeeInterfaceDeconzReply::finished, this, [this, readNetworkParametersReply, replyTrustCenterAddress](){ if (replyTrustCenterAddress->statusCode() != Deconz::StatusCodeSuccess) { - qCWarning(dcZigbeeController()) << "Request" << replyTrustCenterAddress->command() << Deconz::ParameterTrustCenterAddress - << "finished with error" << replyTrustCenterAddress->statusCode(); + qCWarning(dcZigbeeController()) << "Request" << "SQN:" << replyTrustCenterAddress->sequenceNumber() << replyTrustCenterAddress->command() + << Deconz::ParameterTrustCenterAddress << "finished with error" << replyTrustCenterAddress->statusCode(); readNetworkParametersReply->m_statusCode = replyTrustCenterAddress->statusCode(); readNetworkParametersReply->finished(); return; @@ -565,16 +578,16 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters( stream >> payloadLenght >> parameter >> trustCenterAddress; m_networkConfiguration.trustCenterAddress = ZigbeeAddress(trustCenterAddress); - qCDebug(dcZigbeeController()) << "Request" << replyTrustCenterAddress->command() << static_cast(parameter) - << "finished successfully"; + qCDebug(dcZigbeeController()) << "Request" << "SQN:" << replyTrustCenterAddress->sequenceNumber() << replyTrustCenterAddress->command() + << static_cast(parameter) << "finished successfully"; qCDebug(dcZigbeeController()) << m_networkConfiguration.trustCenterAddress; // Read security mode ZigbeeInterfaceDeconzReply *replySecurityMode = requestReadParameter(Deconz::ParameterSecurityMode); connect(replySecurityMode, &ZigbeeInterfaceDeconzReply::finished, this, [this, readNetworkParametersReply, replySecurityMode](){ if (replySecurityMode->statusCode() != Deconz::StatusCodeSuccess) { - qCWarning(dcZigbeeController()) << "Request" << replySecurityMode->command() << Deconz::ParameterSecurityMode - << "finished with error" << replySecurityMode->statusCode(); + qCWarning(dcZigbeeController()) << "Request" << "SQN:" << replySecurityMode->sequenceNumber() << replySecurityMode->command() + << Deconz::ParameterSecurityMode << "finished with error" << replySecurityMode->statusCode(); readNetworkParametersReply->m_statusCode = replySecurityMode->statusCode(); readNetworkParametersReply->finished(); return; @@ -586,8 +599,8 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters( stream >> payloadLenght >> parameter >> securityMode; m_networkConfiguration.securityMode = static_cast(securityMode); - qCDebug(dcZigbeeController()) << "Request" << replySecurityMode->command() << static_cast(parameter) - << "finished successfully"; + qCDebug(dcZigbeeController()) << "Request" << "SQN:" << replySecurityMode->sequenceNumber() << replySecurityMode->command() + << static_cast(parameter) << "finished successfully"; qCDebug(dcZigbeeController()) << m_networkConfiguration.securityMode; // Note: reading the network key returns "InavlidParameter". Might be for security reasons which is good! @@ -596,7 +609,7 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters( ZigbeeInterfaceDeconzReply *replyChannel = requestReadParameter(Deconz::ParameterCurrentChannel); connect(replyChannel, &ZigbeeInterfaceDeconzReply::finished, this, [this, readNetworkParametersReply, replyChannel](){ if (replyChannel->statusCode() != Deconz::StatusCodeSuccess) { - qCWarning(dcZigbeeController()) << "Request" << replyChannel->command() << Deconz::ParameterCurrentChannel + qCWarning(dcZigbeeController()) << "Request" << "SQN:" << replyChannel->sequenceNumber() << replyChannel->command() << Deconz::ParameterCurrentChannel << "finished with error" << replyChannel->statusCode(); readNetworkParametersReply->m_statusCode = replyChannel->statusCode(); readNetworkParametersReply->finished(); @@ -608,7 +621,7 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters( quint16 payloadLenght = 0; quint8 parameter = 0; quint8 channel = 0; stream >> payloadLenght >> parameter >> channel; m_networkConfiguration.currentChannel = channel; - qCDebug(dcZigbeeController()) << "Request" << replyChannel->command() << static_cast(parameter) + qCDebug(dcZigbeeController()) << "Request" << "SQN:" << replyChannel->sequenceNumber() << replyChannel->command() << static_cast(parameter) << "finished successfully"; qCDebug(dcZigbeeController()) << "Current channel:" << m_networkConfiguration.currentChannel; @@ -617,8 +630,8 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters( ZigbeeInterfaceDeconzReply *replyPermitJoin = requestReadParameter(Deconz::ParameterPermitJoin); connect(replyPermitJoin, &ZigbeeInterfaceDeconzReply::finished, this, [this, readNetworkParametersReply, replyPermitJoin](){ if (replyPermitJoin->statusCode() != Deconz::StatusCodeSuccess) { - qCWarning(dcZigbeeController()) << "Request" << replyPermitJoin->command() << Deconz::ParameterPermitJoin - << "finished with error" << replyPermitJoin->statusCode(); + qCWarning(dcZigbeeController()) << "Request" << "SQN:" << replyPermitJoin->sequenceNumber() << replyPermitJoin->command() + << Deconz::ParameterPermitJoin << "finished with error" << replyPermitJoin->statusCode(); readNetworkParametersReply->m_statusCode = replyPermitJoin->statusCode(); readNetworkParametersReply->finished(); return; @@ -629,7 +642,7 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters( quint16 payloadLenght = 0; quint8 parameter = 0; stream >> payloadLenght >> parameter; //m_networkConfiguration.currentChannel = channel; - qCDebug(dcZigbeeController()) << "Request" << replyPermitJoin->command() << static_cast(parameter) + qCDebug(dcZigbeeController()) << "Request" << "SQN:" << replyPermitJoin->sequenceNumber() << replyPermitJoin->command() << static_cast(parameter) << "finished successfully" << ZigbeeUtils::convertByteArrayToHexString(replyPermitJoin->responseData()); @@ -637,8 +650,8 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters( ZigbeeInterfaceDeconzReply *replyProtocolVersion = requestReadParameter(Deconz::ParameterProtocolVersion); connect(replyProtocolVersion, &ZigbeeInterfaceDeconzReply::finished, this, [this, readNetworkParametersReply, replyProtocolVersion](){ if (replyProtocolVersion->statusCode() != Deconz::StatusCodeSuccess) { - qCWarning(dcZigbeeController()) << "Request" << replyProtocolVersion->command() << Deconz::ParameterProtocolVersion - << "finished with error" << replyProtocolVersion->statusCode(); + qCWarning(dcZigbeeController()) << "Request" << "SQN:" << replyProtocolVersion->sequenceNumber() << replyProtocolVersion->command() + << Deconz::ParameterProtocolVersion << "finished with error" << replyProtocolVersion->statusCode(); readNetworkParametersReply->m_statusCode = replyProtocolVersion->statusCode(); readNetworkParametersReply->finished(); return; @@ -649,16 +662,16 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters( quint16 payloadLenght = 0; quint8 parameter = 0; quint16 protocolVersion = 0; stream >> payloadLenght >> parameter >> protocolVersion; m_networkConfiguration.protocolVersion = protocolVersion; - qCDebug(dcZigbeeController()) << "Request" << replyProtocolVersion->command() << static_cast(parameter) - << "finished successfully"; + qCDebug(dcZigbeeController()) << "Request" << "SQN:" << replyProtocolVersion->sequenceNumber() << replyProtocolVersion->command() + << static_cast(parameter) << "finished successfully"; qCDebug(dcZigbeeController()) << "Protocol version:" << ZigbeeUtils::convertUint16ToHexString(m_networkConfiguration.protocolVersion); // Read network updat id ZigbeeInterfaceDeconzReply *replyNetworkUpdateId = requestReadParameter(Deconz::ParameterNetworkUpdateId); connect(replyNetworkUpdateId, &ZigbeeInterfaceDeconzReply::finished, this, [this, readNetworkParametersReply, replyNetworkUpdateId](){ if (replyNetworkUpdateId->statusCode() != Deconz::StatusCodeSuccess) { - qCWarning(dcZigbeeController()) << "Request" << replyNetworkUpdateId->command() << Deconz::ParameterNetworkUpdateId - << "finished with error" << replyNetworkUpdateId->statusCode(); + qCWarning(dcZigbeeController()) << "Request" << "SQN:" << replyNetworkUpdateId->sequenceNumber() << replyNetworkUpdateId->command() + << Deconz::ParameterNetworkUpdateId << "finished with error" << replyNetworkUpdateId->statusCode(); readNetworkParametersReply->m_statusCode = replyNetworkUpdateId->statusCode(); readNetworkParametersReply->finished(); return; @@ -669,8 +682,8 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters( quint16 payloadLenght = 0; quint8 parameter = 0; quint8 networkUpdateId = 0; stream >> payloadLenght >> parameter >> networkUpdateId; m_networkConfiguration.networkUpdateId = networkUpdateId; - qCDebug(dcZigbeeController()) << "Request" << replyNetworkUpdateId->command() << static_cast(parameter) - << "finished successfully"; + qCDebug(dcZigbeeController()) << "Request" << "SQN:" << replyNetworkUpdateId->sequenceNumber() << replyNetworkUpdateId->command() + << static_cast(parameter) << "finished successfully"; qCDebug(dcZigbeeController()) << "Network update ID:" << m_networkConfiguration.networkUpdateId; // Make sure the watchdog is available for this version @@ -692,8 +705,8 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters( ZigbeeInterfaceDeconzReply *replyWatchdogTimeout = requestReadParameter(Deconz::ParameterWatchdogTtl); connect(replyWatchdogTimeout, &ZigbeeInterfaceDeconzReply::finished, this, [this, readNetworkParametersReply, replyWatchdogTimeout](){ if (replyWatchdogTimeout->statusCode() != Deconz::StatusCodeSuccess) { - qCWarning(dcZigbeeController()) << "Request" << replyWatchdogTimeout->command() << Deconz::ParameterWatchdogTtl - << "finished with error" << replyWatchdogTimeout->statusCode(); + qCWarning(dcZigbeeController()) << "Request" << "SQN:" << replyWatchdogTimeout->sequenceNumber() << replyWatchdogTimeout->command() + << Deconz::ParameterWatchdogTtl << "finished with error" << replyWatchdogTimeout->statusCode(); readNetworkParametersReply->m_statusCode = replyWatchdogTimeout->statusCode(); readNetworkParametersReply->finished(); return; @@ -704,8 +717,8 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters( quint16 payloadLenght = 0; quint8 parameter = 0; quint32 watchdogTimeout = 0; stream >> payloadLenght >> parameter >> watchdogTimeout; m_networkConfiguration.watchdogTimeout = watchdogTimeout; - qCDebug(dcZigbeeController()) << "Request" << replyWatchdogTimeout->command() << static_cast(parameter) - << "finished successfully"; + qCDebug(dcZigbeeController()) << "Request" << "SQN:" << replyWatchdogTimeout->sequenceNumber() << replyWatchdogTimeout->command() + << static_cast(parameter) << "finished successfully"; qCDebug(dcZigbeeController()) << "Watchdog timeout:" << m_networkConfiguration.watchdogTimeout; // Finished reading all parameters. Finish the independent reply in order to indicate the process has finished @@ -746,13 +759,13 @@ void ZigbeeBridgeControllerDeconz::readDataIndication() ZigbeeInterfaceDeconzReply *reply = requestReadReceivedDataIndication(); connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){ if (reply->statusCode() != Deconz::StatusCodeSuccess) { - qCWarning(dcZigbeeController()) << "Could not read data indication." << reply->statusCode(); + qCWarning(dcZigbeeController()) << "Could not read data indication." << "SQN:" << reply->sequenceNumber() << reply->statusCode(); // FIXME: set an appropriate error return; } // APS data indication received, process the content - qCDebug(dcZigbeeController()) << "Reading data indication finished successfully"; + qCDebug(dcZigbeeController()) << "Reading data indication finished successfully" << "SQN:" << reply->sequenceNumber(); processDataIndication(reply->responseData()); }); } @@ -762,20 +775,20 @@ void ZigbeeBridgeControllerDeconz::readDataConfirm() ZigbeeInterfaceDeconzReply *reply = requestQuerySendDataConfirm(); connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){ if (reply->statusCode() != Deconz::StatusCodeSuccess) { - qCWarning(dcZigbeeController()) << "Could not read data confirm." << reply->statusCode(); + qCWarning(dcZigbeeController()) << "Could not read data confirm." << "SQN:" << reply->sequenceNumber() << reply->statusCode(); // FIXME: set an appropriate error return; } // APS data confirm received, process the content - qCDebug(dcZigbeeController()) << "Reading data confirm finished successfully"; + qCDebug(dcZigbeeController()) << "Reading data confirm finished successfully" << "SQN:" << reply->sequenceNumber(); processDataConfirm(reply->responseData()); }); } void ZigbeeBridgeControllerDeconz::processDeviceState(DeconzDeviceState deviceState) { - qCDebug(dcZigbeeController()) << deviceState; + qCDebug(dcZigbeeController()) << "Process device state notification" << deviceState; if (m_networkState != deviceState.networkState) { qCDebug(dcZigbeeController()) << "Network state changed" << deviceState.networkState; @@ -785,7 +798,10 @@ void ZigbeeBridgeControllerDeconz::processDeviceState(DeconzDeviceState deviceSt if (m_apsFreeSlotsAvailable != deviceState.apsDataRequestFreeSlots) { m_apsFreeSlotsAvailable = deviceState.apsDataRequestFreeSlots; - + if (!m_apsFreeSlotsAvailable) { + qCWarning(dcZigbeeController()) << "The APS request table is full on the device. Cannot send requests until the queue gets processed on the controller."; + return; + } // FIXME: if changed to true, send next aps data request } @@ -851,6 +867,7 @@ void ZigbeeBridgeControllerDeconz::processDataIndication(const QByteArray &data) // Process the device state in order to check if we have to request another indication DeconzDeviceState deviceState = parseDeviceStateFlag(deviceStateFlag); + qCDebug(dcZigbeeController()) << "Verify device state after data indication response" << deviceState; if (deviceState.apsDataIndication) { readDataIndication(); } @@ -881,6 +898,7 @@ void ZigbeeBridgeControllerDeconz::processDataConfirm(const QByteArray &data) // Process the device state in order to check if we have to request another indication DeconzDeviceState deviceState = parseDeviceStateFlag(deviceStateFlag); + qCDebug(dcZigbeeController()) << "Verify device state after data confirmation response" << deviceState; if (deviceState.apsDataConfirm) { readDataConfirm(); } @@ -912,12 +930,16 @@ void ZigbeeBridgeControllerDeconz::onInterfacePackageReceived(const QByteArray & QByteArray data = package.right(package.length() - 5); Deconz::Command command = static_cast(commandInt); Deconz::StatusCode status = static_cast(statusInt); - //qCDebug(dcZigbeeController()) << "Interface message received" << command << "SQN:" << sequenceNumber - // << status << "Frame length:" << frameLength << ZigbeeUtils::convertByteArrayToHexString(data); + qCDebug(dcZigbeeController()) << "Interface message received" << command << "SQN:" << sequenceNumber + << status << "Frame length:" << frameLength << ZigbeeUtils::convertByteArrayToHexString(data); // Check if this is an interface response for a pending reply if (m_pendingReplies.contains(sequenceNumber) && m_pendingReplies.value(sequenceNumber)->command() == command) { ZigbeeInterfaceDeconzReply *reply = m_pendingReplies.take(sequenceNumber); + if (!reply) { + qCWarning(dcZigbeeController()) << "Received message but the corresponding reply does not exist any more."; + return; + } reply->m_responseData = data; reply->m_statusCode = status; reply->finished(); @@ -927,7 +949,7 @@ void ZigbeeBridgeControllerDeconz::onInterfacePackageReceived(const QByteArray & // Note: we got a notification, lets set the current sequence number to the notification id, // so the next request will be a continuouse increase - m_sequenceNumber = sequenceNumber; + m_sequenceNumber = sequenceNumber + 1; // No request for this data, lets check which notification and process the data switch (command) { @@ -938,12 +960,12 @@ void ZigbeeBridgeControllerDeconz::onInterfacePackageReceived(const QByteArray & break; } case Deconz::CommandMacPoll: { - // FIXME: parse the data and print info - + qCDebug(dcZigbeeController()) << "MAC Poll command received" << ZigbeeUtils::convertByteArrayToHexString(data);// FIXME: parse the data and print info break; } case Deconz::CommandSimplifiedBeacon: { // FIXME: parse the data and print info + qCDebug(dcZigbeeController()) << "Simplified beacon command received" << ZigbeeUtils::convertByteArrayToHexString(data); break; } default: @@ -951,7 +973,6 @@ void ZigbeeBridgeControllerDeconz::onInterfacePackageReceived(const QByteArray & << status << "Frame length:" << frameLength << ZigbeeUtils::convertByteArrayToHexString(data); break; } - } void ZigbeeBridgeControllerDeconz::resetControllerWatchdog() diff --git a/libnymea-zigbee/backends/deconz/zigbeebridgecontrollerdeconz.h b/libnymea-zigbee/backends/deconz/zigbeebridgecontrollerdeconz.h index d433164..0ae2988 100644 --- a/libnymea-zigbee/backends/deconz/zigbeebridgecontrollerdeconz.h +++ b/libnymea-zigbee/backends/deconz/zigbeebridgecontrollerdeconz.h @@ -29,8 +29,9 @@ #define ZIGBEEBRIDGECONTROLLERDECONZ_H #include -#include #include +#include +#include #include "zigbee.h" #include "zigbeeaddress.h" @@ -85,6 +86,7 @@ public: Deconz::NetworkState networkState() const; + // Controllere requests ZigbeeInterfaceDeconzReply *requestVersion(); ZigbeeInterfaceDeconzReply *requestDeviceState(); ZigbeeInterfaceDeconzReply *requestReadParameter(Deconz::Parameter parameter); @@ -96,12 +98,8 @@ public: ZigbeeInterfaceDeconzReply *requestQuerySendDataConfirm(); // Send data - 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); - private: ZigbeeInterfaceDeconz *m_interface = nullptr; quint8 m_sequenceNumber = 0; @@ -112,12 +110,20 @@ private: Deconz::NetworkState m_networkState = Deconz::NetworkStateOffline; QTimer *m_watchdogTimer = nullptr; + // APS request queue bool m_apsFreeSlotsAvailable = false; + QQueue m_requestQueue; + void sendNextRequest(); quint8 generateSequenceNumber(); ZigbeeInterfaceDeconzReply *createReply(Deconz::Command command, quint8 sequenceNumber, QObject *parent); + // Send data depending on the request destination address mode + 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); + // Note: this method reads all parameters individual. The returned reply it self will not send or receive any data. // The data can be fetched from m_networkConfiguration on success. ZigbeeInterfaceDeconzReply *readNetworkParameters(); diff --git a/libnymea-zigbee/backends/deconz/zigbeenetworkdeconz.cpp b/libnymea-zigbee/backends/deconz/zigbeenetworkdeconz.cpp index ae3e3a6..de90eeb 100644 --- a/libnymea-zigbee/backends/deconz/zigbeenetworkdeconz.cpp +++ b/libnymea-zigbee/backends/deconz/zigbeenetworkdeconz.cpp @@ -191,7 +191,7 @@ void ZigbeeNetworkDeconz::setCreateNetworkState(ZigbeeNetworkDeconz::CreateNetwo return; } - qCDebug(dcZigbeeNetwork()) << "Configured APS extended PANID successfully. SQN:" << reply->sequenceNumber(); + qCDebug(dcZigbeeController()) << "Configured APS extended PANID successfully. SQN:" << reply->sequenceNumber(); QByteArray paramData; QDataStream stream(¶mData, QIODevice::WriteOnly); @@ -206,7 +206,7 @@ void ZigbeeNetworkDeconz::setCreateNetworkState(ZigbeeNetworkDeconz::CreateNetwo return; } - qCDebug(dcZigbeeNetwork()) << "Configured trust center address successfully. SQN:" << reply->sequenceNumber(); + qCDebug(dcZigbeeController()) << "Configured trust center address successfully. SQN:" << reply->sequenceNumber(); QByteArray paramData; QDataStream stream(¶mData, QIODevice::WriteOnly); @@ -221,7 +221,7 @@ void ZigbeeNetworkDeconz::setCreateNetworkState(ZigbeeNetworkDeconz::CreateNetwo return; } - qCDebug(dcZigbeeNetwork()) << "Configured security mode successfully. SQN:" << reply->sequenceNumber(); + qCDebug(dcZigbeeController()) << "Configured security mode successfully. SQN:" << reply->sequenceNumber(); qCDebug(dcZigbeeNetwork()) << "Configure network key" << securityConfiguration().networkKey().toString(); @@ -233,7 +233,7 @@ void ZigbeeNetworkDeconz::setCreateNetworkState(ZigbeeNetworkDeconz::CreateNetwo // Note: writing the network key fails all the time... //return; } else { - qCDebug(dcZigbeeNetwork()) << "Configured network key successfully. SQN:" << reply->sequenceNumber(); + qCDebug(dcZigbeeController()) << "Configured network key successfully. SQN:" << reply->sequenceNumber(); } // Configuration finished, lets start the network @@ -284,7 +284,9 @@ void ZigbeeNetworkDeconz::setCreateNetworkState(ZigbeeNetworkDeconz::CreateNetwo case CreateNetworkStateInitializeCoordinatorNode: { if (m_coordinatorNode) { qCDebug(dcZigbeeNetwork()) << "We already have the coordinator node. Network starting done."; + m_initializing = false; setState(StateRunning); + setPermitJoiningInternal(false); return; } @@ -295,7 +297,9 @@ void ZigbeeNetworkDeconz::setCreateNetworkState(ZigbeeNetworkDeconz::CreateNetwo connect(coordinatorNode, &ZigbeeNode::stateChanged, this, [this, coordinatorNode](ZigbeeNode::State state){ if (state == ZigbeeNode::StateInitialized) { qCDebug(dcZigbeeNetwork()) << "Coordinator initialized successfully." << coordinatorNode; + m_initializing = false; setState(StateRunning); + setPermitJoiningInternal(false); return; } }); @@ -350,6 +354,11 @@ void ZigbeeNetworkDeconz::handleZigbeeClusterLibraryIndication(const Zigbee::Aps if (!node) { qCWarning(dcZigbeeNetwork()) << "Received a ZCL indication for an unrecognized node. There is no such node in the system. Ignoring indication" << indication; // FIXME: maybe create and init the node, since it is in the network, but not recognized + + // Remove this node since we might have removed it but it did not respond, or we not explicitly allowed it to join. + + + return; } @@ -467,6 +476,8 @@ void ZigbeeNetworkDeconz::startNetworkInternally() return; } + qCDebug(dcZigbeeNetwork()) << "Reading current network state finished successfully." << "SQN:" << reply->sequenceNumber(); + QDataStream stream(reply->responseData()); stream.setByteOrder(QDataStream::LittleEndian); quint8 deviceStateFlag = 0; @@ -489,8 +500,11 @@ void ZigbeeNetworkDeconz::startNetworkInternally() // Get the network state and start the network if required if (m_controller->networkState() == Deconz::NetworkStateConnected) { qCDebug(dcZigbeeNetwork()) << "The network is already running."; + m_initializing = false; setState(StateRunning); + setPermitJoiningInternal(false); } else if (m_controller->networkState() == Deconz::NetworkStateOffline) { + m_initializing = true; qCDebug(dcZigbeeNetwork()) << "The network is offline. Lets start it"; setCreateNetworkState(CreateNetworkStateStartNetwork); } else { @@ -507,12 +521,14 @@ void ZigbeeNetworkDeconz::onControllerAvailableChanged(bool available) if (!available) { qCWarning(dcZigbeeNetwork()) << "Hardware controller is not available any more."; setError(ErrorHardwareUnavailable); + m_initializing = false; m_permitJoining = false; emit permitJoiningChanged(m_permitJoining); setState(StateOffline); } else { m_error = ErrorNoError; m_permitJoining = false; + m_initializing = true; emit permitJoiningChanged(m_permitJoining); setState(StateStarting); qCDebug(dcZigbeeNetwork()) << "Hardware controller is now available."; @@ -647,7 +663,10 @@ void ZigbeeNetworkDeconz::startNetwork() m_permitJoining = false; emit permitJoiningChanged(m_permitJoining); + // Note: wait for the controller available signal and start the initialization there + + m_initializing = true; } void ZigbeeNetworkDeconz::stopNetwork() @@ -674,6 +693,7 @@ void ZigbeeNetworkDeconz::reset() void ZigbeeNetworkDeconz::factoryResetNetwork() { qCDebug(dcZigbeeNetwork()) << "Factory reset network and forget all information. This cannot be undone."; + m_controller->disable(); clearSettings(); setState(StateUninitialized); qCDebug(dcZigbeeNetwork()) << "The factory reset is finished. Start restart with a fresh network."; diff --git a/libnymea-zigbee/backends/deconz/zigbeenetworkdeconz.h b/libnymea-zigbee/backends/deconz/zigbeenetworkdeconz.h index 5b3c25b..abd2b53 100644 --- a/libnymea-zigbee/backends/deconz/zigbeenetworkdeconz.h +++ b/libnymea-zigbee/backends/deconz/zigbeenetworkdeconz.h @@ -56,13 +56,14 @@ public: // Sending an APSDE-DATA.request, will be finished on APSDE-DATA.confirm ZigbeeNetworkReply *sendRequest(const ZigbeeNetworkRequest &request) override; - ZigbeeNetworkReply *setPermitJoin(quint16 shortAddress, quint8 duration); + ZigbeeNetworkReply *setPermitJoin(quint16 shortAddress = Zigbee::BroadcastAddressAllRouters, quint8 duration = 0xfe); private: ZigbeeBridgeControllerDeconz *m_controller = nullptr; bool m_networkRunning = false; CreateNetworkState m_createState = CreateNetworkStateIdle; bool m_createNewNetwork = false; + bool m_initializing = false; QTimer *m_permitJoinRefreshTimer = nullptr; diff --git a/libnymea-zigbee/libnymea-zigbee.pro b/libnymea-zigbee/libnymea-zigbee.pro index 5c97e15..4816dd4 100644 --- a/libnymea-zigbee/libnymea-zigbee.pro +++ b/libnymea-zigbee/libnymea-zigbee.pro @@ -9,6 +9,7 @@ SOURCES += \ backends/deconz/zigbeebridgecontrollerdeconz.cpp \ backends/deconz/zigbeenetworkdeconz.cpp \ zcl/general/zigbeeclusteridentify.cpp \ + zcl/general/zigbeeclusterlevelcontrol.cpp \ zcl/general/zigbeeclusteronoff.cpp \ zcl/measurement/zigbeeclusterilluminancemeasurment.cpp \ zcl/measurement/zigbeeclusteroccupancysensing.cpp \ @@ -57,6 +58,7 @@ HEADERS += \ backends/deconz/zigbeebridgecontrollerdeconz.h \ backends/deconz/zigbeenetworkdeconz.h \ zcl/general/zigbeeclusteridentify.h \ + zcl/general/zigbeeclusterlevelcontrol.h \ zcl/general/zigbeeclusteronoff.h \ zcl/measurement/zigbeeclusterilluminancemeasurment.h \ zcl/measurement/zigbeeclusteroccupancysensing.h \ diff --git a/libnymea-zigbee/zcl/general/zigbeeclusteridentify.cpp b/libnymea-zigbee/zcl/general/zigbeeclusteridentify.cpp index a164184..1ab34df 100644 --- a/libnymea-zigbee/zcl/general/zigbeeclusteridentify.cpp +++ b/libnymea-zigbee/zcl/general/zigbeeclusteridentify.cpp @@ -40,156 +40,25 @@ ZigbeeClusterIdentify::ZigbeeClusterIdentify(ZigbeeNetwork *network, ZigbeeNode ZigbeeClusterReply *ZigbeeClusterIdentify::identify(quint16 seconds) { - ZigbeeNetworkRequest request = createGeneralRequest(); - - // Build ZCL frame - - ZigbeeClusterLibrary::FrameControl frameControl; - frameControl.frameType = ZigbeeClusterLibrary::FrameTypeClusterSpecific; - frameControl.manufacturerSpecific = false; - frameControl.direction = ZigbeeClusterLibrary::DirectionClientToServer; - frameControl.disableDefaultResponse = false; - - // ZCL header - ZigbeeClusterLibrary::Header header; - header.frameControl = frameControl; - header.command = ZigbeeClusterIdentify::CommandIdentify; - header.transactionSequenceNumber = m_transactionSequenceNumber++; - // Note: the identify time unit is 0.5 seconds QByteArray payload = ZigbeeDataType(seconds * 2).data(); - - // Put them together - ZigbeeClusterLibrary::Frame frame; - frame.header = header; - frame.payload = payload; - - request.setTxOptions(Zigbee::ZigbeeTxOptions(Zigbee::ZigbeeTxOptionAckTransmission)); - request.setAsdu(ZigbeeClusterLibrary::buildFrame(frame)); - - ZigbeeClusterReply *zclReply = createClusterReply(request, frame); - ZigbeeNetworkReply *networkReply = m_network->sendRequest(request); - connect(networkReply, &ZigbeeNetworkReply::finished, this, [this, networkReply, zclReply](){ - if (!verifyNetworkError(zclReply, networkReply)) { - qCWarning(dcZigbeeClusterLibrary()) << "Failed to send request" - << m_node << networkReply->error() - << networkReply->zigbeeApsStatus(); - finishZclReply(zclReply); - return; - } - - // The request was successfully sent to the device - // Now check if the expected indication response received already - if (zclReply->isComplete()) { - finishZclReply(zclReply); - return; - } - }); - - return zclReply; + return executeClusterCommand(ZigbeeClusterIdentify::CommandIdentify, payload); } ZigbeeClusterReply *ZigbeeClusterIdentify::identifyQuery() { - ZigbeeNetworkRequest request = createGeneralRequest(); - - // Build ZCL frame - - ZigbeeClusterLibrary::FrameControl frameControl; - frameControl.frameType = ZigbeeClusterLibrary::FrameTypeClusterSpecific; - frameControl.manufacturerSpecific = false; - frameControl.direction = ZigbeeClusterLibrary::DirectionClientToServer; - frameControl.disableDefaultResponse = false; - - // ZCL header - ZigbeeClusterLibrary::Header header; - header.frameControl = frameControl; - header.command = ZigbeeClusterIdentify::CommandIdentifyQuery; - header.transactionSequenceNumber = m_transactionSequenceNumber++; - - // No payload - - // Put them together - ZigbeeClusterLibrary::Frame frame; - frame.header = header; - - request.setTxOptions(Zigbee::ZigbeeTxOptions(Zigbee::ZigbeeTxOptionAckTransmission)); - request.setAsdu(ZigbeeClusterLibrary::buildFrame(frame)); - - ZigbeeClusterReply *zclReply = createClusterReply(request, frame); - ZigbeeNetworkReply *networkReply = m_network->sendRequest(request); - connect(networkReply, &ZigbeeNetworkReply::finished, this, [this, networkReply, zclReply](){ - if (!verifyNetworkError(zclReply, networkReply)) { - qCWarning(dcZigbeeClusterLibrary()) << "Failed to send request" - << m_node << networkReply->error() - << networkReply->zigbeeApsStatus(); - finishZclReply(zclReply); - return; - } - - // The request was successfully sent to the device - // Now check if the expected indication response received already - if (zclReply->isComplete()) { - finishZclReply(zclReply); - return; - } - }); - - return zclReply; + return executeClusterCommand(ZigbeeClusterIdentify::CommandIdentifyQuery); } ZigbeeClusterReply *ZigbeeClusterIdentify::triggerEffect(ZigbeeClusterIdentify::Effect effect, quint8 effectVariant) { - ZigbeeNetworkRequest request = createGeneralRequest(); - - // Build ZCL frame - - ZigbeeClusterLibrary::FrameControl frameControl; - frameControl.frameType = ZigbeeClusterLibrary::FrameTypeClusterSpecific; - frameControl.manufacturerSpecific = false; - frameControl.direction = ZigbeeClusterLibrary::DirectionClientToServer; - frameControl.disableDefaultResponse = false; - - // ZCL header - ZigbeeClusterLibrary::Header header; - header.frameControl = frameControl; - header.command = ZigbeeClusterIdentify::CommandTriggerEffect; - header.transactionSequenceNumber = m_transactionSequenceNumber++; - QByteArray payload; QDataStream stream(&payload, QIODevice::WriteOnly); stream.setByteOrder(QDataStream::LittleEndian); stream << static_cast(effect); stream << static_cast(effectVariant); - // Put them together - ZigbeeClusterLibrary::Frame frame; - frame.header = header; - frame.payload = payload; - - request.setTxOptions(Zigbee::ZigbeeTxOptions(Zigbee::ZigbeeTxOptionAckTransmission)); - request.setAsdu(ZigbeeClusterLibrary::buildFrame(frame)); - - ZigbeeClusterReply *zclReply = createClusterReply(request, frame); - ZigbeeNetworkReply *networkReply = m_network->sendRequest(request); - connect(networkReply, &ZigbeeNetworkReply::finished, this, [this, networkReply, zclReply](){ - if (!verifyNetworkError(zclReply, networkReply)) { - qCWarning(dcZigbeeClusterLibrary()) << "Failed to send request" - << m_node << networkReply->error() - << networkReply->zigbeeApsStatus(); - finishZclReply(zclReply); - return; - } - - // The request was successfully sent to the device - // Now check if the expected indication response received already - if (zclReply->isComplete()) { - finishZclReply(zclReply); - return; - } - }); - - return zclReply; + return executeClusterCommand(ZigbeeClusterIdentify::CommandTriggerEffect, payload); } void ZigbeeClusterIdentify::setAttribute(const ZigbeeClusterAttribute &attribute) diff --git a/libnymea-zigbee/zcl/general/zigbeeclusterlevelcontrol.cpp b/libnymea-zigbee/zcl/general/zigbeeclusterlevelcontrol.cpp new file mode 100644 index 0000000..c2e973d --- /dev/null +++ b/libnymea-zigbee/zcl/general/zigbeeclusterlevelcontrol.cpp @@ -0,0 +1,137 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* 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 . +* +* 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 "zigbeeclusterlevelcontrol.h" +#include "zigbeenetworkreply.h" +#include "loggingcategory.h" +#include "zigbeenetwork.h" + +#include + +ZigbeeClusterLevelControl::ZigbeeClusterLevelControl(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, ZigbeeCluster::Direction direction, QObject *parent) : + ZigbeeCluster(network, node, endpoint, Zigbee::ClusterIdLevelControl, direction, parent) +{ + +} + +ZigbeeClusterReply *ZigbeeClusterLevelControl::commandMoveToLevel(quint8 level, quint16 transistionTime) +{ + QByteArray payload; + QDataStream stream(&payload, QIODevice::WriteOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream << level << transistionTime; + return executeClusterCommand(ZigbeeClusterLevelControl::CommandMoveToLevel, payload); +} + +ZigbeeClusterReply *ZigbeeClusterLevelControl::commandMove(ZigbeeClusterLevelControl::MoveMode moveMode, quint8 rate) +{ + QByteArray payload; + QDataStream stream(&payload, QIODevice::WriteOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream << static_cast(moveMode) << rate; + return executeClusterCommand(ZigbeeClusterLevelControl::CommandMove, payload); +} + +ZigbeeClusterReply *ZigbeeClusterLevelControl::commandStep(ZigbeeClusterLevelControl::FadeMode fadeMode, quint8 stepSize, quint16 transistionTime) +{ + QByteArray payload; + QDataStream stream(&payload, QIODevice::WriteOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream << static_cast(fadeMode) << stepSize << transistionTime; + return executeClusterCommand(ZigbeeClusterLevelControl::CommandStep, payload); +} + +ZigbeeClusterReply *ZigbeeClusterLevelControl::commandStop() +{ + return executeClusterCommand(ZigbeeClusterLevelControl::CommandStop); +} + +ZigbeeClusterReply *ZigbeeClusterLevelControl::commandMoveToLevelWithOnOff(quint8 level, quint16 transistionTime) +{ + QByteArray payload; + QDataStream stream(&payload, QIODevice::WriteOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream << level << transistionTime; + return executeClusterCommand(ZigbeeClusterLevelControl::CommandMoveToLevelWithOnOff, payload); +} + +ZigbeeClusterReply *ZigbeeClusterLevelControl::commandMoveWithOnOff(ZigbeeClusterLevelControl::MoveMode moveMode, quint8 rate) +{ + QByteArray payload; + QDataStream stream(&payload, QIODevice::WriteOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream << static_cast(moveMode) << rate; + return executeClusterCommand(ZigbeeClusterLevelControl::CommandMoveWithOnOff, payload); +} + +ZigbeeClusterReply *ZigbeeClusterLevelControl::commandStepWithOnOff(ZigbeeClusterLevelControl::FadeMode fadeMode, quint8 stepSize, quint16 transistionTime) +{ + QByteArray payload; + QDataStream stream(&payload, QIODevice::WriteOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream << static_cast(fadeMode) << stepSize << transistionTime; + return executeClusterCommand(ZigbeeClusterLevelControl::CommandStepWithOnOff, payload); +} + +ZigbeeClusterReply *ZigbeeClusterLevelControl::commandStopWithOnOff() +{ + return executeClusterCommand(ZigbeeClusterLevelControl::CommandStopWithOnOff); +} + +void ZigbeeClusterLevelControl::setAttribute(const ZigbeeClusterAttribute &attribute) +{ + qCDebug(dcZigbeeCluster()) << "Update attribute" << m_node << m_endpoint << this << static_cast(attribute.id()) << attribute.dataType(); + if (hasAttribute(attribute.id())) { + m_attributes[attribute.id()] = attribute; + emit attributeChanged(attribute); + } else { + m_attributes.insert(attribute.id(), attribute); + emit attributeChanged(attribute); + } + + // Parse the information for convinience + if (attribute.id() == AttributeCurrentLevel) { + bool valueOk = false; + quint8 value = attribute.dataType().toUInt8(&valueOk); + if (valueOk) { + qCDebug(dcZigbeeCluster()) << "CurrentLevel state changed on" << m_node << m_endpoint << this << value; + emit currentLevelChanged(value); + } else { + qCWarning(dcZigbeeCluster()) << "Failed to parse attribute data" << m_node << m_endpoint << this << attribute; + } + } +} + +void ZigbeeClusterLevelControl::processDataIndication(ZigbeeClusterLibrary::Frame frame) +{ + qCDebug(dcZigbeeCluster()) << "Processing cluster frame" << m_node << m_endpoint << this << frame; + + // Increase the tsn for continuouse id increasing on both sides + m_transactionSequenceNumber = frame.header.transactionSequenceNumber; + + // FIXME: parse client commands to group +} diff --git a/libnymea-zigbee/zcl/general/zigbeeclusterlevelcontrol.h b/libnymea-zigbee/zcl/general/zigbeeclusterlevelcontrol.h new file mode 100644 index 0000000..ce34bf7 --- /dev/null +++ b/libnymea-zigbee/zcl/general/zigbeeclusterlevelcontrol.h @@ -0,0 +1,108 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* 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 . +* +* 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 +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef ZIGBEECLUSTERLEVELCONTROL_H +#define ZIGBEECLUSTERLEVELCONTROL_H + +#include + +#include "zcl/zigbeecluster.h" +#include "zcl/zigbeeclusterreply.h" + +class ZigbeeNode; +class ZigbeeNetwork; +class ZigbeeNodeEndpoint; +class ZigbeeNetworkReply; + +class ZigbeeClusterLevelControl : public ZigbeeCluster +{ + Q_OBJECT + + friend class ZigbeeNode; + friend class ZigbeeNetwork; + +public: + enum Attribute { + AttributeCurrentLevel = 0x0000, + AttributeRemainingTime = 0x0001, + AttributeOnOffTransitionTime = 0x0010, + AttributeOnLevel = 0x0011, + AttributeOnTransitionTime = 0x0012, + AttributeOffTransitionTime = 0x0013, + AttributeDefaultMoveRate = 0x0014 + }; + Q_ENUM(Attribute) + + enum Command { + CommandMoveToLevel = 0x00, + CommandMove = 0x01, + CommandStep = 0x02, + CommandStop = 0x03, + CommandMoveToLevelWithOnOff = 0x04, + CommandMoveWithOnOff = 0x05, + CommandStepWithOnOff = 0x06, + CommandStopWithOnOff = 0x07 + }; + Q_ENUM(Command) + + enum MoveMode { + MoveModeUp = 0x00, + ModeModeDown = 0x01 + }; + Q_ENUM(MoveMode) + + enum FadeMode { + FadeModeUp = 0x00, + FadeModeDown = 0x01 + }; + Q_ENUM(FadeMode) + + explicit ZigbeeClusterLevelControl(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent = nullptr); + + ZigbeeClusterReply *commandMoveToLevel(quint8 level, quint16 transistionTime = 0xffff); + ZigbeeClusterReply *commandMove(MoveMode moveMode, quint8 rate = 0xff); + ZigbeeClusterReply *commandStep(FadeMode fadeMode, quint8 stepSize = 0x01, quint16 transistionTime = 0xffff); + ZigbeeClusterReply *commandStop(); + + // With on/off + ZigbeeClusterReply *commandMoveToLevelWithOnOff(quint8 level, quint16 transistionTime = 0xffff); + ZigbeeClusterReply *commandMoveWithOnOff(MoveMode moveMode, quint8 rate = 0xff); + ZigbeeClusterReply *commandStepWithOnOff(FadeMode fadeMode, quint8 stepSize = 0x01, quint16 transistionTime = 0xffff); + ZigbeeClusterReply *commandStopWithOnOff(); + +private: + void setAttribute(const ZigbeeClusterAttribute &attribute) override; + +protected: + void processDataIndication(ZigbeeClusterLibrary::Frame frame) override; + +signals: + void currentLevelChanged(quint8 level); + +}; + +#endif // ZIGBEECLUSTERLEVELCONTROL_H diff --git a/libnymea-zigbee/zcl/general/zigbeeclusteronoff.cpp b/libnymea-zigbee/zcl/general/zigbeeclusteronoff.cpp index b6350d4..11115eb 100644 --- a/libnymea-zigbee/zcl/general/zigbeeclusteronoff.cpp +++ b/libnymea-zigbee/zcl/general/zigbeeclusteronoff.cpp @@ -40,152 +40,17 @@ ZigbeeClusterOnOff::ZigbeeClusterOnOff(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeClusterReply *ZigbeeClusterOnOff::commandOff() { - ZigbeeNetworkRequest request = createGeneralRequest(); - - // Build ZCL frame - - // Note: for basic commands the frame control files has to be zero accoring to spec ZCL 2.4.1.1 - ZigbeeClusterLibrary::FrameControl frameControl; - frameControl.frameType = ZigbeeClusterLibrary::FrameTypeClusterSpecific; - frameControl.manufacturerSpecific = false; - frameControl.direction = ZigbeeClusterLibrary::DirectionClientToServer; - frameControl.disableDefaultResponse = false; - - // ZCL header - ZigbeeClusterLibrary::Header header; - header.frameControl = frameControl; - header.command = ZigbeeClusterOnOff::CommandOff; - header.transactionSequenceNumber = m_transactionSequenceNumber++; - - // there is no ZCL payload - - // Put them together - ZigbeeClusterLibrary::Frame frame; - frame.header = header; - - request.setTxOptions(Zigbee::ZigbeeTxOptions(Zigbee::ZigbeeTxOptionAckTransmission)); - request.setAsdu(ZigbeeClusterLibrary::buildFrame(frame)); - - ZigbeeClusterReply *zclReply = createClusterReply(request, frame); - ZigbeeNetworkReply *networkReply = m_network->sendRequest(request); - connect(networkReply, &ZigbeeNetworkReply::finished, this, [this, networkReply, zclReply](){ - if (!verifyNetworkError(zclReply, networkReply)) { - qCWarning(dcZigbeeClusterLibrary()) << "Failed to send request" - << m_node << networkReply->error() - << networkReply->zigbeeApsStatus(); - finishZclReply(zclReply); - return; - } - - // The request was successfully sent to the device - // Now check if the expected indication response received already - if (zclReply->isComplete()) { - finishZclReply(zclReply); - return; - } - }); - - return zclReply; + return executeClusterCommand(ZigbeeClusterOnOff::CommandOff); } ZigbeeClusterReply *ZigbeeClusterOnOff::commandOn() { - ZigbeeNetworkRequest request = createGeneralRequest(); - - // Build ZCL frame - - // Note: for basic commands the frame control files has to be zero accoring to spec ZCL 2.4.1.1 - ZigbeeClusterLibrary::FrameControl frameControl; - frameControl.frameType = ZigbeeClusterLibrary::FrameTypeClusterSpecific; - frameControl.manufacturerSpecific = false; - frameControl.direction = ZigbeeClusterLibrary::DirectionClientToServer; - frameControl.disableDefaultResponse = false; - - // ZCL header - ZigbeeClusterLibrary::Header header; - header.frameControl = frameControl; - header.command = ZigbeeClusterOnOff::CommandOn; - header.transactionSequenceNumber = m_transactionSequenceNumber++; - - // There is no ZCL payload - - // Put them together - ZigbeeClusterLibrary::Frame frame; - frame.header = header; - - request.setTxOptions(Zigbee::ZigbeeTxOptions(Zigbee::ZigbeeTxOptionAckTransmission)); - request.setAsdu(ZigbeeClusterLibrary::buildFrame(frame)); - - ZigbeeClusterReply *zclReply = createClusterReply(request, frame); - ZigbeeNetworkReply *networkReply = m_network->sendRequest(request); - connect(networkReply, &ZigbeeNetworkReply::finished, this, [this, networkReply, zclReply](){ - if (!verifyNetworkError(zclReply, networkReply)) { - qCWarning(dcZigbeeClusterLibrary()) << "Failed to send request" - << m_node << networkReply->error() - << networkReply->zigbeeApsStatus(); - finishZclReply(zclReply); - return; - } - - // The request was successfully sent to the device - // Now check if the expected indication response received already - if (zclReply->isComplete()) { - finishZclReply(zclReply); - return; - } - }); - - return zclReply; + return executeClusterCommand(ZigbeeClusterOnOff::CommandOn); } ZigbeeClusterReply *ZigbeeClusterOnOff::commandToggle() { - ZigbeeNetworkRequest request = createGeneralRequest(); - - // Build ZCL frame - - // Note: for basic commands the frame control files has to be zero accoring to spec ZCL 2.4.1.1 - ZigbeeClusterLibrary::FrameControl frameControl; - frameControl.frameType = ZigbeeClusterLibrary::FrameTypeClusterSpecific; - frameControl.manufacturerSpecific = false; - frameControl.direction = ZigbeeClusterLibrary::DirectionClientToServer; - frameControl.disableDefaultResponse = false; - - // ZCL header - ZigbeeClusterLibrary::Header header; - header.frameControl = frameControl; - header.command = ZigbeeClusterOnOff::CommandToggle; - header.transactionSequenceNumber = m_transactionSequenceNumber++; - - // There is no ZCL payload - - // Put them together - ZigbeeClusterLibrary::Frame frame; - frame.header = header; - - request.setTxOptions(Zigbee::ZigbeeTxOptions(Zigbee::ZigbeeTxOptionAckTransmission)); - request.setAsdu(ZigbeeClusterLibrary::buildFrame(frame)); - - ZigbeeClusterReply *zclReply = createClusterReply(request, frame); - ZigbeeNetworkReply *networkReply = m_network->sendRequest(request); - connect(networkReply, &ZigbeeNetworkReply::finished, this, [this, networkReply, zclReply](){ - if (!verifyNetworkError(zclReply, networkReply)) { - qCWarning(dcZigbeeClusterLibrary()) << "Failed to send request" - << m_node << networkReply->error() - << networkReply->zigbeeApsStatus(); - finishZclReply(zclReply); - return; - } - - // The request was successfully sent to the device - // Now check if the expected indication response received already - if (zclReply->isComplete()) { - finishZclReply(zclReply); - return; - } - }); - - return zclReply; + return executeClusterCommand(ZigbeeClusterOnOff::CommandToggle); } void ZigbeeClusterOnOff::setAttribute(const ZigbeeClusterAttribute &attribute) @@ -240,13 +105,10 @@ void ZigbeeClusterOnOff::processDataIndication(ZigbeeClusterLibrary::Frame frame qCWarning(dcZigbeeCluster()) << "Unhandled command sent from" << m_node << m_endpoint << this << command; break; } - return; } break; case Server: - // keep it unhandled if not parsed yet in order to warn about the handled indication + qCWarning(dcZigbeeCluster()) << "Unhandled ZCL indication in" << m_node << m_endpoint << this << frame; break; } - - qCWarning(dcZigbeeCluster()) << "Unhandled ZCL indication in" << m_node << m_endpoint << this << frame; } diff --git a/libnymea-zigbee/zcl/security/zigbeeclusteriaszone.cpp b/libnymea-zigbee/zcl/security/zigbeeclusteriaszone.cpp index 5695653..118b233 100644 --- a/libnymea-zigbee/zcl/security/zigbeeclusteriaszone.cpp +++ b/libnymea-zigbee/zcl/security/zigbeeclusteriaszone.cpp @@ -80,7 +80,6 @@ void ZigbeeClusterIasZone::processDataIndication(ZigbeeClusterLibrary::Frame fra // Update the ZoneState attribute setAttribute(ZigbeeClusterAttribute(AttributeZoneState, ZigbeeDataType(Zigbee::BitMap16, frame.payload.left(2)))); - emit zoneStatusChanged(ZoneStatusFlags(zoneStatus), extendedStatus, zoneId, delay); break; } diff --git a/libnymea-zigbee/zcl/zigbeecluster.cpp b/libnymea-zigbee/zcl/zigbeecluster.cpp index c2c5508..7dd4cda 100644 --- a/libnymea-zigbee/zcl/zigbeecluster.cpp +++ b/libnymea-zigbee/zcl/zigbeecluster.cpp @@ -43,7 +43,7 @@ ZigbeeCluster::ZigbeeCluster(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNod m_clusterId(clusterId), m_direction(direction) { - qCDebug(dcZigbeeCluster()) << "Create cluster" << ZigbeeUtils::convertUint16ToHexString(clusterId) << direction; + //qCDebug(dcZigbeeCluster()) << "Create cluster" << ZigbeeUtils::convertUint16ToHexString(clusterId) << direction; } ZigbeeCluster::Direction ZigbeeCluster::direction() const @@ -162,6 +162,53 @@ ZigbeeClusterReply *ZigbeeCluster::createClusterReply(const ZigbeeNetworkRequest return zclReply; } +ZigbeeClusterReply *ZigbeeCluster::executeClusterCommand(quint8 command, const QByteArray &payload) +{ + ZigbeeNetworkRequest request = createGeneralRequest(); + + // Build ZCL frame control + ZigbeeClusterLibrary::FrameControl frameControl; + frameControl.frameType = ZigbeeClusterLibrary::FrameTypeClusterSpecific; + frameControl.manufacturerSpecific = false; + frameControl.direction = ZigbeeClusterLibrary::DirectionClientToServer; + frameControl.disableDefaultResponse = false; + + // Build ZCL header + ZigbeeClusterLibrary::Header header; + header.frameControl = frameControl; + header.command = command; + header.transactionSequenceNumber = m_transactionSequenceNumber++; + + // Build ZCL frame + ZigbeeClusterLibrary::Frame frame; + frame.header = header; + frame.payload = payload; + + request.setTxOptions(Zigbee::ZigbeeTxOptions(Zigbee::ZigbeeTxOptionAckTransmission)); + request.setAsdu(ZigbeeClusterLibrary::buildFrame(frame)); + + ZigbeeClusterReply *zclReply = createClusterReply(request, frame); + ZigbeeNetworkReply *networkReply = m_network->sendRequest(request); + connect(networkReply, &ZigbeeNetworkReply::finished, this, [this, networkReply, zclReply](){ + if (!verifyNetworkError(zclReply, networkReply)) { + qCWarning(dcZigbeeClusterLibrary()) << "Failed to send request" + << m_node << networkReply->error() + << networkReply->zigbeeApsStatus(); + finishZclReply(zclReply); + return; + } + + // The request was successfully sent to the device + // Now check if the expected indication response received already + if (zclReply->isComplete()) { + finishZclReply(zclReply); + return; + } + }); + + return zclReply; +} + ZigbeeNetworkRequest ZigbeeCluster::createGeneralRequest() { // Build the request diff --git a/libnymea-zigbee/zcl/zigbeecluster.h b/libnymea-zigbee/zcl/zigbeecluster.h index ebf8c04..429338e 100644 --- a/libnymea-zigbee/zcl/zigbeecluster.h +++ b/libnymea-zigbee/zcl/zigbeecluster.h @@ -90,31 +90,6 @@ public: // Q_ENUM(PowerConfigurationAttribute) -// // Level cluster 0x0008 - -// enum LevelClusterAttribute { -// LevelClusterAttributeCurrentLevel = 0x0000, -// LevelClusterAttributeRemainingTime = 0x0001, -// LevelClusterAttributeOnOffTransitionTime = 0x0010, -// LevelClusterAttributeOnLevel = 0x0011, -// LevelClusterAttributeOnTransitionTime = 0x0012, -// LevelClusterAttributeOffTransitionTime = 0x0013, -// LevelClusterAttributeDefaultMoveRate = 0x0014 -// }; -// Q_ENUM(LevelClusterAttribute) - -// enum LevelClusterCommand { -// LevelClusterCommandMoveToLevel = 0x00, -// LevelClusterCommandMove = 0x01, -// LevelClusterCommandStep = 0x02, -// LevelClusterCommandStop = 0x03, -// LevelClusterCommandMoveToLevelWithOnOff = 0x04, -// LevelClusterCommandMoveWithOnOff = 0x05, -// LevelClusterCommandStepWithOnOff = 0x06, -// LevelClusterCommandStopWithOnOff = 0x07, -// }; -// Q_ENUM(LevelClusterCommand) - // // Color cluster 0x0300 // enum ColorControlClusterAttribute { @@ -170,11 +145,12 @@ protected: Direction m_direction = Server; QHash m_attributes; + // Helper methods for sending cluster specific commands ZigbeeNetworkRequest createGeneralRequest(); quint8 m_transactionSequenceNumber = 0; QHash m_pendingReplies; ZigbeeClusterReply *createClusterReply(const ZigbeeNetworkRequest &request, ZigbeeClusterLibrary::Frame frame); - + ZigbeeClusterReply *executeClusterCommand(quint8 command, const QByteArray &payload = QByteArray()); bool verifyNetworkError(ZigbeeClusterReply *zclReply, ZigbeeNetworkReply *networkReply); void finishZclReply(ZigbeeClusterReply *zclReply); diff --git a/libnymea-zigbee/zdo/zigbeedeviceprofile.cpp b/libnymea-zigbee/zdo/zigbeedeviceprofile.cpp index b23d688..dedd7b2 100644 --- a/libnymea-zigbee/zdo/zigbeedeviceprofile.cpp +++ b/libnymea-zigbee/zdo/zigbeedeviceprofile.cpp @@ -30,6 +30,158 @@ #include +ZigbeeDeviceProfile::NodeDescriptor ZigbeeDeviceProfile::parseNodeDescriptor(const QByteArray &payload) +{ + NodeDescriptor nodeDescriptor; + nodeDescriptor.descriptorRawData = payload; + + // Parse and set the node descriptor + QDataStream stream(payload); + stream.setByteOrder(QDataStream::LittleEndian); + quint8 typeDescriptorFlag = 0; quint8 frequencyFlag = 0; quint8 macCapabilitiesFlag = 0; + quint16 serverMaskFlag = 0; quint8 descriptorCapabilitiesFlag = 0; + + stream >> typeDescriptorFlag >> frequencyFlag >> macCapabilitiesFlag >> nodeDescriptor.manufacturerCode >> nodeDescriptor.maximumBufferSize; + stream >> nodeDescriptor.maximumRxSize >> serverMaskFlag >> nodeDescriptor.maximumTxSize >> descriptorCapabilitiesFlag; + + // 0-2 Bit = logical type, 0 = coordinator, 1 = router, 2 = end device + if (!ZigbeeUtils::checkBitUint8(typeDescriptorFlag, 0) && !ZigbeeUtils::checkBitUint8(typeDescriptorFlag, 1)) { + nodeDescriptor.nodeType = NodeTypeCoordinator; + } else if (!ZigbeeUtils::checkBitUint8(typeDescriptorFlag, 0) && ZigbeeUtils::checkBitUint8(typeDescriptorFlag, 1)) { + nodeDescriptor.nodeType = NodeTypeRouter; + } else if (ZigbeeUtils::checkBitUint8(typeDescriptorFlag, 0) && !ZigbeeUtils::checkBitUint8(typeDescriptorFlag, 1)) { + nodeDescriptor.nodeType = NodeTypeEndDevice; + } + + nodeDescriptor.complexDescriptorAvailable = (typeDescriptorFlag >> 3) & 0x0001; + nodeDescriptor.userDescriptorAvailable = (typeDescriptorFlag >> 4) & 0x0001; + + // Frequency band, 5 bits + if (ZigbeeUtils::checkBitUint8(frequencyFlag, 3)) { + nodeDescriptor.frequencyBand = FrequencyBand868Mhz; + } else if (ZigbeeUtils::checkBitUint8(frequencyFlag, 5)) { + nodeDescriptor.frequencyBand = FrequencyBand902Mhz; + } else if (ZigbeeUtils::checkBitUint8(frequencyFlag, 6)) { + nodeDescriptor.frequencyBand = FrequencyBand2400Mhz; + } + + nodeDescriptor.macCapabilities = parseMacCapabilities(macCapabilitiesFlag); + nodeDescriptor.serverMask = parseServerMask(serverMaskFlag); + nodeDescriptor.descriptorCapabilities = parseDescriptorCapabilities(descriptorCapabilitiesFlag); + + return nodeDescriptor; +} + +ZigbeeDeviceProfile::MacCapabilities ZigbeeDeviceProfile::parseMacCapabilities(quint8 macCapabilitiesFlag) +{ + MacCapabilities capabilities; + capabilities.flag = macCapabilitiesFlag; + capabilities.alternatePanCoordinator = ((macCapabilitiesFlag >> 0) & 0x01); + if (((macCapabilitiesFlag >> 1) & 0x01)) { + capabilities.deviceType = DeviceTypeFullFunction; + } else { + capabilities.deviceType = DeviceTypeReducedFunction; + } + capabilities.powerSourceFlagMainPower = ((macCapabilitiesFlag >> 2) & 0x01); + capabilities.receiverOnWhenIdle = ((macCapabilitiesFlag >> 3) & 0x01); + capabilities.securityCapability = ((macCapabilitiesFlag >> 6) & 0x01); + capabilities.allocateAddress = ((macCapabilitiesFlag >> 7) & 0x01); + return capabilities; +} + +ZigbeeDeviceProfile::ServerMask ZigbeeDeviceProfile::parseServerMask(quint16 serverMaskFlag) +{ + ServerMask serverMask; + serverMask.serverMaskFlag = serverMaskFlag; + serverMask.primaryTrustCenter = ((serverMaskFlag >> 0) & 0x0001); + serverMask.backupTrustCenter = ((serverMaskFlag >> 1) & 0x0001); + serverMask.primaryBindingCache = ((serverMaskFlag >> 2) & 0x0001); + serverMask.backupBindingCache = ((serverMaskFlag >> 3) & 0x0001); + serverMask.primaryDiscoveryCache = ((serverMaskFlag >> 4) & 0x0001); + serverMask.backupDiscoveryCache = ((serverMaskFlag >> 5) & 0x0001); + serverMask.networkManager = ((serverMaskFlag >> 6) & 0x0001); + return serverMask; +} + +ZigbeeDeviceProfile::DescriptorCapabilities ZigbeeDeviceProfile::parseDescriptorCapabilities(quint8 descriptorCapabilitiesFlag) +{ + DescriptorCapabilities capabilities; + capabilities.descriptorCapabilitiesFlag = descriptorCapabilitiesFlag; + capabilities.extendedActiveEndpointListAvailable = ((descriptorCapabilitiesFlag >> 0) & 0x01); + capabilities.extendedSimpleDescriptorListAvailable = ((descriptorCapabilitiesFlag >> 1) & 0x01); + return capabilities; +} + +ZigbeeDeviceProfile::PowerDescriptor ZigbeeDeviceProfile::parsePowerDescriptor(quint16 powerDescriptorFlag) +{ + PowerDescriptor powerDescriptor; + + powerDescriptor.powerDescriptoFlag = powerDescriptorFlag; + + + // Bit 0 - 3 Power mode + // 0000: Receiver configured according to “Receiver on when idle” MAC flag in the Node Descriptor + // 0001: Receiver switched on periodically + // 0010: Receiver switched on when stimulated, e.g. by pressing a button + + powerDescriptor.powerMode = ZigbeeDeviceProfile::PowerModeAlwaysOn; + if (!ZigbeeUtils::checkBitUint16(powerDescriptorFlag, 0) && !ZigbeeUtils::checkBitUint16(powerDescriptorFlag, 1)) { + powerDescriptor.powerMode = ZigbeeDeviceProfile::PowerModeAlwaysOn; + } else if (ZigbeeUtils::checkBitUint16(powerDescriptorFlag, 0) && !ZigbeeUtils::checkBitUint16(powerDescriptorFlag, 1)) { + powerDescriptor.powerMode = ZigbeeDeviceProfile::PowerModeOnPeriodically; + } else if (!ZigbeeUtils::checkBitUint16(powerDescriptorFlag, 0) && ZigbeeUtils::checkBitUint16(powerDescriptorFlag, 1)) { + powerDescriptor.powerMode = ZigbeeDeviceProfile::PowerModeOnWhenStimulated; + } + + // Bit 4 - 7 Available power sources + // Bit 0: Permanent mains supply + // Bit 1: Rechargeable battery + // Bit 2: Disposable battery + // Bit 4: Reserved + if (ZigbeeUtils::checkBitUint16(powerDescriptorFlag, 4)) { + powerDescriptor.availablePowerSources.append(ZigbeeDeviceProfile::PowerSourcePermanentMainSupply); + } else if (ZigbeeUtils::checkBitUint16(powerDescriptorFlag, 5)) { + powerDescriptor.availablePowerSources.append(ZigbeeDeviceProfile::PowerSourceRecharchableBattery); + } else if (ZigbeeUtils::checkBitUint16(powerDescriptorFlag, 6)) { + powerDescriptor.availablePowerSources.append(ZigbeeDeviceProfile::PowerSourceDisposableBattery); + } + + // Bit 8 - 11 Active source: according to the same schema as available power sources + powerDescriptor.powerSource = ZigbeeDeviceProfile::PowerSourcePermanentMainSupply; + if (ZigbeeUtils::checkBitUint16(powerDescriptorFlag, 8)) { + powerDescriptor.powerSource = ZigbeeDeviceProfile::PowerSourcePermanentMainSupply; + } else if (ZigbeeUtils::checkBitUint16(powerDescriptorFlag, 9)) { + powerDescriptor.powerSource = ZigbeeDeviceProfile::PowerSourceRecharchableBattery; + } else if (ZigbeeUtils::checkBitUint16(powerDescriptorFlag, 10)) { + powerDescriptor.powerSource = ZigbeeDeviceProfile::PowerSourceDisposableBattery; + } + + // Bit 12 - 15: Battery level if available + // 0000: Critically low + // 0100: Approximately 33% + // 1000: Approximately 66% + // 1100: Approximately 100% (near fully charged) + if (!ZigbeeUtils::checkBitUint16(powerDescriptorFlag, 14) && !ZigbeeUtils::checkBitUint16(powerDescriptorFlag, 15)) { + powerDescriptor.powerLevel = ZigbeeDeviceProfile::PowerLevelCriticalLow; + } else if (ZigbeeUtils::checkBitUint16(powerDescriptorFlag, 14) && !ZigbeeUtils::checkBitUint16(powerDescriptorFlag, 15)) { + powerDescriptor.powerLevel = ZigbeeDeviceProfile::PowerLevelLow; + } else if (!ZigbeeUtils::checkBitUint16(powerDescriptorFlag, 14) && ZigbeeUtils::checkBitUint16(powerDescriptorFlag, 15)) { + powerDescriptor.powerLevel = ZigbeeDeviceProfile::PowerLevelOk; + } else if (ZigbeeUtils::checkBitUint16(powerDescriptorFlag, 14) && ZigbeeUtils::checkBitUint16(powerDescriptorFlag, 15)) { + powerDescriptor.powerLevel = ZigbeeDeviceProfile::PowerLevelFull; + } + + // qCDebug(dcZigbeeNode()) << "Node power descriptor (" << ZigbeeUtils::convertUint16ToHexString(m_powerDescriptorFlag) << "):"; + // qCDebug(dcZigbeeNode()) << " Power mode:" << m_powerMode; + // qCDebug(dcZigbeeNode()) << " Available power sources:"; + // foreach (const PowerSource &source, m_availablePowerSources) { + // qCDebug(dcZigbeeNode()) << " " << source; + // } + // qCDebug(dcZigbeeNode()) << " Power source:" << m_powerSource; + // qCDebug(dcZigbeeNode()) << " Power level:" << m_powerLevel; + return powerDescriptor; +} + ZigbeeDeviceProfile::Adpu ZigbeeDeviceProfile::parseAdpu(const QByteArray &adpu) { QDataStream stream(adpu); @@ -51,3 +203,62 @@ QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::Adpu &deviceAdpu) debug.nospace() << ZigbeeUtils::convertByteArrayToHexString(deviceAdpu.payload) << ")"; return debug.space(); } + +QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::NodeDescriptor &nodeDescriptor) +{ + debug.nospace() << "NodeDescriptor(" << nodeDescriptor.nodeType << ")" << endl; + debug.nospace() << " Complex descriptor available: " << nodeDescriptor.complexDescriptorAvailable << endl; + debug.nospace() << " User descriptor available: " << nodeDescriptor.userDescriptorAvailable << endl; + debug.nospace() << " " << nodeDescriptor.frequencyBand << endl; + debug.nospace() << " " << nodeDescriptor.macCapabilities; + debug.nospace() << " Manufacturer code: " << ZigbeeUtils::convertUint16ToHexString(nodeDescriptor.manufacturerCode) << "(" << nodeDescriptor.manufacturerCode << ")" << endl; + debug.nospace() << " Maximum buffer size: " << nodeDescriptor.maximumBufferSize << endl; + debug.nospace() << " Maximum RX size: " << nodeDescriptor.maximumRxSize << endl; + debug.nospace() << " Maximum TX size: " << nodeDescriptor.maximumTxSize << endl; + debug.nospace() << " " << nodeDescriptor.serverMask; + debug.nospace() << " " << nodeDescriptor.descriptorCapabilities; + return debug; +} + +QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::MacCapabilities &macCapabilities) +{ + debug.nospace() << "MacCapabilities(" << ZigbeeUtils::convertByteToHexString(macCapabilities.flag) << ")" << endl; + debug.nospace() << " Alternate PAN Coordinator: " << macCapabilities.alternatePanCoordinator << endl; + debug.nospace() << " " << macCapabilities.deviceType << endl; + debug.nospace() << " Power source main power: " << macCapabilities.powerSourceFlagMainPower << endl; + debug.nospace() << " Receiver on when idle: " << macCapabilities.receiverOnWhenIdle << endl; + debug.nospace() << " Security capability: " << macCapabilities.securityCapability << endl; + debug.nospace() << " Allocate address: " << macCapabilities.allocateAddress << endl; + return debug; +} + +QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::ServerMask &serverMask) +{ + debug.nospace() << "ServerMask(" << ZigbeeUtils::convertUint16ToHexString(serverMask.serverMaskFlag) << ")" << endl; + debug.nospace() << " Primary trust center: " << serverMask.primaryTrustCenter << endl; + debug.nospace() << " Backup trust center: " << serverMask.backupTrustCenter << endl; + debug.nospace() << " Primary binding cache: " << serverMask.primaryBindingCache << endl; + debug.nospace() << " Backup binding cache: " << serverMask.backupBindingCache << endl; + debug.nospace() << " Primary discovery cache: " << serverMask.primaryDiscoveryCache << endl; + debug.nospace() << " Backup discovery cache: " << serverMask.backupDiscoveryCache << endl; + debug.nospace() << " Network manager: " << serverMask.networkManager << endl; + return debug; +} + +QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::DescriptorCapabilities &descriptorCapabilities) +{ + debug.nospace() << "DescriptorCapabilities(" << ZigbeeUtils::convertByteToHexString(descriptorCapabilities.descriptorCapabilitiesFlag) << ")" << endl; + debug.nospace() << " Extended active endpoint list available: " << descriptorCapabilities.extendedActiveEndpointListAvailable << endl; + debug.nospace() << " Extended simple descriptor list available: " << descriptorCapabilities.extendedSimpleDescriptorListAvailable << endl; + return debug; +} + +QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::PowerDescriptor &powerDescriptor) +{ + debug.nospace() << "PowerDescriptor(" << ZigbeeUtils::convertByteToHexString(powerDescriptor.powerDescriptoFlag) << ")" << endl; + debug.nospace() << " Power mode: " << powerDescriptor.powerMode << endl; + debug.nospace() << " Available power sources: " << powerDescriptor.availablePowerSources << endl; + debug.nospace() << " Power source: " << powerDescriptor.powerSource << endl; + debug.nospace() << " Power level: " << powerDescriptor.powerLevel << endl; + return debug; +} diff --git a/libnymea-zigbee/zdo/zigbeedeviceprofile.h b/libnymea-zigbee/zdo/zigbeedeviceprofile.h index f643667..e1cedba 100644 --- a/libnymea-zigbee/zdo/zigbeedeviceprofile.h +++ b/libnymea-zigbee/zdo/zigbeedeviceprofile.h @@ -174,8 +174,121 @@ public: } Adpu; static ZigbeeDeviceProfile::Adpu parseAdpu(const QByteArray &adpu); + + // Node information + enum NodeType { + NodeTypeCoordinator = 0, + NodeTypeRouter = 1, + NodeTypeEndDevice = 2 + }; + Q_ENUM(NodeType) + + enum FrequencyBand { + FrequencyBand868Mhz, + FrequencyBand902Mhz, + FrequencyBand2400Mhz + }; + Q_ENUM(FrequencyBand) + + enum DeviceType { + DeviceTypeFullFunction, + DeviceTypeReducedFunction + }; + Q_ENUM(DeviceType) + + enum Relationship { + Parent, + Child, + Sibling + }; + Q_ENUM(Relationship) + + enum PowerMode { + PowerModeAlwaysOn, + PowerModeOnPeriodically, + PowerModeOnWhenStimulated + }; + Q_ENUM(PowerMode) + + enum PowerSource { + PowerSourcePermanentMainSupply, + PowerSourceRecharchableBattery, + PowerSourceDisposableBattery + }; + Q_ENUM(PowerSource) + + enum PowerLevel { + PowerLevelCriticalLow, + PowerLevelLow, + PowerLevelOk, + PowerLevelFull + }; + Q_ENUM(PowerLevel) + + typedef struct MacCapabilities { + quint8 flag = 0x00; // For saving + bool alternatePanCoordinator = false; + DeviceType deviceType = DeviceTypeReducedFunction; + bool powerSourceFlagMainPower = false; + bool receiverOnWhenIdle = false; + bool securityCapability = false; + bool allocateAddress = false; + } MacCapabilities; + + typedef struct DescriptorCapabilities { + quint8 descriptorCapabilitiesFlag = 0x00; // For saving + bool extendedActiveEndpointListAvailable = false; + bool extendedSimpleDescriptorListAvailable = false; + } DescriptorCapabilities; + + typedef struct ServerMask { + quint16 serverMaskFlag = 0x0000; // For saving + bool primaryTrustCenter = false; + bool backupTrustCenter = false; + bool primaryBindingCache = false; + bool backupBindingCache = false; + bool primaryDiscoveryCache = false; + bool backupDiscoveryCache = false; + bool networkManager = false; + quint8 stackComplianceVersion = 0; + } ServerMask; + + typedef struct NodeDescriptor { + QByteArray descriptorRawData; // For saving + NodeType nodeType = NodeTypeEndDevice; + bool complexDescriptorAvailable = false; + bool userDescriptorAvailable = false; + FrequencyBand frequencyBand = FrequencyBand2400Mhz; + MacCapabilities macCapabilities; + quint16 manufacturerCode = 0; + quint8 maximumBufferSize = 0; + quint16 maximumRxSize = 0; + ServerMask serverMask; + quint16 maximumTxSize = 0; + DescriptorCapabilities descriptorCapabilities; + } NodeDescriptor; + + typedef struct PowerDescriptor { + quint16 powerDescriptoFlag = 0x0000; + PowerMode powerMode = PowerModeAlwaysOn; + QList availablePowerSources; + PowerSource powerSource = PowerSourcePermanentMainSupply; + PowerLevel powerLevel = PowerLevelFull; + } PowerDescriptor; + + static NodeDescriptor parseNodeDescriptor(const QByteArray &payload); + static MacCapabilities parseMacCapabilities(quint8 macCapabilitiesFlag); + static ServerMask parseServerMask(quint16 serverMaskFlag); + static DescriptorCapabilities parseDescriptorCapabilities(quint8 descriptorCapabilitiesFlag); + + static PowerDescriptor parsePowerDescriptor(quint16 powerDescriptorFlag); }; QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::Adpu &deviceAdpu); +QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::NodeDescriptor &nodeDescriptor); +QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::MacCapabilities &macCapabilities); +QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::ServerMask &serverMask); +QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::DescriptorCapabilities &descriptorCapabilities); +QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::PowerDescriptor &powerDescriptor); #endif // ZIGBEEDEVICEPROFILE_H diff --git a/libnymea-zigbee/zigbeenetwork.cpp b/libnymea-zigbee/zigbeenetwork.cpp index 8378ccb..8d34c8d 100644 --- a/libnymea-zigbee/zigbeenetwork.cpp +++ b/libnymea-zigbee/zigbeenetwork.cpp @@ -28,6 +28,7 @@ #include "zigbeeutils.h" #include "zigbeenetwork.h" #include "loggingcategory.h" +#include "zdo/zigbeedeviceprofile.h" #include @@ -174,11 +175,6 @@ quint8 ZigbeeNetwork::generateSequenceNumber() return m_sequenceNumber++; } -quint8 ZigbeeNetwork::generateTranactionSequenceNumber() -{ - return m_transactionSequenceNumber++; -} - QList ZigbeeNetwork::nodes() const { return m_nodes; @@ -233,6 +229,28 @@ bool ZigbeeNetwork::hasNode(const ZigbeeAddress &address) const return getZigbeeNode(address) != nullptr; } +void ZigbeeNetwork::removeZigbeeNode(const ZigbeeAddress &address) +{ + + ZigbeeNode *node = getZigbeeNode(address); + if (!node) { + qCWarning(dcZigbeeNetwork()) << "Failed remove zigbee node since there is no node with" << address; + return; + } + + qCDebug(dcZigbeeNetwork()) << "Removing" << node << "from the newtork"; + ZigbeeDeviceObjectReply *zdoReply = node->deviceObject()->requestMgmtLeaveNetwork(); + connect(zdoReply, &ZigbeeDeviceObjectReply::finished, this, [this, zdoReply, node](){ + if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) { + qCWarning(dcZigbeeNode()) << "Failed to send management leave request to" << node << zdoReply->error(); + qCWarning(dcZigbeeNode()) << "This node is gonna be removed internally and tried to remove once it shows up the next time."; + } + + removeNode(node); + }); + +} + void ZigbeeNetwork::addNodeInternally(ZigbeeNode *node) { if (m_nodes.contains(node)) { @@ -272,7 +290,7 @@ ZigbeeNode *ZigbeeNetwork::createNode(quint16 shortAddress, const ZigbeeAddress ZigbeeNode *ZigbeeNetwork::createNode(quint16 shortAddress, const ZigbeeAddress &extendedAddress, quint8 macCapabilities, QObject *parent) { ZigbeeNode *node = createNode(shortAddress, extendedAddress, parent); - node->setMacCapabilitiesFlag(macCapabilities); + node->m_macCapabilities = ZigbeeDeviceProfile::parseMacCapabilities(macCapabilities); return node; } @@ -317,22 +335,9 @@ void ZigbeeNetwork::loadNetwork() settings.beginGroup(ieeeAddressString); quint16 shortAddress = static_cast(settings.value("nwkAddress", 0).toUInt()); ZigbeeNode *node = createNode(shortAddress, ZigbeeAddress(ieeeAddressString), this); - - // 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->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())); + node->m_nodeDescriptor = ZigbeeDeviceProfile::parseNodeDescriptor(settings.value("nodeDescriptorRaw").toByteArray()); + node->m_macCapabilities = node->nodeDescriptor().macCapabilities; + node->m_powerDescriptor = ZigbeeDeviceProfile::parsePowerDescriptor(static_cast(settings.value("powerDescriptorFlag", 0).toUInt())); int endpointsCount = settings.beginReadArray("endpoints"); //qCDebug(dcZigbeeNetwork()) << "loading endpoints" << endpointsCount << settings.childKeys() << settings.childGroups(); @@ -399,7 +404,7 @@ void ZigbeeNetwork::clearSettings() m_extendedPanId = 0; m_channel = 0; m_securityConfiguration.clear(); - m_nodeType = ZigbeeNode::NodeTypeCoordinator; + m_nodeType = ZigbeeDeviceProfile::NodeTypeCoordinator; } void ZigbeeNetwork::saveNode(ZigbeeNode *node) @@ -412,20 +417,10 @@ void ZigbeeNetwork::saveNode(ZigbeeNode *node) 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("nodeDescriptorRaw", node->nodeDescriptor().descriptorRawData); // Power descriptor - settings.setValue("powerDescriptorFlag", node->m_powerDescriptorFlag); + settings.setValue("powerDescriptorFlag", node->powerDescriptor().powerDescriptoFlag); settings.beginWriteArray("endpoints"); for (int i = 0; i < node->endpoints().count(); i++) { @@ -603,53 +598,25 @@ void ZigbeeNetwork::onNodeClusterAttributeChanged(ZigbeeCluster *cluster, const saveNode(node); } - QDebug operator<<(QDebug debug, ZigbeeNetwork *network) { debug.nospace().noquote() << "ZigbeeNetwork (" << ZigbeeUtils::convertUint64ToHexString(network->extendedPanId()) << ", Channel " << network->channel() << ")" << endl; foreach (ZigbeeNode *node, network->nodes()) { - debug.nospace().noquote() << " - " << node << endl; - debug.nospace().noquote() << " Node type:" << node->nodeType() << endl; - debug.nospace().noquote() << " Manufacturer code: " << ZigbeeUtils::convertUint16ToHexString(node->manufacturerCode()) << endl; - debug.nospace().noquote() << " Maximum Rx size: " << ZigbeeUtils::convertUint16ToHexString(node->maximumRxSize()) << endl; - debug.nospace().noquote() << " Maximum Tx size: " << ZigbeeUtils::convertUint16ToHexString(node->maximumTxSize()) << endl; - debug.nospace().noquote() << " Maximum buffer size: " << ZigbeeUtils::convertByteToHexString(node->maximumBufferSize()) << endl; - debug.nospace().noquote() << " Primary Trust center: " << node->isPrimaryTrustCenter() << endl; - debug.nospace().noquote() << " Backup Trust center: " << node->isBackupTrustCenter() << endl; - debug.nospace().noquote() << " Primary Binding cache: " << node->isPrimaryBindingCache() << endl; - debug.nospace().noquote() << " Backup Binding cache: " << node->isBackupBindingCache() << endl; - debug.nospace().noquote() << " Primary Discovery cache: " << node->isPrimaryDiscoveryCache() << endl; - debug.nospace().noquote() << " Backup Discovery cache: " << node->isBackupDiscoveryCache() << endl; - debug.nospace().noquote() << " Network Manager: " << node->isNetworkManager() << endl; - debug.nospace().noquote() << " Extended active endpoint list available: " << node->extendedActiveEndpointListAvailable() << endl; - debug.nospace().noquote() << " Extended simple descriptor list available: " << node->extendedSimpleDescriptorListAvailable() << endl; - debug.nospace().noquote() << " Alternate PAN coordinator: " << node->alternatePanCoordinator() << endl; - debug.nospace().noquote() << " Device type: " << node->deviceType() << endl; - debug.nospace().noquote() << " Power source flag main power: " << node->powerSourceFlagMainPower() << endl; - debug.nospace().noquote() << " Receiver on when idle: " << node->receiverOnWhenIdle() << endl; - debug.nospace().noquote() << " Security capability: " << node->securityCapability() << endl; - debug.nospace().noquote() << " Allocate address: " << node->allocateAddress() << endl; - debug.nospace().noquote() << " Complex desciptor available: " << node->complexDescriptorAvailable() << endl; - debug.nospace().noquote() << " User desciptor available: " << node->userDescriptorAvailable() << endl; - debug.nospace().noquote() << " Power mode: " << node->powerMode() << endl; - debug.nospace().noquote() << " Available power sources:" << endl; - foreach (const ZigbeeNode::PowerSource &source, node->availablePowerSources()) { - debug.nospace().noquote() << " - " << source << endl; - } - debug.nospace().noquote() << " Power source: " << node->powerSource() << endl; - debug.nospace().noquote() << " Power level: " << node->powerLevel() << endl; - debug.nospace().noquote() << " Endpoints: " << node->endpoints().count() << endl; + debug.nospace().noquote() << " ---> " << node << endl; + debug.nospace().noquote() << " " << node->nodeDescriptor(); + debug.nospace().noquote() << " " << node->powerDescriptor(); + debug.nospace().noquote() << " Endpoints: " << node->endpoints().count() << endl; foreach (ZigbeeNodeEndpoint *endpoint, node->endpoints()) { - debug.nospace().noquote() << " - " << endpoint << endl; - debug.nospace().noquote() << " Input clusters:" << endl; + debug.nospace().noquote() << " - " << endpoint << endl; + debug.nospace().noquote() << " Input clusters:" << endl; foreach (ZigbeeCluster *cluster, endpoint->inputClusters()) { - debug.nospace().noquote() << " - " << cluster << endl; + debug.nospace().noquote() << " - " << cluster << endl; } - debug.nospace().noquote() << " Output clusters:" << endl; + debug.nospace().noquote() << " Output clusters:" << endl; foreach (ZigbeeCluster *cluster, endpoint->outputClusters()) { - debug.nospace().noquote() << " - " << cluster << endl; + debug.nospace().noquote() << " - " << cluster << endl; } } } diff --git a/libnymea-zigbee/zigbeenetwork.h b/libnymea-zigbee/zigbeenetwork.h index 79396b3..e034b5a 100644 --- a/libnymea-zigbee/zigbeenetwork.h +++ b/libnymea-zigbee/zigbeenetwork.h @@ -98,7 +98,6 @@ public: void setPermitJoining(bool permitJoining); quint8 generateSequenceNumber(); - quint8 generateTranactionSequenceNumber(); // Network nodes QList nodes() const; @@ -113,6 +112,8 @@ public: virtual ZigbeeNetworkReply *sendRequest(const ZigbeeNetworkRequest &request) = 0; + void removeZigbeeNode(const ZigbeeAddress &address); + private: State m_state = StateUninitialized; @@ -120,15 +121,15 @@ private: QString m_serialPortName = "/dev/ttyUSB0"; qint32 m_serialBaudrate = 115200; + // Continuouse ASP sequence number for network requests quint8 m_sequenceNumber = 0; - quint8 m_transactionSequenceNumber = 0; // Network configurations quint16 m_panId = 0; quint64 m_extendedPanId = 0; quint32 m_channel = 0; ZigbeeChannelMask m_channelMask = ZigbeeChannelMask(ZigbeeChannelMask::ChannelConfigurationAllChannels); - ZigbeeNode::NodeType m_nodeType = ZigbeeNode::NodeTypeCoordinator; + ZigbeeDeviceProfile::NodeType m_nodeType = ZigbeeDeviceProfile::NodeTypeCoordinator; // Network storage QString m_settingsFileName = "/etc/nymea/nymea-zigbee.conf"; diff --git a/libnymea-zigbee/zigbeenode.cpp b/libnymea-zigbee/zigbeenode.cpp index 0fa7b3f..f948820 100644 --- a/libnymea-zigbee/zigbeenode.cpp +++ b/libnymea-zigbee/zigbeenode.cpp @@ -87,144 +87,19 @@ ZigbeeNodeEndpoint *ZigbeeNode::getEndpoint(quint8 endpointId) const return nullptr; } -ZigbeeNode::NodeType ZigbeeNode::nodeType() const +ZigbeeDeviceProfile::NodeDescriptor ZigbeeNode::nodeDescriptor() const { - return m_nodeType; + return m_nodeDescriptor; } -ZigbeeNode::FrequencyBand ZigbeeNode::frequencyBand() const +ZigbeeDeviceProfile::MacCapabilities ZigbeeNode::macCapabilities() const { - return m_frequencyBand; + return m_macCapabilities; } -ZigbeeNode::Relationship ZigbeeNode::relationship() const +ZigbeeDeviceProfile::PowerDescriptor ZigbeeNode::powerDescriptor() const { - return m_relationship; -} - -quint16 ZigbeeNode::manufacturerCode() const -{ - return m_manufacturerCode; -} - -bool ZigbeeNode::complexDescriptorAvailable() const -{ - return m_complexDescriptorAvailable; -} - -bool ZigbeeNode::userDescriptorAvailable() const -{ - return m_userDescriptorAvailable; -} - -quint16 ZigbeeNode::maximumRxSize() const -{ - return m_maximumRxSize; -} - -quint16 ZigbeeNode::maximumTxSize() const -{ - return m_maximumTxSize; -} - -quint8 ZigbeeNode::maximumBufferSize() const -{ - return m_maximumBufferSize; -} - -bool ZigbeeNode::isPrimaryTrustCenter() const -{ - return m_isPrimaryTrustCenter; -} - -bool ZigbeeNode::isBackupTrustCenter() const -{ - return m_isBackupTrustCenter; -} - -bool ZigbeeNode::isPrimaryBindingCache() const -{ - return m_isPrimaryBindingCache; -} - -bool ZigbeeNode::isBackupBindingCache() const -{ - return m_isBackupBindingCache; -} - -bool ZigbeeNode::isPrimaryDiscoveryCache() const -{ - return m_isPrimaryDiscoveryCache; -} - -bool ZigbeeNode::isBackupDiscoveryCache() const -{ - return m_isBackupDiscoveryCache; -} - -bool ZigbeeNode::isNetworkManager() const -{ - return m_isNetworkManager; -} - -bool ZigbeeNode::extendedActiveEndpointListAvailable() const -{ - return m_extendedActiveEndpointListAvailable; -} - -bool ZigbeeNode::extendedSimpleDescriptorListAvailable() const -{ - return m_extendedSimpleDescriptorListAvailable; -} - -bool ZigbeeNode::alternatePanCoordinator() const -{ - return m_alternatePanCoordinator; -} - -ZigbeeNode::DeviceType ZigbeeNode::deviceType() const -{ - return m_deviceType; -} - -bool ZigbeeNode::powerSourceFlagMainPower() const -{ - return m_powerSourceFlagMainPower; -} - -bool ZigbeeNode::receiverOnWhenIdle() const -{ - return m_receiverOnWhenIdle; -} - -bool ZigbeeNode::securityCapability() const -{ - return m_securityCapability; -} - -bool ZigbeeNode::allocateAddress() const -{ - return m_allocateAddress; -} - -ZigbeeNode::PowerMode ZigbeeNode::powerMode() const -{ - return m_powerMode; -} - -ZigbeeNode::PowerSource ZigbeeNode::powerSource() const -{ - return m_powerSource; -} - -QList ZigbeeNode::availablePowerSources() const -{ - return m_availablePowerSources; -} - -ZigbeeNode::PowerLevel ZigbeeNode::powerLevel() const -{ - return m_powerLevel; + return m_powerDescriptor; } void ZigbeeNode::setState(ZigbeeNode::State state) @@ -247,136 +122,6 @@ void ZigbeeNode::setConnected(bool connected) emit connectedChanged(m_connected); } -void ZigbeeNode::setShortAddress(const quint16 &shortAddress) -{ - m_shortAddress = shortAddress; -} - -void ZigbeeNode::setExtendedAddress(const ZigbeeAddress &extendedAddress) -{ - m_extendedAddress = extendedAddress; -} - -quint16 ZigbeeNode::serverMask() const -{ - return m_serverMask; -} - -void ZigbeeNode::setServerMask(quint16 serverMask) -{ - m_serverMask = serverMask; - m_isPrimaryTrustCenter = ((m_serverMask >> 0) & 0x0001); - m_isBackupTrustCenter = ((m_serverMask >> 1) & 0x0001); - m_isPrimaryBindingCache = ((m_serverMask >> 2) & 0x0001); - m_isBackupBindingCache = ((m_serverMask >> 3) & 0x0001); - m_isPrimaryDiscoveryCache = ((m_serverMask >> 4) & 0x0001); - m_isBackupDiscoveryCache = ((m_serverMask >> 5) & 0x0001); - m_isNetworkManager = ((m_serverMask >> 6) & 0x0001); -} - -quint8 ZigbeeNode::macCapabilitiesFlag() const -{ - return m_macCapabilitiesFlag; -} - -void ZigbeeNode::setMacCapabilitiesFlag(quint8 macFlag) -{ - m_macCapabilitiesFlag = macFlag; - m_alternatePanCoordinator = ((m_macCapabilitiesFlag >> 0) & 0x01); - if (((m_macCapabilitiesFlag >> 1) & 0x01)) { - m_deviceType = DeviceTypeFullFunction; - } else { - m_deviceType = DeviceTypeReducedFunction; - } - m_powerSourceFlagMainPower = ((m_macCapabilitiesFlag >> 2) & 0x01); - m_receiverOnWhenIdle = ((m_macCapabilitiesFlag >> 3) & 0x01); - m_securityCapability = ((m_macCapabilitiesFlag >> 6) & 0x01); - m_allocateAddress = ((m_macCapabilitiesFlag >> 7) & 0x01); -} - -void ZigbeeNode::setDescriptorFlag(quint8 descriptorFlag) -{ - m_descriptorFlag = descriptorFlag; - m_extendedActiveEndpointListAvailable = ((m_descriptorFlag >> 0) & 0x01); - m_extendedSimpleDescriptorListAvailable = ((m_descriptorFlag >> 1) & 0x01); -} - -quint16 ZigbeeNode::powerDescriptorFlag() const -{ - return m_powerDescriptorFlag; -} - -void ZigbeeNode::setPowerDescriptorFlag(quint16 powerDescriptorFlag) -{ - m_powerDescriptorFlag = powerDescriptorFlag; - - qCDebug(dcZigbeeNode()) << "Parse power descriptor flag" << ZigbeeUtils::convertUint16ToHexString(m_powerDescriptorFlag); - - // Bit 0 - 3 Power mode - // 0000: Receiver configured according to “Receiver on when idle” MAC flag in the Node Descriptor - // 0001: Receiver switched on periodically - // 0010: Receiver switched on when stimulated, e.g. by pressing a button - - m_powerMode = PowerModeAlwaysOn; - if (!ZigbeeUtils::checkBitUint16(m_powerDescriptorFlag, 0) && !ZigbeeUtils::checkBitUint16(m_powerDescriptorFlag, 1)) { - m_powerMode = PowerModeAlwaysOn; - } else if (ZigbeeUtils::checkBitUint16(m_powerDescriptorFlag, 0) && !ZigbeeUtils::checkBitUint16(m_powerDescriptorFlag, 1)) { - m_powerMode = PowerModeOnPeriodically; - } else if (!ZigbeeUtils::checkBitUint16(m_powerDescriptorFlag, 0) && ZigbeeUtils::checkBitUint16(m_powerDescriptorFlag, 1)) { - m_powerMode = PowerModeOnWhenStimulated; - } - - // Bit 4 - 7 Available power sources - // Bit 0: Permanent mains supply - // Bit 1: Rechargeable battery - // Bit 2: Disposable battery - // Bit 4: Reserved - - m_availablePowerSources.clear(); - if (ZigbeeUtils::checkBitUint16(m_powerDescriptorFlag, 4)) { - m_availablePowerSources.append(PowerSourcePermanentMainSupply); - } else if (ZigbeeUtils::checkBitUint16(m_powerDescriptorFlag, 5)) { - m_availablePowerSources.append(PowerSourceRecharchableBattery); - } else if (ZigbeeUtils::checkBitUint16(m_powerDescriptorFlag, 6)) { - m_availablePowerSources.append(PowerSourceDisposableBattery); - } - - // Bit 8 - 11 Active source: according to the same schema as available power sources - m_powerSource = PowerSourcePermanentMainSupply; - if (ZigbeeUtils::checkBitUint16(m_powerDescriptorFlag, 8)) { - m_powerSource = PowerSourcePermanentMainSupply; - } else if (ZigbeeUtils::checkBitUint16(m_powerDescriptorFlag, 9)) { - m_powerSource = PowerSourceRecharchableBattery; - } else if (ZigbeeUtils::checkBitUint16(m_powerDescriptorFlag, 10)) { - m_powerSource = PowerSourceDisposableBattery; - } - - // Bit 12 - 15: Battery level if available - // 0000: Critically low - // 0100: Approximately 33% - // 1000: Approximately 66% - // 1100: Approximately 100% (near fully charged) - m_powerLevel = PowerLevelCriticalLow; - if (!ZigbeeUtils::checkBitUint16(m_powerDescriptorFlag, 14) && !ZigbeeUtils::checkBitUint16(m_powerDescriptorFlag, 15)) { - m_powerLevel = PowerLevelCriticalLow; - } else if (ZigbeeUtils::checkBitUint16(m_powerDescriptorFlag, 14) && !ZigbeeUtils::checkBitUint16(m_powerDescriptorFlag, 15)) { - m_powerLevel = PowerLevelLow; - } else if (!ZigbeeUtils::checkBitUint16(m_powerDescriptorFlag, 14) && ZigbeeUtils::checkBitUint16(m_powerDescriptorFlag, 15)) { - m_powerLevel = PowerLevelOk; - } else if (ZigbeeUtils::checkBitUint16(m_powerDescriptorFlag, 14) && ZigbeeUtils::checkBitUint16(m_powerDescriptorFlag, 15)) { - m_powerLevel = PowerLevelFull; - } - - // qCDebug(dcZigbeeNode()) << "Node power descriptor (" << ZigbeeUtils::convertUint16ToHexString(m_powerDescriptorFlag) << "):"; - // qCDebug(dcZigbeeNode()) << " Power mode:" << m_powerMode; - // qCDebug(dcZigbeeNode()) << " Available power sources:"; - // foreach (const PowerSource &source, m_availablePowerSources) { - // qCDebug(dcZigbeeNode()) << " " << source; - // } - // qCDebug(dcZigbeeNode()) << " Power source:" << m_powerSource; - // qCDebug(dcZigbeeNode()) << " Power level:" << m_powerLevel; -} - void ZigbeeNode::startInitialization() { setState(StateInitializing); @@ -421,72 +166,10 @@ void ZigbeeNode::initNodeDescriptor() } qCDebug(dcZigbeeNode()) << this << "reading node descriptor finished successfully."; + m_nodeDescriptor = ZigbeeDeviceProfile::parseNodeDescriptor(reply->responseAdpu().payload); + qCDebug(dcZigbeeNode()) << m_nodeDescriptor; m_requestRetry = 0; - // Parse and set the node descriptor FIXME: make it nicer using the data types - QDataStream stream(reply->responseAdpu().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(); - - // Continue with the power descriptor initPowerDescriptor(); }); @@ -524,7 +207,8 @@ void ZigbeeNode::initPowerDescriptor() stream.setByteOrder(QDataStream::LittleEndian); quint16 powerDescriptorFlag = 0; stream >> powerDescriptorFlag; - setPowerDescriptorFlag(powerDescriptorFlag); + m_powerDescriptor = ZigbeeDeviceProfile::parsePowerDescriptor(powerDescriptorFlag); + qCDebug(dcZigbeeNode()) << m_powerDescriptor; // Continue with endpoint fetching initEndpoints(); diff --git a/libnymea-zigbee/zigbeenode.h b/libnymea-zigbee/zigbeenode.h index 88a91ef..f70ddb1 100644 --- a/libnymea-zigbee/zigbeenode.h +++ b/libnymea-zigbee/zigbeenode.h @@ -34,6 +34,7 @@ #include "zigbeeaddress.h" #include "zigbeenodeendpoint.h" #include "zdo/zigbeedeviceobject.h" +#include "zdo/zigbeedeviceprofile.h" class ZigbeeNetwork; @@ -51,94 +52,6 @@ public: }; Q_ENUM(State) - enum NodeType { - NodeTypeCoordinator = 0, - NodeTypeRouter = 1, - NodeTypeEndDevice = 2 - }; - Q_ENUM(NodeType) - - enum FrequencyBand { - FrequencyBand868Mhz, - FrequencyBand902Mhz, - FrequencyBand2400Mhz - }; - Q_ENUM(FrequencyBand) - - enum DeviceType { - DeviceTypeFullFunction, - DeviceTypeReducedFunction - }; - Q_ENUM(DeviceType) - - enum Relationship { - Parent, - Child, - Sibling - }; - Q_ENUM(Relationship) - - enum PowerMode { - PowerModeAlwaysOn, - PowerModeOnPeriodically, - PowerModeOnWhenStimulated - }; - Q_ENUM(PowerMode) - - enum PowerSource { - PowerSourcePermanentMainSupply, - PowerSourceRecharchableBattery, - PowerSourceDisposableBattery - }; - Q_ENUM(PowerSource) - - enum PowerLevel { - PowerLevelCriticalLow, - PowerLevelLow, - PowerLevelOk, - PowerLevelFull - }; - Q_ENUM(PowerLevel) - - typedef struct MacCapabilities { - bool alternatePanCoordinator = false; - DeviceType deviceType = DeviceTypeReducedFunction; - bool powerSourceFlagMainPower = false; - bool receiverOnWhenIdle = false; - bool securityCapability = false; - bool allocateAddress = false; - } MacCapabilities; - - typedef struct DescriptorCapabilities { - bool extendedActiveEndpointListAvailable = false; - bool extendedSimpleDescriptorListAvailable = false; - } DescriptorCapabilities; - - typedef struct ServerMask { - bool primaryTrustCenter = false; - bool backupTrustCenter = false; - bool primaryBindingCache = false; - bool backupBindingCache = false; - bool primaryDiscoveryCache = false; - bool backupDiscoveryCache = false; - bool networkManager = false; - quint8 stackComplianceVersion = 0; - } ServerMask; - - typedef struct NodeDescriptor { - NodeType nodeType = NodeTypeEndDevice; - bool complexDescriptorAvailable = false; - bool userDescriptorAvailable = false; - FrequencyBand frequencyBand = FrequencyBand2400Mhz; - MacCapabilities macCapabilities; - quint16 manufacturerCode = 0; - quint8 maximumBufferSize = 0; - quint16 maximumRxSize = 0; - ServerMask serverMask; - quint16 maximumTxSize = 0; - DescriptorCapabilities descriptorCapabilities; - } NodeDescriptor; - State state() const; bool connected() const; @@ -151,45 +64,16 @@ public: bool hasEndpoint(quint8 endpointId) const; ZigbeeNodeEndpoint *getEndpoint(quint8 endpointId) const; - // Information from node descriptor - NodeType nodeType() const; - FrequencyBand frequencyBand() const; - Relationship relationship() const; - quint16 manufacturerCode() const; - - bool complexDescriptorAvailable() const; - bool userDescriptorAvailable() const; - - quint16 maximumRxSize() const; - quint16 maximumTxSize() const; - quint8 maximumBufferSize() const; - - // Server Mask - bool isPrimaryTrustCenter() const; - bool isBackupTrustCenter() const; - bool isPrimaryBindingCache() const; - bool isBackupBindingCache() const; - bool isPrimaryDiscoveryCache() const; - bool isBackupDiscoveryCache() const; - bool isNetworkManager() const; - - // Descriptor capability - bool extendedActiveEndpointListAvailable() const; - bool extendedSimpleDescriptorListAvailable() const; - - // Mac capabilities flag - bool alternatePanCoordinator() const; - DeviceType deviceType() const; - bool powerSourceFlagMainPower() const; - bool receiverOnWhenIdle() const; - bool securityCapability() const; - bool allocateAddress() const; + // Information from descriptors + ZigbeeDeviceProfile::NodeDescriptor nodeDescriptor() const; + ZigbeeDeviceProfile::MacCapabilities macCapabilities() const; + ZigbeeDeviceProfile::PowerDescriptor powerDescriptor() const; // Information from node power descriptor - PowerMode powerMode() const; - PowerSource powerSource() const; - QList availablePowerSources() const; - PowerLevel powerLevel() const; + ZigbeeDeviceProfile::PowerMode powerMode() const; + ZigbeeDeviceProfile::PowerSource powerSource() const; + QList availablePowerSources() const; + ZigbeeDeviceProfile::PowerLevel powerLevel() const; // This method starts the node initialization phase (read descriptors and endpoints) void startInitialization(); @@ -198,86 +82,26 @@ private: ZigbeeNode(ZigbeeNetwork *network, quint16 shortAddress, const ZigbeeAddress &extendedAddress, QObject *parent = nullptr); ZigbeeNetwork *m_network; + quint16 m_shortAddress = 0; + ZigbeeAddress m_extendedAddress; + ZigbeeDeviceObject *m_deviceObject = nullptr; QList m_endpoints; + bool m_connected = false; + State m_state = StateUninitialized; + quint8 m_lqi = 0; - // Node descriptor information - QByteArray m_nodeDescriptorRawData; - NodeType m_nodeType = NodeTypeRouter; - FrequencyBand m_frequencyBand = FrequencyBand2400Mhz; - Relationship m_relationship = Parent; - quint16 m_manufacturerCode = 0; - - bool m_complexDescriptorAvailable = false; - bool m_userDescriptorAvailable = false; - - quint16 m_maximumRxSize = 0; - quint16 m_maximumTxSize = 0; - quint8 m_maximumBufferSize = 0; + // Node information + ZigbeeDeviceProfile::NodeDescriptor m_nodeDescriptor; + ZigbeeDeviceProfile::MacCapabilities m_macCapabilities; + ZigbeeDeviceProfile::PowerDescriptor m_powerDescriptor; void setState(State state); void setConnected(bool connected); - void setShortAddress(const quint16 &shortAddress); - void setExtendedAddress(const ZigbeeAddress &extendedAddress); - - quint16 serverMask() const; - void setServerMask(quint16 serverMask); - - // MAC capability raw data flag for settings - quint8 macCapabilitiesFlag() const; - void setMacCapabilitiesFlag(quint8 macFlag); - - quint8 descriptorFlag() const; - void setDescriptorFlag(quint8 descriptorFlag); - - // Power decriptor data - quint16 powerDescriptorFlag() const; - void setPowerDescriptorFlag(quint16 powerDescriptorFlag); - - bool m_connected = false; - State m_state = StateUninitialized; - - quint16 m_shortAddress = 0; - ZigbeeAddress m_extendedAddress; - - // Server Mask - quint16 m_serverMask = 0; - bool m_isPrimaryTrustCenter = false; - bool m_isBackupTrustCenter = false; - bool m_isPrimaryBindingCache = false; - bool m_isBackupBindingCache = false; - bool m_isPrimaryDiscoveryCache = false; - bool m_isBackupDiscoveryCache = false; - bool m_isNetworkManager = false; - - // Power information - quint16 m_powerDescriptorFlag = 0; - PowerMode m_powerMode; - PowerSource m_powerSource; - QList m_availablePowerSources; - PowerLevel m_powerLevel; - - // Mac capabilities flag - quint8 m_macCapabilitiesFlag = 0; - bool m_alternatePanCoordinator = false; - DeviceType m_deviceType = DeviceTypeFullFunction; - bool m_powerSourceFlagMainPower = false; - bool m_receiverOnWhenIdle = false; - bool m_securityCapability = false; - bool m_allocateAddress = false; - - // Descriptor capability - quint8 m_descriptorFlag = 0; - bool m_extendedActiveEndpointListAvailable = false; - bool m_extendedSimpleDescriptorListAvailable = false; - - //virtual void setClusterAttributeReport(const ZigbeeClusterAttributeReport &report) = 0; - // Init methods int m_requestRetry = 0; QList m_uninitializedEndpoints; - void initNodeDescriptor(); void initPowerDescriptor(); void initEndpoints(); @@ -291,6 +115,7 @@ private: signals: void stateChanged(State state); + void lqiChanged(quint8 lqi); void connectedChanged(bool connected); void clusterAdded(ZigbeeCluster *cluster); void clusterAttributeChanged(ZigbeeCluster *cluster, const ZigbeeClusterAttribute &attribute); diff --git a/libnymea-zigbee/zigbeenodeendpoint.cpp b/libnymea-zigbee/zigbeenodeendpoint.cpp index 348d41d..38785b6 100644 --- a/libnymea-zigbee/zigbeenodeendpoint.cpp +++ b/libnymea-zigbee/zigbeenodeendpoint.cpp @@ -176,7 +176,11 @@ ZigbeeCluster *ZigbeeNodeEndpoint::createCluster(Zigbee::ClusterId clusterId, Zi case Zigbee::ClusterIdIdentify: return new ZigbeeClusterIdentify(m_network, m_node, this, direction, this); break; - // Measurement + case Zigbee::ClusterIdLevelControl: + return new ZigbeeClusterLevelControl(m_network, m_node, this, direction, this); + break; + + // Measurement case Zigbee::ClusterIdTemperatureMeasurement: return new ZigbeeClusterTemperatureMeasurement(m_network, m_node, this, direction, this); break; @@ -189,7 +193,8 @@ ZigbeeCluster *ZigbeeNodeEndpoint::createCluster(Zigbee::ClusterId clusterId, Zi case Zigbee::ClusterIdIlluminanceMeasurement: return new ZigbeeClusterIlluminanceMeasurment(m_network, m_node, this, direction, this); break; - // Security + + // Security case Zigbee::ClusterIdIasZone: return new ZigbeeClusterIasZone(m_network, m_node, this, direction, this); break; diff --git a/libnymea-zigbee/zigbeenodeendpoint.h b/libnymea-zigbee/zigbeenodeendpoint.h index 2434770..74ea2df 100644 --- a/libnymea-zigbee/zigbeenodeendpoint.h +++ b/libnymea-zigbee/zigbeenodeendpoint.h @@ -40,6 +40,7 @@ #include "zcl/general/zigbeeclusterbasic.h" #include "zcl/general/zigbeeclusteronoff.h" #include "zcl/general/zigbeeclusteridentify.h" +#include "zcl/general/zigbeeclusterlevelcontrol.h" #include "zcl/measurement/zigbeeclusteroccupancysensing.h" #include "zcl/measurement/zigbeeclusterilluminancemeasurment.h"