diff --git a/README.md b/README.md index fd9af9a..093553e 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,13 @@ # nymea-zigbee +---------------------- This repository contains the nymea-zigbee library and tools. # Supported hardware -## NXP +Currently the only supported hardware is from NXP, but the tool will be designed to support also other hardware modules. +## NXP * JN5168 (SoM) * JN5169 (USB Stick) diff --git a/libnymea-zigbee/zigbeenetworkmanager.cpp b/libnymea-zigbee/zigbeenetworkmanager.cpp index bb2c698..0a70171 100644 --- a/libnymea-zigbee/zigbeenetworkmanager.cpp +++ b/libnymea-zigbee/zigbeenetworkmanager.cpp @@ -38,7 +38,19 @@ void ZigbeeNetworkManager::setPermitJoining(bool permitJoining) return; ZigbeeInterfaceReply *reply = m_controller->commandPermitJoin(0, (permitJoining ? 255 : 0)); - connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onCommandPermitJoiningFinished); + connect(reply, &ZigbeeInterfaceReply::finished, this, [this, reply, permitJoining](){ + reply->deleteLater(); + + if (reply->status() != ZigbeeInterfaceReply::Success) { + qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); + return; + } + + qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully." << permitJoining; + + // Read the permit joining status back in order to update the state + readPermitJoinStatus(); + }); } void ZigbeeNetworkManager::setStartingState(ZigbeeNetworkManager::StartingState state) @@ -67,8 +79,7 @@ void ZigbeeNetworkManager::setStartingState(ZigbeeNetworkManager::StartingState } case StartingStateGetVersion: { qCDebug(dcZigbeeNetwork()) << "Starting state changed: Get controller version"; - ZigbeeInterfaceReply *reply = m_controller->commandGetVersion(); - connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onCommandGetVersionFinished); + readControllerVersion(); break; } case StartingStateSetPanId: { @@ -115,8 +126,7 @@ void ZigbeeNetworkManager::setStartingState(ZigbeeNetworkManager::StartingState } case StartingStateGetPermitJoinStatus: { qCDebug(dcZigbeeNetwork()) << "Starting state changed: Get permit join status"; - ZigbeeInterfaceReply *reply = m_controller->commandGetPermitJoinStatus(); - connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onCommandGetPermitJoiningStatusFinished); + readPermitJoinStatus(); break; } case StartingStateReadeNodeDescriptor: { @@ -140,6 +150,57 @@ void ZigbeeNetworkManager::setStartingState(ZigbeeNetworkManager::StartingState } } +void ZigbeeNetworkManager::readControllerVersion() +{ + ZigbeeInterfaceReply *reply = m_controller->commandGetVersion(); + connect(reply, &ZigbeeInterfaceReply::finished, this, [this, reply](){ + reply->deleteLater(); + + if (reply->status() != ZigbeeInterfaceReply::Success) { + qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); + return; + } + + if (reply->additionalMessage().data().count() != 4) { + qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << ":" << "Invalid payload size"; + return; + } + + qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; + + // Parse version + quint16 majorVersion = ZigbeeUtils::convertByteArrayToUint16(reply->additionalMessage().data().mid(0, 2)); + quint16 minorVersion = ZigbeeUtils::convertByteArrayToUint16(reply->additionalMessage().data().mid(2, 2)); + + m_controllerFirmwareVersion = QString("%1.%2").arg(majorVersion).arg(minorVersion); + qCDebug(dcZigbeeNetwork()) << "Controller version:" << m_controllerFirmwareVersion; + + if (m_startingState == StartingStateGetVersion) setStartingState(StartingStateSetPanId); + + }); +} + +void ZigbeeNetworkManager::readPermitJoinStatus() +{ + ZigbeeInterfaceReply *reply = m_controller->commandGetPermitJoinStatus(); + connect(reply, &ZigbeeInterfaceReply::finished, this, [this, reply](){ + reply->deleteLater(); + + if (reply->status() != ZigbeeInterfaceReply::Success) { + qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); + return; + } + + qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; + qCDebug(dcZigbeeController()) << reply->additionalMessage(); + + m_permitJoining = static_cast(reply->additionalMessage().data().at(0)); + emit permitJoiningChanged(m_permitJoining); + + if (m_startingState == StartingStateGetPermitJoinStatus) setStartingState(StartingStateReadeNodeDescriptor); + }); +} + //void ZigbeeNetworkManager::requestMatchDescriptor(const quint16 &shortAddress, const Zigbee::ZigbeeProfile &profile) //{ @@ -209,33 +270,6 @@ void ZigbeeNetworkManager::onCommandErasePersistentDataFinished() } } -void ZigbeeNetworkManager::onCommandGetVersionFinished() -{ - ZigbeeInterfaceReply *reply = static_cast(sender()); - reply->deleteLater(); - - if (reply->status() != ZigbeeInterfaceReply::Success) { - qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); - return; - } - - if (reply->additionalMessage().data().count() != 4) { - qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << ":" << "Invalid payload size"; - return; - } - - qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; - - // Parse version - quint16 majorVersion = ZigbeeUtils::convertByteArrayToUint16(reply->additionalMessage().data().mid(0, 2)); - quint16 minorVersion = ZigbeeUtils::convertByteArrayToUint16(reply->additionalMessage().data().mid(2, 2)); - - m_controllerFirmwareVersion = QString("%1.%2").arg(majorVersion).arg(minorVersion); - qCDebug(dcZigbeeNetwork()) << "Controller version:" << m_controllerFirmwareVersion; - - if (m_startingState == StartingStateGetVersion) setStartingState(StartingStateSetPanId); -} - void ZigbeeNetworkManager::onCommandSetExtendedPanIdFinished() { ZigbeeInterfaceReply *reply = static_cast(sender()); @@ -309,42 +343,6 @@ void ZigbeeNetworkManager::onCommandStartScanFinished() processNetworkFormed(reply->additionalMessage()); } -void ZigbeeNetworkManager::onCommandGetPermitJoiningStatusFinished() -{ - ZigbeeInterfaceReply *reply = static_cast(sender()); - reply->deleteLater(); - - if (reply->status() != ZigbeeInterfaceReply::Success) { - qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); - return; - } - - qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; - qCDebug(dcZigbeeController()) << reply->additionalMessage(); - - m_permitJoining = static_cast(reply->additionalMessage().data().at(0)); - emit permitJoiningChanged(m_permitJoining); - - if (m_startingState == StartingStateGetPermitJoinStatus) setStartingState(StartingStateReadeNodeDescriptor); -} - -void ZigbeeNetworkManager::onCommandPermitJoiningFinished() -{ - ZigbeeInterfaceReply *reply = static_cast(sender()); - reply->deleteLater(); - - if (reply->status() != ZigbeeInterfaceReply::Success) { - qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); - return; - } - - qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; - - // Read the permit joining status back in order to update the state - ZigbeeInterfaceReply *getJoiningReply = m_controller->commandGetPermitJoinStatus(); - connect(getJoiningReply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onCommandGetPermitJoiningStatusFinished); -} - void ZigbeeNetworkManager::onCommandRequestMatchDescriptorFinished() { ZigbeeInterfaceReply *reply = static_cast(sender()); @@ -473,10 +471,10 @@ void ZigbeeNetworkManager::processNetworkFormed(const ZigbeeInterfaceMessage &me quint8 channel = static_cast(data.at(11)); qCDebug(dcZigbeeNetwork()).noquote() << "Network" << networkStatusString; + qCDebug(dcZigbeeNetwork()) << " Extended PAN ID:" << extendedPanId(); qCDebug(dcZigbeeNetwork()) << " Address:" << ZigbeeUtils::convertUint16ToHexString(shortAddress); qCDebug(dcZigbeeNetwork()) << " Extended address:" << ZigbeeAddress(extendedAddress); qCDebug(dcZigbeeNetwork()) << " Channel:" << channel; - qCDebug(dcZigbeeNetwork()) << " Extended PAN ID:" << extendedPanId(); qCDebug(dcZigbeeNetwork()) << " Permit joining:" << permitJoining(); m_networkRunning = true; @@ -829,6 +827,7 @@ void ZigbeeNetworkManager::onCommandPowerDescriptorRequestFinished() if (m_startingState == StartingStateReadPowerDescriptor) { setStartingState(StartingStateNone); setState(StateRunning); + readControllerVersion(); foreach (ZigbeeNode *node, nodes()) { node->setConnected(true); } @@ -1091,16 +1090,41 @@ void ZigbeeNetworkManager::processDeviceAnnounce(const ZigbeeInterfaceMessage &m void ZigbeeNetworkManager::processAttributeReport(const ZigbeeInterfaceMessage &message) { - quint8 sequenceNumber = static_cast(message.data().at(0)); - quint16 sourceAddress = ZigbeeUtils::convertByteArrayToUint16(message.data().mid(1, 2)); - quint8 endPoint = static_cast(message.data().at(3)); - quint16 clusterId = ZigbeeUtils::convertByteArrayToUint16(message.data().mid(4, 2)); - quint16 attributeId = ZigbeeUtils::convertByteArrayToUint16(message.data().mid(6, 2)); - quint8 attributDataType = static_cast(message.data().at(8)); + QByteArray data = message.data(); + quint8 sequenceNumber = 0; + quint16 sourceAddress = 0; + quint8 endPoint = 0; + quint16 clusterId = 0; + quint16 attributeId = 0; + quint8 attributeStatus = 0; + quint8 attributDataType = 0; + quint16 dataSize = 0; + + QDataStream stream(&data, QIODevice::ReadOnly); + stream >> sequenceNumber >> sourceAddress >> endPoint >> clusterId >> attributeId >> attributeStatus >> attributDataType >> dataSize; - quint16 attributeSize = ZigbeeUtils::convertByteArrayToUint16(message.data().mid(9, 2)); - QByteArray data = message.data().mid(11); Zigbee::DataType dataType = static_cast(attributDataType); + QByteArray attributeData = data.left(dataSize); + + if (attributeData.length() != dataSize) { + qCCritical(dcZigbeeNetwork()) << "HACK"; + // Note: the NXP firmware for JN5169 has a bug here and does not send the attributeStatus. + // Repars data without attribute status + sequenceNumber = 0; + sourceAddress = 0; + endPoint = 0; + clusterId = 0; + attributeId = 0; + attributeStatus = 0; + attributDataType = 0; + dataSize = 0; + + QDataStream alternativeStream(&data, QIODevice::ReadOnly); + alternativeStream >> sequenceNumber >> sourceAddress >> endPoint >> clusterId >> attributeId >> attributDataType >> dataSize; + + dataType = static_cast(attributDataType); + attributeData = data.left(dataSize); + } qCDebug(dcZigbeeNetwork()) << "Attribute report:"; qCDebug(dcZigbeeNetwork()) << " SQN:" << ZigbeeUtils::convertByteToHexString(sequenceNumber); @@ -1109,7 +1133,7 @@ void ZigbeeNetworkManager::processAttributeReport(const ZigbeeInterfaceMessage & qCDebug(dcZigbeeNetwork()) << " Cluster:" << ZigbeeUtils::clusterIdToString(static_cast(clusterId)); qCDebug(dcZigbeeNetwork()) << " Attribut id:" << ZigbeeUtils::convertUint16ToHexString(attributeId); qCDebug(dcZigbeeNetwork()) << " Attribut data type:" << dataType; - qCDebug(dcZigbeeNetwork()) << " Attribut size:" << attributeSize; + qCDebug(dcZigbeeNetwork()) << " Attribut size:" << dataSize; qCDebug(dcZigbeeNetwork()) << " Data:" << ZigbeeUtils::convertByteArrayToHexString(data); switch (dataType) { diff --git a/libnymea-zigbee/zigbeenetworkmanager.h b/libnymea-zigbee/zigbeenetworkmanager.h index 83d4de4..feddf74 100644 --- a/libnymea-zigbee/zigbeenetworkmanager.h +++ b/libnymea-zigbee/zigbeenetworkmanager.h @@ -54,6 +54,9 @@ private: bool m_networkRunning = false; bool m_factoryResetting = false; + void readControllerVersion(); + void readPermitJoinStatus(); + signals: void permitJoiningChanged(bool permitJoining); @@ -65,14 +68,11 @@ private slots: void onCommandResetControllerFinished(); void onCommandSoftResetControllerFinished(); void onCommandErasePersistentDataFinished(); - void onCommandGetVersionFinished(); void onCommandSetExtendedPanIdFinished(); void onCommandSetChannelMaskFinished(); void onCommandSetNodeTypeFinished(); void onCommandStartNetworkFinished(); void onCommandStartScanFinished(); - void onCommandGetPermitJoiningStatusFinished(); - void onCommandPermitJoiningFinished(); void onCommandEnableWhitelistFinished(); void onCommandNodeDescriptorRequestFinished();