From 7745f095815f86ebca7d5e7947811190f9f6ce80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Thu, 16 May 2019 02:54:17 +0200 Subject: [PATCH] Add cluster and attribute base classes and improve node initialization --- libnymea-zigbee/interface/zigbeeinterface.cpp | 13 +- libnymea-zigbee/libnymea-zigbee.pro | 4 + libnymea-zigbee/loggingcategory.cpp | 1 + libnymea-zigbee/loggingcategory.h | 1 + libnymea-zigbee/zigbee.h | 19 ++ libnymea-zigbee/zigbeebridgecontroller.cpp | 9 +- libnymea-zigbee/zigbeebridgecontroller.h | 1 + libnymea-zigbee/zigbeecluster.cpp | 59 +++++ libnymea-zigbee/zigbeecluster.h | 42 +++ libnymea-zigbee/zigbeeclusterattribute.cpp | 73 ++++++ libnymea-zigbee/zigbeeclusterattribute.h | 34 +++ libnymea-zigbee/zigbeenetwork.cpp | 163 ++++++++++-- libnymea-zigbee/zigbeenetwork.h | 11 +- libnymea-zigbee/zigbeenetworkmanager.cpp | 248 +++++++++++------- libnymea-zigbee/zigbeenode.cpp | 106 ++++++++ libnymea-zigbee/zigbeenode.h | 45 +++- .../zigbeesecurityconfiguration.cpp | 14 +- libnymea-zigbee/zigbeesecurityconfiguration.h | 11 + libnymea-zigbee/zigbeeutils.cpp | 3 +- 19 files changed, 725 insertions(+), 132 deletions(-) create mode 100644 libnymea-zigbee/zigbeecluster.cpp create mode 100644 libnymea-zigbee/zigbeecluster.h create mode 100644 libnymea-zigbee/zigbeeclusterattribute.cpp create mode 100644 libnymea-zigbee/zigbeeclusterattribute.h diff --git a/libnymea-zigbee/interface/zigbeeinterface.cpp b/libnymea-zigbee/interface/zigbeeinterface.cpp index 7bdddc2..e6ac6b1 100644 --- a/libnymea-zigbee/interface/zigbeeinterface.cpp +++ b/libnymea-zigbee/interface/zigbeeinterface.cpp @@ -14,6 +14,7 @@ ZigbeeInterface::ZigbeeInterface(QObject *parent) : ZigbeeInterface::~ZigbeeInterface() { + qCCritical(dcZigbeeInterface()) << "Destroy interface"; disable(); } @@ -127,14 +128,21 @@ void ZigbeeInterface::onReadyRead() // Check message sanity quint8 crc = calculateCrc(m_messageTypeValue, m_lengthValue, m_data); if (crc != m_crcValue) { - qCWarning(dcZigbeeInterface()) << "Invalid CRC value" << crc << "!=" << m_crcValue; + qCWarning(dcZigbeeInterface()) << "Received message end: Invalid CRC value" << crc << "!=" << m_crcValue; } else if (m_data.count() != m_lengthValue) { - qCWarning(dcZigbeeInterface()) << "ERROR:s Invalid data length" << m_data.count() << "!=" << m_lengthValue; + qCWarning(dcZigbeeInterface()) << "Received message end: Invalid data length of message" << m_data.count() << "!=" << m_lengthValue; } else { // We got a valid message ZigbeeInterfaceMessage message(messageType, m_data); qCDebug(dcZigbeeInterface()) << "<--" << message << "|" << "crc:" << ZigbeeUtils::convertByteToHexString(m_crcValue) << ", length:" << ZigbeeUtils::convertUint16ToHexString(m_lengthValue); emit messageReceived(message); + + // Clear all information for the next byte + m_crcValue = 0; + m_messageTypeValue = 0; + m_lengthValue = 0; + m_escapeDetected = false; + m_data.clear(); } setReadingState(WaitForStart); break; @@ -150,6 +158,7 @@ void ZigbeeInterface::onReadyRead() // Read data bytes depending on the reading state switch (m_readingState) { case WaitForStart: + qCWarning(dcZigbeeInterfaceTraffic()) << "Wait for start but reviced data:" << byte; break; case WaitForTypeMsb: m_messageTypeValue = byte; diff --git a/libnymea-zigbee/libnymea-zigbee.pro b/libnymea-zigbee/libnymea-zigbee.pro index e0c46e0..7a7290d 100644 --- a/libnymea-zigbee/libnymea-zigbee.pro +++ b/libnymea-zigbee/libnymea-zigbee.pro @@ -11,6 +11,8 @@ SOURCES += \ interface/zigbeeinterfacerequest.cpp \ interface/zigbeeinterfacereply.cpp \ nxp/nxpzigbeenetworkmanager.cpp \ + zigbeecluster.cpp \ + zigbeeclusterattribute.cpp \ zigbeenetwork.cpp \ zigbeenetworkmanager.cpp \ zigbee.cpp \ @@ -27,6 +29,8 @@ HEADERS += \ interface/zigbeeinterfacerequest.h \ interface/zigbeeinterfacereply.h \ nxp/nxpzigbeenetworkmanager.h \ + zigbeecluster.h \ + zigbeeclusterattribute.h \ zigbeenetwork.h \ zigbeenetworkmanager.h \ zigbee.h \ diff --git a/libnymea-zigbee/loggingcategory.cpp b/libnymea-zigbee/loggingcategory.cpp index 45d3f74..6478874 100644 --- a/libnymea-zigbee/loggingcategory.cpp +++ b/libnymea-zigbee/loggingcategory.cpp @@ -1,6 +1,7 @@ #include "loggingcategory.h" Q_LOGGING_CATEGORY(dcZigbeeNetwork, "ZigbeeNetwork") +Q_LOGGING_CATEGORY(dcZigbeeNode, "ZigbeeNode") Q_LOGGING_CATEGORY(dcZigbeeInterface, "ZigbeeInterface") Q_LOGGING_CATEGORY(dcZigbeeController, "ZigbeeController") Q_LOGGING_CATEGORY(dcZigbeeInterfaceTraffic, "ZigbeeInterfaceTraffic") diff --git a/libnymea-zigbee/loggingcategory.h b/libnymea-zigbee/loggingcategory.h index 65a4cc6..11b499a 100644 --- a/libnymea-zigbee/loggingcategory.h +++ b/libnymea-zigbee/loggingcategory.h @@ -5,6 +5,7 @@ #include Q_DECLARE_LOGGING_CATEGORY(dcZigbeeNetwork) +Q_DECLARE_LOGGING_CATEGORY(dcZigbeeNode) Q_DECLARE_LOGGING_CATEGORY(dcZigbeeInterface) Q_DECLARE_LOGGING_CATEGORY(dcZigbeeController) Q_DECLARE_LOGGING_CATEGORY(dcZigbeeInterfaceTraffic) diff --git a/libnymea-zigbee/zigbee.h b/libnymea-zigbee/zigbee.h index 93bfa33..3f78936 100644 --- a/libnymea-zigbee/zigbee.h +++ b/libnymea-zigbee/zigbee.h @@ -216,6 +216,7 @@ public: enum ClusterId { // Basics + ClusterIdUnknown = 0xffff, ClusterIdBasic = 0x0000, ClusterIdPower = 0x0001, ClusterIdDeviceTemperature = 0x0002, @@ -292,6 +293,24 @@ public: }; Q_ENUM(ClusterId) + enum ClusterAttributeBasic { + ClusterAttributeBasicZclVersion = 0x0000, + ClusterAttributeBasicApplicationVersion = 0x0001, + ClusterAttributeBasicStackVersion = 0x0002, + ClusterAttributeBasicHardwareVersion = 0x0003, + ClusterAttributeBasicManufacturerName = 0x0004, + ClusterAttributeBasicModelIdentifier = 0x0005, + ClusterAttributeBasicDataCode = 0x0006, + ClusterAttributeBasicPowerSource = 0x0007, + ClusterAttributeBasicLocationDescription = 0x0010, + ClusterAttributeBasicPhysicalEnvironment = 0x0011, + ClusterAttributeBasicDeviceEnabled = 0x0012, + ClusterAttributeBasicAlarmMask = 0x0013, + ClusterAttributeBasicDisableLocalConfig = 0x0014, + ClusterAttributeBasicSoftwareBuildId = 0x4000 + }; + Q_ENUM(ClusterAttributeBasic) + enum LightLinkDevice { // Lightning devices diff --git a/libnymea-zigbee/zigbeebridgecontroller.cpp b/libnymea-zigbee/zigbeebridgecontroller.cpp index 54ffe14..78f45cc 100644 --- a/libnymea-zigbee/zigbeebridgecontroller.cpp +++ b/libnymea-zigbee/zigbeebridgecontroller.cpp @@ -12,6 +12,11 @@ ZigbeeBridgeController::ZigbeeBridgeController(QObject *parent) : connect(m_interface, &ZigbeeInterface::messageReceived, this, &ZigbeeBridgeController::onMessageReceived); } +ZigbeeBridgeController::~ZigbeeBridgeController() +{ + qCDebug(dcZigbeeController()) << "Destroy controller"; +} + bool ZigbeeBridgeController::available() const { return m_interface->available(); @@ -247,7 +252,7 @@ ZigbeeInterfaceReply *ZigbeeBridgeController::commandAuthenticateDevice(const Zi ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeAuthenticateDeviceRequest, data)); request.setExpectedAdditionalMessageType(Zigbee::MessageTypeAuthenticateDeviceResponse); request.setDescription(QString("Authenticate device %1").arg(ieeeAddress.toString())); - request.setTimoutIntervall(5000); + request.setTimoutIntervall(2000); return sendRequest(request); } @@ -261,7 +266,7 @@ ZigbeeInterfaceReply *ZigbeeBridgeController::commandNodeDescriptorRequest(quint ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeNodeDescriptorRequest, data)); request.setExpectedAdditionalMessageType(Zigbee::MessageTypeNodeDescriptorRsponse); request.setDescription("Node descriptor request for " + ZigbeeUtils::convertUint16ToHexString(shortAddress)); - request.setTimoutIntervall(10000); + request.setTimoutIntervall(5000); return sendRequest(request); } diff --git a/libnymea-zigbee/zigbeebridgecontroller.h b/libnymea-zigbee/zigbeebridgecontroller.h index 8a07d84..926904a 100644 --- a/libnymea-zigbee/zigbeebridgecontroller.h +++ b/libnymea-zigbee/zigbeebridgecontroller.h @@ -16,6 +16,7 @@ class ZigbeeBridgeController : public QObject Q_OBJECT public: explicit ZigbeeBridgeController(QObject *parent = nullptr); + ~ZigbeeBridgeController(); bool available() const; diff --git a/libnymea-zigbee/zigbeecluster.cpp b/libnymea-zigbee/zigbeecluster.cpp new file mode 100644 index 0000000..a172ce5 --- /dev/null +++ b/libnymea-zigbee/zigbeecluster.cpp @@ -0,0 +1,59 @@ +#include "zigbeeutils.h" +#include "zigbeecluster.h" + +ZigbeeCluster::ZigbeeCluster(Zigbee::ClusterId clusterId, QObject *parent) : + QObject(parent), + m_clusterId(clusterId) +{ + +} + +Zigbee::ClusterId ZigbeeCluster::clusterId() const +{ + return m_clusterId; +} + +QString ZigbeeCluster::clusterName() const +{ + return ZigbeeUtils::clusterIdToString(static_cast(m_clusterId)); +} + +QList ZigbeeCluster::attributes() const +{ + return m_attributes.values(); +} + +bool ZigbeeCluster::hasAttribute(quint16 attributeId) const +{ + if (m_attributes.keys().isEmpty()) + return false; + + return m_attributes.keys().contains(attributeId); +} + +ZigbeeClusterAttribute ZigbeeCluster::attribute(quint16 id) +{ + return m_attributes.value(id); +} + +void ZigbeeCluster::setAttribute(const ZigbeeClusterAttribute &attribute) +{ + if (hasAttribute(attribute.id())) { + if (m_attributes.value(attribute.id()) != attribute) { + m_attributes[attribute.id()] = attribute; + emit attributeChanged(attribute); + } + } else { + m_attributes.insert(attribute.id(), attribute); + emit attributeChanged(attribute); + } +} + +QDebug operator<<(QDebug debug, ZigbeeCluster *cluster) +{ + debug.nospace().noquote() << "ZigbeeCluster(" + << ZigbeeUtils::convertUint16ToHexString(static_cast(cluster->clusterId())) << ", " + << cluster->clusterName() << ")"; + + return debug.space(); +} diff --git a/libnymea-zigbee/zigbeecluster.h b/libnymea-zigbee/zigbeecluster.h new file mode 100644 index 0000000..ca5b820 --- /dev/null +++ b/libnymea-zigbee/zigbeecluster.h @@ -0,0 +1,42 @@ +#ifndef ZIGBEECLUSTER_H +#define ZIGBEECLUSTER_H + +#include + +#include "zigbee.h" +#include "zigbeeclusterattribute.h" + +class ZigbeeCluster : public QObject +{ + Q_OBJECT + + friend class ZigbeeNode; + +public: + explicit ZigbeeCluster(Zigbee::ClusterId clusterId, QObject *parent = nullptr); + + Zigbee::ClusterId clusterId() const; + QString clusterName() const; + + QList attributes() const; + bool hasAttribute(quint16 attributeId) const; + + ZigbeeClusterAttribute attribute(quint16 id); + +private: + Zigbee::ClusterId m_clusterId = Zigbee::ClusterIdUnknown; + QHash m_attributes; + +protected: + void setAttribute(const ZigbeeClusterAttribute &attribute); + +signals: + void attributeChanged(const ZigbeeClusterAttribute &attribute); + +public slots: + +}; + +QDebug operator<<(QDebug debug, ZigbeeCluster *cluster); + +#endif // ZIGBEECLUSTER_H diff --git a/libnymea-zigbee/zigbeeclusterattribute.cpp b/libnymea-zigbee/zigbeeclusterattribute.cpp new file mode 100644 index 0000000..8cc40f0 --- /dev/null +++ b/libnymea-zigbee/zigbeeclusterattribute.cpp @@ -0,0 +1,73 @@ +#include "zigbeeutils.h" +#include "zigbeeclusterattribute.h" + +ZigbeeClusterAttribute::ZigbeeClusterAttribute() +{ + +} + +ZigbeeClusterAttribute::ZigbeeClusterAttribute(quint16 id, Zigbee::DataType dataType, QByteArray data): + m_id(id), + m_dataType(dataType), + m_data(data) +{ + +} + +ZigbeeClusterAttribute::ZigbeeClusterAttribute(const ZigbeeClusterAttribute &other) +{ + m_id = other.id(); + m_dataType = other.dataType(); + m_data = other.data(); +} + +quint16 ZigbeeClusterAttribute::id() const +{ + return m_id; +} + +Zigbee::DataType ZigbeeClusterAttribute::dataType() const +{ + return m_dataType; +} + +QByteArray ZigbeeClusterAttribute::data() const +{ + return m_data; +} + +ZigbeeClusterAttribute &ZigbeeClusterAttribute::operator=(const ZigbeeClusterAttribute &other) +{ + m_id = other.id(); + m_dataType = other.dataType(); + m_data = other.data(); + return *this; +} + +bool ZigbeeClusterAttribute::operator==(const ZigbeeClusterAttribute &other) const +{ + return m_id == other.id() && + m_dataType == other.dataType() && + m_data == other.data(); +} + +bool ZigbeeClusterAttribute::operator!=(const ZigbeeClusterAttribute &other) const +{ + return !operator==(other); +} + +bool ZigbeeClusterAttribute::isValid() const +{ + return m_id != 0 || + m_dataType != Zigbee::NoData || + !m_data.isNull(); +} + +QDebug operator<<(QDebug debug, const ZigbeeClusterAttribute &attribute) +{ + debug.nospace().noquote() << "ZigbeeClusterAttribute(" + << ZigbeeUtils::convertUint16ToHexString(attribute.id()) << ", " + << attribute.dataType() << ", " + << ZigbeeUtils::convertByteArrayToHexString(attribute.data()) << ")"; + return debug.space(); +} diff --git a/libnymea-zigbee/zigbeeclusterattribute.h b/libnymea-zigbee/zigbeeclusterattribute.h new file mode 100644 index 0000000..d7e2a87 --- /dev/null +++ b/libnymea-zigbee/zigbeeclusterattribute.h @@ -0,0 +1,34 @@ +#ifndef ZIGBEECLUSTERATTRIBUTE_H +#define ZIGBEECLUSTERATTRIBUTE_H + +#include + +#include "zigbee.h" + +class ZigbeeClusterAttribute +{ +public: + ZigbeeClusterAttribute(); + ZigbeeClusterAttribute(quint16 id, Zigbee::DataType dataType, QByteArray data); + ZigbeeClusterAttribute(const ZigbeeClusterAttribute &other); + + quint16 id() const; + Zigbee::DataType dataType() const; + QByteArray data() const; + + ZigbeeClusterAttribute &operator=(const ZigbeeClusterAttribute &other); + bool operator==(const ZigbeeClusterAttribute &other) const; + bool operator!=(const ZigbeeClusterAttribute &other) const; + + bool isValid() const; + +private: + quint16 m_id = 0; + Zigbee::DataType m_dataType = Zigbee::NoData; + QByteArray m_data; +}; + +QDebug operator<<(QDebug debug, const ZigbeeClusterAttribute &attribute); + + +#endif // ZIGBEECLUSTERATTRIBUTE_H diff --git a/libnymea-zigbee/zigbeenetwork.cpp b/libnymea-zigbee/zigbeenetwork.cpp index 751c716..4afed51 100644 --- a/libnymea-zigbee/zigbeenetwork.cpp +++ b/libnymea-zigbee/zigbeenetwork.cpp @@ -120,6 +120,12 @@ ZigbeeNode *ZigbeeNetwork::coordinatorNode() const ZigbeeNode *ZigbeeNetwork::getZigbeeNode(quint16 shortAddress) const { + foreach (ZigbeeNode *node, m_uninitializedNodes) { + if (node->shortAddress() == shortAddress) { + return node; + } + } + foreach (ZigbeeNode *node, m_nodes) { if (node->shortAddress() == shortAddress) { return node; @@ -131,6 +137,12 @@ ZigbeeNode *ZigbeeNetwork::getZigbeeNode(quint16 shortAddress) const ZigbeeNode *ZigbeeNetwork::getZigbeeNode(const ZigbeeAddress &address) const { + foreach (ZigbeeNode *node, m_uninitializedNodes) { + if (node->extendedAddress() == address) { + return node; + } + } + foreach (ZigbeeNode *node, m_nodes) { if (node->extendedAddress() == address) { return node; @@ -164,11 +176,52 @@ void ZigbeeNetwork::saveNetwork() settings.endGroup(); settings.beginWriteArray("Nodes"); - for (int i = 0; i < nodes().count(); i++) { - settings.setArrayIndex(i); - settings.setValue("nwkAddress", nodes().at(i)->shortAddress()); - settings.setValue("ieeeAddress", nodes().at(i)->extendedAddress().toString()); + for (int x = 0; x < nodes().count(); x++) { + settings.setArrayIndex(x); + ZigbeeNode *node = nodes().at(x); + settings.setValue("nwkAddress", node->shortAddress()); + settings.setValue("ieeeAddress", node->extendedAddress().toString()); // TODO: save the rest of the node + + // Input clusters + settings.beginWriteArray("inputCluster"); + for (int i = 0; i < node->inputClusters().count(); i++) { + settings.setArrayIndex(i); + ZigbeeCluster *cluster = node->inputClusters().at(i); + settings.setValue("id", static_cast(cluster->clusterId())); + settings.beginWriteArray("attributes"); + + settings.beginWriteArray("attributes"); + for (int j = 0; j < cluster->attributes().count(); j++) { + settings.setArrayIndex(j); + ZigbeeClusterAttribute attribute = cluster->attributes().at(j); + settings.setValue("id", attribute.id()); + settings.setValue("dataType", static_cast(attribute.dataType())); + settings.setValue("data", attribute.data()); + } + settings.endArray(); + } + settings.endArray(); + + // Output clusters + settings.beginWriteArray("outputCluster"); + for (int i = 0; i < node->outputClusters().count(); i++) { + settings.setArrayIndex(i); + ZigbeeCluster *cluster = node->outputClusters().at(i); + settings.setValue("id", static_cast(cluster->clusterId())); + settings.beginWriteArray("attributes"); + + settings.beginWriteArray("attributes"); + for (int j = 0; j < cluster->attributes().count(); j++) { + settings.setArrayIndex(j); + ZigbeeClusterAttribute attribute = cluster->attributes().at(j); + settings.setValue("id", attribute.id()); + settings.setValue("dataType", static_cast(attribute.dataType())); + settings.setValue("data", attribute.data()); + } + settings.endArray(); + } + settings.endArray(); } settings.endArray(); } @@ -178,27 +231,78 @@ void ZigbeeNetwork::loadNetwork() qCDebug(dcZigbeeNetwork()) << "Load current network configuration from" << m_settingsFileName; QSettings settings(m_settingsFileName, QSettings::IniFormat, this); settings.beginGroup("Network"); - quint64 extendedPanId = static_cast(settings.value("panId", 0).toUInt()); + quint64 extendedPanId = static_cast(settings.value("panId", 0).toULongLong()); if (extendedPanId == 0) { extendedPanId = ZigbeeUtils::generateRandomPanId(); - qCDebug(dcZigbeeNetwork()) << "Create new PAN id" << extendedPanId; + qCDebug(dcZigbeeNetwork()) << "Create new PAN ID" << extendedPanId; } setExtendedPanId(extendedPanId); setChannel(settings.value("channel", 0).toUInt()); settings.endGroup(); int nodesCount = settings.beginReadArray("Nodes"); - for (int i = 0; i < nodesCount; i++) { - settings.setArrayIndex(i); - ZigbeeNode *node = new ZigbeeNode(this); + for (int x = 0; x < nodesCount; x++) { + settings.setArrayIndex(x); + ZigbeeNode *node = createNode(); node->setShortAddress(static_cast(settings.value("nwkAddress", 0).toUInt())); node->setExtendedAddress(ZigbeeAddress(settings.value("ieeeAddress").toString())); // TODO: load the rest of the node + + // Input clusters + int inputClusterCount =settings.beginReadArray("inputCluster"); + for (int i = 0; i < inputClusterCount; i++) { + settings.setArrayIndex(i); + Zigbee::ClusterId clusterId = static_cast(settings.value("id", 0).toInt()); + settings.beginWriteArray("attributes"); + + int attributeCount = settings.beginReadArray("attributes"); + if (attributeCount == 0) { + node->setClusterAttribute(clusterId); + } else { + for (int j = 0; j < attributeCount; j++) { + settings.setArrayIndex(i); + ZigbeeClusterAttribute attribute; + quint16 id = static_cast(settings.value("id", 0).toInt()); + Zigbee::DataType dataType = static_cast(settings.value("dataType", 0).toInt()); + QByteArray data = settings.value("data").toByteArray(); + node->setClusterAttribute(clusterId, ZigbeeClusterAttribute(id, dataType, data)); + } + } + settings.endArray(); + } + settings.endArray(); + + // Output clusters + int outputClusterCount =settings.beginReadArray("outputCluster"); + for (int i = 0; i < outputClusterCount; i++) { + settings.setArrayIndex(i); + Zigbee::ClusterId clusterId = static_cast(settings.value("id", 0).toInt()); + settings.beginWriteArray("attributes"); + + int attributeCount = settings.beginReadArray("attributes"); + if (attributeCount == 0) { + node->setClusterAttribute(clusterId); + } else { + for (int j = 0; j < attributeCount; j++) { + settings.setArrayIndex(i); + ZigbeeClusterAttribute attribute; + quint16 id = static_cast(settings.value("id", 0).toInt()); + Zigbee::DataType dataType = static_cast(settings.value("dataType", 0).toInt()); + QByteArray data = settings.value("data").toByteArray(); + node->setClusterAttribute(clusterId, ZigbeeClusterAttribute(id, dataType, data)); + } + } + + settings.endArray(); + } + settings.endArray(); + + node->setState(StateInitialized); addNodeInternally(node); } settings.endArray(); - qCDebug(dcZigbeeNetwork()) << "Extended PAN Id:" << m_extendedPanId << ZigbeeUtils::convertUint64ToHexString(m_extendedPanId); + qCDebug(dcZigbeeNetwork()) << "Extended PAN ID:" << m_extendedPanId << ZigbeeUtils::convertUint64ToHexString(m_extendedPanId); qCDebug(dcZigbeeNetwork()) << "Channel" << m_channel; qCDebug(dcZigbeeNetwork()) << QStringLiteral("Nodes: (%1)").arg(m_nodes.count()); foreach (ZigbeeNode *node, nodes()) { @@ -206,16 +310,21 @@ void ZigbeeNetwork::loadNetwork() } } + void ZigbeeNetwork::addNode(ZigbeeNode *node) { - if (hasNode(node->extendedAddress())) { - qCWarning(dcZigbeeNetwork()) << "The node" << node << "has already been added."; + addNodeInternally(node); + saveNetwork(); +} + +void ZigbeeNetwork::addUnitializedNode(ZigbeeNode *node) +{ + if (m_uninitializedNodes.contains(node)) { + qCWarning(dcZigbeeNetwork()) << "The uninitialized node" << node << "has already been added."; return; } - m_nodes.append(node); - emit nodeAdded(node); - saveNetwork(); + m_uninitializedNodes.append(node); } void ZigbeeNetwork::addNodeInternally(ZigbeeNode *node) @@ -231,13 +340,7 @@ void ZigbeeNetwork::addNodeInternally(ZigbeeNode *node) void ZigbeeNetwork::removeNode(ZigbeeNode *node) { - if (!m_nodes.contains(node)) { - qCWarning(dcZigbeeNetwork()) << "Try to remove node" << node << "but not in the node list."; - return; - } - - m_nodes.removeAll(node); - emit nodeRemoved(node); + removeNodeInternally(node); saveNetwork(); } @@ -253,6 +356,13 @@ void ZigbeeNetwork::removeNodeInternally(ZigbeeNode *node) node->deleteLater(); } +ZigbeeNode *ZigbeeNetwork::createNode() +{ + ZigbeeNode *node = new ZigbeeNode(this); + connect(node, &ZigbeeNode::stateChanged, this, &ZigbeeNetwork::onNodeStateChanged); + return node; +} + void ZigbeeNetwork::clearSettings() { qCDebug(dcZigbeeNetwork()) << "Clear network settings"; @@ -296,3 +406,12 @@ void ZigbeeNetwork::setError(ZigbeeNetwork::Error error) emit errorOccured(m_error); } +void ZigbeeNetwork::onNodeStateChanged(ZigbeeNode::State state) +{ + ZigbeeNode *node = qobject_cast(sender()); + if (state == ZigbeeNode::StateInitialized && m_uninitializedNodes.contains(node)) { + m_uninitializedNodes.removeAll(node); + addNode(node); + } +} + diff --git a/libnymea-zigbee/zigbeenetwork.h b/libnymea-zigbee/zigbeenetwork.h index 2d0167f..a5bd481 100644 --- a/libnymea-zigbee/zigbeenetwork.h +++ b/libnymea-zigbee/zigbeenetwork.h @@ -87,15 +87,19 @@ private: QString m_settingsFileName = "/etc/nymea/nymea-zigbee.conf"; QList m_nodes; + QList m_uninitializedNodes; void saveNetwork(); void loadNetwork(); + void addNodeInternally(ZigbeeNode *node); + void removeNodeInternally(ZigbeeNode *node); protected: void addNode(ZigbeeNode *node); - void addNodeInternally(ZigbeeNode *node); + void addUnitializedNode(ZigbeeNode *node); void removeNode(ZigbeeNode *node); - void removeNodeInternally(ZigbeeNode *node); + + ZigbeeNode *createNode(); void clearSettings(); @@ -118,6 +122,9 @@ signals: void stateChanged(State state); void errorOccured(Error error); +private slots: + void onNodeStateChanged(ZigbeeNode::State state); + public slots: virtual void startNetwork() = 0; virtual void stopNetwork() = 0; diff --git a/libnymea-zigbee/zigbeenetworkmanager.cpp b/libnymea-zigbee/zigbeenetworkmanager.cpp index 4f3e746..63d4e72 100644 --- a/libnymea-zigbee/zigbeenetworkmanager.cpp +++ b/libnymea-zigbee/zigbeenetworkmanager.cpp @@ -480,8 +480,7 @@ void ZigbeeNetworkManager::processNetworkFormed(const ZigbeeInterfaceMessage &me qCDebug(dcZigbeeNetwork()) << " Extended address:" << ZigbeeAddress(extendedAddress); qCDebug(dcZigbeeNetwork()) << " Channel:" << channel; qCDebug(dcZigbeeNetwork()) << " Extended PAN ID:" << extendedPanId(); - qCDebug(dcZigbeeNetwork()) << " Permit joining" << permitJoining(); - + qCDebug(dcZigbeeNetwork()) << " Permit joining:" << permitJoining(); m_networkRunning = true; @@ -490,7 +489,7 @@ void ZigbeeNetworkManager::processNetworkFormed(const ZigbeeInterfaceMessage &me setExtendedAddress(ZigbeeAddress(extendedAddress)); setChannel(channel); - addNode(this); + addUnitializedNode(this); } void ZigbeeNetworkManager::onCommandEnableWhitelistFinished() @@ -519,42 +518,40 @@ void ZigbeeNetworkManager::onCommandNodeDescriptorRequestFinished() qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; qCDebug(dcZigbeeController()) << reply->additionalMessage(); - quint8 sequenceNumber = static_cast(reply->additionalMessage().data().at(0)); - quint8 status = static_cast(reply->additionalMessage().data().at(1)); - Q_UNUSED(sequenceNumber) - Q_UNUSED(status) + QByteArray data = reply->additionalMessage().data(); - quint16 shortAddress = static_cast(reply->additionalMessage().data().at(2)); - shortAddress <<= 8; - shortAddress |= reply->additionalMessage().data().at(3); + quint8 sequenceNumber = 0; + quint8 status = 0; + quint16 shortAddress = 0; + quint16 manufacturerCode = 0; + quint16 maximalRxSize = 0; + quint16 maximalTxSize = 0; + quint16 serverMask = 0; + quint8 descriptorFlag = 0; + quint8 macFlags = 0; + quint8 maxBufferSize = 0; + quint16 bitField = 0; - quint16 manufacturerCode = static_cast(reply->additionalMessage().data().at(4)); - manufacturerCode <<= 8; - manufacturerCode |= reply->additionalMessage().data().at(5); - - quint16 maximalRxSize = static_cast(reply->additionalMessage().data().at(6)); - maximalRxSize <<= 8; - maximalRxSize |= reply->additionalMessage().data().at(7); - - quint16 maximalTxSize = static_cast(reply->additionalMessage().data().at(8)); - maximalTxSize <<= 8; - maximalTxSize |= reply->additionalMessage().data().at(9); - - quint16 serverMask = static_cast(reply->additionalMessage().data().at(10)); - serverMask <<= 8; - serverMask |= reply->additionalMessage().data().at(11); - - quint8 descriptorFlag = static_cast(reply->additionalMessage().data().at(12)); - quint8 macFlags = static_cast(reply->additionalMessage().data().at(13)); - quint8 maxBufferSize = static_cast(reply->additionalMessage().data().at(14)); - - quint16 bitField = static_cast(reply->additionalMessage().data().at(15)); - bitField <<= 8; - bitField |= reply->additionalMessage().data().at(16); + QDataStream stream(&data, QIODevice::ReadOnly); + stream >> sequenceNumber; + stream >> status; + stream >> shortAddress; + stream >> manufacturerCode; + stream >> maximalRxSize; + stream >> maximalTxSize; + stream >> serverMask; + stream >> descriptorFlag; + stream >> macFlags; + stream >> maxBufferSize; + stream >> bitField; // Get node object ZigbeeNode *node = getZigbeeNode(shortAddress); + if (!node) { + qCWarning(dcZigbeeNetwork()) << "Could not find node for address" << shortAddress << ZigbeeUtils::convertUint16ToHexString(shortAddress); + return; + } // Set node data node->setManufacturerCode(manufacturerCode); @@ -570,7 +567,7 @@ void ZigbeeNetworkManager::onCommandNodeDescriptorRequestFinished() if (!ZigbeeUtils::checkBitUint16(bitField, 0) && !ZigbeeUtils::checkBitUint16(bitField, 1)) { node->setNodeType(NodeTypeCoordinator); } else if (!ZigbeeUtils::checkBitUint16(bitField, 0) && ZigbeeUtils::checkBitUint16(bitField, 1)) { - node->setNodeType(NodeTypeCoordinator); + node->setNodeType(NodeTypeRouter); } else if (ZigbeeUtils::checkBitUint16(bitField, 0) && !ZigbeeUtils::checkBitUint16(bitField, 1)) { node->setNodeType(NodeTypeEndDevice); } @@ -625,36 +622,39 @@ void ZigbeeNetworkManager::onCommandSimpleDescriptorRequestFinished() qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; qCDebug(dcZigbeeController()) << reply->additionalMessage(); - quint8 sequenceNumber = static_cast(reply->additionalMessage().data().at(0)); - quint8 status = static_cast(reply->additionalMessage().data().at(1)); + QByteArray data = reply->additionalMessage().data(); - quint16 nwkAddress = static_cast(reply->additionalMessage().data().at(2)); - nwkAddress <<= 8; - nwkAddress |= reply->additionalMessage().data().at(3); + quint8 sequenceNumber = 0; + quint8 status = 0; + quint16 shortAddress = 0; + quint8 length = 0; + quint8 endPoint = 0; + quint16 profileId = 0; + quint16 deviceId = 0; + quint8 bitField = 0; + quint8 inputClusterCount = 0; + quint8 outputClusterCount = 0; - quint8 length = static_cast(reply->additionalMessage().data().at(4)); + QDataStream stream(&data, QIODevice::ReadOnly); + stream >> sequenceNumber; + stream >> status; + stream >> shortAddress; + stream >> length; if (length == 0) { - qCWarning(dcZigbeeNetwork()) << "Length 0"; + qCWarning(dcZigbeeNetwork()) << "Simple node descriptior has a length of 0."; return; } - quint8 endPoint = static_cast(reply->additionalMessage().data().at(5)); - - quint16 profileId = static_cast(reply->additionalMessage().data().at(6)); - profileId <<= 8; - profileId |= reply->additionalMessage().data().at(7); - - quint16 deviceId = static_cast(reply->additionalMessage().data().at(8)); - deviceId <<= 8; - deviceId |= reply->additionalMessage().data().at(9); - - quint8 bitField = static_cast(reply->additionalMessage().data().at(10)); + stream >> endPoint; + stream >> profileId; + stream >> deviceId; + stream >> bitField; qCDebug(dcZigbeeNetwork()) << "Node simple descriptor:"; qCDebug(dcZigbeeNetwork()) << " Sequence number:" << ZigbeeUtils::convertByteToHexString(sequenceNumber); qCDebug(dcZigbeeNetwork()) << " Status:" << ZigbeeUtils::convertByteToHexString(status); - qCDebug(dcZigbeeNetwork()) << " Nwk address:" << ZigbeeUtils::convertUint16ToHexString(nwkAddress); + qCDebug(dcZigbeeNetwork()) << " Nwk address:" << ZigbeeUtils::convertUint16ToHexString(shortAddress); qCDebug(dcZigbeeNetwork()) << " Lenght:" << ZigbeeUtils::convertByteToHexString(length); qCDebug(dcZigbeeNetwork()) << " End Point:" << ZigbeeUtils::convertByteToHexString(endPoint); qCDebug(dcZigbeeNetwork()) << " Profile:" << ZigbeeUtils::profileIdToString(static_cast(profileId)); @@ -667,30 +667,40 @@ void ZigbeeNetworkManager::onCommandSimpleDescriptorRequestFinished() qCDebug(dcZigbeeNetwork()) << " Bit field:" << ZigbeeUtils::convertByteToHexString(bitField); - quint8 inputClusterCount = static_cast(reply->additionalMessage().data().at(10)); - - qCDebug(dcZigbeeNetwork()) << " Input clusters:"; - QByteArray inputClusterListData = reply->additionalMessage().data().mid(11, inputClusterCount * 2); - for (int i = 0; i < inputClusterListData.count(); i+=2) { - quint16 clusterId = static_cast(inputClusterListData.at(i)); - clusterId <<= 8; - clusterId |= inputClusterListData .at(i+1); - + stream >> inputClusterCount; + qCDebug(dcZigbeeNetwork()) << " Input clusters: (" << inputClusterCount << ")"; + for (int i = 0; i < inputClusterCount; i+=1) { + quint16 clusterId = 0; + stream >> clusterId; qCDebug(dcZigbeeNetwork()) << " Cluster ID:" << ZigbeeUtils::convertUint16ToHexString(clusterId) << ZigbeeUtils::clusterIdToString(static_cast(clusterId)); } - quint8 outputClusterCount = static_cast(reply->additionalMessage().data().at(12 + inputClusterCount * 2)); - - qCDebug(dcZigbeeNetwork()) << " Output clusters:"; - QByteArray outputClusterListData = reply->additionalMessage().data().mid(12 + inputClusterCount * 2, outputClusterCount * 2); - for (int i = 0; i < outputClusterListData.count(); i+=2) { - quint16 clusterId = static_cast(outputClusterListData.at(i)); - clusterId <<= 8; - clusterId |= outputClusterListData .at(i+1); + stream >> outputClusterCount; + qCDebug(dcZigbeeNetwork()) << " Output clusters: (" << outputClusterCount << ")"; + for (int i = 0; i < outputClusterCount; i+=1) { + if (stream.atEnd()) { + qCWarning(dcZigbeeNode()) << "Data stream already at the end but more data expected. Looks like the firmware doesn't provide more data."; + break; + } + quint16 clusterId = 0; + stream >> clusterId; qCDebug(dcZigbeeNetwork()) << " Cluster ID:" << ZigbeeUtils::convertUint16ToHexString(clusterId) << ZigbeeUtils::clusterIdToString(static_cast(clusterId)); } + // Get node object + ZigbeeNode *node = getZigbeeNode(shortAddress); + if (!node) { + qCWarning(dcZigbeeNetwork()) << "Could not find node for address" << shortAddress << ZigbeeUtils::convertUint16ToHexString(shortAddress); + if (m_startingState == StartingStateReadSimpleDescriptor) setStartingState(StartingStateReadPowerDescriptor); + return; + } + + // Set node data + node->setEndPoint(endPoint); + node->setZigbeeProfile(static_cast(profileId)); + node->setDeviceId(deviceId); + if (m_startingState == StartingStateReadSimpleDescriptor) setStartingState(StartingStateReadPowerDescriptor); } @@ -699,8 +709,24 @@ void ZigbeeNetworkManager::onCommandPowerDescriptorRequestFinished() ZigbeeInterfaceReply *reply = static_cast(sender()); reply->deleteLater(); + // Note: get the short address from the request data + QByteArray requestData = reply->request().message().data(); + quint16 shortAddress; + QDataStream stream(&requestData, QIODevice::ReadOnly); + stream >> shortAddress; + if (reply->status() != ZigbeeInterfaceReply::Success) { qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); + // Note: the power descriptor is the last request from the initialization + ZigbeeNode *node = getZigbeeNode(shortAddress); + if (!node) { + qCWarning(dcZigbeeNetwork()) << "Could not find node for address" << shortAddress << ZigbeeUtils::convertUint16ToHexString(shortAddress); + if (m_startingState == StartingStateReadSimpleDescriptor) setStartingState(StartingStateReadPowerDescriptor); + return; + } + if (node->state() != ZigbeeNode::StateInitialized) { + node->setState(ZigbeeNode::StateInitialized); + } return; } @@ -712,19 +738,20 @@ void ZigbeeNetworkManager::onCommandPowerDescriptorRequestFinished() quint16 bitField = static_cast(reply->additionalMessage().data().at(2)); bitField <<= 8; - bitField |= reply->additionalMessage().data().at(3); + bitField |= static_cast(reply->additionalMessage().data().at(3)); // Bit 0 - 3 Power mode // 0000: Receiver configured according to “Receiver on when idle” MAC flag in the Node Descriptor // 0001: Receiver switched on periodically // 0010: Receiver switched on when stimulated, e.g. by pressing a button + ZigbeeNode::PowerMode powerMode = PowerModeAlwaysOn; if (!ZigbeeUtils::checkBitUint16(bitField, 0) && !ZigbeeUtils::checkBitUint16(bitField, 1)) { - m_powerMode = PowerModeAlwaysOn; + powerMode = PowerModeAlwaysOn; } else if (ZigbeeUtils::checkBitUint16(bitField, 0) && !ZigbeeUtils::checkBitUint16(bitField, 1)) { - m_powerMode = PowerModeOnPeriodically; + powerMode = PowerModeOnPeriodically; } else if (!ZigbeeUtils::checkBitUint16(bitField, 0) && ZigbeeUtils::checkBitUint16(bitField, 1)) { - m_powerMode = PowerModeOnWhenStimulated; + powerMode = PowerModeOnWhenStimulated; } // Bit 4 - 7 Available power sources @@ -733,21 +760,23 @@ void ZigbeeNetworkManager::onCommandPowerDescriptorRequestFinished() // Bit 2: Disposable battery // Bit 4: Reserved + QList availablePowerSources; if (ZigbeeUtils::checkBitUint16(bitField, 4)) { - m_availablePowerSources.append(PowerSourcePermanentMainSupply); + availablePowerSources.append(PowerSourcePermanentMainSupply); } else if (ZigbeeUtils::checkBitUint16(bitField, 5)) { - m_availablePowerSources.append(PowerSourceRecharchableBattery); + availablePowerSources.append(PowerSourceRecharchableBattery); } else if (ZigbeeUtils::checkBitUint16(bitField, 6)) { - m_availablePowerSources.append(PowerSourceDisposableBattery); + availablePowerSources.append(PowerSourceDisposableBattery); } // Bit 8 - 11 Active source: according to the same schema as available power sources + ZigbeeNode::PowerSource powerSource = PowerSourcePermanentMainSupply; if (ZigbeeUtils::checkBitUint16(bitField, 8)) { - m_powerSource = PowerSourcePermanentMainSupply; + powerSource = PowerSourcePermanentMainSupply; } else if (ZigbeeUtils::checkBitUint16(bitField, 9)) { - m_powerSource = PowerSourceRecharchableBattery; + powerSource = PowerSourceRecharchableBattery; } else if (ZigbeeUtils::checkBitUint16(bitField, 10)) { - m_powerSource = PowerSourceDisposableBattery; + powerSource = PowerSourceDisposableBattery; } // Bit 12 - 15: Battery level if available @@ -755,15 +784,15 @@ void ZigbeeNetworkManager::onCommandPowerDescriptorRequestFinished() // 0100: Approximately 33% // 1000: Approximately 66% // 1100: Approximately 100% (near fully charged) - + ZigbeeNode::PowerLevel powerLevel = PowerLevelCriticalLow; if (!ZigbeeUtils::checkBitUint16(bitField, 14) && !ZigbeeUtils::checkBitUint16(bitField, 15)) { - m_powerLevel = PowerLevelCriticalLow; + powerLevel = PowerLevelCriticalLow; } else if (ZigbeeUtils::checkBitUint16(bitField, 14) && !ZigbeeUtils::checkBitUint16(bitField, 15)) { - m_powerLevel = PowerLevelLow; + powerLevel = PowerLevelLow; } else if (!ZigbeeUtils::checkBitUint16(bitField, 14) && ZigbeeUtils::checkBitUint16(bitField, 15)) { - m_powerLevel = PowerLevelOk; + powerLevel = PowerLevelOk; } else if (ZigbeeUtils::checkBitUint16(bitField, 14) && ZigbeeUtils::checkBitUint16(bitField, 15)) { - m_powerLevel = PowerLevelFull; + powerLevel = PowerLevelFull; } qCDebug(dcZigbeeNetwork()) << "Node power descriptor:"; @@ -772,12 +801,32 @@ void ZigbeeNetworkManager::onCommandPowerDescriptorRequestFinished() qCDebug(dcZigbeeNetwork()) << " Bitfiled:" << ZigbeeUtils::convertUint16ToHexString(bitField); qCDebug(dcZigbeeNetwork()) << " Power mode:" << m_powerMode; qCDebug(dcZigbeeNetwork()) << " Available power sources:"; - foreach (const PowerSource &source, m_availablePowerSources) { + foreach (const PowerSource &source, availablePowerSources) { qCDebug(dcZigbeeNetwork()) << " " << source; } - qCDebug(dcZigbeeNetwork()) << " Power source:" << m_powerSource; - qCDebug(dcZigbeeNetwork()) << " Power level:" << m_powerLevel; + qCDebug(dcZigbeeNetwork()) << " Power source:" << powerSource; + qCDebug(dcZigbeeNetwork()) << " Power level:" << powerLevel; + // Get node object + ZigbeeNode *node = getZigbeeNode(shortAddress); + if (!node) { + qCWarning(dcZigbeeNetwork()) << "Could not find node for address" << shortAddress << ZigbeeUtils::convertUint16ToHexString(shortAddress); + if (m_startingState == StartingStateReadSimpleDescriptor) setStartingState(StartingStateReadPowerDescriptor); + return; + } + + // Set node data + node->setPowerMode(powerMode); + node->setPowerSource(powerSource); + node->setAvailablePowerSources(availablePowerSources); + node->setPowerLevel(powerLevel); + + // Note: the power descriptor is the last request from the initialization + if (node->state() != ZigbeeNode::StateInitialized) { + node->setState(ZigbeeNode::StateInitialized); + } + + // Note: the power descriptor request for the coordinator is the last step from the network init process if (m_startingState == StartingStateReadPowerDescriptor) { setStartingState(StartingStateNone); setState(StateRunning); @@ -1013,7 +1062,7 @@ void ZigbeeNetworkManager::processDeviceAnnounce(const ZigbeeInterfaceMessage &m qCDebug(dcZigbeeNetwork()) << " Extended address:" << ZigbeeAddress(ieeeAddress); qCDebug(dcZigbeeNetwork()) << " Mac capabilities:" << ZigbeeUtils::convertByteToHexString(macCapabilitiesFlag); - ZigbeeNode *node = new ZigbeeNode(this); + ZigbeeNode *node = createNode(); node->setShortAddress(shortAddress); node->setExtendedAddress(ZigbeeAddress(ieeeAddress)); node->setMacCapabilitiesFlag(macCapabilitiesFlag); @@ -1021,7 +1070,8 @@ void ZigbeeNetworkManager::processDeviceAnnounce(const ZigbeeInterfaceMessage &m qCDebug(dcZigbeeNetwork()) << " Node:" << node; // FIXME: check if node already added, and if we have to update it - addNode(node); + addUnitializedNode(node); + node->setState(StateInitializing); ZigbeeInterfaceReply *reply = nullptr; reply = m_controller->commandAuthenticateDevice(node->extendedAddress(), securityConfiguration().globalTrustCenterLinkKey()); @@ -1071,8 +1121,13 @@ void ZigbeeNetworkManager::processAttributeReport(const ZigbeeInterfaceMessage & break; } - // TODO: find node and set attribute value + ZigbeeNode *node = getZigbeeNode(sourceAddress); + if (!node) { + qCWarning(dcZigbeeNode()) << "Received an attribute report from an unknown node. Ignoring data."; + return; + } + node->setClusterAttribute(static_cast(clusterId), ZigbeeClusterAttribute(attributeId, dataType, data)); } void ZigbeeNetworkManager::processLeaveIndication(const ZigbeeInterfaceMessage &message) @@ -1080,23 +1135,16 @@ void ZigbeeNetworkManager::processLeaveIndication(const ZigbeeInterfaceMessage & QByteArray data = message.data(); quint64 extendedAddress = 0; bool rejoining = 0; + QDataStream stream(&data, QIODevice::ReadOnly); stream >> extendedAddress; stream >> rejoining; ZigbeeAddress address(extendedAddress); - -// ZigbeeAddress extendedAddress = ZigbeeAddress(ZigbeeUtils::convertByteArrayToUint64(message.data().mid(0, 8))); -// quint8 rejoining = static_cast(message.data().at(9)); - qCDebug(dcZigbeeNetwork()) << "Node leaving indication:" << address.toString() << "rejoining:" << rejoining; ZigbeeNode *node = getZigbeeNode(address); - if (node) { - removeNode(node); - } - - // TODO: remove node + if (node) removeNode(node); } void ZigbeeNetworkManager::processRestartProvisioned(const ZigbeeInterfaceMessage &message) diff --git a/libnymea-zigbee/zigbeenode.cpp b/libnymea-zigbee/zigbeenode.cpp index 177aecd..9963480 100644 --- a/libnymea-zigbee/zigbeenode.cpp +++ b/libnymea-zigbee/zigbeenode.cpp @@ -10,6 +10,11 @@ ZigbeeNode::ZigbeeNode(QObject *parent) : } +ZigbeeNode::State ZigbeeNode::state() const +{ + return m_state; +} + quint16 ZigbeeNode::shortAddress() const { return m_shortAddress; @@ -50,6 +55,11 @@ quint16 ZigbeeNode::manufacturerCode() const return m_manufacturerCode; } +quint16 ZigbeeNode::deviceId() const +{ + return m_deviceId; +} + bool ZigbeeNode::complexDescriptorAvailable() const { return m_complexDescriptorAvailable; @@ -75,6 +85,36 @@ quint8 ZigbeeNode::maximumBufferSize() const return m_maximumBufferSize; } +QList ZigbeeNode::inputClusters() const +{ + return m_inputClusters.values(); +} + +ZigbeeCluster *ZigbeeNode::getInputCluster(Zigbee::ClusterId clusterId) const +{ + return m_inputClusters.value(clusterId); +} + +bool ZigbeeNode::hasInputCluster(Zigbee::ClusterId clusterId) const +{ + return m_inputClusters.keys().contains(clusterId); +} + +QList ZigbeeNode::outputClusters() const +{ + return m_outputClusters.values(); +} + +bool ZigbeeNode::hasOutputCluster(Zigbee::ClusterId clusterId) const +{ + return m_outputClusters.keys().contains(clusterId); +} + +ZigbeeCluster *ZigbeeNode::getOutputCluster(Zigbee::ClusterId clusterId) const +{ + return m_outputClusters.value(clusterId); +} + bool ZigbeeNode::isPrimaryTrustCenter() const { return m_isPrimaryTrustCenter; @@ -170,6 +210,16 @@ ZigbeeNode::PowerLevel ZigbeeNode::powerLevel() const return m_powerLevel; } +void ZigbeeNode::setState(ZigbeeNode::State state) +{ + if (m_state == state) + return; + + qCDebug(dcZigbeeNode()) << "State changed" << state; + m_state = state; + emit stateChanged(m_state); +} + //void ZigbeeNode::identify() //{ @@ -246,6 +296,11 @@ void ZigbeeNode::setManufacturerCode(quint16 manufacturerCode) m_manufacturerCode = manufacturerCode; } +void ZigbeeNode::setDeviceId(quint16 deviceId) +{ + m_deviceId = deviceId; +} + void ZigbeeNode::setMaximumRxSize(quint16 size) { m_maximumRxSize = size; @@ -303,6 +358,57 @@ void ZigbeeNode::setDescriptorFlag(quint8 descriptorFlag) m_extendedSimpleDescriptorListAvailable = ((descriptorFlag >> 1) & 0x01); } +void ZigbeeNode::setPowerMode(ZigbeeNode::PowerMode powerMode) +{ + m_powerMode = powerMode; +} + +void ZigbeeNode::setPowerSource(ZigbeeNode::PowerSource powerSource) +{ + m_powerSource = powerSource; +} + +void ZigbeeNode::setAvailablePowerSources(QList availablePowerSources) +{ + m_availablePowerSources = availablePowerSources; +} + +void ZigbeeNode::setPowerLevel(ZigbeeNode::PowerLevel powerLevel) +{ + m_powerLevel = powerLevel; +} + +void ZigbeeNode::setClusterAttribute(Zigbee::ClusterId clusterId, const ZigbeeClusterAttribute &attribute) +{ + //qCDebug(dcZigbeeNode()) << this << "cluster attribute changed" << clusterId << attribute; + ZigbeeCluster *cluster = m_outputClusters.value(clusterId); + + // Note: create the cluster if not there yet + bool clusterCreated = false; + if (!cluster) { + cluster = new ZigbeeCluster(clusterId, this); + qCWarning(dcZigbeeNode()) << "Created cluster" << cluster; + connect(cluster, &ZigbeeCluster::attributeChanged, this, &ZigbeeNode::onClusterAttributeChanged); + m_outputClusters.insert(clusterId, cluster); + clusterCreated = true; + } + + // Set the attribute if valid + if (attribute.isValid()) + cluster->setAttribute(attribute); + + if (clusterCreated) + emit clusterAdded(cluster); + +} + +void ZigbeeNode::onClusterAttributeChanged(const ZigbeeClusterAttribute &attribute) +{ + ZigbeeCluster *cluster = static_cast(sender()); + qCDebug(dcZigbeeNode()) << "Cluster" << cluster << "attribute changed" << attribute; + emit clusterAttributeChanged(cluster, attribute); +} + //void ZigbeeNode::onRequestUserDescriptorFinished() //{ // ZigbeeInterfaceReply *reply = static_cast(sender()); diff --git a/libnymea-zigbee/zigbeenode.h b/libnymea-zigbee/zigbeenode.h index f8bb671..9cdbe5f 100644 --- a/libnymea-zigbee/zigbeenode.h +++ b/libnymea-zigbee/zigbeenode.h @@ -4,6 +4,7 @@ #include #include "zigbee.h" +#include "zigbeecluster.h" #include "zigbeeaddress.h" class ZigbeeNode : public QObject @@ -14,6 +15,13 @@ class ZigbeeNode : public QObject friend class ZigbeeNetworkManager; public: + enum State { + StateUninitialized, + StateInitializing, + StateInitialized + }; + Q_ENUM(State) + enum NodeType { NodeTypeCoordinator = 0, NodeTypeRouter = 1, @@ -63,7 +71,7 @@ public: }; Q_ENUM(PowerLevel) - ZigbeeNode(QObject *parent = nullptr); + State state() const; quint16 shortAddress() const; ZigbeeAddress extendedAddress() const; @@ -75,6 +83,7 @@ public: Relationship relationship() const; Zigbee::ZigbeeProfile profile() const; quint16 manufacturerCode() const; + quint16 deviceId() const; bool complexDescriptorAvailable() const; bool userDescriptorAvailable() const; @@ -83,6 +92,14 @@ public: quint16 maximumTxSize() const; quint8 maximumBufferSize() const; + QList inputClusters() const; + ZigbeeCluster *getInputCluster(Zigbee::ClusterId clusterId) const; + bool hasInputCluster(Zigbee::ClusterId clusterId) const; + + QList outputClusters() const; + ZigbeeCluster *getOutputCluster(Zigbee::ClusterId clusterId) const; + bool hasOutputCluster(Zigbee::ClusterId clusterId) const; + // Server Mask bool isPrimaryTrustCenter() const; bool isBackupTrustCenter() const; @@ -111,6 +128,12 @@ public: PowerLevel powerLevel() const; private: + ZigbeeNode(QObject *parent = nullptr); + State m_state = StateUninitialized; + + QHash m_inputClusters; + QHash m_outputClusters; + quint16 m_shortAddress = 0; ZigbeeAddress m_extendedAddress; quint8 m_endPoint = 1; @@ -120,6 +143,7 @@ private: Relationship m_relationship = Parent; Zigbee::ZigbeeProfile m_profile; quint16 m_manufacturerCode = 0; + quint16 m_deviceId = 0; bool m_complexDescriptorAvailable = false; bool m_userDescriptorAvailable = false; @@ -156,6 +180,8 @@ private: bool m_extendedSimpleDescriptorListAvailable = false; protected: + void setState(State state); + void setShortAddress(const quint16 &shortAddress); void setExtendedAddress(const ZigbeeAddress &extendedAddress); void setEndPoint(quint8 endPoint); @@ -165,6 +191,7 @@ protected: void setRelationship(Relationship relationship); void setZigbeeProfile(Zigbee::ZigbeeProfile profile); void setManufacturerCode(quint16 manufacturerCode); + void setDeviceId(quint16 deviceType); void setMaximumRxSize(quint16 size); void setMaximumTxSize(quint16 size); @@ -176,6 +203,22 @@ protected: void setMacCapabilitiesFlag(quint16 macFlag); void setDescriptorFlag(quint8 descriptorFlag); + void setPowerMode(PowerMode powerMode); + void setPowerSource(PowerSource powerSource); + void setAvailablePowerSources(QList availablePowerSources); + void setPowerLevel(PowerLevel powerLevel); + + // Cluster commands + void setClusterAttribute(Zigbee::ClusterId clusterId, const ZigbeeClusterAttribute &attribute = ZigbeeClusterAttribute()); + +signals: + void stateChanged(State state); + void clusterAdded(ZigbeeCluster *cluster); + void clusterAttributeChanged(ZigbeeCluster *cluster, const ZigbeeClusterAttribute &attribute); + +private slots: + void onClusterAttributeChanged(const ZigbeeClusterAttribute &attribute); + }; QDebug operator<<(QDebug debug, ZigbeeNode *node); diff --git a/libnymea-zigbee/zigbeesecurityconfiguration.cpp b/libnymea-zigbee/zigbeesecurityconfiguration.cpp index 18555c4..c6d6ec0 100644 --- a/libnymea-zigbee/zigbeesecurityconfiguration.cpp +++ b/libnymea-zigbee/zigbeesecurityconfiguration.cpp @@ -11,6 +11,16 @@ ZigbeeSecurityConfiguration::ZigbeeSecurityConfiguration(const ZigbeeSecurityCon m_globalTrustCenterLinkKey = other.networkKey(); } +ZigbeeSecurityConfiguration::ZigbeeSecurityMode ZigbeeSecurityConfiguration::zigbeeSecurityMode() const +{ + return m_zigbeeSecurityMode; +} + +void ZigbeeSecurityConfiguration::setZigbeeSecurityMode(ZigbeeSecurityConfiguration::ZigbeeSecurityMode zigbeeSecurityMode) +{ + m_zigbeeSecurityMode = zigbeeSecurityMode; +} + QString ZigbeeSecurityConfiguration::networkKey() const { return m_networkKey; @@ -33,12 +43,14 @@ void ZigbeeSecurityConfiguration::setGlobalTrustCenterlinkKey(const QString &glo void ZigbeeSecurityConfiguration::clear() { + m_zigbeeSecurityMode = ZigbeeSecurityModeNone; m_networkKey.clear(); + m_globalTrustCenterLinkKey = "5A6967426565416C6C69616E63653039"; } bool ZigbeeSecurityConfiguration::operator==(const ZigbeeSecurityConfiguration &other) const { - return m_networkKey == other.networkKey() && m_globalTrustCenterLinkKey == other.globalTrustCenterLinkKey(); + return m_networkKey == other.networkKey() && m_globalTrustCenterLinkKey == other.globalTrustCenterLinkKey() && m_zigbeeSecurityMode == other.zigbeeSecurityMode(); } bool ZigbeeSecurityConfiguration::operator!=(const ZigbeeSecurityConfiguration &other) const diff --git a/libnymea-zigbee/zigbeesecurityconfiguration.h b/libnymea-zigbee/zigbeesecurityconfiguration.h index 66360fb..ce46b7b 100644 --- a/libnymea-zigbee/zigbeesecurityconfiguration.h +++ b/libnymea-zigbee/zigbeesecurityconfiguration.h @@ -6,9 +6,18 @@ class ZigbeeSecurityConfiguration { public: + enum ZigbeeSecurityMode { + ZigbeeSecurityModeNone, + ZigbeeSecurityModeNetworkLayer, + ZigbeeSecurityModeApplicationLayer + }; + explicit ZigbeeSecurityConfiguration(); ZigbeeSecurityConfiguration(const ZigbeeSecurityConfiguration &other); + ZigbeeSecurityMode zigbeeSecurityMode() const; + void setZigbeeSecurityMode(ZigbeeSecurityMode zigbeeSecurityMode); + QString networkKey() const; void setNetworkKey(const QString &networkKey); @@ -21,6 +30,8 @@ public: bool operator!=(const ZigbeeSecurityConfiguration &other) const; private: + ZigbeeSecurityMode m_zigbeeSecurityMode = ZigbeeSecurityModeNone; + // This is the local network key QString m_networkKey; diff --git a/libnymea-zigbee/zigbeeutils.cpp b/libnymea-zigbee/zigbeeutils.cpp index 3562d92..7153cd9 100644 --- a/libnymea-zigbee/zigbeeutils.cpp +++ b/libnymea-zigbee/zigbeeutils.cpp @@ -133,7 +133,6 @@ QString ZigbeeUtils::profileIdToString(const Zigbee::ZigbeeProfile &profileId) quint64 ZigbeeUtils::generateRandomPanId() { - srand(static_cast(QDateTime::currentMSecsSinceEpoch() / 1000)); - srand(static_cast(qrand())); + qsrand(static_cast(QDateTime::currentMSecsSinceEpoch() / 1000)); return static_cast((ULLONG_MAX - 0) * (qrand()/static_cast(RAND_MAX))); }