From 5085487bda608a68d4a2f118af86f63bdd924e22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Thu, 4 Jun 2020 21:58:21 +0200 Subject: [PATCH] Implement first attempt for network database loading and saving --- .../backends/deconz/zigbeenetworkdeconz.cpp | 6 +- libnymea-zigbee/zcl/zigbeecluster.cpp | 10 + libnymea-zigbee/zcl/zigbeecluster.h | 4 + libnymea-zigbee/zigbeenetwork.cpp | 39 ++- libnymea-zigbee/zigbeenetwork.h | 4 +- libnymea-zigbee/zigbeenetworkdatabase.cpp | 300 +++++++++++++++++- libnymea-zigbee/zigbeenetworkdatabase.h | 24 +- libnymea-zigbee/zigbeenode.h | 1 + libnymea-zigbee/zigbeenodeendpoint.h | 1 + 9 files changed, 377 insertions(+), 12 deletions(-) diff --git a/libnymea-zigbee/backends/deconz/zigbeenetworkdeconz.cpp b/libnymea-zigbee/backends/deconz/zigbeenetworkdeconz.cpp index de90eeb..8ddd1fe 100644 --- a/libnymea-zigbee/backends/deconz/zigbeenetworkdeconz.cpp +++ b/libnymea-zigbee/backends/deconz/zigbeenetworkdeconz.cpp @@ -354,11 +354,7 @@ void ZigbeeNetworkDeconz::handleZigbeeClusterLibraryIndication(const Zigbee::Aps if (!node) { qCWarning(dcZigbeeNetwork()) << "Received a ZCL indication for an unrecognized node. There is no such node in the system. Ignoring indication" << indication; // FIXME: maybe create and init the node, since it is in the network, but not recognized - - // Remove this node since we might have removed it but it did not respond, or we not explicitly allowed it to join. - - - + // FIXME: maybe remove this node since we might have removed it but it did not respond, or we not explicitly allowed it to join. return; } diff --git a/libnymea-zigbee/zcl/zigbeecluster.cpp b/libnymea-zigbee/zcl/zigbeecluster.cpp index 7dd4cda..258066f 100644 --- a/libnymea-zigbee/zcl/zigbeecluster.cpp +++ b/libnymea-zigbee/zcl/zigbeecluster.cpp @@ -46,6 +46,16 @@ ZigbeeCluster::ZigbeeCluster(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNod //qCDebug(dcZigbeeCluster()) << "Create cluster" << ZigbeeUtils::convertUint16ToHexString(clusterId) << direction; } +ZigbeeNode *ZigbeeCluster::node() const +{ + return m_node; +} + +ZigbeeNodeEndpoint *ZigbeeCluster::endpoint() const +{ + return m_endpoint; +} + ZigbeeCluster::Direction ZigbeeCluster::direction() const { return m_direction; diff --git a/libnymea-zigbee/zcl/zigbeecluster.h b/libnymea-zigbee/zcl/zigbeecluster.h index 429338e..01728ca 100644 --- a/libnymea-zigbee/zcl/zigbeecluster.h +++ b/libnymea-zigbee/zcl/zigbeecluster.h @@ -66,6 +66,7 @@ class ZigbeeCluster : public QObject friend class ZigbeeNode; friend class ZigbeeNetwork; + friend class ZigbeeNetworkDatabase; public: enum Direction { @@ -124,6 +125,9 @@ public: explicit ZigbeeCluster(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Zigbee::ClusterId clusterId, Direction direction, QObject *parent = nullptr); + ZigbeeNode *node() const; + ZigbeeNodeEndpoint *endpoint() const; + Direction direction() const; Zigbee::ClusterId clusterId() const; diff --git a/libnymea-zigbee/zigbeenetwork.cpp b/libnymea-zigbee/zigbeenetwork.cpp index 8d34c8d..daf80a8 100644 --- a/libnymea-zigbee/zigbeenetwork.cpp +++ b/libnymea-zigbee/zigbeenetwork.cpp @@ -30,7 +30,8 @@ #include "loggingcategory.h" #include "zdo/zigbeedeviceprofile.h" -#include +#include +#include ZigbeeNetwork::ZigbeeNetwork(QObject *parent) : QObject(parent) @@ -231,7 +232,6 @@ bool ZigbeeNetwork::hasNode(const ZigbeeAddress &address) const void ZigbeeNetwork::removeZigbeeNode(const ZigbeeAddress &address) { - ZigbeeNode *node = getZigbeeNode(address); if (!node) { qCWarning(dcZigbeeNetwork()) << "Failed remove zigbee node since there is no node with" << address; @@ -313,6 +313,12 @@ void ZigbeeNetwork::saveNetwork() void ZigbeeNetwork::loadNetwork() { qCDebug(dcZigbeeNetwork()) << "Load current network configuration from" << m_settingsFileName; + + if (!m_database) { + QDir storagePath = QFileInfo(m_settingsFileName).absoluteDir(); + m_database = new ZigbeeNetworkDatabase(this, storagePath.absolutePath() + QDir::separator() + "zigbee-network.db", this); + } + QSettings settings(m_settingsFileName, QSettings::IniFormat, this); settings.beginGroup("Network"); @@ -381,6 +387,35 @@ void ZigbeeNetwork::loadNetwork() addNodeInternally(node); } settings.endGroup(); // Nodes + + qCWarning(dcZigbeeNetwork()) << this; + + // FIXME: for testing + foreach(ZigbeeNode *node, m_nodes) { + m_database->saveNode(node); + } + + // Now load and print the nodes again for checking + QList dbNodes = m_database->loadNodes(); + + foreach (ZigbeeNode *node, dbNodes) { + qCWarning(dcZigbeeNetwork()) << " ---> " << node << endl; + qCWarning(dcZigbeeNetwork()) << " " << node->nodeDescriptor(); + qCWarning(dcZigbeeNetwork()) << " " << node->powerDescriptor(); + qCWarning(dcZigbeeNetwork()) << " Endpoints: " << node->endpoints().count() << endl; + foreach (ZigbeeNodeEndpoint *endpoint, node->endpoints()) { + qCWarning(dcZigbeeNetwork()) << " - " << endpoint << endl; + qCWarning(dcZigbeeNetwork()) << " Input clusters:" << endl; + foreach (ZigbeeCluster *cluster, endpoint->inputClusters()) { + qCWarning(dcZigbeeNetwork()) << " - " << cluster << endl; + } + qCWarning(dcZigbeeNetwork()) << " Output clusters:" << endl; + foreach (ZigbeeCluster *cluster, endpoint->outputClusters()) { + qCWarning(dcZigbeeNetwork()) << " - " << cluster << endl; + } + } + } + } void ZigbeeNetwork::clearSettings() diff --git a/libnymea-zigbee/zigbeenetwork.h b/libnymea-zigbee/zigbeenetwork.h index e034b5a..b3d1eba 100644 --- a/libnymea-zigbee/zigbeenetwork.h +++ b/libnymea-zigbee/zigbeenetwork.h @@ -34,8 +34,9 @@ #include #include "zigbeenode.h" -#include "zigbeenodeendpoint.h" #include "zigbeechannelmask.h" +#include "zigbeenodeendpoint.h" +#include "zigbeenetworkdatabase.h" #include "zigbeebridgecontroller.h" #include "zigbeesecurityconfiguration.h" @@ -132,6 +133,7 @@ private: ZigbeeDeviceProfile::NodeType m_nodeType = ZigbeeDeviceProfile::NodeTypeCoordinator; // Network storage + ZigbeeNetworkDatabase *m_database = nullptr; QString m_settingsFileName = "/etc/nymea/nymea-zigbee.conf"; QList m_nodes; QList m_uninitializedNodes; diff --git a/libnymea-zigbee/zigbeenetworkdatabase.cpp b/libnymea-zigbee/zigbeenetworkdatabase.cpp index e60a851..c81afef 100644 --- a/libnymea-zigbee/zigbeenetworkdatabase.cpp +++ b/libnymea-zigbee/zigbeenetworkdatabase.cpp @@ -27,8 +27,16 @@ #include "zigbeenetworkdatabase.h" #include "loggingcategory.h" +#include "zigbeenetwork.h" +#include "zigbeeutils.h" +#include "zigbeenode.h" -ZigbeeNetworkDatabase::ZigbeeNetworkDatabase(const QString &databaseName, QObject *parent) : QObject(parent) +#include +#include + +ZigbeeNetworkDatabase::ZigbeeNetworkDatabase(ZigbeeNetwork *network, const QString &databaseName, QObject *parent) : + QObject(parent), + m_network(network) { m_db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), "zigbee"); m_db.setDatabaseName(databaseName); @@ -36,7 +44,295 @@ ZigbeeNetworkDatabase::ZigbeeNetworkDatabase(const QString &databaseName, QObjec if (!m_db.isValid()) { qCWarning(dcZigbeeNetworkDatabase()) << "The zigbee network database is not valid" << m_db.databaseName(); - // FIXME: create a new one + // FIXME: rotate database + return; + } + + if (!initDatabase()) { + qCWarning(dcZigbeeNetworkDatabase()) << "Failed to initialize the database" << m_db.databaseName(); + // FIXME: rotate database return; } } + +QList ZigbeeNetworkDatabase::loadNodes() +{ + qCDebug(dcZigbeeNetworkDatabase()) << "Loading nodes from database" << m_db.databaseName(); + QList nodes; + QString query("SELECT * FROM nodes;"); + QSqlQuery nodesQuery = m_db.exec(query); + while (nodesQuery.next()) { + quint64 ieeeAddress = nodesQuery.value("ieeeAddress").toULongLong(); + quint16 shortAddress = nodesQuery.value("shortAddress").toUInt(); + QByteArray nodeDescriptor = QByteArray::fromBase64(nodesQuery.value("nodeDescriptor").toByteArray()); + quint16 powerDescriptor = nodesQuery.value("powerDescriptor").toUInt(); + + // Build the node object + ZigbeeNode *node = new ZigbeeNode(m_network, shortAddress, ZigbeeAddress(ieeeAddress), m_network); + node->m_nodeDescriptor = ZigbeeDeviceProfile::parseNodeDescriptor(nodeDescriptor); + node->m_powerDescriptor = ZigbeeDeviceProfile::parsePowerDescriptor(powerDescriptor); + + qCDebug(dcZigbeeNetworkDatabase()) << "Loaded node" << node; + + // Now load all endpoints for this node + query = QString("SELECT * FROM endpoints WHERE ieeeAddress = \"%1\";").arg(ieeeAddress); + QSqlQuery endpointsQuery = m_db.exec(query); + while (endpointsQuery.next()) { + quint8 endpointId = endpointsQuery.value("endpointId").toUInt(); + ZigbeeNodeEndpoint *endpoint = new ZigbeeNodeEndpoint(m_network, node, endpointId, node); + endpoint->setProfile(static_cast(endpointsQuery.value("profileId").toUInt())); + endpoint->setDeviceId(static_cast(endpointsQuery.value("deviceId").toUInt())); + endpoint->setDeviceVersion(static_cast(endpointsQuery.value("deviceVersion").toUInt())); + + qCDebug(dcZigbeeNetworkDatabase()) << "Loaded endpoint" << endpoint; + + // Load input clusters for this endpoint + query = QString("SELECT * FROM serverClusters WHERE ieeeAddress = \"%1\" AND endpointId = \"%2\";").arg(ieeeAddress).arg(endpointId); + QSqlQuery inputClustersQuery = m_db.exec(query); + while (inputClustersQuery.next()) { + Zigbee::ClusterId clusterId = static_cast(inputClustersQuery.value("clusterId").toUInt()); + ZigbeeCluster *cluster = endpoint->createCluster(clusterId, ZigbeeCluster::Server); + endpoint->addInputCluster(cluster); + + // Load cluster attributes for the server cluster + query = QString("SELECT * FROM attributes WHERE ieeeAddress = \"%1\" AND endpointId = \"%2\" AND clusterId = \"%3\";") + .arg(ieeeAddress) + .arg(endpointId) + .arg(cluster->clusterId()); + QSqlQuery attributesQuery = m_db.exec(query); + while (attributesQuery.next()) { + quint16 attributeId = attributesQuery.value("attributeId").toUInt(); + Zigbee::DataType type = static_cast(attributesQuery.value("dataType").toUInt()); + QByteArray data = attributesQuery.value("data").toByteArray(); + cluster->setAttribute(ZigbeeClusterAttribute(attributeId, ZigbeeDataType(type, data))); + } + } + + // Load output clusters for this endpoint + query = QString("SELECT * FROM clientClusters WHERE ieeeAddress = \"%1\" AND endpointId = \"%2\";").arg(ieeeAddress).arg(endpointId); + QSqlQuery outputClustersQuery = m_db.exec(query); + while (outputClustersQuery.next()) { + Zigbee::ClusterId clusterId = static_cast(outputClustersQuery.value("clusterId").toUInt()); + ZigbeeCluster *cluster = endpoint->createCluster(clusterId, ZigbeeCluster::Client); + endpoint->addOutputCluster(cluster); + } + + node->m_endpoints.append(endpoint); + } + nodes.append(node); + } + + return nodes; +} + +bool ZigbeeNetworkDatabase::initDatabase() +{ + // Reopen the db to make sure it can be written + m_db.close(); + if (!m_db.open()) { + qCWarning(dcZigbeeNetworkDatabase()) << "Could not open zigbee database" << m_db.databaseName() << "Initialization failed."; + return false; + } + + // Enable foreigen keys + m_db.exec("PRAGMA foreign_keys = ON;"); + + // Create nodes table + createTable("nodes", "(ieeeAddress INTEGER PRIMARY KEY, " // uint64 + "shortAddress INTEGER NOT NULL, " // uint16 + "nodeDescriptor BLOB NOT NULL, " // bytes as received from the node + "powerDescriptor INTEGER NOT NULL)"); // uint16 + createIndices("ieeeAddressIndex", "nodes", "ieeeAddress"); + + + // Create endpoints table + createTable("endpoints", "(id INTEGER PRIMARY KEY AUTOINCREMENT, " // for db relation + "ieeeAddress INTEGER NOT NULL, " // uint64 + "endpointId INTEGER NOT NULL, " // uint8 + "profileId INTEGER NOT NULL, " // uint16 + "deviceId INTEGER NOT NULL, " // uint16 + "deviceVersion INTEGER, " // uint8 + "CONSTRAINT fk_ieeeAddress FOREIGN KEY(ieeeAddress) REFERENCES nodes(ieeeAddress) ON DELETE CASCADE)"); + createIndices("endpointIndex", "endpoints", "ieeeAddress, endpointId"); + + + // Create server cluster table + createTable("serverClusters", "(id INTEGER PRIMARY KEY AUTOINCREMENT, " // for db relation + "ieeeAddress INTEGER NOT NULL, " // uint64 + "endpointId INTEGER NOT NULL, " // uint8 + "clusterId INTEGER NOT NULL, " // uint16 + "CONSTRAINT fk_endpoint FOREIGN KEY(ieeeAddress, endpointId) REFERENCES endpoints(ieeeAddress, endpointId) ON DELETE CASCADE)"); + createIndices("serverClusterIndex", "serverClusters", "ieeeAddress, endpointId, clusterId"); + + + // Create client cluster table + createTable("clientClusters", "(id INTEGER PRIMARY KEY AUTOINCREMENT, " // for db relation + "ieeeAddress INTEGER NOT NULL, " // uint64 + "endpointId INTEGER NOT NULL, " // uint8 + "clusterId INTEGER NOT NULL, " // uint16 + "CONSTRAINT fk_endpoint FOREIGN KEY(ieeeAddress, endpointId) REFERENCES endpoints(ieeeAddress, endpointId) ON DELETE CASCADE)"); + createIndices("clientClusterIndex", "clientClusters", "ieeeAddress, endpointId, clusterId"); + + // Create cluster attributes table + createTable("attributes", "(id INTEGER PRIMARY KEY AUTOINCREMENT, " // for db relation + "ieeeAddress INTEGER NOT NULL, " // uint64 + "endpointId INTEGER NOT NULL, " // uint8 + "clusterId INTEGER NOT NULL, " // uint16 + "attributeId INTEGER NOT NULL, " // uint16 + "dataType INTEGER NOT NULL, " // uint8 + "data BLOB NOT NULL, " // raw data from attribute + "CONSTRAINT fk_cluster FOREIGN KEY(ieeeAddress, endpointId, clusterId) REFERENCES serverClusters(ieeeAddress, endpointId, clusterId) ON DELETE CASCADE)"); + createIndices("attributesIndex", "attributes", "ieeeAddress, endpointId, clusterId, attributeId"); + + return true; +} + +void ZigbeeNetworkDatabase::createTable(const QString &tableName, const QString &schema) +{ + m_db.exec(QString("CREATE TABLE IF NOT EXISTS %1 %2;").arg(tableName).arg(schema)); + m_db.exec(QString("PRAGMA schema_version = %1;").arg(DB_VERSION)); + m_db.exec(QString("PRAGMA user_version = %1;").arg(DB_VERSION)); +} + +void ZigbeeNetworkDatabase::createIndices(const QString &indexName, const QString &tableName, const QString &columns) +{ + m_db.exec(QString("CREATE UNIQUE INDEX IF NOT EXISTS %1 ON %2(%3);").arg(indexName).arg(tableName).arg(columns)); +} + +bool ZigbeeNetworkDatabase::saveNodeEndpoint(ZigbeeNodeEndpoint *endpoint) +{ + qCDebug(dcZigbeeNetworkDatabase()) << "Store" << endpoint; + QString queryString = QString("INSERT OR REPLACE INTO endpoints (ieeeAddress, endpointId, profileId, deviceId, deviceVersion) " + "VALUES (\"%1\", \"%2\", \"%3\", \"%4\", \"%5\");") + .arg(endpoint->node()->extendedAddress().toUInt64()) + .arg(endpoint->endpointId()) + .arg(static_cast(endpoint->profile())) + .arg(static_cast(endpoint->deviceId())) + .arg(static_cast(endpoint->deviceVersion())); + + m_db.exec(queryString); + if (m_db.lastError().type() != QSqlError::NoError) { + qCWarning(dcZigbeeNetworkDatabase()) << "Could not save node into database." << m_db.lastError().databaseText() << m_db.lastError().driverText(); + return false; + } + + // Save input/output clusters + foreach(ZigbeeCluster *cluster, endpoint->inputClusters()) { + if (!saveInputCluster(cluster)) { + return false; + } + + foreach(const ZigbeeClusterAttribute &attribute, cluster->attributes()) { + if (!saveAttribute(cluster, attribute)) { + return false; + } + } + } + + foreach(ZigbeeCluster *cluster, endpoint->outputClusters()) { + if (!saveOutputCluster(cluster)) { + return false; + } + } + + return true; +} + +bool ZigbeeNetworkDatabase::saveInputCluster(ZigbeeCluster *cluster) +{ + qCDebug(dcZigbeeNetworkDatabase()) << "Store" << cluster; + QString queryString = QString("INSERT OR REPLACE INTO serverClusters (ieeeAddress, endpointId, clusterId) " + "VALUES (\"%1\", \"%2\", \"%3\");") + .arg(cluster->node()->extendedAddress().toUInt64()) + .arg(cluster->endpoint()->endpointId()) + .arg(static_cast(cluster->clusterId())); + + m_db.exec(queryString); + if (m_db.lastError().type() != QSqlError::NoError) { + qCWarning(dcZigbeeNetworkDatabase()) << "Could not save input cluster into database." << m_db.lastError().databaseText() << m_db.lastError().driverText(); + return false; + } + + return true; +} + +bool ZigbeeNetworkDatabase::saveOutputCluster(ZigbeeCluster *cluster) +{ + qCDebug(dcZigbeeNetworkDatabase()) << "Store" << cluster; + QString queryString = QString("INSERT OR REPLACE INTO clientClusters (ieeeAddress, endpointId, clusterId) " + "VALUES (\"%1\", \"%2\", \"%3\");") + .arg(cluster->node()->extendedAddress().toUInt64()) + .arg(cluster->endpoint()->endpointId()) + .arg(static_cast(cluster->clusterId())); + + m_db.exec(queryString); + if (m_db.lastError().type() != QSqlError::NoError) { + qCWarning(dcZigbeeNetworkDatabase()) << "Could not save output cluster into database." << m_db.lastError().databaseText() << m_db.lastError().driverText(); + return false; + } + + return true; +} + +bool ZigbeeNetworkDatabase::saveAttribute(ZigbeeCluster *cluster, const ZigbeeClusterAttribute &attribute) +{ + qCDebug(dcZigbeeNetworkDatabase()) << "Store" << cluster << attribute; + QString queryString = QString("INSERT OR REPLACE INTO attributes (ieeeAddress, endpointId, clusterId, attributeId, dataType, data) " + "VALUES (\"%1\", \"%2\", \"%3\", \"%4\", \"%5\", \"%6\");") + .arg(cluster->node()->extendedAddress().toUInt64()) + .arg(cluster->endpoint()->endpointId()) + .arg(static_cast(cluster->clusterId())) + .arg(static_cast(attribute.id())) + .arg(static_cast(attribute.dataType().dataType())) + .arg((attribute.dataType().data().toBase64().data())); + + m_db.exec(queryString); + if (m_db.lastError().type() != QSqlError::NoError) { + qCWarning(dcZigbeeNetworkDatabase()) << "Could not save cluster cluster attribute into database." << m_db.lastError().databaseText() << m_db.lastError().driverText(); + return false; + } + + return true; +} + +bool ZigbeeNetworkDatabase::saveNode(ZigbeeNode *node) +{ + qCDebug(dcZigbeeNetworkDatabase()) << "Store" << node; + QString queryString = QString("INSERT OR REPLACE INTO nodes (ieeeAddress, shortAddress, nodeDescriptor, powerDescriptor) " + "VALUES (\"%1\", \"%2\", \"%3\", \"%4\");") + .arg(node->extendedAddress().toUInt64()) + .arg(node->shortAddress()) + .arg(node->nodeDescriptor().descriptorRawData.toBase64().data()) // Note: convert to base64 for saving zeros as string + .arg(node->powerDescriptor().powerDescriptoFlag); + + m_db.exec(queryString); + if (m_db.lastError().type() != QSqlError::NoError) { + qCWarning(dcZigbeeNetworkDatabase()) << "Could not save node into database." << m_db.lastError().databaseText() << m_db.lastError().driverText(); + return false; + } + + // Save endpoints + foreach (ZigbeeNodeEndpoint *endpoint, node->endpoints()) { + if (!saveNodeEndpoint(endpoint)) { + return false; + } + } + + return true; +} + +bool ZigbeeNetworkDatabase::removeNode(ZigbeeNode *node) +{ + qCDebug(dcZigbeeNetworkDatabase()) << "Remove" << node; + // Note: cascade delete will clean up all other tables + QString queryString = QString("DELETE FROM nodes WHERE ieeeAddress = %1;").arg(node->extendedAddress().toUInt64()); + m_db.exec(queryString); + if (m_db.lastError().type() != QSqlError::NoError) { + qCWarning(dcZigbeeNetworkDatabase()) << "Could not remove node from database." << m_db.lastError().databaseText() << m_db.lastError().driverText(); + return false; + } + + return true; +} + + diff --git a/libnymea-zigbee/zigbeenetworkdatabase.h b/libnymea-zigbee/zigbeenetworkdatabase.h index 0abbfee..b8795a7 100644 --- a/libnymea-zigbee/zigbeenetworkdatabase.h +++ b/libnymea-zigbee/zigbeenetworkdatabase.h @@ -31,18 +31,38 @@ #include #include +#define DB_VERSION 1 + +class ZigbeeNode; +class ZigbeeCluster; +class ZigbeeNetwork; +class ZigbeeNodeEndpoint; +class ZigbeeClusterAttribute; class ZigbeeNetworkDatabase : public QObject { Q_OBJECT public: - explicit ZigbeeNetworkDatabase(const QString &databaseName, QObject *parent = nullptr); + explicit ZigbeeNetworkDatabase(ZigbeeNetwork *network, const QString &databaseName, QObject *parent = nullptr); + + QList loadNodes(); private: + ZigbeeNetwork *m_network = nullptr; QSqlDatabase m_db; + bool initDatabase(); + void createTable(const QString &tableName, const QString &schema); + void createIndices(const QString &indexName, const QString &tableName, const QString &columns); -signals: + bool saveNodeEndpoint(ZigbeeNodeEndpoint *endpoint); + bool saveInputCluster(ZigbeeCluster *cluster); + bool saveOutputCluster(ZigbeeCluster *cluster); + bool saveAttribute(ZigbeeCluster *cluster, const ZigbeeClusterAttribute &attribute); + +public slots: + bool saveNode(ZigbeeNode *node); + bool removeNode(ZigbeeNode *node); }; diff --git a/libnymea-zigbee/zigbeenode.h b/libnymea-zigbee/zigbeenode.h index f70ddb1..4fd3221 100644 --- a/libnymea-zigbee/zigbeenode.h +++ b/libnymea-zigbee/zigbeenode.h @@ -43,6 +43,7 @@ class ZigbeeNode : public QObject Q_OBJECT friend class ZigbeeNetwork; + friend class ZigbeeNetworkDatabase; public: enum State { diff --git a/libnymea-zigbee/zigbeenodeendpoint.h b/libnymea-zigbee/zigbeenodeendpoint.h index 74ea2df..3641cc4 100644 --- a/libnymea-zigbee/zigbeenodeendpoint.h +++ b/libnymea-zigbee/zigbeenodeendpoint.h @@ -58,6 +58,7 @@ class ZigbeeNodeEndpoint : public QObject friend class ZigbeeNode; friend class ZigbeeNetwork; + friend class ZigbeeNetworkDatabase; public: quint8 endpointId() const;