/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 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 "zigbeenetworknxp.h" #include "loggingcategory.h" #include "zigbeeutils.h" #include "zigbeenetworkdatabase.h" #include ZigbeeNetworkNxp::ZigbeeNetworkNxp(const QUuid &networkUuid, QObject *parent) : ZigbeeNetwork(networkUuid, parent) { m_controller = new ZigbeeBridgeControllerNxp(this); connect(m_controller, &ZigbeeBridgeControllerNxp::availableChanged, this, &ZigbeeNetworkNxp::onControllerAvailableChanged); connect(m_controller, &ZigbeeBridgeControllerNxp::firmwareVersionChanged, this, &ZigbeeNetworkNxp::firmwareVersionChanged); 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); connect(m_controller, &ZigbeeBridgeControllerNxp::nodeLeft, this, &ZigbeeNetworkNxp::onNodeLeftIndication); connect(m_controller, &ZigbeeBridgeControllerNxp::canUpdateChanged, this, [](bool canUpdate){ if (canUpdate) { qCDebug(dcZigbeeNetwork()) << "The controller of this network can be updated."; } else { qCDebug(dcZigbeeNetwork()) << "The controller of this network can not be updated."; } }); connect(m_controller, &ZigbeeBridgeControllerNxp::updateRunningChanged, this, [this](bool updateRunning){ if (updateRunning) { qCDebug(dcZigbeeNetwork()) << "The controller is performing an update."; setState(StateUpdating); } }); } ZigbeeBridgeController *ZigbeeNetworkNxp::bridgeController() const { if (!m_controller) return nullptr; return qobject_cast(m_controller); } Zigbee::ZigbeeBackendType ZigbeeNetworkNxp::backendType() const { return Zigbee::ZigbeeBackendTypeNxp; } ZigbeeNetworkReply *ZigbeeNetworkNxp::sendRequest(const ZigbeeNetworkRequest &request) { ZigbeeNetworkReply *reply = createNetworkReply(request); // Send the request, and keep the reply until transposrt, zigbee trasmission and response arrived connect(reply, &ZigbeeNetworkReply::finished, this, [this, reply](){ if (!m_pendingReplies.values().contains(reply)) { //qCWarning(dcZigbeeNetwork()) << "#### Reply finished but not in the pending replies list" << reply; return; } quint8 requestId = m_pendingReplies.key(reply); m_pendingReplies.remove(requestId); //qCWarning(dcZigbeeNetwork()) << "#### Removed network reply" << reply << "ID:" << requestId << "Current reply count" << m_pendingReplies.count(); }); // Finish the reply right the way if the network is offline if (!m_controller->available() || state() == ZigbeeNetwork::StateOffline) { finishReplyInternally(reply, ZigbeeNetworkReply::ErrorNetworkOffline); return reply; } // 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; } void ZigbeeNetworkNxp::setPermitJoining(quint8 duration, quint16 address) { if (duration > 0) { qCDebug(dcZigbeeNetwork()) << "Set permit join for" << duration << "s on" << ZigbeeUtils::convertUint16ToHexString(address); } else { qCDebug(dcZigbeeNetwork()) << "Disable permit join on"<< ZigbeeUtils::convertUint16ToHexString(address); } // Note: will be reseted if permit join will not work setPermitJoiningEnabled(duration > 0); setPermitJoiningDuration(duration); setPermitJoiningRemaining(duration); if (address == 0x0000) { // Only the coordinator is allowed to join the network qCDebug(dcZigbeeNetwork()) << "Set permit join in the coordinator node only to" << duration << "[s]"; ZigbeeInterfaceNxpReply *reply = m_controller->requestSetPermitJoinCoordinator(duration); connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [this, reply, duration](){ qCDebug(dcZigbeeNetwork()) << "Set permit join in the coordinator finished" << reply->status(); if (reply->status() != Nxp::StatusSuccess) { qCWarning(dcZigbeeNetwork()) << "Failed to set permit join status in coordinator"; setPermitJoiningEnabled(false); setPermitJoiningDuration(duration); } else { setPermitJoiningEnabled(duration > 0); setPermitJoiningDuration(duration); setPermitJoiningRemaining(duration); if (duration > 0) { m_permitJoinTimer->start(); } } }); return; } // Note: since compliance version >= 21 the value 255 is not any more endless. // We need to refresh the command on timeout if the duration is longer ZigbeeNetworkReply *reply = requestSetPermitJoin(address, duration); connect(reply, &ZigbeeNetworkReply::finished, this, [this, reply, duration, address](){ if (reply->zigbeeApsStatus() != Zigbee::ZigbeeApsStatusSuccess) { qCWarning(dcZigbeeNetwork()) << "Could not set permit join to" << duration << ZigbeeUtils::convertUint16ToHexString(address) << reply->zigbeeApsStatus(); setPermitJoiningEnabled(false); setPermitJoiningDuration(duration); m_permitJoinTimer->stop(); return; } qCDebug(dcZigbeeNetwork()) << "Permit join request finished successfully"; setPermitJoiningEnabled(duration > 0); setPermitJoiningDuration(duration); setPermitJoiningRemaining(duration); if (duration > 0) { m_permitJoinTimer->start(); } else { m_permitJoinTimer->stop(); } if (address == Zigbee::BroadcastAddressAllRouters || address == 0x0000) { qCDebug(dcZigbeeNetwork()) << "Set permit join in the coordinator node to" << duration << "[s]"; ZigbeeInterfaceNxpReply *reply = m_controller->requestSetPermitJoinCoordinator(duration); connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [reply](){ qCDebug(dcZigbeeNetwork()) << "Set permit join in the coordinator finished" << reply->status(); if (reply->status() != Nxp::StatusSuccess) { qCWarning(dcZigbeeNetwork()) << "Failed to set permit join status in coordinator"; } }); } }); } void ZigbeeNetworkNxp::sendNextReply() { if (m_replyQueue.isEmpty()) return; if (m_currentReply) return; 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) { qCWarning(dcZigbeeController()) << "Could send request to controller. SQN:" << interfaceReply->sequenceNumber() << interfaceReply->status(); finishReplyInternally(reply, ZigbeeNetworkReply::ErrorInterfaceError); return; } // Note: this is a special case for nxp coordinator requests, they don't send a confirm because the request will not be sent trough the network if (reply->request().destinationShortAddress() == 0x0000 && reply->request().profileId() == Zigbee::ZigbeeProfileDevice) { qCDebug(dcZigbeeNetwork()) << "Finish reply since there will be no CONFIRM for local node requests."; finishReplyInternally(reply); return; } quint8 networkRequestId = interfaceReply->responseData().at(0); //qCDebug(dcZigbeeNetwork()) << "Request has network SQN" << networkRequestId; reply->request().setRequestId(networkRequestId); //qCWarning(dcZigbeeNetwork()) << "#### Insert network reply" << reply << "ID:" << networkRequestId << "Current reply count" << m_pendingReplies.count(); m_pendingReplies.insert(networkRequestId, reply); // The request has been sent successfully to the device, start the timeout timer now startWaitingReply(reply); }); } void ZigbeeNetworkNxp::finishReplyInternally(ZigbeeNetworkReply *reply, ZigbeeNetworkReply::Error error) { finishNetworkReply(reply, error); if (m_currentReply == reply) { m_currentReply = nullptr; } sendNextReply(); } ZigbeeNetworkReply *ZigbeeNetworkNxp::requestSetPermitJoin(quint16 shortAddress, quint8 duration) { // Get the power descriptor ZigbeeNetworkRequest request; request.setRequestId(generateSequenceNumber()); request.setDestinationAddressMode(Zigbee::DestinationAddressModeShortAddress); request.setDestinationShortAddress(static_cast(shortAddress)); request.setProfileId(Zigbee::ZigbeeProfileDevice); // ZDP request.setClusterId(ZigbeeDeviceProfile::MgmtPermitJoinRequest); request.setSourceEndpoint(0); // ZDO request.setRadius(10); // Build ASDU QByteArray asdu; QDataStream stream(&asdu, QIODevice::WriteOnly); stream.setByteOrder(QDataStream::LittleEndian); stream << request.requestId(); stream << duration; stream << static_cast(0x01); // TrustCenter significance, always force to 1 according to Spec. request.setTxOptions(Zigbee::ZigbeeTxOptions()); // no ACK for broadcasts request.setAsdu(asdu); qCDebug(dcZigbeeNetwork()) << "Send permit join request" << ZigbeeUtils::convertUint16ToHexString(request.destinationShortAddress()) << duration << "s"; return sendRequest(request); } bool ZigbeeNetworkNxp::processVersionReply(ZigbeeInterfaceNxpReply *reply) { qCDebug(dcZigbeeNetwork()) << "Version reply finished" << reply->status(); if (reply->timendOut()) { m_reconnectCounter++; if (m_reconnectCounter >= 3) { if (m_controller->canUpdate()) { qCDebug(dcZigbeeNetwork()) << "Unable to get controller version."; qCDebug(dcZigbeeNetwork()) << "Firmware update provider available. Try to flash the firmware, maybe that fixes the problem."; // FIXME: try 3 times, then give up or perform a factory flash if (!m_controller->updateRunning()) { clearSettings(); qCDebug(dcZigbeeNetwork()) << "Starting firmware update..."; m_controller->startFirmwareUpdate(); } else { qCWarning(dcZigbeeNetwork()) << "There is already an update running..."; } return false; } else { qCDebug(dcZigbeeNetwork()) << "Unable to get controller version. There is no firmware upgrade available. Giving up."; return false; } } qCWarning(dcZigbeeNetwork()) << "Failed to read firmware version. Retry" << m_reconnectCounter << "/ 3"; ZigbeeInterfaceNxpReply *reply = m_controller->requestVersion(); connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [this, reply](){ if (processVersionReply(reply)) { m_controller->refreshControllerState(); } }); return false; } QByteArray payload = reply->responseData(); QDataStream stream(&payload, QIODevice::ReadOnly); stream.setByteOrder(QDataStream::LittleEndian); quint8 major = 0; quint8 minor = 0; quint8 patch = 0; quint16 sdkVersion = 0; stream >> major >> minor >> patch >> sdkVersion; QString version = QString("%1.%2.%3").arg(major).arg(minor).arg(patch); QString versionString = QString("%1 - %2").arg(version).arg(sdkVersion); qCDebug(dcZigbeeNetwork()) << "Controller version" << versionString; m_controller->setFirmwareVersion(versionString); if (m_controller->canUpdate()) { if (m_controller->updateAvailable(version)) { qCDebug(dcZigbeeNetwork()) << "There is an update available for the this zigbee controller:" << version << "-->" << m_controller->updateFirmwareVersion(); qCDebug(dcZigbeeNetwork()) << "Starting firmware update..."; m_controller->startFirmwareUpdate(); return false; } else { qCDebug(dcZigbeeNetwork()) << "The current firmware is up to date."; } } return true; } void ZigbeeNetworkNxp::onControllerAvailableChanged(bool available) { qCDebug(dcZigbeeNetwork()) << "Controller is" << (available ? "now available" : "not available any more"); if (available) { if (m_controller->canUpdate() && !m_controller->initiallyFlashed()) { qCDebug(dcZigbeeNetwork()) << "The firmware of the controller can be updated and has not been initially flashed. Perform a factory reset flash procedure..."; m_controller->startFactoryResetUpdate(); return; } m_reconnectCounter = 0; ZigbeeInterfaceNxpReply *reply = m_controller->requestVersion(); connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [this, reply](){ // Retry or firmware upgrade if available if (!processVersionReply(reply)) return; m_controller->refreshControllerState(); }); } else { setState(StateOffline); } } void ZigbeeNetworkNxp::onControllerStateChanged(ZigbeeBridgeControllerNxp::ControllerState controllerState) { qCDebug(dcZigbeeNetwork()) << "Controller state changed" << controllerState; switch (controllerState) { case ZigbeeBridgeControllerNxp::ControllerStateRunning: { setState(StateStarting); m_reconnectCounter = 0; qCDebug(dcZigbeeNetwork()) << "Request controller version"; ZigbeeInterfaceNxpReply *reply = m_controller->requestVersion(); connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [this, reply](){ // Retry or firmware upgrade if available if (!processVersionReply(reply)) return; 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) { if (!macAddress().isNull() && ZigbeeAddress(ieeeAddress) != macAddress()) { qCWarning(dcZigbeeNetwork()) << "The mac address of the coordinator has changed since the network has been set up."; qCWarning(dcZigbeeNetwork()) << "The network is bound to a specific controller. Since the controller has changed the network can not be started."; qCWarning(dcZigbeeNetwork()) << "Please factory reset the network or plug in the original controller."; setError(ZigbeeNetwork::ErrorHardwareModuleChanged); stopNetwork(); return; } qCDebug(dcZigbeeNetwork()) << "We already have the coordinator node. Network starting done."; setNodeInformation(m_coordinatorNode, "NXP", "JN516x", bridgeController()->firmwareVersion()); m_database->saveNode(m_coordinatorNode); setPermitJoining(0); setState(StateRunning); return; } ZigbeeNode *coordinatorNode = createNode(networkAddress, ZigbeeAddress(ieeeAddress), this); m_coordinatorNode = coordinatorNode; // 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; setNodeInformation(m_coordinatorNode, "NXP", "JN516x", bridgeController()->firmwareVersion()); /* Note: this currently has been hardcoded into the firmware. TODO: implement appropriate method for binding coordinator to group 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); return; } }); coordinatorNode->startInitialization(); addUnitializedNode(coordinatorNode); }); }); break; } case ZigbeeBridgeControllerNxp::ControllerStateStarting: setState(StateStarting); break; case ZigbeeBridgeControllerNxp::ControllerStateBooting: setState(StateStarting); break; case ZigbeeBridgeControllerNxp::ControllerStateRunningUninitialized: { // Create the database if there is no database available setState(StateStarting); if (!m_database) { QString networkDatabaseFileName = settingsDirectory().absolutePath() + QDir::separator() + QString("zigbee-network-%1.db").arg(networkUuid().toString().remove('{').remove('}')); qCDebug(dcZigbeeNetwork()) << "Using ZigBee network database" << QFileInfo(networkDatabaseFileName).fileName(); m_database = new ZigbeeNetworkDatabase(this, networkDatabaseFileName, this); } qCDebug(dcZigbeeNetwork()) << "Request controller version"; ZigbeeInterfaceNxpReply *reply = m_controller->requestVersion(); connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [this, reply](){ // Retry or firmware upgrade if available if (!processVersionReply(reply)) return; QByteArray payload = reply->responseData(); QDataStream stream(&payload, QIODevice::ReadOnly); stream.setByteOrder(QDataStream::LittleEndian); quint8 major = 0; quint8 minor = 0; quint8 patch = 0; quint16 sdkVersion = 0; stream >> major >> minor >> patch >> sdkVersion; QString versionString = QString ("%1.%2.%3 - %4").arg(major).arg(minor).arg(patch).arg(sdkVersion); qCDebug(dcZigbeeNetwork()) << "Controller version" << versionString; m_controller->setFirmwareVersion(versionString); 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 response" << reply->status(); //FIXME: error handling 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 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 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 response" << reply->status(); //FIXME: error handling qCDebug(dcZigbeeNetwork()) << "Start the network"; ZigbeeInterfaceNxpReply *reply = m_controller->requestStartNetwork(); connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [reply](){ qCDebug(dcZigbeeNetwork()) << "Start network response" << reply->status(); //FIXME: error handling qCDebug(dcZigbeeNetwork()) << "Waiting for the network to start..."; }); }); }); }); }); }); 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); break; } } void ZigbeeNetworkNxp::onApsDataConfirmReceived(const Zigbee::ApsdeDataConfirm &confirm) { ZigbeeNetworkReply *reply = m_pendingReplies.value(confirm.requestId); if (!reply) { qCDebug(dcZigbeeNetwork()) << "Received confirmation but could not find any reply. Ignoring the confirmation"; return; } setReplyResponseError(reply, static_cast(confirm.zigbeeStatusCode)); } void ZigbeeNetworkNxp::onApsDataIndicationReceived(const Zigbee::ApsdeDataIndication &indication) { // Check if this indocation is related to any pending reply if (indication.profileId == Zigbee::ZigbeeProfileDevice) { handleZigbeeDeviceProfileIndication(indication); return; } // Let the node handle this indication handleZigbeeClusterLibraryIndication(indication); } void ZigbeeNetworkNxp::onNodeLeftIndication(const ZigbeeAddress &ieeeAddress, bool rejoining) { qCDebug(dcZigbeeNetwork()) << "Received node left indication" << ieeeAddress.toString() << "rejoining:" << rejoining; if (!hasNode(ieeeAddress)) { qCDebug(dcZigbeeNetwork()) << "Node left the network" << ieeeAddress.toString(); return; } ZigbeeNode *node = getZigbeeNode(ieeeAddress); qCDebug(dcZigbeeNetwork()) << node << "left the network"; removeNode(node); } void ZigbeeNetworkNxp::startNetwork() { loadNetwork(); setPermitJoiningEnabled(false); if (!m_controller->enable(serialPortName(), serialBaudrate())) { setState(StateOffline); setError(ErrorHardwareUnavailable); return; } // Wait for available signal... } void ZigbeeNetworkNxp::stopNetwork() { m_controller->disable(); } 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, [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."; clearSettings(); ZigbeeInterfaceNxpReply *reply = m_controller->requestFactoryResetController(); connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [reply](){ qCDebug(dcZigbeeNetwork()) << "Factory reset reply finished" << reply->status(); }); } void ZigbeeNetworkNxp::destroyNetwork() { qCDebug(dcZigbeeNetwork()) << "Destroy network and delete the database"; m_controller->disable(); clearSettings(); }