diff --git a/QtZeroConf b/QtZeroConf index 39efbe28..cbf4f2bf 160000 --- a/QtZeroConf +++ b/QtZeroConf @@ -1 +1 @@ -Subproject commit 39efbe2879d93bd483ee1f14fc94281a03b49935 +Subproject commit cbf4f2bf7b926b9a874af013efbf0f6dde3155e9 diff --git a/libnymea-app/engine.cpp b/libnymea-app/engine.cpp index e79b06eb..b26215cc 100644 --- a/libnymea-app/engine.cpp +++ b/libnymea-app/engine.cpp @@ -38,7 +38,6 @@ #include "connection/awsclient.h" #include "system/systemcontroller.h" #include "configuration/networkmanager.h" -#include "zigbee/zigbeemanager.h" Engine::Engine(QObject *parent) : QObject(parent), @@ -49,8 +48,7 @@ Engine::Engine(QObject *parent) : m_logManager(new LogManager(m_jsonRpcClient, this)), m_tagsManager(new TagsManager(m_jsonRpcClient, this)), m_nymeaConfiguration(new NymeaConfiguration(m_jsonRpcClient, this)), - m_systemController(new SystemController(m_jsonRpcClient, this)), - m_zigbeeManager(new ZigbeeManager(m_jsonRpcClient, this)) + m_systemController(new SystemController(m_jsonRpcClient, this)) { connect(m_jsonRpcClient, &JsonRpcClient::connectedChanged, this, &Engine::onConnectedChanged); @@ -119,11 +117,6 @@ SystemController *Engine::systemController() const return m_systemController; } -ZigbeeManager *Engine::zigbeeManager() const -{ - return m_zigbeeManager; -} - void Engine::deployCertificate() { if (!m_jsonRpcClient->connected()) { @@ -165,9 +158,5 @@ void Engine::onDeviceManagerFetchingChanged() if (m_jsonRpcClient->ensureServerVersion("1.7")) { m_tagsManager->init(); } - - if (m_jsonRpcClient->ensureServerVersion("5.1")) { - m_zigbeeManager->init(); - } } } diff --git a/libnymea-app/engine.h b/libnymea-app/engine.h index c81b2ef4..07b38c6a 100644 --- a/libnymea-app/engine.h +++ b/libnymea-app/engine.h @@ -45,7 +45,6 @@ class TagsManager; class NymeaConfiguration; class SystemController; class NetworkManager; -class ZigbeeManager; class Engine : public QObject { @@ -58,7 +57,6 @@ class Engine : public QObject Q_PROPERTY(JsonRpcClient* jsonRpcClient READ jsonRpcClient CONSTANT) Q_PROPERTY(NymeaConfiguration* nymeaConfiguration READ nymeaConfiguration CONSTANT) Q_PROPERTY(SystemController* systemController READ systemController CONSTANT) - Q_PROPERTY(ZigbeeManager *zigbeeManager READ zigbeeManager CONSTANT) public: explicit Engine(QObject *parent = nullptr); @@ -72,7 +70,6 @@ public: LogManager *logManager() const; NymeaConfiguration *nymeaConfiguration() const; SystemController *systemController() const; - ZigbeeManager *zigbeeManager() const; Q_INVOKABLE void deployCertificate(); @@ -85,7 +82,6 @@ private: TagsManager *m_tagsManager; NymeaConfiguration *m_nymeaConfiguration; SystemController *m_systemController; - ZigbeeManager *m_zigbeeManager; private slots: void onConnectedChanged(); diff --git a/libnymea-app/libnymea-app-core.h b/libnymea-app/libnymea-app-core.h index 89539b1c..279becaf 100644 --- a/libnymea-app/libnymea-app-core.h +++ b/libnymea-app/libnymea-app-core.h @@ -302,7 +302,7 @@ void registerQmlTypes() { qmlRegisterUncreatableType(uri, 1, 0, "Repository", "Get it from Repositories"); qmlRegisterType(uri, 1, 0, "PackagesFilterModel"); - qmlRegisterUncreatableType(uri, 1, 0, "ZigbeeManager", "Get it from Engine"); + qmlRegisterType(uri, 1, 0, "ZigbeeManager"); qmlRegisterUncreatableType(uri, 1, 0, "ZigbeeAdapter", "Get it from the ZigbeeAdapters"); qmlRegisterUncreatableType(uri, 1, 0, "ZigbeeAdapters", "Get it from ZigbeeManager"); qmlRegisterType(uri, 1, 0, "ZigbeeAdaptersProxy"); diff --git a/libnymea-app/libnymea-app.pro b/libnymea-app/libnymea-app.pro index b0cdc5b9..0ca71ae5 100644 --- a/libnymea-app/libnymea-app.pro +++ b/libnymea-app/libnymea-app.pro @@ -165,7 +165,8 @@ SOURCES += \ zigbee/zigbeemanager.cpp \ zigbee/zigbeeadapter.cpp \ zigbee/zigbeenetwork.cpp \ - zigbee/zigbeenetworks.cpp + zigbee/zigbeenetworks.cpp \ + zigbee/zigbeenetworksandadapters.cpp @@ -311,7 +312,8 @@ HEADERS += \ zigbee/zigbeemanager.h \ zigbee/zigbeeadapter.h \ zigbee/zigbeenetwork.h \ - zigbee/zigbeenetworks.h + zigbee/zigbeenetworks.h \ + zigbee/zigbeenetworksandadapters.h ubports: { DEFINES += UBPORTS diff --git a/libnymea-app/zigbee/zigbeeadapters.cpp b/libnymea-app/zigbee/zigbeeadapters.cpp index 190628dc..6f701f46 100644 --- a/libnymea-app/zigbee/zigbeeadapters.cpp +++ b/libnymea-app/zigbee/zigbeeadapters.cpp @@ -137,7 +137,7 @@ void ZigbeeAdapters::clear() ZigbeeAdapter *ZigbeeAdapters::get(int index) const { - if (index < 0 || index > m_adapters.count() - 1) { + if (index < 0 || index >= m_adapters.count()) { return nullptr; } diff --git a/libnymea-app/zigbee/zigbeeadaptersproxy.cpp b/libnymea-app/zigbee/zigbeeadaptersproxy.cpp index 9b365c30..8786a873 100644 --- a/libnymea-app/zigbee/zigbeeadaptersproxy.cpp +++ b/libnymea-app/zigbee/zigbeeadaptersproxy.cpp @@ -30,31 +30,36 @@ #include "zigbeeadaptersproxy.h" +#include "zigbeemanager.h" #include "zigbeeadapters.h" +#include "zigbeenetworks.h" ZigbeeAdaptersProxy::ZigbeeAdaptersProxy(QObject *parent) : QSortFilterProxyModel(parent) { } -ZigbeeAdapters *ZigbeeAdaptersProxy::adapters() const +ZigbeeManager *ZigbeeAdaptersProxy::manager() const { - return m_adapters; + return m_manager; } -void ZigbeeAdaptersProxy::setAdapters(ZigbeeAdapters *adapters) +void ZigbeeAdaptersProxy::setManager(ZigbeeManager *manager) { - m_adapters = adapters; - connect(m_adapters, &ZigbeeAdapters::countChanged, this, [this](){ + if (m_manager != manager) { + m_manager = manager; + connect(m_manager->adapters(), &ZigbeeAdapters::countChanged, this, [this](){ + emit countChanged(); + }); + connect(m_manager->networks(), &ZigbeeNetworks::countChanged, this, [this]() { + invalidateFilter(); + }); + setSourceModel(m_manager->adapters()); + setSortRole(ZigbeeAdapters::RoleSerialPort); + sort(0, Qt::DescendingOrder); invalidateFilter(); emit countChanged(); - }); - - setSourceModel(m_adapters); - setSortRole(ZigbeeAdapters::RoleSerialPort); - sort(0, Qt::DescendingOrder); - invalidateFilter(); - emit countChanged(); + } } ZigbeeAdaptersProxy::HardwareFilter ZigbeeAdaptersProxy::hardwareFilter() const @@ -64,30 +69,57 @@ ZigbeeAdaptersProxy::HardwareFilter ZigbeeAdaptersProxy::hardwareFilter() const void ZigbeeAdaptersProxy::setHardwareFilter(ZigbeeAdaptersProxy::HardwareFilter hardwareFilter) { - m_hardwareFilter = hardwareFilter; - emit hardwareFilterChanged(m_hardwareFilter); + if (m_hardwareFilter != hardwareFilter) { + m_hardwareFilter = hardwareFilter; + emit hardwareFilterChanged(m_hardwareFilter); - invalidateFilter(); - emit countChanged(); + invalidateFilter(); + emit countChanged(); + } +} + +bool ZigbeeAdaptersProxy::onlyUnused() const +{ + return m_onlyUnused; +} + +void ZigbeeAdaptersProxy::setOnlyUnused(bool onlyUnused) +{ + if (m_onlyUnused != onlyUnused) { + m_onlyUnused = onlyUnused; + emit onlyUnusedChanged(); + + invalidateFilter(); + emit countChanged(); + } } ZigbeeAdapter *ZigbeeAdaptersProxy::get(int index) const { - return m_adapters->get(mapToSource(this->index(index, 0)).row()); + if (index >= 0 && index < m_manager->adapters()->rowCount()) { + return m_manager->adapters()->get(mapToSource(this->index(index, 0)).row()); + } + return nullptr; } bool ZigbeeAdaptersProxy::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { Q_UNUSED(source_parent) - ZigbeeAdapter *adapter = m_adapters->get(source_row); - if (!adapter) - return false; + ZigbeeAdapter *adapter = m_manager->adapters()->get(source_row); - if (m_hardwareFilter == HardwareFilterRecognized) { - return adapter->hardwareRecognized(); - } else if (m_hardwareFilter == HardwareFilterUnrecognized) { - return !adapter->hardwareRecognized(); + if (m_hardwareFilter == HardwareFilterRecognized && !adapter->hardwareRecognized()) { + return false; + } + + if (m_hardwareFilter == HardwareFilterUnrecognized && adapter->hardwareRecognized()) { + return false; + } + + if (m_onlyUnused) { + if (m_manager->networks()->findBySerialPort(adapter->serialPort()) != nullptr) { + return false; + } } return true; diff --git a/libnymea-app/zigbee/zigbeeadaptersproxy.h b/libnymea-app/zigbee/zigbeeadaptersproxy.h index 44fa53d4..7005d7eb 100644 --- a/libnymea-app/zigbee/zigbeeadaptersproxy.h +++ b/libnymea-app/zigbee/zigbeeadaptersproxy.h @@ -35,14 +35,20 @@ #include class ZigbeeAdapter; -class ZigbeeAdapters; +class ZigbeeManager; class ZigbeeAdaptersProxy : public QSortFilterProxyModel { Q_OBJECT Q_PROPERTY(int count READ rowCount NOTIFY countChanged) - Q_PROPERTY(ZigbeeAdapters * adapters READ adapters WRITE setAdapters NOTIFY adaptersChanged) + + // Required + Q_PROPERTY(ZigbeeManager* manager READ manager WRITE setManager NOTIFY managerChanged) + + // Optional filtering Q_PROPERTY(ZigbeeAdaptersProxy::HardwareFilter hardwareFilter READ hardwareFilter WRITE setHardwareFilter NOTIFY hardwareFilterChanged) + Q_PROPERTY(bool onlyUnused READ onlyUnused WRITE setOnlyUnused NOTIFY onlyUnusedChanged) + public: enum HardwareFilter { HardwareFilterNone, @@ -53,12 +59,15 @@ public: explicit ZigbeeAdaptersProxy(QObject *parent = nullptr); - ZigbeeAdapters *adapters() const; - void setAdapters(ZigbeeAdapters *adapters); + ZigbeeManager *manager() const; + void setManager(ZigbeeManager *manager); HardwareFilter hardwareFilter() const; void setHardwareFilter(HardwareFilter hardwareFilter); + bool onlyUnused() const; + void setOnlyUnused(bool onlyUnused); + Q_INVOKABLE ZigbeeAdapter* get(int index) const; protected: @@ -66,13 +75,14 @@ protected: signals: void countChanged(); - void adaptersChanged(); + void managerChanged(); void hardwareFilterChanged(HardwareFilter hardwareFilter); + void onlyUnusedChanged(); private: - ZigbeeAdapters *m_adapters = nullptr; + ZigbeeManager *m_manager = nullptr; HardwareFilter m_hardwareFilter = HardwareFilterNone; - + bool m_onlyUnused = false; }; #endif // ZIGBEEADAPTERSPROXY_H diff --git a/libnymea-app/zigbee/zigbeemanager.cpp b/libnymea-app/zigbee/zigbeemanager.cpp index 5c114dff..b967d339 100644 --- a/libnymea-app/zigbee/zigbeemanager.cpp +++ b/libnymea-app/zigbee/zigbeemanager.cpp @@ -30,23 +30,24 @@ #include "zigbeemanager.h" +#include "engine.h" #include "jsonrpc/jsonrpcclient.h" #include "zigbee/zigbeeadapter.h" #include "zigbee/zigbeeadapters.h" #include "zigbee/zigbeenetwork.h" #include "zigbee/zigbeenetworks.h" +#include "zigbeenetworksandadapters.h" #include -ZigbeeManager::ZigbeeManager(JsonRpcClient *client, QObject *parent) : +ZigbeeManager::ZigbeeManager(QObject *parent) : JsonHandler(parent), - m_client(client), m_adapters(new ZigbeeAdapters(this)), - m_networks(new ZigbeeNetworks(this)) + m_networks(new ZigbeeNetworks(this)), + m_networksAndAdapters(new ZigbeeNetworksAndAdapters(this)) { qRegisterMetaType(); - client->registerNotificationHandler(this, "notificationReceived"); } ZigbeeManager::~ZigbeeManager() @@ -59,6 +60,20 @@ QString ZigbeeManager::nameSpace() const return "Zigbee"; } +void ZigbeeManager::setEngine(Engine *engine) +{ + if (m_engine != engine) { + m_engine = engine; + emit engineChanged(); + init(); + } +} + +Engine *ZigbeeManager::engine() const +{ + return m_engine; +} + ZigbeeAdapters *ZigbeeManager::adapters() const { return m_adapters; @@ -69,7 +84,12 @@ ZigbeeNetworks *ZigbeeManager::networks() const return m_networks; } -void ZigbeeManager::addNetwork(const QString &serialPort, uint baudRate, ZigbeeAdapter::ZigbeeBackendType backendType) +ZigbeeNetworksAndAdapters *ZigbeeManager::networksAndAdapters() const +{ + return m_networksAndAdapters; +} + +int ZigbeeManager::addNetwork(const QString &serialPort, uint baudRate, ZigbeeAdapter::ZigbeeBackendType backendType) { QVariantMap params; params.insert("serialPort", serialPort); @@ -78,7 +98,7 @@ void ZigbeeManager::addNetwork(const QString &serialPort, uint baudRate, ZigbeeA params.insert("backendType", metaEnum.valueToKey(backendType)); qDebug() << "Add zigbee network" << params; - m_client->sendCommand("Zigbee.AddNetwork", params, this, "addNetworkResponse"); + return m_engine->jsonRpcClient()->sendCommand("Zigbee.AddNetwork", params, this, "addNetworkResponse"); } void ZigbeeManager::removeNetwork(const QUuid &networkUuid) @@ -86,7 +106,7 @@ void ZigbeeManager::removeNetwork(const QUuid &networkUuid) QVariantMap params; params.insert("networkUuid", networkUuid); qDebug() << "Remove zigbee network" << params; - m_client->sendCommand("Zigbee.RemoveNetwork", params, this, "removeNetworkResponse"); + m_engine->jsonRpcClient()->sendCommand("Zigbee.RemoveNetwork", params, this, "removeNetworkResponse"); } void ZigbeeManager::setPermitJoin(const QUuid &networkUuid, uint duration) @@ -94,23 +114,25 @@ void ZigbeeManager::setPermitJoin(const QUuid &networkUuid, uint duration) QVariantMap params; params.insert("networkUuid", networkUuid); params.insert("duration", duration); - m_client->sendCommand("Zigbee.SetPermitJoin", params, this, "setPermitJoinResponse"); + m_engine->jsonRpcClient()->sendCommand("Zigbee.SetPermitJoin", params, this, "setPermitJoinResponse"); } void ZigbeeManager::factoryResetNetwork(const QUuid &networkUuid) { QVariantMap params; params.insert("networkUuid", networkUuid); - m_client->sendCommand("Zigbee.FactoryResetNetwork", params, this, "factoryResetNetworkResponse"); + m_engine->jsonRpcClient()->sendCommand("Zigbee.FactoryResetNetwork", params, this, "factoryResetNetworkResponse"); } void ZigbeeManager::init() { - // FIXME: load only when used m_adapters->clear(); m_networks->clear(); - m_client->sendCommand("Zigbee.GetAdapters", this, "getAdaptersResponse"); - m_client->sendCommand("Zigbee.GetNetworks", this, "getNetworksResponse"); + + m_engine->jsonRpcClient()->registerNotificationHandler(this, "notificationReceived"); + + m_engine->jsonRpcClient()->sendCommand("Zigbee.GetAdapters", this, "getAdaptersResponse"); + m_engine->jsonRpcClient()->sendCommand("Zigbee.GetNetworks", this, "getNetworksResponse"); } void ZigbeeManager::getAdaptersResponse(int commandId, const QVariantMap ¶ms) @@ -123,6 +145,15 @@ void ZigbeeManager::getAdaptersResponse(int commandId, const QVariantMap ¶ms qDebug() << "Zigbee adapter added" << adapter->description() << adapter->serialPort() << adapter->hardwareRecognized(); m_adapters->addAdapter(adapter); } + + ZigbeeAdapter *fakeAdapter = new ZigbeeAdapter(); + fakeAdapter->setSerialPort("/dev/fake"); + fakeAdapter->setBackendType(ZigbeeAdapter::ZigbeeBackendTypeDeconz); + fakeAdapter->setBaudRate(9600); + fakeAdapter->setDescription("Fake adapter"); + fakeAdapter->setHardwareRecognized(true); + fakeAdapter->setName("Fake"); + m_adapters->addAdapter(fakeAdapter); } void ZigbeeManager::getNetworksResponse(int commandId, const QVariantMap ¶ms) @@ -140,6 +171,7 @@ void ZigbeeManager::getNetworksResponse(int commandId, const QVariantMap ¶ms void ZigbeeManager::addNetworkResponse(int commandId, const QVariantMap ¶ms) { qDebug() << "Zigbee add network response" << commandId << params; + emit addNetworkReply(commandId, params.value("zigbeeError").toString(), params.value("networkUuid").toUuid()); } void ZigbeeManager::removeNetworkResponse(int commandId, const QVariantMap ¶ms) diff --git a/libnymea-app/zigbee/zigbeemanager.h b/libnymea-app/zigbee/zigbeemanager.h index e09c61ed..73cbff98 100644 --- a/libnymea-app/zigbee/zigbeemanager.h +++ b/libnymea-app/zigbee/zigbeemanager.h @@ -39,32 +39,44 @@ class JsonRpcClient; class ZigbeeAdapters; class ZigbeeNetwork; class ZigbeeNetworks; +class ZigbeeNetworksAndAdapters; +class Engine; class ZigbeeManager : public JsonHandler { Q_OBJECT + Q_PROPERTY(Engine* engine READ engine WRITE setEngine NOTIFY engineChanged) + Q_PROPERTY(ZigbeeAdapters *adapters READ adapters CONSTANT) Q_PROPERTY(ZigbeeNetworks *networks READ networks CONSTANT) + Q_PROPERTY(ZigbeeNetworksAndAdapters *networksAndAdapters READ networksAndAdapters CONSTANT) public: - explicit ZigbeeManager(JsonRpcClient* client, QObject *parent = nullptr); + explicit ZigbeeManager(QObject *parent = nullptr); ~ZigbeeManager(); QString nameSpace() const override; + void setEngine(Engine *engine); + Engine *engine() const; + ZigbeeAdapters *adapters() const; ZigbeeNetworks *networks() const; + ZigbeeNetworksAndAdapters *networksAndAdapters() const; - Q_INVOKABLE void addNetwork(const QString &serialPort, uint baudRate, ZigbeeAdapter::ZigbeeBackendType backendType); + Q_INVOKABLE int addNetwork(const QString &serialPort, uint baudRate, ZigbeeAdapter::ZigbeeBackendType backendType); Q_INVOKABLE void removeNetwork(const QUuid &networkUuid); Q_INVOKABLE void setPermitJoin(const QUuid &networkUuid, uint duration = 120); Q_INVOKABLE void factoryResetNetwork(const QUuid &networkUuid); - void init(); - signals: + void engineChanged(); + + void addNetworkReply(int commandId, const QString &error, const QUuid &networkUuid); private: + void init(); + Q_INVOKABLE void getAdaptersResponse(int commandId, const QVariantMap ¶ms); Q_INVOKABLE void getNetworksResponse(int commandId, const QVariantMap ¶ms); @@ -73,13 +85,13 @@ private: Q_INVOKABLE void setPermitJoinResponse(int commandId, const QVariantMap ¶ms); Q_INVOKABLE void factoryResetNetworkResponse(int commandId, const QVariantMap ¶ms); - Q_INVOKABLE void notificationReceived(const QVariantMap ¬ification); private: - JsonRpcClient* m_client = nullptr; + Engine* m_engine = nullptr; ZigbeeAdapters *m_adapters = nullptr; ZigbeeNetworks *m_networks = nullptr; + ZigbeeNetworksAndAdapters *m_networksAndAdapters = nullptr; ZigbeeAdapter *unpackAdapter(const QVariantMap &adapterMap); ZigbeeNetwork *unpackNetwork(const QVariantMap &networkMap); diff --git a/libnymea-app/zigbee/zigbeenetworks.cpp b/libnymea-app/zigbee/zigbeenetworks.cpp index 41a3ec62..66cfb5ed 100644 --- a/libnymea-app/zigbee/zigbeenetworks.cpp +++ b/libnymea-app/zigbee/zigbeenetworks.cpp @@ -177,6 +177,7 @@ void ZigbeeNetworks::removeNetwork(const QUuid &networkUuid) beginRemoveRows(QModelIndex(), i, i); m_networks.takeAt(i)->deleteLater(); endRemoveRows(); + emit countChanged(); return; } } @@ -193,7 +194,7 @@ void ZigbeeNetworks::clear() ZigbeeNetwork *ZigbeeNetworks::get(int index) const { - if (index < 0 || index > m_networks.count() - 1) { + if (index < 0 || index >= m_networks.count()) { return nullptr; } return m_networks.at(index); @@ -209,3 +210,13 @@ ZigbeeNetwork *ZigbeeNetworks::getNetwork(const QUuid &networkUuid) const return nullptr; } + +ZigbeeNetwork *ZigbeeNetworks::findBySerialPort(const QString &serialPort) const +{ + foreach (ZigbeeNetwork* network, m_networks) { + if (network->serialPort() == serialPort) { + return network; + } + } + return nullptr; +} diff --git a/libnymea-app/zigbee/zigbeenetworks.h b/libnymea-app/zigbee/zigbeenetworks.h index 3a6376ff..1c71a68f 100644 --- a/libnymea-app/zigbee/zigbeenetworks.h +++ b/libnymea-app/zigbee/zigbeenetworks.h @@ -73,6 +73,7 @@ public: Q_INVOKABLE virtual ZigbeeNetwork *get(int index) const; Q_INVOKABLE ZigbeeNetwork *getNetwork(const QUuid &networkUuid) const; + ZigbeeNetwork *findBySerialPort(const QString &serialPort) const; signals: void countChanged(); diff --git a/libnymea-app/zigbee/zigbeenetworksandadapters.cpp b/libnymea-app/zigbee/zigbeenetworksandadapters.cpp new file mode 100644 index 00000000..311fe652 --- /dev/null +++ b/libnymea-app/zigbee/zigbeenetworksandadapters.cpp @@ -0,0 +1,21 @@ +#include "zigbeenetworksandadapters.h" + +#include "zigbeemanager.h" + +ZigbeeNetworksAndAdapters::ZigbeeNetworksAndAdapters(ZigbeeManager *manager): + QAbstractListModel(manager), + m_manager(manager) +{ + +} + +int ZigbeeNetworksAndAdapters::rowCount(const QModelIndex &/*parent*/) const +{ +// return m_list.count(); + return 0; +} + +QVariant ZigbeeNetworksAndAdapters::data(const QModelIndex &index, int role) const +{ + return QVariant(); +} diff --git a/libnymea-app/zigbee/zigbeenetworksandadapters.h b/libnymea-app/zigbee/zigbeenetworksandadapters.h new file mode 100644 index 00000000..9977555d --- /dev/null +++ b/libnymea-app/zigbee/zigbeenetworksandadapters.h @@ -0,0 +1,26 @@ +#ifndef ZIGBEENETWORKSANDADAPTERS_H +#define ZIGBEENETWORKSANDADAPTERS_H + +#include + +class ZigbeeManager; + +class ZigbeeNetworksAndAdapters : public QAbstractListModel +{ + Q_OBJECT + +public: + explicit ZigbeeNetworksAndAdapters(ZigbeeManager *manager); + + int rowCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + +signals: + +private: + ZigbeeManager *m_manager = nullptr; + +// QList< +}; + +#endif // ZIGBEENETWORKSANDADAPTERS_H diff --git a/nymea-app/resources.qrc b/nymea-app/resources.qrc index c81ba76a..8e01e89b 100644 --- a/nymea-app/resources.qrc +++ b/nymea-app/resources.qrc @@ -227,6 +227,8 @@ ui/components/ThingInfoPane.qml ui/system/ZigbeeNetworkSettingsPage.qml ui/system/ZigbeeNetworkAddPage.qml + ui/system/ZigbeeSettingsPage.qml + ui/system/ZigbeeAddNetworkPage.qml ui/system/ZigbeeNetworkPage.qml ui/system/ZigbeeNetworkInfoPage.qml diff --git a/nymea-app/ui/SettingsPage.qml b/nymea-app/ui/SettingsPage.qml index 3ab74e44..023a40d1 100644 --- a/nymea-app/ui/SettingsPage.qml +++ b/nymea-app/ui/SettingsPage.qml @@ -156,17 +156,17 @@ Page { Pane { Layout.fillWidth: true Material.elevation: layout.isGrid ? 1 : 0 - visible: engine.jsonRpcClient.ensureServerVersion("1.11") + visible: engine.jsonRpcClient.ensureServerVersion("5.2") padding: 0 NymeaListItemDelegate { width: parent.width iconName: "../images/zigbee.svg" text: qsTr("Zigbee") - subText: qsTr("Manage and configure Zigbee networks") + subText: qsTr("Configure Zigbee networks") prominentSubText: false wrapTexts: false - onClicked: pageStack.push(Qt.resolvedUrl("system/ZigbeeNetworkSettingsPage.qml")) + onClicked: pageStack.push(Qt.resolvedUrl("system/ZigbeeSettingsPage.qml")) } } diff --git a/nymea-app/ui/system/NetworkSettingsPage.qml b/nymea-app/ui/system/NetworkSettingsPage.qml index 42da6468..f7865de5 100644 --- a/nymea-app/ui/system/NetworkSettingsPage.qml +++ b/nymea-app/ui/system/NetworkSettingsPage.qml @@ -132,6 +132,8 @@ SettingsPageBase { RowLayout { Layout.topMargin: app.margins * 6 + Layout.leftMargin: app.margins + Layout.rightMargin: app.margins visible: !networkManager.available && !networkManager.loading spacing: app.margins ColorIcon { diff --git a/nymea-app/ui/system/ZigbeeAddNetworkPage.qml b/nymea-app/ui/system/ZigbeeAddNetworkPage.qml new file mode 100644 index 00000000..ad1b9379 --- /dev/null +++ b/nymea-app/ui/system/ZigbeeAddNetworkPage.qml @@ -0,0 +1,218 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* 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 General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free Software +* Foundation, GNU 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 General +* Public License for more details. +* +* You should have received a copy of the GNU 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 +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import QtQuick 2.9 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 +import Nymea 1.0 + +import "../components" + +SettingsPageBase { + id: root + title: qsTr("Add a new Zigbee network") + busy: d.pendingCommandId != -1 + + property ZigbeeManager zigbeeManager: null + + + QtObject { + id: d + property int pendingCommandId: -1 + + function addNetwork(serialPort, baudRate, backendType) { + d.pendingCommandId = root.zigbeeManager.addNetwork(serialPort, baudRate, backendType) + } + } + + + Connections { + target: root.zigbeeManager + onAddNetworkReply: { + if (commandId == d.pendingCommandId) { + d.pendingCommandId = -1 + var props = {}; + switch (error) { + case "ZigbeeErrorNoError": + pageStack.pop(); + return; + case "ZigbeeErrorAdapterNotAvailable": + props.text = qsTr("The selected adapter is not available or the selected serial port configration is incorrect."); + break; + case "ZigbeeErrorAdapterAlreadyInUse": + props.text = qsTr("The selected adapter is already in use."); + break; + default: + props.errorCode = error; + } + var comp = Qt.createComponent("../components/ErrorDialog.qml") + var popup = comp.createObject(app, props) + popup.open(); + } + } + } + + SettingsPageSectionHeader { + text: qsTr("Hardware not available") + visible: root.zigbeeManager.adapters.count == 0 + } + + RowLayout { + Layout.leftMargin: app.margins + Layout.rightMargin: app.margins + visible: root.zigbeeManager.adapters.count == 0 + spacing: app.margins + ColorIcon { + Layout.preferredHeight: app.iconSize + Layout.preferredWidth: app.iconSize + name: "../images/connections/network-wifi-offline.svg" + } + Label { + Layout.fillWidth: true + wrapMode: Text.WordWrap + text: qsTr("No Zigbee adapters or serial ports are available on this system. Connect a Zigbee adapter via USB or UART serial port.") + } + } + + SettingsPageSectionHeader { + text: qsTr("Available Zigbee adapters") + visible: recognizedAdapters.count > 0 + } + + Label { + Layout.fillWidth: true + Layout.leftMargin: app.margins; Layout.rightMargin: app.margins + text: qsTr("Please select the Zigbee adapter on which the new network will be created.") + font.pixelSize: app.smallFont + wrapMode: Text.WordWrap + visible: recognizedAdapters.count > 0 + } + + + Repeater { + id: recognizedRepeater + model: ZigbeeAdaptersProxy { + id: recognizedAdapters + manager: root.zigbeeManager + hardwareFilter: ZigbeeAdaptersProxy.HardwareFilterRecognized + onlyUnused: true + } + + delegate: NymeaListItemDelegate { + Layout.fillWidth: true + iconName: "../images/zigbee.svg" + text: model.name + " - " + model.description + " - " + model.serialPort + onClicked: d.addNetwork(model.serialPort, model.baudRate, model.backendType) + } + } + + SettingsPageSectionHeader { + text: qsTr("Available serial ports") + visible: serialPorts.count > 0 + } + + Label { + Layout.fillWidth: true; Layout.leftMargin: app.margins; Layout.rightMargin: app.margins + wrapMode: Text.WordWrap + font.pixelSize: app.smallFont + text: qsTr("Please verify that the Zigbee adapter is properly connected to a serial port and select the appropriate port.") + visible: serialPorts.count > 0 + } + + Repeater { + id: unrecognizedRepeater + model: ZigbeeAdaptersProxy { + id: serialPorts + manager: root.zigbeeManager + hardwareFilter: ZigbeeAdaptersProxy.HardwareFilterUnrecognized + onlyUnused: true + } + + delegate: NymeaListItemDelegate { + Layout.fillWidth: true + property ZigbeeAdapter adapter: root.zigbeeManager.adapters.get(index) + iconName: "../images/stock_usb.svg" + text: model.description + " - " + model.serialPort + onClicked: { + var dialog = serialPortOptionsDialogComponent.createObject(app, {serialPort: model.serialPort, baudRate: model.baudRate}) + dialog.open() + } + } + } + + + Component { + id: serialPortOptionsDialogComponent + MeaDialog { + id: serialPortOptionsDialog + property string serialPort + property int baudRate + + headerIcon: "../images/stock_usb.svg" + title: qsTr("Serial port options") + text: qsTr("Please select the serial port options for using the Zigbee adapter") + standardButtons: Dialog.Ok | Dialog.Cancel + + RowLayout { + Label { + text: qsTr("Adapter") + Layout.fillWidth: true + } + ComboBox { + id: backendComboBox + model: ListModel { + id: backendModel + ListElement { displayName: "deConz"; backendValue: ZigbeeAdapter.ZigbeeBackendTypeDeconz } + ListElement { displayName: "NXP"; backendValue: ZigbeeAdapter.ZigbeeBackendTypeNxp } + } + textRole: "displayName" + } + } + + RowLayout { + Label { + text: qsTr("Baud rate") + Layout.fillWidth: true + } + ComboBox { + id: baudRateComboBox + model: [300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 57600] + Component.onCompleted: { + currentIndex = indexOfValue(serialPortOptionsDialog.baudRate) + } + } + } + + onAccepted: { + d.addNetwork(serialPortOptionsDialog.serialPort, baudRateComboBox.currentValue, backendModel.get(backendComboBox.currentIndex).backendValue) + } + } + } +} diff --git a/nymea-app/ui/system/ZigbeeNetworkAddPage.qml b/nymea-app/ui/system/ZigbeeNetworkAddPage.qml deleted file mode 100644 index 2c97a356..00000000 --- a/nymea-app/ui/system/ZigbeeNetworkAddPage.qml +++ /dev/null @@ -1,91 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* 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 General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the -* terms of the GNU General Public License as published by the Free Software -* Foundation, GNU 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 General -* Public License for more details. -* -* You should have received a copy of the GNU 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 -* -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -import QtQuick 2.9 -import QtQuick.Controls 2.2 -import QtQuick.Layouts 1.3 -import Nymea 1.0 - -import "../components" - -SettingsPageBase { - id: root - title: qsTr("Add a new Zigbee network") - - - - SettingsPageSectionHeader { - text: qsTr("Zigbee adapters") - } - - Repeater { - id: recognizedRepeater - model: ZigbeeAdaptersProxy { - adapters: engine.zigbeeManager.adapters - hardwareFilter: ZigbeeAdaptersProxy.HardwareFilterRecognized - } - - delegate: NymeaListItemDelegate { - Layout.fillWidth: true - property ZigbeeAdapter adapter: engine.zigbeeManager.adapters.get(index) - iconName: "../images/stock_usb.svg" - progressive: false - text: model.description + " - " + model.serialPort - onClicked: { - engine.zigbeeManager.addNetwork(adapter.serialPort, adapter.baudRate, adapter.backendType) - pageStack.pop() - } - } - } - - SettingsPageSectionHeader { - text: qsTr("Unrecognized Zigbee adapters") - visible: unrecognizedRepeater.count !== 0 - } - - Repeater { - id: unrecognizedRepeater - model: ZigbeeAdaptersProxy { - adapters: engine.zigbeeManager.adapters - hardwareFilter: ZigbeeAdaptersProxy.HardwareFilterUnrecognized - } - - delegate: NymeaListItemDelegate { - Layout.fillWidth: true - property ZigbeeAdapter adapter: engine.zigbeeManager.adapters.get(index) - iconName: "../images/stock_usb.svg" - text: model.description + " - " + model.serialPort - // TODO: show backend and baudrate popup before adding - onClicked: engine.zigbeeManager.addNetwork(adapter.serialPort, adapter.baudRate, adapter.backendType) - } - } - - -} diff --git a/nymea-app/ui/system/ZigbeeNetworkInfoPage.qml b/nymea-app/ui/system/ZigbeeNetworkInfoPage.qml index 226f40f1..512d1517 100644 --- a/nymea-app/ui/system/ZigbeeNetworkInfoPage.qml +++ b/nymea-app/ui/system/ZigbeeNetworkInfoPage.qml @@ -38,6 +38,8 @@ import Nymea 1.0 SettingsPageBase { id: root + property ZigbeeManager zigbeeManager: null + property ZigbeeNetwork network: null header: NymeaHeader { @@ -103,7 +105,7 @@ SettingsPageBase { Layout.rightMargin: app.margins text: qsTr("Remove network") onClicked: { - engine.zigbeeManager.removeNetwork(root.network.networkUuid) + root.zigbeeManager.removeNetwork(root.network.networkUuid) pageStack.pop() pageStack.pop() } diff --git a/nymea-app/ui/system/ZigbeeNetworkPage.qml b/nymea-app/ui/system/ZigbeeNetworkPage.qml index 3848a6e5..10aad8c0 100644 --- a/nymea-app/ui/system/ZigbeeNetworkPage.qml +++ b/nymea-app/ui/system/ZigbeeNetworkPage.qml @@ -38,6 +38,7 @@ import Nymea 1.0 SettingsPageBase { id: root + property ZigbeeManager zigbeeManager: null property ZigbeeNetwork network: null header: NymeaHeader { @@ -48,7 +49,7 @@ SettingsPageBase { HeaderButton { text: qsTr("Settings") imageSource: "../images/settings.svg" - onClicked: pageStack.push(Qt.resolvedUrl("ZigbeeNetworkInfoPage.qml"), { network: root.network }) + onClicked: pageStack.push(Qt.resolvedUrl("ZigbeeNetworkInfoPage.qml"), { network: root.network, zigbeeManager: root.zigbeeManager }) } } @@ -104,7 +105,6 @@ SettingsPageBase { } ColumnLayout { - anchors { left: parent.left; right: parent.right } ProgressBar { Layout.fillWidth: true @@ -122,7 +122,7 @@ SettingsPageBase { Layout.rightMargin: app.margins enabled: network.networkState === ZigbeeNetwork.ZigbeeNetworkStateOnline text: root.network.permitJoiningEnabled ? qsTr("Extend network open duration") : qsTr("Open network for new Zigbee devices") - onClicked: engine.zigbeeManager.setPermitJoin(root.network.networkUuid) + onClicked: root.zigbeeManager.setPermitJoin(root.network.networkUuid) } Button { @@ -131,7 +131,7 @@ SettingsPageBase { Layout.rightMargin: app.margins visible: network.networkState === ZigbeeNetwork.ZigbeeNetworkStateOnline && root.network.permitJoiningEnabled text: qsTr("Close network") - onClicked: engine.zigbeeManager.setPermitJoin(root.network.networkUuid, 0) + onClicked: root.zigbeeManager.setPermitJoin(root.network.networkUuid, 0) } } diff --git a/nymea-app/ui/system/ZigbeeNetworkSettingsPage.qml b/nymea-app/ui/system/ZigbeeNetworkSettingsPage.qml deleted file mode 100644 index c1f64411..00000000 --- a/nymea-app/ui/system/ZigbeeNetworkSettingsPage.qml +++ /dev/null @@ -1,86 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* 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 General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the -* terms of the GNU General Public License as published by the Free Software -* Foundation, GNU 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 General -* Public License for more details. -* -* You should have received a copy of the GNU 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 -* -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -import QtQuick 2.8 -import QtQuick.Controls 2.2 -import QtQuick.Controls.Material 2.1 -import QtQuick.Layouts 1.3 -import "../components" -import Nymea 1.0 - -SettingsPageBase { - id: root - header: NymeaHeader { - text: qsTr("Zigbee") - backButtonVisible: true - onBackPressed: pageStack.pop() - - // TODO: create a settings page where zigbee networking can be enabled/disabled in general - // HeaderButton { - // text: qsTr("Settings") - // imageSource: "../images/settings.svg" - // onClicked: { - // //pageStack.push() - // } - // } - } - - ColumnLayout { - id: contentColumn - anchors.fill: parent - - // List networks - SettingsPageSectionHeader { - text: qsTr("Zigbee networks") - } - - Repeater { - model: engine.zigbeeManager.networks - delegate: NymeaListItemDelegate { - Layout.fillWidth: true - property var network: engine.zigbeeManager.networks.get(index) - iconName: "../images/zigbee.svg" - text: model.macAddress - subText: model.serialPort + " - " + model.firmwareVersion - onClicked: pageStack.push(Qt.resolvedUrl("ZigbeeNetworkPage.qml"), { network: network }) - } - } - - NymeaListItemDelegate { - Layout.fillWidth: true - text: qsTr("Add Zigbee network") - subText: qsTr("Set up a new Zigbee network") - prominentSubText: false - onClicked: { - pageStack.push(Qt.resolvedUrl("../system/ZigbeeNetworkAddPage.qml")) - } - } - } -} diff --git a/nymea-app/ui/system/ZigbeeSettingsPage.qml b/nymea-app/ui/system/ZigbeeSettingsPage.qml new file mode 100644 index 00000000..8c659685 --- /dev/null +++ b/nymea-app/ui/system/ZigbeeSettingsPage.qml @@ -0,0 +1,160 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* 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 General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free Software +* Foundation, GNU 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 General +* Public License for more details. +* +* You should have received a copy of the GNU 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 +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +import QtQuick 2.8 +import QtQuick.Controls 2.2 +import QtQuick.Controls.Material 2.1 +import QtQuick.Layouts 1.3 +import "../components" +import Nymea 1.0 + +SettingsPageBase { + id: root + header: NymeaHeader { + text: qsTr("Zigbee") + backButtonVisible: true + onBackPressed: pageStack.pop() + + HeaderButton { + imageSource: "../images/add.svg" + text: qsTr("Add Zigbee network") + onClicked: pageStack.push(Qt.resolvedUrl("ZigbeeAddNetworkPage.qml"), {zigbeeManager: zigbeeManager}) + } + } + + ZigbeeManager { + id: zigbeeManager + engine: _engine + } + + SettingsPageSectionHeader { + text: qsTr("General") + } + + NymeaListItemDelegate { + Layout.fillWidth: true + text: qsTr("Zigbee enabled") + subText: qsTr("Enable or disable Zigbee altogether") + prominentSubText: false + progressive: false + additionalItem: Switch { + anchors.centerIn: parent + } + } + + SettingsPageSectionHeader { + text: qsTr("Zigbee networks") + } + + Label { + Layout.fillWidth: true + Layout.leftMargin: app.margins; Layout.rightMargin: app.margins + wrapMode: Text.WordWrap + text: qsTr("There are no Zigbee networks set up yet. In order to use Zigbee, create a Zigbee network.") + visible: zigbeeManager.networks.count == 0 + } + + Repeater { + model: zigbeeManager.networks + delegate: NymeaListItemDelegate { + Layout.fillWidth: true + property var network: zigbeeManager.networks.get(index) + iconName: "../images/zigbee.svg" + text: model.macAddress + subText: model.serialPort + " - " + model.firmwareVersion + onClicked: pageStack.push(Qt.resolvedUrl("ZigbeeNetworkPage.qml"), { zigbeeManager: zigbeeManager, network: network }) + } + } + + Button { + Layout.fillWidth: true + Layout.margins: app.margins + text: qsTr("Add a Zigbee network") + onClicked: pageStack.push(Qt.resolvedUrl("ZigbeeAddNetworkPage.qml"), {zigbeeManager: zigbeeManager}) + } + + +// SettingsPageSectionHeader { +// text: qsTr("Available Zigbee adapters") +// } + +// Repeater { +// id: recognizedAdaptersRepeater +// model: ZigbeeAdaptersProxy { +// id: recognizedAdaptersProxy +// manager: zigbeeManager +// onlyUnused: true +//// hardwareFilter: ZigbeeAdaptersProxy.HardwareFilterRecognized +// } + +// delegate: NymeaListItemDelegate { +// Layout.fillWidth: true +// property ZigbeeAdapter adapter: recognizedAdaptersProxy.get(index) +// iconName: "../images/stock_usb.svg" +// progressive: false +// text: model.description + " - " + model.serialPort +// subText: adapter.serialPort + " - " + adapter.baudRate + " - " + adapter.backendType + " - " + adapter.serialNumber +// onClicked: { +// zigbeeManager.addNetwork(adapter.serialPort, adapter.baudRate, adapter.backendType) +//// pageStack.pop() +// } +// } +// } + + +// // List networks +// SettingsPageSectionHeader { +// text: qsTr("Zigbee networks") +// } + +// Repeater { +// model: zigbeeManager.networks +// delegate: NymeaListItemDelegate { +// Layout.fillWidth: true +// property var network: zigbeeManager.networks.get(index) +// iconName: "../images/zigbee.svg" +// text: model.macAddress +// subText: model.serialPort + " - " + model.firmwareVersion +// onClicked: pageStack.push(Qt.resolvedUrl("ZigbeeNetworkPage.qml"), { network: network }) +// } +// } + +// NymeaListItemDelegate { +// Layout.fillWidth: true +// text: qsTr("Add Zigbee network") +// subText: qsTr("Set up a new Zigbee network") +// prominentSubText: false +// onClicked: { +// pageStack.push(Qt.resolvedUrl("../system/ZigbeeNetworkAddPage.qml"), {zigbeeManager: zigbeeManager}) +// } +// } + +} +