diff --git a/libnymea-zigbee/backends/nxp/interface/nxp.h b/libnymea-zigbee/backends/nxp/interface/nxp.h index 4fbe95d..9869aa1 100644 --- a/libnymea-zigbee/backends/nxp/interface/nxp.h +++ b/libnymea-zigbee/backends/nxp/interface/nxp.h @@ -68,7 +68,8 @@ public: StatusProtocolError = 0x01, StatusUnknownCommand = 0x02, StatusInvalidCrc = 0x03, - StatusStackError = 0x04 + StatusStackError = 0x04, + StatusTimeout = 0xff }; Q_ENUM(Status) diff --git a/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacenxpreply.cpp b/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacenxpreply.cpp index 043d77b..5898d9f 100644 --- a/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacenxpreply.cpp +++ b/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacenxpreply.cpp @@ -98,6 +98,8 @@ void ZigbeeInterfaceNxpReply::setFinished() void ZigbeeInterfaceNxpReply::onTimeout() { m_timeout = true; + m_status = Nxp::StatusTimeout; + emit timeout(); emit finished(); } diff --git a/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacenxpreply.h b/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacenxpreply.h index 4864520..5cf4ff3 100644 --- a/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacenxpreply.h +++ b/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacenxpreply.h @@ -71,7 +71,7 @@ private: QByteArray m_requestData; // Response content - Nxp::Status m_status = Nxp::StatusUnknownCommand; // FIXME + Nxp::Status m_status = Nxp::StatusUnknownCommand; QByteArray m_responseData; void setFinished(); diff --git a/libnymea-zigbee/backends/nxp/zigbeenetworknxp.cpp b/libnymea-zigbee/backends/nxp/zigbeenetworknxp.cpp index 5362f1a..2cf43cb 100644 --- a/libnymea-zigbee/backends/nxp/zigbeenetworknxp.cpp +++ b/libnymea-zigbee/backends/nxp/zigbeenetworknxp.cpp @@ -94,6 +94,7 @@ ZigbeeNetworkReply *ZigbeeNetworkNxp::sendRequest(const ZigbeeNetworkRequest &re // Enqueu reply and send next one if we have enouth capacity m_replyQueue.enqueue(reply); + qCDebug(dcZigbeeNetwork()) << "=== Pending replies count (enqueued)" << m_replyQueue.count(); sendNextReply(); return reply; @@ -181,6 +182,8 @@ void ZigbeeNetworkNxp::sendNextReply() ZigbeeNetworkReply *reply = m_replyQueue.dequeue(); + qCDebug(dcZigbeeNetwork()) << "=== Pending replies count (dequeued)" << m_replyQueue.count(); + ZigbeeInterfaceNxpReply *interfaceReply = m_controller->requestSendRequest(reply->request()); connect(interfaceReply, &ZigbeeInterfaceNxpReply::finished, reply, [this, reply, interfaceReply](){ if (interfaceReply->status() != Nxp::StatusSuccess) { @@ -246,7 +249,6 @@ ZigbeeNetworkReply *ZigbeeNetworkNxp::requestSetPermitJoin(quint16 shortAddress, bool ZigbeeNetworkNxp::processVersionReply(ZigbeeInterfaceNxpReply *reply) { qCDebug(dcZigbeeNetwork()) << "Version reply finished" << reply->status(); - if (reply->timendOut()) { m_reconnectCounter++; if (m_reconnectCounter >= 3) { @@ -271,7 +273,9 @@ bool ZigbeeNetworkNxp::processVersionReply(ZigbeeInterfaceNxpReply *reply) qCWarning(dcZigbeeNetwork()) << "Failed to read firmware version. Retry" << m_reconnectCounter << "/ 3"; ZigbeeInterfaceNxpReply *reply = m_controller->requestVersion(); connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [this, reply](){ - processVersionReply(reply); + if (processVersionReply(reply)) { + m_controller->refreshControllerState(); + } }); return false; @@ -436,22 +440,25 @@ void ZigbeeNetworkNxp::onControllerStateChanged(ZigbeeBridgeControllerNxp::Contr connect(coordinatorNode, &ZigbeeNode::stateChanged, this, [this, coordinatorNode](ZigbeeNode::State state){ if (state == ZigbeeNode::StateInitialized) { qCDebug(dcZigbeeNetwork()) << "Coordinator initialized successfully." << coordinatorNode; - // ZigbeeClusterGroups *groupsCluster = coordinatorNode->getEndpoint(0x01)->inputCluster(ZigbeeClusterLibrary::ClusterIdGroups); - // if (!groupsCluster) { - // qCWarning(dcZigbeeNetwork()) << "Failed to get groups cluster from coordinator. The coordinator will not be in default group 0x0000"; - // setState(StateRunning); - // setPermitJoining(0); - // return; - // } + /* Note: this currently has been hardcoded into the firmware. TODO: implement appropriate method for binding coordinator to group - // ZigbeeClusterReply *reply = groupsCluster->addGroup(0x0000, "Default"); - // connect(reply, &ZigbeeClusterReply::finished, this, [=](){ - // if (reply->error() != ZigbeeClusterReply::ErrorNoError) { - // qCWarning(dcZigbeeNetwork()) << "Failed to add coordinator to default group 0x0000. The coordinator will not be in default group 0x0000"; - // } - // setState(StateRunning); - // setPermitJoining(0); - // }); + ZigbeeClusterGroups *groupsCluster = coordinatorNode->getEndpoint(0x01)->inputCluster(ZigbeeClusterLibrary::ClusterIdGroups); + if (!groupsCluster) { + qCWarning(dcZigbeeNetwork()) << "Failed to get groups cluster from coordinator. The coordinator will not be in default group 0x0000"; + setState(StateRunning); + setPermitJoining(0); + return; + } + + ZigbeeClusterReply *reply = groupsCluster->addGroup(0x0000, "Default"); + connect(reply, &ZigbeeClusterReply::finished, this, [=](){ + if (reply->error() != ZigbeeClusterReply::ErrorNoError) { + qCWarning(dcZigbeeNetwork()) << "Failed to add coordinator to default group 0x0000. The coordinator will not be in default group 0x0000"; + } + setState(StateRunning); + setPermitJoining(0); + }); + */ setState(StateRunning); setPermitJoining(0); @@ -613,8 +620,10 @@ void ZigbeeNetworkNxp::onDeviceAnnounced(quint16 shortAddress, ZigbeeAddress iee setNodeReachable(node, true); return; } else { - qCDebug(dcZigbeeNetwork()) << "Already known device announced with different network address. Removing node and reinitialize..."; - removeNode(node); + qCWarning(dcZigbeeNetwork()) << "Already known device announced with different network address. FIXME: update the network address or reinitialize node..."; + + + //removeNode(node); } } diff --git a/libnymea-zigbee/zdo/zigbeedeviceobject.cpp b/libnymea-zigbee/zdo/zigbeedeviceobject.cpp index a8af525..9120c25 100644 --- a/libnymea-zigbee/zdo/zigbeedeviceobject.cpp +++ b/libnymea-zigbee/zdo/zigbeedeviceobject.cpp @@ -211,9 +211,10 @@ ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestSimpleDescriptor(quint8 endp return zdoReply; } -ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestBindShortAddress(quint8 sourceEndpointId, quint16 clusterId, quint16 destinationAddress) +ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestBindGroupAddress(quint8 sourceEndpointId, quint16 clusterId, quint16 destinationAddress) { - qCDebug(dcZigbeeDeviceObject()) << "Request bind short address from" << m_node << "endpoint" << clusterId << "to" << destinationAddress; + qCDebug(dcZigbeeDeviceObject()) << "Request bind group address from" << m_node << "endpoint" << ZigbeeUtils::convertByteToHexString(sourceEndpointId) + << static_cast(clusterId) << "to group" << ZigbeeUtils::convertUint16ToHexString(destinationAddress); // Build APS request ZigbeeNetworkRequest request = buildZdoRequest(ZigbeeDeviceProfile::BindRequest); @@ -260,7 +261,9 @@ ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestBindShortAddress(quint8 sour ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestBindIeeeAddress(quint8 sourceEndpointId, quint16 clusterId, const ZigbeeAddress &destinationIeeeAddress, quint8 destinationEndpointId) { - qCDebug(dcZigbeeDeviceObject()) << "Request bind ieee address from" << m_node << "endpoint" << clusterId << "to" << destinationIeeeAddress.toString() << destinationEndpointId; + qCDebug(dcZigbeeDeviceObject()) << "Request bind IEEE address from" << m_node << "endpoint" << ZigbeeUtils::convertByteToHexString(sourceEndpointId) + << static_cast(clusterId) << "to" << destinationIeeeAddress.toString() << "endpoint" + << ZigbeeUtils::convertByteToHexString(destinationEndpointId); // Build APS request ZigbeeNetworkRequest request = buildZdoRequest(ZigbeeDeviceProfile::BindRequest); @@ -358,6 +361,49 @@ ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestUnbind(const ZigbeeDevicePro return zdoReply; } +ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestBindRegister(const ZigbeeAddress &ieeeAddress) +{ + qCDebug(dcZigbeeDeviceObject()) << "Request bind register" << m_node << ieeeAddress.toString(); + + // Build APS request + ZigbeeNetworkRequest request = buildZdoRequest(ZigbeeDeviceProfile::BindRegisterRequest); + + // Generate a new transaction sequence number for this device object + quint8 transactionSequenceNumber = m_transactionSequenceNumber++; + + // Build ZDO frame + QByteArray asdu; + QDataStream stream(&asdu, QIODevice::WriteOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream << transactionSequenceNumber; + stream << ieeeAddress.toUInt64(); + + // Set the ZDO frame as APS request payload + request.setAsdu(asdu); + + // Create the device object reply and wait for the response indication + ZigbeeDeviceObjectReply *zdoReply = createZigbeeDeviceObjectReply(request, transactionSequenceNumber); + + // Send the request, on finished read the confirm information + ZigbeeNetworkReply *networkReply = m_network->sendRequest(request); + connect(networkReply, &ZigbeeNetworkReply::finished, this, [this, networkReply, zdoReply](){ + if (!verifyNetworkError(zdoReply, networkReply)) { + finishZdoReply(zdoReply); + return; + } + + // The request was successfully sent to the device + // Now check if the expected indication response received already + if (zdoReply->isComplete()) { + finishZdoReply(zdoReply); + return; + } + // We received the confirmation but not yet the indication + }); + + return zdoReply; +} + ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestMgmtLeaveNetwork(bool rejoin, bool removeChildren) { qCDebug(dcZigbeeDeviceObject()) << "Request management leave network from" << m_node << "rejoin" << rejoin << "remove children" << removeChildren; @@ -442,17 +488,6 @@ ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestMgmtLqi(quint8 startIndex) // Now check if the expected indication response received already if (zdoReply->isComplete()) { qCDebug(dcZigbeeDeviceObject()) << "Successfully received response for" << static_cast(networkReply->request().clusterId()); - // TODO: pars child table -// QByteArray response = zdoReply->responseAdpu().payload; -// QDataStream stream(&response, QIODevice::ReadOnly); -// stream.setByteOrder(QDataStream::LittleEndian); -// quint8 statusValue; quint8 tableEntries; quint8 startIndex; - -// ZigbeeDeviceProfile::Status status; - - - - finishZdoReply(zdoReply); return; } diff --git a/libnymea-zigbee/zdo/zigbeedeviceobject.h b/libnymea-zigbee/zdo/zigbeedeviceobject.h index 2f497c4..1b900e7 100644 --- a/libnymea-zigbee/zdo/zigbeedeviceobject.h +++ b/libnymea-zigbee/zdo/zigbeedeviceobject.h @@ -50,10 +50,10 @@ public: // TODO: implement other device and service discovery methods // End device binding - ZigbeeDeviceObjectReply *requestBindShortAddress(quint8 sourceEndpointId, quint16 clusterId, quint16 destinationAddress); + ZigbeeDeviceObjectReply *requestBindGroupAddress(quint8 sourceEndpointId, quint16 clusterId, quint16 destinationAddress); ZigbeeDeviceObjectReply *requestBindIeeeAddress(quint8 sourceEndpointId, quint16 clusterId, const ZigbeeAddress &destinationIeeeAddress, quint8 destinationEndpointId); - ZigbeeDeviceObjectReply *requestUnbind(const ZigbeeDeviceProfile::BindingTableListRecord &bindingRecord); + ZigbeeDeviceObjectReply *requestBindRegister(const ZigbeeAddress &ieeeAddress); // Management request ZigbeeDeviceObjectReply *requestMgmtLeaveNetwork(bool rejoin = false, bool removeChildren = false); diff --git a/libnymea-zigbee/zdo/zigbeedeviceprofile.cpp b/libnymea-zigbee/zdo/zigbeedeviceprofile.cpp index 71267e7..dda88dc 100644 --- a/libnymea-zigbee/zdo/zigbeedeviceprofile.cpp +++ b/libnymea-zigbee/zdo/zigbeedeviceprofile.cpp @@ -197,7 +197,7 @@ ZigbeeDeviceProfile::Adpu ZigbeeDeviceProfile::parseAdpu(const QByteArray &adpu) QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::Adpu &deviceAdpu) { - debug.nospace() << "DeviceAdpu(SQN: " << deviceAdpu.transactionSequenceNumber << ", "; + debug.nospace() << "DeviceProfileAdpu(SQN: " << deviceAdpu.transactionSequenceNumber << ", "; debug.nospace() << deviceAdpu.status << ", "; debug.nospace() << ZigbeeUtils::convertUint16ToHexString(deviceAdpu.addressOfInterest) << ", "; debug.nospace() << "Payload: " << ZigbeeUtils::convertByteArrayToHexString(deviceAdpu.payload) << ")"; @@ -210,12 +210,12 @@ QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::NodeDescriptor &nodeD debug.nospace() << " Complex descriptor available: " << nodeDescriptor.complexDescriptorAvailable << "\n"; debug.nospace() << " User descriptor available: " << nodeDescriptor.userDescriptorAvailable << "\n"; debug.nospace() << " " << nodeDescriptor.frequencyBand << "\n"; - debug.nospace() << " " << nodeDescriptor.macCapabilities; + debug.nospace() << " " << nodeDescriptor.macCapabilities << "\n"; debug.nospace() << " Manufacturer code: " << ZigbeeUtils::convertUint16ToHexString(nodeDescriptor.manufacturerCode) << "(" << nodeDescriptor.manufacturerCode << ")" << "\n"; debug.nospace() << " Maximum buffer size: " << nodeDescriptor.maximumBufferSize << "\n"; debug.nospace() << " Maximum RX size: " << nodeDescriptor.maximumRxSize << "\n"; debug.nospace() << " Maximum TX size: " << nodeDescriptor.maximumTxSize << "\n"; - debug.nospace() << " " << nodeDescriptor.serverMask; + debug.nospace() << " " << nodeDescriptor.serverMask << "\n"; debug.nospace() << " " << nodeDescriptor.descriptorCapabilities; return debug; } diff --git a/libnymea-zigbee/zdo/zigbeedeviceprofile.h b/libnymea-zigbee/zdo/zigbeedeviceprofile.h index 679c9cf..03444fc 100644 --- a/libnymea-zigbee/zdo/zigbeedeviceprofile.h +++ b/libnymea-zigbee/zdo/zigbeedeviceprofile.h @@ -292,7 +292,6 @@ public: static MacCapabilities parseMacCapabilities(quint8 macCapabilitiesFlag); static ServerMask parseServerMask(quint16 serverMaskFlag); static DescriptorCapabilities parseDescriptorCapabilities(quint8 descriptorCapabilitiesFlag); - static PowerDescriptor parsePowerDescriptor(quint16 powerDescriptorFlag); }; diff --git a/libnymea-zigbee/zigbeenetwork.cpp b/libnymea-zigbee/zigbeenetwork.cpp index dae2e8f..7992772 100644 --- a/libnymea-zigbee/zigbeenetwork.cpp +++ b/libnymea-zigbee/zigbeenetwork.cpp @@ -691,8 +691,9 @@ void ZigbeeNetwork::evaluateNodeReachableStates() // Note: sleeping devices should send some message within 6 hours, // otherwise the device might not be reachable any more int msSinceLastSeen = node->lastSeen().msecsTo(QDateTime::currentDateTimeUtc()); - qCDebug(dcZigbeeNetwork()) << node << "last seen" << QTime::fromMSecsSinceStartOfDay(msSinceLastSeen).toString(); - if (msSinceLastSeen < 1000*60*60*6) { + qCDebug(dcZigbeeNetwork()) << node << "has been seen the last time" << QTime::fromMSecsSinceStartOfDay(msSinceLastSeen).toString() << "ago."; + // 6 Hours = 6 * 60 * 60 * 1000 = 21600000 ms + if (msSinceLastSeen < 21600000) { setNodeReachable(node, true); } else { setNodeReachable(node, false);