diff --git a/libnymea-zigbee/backends/nxp/interface/nxp.h b/libnymea-zigbee/backends/nxp/interface/nxp.h index fd5c16e..942395e 100644 --- a/libnymea-zigbee/backends/nxp/interface/nxp.h +++ b/libnymea-zigbee/backends/nxp/interface/nxp.h @@ -12,15 +12,20 @@ public: CommandGetControllerState = 0x01, CommandSoftReset = 0x02, CommandFactoryReset = 0x03, + CommandSetPanId = 0x04, CommandSetChannelMask = 0x05, CommandSetSecurityKey = 0x06, - CommandStartNetwork = 0x07 + + CommandStartNetwork = 0x07, + CommandGetNetworkState = 0x08, + CommandSetPermitJoinCoordinator = 0x09 }; Q_ENUM(Command) enum Notification { NotificationDeviceStatusChanged = 0x7D, + NotificationNetworkStarted = 0x7E, NotificationDebugMessage = 0xFE }; Q_ENUM(Notification) diff --git a/libnymea-zigbee/backends/nxp/zigbeebridgecontrollernxp.cpp b/libnymea-zigbee/backends/nxp/zigbeebridgecontrollernxp.cpp index 7279ca8..de0f603 100644 --- a/libnymea-zigbee/backends/nxp/zigbeebridgecontrollernxp.cpp +++ b/libnymea-zigbee/backends/nxp/zigbeebridgecontrollernxp.cpp @@ -133,6 +133,33 @@ ZigbeeInterfaceNxpReply *ZigbeeBridgeControllerNxp::requestStartNetwork() return createReply(Nxp::CommandStartNetwork, m_sequenceNumber, "Request start network", message, this); } +ZigbeeInterfaceNxpReply *ZigbeeBridgeControllerNxp::requestNetworkState() +{ + QByteArray message; + bumpSequenceNumber(); + QDataStream stream(&message, QIODevice::WriteOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream << static_cast(Nxp::CommandGetNetworkState); + stream << static_cast(m_sequenceNumber); + stream << static_cast(0); // Frame length + + return createReply(Nxp::CommandGetNetworkState, m_sequenceNumber, "Request network state", message, this); +} + +ZigbeeInterfaceNxpReply *ZigbeeBridgeControllerNxp::requestSetPermitJoinCoordinator(quint8 duration) +{ + QByteArray message; + bumpSequenceNumber(); + QDataStream stream(&message, QIODevice::WriteOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream << static_cast(Nxp::CommandSetPermitJoinCoordinator); + stream << static_cast(m_sequenceNumber); + stream << static_cast(1); // Frame length + stream << duration; + + return createReply(Nxp::CommandSetPermitJoinCoordinator, m_sequenceNumber, "Request set permit join in coordinator", message, this); +} + ZigbeeInterfaceNxpReply *ZigbeeBridgeControllerNxp::createReply(Nxp::Command command, quint8 sequenceNumber, const QString &requestName, const QByteArray &requestData, QObject *parent) { // Create the reply diff --git a/libnymea-zigbee/backends/nxp/zigbeebridgecontrollernxp.h b/libnymea-zigbee/backends/nxp/zigbeebridgecontrollernxp.h index a72495b..c4644ca 100644 --- a/libnymea-zigbee/backends/nxp/zigbeebridgecontrollernxp.h +++ b/libnymea-zigbee/backends/nxp/zigbeebridgecontrollernxp.h @@ -46,7 +46,12 @@ public: ZigbeeInterfaceNxpReply *requestSetChannelMask(quint32 channelMask); ZigbeeInterfaceNxpReply *requestSetSecurityKey(Nxp::KeyType keyType, const ZigbeeNetworkKey &key); + // Network commands ZigbeeInterfaceNxpReply *requestStartNetwork(); + ZigbeeInterfaceNxpReply *requestNetworkState(); + ZigbeeInterfaceNxpReply *requestSetPermitJoinCoordinator(quint8 duration); + + // APS signals: diff --git a/libnymea-zigbee/backends/nxp/zigbeenetworknxp.cpp b/libnymea-zigbee/backends/nxp/zigbeenetworknxp.cpp index 3e8467e..17ccfe1 100644 --- a/libnymea-zigbee/backends/nxp/zigbeenetworknxp.cpp +++ b/libnymea-zigbee/backends/nxp/zigbeenetworknxp.cpp @@ -9,10 +9,10 @@ ZigbeeNetworkNxp::ZigbeeNetworkNxp(QObject *parent) : { m_controller = new ZigbeeBridgeControllerNxp(this); connect(m_controller, &ZigbeeBridgeControllerNxp::availableChanged, this, &ZigbeeNetworkNxp::onControllerAvailableChanged); + connect(m_controller, &ZigbeeBridgeControllerNxp::interfaceNotificationReceived, this, &ZigbeeNetworkNxp::onInterfaceNotificationReceived); connect(m_controller, &ZigbeeBridgeControllerNxp::controllerStateChanged, this, &ZigbeeNetworkNxp::onControllerStateChanged); //connect(m_controller, &ZigbeeBridgeControllerNxp::apsDataConfirmReceived, this, &ZigbeeNetworkNxp::onApsDataConfirmReceived); //connect(m_controller, &ZigbeeBridgeControllerNxp::apsDataIndicationReceived, this, &ZigbeeNetworkNxp::onApsDataIndicationReceived); - } ZigbeeBridgeController *ZigbeeNetworkNxp::bridgeController() const @@ -39,17 +39,24 @@ ZigbeeNetworkReply *ZigbeeNetworkNxp::setPermitJoin(quint16 shortAddress, quint8 void ZigbeeNetworkNxp::onControllerAvailableChanged(bool available) { qCDebug(dcZigbeeNetwork()) << "Controller is" << (available ? "now available" : "not available any more"); - if (available) { + // Get controller state + + // Get network state, depending on the controller state + //reset(); factoryResetNetwork(); + } else { + setState(StateOffline); } } void ZigbeeNetworkNxp::onControllerStateChanged(ZigbeeBridgeControllerNxp::ControllerState controllerState) { + qCDebug(dcZigbeeNetwork()) << "Controller state changed" << controllerState; switch (controllerState) { case ZigbeeBridgeControllerNxp::ControllerStateRunning: { + setState(StateStarting); qCDebug(dcZigbeeNetwork()) << "Request controller version"; ZigbeeInterfaceNxpReply *reply = m_controller->requestVersion(); connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [this, reply](){ @@ -64,16 +71,74 @@ void ZigbeeNetworkNxp::onControllerStateChanged(ZigbeeBridgeControllerNxp::Contr qCDebug(dcZigbeeNetwork()) << "Controller version" << versionString; m_controller->setFirmwareVersion(versionString); - // We are done here... + qCDebug(dcZigbeeNetwork()) << "Get the current network state"; + ZigbeeInterfaceNxpReply *reply = m_controller->requestNetworkState(); + connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [this, reply](){ + qCDebug(dcZigbeeNetwork()) << "Get network state response" << reply->status(); + //FIXME: error handling + + QByteArray data = reply->responseData(); + QDataStream payloadStream(&data, QIODevice::ReadOnly); + payloadStream.setByteOrder(QDataStream::LittleEndian); + quint16 networkAddress; quint64 ieeeAddress; quint8 channel; + quint16 panId; quint64 extendedPanId; + payloadStream >> networkAddress >> ieeeAddress >> channel >> panId >> extendedPanId; + qCDebug(dcZigbeeNetwork()) << "Network running" << ZigbeeUtils::convertUint16ToHexString(networkAddress) + << ZigbeeAddress(ieeeAddress).toString() + << "Channel:" << channel + << "PAN ID:" << panId << "Extended PAN ID:" << ZigbeeUtils::convertUint64ToHexString(extendedPanId); + + setPanId(panId); + setChannel(channel); + + + // Initialize the coordinator node if not already done. + + if (m_coordinatorNode) { + qCDebug(dcZigbeeNetwork()) << "We already have the coordinator node. Network starting done."; + m_database->saveNode(m_coordinatorNode); + setState(StateRunning); + setPermitJoiningInternal(false); + return; + } + + ZigbeeNode *coordinatorNode = createNode(networkAddress, ZigbeeAddress(ieeeAddress), this); + m_coordinatorNode = coordinatorNode; + + // TODO: initialize + + m_database->saveNode(m_coordinatorNode); + saveNetwork(); + setState(StateRunning); + +// // Network creation done when coordinator node is initialized +// 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; +// } +// }); + +// coordinatorNode->startInitialization(); +// addUnitializedNode(coordinatorNode); + + + }); }); break; } case ZigbeeBridgeControllerNxp::ControllerStateStarting: + setState(StateStarting); break; case ZigbeeBridgeControllerNxp::ControllerStateBooting: + setState(StateStarting); break; case ZigbeeBridgeControllerNxp::ControllerStateRunningUninitialized: { + setState(StateStarting); qCDebug(dcZigbeeNetwork()) << "Request controller version"; ZigbeeInterfaceNxpReply *reply = m_controller->requestVersion(); connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [this, reply](){ @@ -90,38 +155,48 @@ void ZigbeeNetworkNxp::onControllerStateChanged(ZigbeeBridgeControllerNxp::Contr qCDebug(dcZigbeeNetwork()) << "Controller version" << versionString; m_controller->setFirmwareVersion(versionString); - qCDebug(dcZigbeeNetwork()) << "Set pan id" << ZigbeeUtils::convertUint64ToHexString(extendedPanId()) << extendedPanId(); + if (extendedPanId() == 0) { + quint64 panId = ZigbeeUtils::generateRandomPanId(); + setExtendedPanId(panId); + qCDebug(dcZigbeeNetwork()) << "There is no pan id set yet. Generated new PAN ID" << panId << ZigbeeUtils::convertUint64ToHexString(panId); + + ZigbeeSecurityConfiguration securityConfiguration; + securityConfiguration.setNetworkKey(ZigbeeNetworkKey::generateKey()); + setSecurityConfiguration(securityConfiguration); + + qCDebug(dcZigbeeNetwork()) << "Generated new network key" << securityConfiguration.networkKey().toString(); + } + + qCDebug(dcZigbeeNetwork()) << "Set PAN ID" << ZigbeeUtils::convertUint64ToHexString(extendedPanId()) << extendedPanId(); ZigbeeInterfaceNxpReply *reply = m_controller->requestSetPanId(extendedPanId()); connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [this, reply](){ - qCDebug(dcZigbeeNetwork()) << "Set PAN ID reply finished" << reply->status(); + qCDebug(dcZigbeeNetwork()) << "Set PAN ID reply response" << reply->status(); //FIXME: error handling - qCDebug(dcZigbeeNetwork()) << "Set channel mask" << channelMask() << ZigbeeUtils::convertUint32ToHexString(channelMask().toUInt32()) << channelMask().toUInt32(); + qCDebug(dcZigbeeNetwork()) << "Set channel mask" << channelMask(); ZigbeeInterfaceNxpReply *reply = m_controller->requestSetChannelMask(channelMask().toUInt32()); connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [this, reply](){ - qCDebug(dcZigbeeNetwork()) << "Set channel mask reply finished" << reply->status(); + qCDebug(dcZigbeeNetwork()) << "Set channel mask reply response" << reply->status(); //FIXME: error handling qCDebug(dcZigbeeNetwork()) << "Set global link key" << securityConfiguration().globalTrustCenterLinkKey().toString(); ZigbeeInterfaceNxpReply *reply = m_controller->requestSetSecurityKey(Nxp::KeyTypeGlobalLinkKey, securityConfiguration().globalTrustCenterLinkKey()); connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [this, reply](){ - qCDebug(dcZigbeeNetwork()) << "Set global link key" << reply->status(); + qCDebug(dcZigbeeNetwork()) << "Set global link key response" << reply->status(); //FIXME: error handling qCDebug(dcZigbeeNetwork()) << "Set network link key" << securityConfiguration().networkKey().toString(); ZigbeeInterfaceNxpReply *reply = m_controller->requestSetSecurityKey(Nxp::KeyTypeUniqueLinkKey, securityConfiguration().networkKey()); connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [this, reply](){ - qCDebug(dcZigbeeNetwork()) << "Set network link key" << reply->status(); + qCDebug(dcZigbeeNetwork()) << "Set network link key response" << reply->status(); //FIXME: error handling qCDebug(dcZigbeeNetwork()) << "Start the network"; ZigbeeInterfaceNxpReply *reply = m_controller->requestStartNetwork(); - connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [this, reply](){ - qCDebug(dcZigbeeNetwork()) << "Start network" << reply->status(); + connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [reply](){ + qCDebug(dcZigbeeNetwork()) << "Start network response" << reply->status(); //FIXME: error handling - - - + qCDebug(dcZigbeeNetwork()) << "Waiting for the network to start..."; }); }); }); @@ -131,13 +206,50 @@ void ZigbeeNetworkNxp::onControllerStateChanged(ZigbeeBridgeControllerNxp::Contr break; } case ZigbeeBridgeControllerNxp::ControllerStateNotRunning: + setState(StateOffline); break; } } +void ZigbeeNetworkNxp::onInterfaceNotificationReceived(Nxp::Notification notification, const QByteArray &payload) +{ + switch (notification) { + case Nxp::NotificationNetworkStarted: { + QByteArray data = payload; + QDataStream payloadStream(&data, QIODevice::ReadOnly); + payloadStream.setByteOrder(QDataStream::LittleEndian); + quint16 networkAddress; quint64 ieeeAddress; quint8 channel; + payloadStream >> networkAddress >> ieeeAddress >> channel; + + qCDebug(dcZigbeeNetwork()) << "Network started" << ZigbeeUtils::convertUint16ToHexString(networkAddress) << ZigbeeAddress(ieeeAddress).toString() << "Channel:" << channel; + break; + } + default: + qCWarning(dcZigbeeNetwork()) << "Unhandeld interface notification received" << notification << ZigbeeUtils::convertByteArrayToHexString(payload); + + } + + +} + void ZigbeeNetworkNxp::setPermitJoiningInternal(bool permitJoining) { qCDebug(dcZigbeeNetwork()) << "Set permit join internal" << permitJoining; + + quint8 duration = 0; + if (permitJoining) { + duration = 255; + } + + // TODO: send broadcast message using ZDO and refrash + + qCDebug(dcZigbeeNetwork()) << "Set permit join in the coordinator node to" << duration << "[s]"; + ZigbeeInterfaceNxpReply *reply = m_controller->requestSetPermitJoinCoordinator(duration); + connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [this, reply, permitJoining](){ + qCDebug(dcZigbeeNetwork()) << "Set permit join in the coordinator finished" << reply->status(); + m_permitJoining = permitJoining; + emit permitJoiningChanged(m_permitJoining); + }); } void ZigbeeNetworkNxp::startNetwork() @@ -165,16 +277,18 @@ void ZigbeeNetworkNxp::stopNetwork() void ZigbeeNetworkNxp::reset() { + qCDebug(dcZigbeeNetwork()) << "Soft reset the controller. The stack will perform a restart."; ZigbeeInterfaceNxpReply *reply = m_controller->requestSoftResetController(); - connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [](){ - qCDebug(dcZigbeeNetwork()) << "Soft reset reply finished"; + connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [reply](){ + qCDebug(dcZigbeeNetwork()) << "Soft reset reply finished" << reply->status(); }); } void ZigbeeNetworkNxp::factoryResetNetwork() { + qCDebug(dcZigbeeNetwork()) << "Factory reset network and forget all information. This cannot be undone."; ZigbeeInterfaceNxpReply *reply = m_controller->requestFactoryResetController(); - connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [](){ - qCDebug(dcZigbeeNetwork()) << "Factory reset reply finished"; + connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [reply](){ + qCDebug(dcZigbeeNetwork()) << "Factory reset reply finished" << reply->status(); }); } diff --git a/libnymea-zigbee/backends/nxp/zigbeenetworknxp.h b/libnymea-zigbee/backends/nxp/zigbeenetworknxp.h index 4a5f138..53f705f 100644 --- a/libnymea-zigbee/backends/nxp/zigbeenetworknxp.h +++ b/libnymea-zigbee/backends/nxp/zigbeenetworknxp.h @@ -27,6 +27,7 @@ private: private slots: void onControllerAvailableChanged(bool available); void onControllerStateChanged(ZigbeeBridgeControllerNxp::ControllerState controllerState); + void onInterfaceNotificationReceived(Nxp::Notification notification, const QByteArray &payload); protected: void setPermitJoiningInternal(bool permitJoining) override;