From a7cb0e4e2650a02603df624f8c94190b99af113e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Thu, 29 Oct 2020 11:44:53 +0100 Subject: [PATCH] Add network settings loading and saving --- libnymea-core/jsonrpc/zigbeehandler.cpp | 13 +- libnymea-core/zigbee/zigbeeadapter.cpp | 36 ++-- libnymea-core/zigbee/zigbeeadapter.h | 31 ++-- libnymea-core/zigbee/zigbeemanager.cpp | 210 +++++++++++++++++++++--- libnymea-core/zigbee/zigbeemanager.h | 21 +-- 5 files changed, 237 insertions(+), 74 deletions(-) diff --git a/libnymea-core/jsonrpc/zigbeehandler.cpp b/libnymea-core/jsonrpc/zigbeehandler.cpp index 6995076c..ee2f1480 100644 --- a/libnymea-core/jsonrpc/zigbeehandler.cpp +++ b/libnymea-core/jsonrpc/zigbeehandler.cpp @@ -41,14 +41,14 @@ ZigbeeHandler::ZigbeeHandler(ZigbeeManager *zigbeeManager, QObject *parent) : m_zigbeeManager(zigbeeManager) { registerEnum(); - registerEnum(); + registerEnum(); registerEnum(); registerObject(); // Network object describing a network instance QVariantMap zigbeeNetworkDescription; - zigbeeNetworkDescription.insert("id", enumValueName(Uuid)); + zigbeeNetworkDescription.insert("uuid", enumValueName(Uuid)); registerObject("ZigbeeNetwork", zigbeeNetworkDescription); QVariantMap params, returns; @@ -56,7 +56,7 @@ ZigbeeHandler::ZigbeeHandler(ZigbeeManager *zigbeeManager, QObject *parent) : // GetAdapters params.clear(); returns.clear(); - description = "Get the list of available ZigBee adapter candidates in order to set up the zigbee network on the desired serial interface. If an adapter has been recognized correctly as a supported hardware, the backendSuggestionAvailable property will be true and the configurations can be used as they where given, otherwise the user might set the backend type and baud rate manually."; + description = "Get the list of available ZigBee adapter candidates in order to set up the zigbee network on the desired serial interface. If an adapter hardware has been recognized as a supported hardware, the \'hardwareRecognized\' property will be true and the configurations can be used as they where given, otherwise the user might set the backend type and baud rate manually."; returns.insert("adapters", objectRef()); registerMethod("GetAdapters", description, params, returns); @@ -100,14 +100,13 @@ ZigbeeHandler::ZigbeeHandler(ZigbeeManager *zigbeeManager, QObject *parent) : emit AdapterRemoved(params); }); - /* 1. GetNetworkStatus + /* 1. GetNetworks * 2. Setup network if the is no network configured - * - GetAvailableAdapters - * - Setup network with given UART interface and backend type + * - GetAdapters + * - AddNetwork (adapter, channelmask) * - */ - // GetNetworkStatus // NetworkStatusChanged // SetupNetwork(network, uart, baudrate, backendtype) diff --git a/libnymea-core/zigbee/zigbeeadapter.cpp b/libnymea-core/zigbee/zigbeeadapter.cpp index 7c099398..ce1af680 100644 --- a/libnymea-core/zigbee/zigbeeadapter.cpp +++ b/libnymea-core/zigbee/zigbeeadapter.cpp @@ -67,34 +67,34 @@ void ZigbeeAdapter::setSystemLocation(const QString &systemLocation) m_systemLocation = systemLocation; } -bool ZigbeeAdapter::backendSuggestionAvailable() const +bool ZigbeeAdapter::hardwareRecognized() const { - return m_backendSuggestionAvailable; + return m_hardwareRecognized; } -void ZigbeeAdapter::setBackendSuggestionAvailable(bool backendSuggestionAvailable) +void ZigbeeAdapter::setHardwareRecognized(bool hardwareRecognized) { - m_backendSuggestionAvailable = backendSuggestionAvailable; + m_hardwareRecognized = hardwareRecognized; } -Zigbee::ZigbeeBackendType ZigbeeAdapter::suggestedZigbeeBackendType() const +ZigbeeAdapter::ZigbeeBackendType ZigbeeAdapter::backendType() const { - return m_suggestedZigbeeBackendType; + return m_backendType; } -void ZigbeeAdapter::setSuggestedZigbeeBackendType(Zigbee::ZigbeeBackendType backendType) +void ZigbeeAdapter::setBackendType(ZigbeeAdapter::ZigbeeBackendType backendType) { - m_suggestedZigbeeBackendType = backendType; + m_backendType = backendType; } -qint32 ZigbeeAdapter::suggestedBaudRate() const +qint32 ZigbeeAdapter::baudRate() const { - return m_suggestedBaudRate; + return m_baudRate; } -void ZigbeeAdapter::setSuggestedBaudRate(qint32 baudRate) +void ZigbeeAdapter::setBaudRate(qint32 baudRate) { - m_suggestedBaudRate = baudRate; + m_baudRate = baudRate; } bool ZigbeeAdapter::operator==(const ZigbeeAdapter &other) const @@ -102,9 +102,9 @@ bool ZigbeeAdapter::operator==(const ZigbeeAdapter &other) const return m_systemLocation == other.systemLocation() && m_name == other.name() && m_description == other.description() - && m_backendSuggestionAvailable == other.backendSuggestionAvailable() - && m_suggestedZigbeeBackendType == other.suggestedZigbeeBackendType() - && m_suggestedBaudRate == other.suggestedBaudRate(); + && m_hardwareRecognized == other.hardwareRecognized() + && m_backendType == other.backendType() + && m_baudRate == other.baudRate(); } @@ -112,9 +112,9 @@ QDebug operator<<(QDebug debug, const ZigbeeAdapter &adapter) { debug.nospace() << "ZigbeeAdapter(" << adapter.name() << " - " << adapter.description(); debug.nospace() << ", " << adapter.systemLocation(); - if (adapter.backendSuggestionAvailable()) { - debug.nospace() << "Suggested backend: " << adapter.suggestedZigbeeBackendType(); - debug.nospace() << ", " << adapter.suggestedBaudRate(); + if (adapter.hardwareRecognized()) { + debug.nospace() << "Hardware recognized: " << adapter.backendType(); + debug.nospace() << ", " << adapter.baudRate(); } debug.nospace() << ")"; diff --git a/libnymea-core/zigbee/zigbeeadapter.h b/libnymea-core/zigbee/zigbeeadapter.h index 8ffe8618..08f88c18 100644 --- a/libnymea-core/zigbee/zigbeeadapter.h +++ b/libnymea-core/zigbee/zigbeeadapter.h @@ -44,11 +44,17 @@ class ZigbeeAdapter Q_PROPERTY(QString name READ name) Q_PROPERTY(QString description READ description) Q_PROPERTY(QString systemLocation READ systemLocation) - Q_PROPERTY(bool backendSuggestionAvailable READ backendSuggestionAvailable) - Q_PROPERTY(Zigbee::ZigbeeBackendType suggestedZigbeeBackendType READ suggestedZigbeeBackendType) - Q_PROPERTY(qint32 suggestedBaudRate READ suggestedBaudRate) + Q_PROPERTY(bool hardwareRecognized READ hardwareRecognized) + Q_PROPERTY(ZigbeeAdapter::ZigbeeBackendType backendType READ backendType) + Q_PROPERTY(qint32 baudRate READ baudRate) public: + enum ZigbeeBackendType { + ZigbeeBackendTypeDeconz, + ZigbeeBackendTypeNxp + }; + Q_ENUM(ZigbeeBackendType) + explicit ZigbeeAdapter(); QString name() const; @@ -60,14 +66,14 @@ public: QString systemLocation() const; void setSystemLocation(const QString &systemLocation); - bool backendSuggestionAvailable() const; - void setBackendSuggestionAvailable(bool backendSuggestionAvailable); + bool hardwareRecognized() const; + void setHardwareRecognized(bool hardwareRecognized); - Zigbee::ZigbeeBackendType suggestedZigbeeBackendType() const; - void setSuggestedZigbeeBackendType(Zigbee::ZigbeeBackendType backendType); + ZigbeeAdapter::ZigbeeBackendType backendType() const; + void setBackendType(ZigbeeAdapter::ZigbeeBackendType backendType); - qint32 suggestedBaudRate() const; - void setSuggestedBaudRate(qint32 baudRate); + qint32 baudRate() const; + void setBaudRate(qint32 baudRate); bool operator==(const ZigbeeAdapter &other) const; @@ -75,10 +81,9 @@ private: QString m_name; QString m_description; QString m_systemLocation; - - bool m_backendSuggestionAvailable = false; - Zigbee::ZigbeeBackendType m_suggestedZigbeeBackendType = Zigbee::ZigbeeBackendTypeDeconz; - qint32 m_suggestedBaudRate = 38400; + bool m_hardwareRecognized = false; + ZigbeeAdapter::ZigbeeBackendType m_backendType = ZigbeeAdapter::ZigbeeBackendTypeDeconz; + qint32 m_baudRate = 38400; }; QDebug operator<<(QDebug debug, const ZigbeeAdapter &adapter); diff --git a/libnymea-core/zigbee/zigbeemanager.cpp b/libnymea-core/zigbee/zigbeemanager.cpp index 7c635ea3..c3c29081 100644 --- a/libnymea-core/zigbee/zigbeemanager.cpp +++ b/libnymea-core/zigbee/zigbeemanager.cpp @@ -39,7 +39,8 @@ NYMEA_LOGGING_CATEGORY(dcZigbeeNetworkLibNymeaZigbee, "ZigbeeNetwork") namespace nymeaserver { -ZigbeeManager::ZigbeeManager(QObject *parent) : QObject(parent) +ZigbeeManager::ZigbeeManager(QObject *parent) : + QObject(parent) { // Adapter monitor qCDebug(dcZigbee()) << "Initialize the ZigBee manager"; @@ -49,12 +50,16 @@ ZigbeeManager::ZigbeeManager(QObject *parent) : QObject(parent) // Lets continue anyways, maybe we can set up existing networks right the way. } + qCDebug(dcZigbee()) << "Loading initial adapter list"; foreach(const ZigbeeUartAdapter &uartAdapter, m_adapterMonitor->availableAdapters()) { - m_adapters.append(createAdapterFromUartAdapter(uartAdapter)); + ZigbeeAdapter adapter = createAdapterFromUartAdapter(uartAdapter); + qCDebug(dcZigbee()) << "Adapter added" << adapter; + m_adapters.append(adapter); } connect(m_adapterMonitor, &ZigbeeUartAdapterMonitor::adapterAdded, this, [this](const ZigbeeUartAdapter &uartAdapter){ ZigbeeAdapter adapter = createAdapterFromUartAdapter(uartAdapter); + qCDebug(dcZigbee()) << "Adapter added" << adapter; m_adapters.append(adapter); emit availableAdapterAdded(adapter); }); @@ -62,6 +67,7 @@ ZigbeeManager::ZigbeeManager(QObject *parent) : QObject(parent) connect(m_adapterMonitor, &ZigbeeUartAdapterMonitor::adapterRemoved, this, [this](const ZigbeeUartAdapter &uartAdapter){ foreach (const ZigbeeAdapter &adapter, m_adapters) { if (adapter.systemLocation() == uartAdapter.systemLocation()) { + qCDebug(dcZigbee()) << "Adapter removed" << adapter; m_adapters.removeAll(adapter); emit availableAdapterRemoved(adapter); } @@ -69,47 +75,189 @@ ZigbeeManager::ZigbeeManager(QObject *parent) : QObject(parent) }); // Load zigbee networks from settings - NymeaSettings settings(NymeaSettings::SettingsRoleZigbee); - - + loadZigbeeNetworks(); // TODO: load platform configuration for networks we know for sure how they work } bool ZigbeeManager::available() const { - return m_zigbeeNetwork != nullptr; + return !m_zigbeeNetworks.isEmpty(); } bool ZigbeeManager::enabled() const { - return m_zigbeeNetwork && m_zigbeeNetwork->state() != ZigbeeNetwork::StateUninitialized; + return true;//m_zigbeeNetwork && m_zigbeeNetwork->state() != ZigbeeNetwork::StateUninitialized; } -ZigbeeNetwork *ZigbeeManager::zigbeeNetwork() const -{ - return m_zigbeeNetwork; -} - -ZigbeeAdapters ZigbeeManager::availableAdapters() +ZigbeeAdapters ZigbeeManager::availableAdapters() const { return m_adapters; } -void ZigbeeManager::createZigbeeNetwork(const QString &serialPort, qint32 baudrate, Zigbee::ZigbeeBackendType backend) +ZigbeeManager::ZigbeeError ZigbeeManager::createZigbeeNetwork(const ZigbeeAdapter &adapter, const ZigbeeChannelMask channelMask) { - if (m_zigbeeNetwork) { - delete m_zigbeeNetwork; - m_zigbeeNetwork = nullptr; + qCDebug(dcZigbee()) << "Start creating network for" << adapter << channelMask; + + // Make sure we don't have aleardy a network for this adapter + foreach (ZigbeeNetwork *existingNetwork, m_zigbeeNetworks.values()) { + if (existingNetwork->serialPortName() == adapter.systemLocation()) { + qCWarning(dcZigbee()) << "Failed to create a network for" << adapter << "which is already in use for network" << existingNetwork->networkUuid().toString(); + return ZigbeeManager::ZigbeeErrorAdapterAlreadyInUse; + } } - m_zigbeeNetwork = ZigbeeNetworkManager::createZigbeeNetwork(backend, this); - m_zigbeeNetwork->setSerialPortName(serialPort); - m_zigbeeNetwork->setSerialBaudrate(baudrate); - m_zigbeeNetwork->setSettingsFileName(NymeaSettings(NymeaSettings::SettingsRoleGlobal).fileName()); - m_zigbeeNetwork->startNetwork(); + if (!m_adapters.contains(adapter)) { + qCWarning(dcZigbee()) << "Failed to create a network for" << adapter << "because the adapter is not available any more"; + return ZigbeeManager::ZigbeeErrorAdapterNotAvailable; + } - emit zigbeeNetworkChanged(m_zigbeeNetwork); + ZigbeeNetwork *network = buildNetworkObject(QUuid::createUuid(), adapter.backendType()); + network->setChannelMask(channelMask); + network->setSerialPortName(adapter.systemLocation()); + network->setSerialBaudrate(adapter.baudRate()); + addNetwork(network); + + qCDebug(dcZigbee()) << "Starting zigbee network" << network->networkUuid().toString(); + network->startNetwork(); + + return ZigbeeErrorNoError; +} + +void ZigbeeManager::saveNetwork(ZigbeeNetwork *network) +{ + NymeaSettings settings(NymeaSettings::SettingsRoleZigbee); + settings.beginGroup("ZigbeeNetworks"); + settings.beginGroup(network->networkUuid().toString()); + settings.setValue("serialPort", network->serialPortName()); + settings.setValue("baudRate", network->serialBaudrate()); + switch (network->backendType()) { + case Zigbee::ZigbeeBackendTypeDeconz: + settings.setValue("backendType", static_cast(ZigbeeAdapter::ZigbeeBackendTypeDeconz)); + break; + case Zigbee::ZigbeeBackendTypeNxp: + settings.setValue("backendType", static_cast(ZigbeeAdapter::ZigbeeBackendTypeNxp)); + break; + default: + qCWarning(dcZigbee()) << "Unhandled backend type" << network->backendType() << "which is not implemented in nymea yet."; + break; + } + settings.setValue("panId", network->panId()); + settings.setValue("channel", network->channel()); + + settings.setValue("channelMask", network->channelMask().toUInt32()); + settings.setValue("networkKey", network->securityConfiguration().networkKey().toString()); + settings.setValue("trustCenterLinkKey", network->securityConfiguration().globalTrustCenterLinkKey().toString()); + + settings.endGroup(); // networkUuid + settings.endGroup(); // ZigbeeNetworks +} + +void ZigbeeManager::loadZigbeeNetworks() +{ + NymeaSettings settings(NymeaSettings::SettingsRoleZigbee); + settings.beginGroup("ZigbeeNetworks"); + + foreach (const QString networkUuidGroupString, settings.childGroups()) { + settings.beginGroup(networkUuidGroupString); + + QUuid networkUuid = QUuid(networkUuidGroupString); + QString serialPortName = settings.value("serialPort").toString(); + qint32 serialBaudRate = settings.value("baudRate").toInt(); + ZigbeeAdapter::ZigbeeBackendType backendType = static_cast(settings.value("backendType").toInt()); + quint16 panId = static_cast(settings.value("panId", 0).toUInt()); + quint8 channel = settings.value("channel", 0).toUInt(); + ZigbeeChannelMask channelMask = ZigbeeChannelMask(static_cast(settings.value("channelMask").toUInt())); + + ZigbeeSecurityConfiguration securityConfiguration; + ZigbeeNetworkKey netKey(settings.value("networkKey", QString()).toString()); + if (netKey.isValid()) { + securityConfiguration.setNetworkKey(netKey); + } + + ZigbeeNetworkKey tcKey(settings.value("trustCenterLinkKey", QString("5A6967426565416C6C69616E63653039")).toString()); + if (!tcKey.isValid()) { + securityConfiguration.setGlobalTrustCenterlinkKey(tcKey); + } + + ZigbeeNetwork *network = buildNetworkObject(networkUuid, backendType); + network->setSerialPortName(serialPortName); + network->setSerialBaudrate(serialBaudRate); + network->setPanId(panId); + network->setChannel(channel); + network->setChannelMask(channelMask); + network->setSecurityConfiguration(securityConfiguration); + addNetwork(network); + settings.endGroup(); // networkUuid + } + settings.endGroup(); // ZigbeeNetworks + + if (m_zigbeeNetworks.isEmpty()) { + qCDebug(dcZigbee()) << "There are no zigbee networks configured yet."; + } + + // Start all loaded networks + foreach (ZigbeeNetwork *network, m_zigbeeNetworks.values()) { + network->startNetwork(); + } +} + +ZigbeeNetwork *ZigbeeManager::buildNetworkObject(const QUuid &networkId, ZigbeeAdapter::ZigbeeBackendType backendType) +{ + ZigbeeNetwork *network = nullptr; + switch (backendType) { + case ZigbeeAdapter::ZigbeeBackendTypeDeconz: + network = ZigbeeNetworkManager::createZigbeeNetwork(networkId, Zigbee::ZigbeeBackendTypeDeconz, this); + break; + case ZigbeeAdapter::ZigbeeBackendTypeNxp: + network = ZigbeeNetworkManager::createZigbeeNetwork(networkId, Zigbee::ZigbeeBackendTypeNxp, this); + break; + } + network->setSettingsDirectory(QDir(NymeaSettings::settingsPath())); + return network; +} + +void ZigbeeManager::addNetwork(ZigbeeNetwork *network) +{ + connect(network, &ZigbeeNetwork::stateChanged, this, [this, network](ZigbeeNetwork::State state){ + qCDebug(dcZigbee()) << "Network state changed" << network->networkUuid().toString() << state; + + // TODO: set state of zigbee resource depending on the state + + emit zigbeeNetworkChanged(network); + }); + + connect(network, &ZigbeeNetwork::errorOccured, this, [network](ZigbeeNetwork::Error error){ + qCDebug(dcZigbee()) << "Network error occured for network" << network->networkUuid().toString() << error; + + // TODO: handle error + }); + + connect(network, &ZigbeeNetwork::panIdChanged, this, [this, network](quint16 panId){ + qCDebug(dcZigbee()) << "Network PAN ID changed" << network->networkUuid().toString() << panId; + saveNetwork(network); + emit zigbeeNetworkChanged(network); + }); + + connect(network, &ZigbeeNetwork::channelChanged, this, [this, network](quint8 channel){ + qCDebug(dcZigbee()) << "Network channel changed" << network->networkUuid().toString() << channel; + saveNetwork(network); + emit zigbeeNetworkChanged(network); + }); + + connect(network, &ZigbeeNetwork::securityConfigurationChanged, this, [this, network](const ZigbeeSecurityConfiguration &securityConfiguration){ + qCDebug(dcZigbee()) << "Network security configuration changed" << network->networkUuid().toString() << securityConfiguration.networkKey().toString(); + saveNetwork(network); + }); + + connect(network, &ZigbeeNetwork::channelMaskChanged, this, [this, network](const ZigbeeChannelMask &channelMask){ + qCDebug(dcZigbee()) << "Network channel mask changed" << network->networkUuid().toString() << channelMask; + saveNetwork(network); + emit zigbeeNetworkChanged(network); + }); + + m_zigbeeNetworks.insert(network->networkUuid(), network); + emit zigbeeNetworkAdded(network); } ZigbeeAdapter ZigbeeManager::createAdapterFromUartAdapter(const ZigbeeUartAdapter &uartAdapter) @@ -118,9 +266,19 @@ ZigbeeAdapter ZigbeeManager::createAdapterFromUartAdapter(const ZigbeeUartAdapte adapter.setName(uartAdapter.name()); adapter.setSystemLocation(uartAdapter.systemLocation()); adapter.setDescription(uartAdapter.description()); - adapter.setBackendSuggestionAvailable(uartAdapter.backendSuggestionAvailable()); - adapter.setSuggestedZigbeeBackendType(uartAdapter.suggestedZigbeeBackendType()); - adapter.setSuggestedBaudRate(uartAdapter.suggestedBaudRate()); + adapter.setHardwareRecognized(uartAdapter.backendSuggestionAvailable()); + adapter.setBaudRate(uartAdapter.suggestedBaudRate()); + switch (uartAdapter.suggestedZigbeeBackendType()) { + case Zigbee::ZigbeeBackendTypeDeconz: + adapter.setBackendType(ZigbeeAdapter::ZigbeeBackendTypeDeconz); + break; + case Zigbee::ZigbeeBackendTypeNxp: + adapter.setBackendType(ZigbeeAdapter::ZigbeeBackendTypeNxp); + break; + default: + qCWarning(dcZigbee()) << "Unhandled backend type" << uartAdapter.suggestedZigbeeBackendType() << "which is not implemented in nymea yet."; + break; + } return adapter; } diff --git a/libnymea-core/zigbee/zigbeemanager.h b/libnymea-core/zigbee/zigbeemanager.h index 7ec50dd8..b0c2c14a 100644 --- a/libnymea-core/zigbee/zigbeemanager.h +++ b/libnymea-core/zigbee/zigbeemanager.h @@ -44,12 +44,6 @@ class ZigbeeManager : public QObject { Q_OBJECT public: - enum ZigbeeBackendType { - ZigbeeBackendTypeDconz, - ZigbeeBackendTypeNxp - }; - Q_ENUM(ZigbeeBackendType) - enum ZigbeeNetworkState { ZigbeeNetworkStateOffline, ZigbeeNetworkStateUpdating, @@ -70,16 +64,21 @@ public: bool available() const; bool enabled() const; - ZigbeeNetwork *zigbeeNetwork() const; - ZigbeeAdapters availableAdapters(); + ZigbeeAdapters availableAdapters() const; - void createZigbeeNetwork(const QString &serialPort, qint32 baudrate, Zigbee::ZigbeeBackendType backend); + ZigbeeError createZigbeeNetwork(const ZigbeeAdapter &adapter, const ZigbeeChannelMask channelMask = ZigbeeChannelMask()); private: ZigbeeAdapters m_adapters; ZigbeeUartAdapterMonitor *m_adapterMonitor = nullptr; - ZigbeeNetwork *m_zigbeeNetwork = nullptr; + QHash m_zigbeeNetworks; + + void saveNetwork(ZigbeeNetwork *network); + void loadZigbeeNetworks(); + + ZigbeeNetwork *buildNetworkObject(const QUuid &networkId, ZigbeeAdapter::ZigbeeBackendType backendType); + void addNetwork(ZigbeeNetwork *network); ZigbeeAdapter createAdapterFromUartAdapter(const ZigbeeUartAdapter &uartAdapter); @@ -87,6 +86,8 @@ signals: void availableAdapterAdded(const ZigbeeAdapter &adapter); void availableAdapterRemoved(const ZigbeeAdapter &adapter); + void zigbeeNetworkAdded(ZigbeeNetwork *zigbeeNetwork); + void zigbeeNetworkRemoved(ZigbeeNetwork *zigbeeNetwork); void zigbeeNetworkChanged(ZigbeeNetwork *zigbeeNetwork); };