diff --git a/libnymea-app/libnymea-app-core.h b/libnymea-app/libnymea-app-core.h index e8881c54..66fefece 100644 --- a/libnymea-app/libnymea-app-core.h +++ b/libnymea-app/libnymea-app-core.h @@ -127,6 +127,8 @@ #include "zigbee/zigbeeadaptersproxy.h" #include "zigbee/zigbeenetwork.h" #include "zigbee/zigbeenetworks.h" +#include "zigbee/zigbeenodes.h" +#include "zigbee/zigbeenodesproxy.h" #include "applogcontroller.h" #include "tagwatcher.h" #include "appdata.h" @@ -314,6 +316,9 @@ void registerQmlTypes() { qmlRegisterType(uri, 1, 0, "ZigbeeAdaptersProxy"); qmlRegisterUncreatableType(uri, 1, 0, "ZigbeeNetwork", "Get it from the ZigbeeManager"); qmlRegisterUncreatableType(uri, 1, 0, "ZigbeeNetworks", "Get it from the ZigbeeManager"); + qmlRegisterUncreatableType(uri, 1, 0, "ZigbeeNode", "Get it from the ZigbeeNodes"); + qmlRegisterUncreatableType(uri, 1, 0, "ZigbeeNodes", "Get it from the ZigbeeNetwork"); + qmlRegisterType(uri, 1, 0, "ZigbeeNodesProxy"); qmlRegisterType(uri, 1, 0, "ModbusRtuManager"); qmlRegisterUncreatableType(uri, 1, 0, "ModbusRtuMaster", "Get it from the ModbusRtuMasters"); diff --git a/libnymea-app/libnymea-app.pri b/libnymea-app/libnymea-app.pri index 6849f4fd..ded89edb 100644 --- a/libnymea-app/libnymea-app.pri +++ b/libnymea-app/libnymea-app.pri @@ -24,6 +24,8 @@ SOURCES += \ $$PWD/models/scriptsproxymodel.cpp \ $$PWD/tagwatcher.cpp \ $$PWD/zigbee/zigbeenode.cpp \ + $$PWD/zigbee/zigbeenodes.cpp \ + $$PWD/zigbee/zigbeenodesproxy.cpp \ $${PWD}/logging.cpp \ $${PWD}/applogcontroller.cpp \ $${PWD}/wifisetup/btwifisetup.cpp \ @@ -179,6 +181,8 @@ HEADERS += \ $$PWD/models/scriptsproxymodel.h \ $$PWD/tagwatcher.h \ $$PWD/zigbee/zigbeenode.h \ + $$PWD/zigbee/zigbeenodes.h \ + $$PWD/zigbee/zigbeenodesproxy.h \ $${PWD}/logging.h \ $${PWD}/applogcontroller.h \ $${PWD}/wifisetup/btwifisetup.h \ diff --git a/libnymea-app/zigbee/zigbeemanager.cpp b/libnymea-app/zigbee/zigbeemanager.cpp index a07d5ebb..c50aab3a 100644 --- a/libnymea-app/zigbee/zigbeemanager.cpp +++ b/libnymea-app/zigbee/zigbeemanager.cpp @@ -36,9 +36,14 @@ #include "zigbee/zigbeeadapters.h" #include "zigbee/zigbeenetwork.h" #include "zigbee/zigbeenetworks.h" +#include "zigbee/zigbeenode.h" +#include "zigbee/zigbeenodes.h" #include +#include "logging.h" +NYMEA_LOGGING_CATEGORY(dcZigbee, "Zigbee") + ZigbeeManager::ZigbeeManager(QObject *parent) : JsonHandler(parent), m_adapters(new ZigbeeAdapters(this)), @@ -100,7 +105,7 @@ int ZigbeeManager::addNetwork(const QString &serialPort, uint baudRate, const QS params.insert("baudRate", baudRate); params.insert("backend", backend); - qDebug() << "Add zigbee network" << params; + qCDebug(dcZigbee()) << "Add zigbee network" << params; return m_engine->jsonRpcClient()->sendCommand("Zigbee.AddNetwork", params, this, "addNetworkResponse"); } @@ -108,7 +113,7 @@ void ZigbeeManager::removeNetwork(const QUuid &networkUuid) { QVariantMap params; params.insert("networkUuid", networkUuid); - qDebug() << "Remove zigbee network" << params; + qCDebug(dcZigbee()) << "Remove zigbee network" << params; m_engine->jsonRpcClient()->sendCommand("Zigbee.RemoveNetwork", params, this, "removeNetworkResponse"); } @@ -127,6 +132,21 @@ void ZigbeeManager::factoryResetNetwork(const QUuid &networkUuid) m_engine->jsonRpcClient()->sendCommand("Zigbee.FactoryResetNetwork", params, this, "factoryResetNetworkResponse"); } +void ZigbeeManager::getNodes(const QUuid &networkUuid) +{ + QVariantMap params; + params.insert("networkUuid", networkUuid); + m_engine->jsonRpcClient()->sendCommand("Zigbee.GetNodes", params, this, "getNodesResponse"); +} + +int ZigbeeManager::removeNode(const QUuid &networkUuid, const QString &ieeeAddress) +{ + QVariantMap params; + params.insert("networkUuid", networkUuid); + params.insert("ieeeAddress", ieeeAddress); + return m_engine->jsonRpcClient()->sendCommand("Zigbee.RemoveNode", params, this, "removeNodeResponse"); +} + void ZigbeeManager::init() { m_adapters->clear(); @@ -142,7 +162,7 @@ void ZigbeeManager::init() void ZigbeeManager::getAvailableBackendsResponse(int commandId, const QVariantMap ¶ms) { - qDebug() << "Zigbee get available backends response" << commandId << params; + qCDebug(dcZigbee()) << "Zigbee get available backends response" << commandId << params; m_availableBackends.clear(); foreach (const QVariant &backendVariant, params.value("backends").toList()) { m_availableBackends << backendVariant.toString(); @@ -152,56 +172,73 @@ void ZigbeeManager::getAvailableBackendsResponse(int commandId, const QVariantMa void ZigbeeManager::getAdaptersResponse(int commandId, const QVariantMap ¶ms) { - qDebug() << "Zigbee get adapters response" << commandId << params; + qCDebug(dcZigbee()) << "Zigbee get adapters response" << commandId << params; m_adapters->clear(); foreach (const QVariant &adapterVariant, params.value("adapters").toList()) { QVariantMap adapterMap = adapterVariant.toMap(); ZigbeeAdapter *adapter = unpackAdapter(adapterMap); - qDebug() << "Zigbee adapter added" << adapter->description() << adapter->serialPort() << adapter->hardwareRecognized(); + qCDebug(dcZigbee()) << "Zigbee adapter added" << adapter->description() << adapter->serialPort() << adapter->hardwareRecognized(); m_adapters->addAdapter(adapter); } - -// ZigbeeAdapter *fakeAdapter = new ZigbeeAdapter(); -// fakeAdapter->setSerialPort("/dev/fake"); -// fakeAdapter->setBackend("Fake"); -// 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) { - qDebug() << "Zigbee get networks response" << commandId << params; + qCDebug(dcZigbee()) << "Zigbee get networks response" << commandId << params; m_networks->clear(); foreach (const QVariant &networkVariant, params.value("zigbeeNetworks").toList()) { QVariantMap networkMap = networkVariant.toMap(); ZigbeeNetwork *network = unpackNetwork(networkMap); - qDebug() << "Zigbee network added" << network->networkUuid().toString() << network->serialPort() << network->macAddress(); + qCDebug(dcZigbee()) << "Zigbee network added" << network->networkUuid().toString() << network->serialPort() << network->macAddress(); m_networks->addNetwork(network); + + // Get nodes from this network + getNodes(network->networkUuid()); } } void ZigbeeManager::addNetworkResponse(int commandId, const QVariantMap ¶ms) { - qDebug() << "Zigbee add network response" << commandId << params; + qCDebug(dcZigbee()) << "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) { - qDebug() << "Zigbee remove network response" << commandId << params; + qCDebug(dcZigbee()) << "Zigbee remove network response" << commandId << params; } void ZigbeeManager::setPermitJoinResponse(int commandId, const QVariantMap ¶ms) { - qDebug() << "Zigbee set permit join network response" << commandId << params; + qCDebug(dcZigbee()) << "Zigbee set permit join network response" << commandId << params; } void ZigbeeManager::factoryResetNetworkResponse(int commandId, const QVariantMap ¶ms) { - qDebug() << "Zigbee factory reset network response" << commandId << params; + qCDebug(dcZigbee()) << "Zigbee factory reset network response" << commandId << params; +} + +void ZigbeeManager::getNodesResponse(int commandId, const QVariantMap ¶ms) +{ + qCDebug(dcZigbee()) << "Zigbee get nodes response" << commandId << params; + + foreach (const QVariant &nodeVariant, params.value("zigbeeNodes").toList()) { + QVariantMap nodeMap = nodeVariant.toMap(); + QUuid networkUuid = nodeMap.value("networkUuid").toUuid(); + ZigbeeNetwork *network = m_networks->getNetwork(networkUuid); + if (!network) { + qCWarning(dcZigbee()) << "Could not find network for node" << nodeMap; + return; + } + + addOrUpdateNode(network, nodeMap); + } +} + +void ZigbeeManager::removeNodeResponse(int commandId, const QVariantMap ¶ms) +{ + qCDebug(dcZigbee()) << "Zigbee remove node response" << commandId << params; + emit removeNodeReply(commandId, params.value("zigbeeError").toString()); } void ZigbeeManager::notificationReceived(const QVariantMap ¬ification) @@ -236,14 +273,54 @@ void ZigbeeManager::notificationReceived(const QVariantMap ¬ification) QUuid networkUuid = networkMap.value("networkUuid").toUuid(); ZigbeeNetwork *network = m_networks->getNetwork(networkUuid); if (!network) { - qWarning() << "Could not find network for changed notification"; + qCWarning(dcZigbee()) << "Could not find network for changed notification"; return; } fillNetworkData(network, networkMap); return; } - qDebug() << "Unhandled Zigbee notification" << notificationString << notification; + if (notificationString == "Zigbee.NodeAdded") { + QVariantMap nodeMap = notification.value("params").toMap().value("zigbeeNode").toMap(); + QUuid networkUuid = nodeMap.value("networkUuid").toUuid(); + ZigbeeNetwork *network = m_networks->getNetwork(networkUuid); + if (!network) { + qCWarning(dcZigbee()) << "Could not find network for node added notification" << nodeMap; + return; + } + + addOrUpdateNode(network, nodeMap); + return; + } + + if (notificationString == "Zigbee.NodeRemoved") { + QVariantMap nodeMap = notification.value("params").toMap().value("zigbeeNode").toMap(); + QUuid networkUuid = nodeMap.value("networkUuid").toUuid(); + ZigbeeNetwork *network = m_networks->getNetwork(networkUuid); + if (!network) { + qCWarning(dcZigbee()) << "Could not find network for node removed notification" << nodeMap; + return; + } + + QString ieeeAddress = nodeMap.value("ieeeAddress").toString(); + network->nodes()->removeNode(ieeeAddress); + return; + } + + if (notificationString == "Zigbee.NodeChanged") { + QVariantMap nodeMap = notification.value("params").toMap().value("zigbeeNode").toMap(); + QUuid networkUuid = nodeMap.value("networkUuid").toUuid(); + ZigbeeNetwork *network = m_networks->getNetwork(networkUuid); + if (!network) { + qCWarning(dcZigbee()) << "Could not find network for node changed notification" << nodeMap; + return; + } + + addOrUpdateNode(network, nodeMap); + return; + } + + qCDebug(dcZigbee()) << "Unhandled Zigbee notification" << notificationString << notification; } ZigbeeAdapter *ZigbeeManager::unpackAdapter(const QVariantMap &adapterMap) @@ -266,6 +343,15 @@ ZigbeeNetwork *ZigbeeManager::unpackNetwork(const QVariantMap &networkMap) return network; } +ZigbeeNode *ZigbeeManager::unpackNode(const QVariantMap &nodeMap) +{ + QUuid networkUuid = nodeMap.value("networkUuid").toUuid(); + QString ieeeAddress = nodeMap.value("ieeeAddress").toString(); + ZigbeeNode *node = new ZigbeeNode(networkUuid, ieeeAddress, this); + node->updateNodeProperties(nodeMap); + return node; +} + void ZigbeeManager::fillNetworkData(ZigbeeNetwork *network, const QVariantMap &networkMap) { network->setNetworkUuid(networkMap.value("networkUuid").toUuid()); @@ -283,3 +369,14 @@ void ZigbeeManager::fillNetworkData(ZigbeeNetwork *network, const QVariantMap &n network->setNetworkState(ZigbeeNetwork::stringToZigbeeNetworkState(networkMap.value("networkState").toString())); } +void ZigbeeManager::addOrUpdateNode(ZigbeeNetwork *network, const QVariantMap &nodeMap) +{ + QString ieeeAddress = nodeMap.value("ieeeAddress").toString(); + ZigbeeNode *node = network->nodes()->getNode(ieeeAddress); + if (node) { + node->updateNodeProperties(nodeMap); + } else { + network->nodes()->addNode(unpackNode(nodeMap)); + } +} + diff --git a/libnymea-app/zigbee/zigbeemanager.h b/libnymea-app/zigbee/zigbeemanager.h index abe55811..b54cee74 100644 --- a/libnymea-app/zigbee/zigbeemanager.h +++ b/libnymea-app/zigbee/zigbeemanager.h @@ -35,11 +35,13 @@ #include "zigbeeadapter.h" #include "jsonrpc/jsonhandler.h" +class Engine; class JsonRpcClient; class ZigbeeAdapters; class ZigbeeNetwork; class ZigbeeNetworks; -class Engine; +class ZigbeeNode; +class ZigbeeNodes; class ZigbeeManager : public JsonHandler { @@ -63,15 +65,19 @@ public: ZigbeeAdapters *adapters() const; ZigbeeNetworks *networks() const; + // Network Q_INVOKABLE int addNetwork(const QString &serialPort, uint baudRate, const QString &backend); Q_INVOKABLE void removeNetwork(const QUuid &networkUuid); Q_INVOKABLE void setPermitJoin(const QUuid &networkUuid, uint duration = 120); Q_INVOKABLE void factoryResetNetwork(const QUuid &networkUuid); + Q_INVOKABLE void getNodes(const QUuid &networkUuid); + Q_INVOKABLE int removeNode(const QUuid &networkUuid, const QString &ieeeAddress); signals: void engineChanged(); void availableBackendsChanged(); void addNetworkReply(int commandId, const QString &error, const QUuid &networkUuid); + void removeNodeReply(int commandId, const QString &error); private: void init(); @@ -85,6 +91,9 @@ private: Q_INVOKABLE void setPermitJoinResponse(int commandId, const QVariantMap ¶ms); Q_INVOKABLE void factoryResetNetworkResponse(int commandId, const QVariantMap ¶ms); + Q_INVOKABLE void getNodesResponse(int commandId, const QVariantMap ¶ms); + Q_INVOKABLE void removeNodeResponse(int commandId, const QVariantMap ¶ms); + Q_INVOKABLE void notificationReceived(const QVariantMap ¬ification); private: @@ -95,8 +104,9 @@ private: ZigbeeAdapter *unpackAdapter(const QVariantMap &adapterMap); ZigbeeNetwork *unpackNetwork(const QVariantMap &networkMap); + ZigbeeNode *unpackNode(const QVariantMap &nodeMap); void fillNetworkData(ZigbeeNetwork *network, const QVariantMap &networkMap); - + void addOrUpdateNode(ZigbeeNetwork *network, const QVariantMap &nodeMap); }; #endif // ZIGBEEMANAGER_H diff --git a/libnymea-app/zigbee/zigbeenetwork.cpp b/libnymea-app/zigbee/zigbeenetwork.cpp index 22e0b6c4..102bd62c 100644 --- a/libnymea-app/zigbee/zigbeenetwork.cpp +++ b/libnymea-app/zigbee/zigbeenetwork.cpp @@ -30,13 +30,14 @@ #include "zigbeenetwork.h" -ZigbeeNetwork::ZigbeeNetwork(QObject *parent) : QObject(parent) +ZigbeeNetwork::ZigbeeNetwork(QObject *parent) : + QObject(parent), + m_nodes(new ZigbeeNodes(this)) { m_permitJoinTimer = new QTimer(this); m_permitJoinTimer->setInterval(1000); m_permitJoinTimer->setSingleShot(true); connect(m_permitJoinTimer, &QTimer::timeout, this, [this](){ - setPermitJoiningRemaining(m_permitJoiningRemaining - 1); if (m_permitJoiningRemaining <= 0) { m_permitJoinTimer->stop(); @@ -44,6 +45,12 @@ ZigbeeNetwork::ZigbeeNetwork(QObject *parent) : QObject(parent) }); } + +ZigbeeNodes *ZigbeeNetwork::nodes() const +{ + return m_nodes; +} + QUuid ZigbeeNetwork::networkUuid() const { return m_networkUuid; diff --git a/libnymea-app/zigbee/zigbeenetwork.h b/libnymea-app/zigbee/zigbeenetwork.h index 6cfd86dc..2d5fdba8 100644 --- a/libnymea-app/zigbee/zigbeenetwork.h +++ b/libnymea-app/zigbee/zigbeenetwork.h @@ -35,6 +35,7 @@ #include #include +#include "zigbeenodes.h" #include "zigbeeadapter.h" class ZigbeeNetwork : public QObject @@ -54,6 +55,7 @@ class ZigbeeNetwork : public QObject Q_PROPERTY(uint permitJoiningRemaining READ permitJoiningRemaining NOTIFY permitJoiningRemainingChanged) Q_PROPERTY(QString backend READ backend NOTIFY backendChanged) Q_PROPERTY(ZigbeeNetworkState networkState READ networkState NOTIFY networkStateChanged) + Q_PROPERTY(ZigbeeNodes *nodes READ nodes CONSTANT) // Internal properties @@ -69,6 +71,8 @@ public: explicit ZigbeeNetwork(QObject *parent = nullptr); + ZigbeeNodes *nodes() const; + QUuid networkUuid() const; void setNetworkUuid(const QUuid &networkUuid); @@ -145,6 +149,8 @@ private: QString m_backend; ZigbeeNetworkState m_networkState; + ZigbeeNodes *m_nodes = nullptr; + QTimer *m_permitJoinTimer = nullptr; }; diff --git a/libnymea-app/zigbee/zigbeenetworks.cpp b/libnymea-app/zigbee/zigbeenetworks.cpp index dd2e08eb..d3c40c32 100644 --- a/libnymea-app/zigbee/zigbeenetworks.cpp +++ b/libnymea-app/zigbee/zigbeenetworks.cpp @@ -92,7 +92,6 @@ QHash ZigbeeNetworks::roleNames() const roles.insert(RoleBackend, "backend"); roles.insert(RoleNetworkState, "networkState"); return roles; - } void ZigbeeNetworks::addNetwork(ZigbeeNetwork *network) diff --git a/libnymea-app/zigbee/zigbeenode.cpp b/libnymea-app/zigbee/zigbeenode.cpp index 895ccf96..6d322355 100644 --- a/libnymea-app/zigbee/zigbeenode.cpp +++ b/libnymea-app/zigbee/zigbeenode.cpp @@ -1,8 +1,39 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2021, 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 +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + #include "zigbeenode.h" -ZigbeeNode::ZigbeeNode(QUuid networkUuid, QObject *parent) : +ZigbeeNode::ZigbeeNode(const QUuid &networkUuid, const QString &ieeeAddress, QObject *parent) : QObject(parent), - m_networkUuid(networkUuid) + m_networkUuid(networkUuid), + m_ieeeAddress(ieeeAddress) { } @@ -11,3 +42,187 @@ QUuid ZigbeeNode::networkUuid() const { return m_networkUuid; } + +QString ZigbeeNode::ieeeAddress() const +{ + return m_ieeeAddress; +} + +quint16 ZigbeeNode::networkAddress() const +{ + return m_networkAddress; +} + +void ZigbeeNode::setNetworkAddress(quint16 networkAddress) +{ + if (m_networkAddress == networkAddress) + return; + + m_networkAddress = networkAddress; + emit networkAddressChanged(m_networkAddress); +} + +ZigbeeNode::ZigbeeNodeType ZigbeeNode::type() const +{ + return m_type; +} + +void ZigbeeNode::setType(ZigbeeNode::ZigbeeNodeType type) +{ + if (m_type == type) + return; + + m_type = type; + emit typeChanged(m_type); +} + +ZigbeeNode::ZigbeeNodeState ZigbeeNode::state() const +{ + return m_state; +} + +void ZigbeeNode::setState(ZigbeeNode::ZigbeeNodeState state) +{ + if (m_state == state) + return; + + m_state = state; + emit stateChanged(m_state); +} + +QString ZigbeeNode::manufacturer() const +{ + return m_manufacturer; +} + +void ZigbeeNode::setManufacturer(const QString &manufacturer) +{ + if (m_manufacturer == manufacturer) + return; + + m_manufacturer = manufacturer; + emit manufacturerChanged(m_manufacturer); +} + +QString ZigbeeNode::model() const +{ + return m_model; +} + +void ZigbeeNode::setModel(const QString &model) +{ + if (m_model == model) + return; + + m_model = model; + emit modelChanged(m_model); +} + +QString ZigbeeNode::version() const +{ + return m_version; +} + +void ZigbeeNode::setVersion(const QString &version) +{ + if (m_version == version) + return; + + m_version = version; + emit versionChanged(m_version); +} + +bool ZigbeeNode::rxOnWhenIdle() const +{ + return m_rxOnWhenIdle; +} + +void ZigbeeNode::setRxOnWhenIdle(bool rxOnWhenIdle) +{ + if (m_rxOnWhenIdle == rxOnWhenIdle) + return; + + m_rxOnWhenIdle = rxOnWhenIdle; + emit rxOnWhenIdleChanged(m_rxOnWhenIdle); +} + +bool ZigbeeNode::reachable() const +{ + return m_reachable; +} + +void ZigbeeNode::setReachable(bool reachable) +{ + if (m_reachable == reachable) + return; + + m_reachable = reachable; + emit reachableChanged(m_reachable); +} + +uint ZigbeeNode::lqi() const +{ + return m_lqi; +} + +void ZigbeeNode::setLqi(uint lqi) +{ + if (m_lqi == lqi) + return; + + m_lqi = lqi; + emit lqiChanged(m_lqi); + +} + +QDateTime ZigbeeNode::lastSeen() const +{ + return m_lastSeen; +} + +void ZigbeeNode::setLastSeen(const QDateTime &lastSeen) +{ + if (m_lastSeen == lastSeen) + return; + + m_lastSeen = lastSeen; + emit lastSeenChanged(m_lastSeen); +} + +ZigbeeNode::ZigbeeNodeState ZigbeeNode::stringToNodeState(const QString &nodeState) +{ + if (nodeState == "ZigbeeNodeStateUninitialized") { + return ZigbeeNodeStateUninitialized; + } else if (nodeState == "ZigbeeNodeStateInitializing") { + return ZigbeeNodeStateInitializing; + } else if (nodeState == "ZigbeeNodeStateInitialized") { + return ZigbeeNodeStateInitialized; + } else { + return ZigbeeNodeStateHandled; + } +} + +ZigbeeNode::ZigbeeNodeType ZigbeeNode::stringToNodeType(const QString &nodeType) +{ + if (nodeType == "ZigbeeNodeTypeCoordinator") { + return ZigbeeNodeTypeCoordinator; + } else if (nodeType == "ZigbeeNodeTypeRouter") { + return ZigbeeNodeTypeRouter; + } else { + return ZigbeeNodeTypeEndDevice; + } +} + +void ZigbeeNode::updateNodeProperties(const QVariantMap &nodeMap) +{ + setNetworkAddress(nodeMap.value("networkAddress").toUInt()); + setType(ZigbeeNode::stringToNodeType(nodeMap.value("type").toString())); + setState(ZigbeeNode::stringToNodeState(nodeMap.value("state").toString())); + setManufacturer(nodeMap.value("manufacturer").toString()); + setModel(nodeMap.value("model").toString()); + setVersion(nodeMap.value("version").toString()); + setRxOnWhenIdle(nodeMap.value("receiverOnWhileIdle").toBool()); + setReachable(nodeMap.value("reachable").toBool()); + setLqi(nodeMap.value("lqi").toUInt()); + setLastSeen(QDateTime::fromMSecsSinceEpoch(nodeMap.value("lastSeen").toUInt() * 1000)); +} diff --git a/libnymea-app/zigbee/zigbeenode.h b/libnymea-app/zigbee/zigbeenode.h index 2f456f52..03722bb1 100644 --- a/libnymea-app/zigbee/zigbeenode.h +++ b/libnymea-app/zigbee/zigbeenode.h @@ -1,28 +1,138 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2021, 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 +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + #ifndef ZIGBEENODE_H #define ZIGBEENODE_H #include #include +#include +#include class ZigbeeNode : public QObject { Q_OBJECT Q_PROPERTY(QUuid networkUuid READ networkUuid CONSTANT) - + Q_PROPERTY(QString ieeeAddress READ ieeeAddress CONSTANT) + Q_PROPERTY(quint16 networkAddress READ networkAddress WRITE setNetworkAddress NOTIFY networkAddressChanged) + Q_PROPERTY(ZigbeeNodeType type READ type WRITE setType NOTIFY typeChanged) + Q_PROPERTY(ZigbeeNodeState state READ state WRITE setState NOTIFY stateChanged) + Q_PROPERTY(QString manufacturer READ manufacturer WRITE setManufacturer NOTIFY manufacturerChanged) + Q_PROPERTY(QString model READ model WRITE setModel NOTIFY modelChanged) + Q_PROPERTY(QString version READ version WRITE setVersion NOTIFY versionChanged) + Q_PROPERTY(bool rxOnWhenIdle READ rxOnWhenIdle WRITE setRxOnWhenIdle NOTIFY rxOnWhenIdleChanged) + Q_PROPERTY(bool reachable READ reachable WRITE setReachable NOTIFY reachableChanged) + Q_PROPERTY(uint lqi READ lqi WRITE setLqi NOTIFY lqiChanged) + Q_PROPERTY(QDateTime lastSeen READ lastSeen WRITE setLastSeen NOTIFY lastSeenChanged) public: - explicit ZigbeeNode(QUuid networkUuid, QObject *parent = nullptr); + enum ZigbeeNodeType { + ZigbeeNodeTypeCoordinator, + ZigbeeNodeTypeRouter, + ZigbeeNodeTypeEndDevice + }; + Q_ENUM(ZigbeeNodeType) + + enum ZigbeeNodeState { + ZigbeeNodeStateUninitialized, + ZigbeeNodeStateInitializing, + ZigbeeNodeStateInitialized, + ZigbeeNodeStateHandled + }; + Q_ENUM(ZigbeeNodeState) + + explicit ZigbeeNode(const QUuid &networkUuid, const QString &ieeeAddress, QObject *parent = nullptr); QUuid networkUuid() const; - QString ieeeAddress() const; - void setIeeeAddress(const QString &ieeeAddress); + + quint16 networkAddress() const; + void setNetworkAddress(quint16 networkAddress); + + ZigbeeNodeType type() const; + void setType(ZigbeeNodeType type); + + ZigbeeNodeState state() const; + void setState(ZigbeeNodeState state); + + QString manufacturer() const; + void setManufacturer(const QString &manufacturer); + + QString model() const; + void setModel(const QString &model); + + QString version() const; + void setVersion(const QString &version); + + bool rxOnWhenIdle() const; + void setRxOnWhenIdle(bool rxOnWhenIdle); + + bool reachable() const; + void setReachable(bool reachable); + + uint lqi() const; + void setLqi(uint lqi); + + QDateTime lastSeen() const; + void setLastSeen(const QDateTime &lastSeen); + + static ZigbeeNodeState stringToNodeState(const QString &nodeState); + static ZigbeeNodeType stringToNodeType(const QString &nodeType); + + void updateNodeProperties(const QVariantMap &nodeMap); signals: + void networkAddressChanged(quint16 networkAddress); + void typeChanged(ZigbeeNodeType type); + void stateChanged(ZigbeeNodeState state); + void manufacturerChanged(const QString &manufacturer); + void modelChanged(const QString &model); + void versionChanged(const QString &version); + void rxOnWhenIdleChanged(bool rxOnWhenIdle); + void reachableChanged(bool reachable); + void lqiChanged(uint lqi); + void lastSeenChanged(const QDateTime &lastSeen); private: QUuid m_networkUuid; QString m_ieeeAddress; + quint16 m_networkAddress = 0; + ZigbeeNodeType m_type = ZigbeeNodeTypeEndDevice; + ZigbeeNodeState m_state = ZigbeeNodeStateUninitialized; + QString m_manufacturer; + QString m_model; + QString m_version; + bool m_rxOnWhenIdle = false; + bool m_reachable = false; + uint m_lqi = 0; + QDateTime m_lastSeen; }; #endif // ZIGBEENODE_H diff --git a/libnymea-app/zigbee/zigbeenodes.cpp b/libnymea-app/zigbee/zigbeenodes.cpp new file mode 100644 index 00000000..afd75390 --- /dev/null +++ b/libnymea-app/zigbee/zigbeenodes.cpp @@ -0,0 +1,193 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2021, 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 +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "zigbeenodes.h" + +ZigbeeNodes::ZigbeeNodes(QObject *parent) : QAbstractListModel(parent) +{ + +} + +int ZigbeeNodes::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return m_nodes.count(); +} + +QVariant ZigbeeNodes::data(const QModelIndex &index, int role) const +{ + switch (role) { + case RoleNetworkUuid: + return m_nodes.at(index.row())->networkUuid(); + case RoleIeeeAddress: + return m_nodes.at(index.row())->ieeeAddress(); + case RoleNetworkAddress: + return m_nodes.at(index.row())->networkAddress(); + case RoleType: + return m_nodes.at(index.row())->type(); + case RoleState: + return m_nodes.at(index.row())->state(); + case RoleManufacturer: + return m_nodes.at(index.row())->manufacturer(); + case RoleModel: + return m_nodes.at(index.row())->model(); + case RoleVersion: + return m_nodes.at(index.row())->version(); + case RoleRxOnWhenIdle: + return m_nodes.at(index.row())->rxOnWhenIdle(); + case RoleReachable: + return m_nodes.at(index.row())->reachable(); + case RoleLqi: + return m_nodes.at(index.row())->lqi(); + case RoleLastSeen: + return m_nodes.at(index.row())->lastSeen(); + } + + return QVariant(); +} + +QHash ZigbeeNodes::roleNames() const +{ + QHash roles; + roles.insert(RoleNetworkUuid, "networkUuid"); + roles.insert(RoleIeeeAddress, "ieeeAddress"); + roles.insert(RoleNetworkAddress, "networkAddress"); + roles.insert(RoleType, "type"); + roles.insert(RoleState, "state"); + roles.insert(RoleManufacturer, "manufacturer"); + roles.insert(RoleModel, "model"); + roles.insert(RoleVersion, "version"); + roles.insert(RoleRxOnWhenIdle, "rxOnWhenIdle"); + roles.insert(RoleReachable, "reachable"); + roles.insert(RoleLqi, "lqi"); + roles.insert(RoleLastSeen, "lastSeen"); + return roles; +} + +void ZigbeeNodes::addNode(ZigbeeNode *node) +{ + node->setParent(this); + beginInsertRows(QModelIndex(), m_nodes.count(), m_nodes.count()); + m_nodes.append(node); + + connect(node, &ZigbeeNode::networkAddressChanged, this, [this, node]() { + QModelIndex idx = index(m_nodes.indexOf(node), 0); + emit dataChanged(idx, idx, {RoleNetworkAddress}); + }); + + connect(node, &ZigbeeNode::typeChanged, this, [this, node]() { + QModelIndex idx = index(m_nodes.indexOf(node), 0); + emit dataChanged(idx, idx, {RoleType}); + }); + + connect(node, &ZigbeeNode::stateChanged, this, [this, node]() { + QModelIndex idx = index(m_nodes.indexOf(node), 0); + emit dataChanged(idx, idx, {RoleState}); + }); + + connect(node, &ZigbeeNode::manufacturerChanged, this, [this, node]() { + QModelIndex idx = index(m_nodes.indexOf(node), 0); + emit dataChanged(idx, idx, {RoleManufacturer}); + }); + + connect(node, &ZigbeeNode::modelChanged, this, [this, node]() { + QModelIndex idx = index(m_nodes.indexOf(node), 0); + emit dataChanged(idx, idx, {RoleModel}); + }); + + connect(node, &ZigbeeNode::versionChanged, this, [this, node]() { + QModelIndex idx = index(m_nodes.indexOf(node), 0); + emit dataChanged(idx, idx, {RoleVersion}); + }); + + connect(node, &ZigbeeNode::rxOnWhenIdleChanged, this, [this, node]() { + QModelIndex idx = index(m_nodes.indexOf(node), 0); + emit dataChanged(idx, idx, {RoleRxOnWhenIdle}); + }); + + connect(node, &ZigbeeNode::reachableChanged, this, [this, node]() { + QModelIndex idx = index(m_nodes.indexOf(node), 0); + emit dataChanged(idx, idx, {RoleReachable}); + }); + + connect(node, &ZigbeeNode::lqiChanged, this, [this, node]() { + QModelIndex idx = index(m_nodes.indexOf(node), 0); + emit dataChanged(idx, idx, {RoleLqi}); + }); + + connect(node, &ZigbeeNode::lastSeenChanged, this, [this, node]() { + QModelIndex idx = index(m_nodes.indexOf(node), 0); + emit dataChanged(idx, idx, {RoleLastSeen}); + }); + + endInsertRows(); + emit countChanged(); +} + +void ZigbeeNodes::removeNode(const QString &ieeeAddress) +{ + for (int i = 0; i < m_nodes.count(); i++) { + if (m_nodes.at(i)->ieeeAddress() == ieeeAddress) { + beginRemoveRows(QModelIndex(), i, i); + m_nodes.takeAt(i)->deleteLater(); + endRemoveRows(); + emit countChanged(); + return; + } + } +} + +void ZigbeeNodes::clear() +{ + beginResetModel(); + qDeleteAll(m_nodes); + m_nodes.clear(); + endResetModel(); + emit countChanged(); +} + +ZigbeeNode *ZigbeeNodes::get(int index) const +{ + if (index < 0 || index >= m_nodes.count()) { + return nullptr; + } + return m_nodes.at(index); +} + +ZigbeeNode *ZigbeeNodes::getNode(const QString &ieeeAddress) const +{ + foreach (ZigbeeNode *node, m_nodes) { + if (node->ieeeAddress() == ieeeAddress) { + return node; + } + } + + return nullptr; +} diff --git a/libnymea-app/zigbee/zigbeenodes.h b/libnymea-app/zigbee/zigbeenodes.h new file mode 100644 index 00000000..c28ae124 --- /dev/null +++ b/libnymea-app/zigbee/zigbeenodes.h @@ -0,0 +1,84 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2021, 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 +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef ZIGBEENODES_H +#define ZIGBEENODES_H + +#include +#include + +#include "zigbeenode.h" + +class ZigbeeNodes : public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY(int count READ rowCount NOTIFY countChanged) + +public: + enum Roles { + RoleNetworkUuid, + RoleIeeeAddress, + RoleNetworkAddress, + RoleType, + RoleState, + RoleManufacturer, + RoleModel, + RoleVersion, + RoleRxOnWhenIdle, + RoleReachable, + RoleLqi, + RoleLastSeen + }; + Q_ENUM(Roles) + + explicit ZigbeeNodes(QObject *parent = nullptr); + virtual ~ZigbeeNodes() override = default; + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role) const override; + QHash roleNames() const override; + + void addNode(ZigbeeNode *node); + void removeNode(const QString &ieeeAddress); + + void clear(); + + Q_INVOKABLE virtual ZigbeeNode *get(int index) const; + Q_INVOKABLE ZigbeeNode *getNode(const QString &ieeeAddress) const; + +signals: + void countChanged(); + +protected: + QList m_nodes; + +}; + +#endif // ZIGBEENODES_H diff --git a/libnymea-app/zigbee/zigbeenodesproxy.cpp b/libnymea-app/zigbee/zigbeenodesproxy.cpp new file mode 100644 index 00000000..7fae0f59 --- /dev/null +++ b/libnymea-app/zigbee/zigbeenodesproxy.cpp @@ -0,0 +1,76 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2021, 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 +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "zigbeenodesproxy.h" +#include "zigbeenode.h" +#include "zigbeenodes.h" + +#include + +ZigbeeNodesProxy::ZigbeeNodesProxy(QObject *parent) : + QSortFilterProxyModel(parent) +{ + +} + +ZigbeeNodes *ZigbeeNodesProxy::zigbeeNodes() const +{ + return m_zigbeeNodes; +} + +void ZigbeeNodesProxy::setZigbeeNodes(ZigbeeNodes *zigbeeNodes) +{ + if (m_zigbeeNodes == zigbeeNodes) + return; + + m_zigbeeNodes = zigbeeNodes; + emit zigbeeNodesChanged(m_zigbeeNodes); + + qWarning() << "Set nodes to proxy" << m_zigbeeNodes->rowCount(); + + connect(m_zigbeeNodes, &ZigbeeNodes::countChanged, this, [this](){ + emit countChanged(); + }); + + setSourceModel(m_zigbeeNodes); + setSortRole(ZigbeeNodes::RoleNetworkAddress); + sort(0, Qt::DescendingOrder); + invalidateFilter(); + + emit countChanged(); +} + +ZigbeeNode *ZigbeeNodesProxy::get(int index) const +{ + if (index >= 0 && index < m_zigbeeNodes->rowCount()) { + return m_zigbeeNodes->get(mapToSource(this->index(index, 0)).row()); + } + return nullptr; +} diff --git a/libnymea-app/zigbee/zigbeenodesproxy.h b/libnymea-app/zigbee/zigbeenodesproxy.h new file mode 100644 index 00000000..11afab4c --- /dev/null +++ b/libnymea-app/zigbee/zigbeenodesproxy.h @@ -0,0 +1,63 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2021, 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 +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef ZIGBEENODESPROXY_H +#define ZIGBEENODESPROXY_H + +#include +#include + +class ZigbeeNode; +class ZigbeeNodes; + +class ZigbeeNodesProxy : public QSortFilterProxyModel +{ + Q_OBJECT + Q_PROPERTY(int count READ rowCount NOTIFY countChanged) + Q_PROPERTY(ZigbeeNodes *zigbeeNodes READ zigbeeNodes WRITE setZigbeeNodes NOTIFY zigbeeNodesChanged) + +public: + explicit ZigbeeNodesProxy(QObject *parent = nullptr); + + ZigbeeNodes *zigbeeNodes() const; + void setZigbeeNodes(ZigbeeNodes *zigbeeNodes); + + Q_INVOKABLE ZigbeeNode *get(int index) const; + +signals: + void countChanged(); + void zigbeeNodesChanged(ZigbeeNodes *zigbeeNodes); + +private: + ZigbeeNodes *m_zigbeeNodes = nullptr; + +}; + +#endif // ZIGBEENODESPROXY_H diff --git a/nymea-app/images.qrc b/nymea-app/images.qrc index 1a17b457..f11b47e0 100644 --- a/nymea-app/images.qrc +++ b/nymea-app/images.qrc @@ -262,5 +262,6 @@ ui/images/modbus.svg ui/images/setupwizard/wired-connection.svg ui/images/setupwizard/wireless-connection.svg + ui/images/system-suspend.svg diff --git a/nymea-app/resources.qrc b/nymea-app/resources.qrc index 2bab6a46..762c374e 100644 --- a/nymea-app/resources.qrc +++ b/nymea-app/resources.qrc @@ -219,7 +219,7 @@ ui/components/ThingInfoPane.qml ui/system/ZigbeeSettingsPage.qml ui/system/ZigbeeAddNetworkPage.qml - ui/system/ZigbeeNetworkPage.qml + ui/system/ZigbeeNetworkSettingsPage.qml ui/MainMenu.qml ui/components/NymeaItemDelegate.qml ui/components/NymeaSwipeDelegate.qml @@ -251,5 +251,6 @@ ui/system/ModbusRtuReconfigureMasterPage.qml ui/connection/NewConnectionWizard.qml ui/components/WizardPageBase.qml + ui/system/ZigbeeNetworkPage.qml diff --git a/nymea-app/ui/images/system-suspend.svg b/nymea-app/ui/images/system-suspend.svg new file mode 100644 index 00000000..1eec72f8 --- /dev/null +++ b/nymea-app/ui/images/system-suspend.svg @@ -0,0 +1,19 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/nymea-app/ui/system/ZigbeeNetworkPage.qml b/nymea-app/ui/system/ZigbeeNetworkPage.qml index cb967384..27893c4a 100644 --- a/nymea-app/ui/system/ZigbeeNetworkPage.qml +++ b/nymea-app/ui/system/ZigbeeNetworkPage.qml @@ -1,6 +1,6 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* Copyright 2013 - 2020, nymea GmbH +* Copyright 2013 - 2021, nymea GmbH * Contact: contact@nymea.io * * This file is part of nymea. @@ -42,120 +42,347 @@ SettingsPageBase { property ZigbeeNetwork network: null header: NymeaHeader { - text: qsTr("ZigBee network settings") + text: qsTr("ZigBee network") backButtonVisible: true onBackPressed: pageStack.pop() + HeaderButton { + imageSource: "/ui/images/configure.svg" + text: qsTr("Network settings") + onClicked: pageStack.push(Qt.resolvedUrl("ZigbeeNetworkSettingsPage.qml"), { zigbeeManager: zigbeeManager, network: network }) + } + } + + busy: d.pendingCommandId != -1 + QtObject { + id: d + property int pendingCommandId: -1 + function removeNode(networkUuid, ieeeAddress) { + d.pendingCommandId = root.zigbeeManager.removeNode(networkUuid, ieeeAddress) + } + } + + Connections { + target: root.zigbeeManager + onRemoveNodeReply: { + if (commandId == d.pendingCommandId) { + d.pendingCommandId = -1 + var props = {}; + switch (error) { + case "ZigbeeErrorNoError": + 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("Network information") + text: qsTr("Network") } - NymeaSwipeDelegate { + RowLayout { Layout.fillWidth: true - text: qsTr("Network state") - subText: { - switch (root.network.networkState) { - case ZigbeeNetwork.ZigbeeNetworkStateOnline: - return qsTr("The network is online") - case ZigbeeNetwork.ZigbeeNetworkStateOffline: - return qsTr("The network is offline") - case ZigbeeNetwork.ZigbeeNetworkStateStarting: - return qsTr("The network is starting...") - case ZigbeeNetwork.ZigbeeNetworkStateUpdating: - return qsTr("The controller is currently installing an update") - case ZigbeeNetwork.ZigbeeNetworkStateError: - return qsTr("The network is in an error state.") + Layout.leftMargin: app.margins + Layout.rightMargin: app.margins + + Label { + //Layout.fillWidth: true + text: { + switch (network.networkState) { + case ZigbeeNetwork.ZigbeeNetworkStateOnline: + return qsTr("Online") + case ZigbeeNetwork.ZigbeeNetworkStateOffline: + return qsTr("Offline") + case ZigbeeNetwork.ZigbeeNetworkStateStarting: + return qsTr("Starting") + case ZigbeeNetwork.ZigbeeNetworkStateUpdating: + return qsTr("Updating") + case ZigbeeNetwork.ZigbeeNetworkStateError: + return qsTr("Error") + } } } - progressive: false + Led { + Layout.preferredHeight: Style.iconSize + Layout.preferredWidth: Style.iconSize + state: { + switch (network.networkState) { + case ZigbeeNetwork.ZigbeeNetworkStateOnline: + return "on" + case ZigbeeNetwork.ZigbeeNetworkStateOffline: + return "off" + case ZigbeeNetwork.ZigbeeNetworkStateStarting: + return "orange" + case ZigbeeNetwork.ZigbeeNetworkStateUpdating: + return "orange" + case ZigbeeNetwork.ZigbeeNetworkStateError: + return "red" + } + } + } } - NymeaSwipeDelegate { + Label { Layout.fillWidth: true - text: qsTr("Channel") - subText: root.network.channel - progressive: false + Layout.leftMargin: app.margins + Layout.rightMargin: app.margins + text: qsTr("Channel") + ": " + network.channel } - NymeaSwipeDelegate { + RowLayout { Layout.fillWidth: true - text: qsTr("Network PAN ID") - subText: root.network.panId - progressive: false - } + Layout.leftMargin: app.margins + Layout.rightMargin: app.margins - SettingsPageSectionHeader { - text: qsTr("Hardware information") - } - - NymeaSwipeDelegate { - Layout.fillWidth: true - text: qsTr("MAC address:") - subText: root.network.macAddress - progressive: false - prominentSubText: false - } - - NymeaSwipeDelegate { - Layout.fillWidth: true - text: qsTr("Serial port") - subText: root.network.serialPort - progressive: false - prominentSubText: false - } - - NymeaSwipeDelegate { - Layout.fillWidth: true - text: qsTr("Baud rate") - subText: root.network.baudRate - progressive: false - prominentSubText: false - } - - NymeaSwipeDelegate { - Layout.fillWidth: true - text: qsTr("Controller backend") - subText: root.network.backend - progressive: false - prominentSubText: false - } - - NymeaSwipeDelegate { - Layout.fillWidth: true - text: qsTr("Controller firmware version") - subText: root.network.firmwareVersion - progressive: false - prominentSubText: false - } - - SettingsPageSectionHeader { - text: qsTr("Manage network") - } - - ColumnLayout { - - Button { + Label { Layout.fillWidth: true - Layout.leftMargin: app.margins - Layout.rightMargin: app.margins - text: qsTr("Remove network") - onClicked: { - root.zigbeeManager.removeNetwork(root.network.networkUuid) - pageStack.pop() + text: qsTr("Permit new devices:") + } + Label { + text: network.permitJoiningEnabled ? qsTr("Open for %0 s").arg(network.permitJoiningRemaining) : qsTr("Closed") + } + ColorIcon { + Layout.preferredHeight: Style.iconSize + Layout.preferredWidth: Style.iconSize + name: network.permitJoiningEnabled ? "/ui/images/lock-open.svg" : "/ui/images/lock-closed.svg" + visible: !network.permitJoiningEnabled + } + Canvas { + id: canvas + Layout.preferredHeight: Style.iconSize + Layout.preferredWidth: Style.iconSize + rotation: -90 + visible: network.permitJoiningEnabled + + property real progress: network.permitJoiningRemaining / network.permitJoiningDuration + onProgressChanged: { + canvas.requestPaint() + } + + onPaint: { + var ctx = canvas.getContext("2d"); + ctx.save(); + ctx.reset(); + var data = [1 - progress, progress]; + var myTotal = 0; + + for(var e = 0; e < data.length; e++) { + myTotal += data[e]; + } + + ctx.fillStyle = Style.accentColor + ctx.strokeStyle = Style.accentColor + ctx.lineWidth = 1; + + ctx.beginPath(); + ctx.moveTo(canvas.width/2,canvas.height/2); + ctx.arc(canvas.width/2,canvas.height/2,canvas.height/2,0,(Math.PI*2*((1-progress)/myTotal)),false); + ctx.lineTo(canvas.width/2,canvas.height/2); + ctx.fill(); + ctx.closePath(); + ctx.beginPath(); + ctx.arc(canvas.width/2,canvas.height/2,canvas.height/2 - 1,0,Math.PI*2,false); + ctx.closePath(); + ctx.stroke(); + + ctx.restore(); + } + } + } + + Button { + Layout.fillWidth: true + Layout.leftMargin: app.margins + Layout.rightMargin: app.margins + + text: network.permitJoiningEnabled ? qsTr("Extend open duration") : qsTr("Open for new devices") + enabled: network.networkState === ZigbeeNetwork.ZigbeeNetworkStateOnline + onClicked: zigbeeManager.setPermitJoin(network.networkUuid) + } + + + SettingsPageSectionHeader { + text: qsTr("Zigbee nodes") + } + + Repeater { + id: zigbeeNodeRepeater + model: ZigbeeNodesProxy { + id: zigbeeNodesProxy + zigbeeNodes: root.network.nodes + } + + delegate: BigTile { + + property ZigbeeNode node: root.network.nodes.get(index) + + Layout.fillWidth: true + interactive: false + + contentItem: ColumnLayout { + spacing: app.margins + + Loader { + id: nodeTypeLoader + Layout.fillWidth: true + sourceComponent: node.type === ZigbeeNode.ZigbeeNodeTypeCoordinator ? coordinatorComponent : deviceComponent + } + + Component { + id: coordinatorComponent + ColumnLayout { + RowLayout { + Led { + Layout.preferredHeight: Style.iconSize + Layout.preferredWidth: Style.iconSize + state: { + switch (network.networkState) { + case ZigbeeNetwork.ZigbeeNetworkStateOnline: + return "on" + case ZigbeeNetwork.ZigbeeNetworkStateOffline: + return "off" + case ZigbeeNetwork.ZigbeeNetworkStateStarting: + return "orange" + case ZigbeeNetwork.ZigbeeNetworkStateUpdating: + return "orange" + case ZigbeeNetwork.ZigbeeNetworkStateError: + return "red" + } + } + } + + Label { + Layout.fillWidth: true + text: qsTr("Coordinator") + } + } + + Label { text: network.backend } + Label { + visible: node.version !== "" + text: qsTr("Version") + ": " + network.firmwareVersion + } + Label { + text: qsTr("IEEE address") + ": " + node.ieeeAddress + } + Label { + text: qsTr("Network address") + ": 0x" + (node.networkAddress + Math.pow(16, 4)).toString(16).slice(-4).toUpperCase(); + } + } + } + + Component { + id: deviceComponent + + ColumnLayout { + RowLayout { + Led { + id: reachableLed + Layout.preferredHeight: Style.iconSize + Layout.preferredWidth: Style.iconSize + state: node.reachable ? "on" : "red" + } + + Connections { + target: node + onLastSeenChanged: { + communicationIndicatorLed.state = "on" + communicationIndicatorLedTimer.start() + } + } + + Timer { + id: communicationIndicatorLedTimer + interval: 200 + repeat: false + onTriggered: communicationIndicatorLed.state = "off" + } + + Led { + id: communicationIndicatorLed + Layout.preferredHeight: Style.iconSize + Layout.preferredWidth: Style.iconSize + state: "off" + } + + BusyIndicator { + Layout.preferredHeight: Style.iconSize + Layout.preferredWidth: Style.iconSize + running: visible + visible: node.state !== ZigbeeNode.ZigbeeNodeStateInitialized + } + + Label { + Layout.fillWidth: true + text: node.type === ZigbeeNode.ZigbeeNodeTypeRouter ? qsTr("Router") : qsTr("End device") + } + + Loader { + id: iconLoader + Layout.preferredHeight: Style.iconSize + Layout.preferredWidth: Style.iconSize + active: !node.rxOnWhenIdle + sourceComponent: sleepyDeviceComponent + } + + Component { + id: sleepyDeviceComponent + ColorIcon { + name: "/ui/images/system-suspend.svg" + } + } + } + + Label { + text: node.manufacturer + " - " + node.model + } + + Label { + visible: node.version !== "" + text: qsTr("Version") + ": " + node.version + } + + Label { + text: qsTr("IEEE address") + ": " + node.ieeeAddress + } + + Label { + text: qsTr("Network address") + ": 0x" + (node.networkAddress + Math.pow(16, 4)).toString(16).slice(-4).toUpperCase(); + } + + Label { + text: qsTr("Signal strength") + ": " + Math.round(node.lqi * 100.0 / 255.0) + "%" + } + + Button { + id: removeNodeButton + text: qsTr("Remove node") + onClicked: d.removeNode(network.networkUuid, node.ieeeAddress) + } + } + } } } - Button { - Layout.fillWidth: true - Layout.leftMargin: app.margins - Layout.rightMargin: app.margins - text: qsTr("Factory reset controller") - onClicked: { - root.zigbeeManager.factoryResetNetwork(root.network.networkUuid) - } - } + + // NymeaSwipeDelegate { + // Layout.fillWidth: true + // iconName: "../images/zigbee.svg" + // text: node.manufacturer + " - " + node.model + " - " + node.version + // subText: node.ieeeAddress + " " + node.networkAddress + // } + } } diff --git a/nymea-app/ui/system/ZigbeeNetworkSettingsPage.qml b/nymea-app/ui/system/ZigbeeNetworkSettingsPage.qml new file mode 100644 index 00000000..cbb51b25 --- /dev/null +++ b/nymea-app/ui/system/ZigbeeNetworkSettingsPage.qml @@ -0,0 +1,161 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* 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 + + property ZigbeeManager zigbeeManager: null + property ZigbeeNetwork network: null + + header: NymeaHeader { + text: qsTr("ZigBee network settings") + backButtonVisible: true + onBackPressed: pageStack.pop() + + } + + SettingsPageSectionHeader { + text: qsTr("Network information") + } + + NymeaSwipeDelegate { + Layout.fillWidth: true + text: qsTr("Network state") + subText: { + switch (root.network.networkState) { + case ZigbeeNetwork.ZigbeeNetworkStateOnline: + return qsTr("The network is online") + case ZigbeeNetwork.ZigbeeNetworkStateOffline: + return qsTr("The network is offline") + case ZigbeeNetwork.ZigbeeNetworkStateStarting: + return qsTr("The network is starting...") + case ZigbeeNetwork.ZigbeeNetworkStateUpdating: + return qsTr("The controller is currently installing an update") + case ZigbeeNetwork.ZigbeeNetworkStateError: + return qsTr("The network is in an error state.") + } + } + + progressive: false + } + + NymeaSwipeDelegate { + Layout.fillWidth: true + text: qsTr("Channel") + subText: root.network ? root.network.channel : "" + progressive: false + } + + NymeaSwipeDelegate { + Layout.fillWidth: true + text: qsTr("Network PAN ID") + subText: root.network ? root.network.panId : "" + progressive: false + } + + SettingsPageSectionHeader { + text: qsTr("Hardware information") + } + + NymeaSwipeDelegate { + Layout.fillWidth: true + text: qsTr("MAC address:") + subText: root.network ? root.network.macAddress : "" + progressive: false + prominentSubText: false + } + + NymeaSwipeDelegate { + Layout.fillWidth: true + text: qsTr("Serial port") + subText: root.network ? root.network.serialPort : "" + progressive: false + prominentSubText: false + } + + NymeaSwipeDelegate { + Layout.fillWidth: true + text: qsTr("Baud rate") + subText: root.network ? root.network.baudRate : "" + progressive: false + prominentSubText: false + } + + NymeaSwipeDelegate { + Layout.fillWidth: true + text: qsTr("Controller backend") + subText: root.network ? root.network.backend : "" + progressive: false + prominentSubText: false + } + + NymeaSwipeDelegate { + Layout.fillWidth: true + text: qsTr("Controller firmware version") + subText: root.network ? root.network.firmwareVersion : "" + progressive: false + prominentSubText: false + } + + SettingsPageSectionHeader { + text: qsTr("Manage network") + } + + ColumnLayout { + + Button { + Layout.fillWidth: true + Layout.leftMargin: app.margins + Layout.rightMargin: app.margins + text: qsTr("Remove network") + onClicked: { + root.zigbeeManager.removeNetwork(root.network.networkUuid) + pageStack.pop() + } + } + + Button { + Layout.fillWidth: true + Layout.leftMargin: app.margins + Layout.rightMargin: app.margins + text: qsTr("Factory reset controller") + onClicked: { + root.zigbeeManager.factoryResetNetwork(root.network.networkUuid) + } + } + } +} diff --git a/nymea-app/ui/system/ZigbeeSettingsPage.qml b/nymea-app/ui/system/ZigbeeSettingsPage.qml index dcf68ac6..e075bcb1 100644 --- a/nymea-app/ui/system/ZigbeeSettingsPage.qml +++ b/nymea-app/ui/system/ZigbeeSettingsPage.qml @@ -81,8 +81,11 @@ SettingsPageBase { Layout.fillWidth: true interactive: false + onClicked: pageStack.push(Qt.resolvedUrl("ZigbeeNetworkPage.qml"), { zigbeeManager: zigbeeManager, network: zigbeeManager.networks.get(index) }) + contentItem: ColumnLayout { spacing: app.margins + RowLayout { ColorIcon { name: "/ui/images/zigbee/" + model.backend + ".svg" @@ -95,14 +98,8 @@ SettingsPageBase { text: model.backend font.pixelSize: app.largeFont } - - ProgressButton { - size: Style.iconSize - imageSource: "/ui/images/configure.svg" - longpressEnabled: false - onClicked: pageStack.push(Qt.resolvedUrl("ZigbeeNetworkPage.qml"), { zigbeeManager: zigbeeManager, network: zigbeeManager.networks.get(index) }) - } } + RowLayout { Label { Layout.fillWidth: true @@ -148,65 +145,21 @@ SettingsPageBase { RowLayout { Label { Layout.fillWidth: true - text: qsTr("Permit new devices:") + text: qsTr("MAC address:") } Label { - text: model.permitJoiningEnabled ? qsTr("Open for %0 s").arg(model.permitJoiningRemaining) : qsTr("Closed") - } - ColorIcon { - Layout.preferredHeight: Style.iconSize - Layout.preferredWidth: Style.iconSize - name: model.permitJoiningEnabled ? "/ui/images/lock-open.svg" : "/ui/images/lock-closed.svg" - visible: !model.permitJoiningEnabled - } - Canvas { - id: canvas - Layout.preferredHeight: Style.iconSize - Layout.preferredWidth: Style.iconSize - rotation: -90 - visible: model.permitJoiningEnabled - - property real progress: model.permitJoiningRemaining / model.permitJoiningDuration - onProgressChanged: { - canvas.requestPaint() - } - - onPaint: { - var ctx = canvas.getContext("2d"); - ctx.save(); - ctx.reset(); - var data = [1 - progress, progress]; - var myTotal = 0; - - for(var e = 0; e < data.length; e++) { - myTotal += data[e]; - } - - ctx.fillStyle = Style.accentColor - ctx.strokeStyle = Style.accentColor - ctx.lineWidth = 1; - - ctx.beginPath(); - ctx.moveTo(canvas.width/2,canvas.height/2); - ctx.arc(canvas.width/2,canvas.height/2,canvas.height/2,0,(Math.PI*2*((1-progress)/myTotal)),false); - ctx.lineTo(canvas.width/2,canvas.height/2); - ctx.fill(); - ctx.closePath(); - ctx.beginPath(); - ctx.arc(canvas.width/2,canvas.height/2,canvas.height/2 - 1,0,Math.PI*2,false); - ctx.closePath(); - ctx.stroke(); - - ctx.restore(); - } + text: model.macAddress } } - Button { - Layout.fillWidth: true - text: model.permitJoiningEnabled ? qsTr("Extend open duration") : qsTr("Open for new devices") - enabled: model.networkState === ZigbeeNetwork.ZigbeeNetworkStateOnline - onClicked: zigbeeManager.setPermitJoin(model.networkUuid) + RowLayout { + Label { + Layout.fillWidth: true + text: qsTr("Firmware version:") + } + Label { + text: model.firmwareVersion + } } } }