From a3a9252092c5bf0e4368f0026472129a71bfc1ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Fri, 20 Oct 2017 15:08:00 +0200 Subject: [PATCH] add descriptor parsing and device types --- core.cpp | 2 +- core.h | 4 +- .../zigbeeinterface.cpp | 32 +- .../zigbeeinterface.h | 2 + .../zigbeeinterfacemessage.cpp | 8 +- .../zigbeeinterfacemessage.h | 4 +- interface/zigbeeinterfacereply.cpp | 111 +++ interface/zigbeeinterfacereply.h | 73 ++ interface/zigbeeinterfacerequest.cpp | 58 ++ interface/zigbeeinterfacerequest.h | 35 + loggingcategory.cpp | 1 + loggingcategory.h | 1 + main.cpp | 26 +- zigbee-daemon.pro | 28 +- zigbee.cpp | 31 - zigbee.h | 474 +++++++---- zigbeeaddress.cpp | 88 ++ zigbeeaddress.h | 34 + zigbeebridgecontroller.cpp | 89 ++ zigbeebridgecontroller.h | 42 + zigbeemanager.cpp | 49 -- zigbeemanager.h | 35 - zigbeenetworkmanager.cpp | 776 ++++++++++++++++++ zigbeenetworkmanager.h | 74 ++ zigbeenode.cpp | 31 + zigbeenode.h | 56 ++ zigbeeutils.cpp | 74 ++ zigbeeutils.h | 26 + 28 files changed, 1968 insertions(+), 296 deletions(-) rename zigbeeinterface.cpp => interface/zigbeeinterface.cpp (85%) rename zigbeeinterface.h => interface/zigbeeinterface.h (96%) rename zigbeeinterfacemessage.cpp => interface/zigbeeinterfacemessage.cpp (66%) rename zigbeeinterfacemessage.h => interface/zigbeeinterfacemessage.h (86%) create mode 100644 interface/zigbeeinterfacereply.cpp create mode 100644 interface/zigbeeinterfacereply.h create mode 100644 interface/zigbeeinterfacerequest.cpp create mode 100644 interface/zigbeeinterfacerequest.h create mode 100644 zigbeeaddress.cpp create mode 100644 zigbeeaddress.h create mode 100644 zigbeebridgecontroller.cpp create mode 100644 zigbeebridgecontroller.h delete mode 100644 zigbeemanager.cpp delete mode 100644 zigbeemanager.h create mode 100644 zigbeenetworkmanager.cpp create mode 100644 zigbeenetworkmanager.h create mode 100644 zigbeenode.cpp create mode 100644 zigbeenode.h create mode 100644 zigbeeutils.cpp create mode 100644 zigbeeutils.h diff --git a/core.cpp b/core.cpp index 478671e..005a29b 100644 --- a/core.cpp +++ b/core.cpp @@ -4,5 +4,5 @@ Core::Core(QObject *parent) : QObject(parent) { - m_manager = new ZigbeeManager("/dev/ttyS0", this); + m_manager = new ZigbeeNetworkManager("/dev/ttyS0", this); } diff --git a/core.h b/core.h index 3e3777d..39f09df 100644 --- a/core.h +++ b/core.h @@ -3,7 +3,7 @@ #include -#include "zigbeemanager.h" +#include "zigbeenetworkmanager.h" class Core : public QObject { @@ -12,7 +12,7 @@ public: explicit Core(QObject *parent = nullptr); private: - ZigbeeManager *m_manager; + ZigbeeNetworkManager *m_manager; signals: diff --git a/zigbeeinterface.cpp b/interface/zigbeeinterface.cpp similarity index 85% rename from zigbeeinterface.cpp rename to interface/zigbeeinterface.cpp index 037e5e2..6b9e2bf 100644 --- a/zigbeeinterface.cpp +++ b/interface/zigbeeinterface.cpp @@ -1,5 +1,6 @@ #include "zigbeeinterface.h" #include "loggingcategory.h" +#include "zigbeeutils.h" ZigbeeInterface::ZigbeeInterface(QObject *parent) : QObject(parent), @@ -9,11 +10,21 @@ ZigbeeInterface::ZigbeeInterface(QObject *parent) : } +ZigbeeInterface::~ZigbeeInterface() +{ + disable(); +} + bool ZigbeeInterface::available() const { return m_serialPort->isOpen(); } +QString ZigbeeInterface::serialPort() const +{ + return m_serialPort->portName(); +} + quint8 ZigbeeInterface::calculateCrc(const quint16 &messageTypeValue, const quint16 &lenghtValue, const QByteArray &data) { quint8 crc = 0; @@ -35,16 +46,16 @@ void ZigbeeInterface::streamByte(quint8 byte, bool specialCharacter) { if (!specialCharacter && byte < 0x10) { // Byte stuffing ESC char before stuffed data byte - qCDebug(dcZigbeeInterfaceTraffic()) << "[out]" << Zigbee::convertByteToHexString(0x02); + qCDebug(dcZigbeeInterfaceTraffic()) << "[out]" << ZigbeeUtils::convertByteToHexString(0x02); if (m_serialPort->write(QByteArray::fromRawData("\x02", 1)) < 0) { - qCWarning(dcZigbeeInterface()) << "Could not stream ESC byte" << Zigbee::convertByteArrayToHexString(QByteArray::fromRawData("\x02", 1)); + qCWarning(dcZigbeeInterface()) << "Could not stream ESC byte" << ZigbeeUtils::convertByteArrayToHexString(QByteArray::fromRawData("\x02", 1)); } byte ^= 0x10; } - qCDebug(dcZigbeeInterfaceTraffic()) << "[out]" << Zigbee::convertByteToHexString(byte); + qCDebug(dcZigbeeInterfaceTraffic()) << "[out]" << ZigbeeUtils::convertByteToHexString(byte); if (m_serialPort->write(QByteArray(1, (char)byte)) < 0) { - qCWarning(dcZigbeeInterface()) << "Could not stream byte" << Zigbee::convertByteToHexString(byte); + qCWarning(dcZigbeeInterface()) << "Could not stream byte" << ZigbeeUtils::convertByteToHexString(byte); } m_serialPort->flush(); } @@ -66,7 +77,7 @@ void ZigbeeInterface::onReadyRead() for (int i = 0; i < data.length(); i++) { quint8 byte = static_cast(data.at(i)); - qCDebug(dcZigbeeInterfaceTraffic()) << "[ in]" << Zigbee::convertByteToHexString(byte); + qCDebug(dcZigbeeInterfaceTraffic()) << "[ in]" << ZigbeeUtils::convertByteToHexString(byte); switch (byte) { case 0x01: @@ -83,14 +94,17 @@ void ZigbeeInterface::onReadyRead() break; case 0x03: { Zigbee::InterfaceMessageType messageType = static_cast(m_messageTypeValue); + + // Check message sanity quint8 crc = calculateCrc(m_messageTypeValue, m_lengthValue, m_data); if (crc != m_crcValue) { qCWarning(dcZigbeeInterface()) << "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; } else { + // We got a valid message ZigbeeInterfaceMessage message(messageType, m_data); - qCDebug(dcZigbeeInterface()) << "<--" << message; + qCDebug(dcZigbeeInterface()) << "<--" << message << "|" << "crc:" << ZigbeeUtils::convertByteToHexString(m_crcValue) << ", length:" << ZigbeeUtils::convertUint16ToHexString(m_lengthValue); emit messageReceived(message); } setReadingState(WaitForStart); @@ -144,7 +158,9 @@ void ZigbeeInterface::onReadyRead() void ZigbeeInterface::onError(const QSerialPort::SerialPortError &error) { - qCWarning(dcZigbeeInterface()) << "Serial port error:" << error << m_serialPort->errorString(); + if (error != QSerialPort::NoError) { + qCWarning(dcZigbeeInterface()) << "Serial port error:" << error << m_serialPort->errorString(); + } } bool ZigbeeInterface::enable(const QString &serialPort) @@ -194,7 +210,7 @@ void ZigbeeInterface::sendMessage(const ZigbeeInterfaceMessage &message) quint8 crcValue = calculateCrc(messageTypeValue, lengthValue, message.data()); - qCDebug(dcZigbeeInterface()) << "-->" << message << "crc:" << Zigbee::convertByteToHexString(crcValue) << ", length:" << Zigbee::convertByte16ToHexString(lengthValue); + qCDebug(dcZigbeeInterface()) << "-->" << message << "|" << "crc:" << ZigbeeUtils::convertByteToHexString(crcValue) << ", length:" << ZigbeeUtils::convertUint16ToHexString(lengthValue); streamByte(0x01, true); streamByte((messageTypeValue >> 8) & 0xff); diff --git a/zigbeeinterface.h b/interface/zigbeeinterface.h similarity index 96% rename from zigbeeinterface.h rename to interface/zigbeeinterface.h index 2a08c6f..e7331ad 100644 --- a/zigbeeinterface.h +++ b/interface/zigbeeinterface.h @@ -23,8 +23,10 @@ public: Q_ENUM(ReadingState) explicit ZigbeeInterface(QObject *parent = nullptr); + ~ZigbeeInterface(); bool available() const; + QString serialPort() const; private: QSerialPort *m_serialPort; diff --git a/zigbeeinterfacemessage.cpp b/interface/zigbeeinterfacemessage.cpp similarity index 66% rename from zigbeeinterfacemessage.cpp rename to interface/zigbeeinterfacemessage.cpp index b34a59d..2b6ff9b 100644 --- a/zigbeeinterfacemessage.cpp +++ b/interface/zigbeeinterfacemessage.cpp @@ -1,4 +1,5 @@ #include "zigbeeinterfacemessage.h" +#include "zigbeeutils.h" ZigbeeInterfaceMessage::ZigbeeInterfaceMessage() { @@ -32,8 +33,13 @@ void ZigbeeInterfaceMessage::setData(const QByteArray &data) m_data = data; } +bool ZigbeeInterfaceMessage::isValid() const +{ + return m_messageType != Zigbee::MessageTypeNone; +} + QDebug operator<<(QDebug dbg, const ZigbeeInterfaceMessage &message) { - dbg.nospace().noquote() << "InterfaceMessage(" << message.messageType() << Zigbee::convertByteArrayToHexString(message.data()) << ")"; + dbg.nospace().noquote() << ZigbeeUtils::messageTypeToString(message.messageType()) << "(" << ZigbeeUtils::convertUint16ToHexString(static_cast(message.messageType())) << ")" << " | " << ZigbeeUtils::convertByteArrayToHexString(message.data()); return dbg.space(); } diff --git a/zigbeeinterfacemessage.h b/interface/zigbeeinterfacemessage.h similarity index 86% rename from zigbeeinterfacemessage.h rename to interface/zigbeeinterfacemessage.h index 030ed77..f533bc5 100644 --- a/zigbeeinterfacemessage.h +++ b/interface/zigbeeinterfacemessage.h @@ -18,8 +18,10 @@ public: QByteArray data() const; void setData(const QByteArray &data); + bool isValid() const; + private: - Zigbee::InterfaceMessageType m_messageType; + Zigbee::InterfaceMessageType m_messageType = Zigbee::MessageTypeNone; QByteArray m_data; }; diff --git a/interface/zigbeeinterfacereply.cpp b/interface/zigbeeinterfacereply.cpp new file mode 100644 index 0000000..814e2a8 --- /dev/null +++ b/interface/zigbeeinterfacereply.cpp @@ -0,0 +1,111 @@ +#include "zigbeeinterfacereply.h" +#include "loggingcategory.h" + +ZigbeeInterfaceReply::ZigbeeInterfaceReply(const ZigbeeInterfaceRequest &request, QObject *parent) : + QObject(parent), + m_request(request) +{ + m_timer = new QTimer(this); + m_timer->setSingleShot(true); + + connect(m_timer, &QTimer::timeout, this, &ZigbeeInterfaceReply::onTimeout); +} + +ZigbeeInterfaceRequest ZigbeeInterfaceReply::request() const +{ + return m_request; +} + +ZigbeeInterfaceMessage ZigbeeInterfaceReply::statusMessage() const +{ + return m_statusMessage; +} + +ZigbeeInterfaceMessage ZigbeeInterfaceReply::additionalMessage() const +{ + return m_additionalMessage; +} + +ZigbeeInterfaceReply::Status ZigbeeInterfaceReply::status() const +{ + return m_status; +} + +quint8 ZigbeeInterfaceReply::statusCode() const +{ + return m_statusCode; +} + +quint8 ZigbeeInterfaceReply::sequenceNumber() const +{ + return m_sequenceNumber; +} + +QString ZigbeeInterfaceReply::statusErrorMessage() const +{ + return m_statusErrorMessage; +} + +void ZigbeeInterfaceReply::setStatusMessage(const ZigbeeInterfaceMessage &statusMessage) +{ + m_statusMessage = statusMessage; + + // Parse status message + m_statusCode = static_cast(statusMessage.data().at(0)); + + // Well known error + if (m_statusCode < 6) { + m_status = static_cast(m_statusCode); + } else if (m_statusCode >= 128 && m_statusCode <= 244) { + qCWarning(dcZigbeeInterface()) << "Got zigbee status code" << m_statusCode; + m_status = ZigbeeErrorEvent; + } else { + qCWarning(dcZigbeeInterface()) << "Got unknown status code"; + } + + // Sequence number + m_sequenceNumber = static_cast(statusMessage.data().at(0)); + + // TODO: compare with the request messageType: 2 bytes of request messageType + + // Got error message + if (statusMessage.data().count() > 4) { + m_statusErrorMessage = QString::fromUtf8(statusMessage.data().right(statusMessage.data().count() -4)); + } +} + +void ZigbeeInterfaceReply::setAdditionalMessage(const ZigbeeInterfaceMessage &additionalMessage) +{ + m_additionalMessage = additionalMessage; +} + +bool ZigbeeInterfaceReply::isComplete() const +{ + // No status received + if (!m_statusMessage.isValid()) + return false; + + // Status received, check if additional message is expected + if (!request().expectsAdditionalMessage()) + return true; + + return m_additionalMessage.isValid(); +} + +void ZigbeeInterfaceReply::startTimer(const int &timeout) +{ + m_timer->start(timeout); +} + +void ZigbeeInterfaceReply::setFinished() +{ + m_timer->stop(); + emit finished(); +} + +void ZigbeeInterfaceReply::onTimeout() +{ + m_timeouted = true; + m_status = Timeouted; + emit timeout(); +} diff --git a/interface/zigbeeinterfacereply.h b/interface/zigbeeinterfacereply.h new file mode 100644 index 0000000..d1216d3 --- /dev/null +++ b/interface/zigbeeinterfacereply.h @@ -0,0 +1,73 @@ +#ifndef ZIGBEEINTERFACEREPLY_H +#define ZIGBEEINTERFACEREPLY_H + +#include +#include + +#include "zigbeeinterfacerequest.h" + +class ZigbeeBridgeController; + +class ZigbeeInterfaceReply : public QObject +{ + Q_OBJECT + friend class ZigbeeBridgeController; + +public: + enum Status { + Success = 0, + InvalidParameter = 1, + UnhandledCommand = 2, + CommandFailed = 3, + Busy = 4, + StackAlreadyStarted = 5, + ZigbeeErrorEvent = 6, + Timeouted = 7 + }; + Q_ENUM(Status) + + explicit ZigbeeInterfaceReply(const ZigbeeInterfaceRequest &request, QObject *parent = nullptr); + + ZigbeeInterfaceRequest request() const; + ZigbeeInterfaceMessage statusMessage() const; + ZigbeeInterfaceMessage additionalMessage() const; + + bool timeouted() const; + + Status status() const; + quint8 statusCode() const; + quint8 sequenceNumber() const; + QString statusErrorMessage() const; + +private: + QTimer *m_timer = nullptr; + bool m_timeouted = false; + + ZigbeeInterfaceRequest m_request; + ZigbeeInterfaceMessage m_statusMessage; + ZigbeeInterfaceMessage m_additionalMessage; + + // Status content + Status m_status; + quint8 m_statusCode; + quint8 m_sequenceNumber; + QString m_statusErrorMessage; + + // Called by ZigbeeBridgeController + void setStatusMessage(const ZigbeeInterfaceMessage &statusMessage); + void setAdditionalMessage(const ZigbeeInterfaceMessage &additionalMessage); + + bool isComplete() const; + void startTimer(const int &timeout = 500); + void setFinished(); + +signals: + void finished(); + void timeout(); + +private slots: + void onTimeout(); + +}; + +#endif // ZIGBEEINTERFACEREPLY_H diff --git a/interface/zigbeeinterfacerequest.cpp b/interface/zigbeeinterfacerequest.cpp new file mode 100644 index 0000000..9b0c39a --- /dev/null +++ b/interface/zigbeeinterfacerequest.cpp @@ -0,0 +1,58 @@ +#include "zigbeeinterfacerequest.h" + +ZigbeeInterfaceRequest::ZigbeeInterfaceRequest() +{ + +} + +ZigbeeInterfaceRequest::ZigbeeInterfaceRequest(const ZigbeeInterfaceMessage &message): + m_message(message) +{ + +} + +void ZigbeeInterfaceRequest::setDescription(const QString &description) +{ + m_description = description; +} + +QString ZigbeeInterfaceRequest::description() const +{ + return m_description; +} + +void ZigbeeInterfaceRequest::setMessage(const ZigbeeInterfaceMessage &message) +{ + m_message = message; +} + +ZigbeeInterfaceMessage ZigbeeInterfaceRequest::message() const +{ + return m_message; +} + +bool ZigbeeInterfaceRequest::expectsAdditionalMessage() const +{ + return m_expectsAdditionalMessage; +} + +void ZigbeeInterfaceRequest::setExpectedAdditionalMessageType(const Zigbee::InterfaceMessageType &messageType) +{ + m_expectedAdditionalMessageType = messageType; + m_expectsAdditionalMessage = true; +} + +Zigbee::InterfaceMessageType ZigbeeInterfaceRequest::expectedAdditionalMessageType() const +{ + return m_expectedAdditionalMessageType; +} + +void ZigbeeInterfaceRequest::setTimoutIntervall(const int &timeoutIntervall) +{ + m_timeoutIntervall = timeoutIntervall; +} + +int ZigbeeInterfaceRequest::timeoutIntervall() const +{ + return m_timeoutIntervall; +} diff --git a/interface/zigbeeinterfacerequest.h b/interface/zigbeeinterfacerequest.h new file mode 100644 index 0000000..9fd820c --- /dev/null +++ b/interface/zigbeeinterfacerequest.h @@ -0,0 +1,35 @@ +#ifndef ZIGBEEINTERFACEREQUEST_H +#define ZIGBEEINTERFACEREQUEST_H + +#include "zigbeeinterfacemessage.h" + +class ZigbeeInterfaceRequest +{ +public: + ZigbeeInterfaceRequest(); + ZigbeeInterfaceRequest(const ZigbeeInterfaceMessage &message); + + void setDescription(const QString &description); + QString description() const; + + void setMessage(const ZigbeeInterfaceMessage &message); + ZigbeeInterfaceMessage message() const; + + bool expectsAdditionalMessage() const; + + void setExpectedAdditionalMessageType(const Zigbee::InterfaceMessageType &messageType); + Zigbee::InterfaceMessageType expectedAdditionalMessageType() const; + + void setTimoutIntervall(const int &timeoutIntervall); + int timeoutIntervall() const; + +private: + QString m_description; + ZigbeeInterfaceMessage m_message; + Zigbee::InterfaceMessageType m_expectedAdditionalMessageType = Zigbee::MessageTypeNone; + bool m_expectsAdditionalMessage = false; + int m_timeoutIntervall = 500; + +}; + +#endif // ZIGBEEINTERFACEREQUEST_H diff --git a/loggingcategory.cpp b/loggingcategory.cpp index f7d06f9..a1d62de 100644 --- a/loggingcategory.cpp +++ b/loggingcategory.cpp @@ -2,4 +2,5 @@ Q_LOGGING_CATEGORY(dcZigbee, "Zigbee") Q_LOGGING_CATEGORY(dcZigbeeInterface, "ZigbeeInterface") +Q_LOGGING_CATEGORY(dcZigbeeController, "ZigbeeController") Q_LOGGING_CATEGORY(dcZigbeeInterfaceTraffic, "ZigbeeInterfaceTraffic") diff --git a/loggingcategory.h b/loggingcategory.h index 2594faa..aa585b8 100644 --- a/loggingcategory.h +++ b/loggingcategory.h @@ -6,6 +6,7 @@ Q_DECLARE_LOGGING_CATEGORY(dcZigbee) Q_DECLARE_LOGGING_CATEGORY(dcZigbeeInterface) +Q_DECLARE_LOGGING_CATEGORY(dcZigbeeController) Q_DECLARE_LOGGING_CATEGORY(dcZigbeeInterfaceTraffic) #endif // LOGGINGCATEGORY_H diff --git a/main.cpp b/main.cpp index 0737d78..8dd05b9 100644 --- a/main.cpp +++ b/main.cpp @@ -58,9 +58,31 @@ int main(int argc, char *argv[]) QCoreApplication application(argc, argv); + // Command line parser + QCommandLineParser parser; + parser.addHelpOption(); + parser.addVersionOption(); + parser.setApplicationDescription(QString("\nDaemon for the zigbee NXP uart bridge.\n\nCopyright %1 2016 Simon Stürz \nAll rights reserved.").arg(QChar(0xA9))); + + QCommandLineOption debugLevelOption(QStringList() << "d" << "debug-level", "Set debug level [1-4]."); + debugLevelOption.setDefaultValue("1"); + debugLevelOption.setValueName("level"); + parser.addOption(debugLevelOption); + + parser.process(application); + + bool debugLevelValueOk = false; + int debugLevel = parser.value(debugLevelOption).toInt(&debugLevelValueOk); + + if (debugLevel < 1 || debugLevel > 4 || !debugLevelValueOk) { + qWarning() << "Invalid debug level passed:" << parser.value(debugLevelOption); + debugLevel = 1; + } + s_loggingFilters.insert("Zigbee", true); - s_loggingFilters.insert("ZigbeeInterface", true); - s_loggingFilters.insert("ZigbeeInterfaceTraffic", false); + s_loggingFilters.insert("ZigbeeController", (debugLevel > 1)); + s_loggingFilters.insert("ZigbeeInterface", (debugLevel > 2)); + s_loggingFilters.insert("ZigbeeInterfaceTraffic", (debugLevel > 3)); QLoggingCategory::installFilter(loggingCategoryFilter); diff --git a/zigbee-daemon.pro b/zigbee-daemon.pro index 1f8b402..c6671cd 100644 --- a/zigbee-daemon.pro +++ b/zigbee-daemon.pro @@ -11,16 +11,28 @@ INSTALLS += target SOURCES += main.cpp \ core.cpp \ - zigbeeinterface.cpp \ - zigbeemanager.cpp \ + interface/zigbeeinterface.cpp \ + interface/zigbeeinterfacemessage.cpp \ + interface/zigbeeinterfacerequest.cpp \ + interface/zigbeeinterfacereply.cpp \ + zigbeenetworkmanager.cpp \ zigbee.cpp \ - zigbeeinterfacemessage.cpp \ - loggingcategory.cpp + loggingcategory.cpp \ + zigbeebridgecontroller.cpp \ + zigbeeutils.cpp \ + zigbeenode.cpp \ + zigbeeaddress.cpp HEADERS += \ core.h \ - zigbeeinterface.h \ - zigbeemanager.h \ + interface/zigbeeinterface.h \ + interface/zigbeeinterfacemessage.h \ + interface/zigbeeinterfacerequest.h \ + interface/zigbeeinterfacereply.h \ + zigbeenetworkmanager.h \ zigbee.h \ - zigbeeinterfacemessage.h \ - loggingcategory.h + loggingcategory.h \ + zigbeebridgecontroller.h \ + zigbeeutils.h \ + zigbeenode.h \ + zigbeeaddress.h diff --git a/zigbee.cpp b/zigbee.cpp index 6e7a411..785ed1b 100644 --- a/zigbee.cpp +++ b/zigbee.cpp @@ -1,33 +1,2 @@ #include "zigbee.h" -QString Zigbee::convertByteToHexString(const quint8 &byte) -{ - QString hexString; - QString byteString = QString::number(byte, 16); - if (byteString.count() == 1) { - hexString = QString("0x0%1").arg(byteString); - } else { - hexString = QString("0x%1").arg(byteString); - } - return hexString.toStdString().data(); -} - -QString Zigbee::convertByteArrayToHexString(const QByteArray &byteArray) -{ - QString hexString; - for (int i = 0; i < byteArray.count(); i++) { - hexString.append(convertByteToHexString((quint8)byteArray.at(i))); - if (i != byteArray.count() -1) { - hexString.append(" "); - } - } - return hexString.toStdString().data(); -} - -QString Zigbee::convertByte16ToHexString(const quint16 &byte) -{ - quint8 msbByte = (byte >> 8) & 0xff; - quint8 lsbByte = (byte >> 0) & 0xff; - - return convertByteToHexString(msbByte) + convertByteToHexString(lsbByte).remove("0x"); -} diff --git a/zigbee.h b/zigbee.h index 11946e6..26ce535 100644 --- a/zigbee.h +++ b/zigbee.h @@ -10,194 +10,352 @@ class Zigbee Q_GADGET public: + enum ZigbeeProfile { + ZigbeeProfileHomeAutomation = 0x0104, + ZigbeeProfileLightLink = 0xC05E + }; + Q_ENUM(ZigbeeProfile) + enum InterfaceMessageType { - /* Common Commands */ - Status = 0x8000, - Logging = 0x8001, + // Common Commands + MessageTypeNone = 0x0000, + MessageTypeStatus = 0x8000, + MessageTypeLogging = 0x8001, - DataIndication = 0x8002, + MessageTypeDataIndication = 0x8002, - NodeClusterList = 0x8003, - NodeAttributeList = 0x8004, - NodeCommandIdList = 0x8005, - RestartProvisioned = 0x8006, - RestartFactoryNew = 0x8007, - GetVersion = 0x0010, - VersionList = 0x8010, + MessageTypeNodeClusterList = 0x8003, + MessageTypeNodeAttributeList = 0x8004, + MessageTypeNodeCommandIdList = 0x8005, + MessageTypeRestartProvisioned = 0x8006, + MessageTypeFactoryNewRestart = 0x8007, + MessageTypeGetVersion = 0x0010, + MessageTypeVersionList = 0x8010, - SetExtendetPanId = 0x0020, - SetChannelMask = 0x0021, - SetSecurity = 0x0022, - SetDeviceType = 0x0023, - StartNetwork = 0x0024, - StartScan = 0x0025, - NetworkJoinedFormed = 0x8024, - NetworkRemoveDevice = 0x0026, - NetworkWhitelistEnable = 0x0027, - AuthenticateDeviceRequest = 0x0028, - AuthenticateDeviceResponse = 0x8028, - OutOfBandCommisioningDataRequest = 0x0029, - OutOfBandCommisioningDataResponse = 0x8029, + MessageTypeSetExtendetPanId = 0x0020, + MessageTypeSetChannelMask = 0x0021, + MessageTypeSetSecurity = 0x0022, + MessageTypeSetDeviceType = 0x0023, + MessageTypeStartNetwork = 0x0024, + MessageTypeStartScan = 0x0025, + MessageTypeNetworkJoinedFormed = 0x8024, + MessageTypeNetworkRemoveDevice = 0x0026, + MessageTypeNetworkWhitelistEnable = 0x0027, + MessageTypeAuthenticateDeviceRequest = 0x0028, + MessageTypeAuthenticateDeviceResponse = 0x8028, + MessageTypeOutOfBandCommisioningDataRequest = 0x0029, + MessageTypeOutOfBandCommisioningDataResponse = 0x8029, - Reset = 0x0011, - ErasePersistentData = 0x0012, - ZllFactoryNew = 0x0013, - GetPermitJoin = 0x0014, - GetPermitJoinResponse = 0x8014, - Bind = 0x0030, - BindResponse = 0x8030, - Unbind = 0x0031, - UnbindResponse = 0x8031, + MessageTypeReset = 0x0011, + MessageTypeErasePersistentData = 0x0012, + MessageTypeZllFactoryNew = 0x0013, + MessageTypeGetPermitJoining = 0x0014, + MessageTypeGetPermitJoiningResponse = 0x8014, + MessageTypeBind = 0x0030, + MessageTypeBindResponse = 0x8030, + MessageTypeUnbind = 0x0031, + MessageTypeUnbindResponse = 0x8031, - NetworkAdressRequest = 0x0040, - NetworkAdressResponse = 0x8040, - IeeeAddressResponse = 0x0041, - IeeeAddressRequest = 0x8041, - NodeDescriptorRequest = 0x0042, - NodeDescriptorRsponse = 0x8042, - SimpleDescriptorRequest = 0x0043, - SimpleDescriptorResponse = 0x8043, - PowerDescriptorRequest = 0x0044, - PowerDescriptorResponse = 0x8044, - ActiveEndpointRequest = 0x0045, - ActiveEndpointResponse = 0x8045, - MatchDescriptorRequest = 0x0046, - MatchDescriptorResponse = 0x8046, - ManagementLeaveRequest = 0x0047, - ManagementLeaveResponse = 0x8047, - LeaveIndication = 0x8048, - PermitJoiningRequest = 0x0049, - ManagementNetworkUpdateRequest = 0x004A, - ManagementNetworkUpdateResponse = 0x804A, - SystemServerDiscoveryRequest = 0x004B, - SystemServerDiscoveryResponse = 0x804B, - DeviceAnnounce = 0x004D, - ManagementLqiRequest = 0x004E, - ManagementLqiResponse = 0x804E, + MessageTypeNetworkAdressRequest = 0x0040, + MessageTypeNetworkAdressResponse = 0x8040, + MessageTypeIeeeAddressResponse = 0x0041, + MessageTypeIeeeAddressRequest = 0x8041, + MessageTypeNodeDescriptorRequest = 0x0042, + MessageTypeNodeDescriptorRsponse = 0x8042, + MessageTypeSimpleDescriptorRequest = 0x0043, + MessageTypeSimpleDescriptorResponse = 0x8043, + MessageTypePowerDescriptorRequest = 0x0044, + MessageTypePowerDescriptorResponse = 0x8044, + MessageTypeActiveEndpointRequest = 0x0045, + MessageTypeActiveEndpointResponse = 0x8045, + MessageTypeMatchDescriptorRequest = 0x0046, + MessageTypeMatchDescriptorResponse = 0x8046, + MessageTypeManagementLeaveRequest = 0x0047, + MessageTypeManagementLeaveResponse = 0x8047, + MessageTypeLeaveIndication = 0x8048, + MessageTypePermitJoiningRequest = 0x0049, + MessageTypeManagementNetworkUpdateRequest = 0x004A, + MessageTypeManagementNetworkUpdateResponse = 0x804A, + MessageTypeSystemServerDiscoveryRequest = 0x004B, + MessageTypeSystemServerDiscoveryResponse = 0x804B, + MessageTypeDeviceAnnounce = 0x004D, + MessageTypeManagementLqiRequest = 0x004E, + MessageTypeManagementLqiResponse = 0x804E, - /* Group Cluster */ - AddGroupRequest = 0x0060, - AddGroupResponse = 0x8060, - ViewGroupRequest = 0x0061, - ViewGroupResponse = 0x8061, - GetGroupMembershipRequest = 0x0062, - GetGroupMembershipResponse = 0x8062, - RemoveGroupRequest = 0x0063, - RemoveGroupResponse = 0x8063, - RemoveAllGroups = 0x0064, - GroupIfIdentify = 0x0065, + // Group Cluster + MessageTypeAddGroupRequest = 0x0060, + MessageTypeAddGroupResponse = 0x8060, + MessageTypeViewGroupRequest = 0x0061, + MessageTypeViewGroupResponse = 0x8061, + MessageTypeGetGroupMembershipRequest = 0x0062, + MessageTypeGetGroupMembershipResponse = 0x8062, + MessageTypeRemoveGroupRequest = 0x0063, + MessageTypeRemoveGroupResponse = 0x8063, + MessageTypeRemoveAllGroups = 0x0064, + MessageTypeGroupIfIdentify = 0x0065, - /* Identify Cluster */ - IdentifySend = 0x0070, - IdentifyQuery = 0x0071, + // Identify Cluster + MessageTypeIdentifySend = 0x0070, + MessageTypeIdentifyQuery = 0x0071, - /* Level Cluster */ - MoveToLevel = 0x0080, - MoveToLevelOnOff = 0x0081, - MoveStep = 0x0082, - MoveStopMove = 0x0083, - MoveStopMoveOnOff = 0x0084, + // Level Cluster + MessageTypeMoveToLevel = 0x0080, + MessageTypeMoveToLevelOnOff = 0x0081, + MessageTypeMoveStep = 0x0082, + MessageTypeMoveStopMove = 0x0083, + MessageTypeMoveStopMoveOnOff = 0x0084, - /* Scenes Cluster */ - ViewScene = 0x00A0, - ViewSceneResponse = 0x80A0, - AddScene = 0x00A1, - AddSceneResponse = 0x80A1, - RemoveScene = 0x00A2, - RemoveSceneResponse = 0x80A2, - RemoveAllScenes = 0x00A3, - RemoveAllScenesResponse = 0x80A3, - StoreScene = 0x00A4, - StoreSceneResponse = 0x80A4, - RecallScene = 0x00A5, - SceneMembershipRequest = 0x00A6, - SceneMembershipResponse = 0x80A6, + // Scenes Cluster + MessageTypeViewScene = 0x00A0, + MessageTypeViewSceneResponse = 0x80A0, + MessageTypeAddScene = 0x00A1, + MessageTypeAddSceneResponse = 0x80A1, + MessageTypeRemoveScene = 0x00A2, + MessageTypeRemoveSceneResponse = 0x80A2, + MessageTypeRemoveAllScenes = 0x00A3, + MessageTypeRemoveAllScenesResponse = 0x80A3, + MessageTypeStoreScene = 0x00A4, + MessageTypeStoreSceneResponse = 0x80A4, + MessageTypeRecallScene = 0x00A5, + MessageTypeSceneMembershipRequest = 0x00A6, + MessageTypeSceneMembershipResponse = 0x80A6, - /* Colour Cluster */ - MoveToHue = 0x00B0, - MoveHue = 0x00B1, - StepHue = 0x00B2, - MoveToSaturation = 0x00B3, - MoveSaturation = 0x00B4, - StepStaturation = 0x00B5, - MoveToHueSaturation = 0x00B6, - MoveToColor = 0x00B7, - MoveColor = 0x00B8, - StepColor = 0x00B9, + //Colour Cluster + MessageTypeMoveToHue = 0x00B0, + MessageTypeMoveHue = 0x00B1, + MessageTypeStepHue = 0x00B2, + MessageTypeMoveToSaturation = 0x00B3, + MessageTypeMoveSaturation = 0x00B4, + MessageTypeStepStaturation = 0x00B5, + MessageTypeMoveToHueSaturation = 0x00B6, + MessageTypeMoveToColor = 0x00B7, + MessageTypeMoveColor = 0x00B8, + MessageTypeStepColor = 0x00B9, - /* ZLL Commands */ + // ZLL Commands /* Touchlink */ - InitiateTouchlink = 0x00D0, - TouchlinkStatus = 0x00D1, - TouchlinkFactoryReset = 0x00D2, + MessageTypeInitiateTouchlink = 0x00D0, + MessageTypeTouchlinkStatus = 0x00D1, + MessageTypeTouchlinkFactoryReset = 0x00D2, - /* Identify Cluster */ - IdentifyTriggerEffect = 0x00E0, + // Identify Cluster + MessageTypeIdentifyTriggerEffect = 0x00E0, - /* On/Off Cluster */ - CluserOnOff = 0x0092, - CluserOnOffTimed = 0x0093, - CluserOnOffEffects = 0x0094, - CluserOnOffUpdate = 0x8095, + // On/Off Cluster + MessageTypeCluserOnOff = 0x0092, + MessageTypeCluserOnOffTimed = 0x0093, + MessageTypeCluserOnOffEffects = 0x0094, + MessageTypeCluserOnOffUpdate = 0x8095, - /* Scenes Cluster */ - AddEnhancedScene = 0x00A7, - ViewEnhancedScene = 0x00A8, - CopyScene = 0x00A9, + // Scenes Cluster + MessageTypeAddEnhancedScene = 0x00A7, + MessageTypeViewEnhancedScene = 0x00A8, + MessageTypeCopyScene = 0x00A9, - /* Colour Cluster */ - EnhancedMoveToHue = 0x00BA, - EnhancedMoveHue = 0x00BB, - EnhancedStepHue = 0x00BC, - EnhancedMoveToHueSaturation = 0x00BD, - ColourLoopSet = 0x00BE, - StopMoveStep = 0x00BF, - MoveToColorTemperature = 0x00C0, - MoveColorTemperature = 0x00C1, - StepColorTemperature = 0x00C2, + // Colour Cluster + MessageTypeEnhancedMoveToHue = 0x00BA, + MessageTypeEnhancedMoveHue = 0x00BB, + MessageTypeEnhancedStepHue = 0x00BC, + MessageTypeEnhancedMoveToHueSaturation = 0x00BD, + MessageTypeColourLoopSet = 0x00BE, + MessageTypeStopMoveStep = 0x00BF, + MessageTypeMoveToColorTemperature = 0x00C0, + MessageTypeMoveColorTemperature = 0x00C1, + MessageTypeStepColorTemperature = 0x00C2, - /* ZHA Commands */ - /* Door Lock Cluster */ - LockUnlockDoor = 0x00F0, + // ZHA Commands + // Door Lock Cluster + MessageTypeLockUnlockDoor = 0x00F0, - /* Attributes */ - ReadAttributeRequest = 0x0100, - ReadAttributeResponse = 0x8100, - DefaultResponse = 0x8101, - AttributeReport = 0x8102, - WriteAttributeRequest = 0x0110, - WriteAttributeResponse = 0x8110, - ConfigReportingRequest = 0x0120, - ConfigReportingResponse = 0x8120, - ReportAttributes = 0x8121, - AttributeDiscoveryRequest = 0x0140, - AttributeDiscoveryResponse = 0x8140, + // Attributes + MessageTypeReadAttributeRequest = 0x0100, + MessageTypeReadAttributeResponse = 0x8100, + MessageTypeDefaultResponse = 0x8101, + MessageTypeAttributeReport = 0x8102, + MessageTypeWriteAttributeRequest = 0x0110, + MessageTypeWriteAttributeResponse = 0x8110, + MessageTypeConfigReportingRequest = 0x0120, + MessageTypeConfigReportingResponse = 0x8120, + MessageTypeReportAttributes = 0x8121, + MessageTypeAttributeDiscoveryRequest = 0x0140, + MessageTypeAttributeDiscoveryResponse = 0x8140, /* Persistant data manager messages */ - DataManagerAvailableRequest = 0x0300, - DataManagerAvailableResponse = 0x8300, - DataManagerSaveRecordRequest = 0x0200, - DataManagerSaveRecordResponse = 0x8200, - DataManagerLoadRecordRequest = 0x0201, - DataManagerLoadRecordResponse = 0x8201, - DataManagerDeleteAllRecordsRequest = 0x0202, - DataManagerDeleteAllRecordsResponse = 0x8202, + MessageTypeDataManagerAvailableRequest = 0x0300, + MessageTypeDataManagerAvailableResponse = 0x8300, + MessageTypeDataManagerSaveRecordRequest = 0x0200, + MessageTypeDataManagerSaveRecordResponse = 0x8200, + MessageTypeDataManagerLoadRecordRequest = 0x0201, + MessageTypeDataManagerLoadRecordResponse = 0x8201, + MessageTypeDataManagerDeleteAllRecordsRequest = 0x0202, + MessageTypeDataManagerDeleteAllRecordsResponse = 0x8202, /* Appliance Statistics Cluster 0x0B03 */ // http://www.nxp.com/documents/user_manual/JN-UG-3076.pdf - StatisticsClusterLogMessage = 0x0301, // Was 0x0500, was 0x0301 - StatisticsClusterLogMessageResponse = 0x8301, + MessageTypeStatisticsClusterLogMessage = 0x0301, // Was 0x0500, was 0x0301 + MessageTypeStatisticsClusterLogMessageResponse = 0x8301, /* IAS Cluster */ - SendIasZoneEnroolResponse = 0x0400, - IasZoneStatusChangeNotify = 0x8401, + MessageTypeSendIasZoneEnroolResponse = 0x0400, + MessageTypeIasZoneStatusChangeNotify = 0x8401, }; Q_ENUM(InterfaceMessageType) - static QString convertByteToHexString(const quint8 &byte); - static QString convertByteArrayToHexString(const QByteArray &byteArray); - static QString convertByte16ToHexString(const quint16 &byte); + enum ClusterId { + // Basics + ClusterIdBasic = 0x0000, + ClusterIdPower = 0x0001, + ClusterIdDeviceTemperature = 0x0002, + ClusterIdIdentify = 0x0003, + ClusterIdGroups = 0x0004, + ClusterIdScenes = 0x0005, + ClusterIdOnOff = 0x0006, + ClusterIdOnOffCOnfiguration = 0x0007, + ClusterIdLevelControl = 0x0008, + ClusterIdAlarms = 0x0009, + ClusterIdTime = 0x000A, + ClusterIdRssiLocation = 0x000B, + ClusterIdAnalogInputBasic = 0x000C, + ClusterIdAnalogOutputBasic = 0x000D, + ClusterIdValueBasic = 0x000E, + ClusterIdBinaryInputBasic = 0x000F, + ClusterIdBinaryOutputBasic = 0x0010, + ClusterIdBinaryValueBasic = 0x0011, + ClusterIdMultiStateInputBasic = 0x0012, + ClusterIdMultiStateOutputBasic = 0x0013, + ClusterIdMultiStateValueBasic = 0x0014, + ClusterIdCommissoning = 0x0015, + + // Over the air uppgrade (OTA) + ClusterIdOtaUpgrade = 0x0019, + + // Closures + ClusterIdShadeConfiguration = 0x0100, + + // Door Lock + ClusterIdDoorLock = 0x0101, + + // Heating, Ventilation and Air-Conditioning (HVAC) + ClusterIdPumpConfigurationControl = 0x0200, + ClusterIdThermostat = 0x0201, + ClusterIdFanControll = 0x0202, + ClusterIdDehumiditationControll = 0x0203, + ClusterIdThermostatUserControll = 0x0204, + + // Lighting + ClusterIdColorControl = 0x0300, + ClusterIdBallastConfiguration = 0x0301, + + // Sensing + ClusterIdMeasurementIlluminance = 0x0400, + ClusterIdIlluminanceLevelSensing = 0x0401, + ClusterIdTemperatureMeasurement = 0x0402, + ClusterIdPressureMeasurement = 0x0403, + ClusterIdFlowMeasurement = 0x0404, + ClusterIdRelativeHumidityMeasurement = 0x0405, + ClusterIdOccapancySensing = 0x0406, + + // Security and Safty + ClusterIdIasZone = 0x0500, + ClusterIdIasAce = 0x0501, + ClusterIdIasWd = 0x0502, + + // Smart energy + ClusterIdPrice = 0x0700, + ClusterIdLoadControl = 0x0701, + ClusterIdSimpleMetering = 0x0702, + + // Electrical Measurement + ClusterIdElectricalMeasurement = 0x0B04, + + // ZLL + ClusterIdTouchlinkCommissioning = 0x1000 + }; + Q_ENUM(ClusterId) + + enum LightLinkDevice { + + // Lightning devices + LightLinkDeviceOnOffLight = 0x0000, + LightLinkDeviceOnOffPlug = 0x0010, + LightLinkDeviceDimmableLight = 0x0100, + LightLinkDeviceDimmablePlug = 0x0110, + LightLinkDeviceColourLight = 0x0200, + LightLinkDeviceExtendedColourLight = 0x0210, + LightLinkDeviceColourTemperatureLight = 0x0220, + + // Controller devices + LightLinkDeviceColourController = 0x8000, + LightLinkDeviceColourSceneController = 0x8010, + LightLinkDeviceNonColourController = 0x8020, + LightLinkDeviceNonColourSceneController = 0x8030, + LightLinkDeviceControlBridge = 0x8040, + LightLinkDeviceOnOffSensor = 0x8050 + }; + Q_ENUM(LightLinkDevice) + + + enum HomeAutomationDevice { + // Generic devices + HomeAutomationDeviceOnOffSwitch = 0x0000, + HomeAutomationDeviceOnOffOutput = 0x0002, + HomeAutomationDeviceRemoteControl = 0x0006, + HomeAutomationDeviceDoorLock = 0x000A, + HomeAutomationDeviceDoorLockController = 0x000B, + HomeAutomationDeviceSimpleSensor = 0x000C, + HomeAutomationDeviceSmartPlug = 0x0051, + + // Lightning devices + HomeAutomationDeviceOnOffLight = 0x0100, + HomeAutomationDeviceDimmableLight = 0x0101, + HomeAutomationDeviceDimmableColorLight = 0x0102, + HomeAutomationDeviceOnOffLightSwitch = 0x0103, + HomeAutomationDeviceDimmableSwitch = 0x0104, + HomeAutomationDeviceColourDimmerSwitch = 0x0105, + HomeAutomationDeviceLightSensor = 0x0106, + HomeAutomationDeviceOccupacySensor = 0x0106, + + // Heating, Ventilation and Air-Conditioning (HVAC) devices + HomeAutomationDeviceThermostat = 0x0301, + + // Intruder Alarm System (IAS) devices + HomeAutomationDeviceIsaControlEquipment = 0x0400, // CIE + HomeAutomationDeviceIsaAncillaryControlEquipment = 0x0401, // ACE + HomeAutomationDeviceIsaZone = 0x0401, + HomeAutomationDeviceIsaWarningDevice = 0x0401 // WD + }; + Q_ENUM(HomeAutomationDevice) + +// enum DeviceType { +// DeviceTypeUnknown = 0xFFFF, // Unknown type +// DeviceTypeBasic = 0x0002, // Gateway +// DeviceTypeGateway = 0x0002, // Gateway +// DeviceTypeSimpleSensor = 0x000C, // ZHA Simple Sensor +// DeviceTypeSmartPlug = 0x0051, // ZHA Smart Plug +// DeviceTypeControlBridge = 0x0840, // Control Bridge +// DeviceTypeLampOnOff = 0x0000, // ZLL on/off lamp +// DeviceTypeLampDimm = 0x0100, // ZLL mono lamp + +//#define SIMPLE_DESCR_LAMP_DIMM_ZLL 0x0100 // ZLL mono lamp +//#define SIMPLE_DESCR_LAMP_ONOFF 0x0100 // ZHA on/off lamp +//#define SIMPLE_DESCR_LAMP_DIMM 0x0101 // ZHA dimmable lamp +//#define SIMPLE_DESCR_LAMP_COLOUR 0x0102 // ZHA dimmable colour lamp +//#define SIMPLE_DESCR_LAMP_CCTW 0x01FF // ZHA / ZLL CCTW lamp +//#define SIMPLE_DESCR_LAMP_COLOUR_DIMM 0x0200 // ZLL dimmable colour lamp +//#define SIMPLE_DESCR_LAMP_COLOUR_EXT 0x0210 // ZLL extended colour lamp +//#define SIMPLE_DESCR_LAMP_COLOUR_TEMP 0x0220 // ZLL colour temperature lamp +//#define SIMPLE_DESCR_HVAC_HC_UNIT 0x0300 // ZHA HVAC HC Unit (HeatingManager) +//#define SIMPLE_DESCR_THERMOSTAT 0x0301 // ZHA Thermostat +//#define SIMPLE_DESCR_HVAC_PUMP 0x0303 // ZHA NVAC Pump +//#define SIMPLE_DESCR_SWITCH_ONOFF 0x0103 // ZHA On/Off Switch +//#define SIMPLE_DESCR_SWITCH_DIMM 0x0104 // ZHA Dimm Switch +//#define SIMPLE_DESCR_SWITCH_COLL_DIMM 0x0105 // ZHA Color Dimm Switch +//#define SIMPLE_DESCR_LIGHT_SENSOR 0x0106 // ZHA Light sensor +//#define SIMPLE_DESCR_SMOKE_SENSOR 0x0012 // CES - TriTech CO/smoke sensor +//#define SIMPLE_DESCR_WINDOW_SENSOR 0x0014 // CES - window sensor +//#define SIMPLE_DESCR_OCCUPANCY_SENSOR 0x0107 // ZH/ZLO - Occupancy Sensor +// }; }; diff --git a/zigbeeaddress.cpp b/zigbeeaddress.cpp new file mode 100644 index 0000000..89d86d6 --- /dev/null +++ b/zigbeeaddress.cpp @@ -0,0 +1,88 @@ +#include "zigbeeaddress.h" + +#include + +ZigbeeAddress::ZigbeeAddress() +{ + +} + +ZigbeeAddress::ZigbeeAddress(quint64 address) +{ + m_address = address; +} + +ZigbeeAddress::ZigbeeAddress(const QString &address) +{ + QString a = address; + + if (a.length() == 17) + a.remove(QLatin1Char(':')); + + if (a.length() == 12) { + bool ok; + m_address = a.toULongLong(&ok, 16); + if (!ok) + clear(); + } else { + m_address = 0; + } +} + +ZigbeeAddress::ZigbeeAddress(const ZigbeeAddress &other) +{ + m_address = other.toUInt64(); +} + +ZigbeeAddress::~ZigbeeAddress() +{ + +} + +quint64 ZigbeeAddress::toUInt64() const +{ + return m_address; +} + +QString ZigbeeAddress::toString() const +{ + QString s(QStringLiteral("%1:%2:%3:%4:%5:%6")); + + for (int i = 5; i >= 0; --i) { + const quint8 a = (m_address >> (i*8)) & 0xff; + s = s.arg(a, 2, 16, QLatin1Char('0')); + } + + return s.toUpper(); +} + +bool ZigbeeAddress::isNull() const +{ + return m_address == 0; +} + +void ZigbeeAddress::clear() +{ + m_address = 0; +} + +bool ZigbeeAddress::operator<(const ZigbeeAddress &other) const +{ + return m_address < other.toUInt64(); +} + +bool ZigbeeAddress::operator==(const ZigbeeAddress &other) const +{ + return m_address == other.toUInt64(); +} + +bool ZigbeeAddress::operator!=(const ZigbeeAddress &other) const +{ + return !operator==(other); +} + +QDebug operator<<(QDebug debug, const ZigbeeAddress &address) +{ + debug << address.toString(); + return debug; +} diff --git a/zigbeeaddress.h b/zigbeeaddress.h new file mode 100644 index 0000000..9af9bba --- /dev/null +++ b/zigbeeaddress.h @@ -0,0 +1,34 @@ +#ifndef ZIGBEEADDRESS_H +#define ZIGBEEADDRESS_H + +#include +#include + +class ZigbeeAddress +{ +public: + ZigbeeAddress(); + explicit ZigbeeAddress(quint64 address); + explicit ZigbeeAddress(const QString &address); + ZigbeeAddress(const ZigbeeAddress &other); + ~ZigbeeAddress(); + + quint64 toUInt64() const; + QString toString() const; + + bool isNull() const; + void clear(); + + ZigbeeAddress &operator=(const ZigbeeAddress &other); + bool operator<(const ZigbeeAddress &other) const; + bool operator==(const ZigbeeAddress &other) const; + inline bool operator!=(const ZigbeeAddress &other) const; + +private: + quint64 m_address; + +}; + +QDebug operator<<(QDebug debug, const ZigbeeAddress &address); + +#endif // ZIGBEEADDRESS_H diff --git a/zigbeebridgecontroller.cpp b/zigbeebridgecontroller.cpp new file mode 100644 index 0000000..1fa290e --- /dev/null +++ b/zigbeebridgecontroller.cpp @@ -0,0 +1,89 @@ +#include "zigbeebridgecontroller.h" +#include "loggingcategory.h" + +ZigbeeBridgeController::ZigbeeBridgeController(const QString &serialPort, QObject *parent) : + QObject(parent) +{ + m_interface = new ZigbeeInterface(this); + + connect(m_interface, &ZigbeeInterface::messageReceived, this, &ZigbeeBridgeController::onMessageReceived); + + if (!m_interface->enable(serialPort)) { + qCWarning(dcZigbeeController()) << "Could not enable ZigbeeInterface on" << serialPort; + return; + } +} + +bool ZigbeeBridgeController::available() const +{ + return m_interface->available(); +} + +void ZigbeeBridgeController::sendMessage(ZigbeeInterfaceReply *reply) +{ + if (!reply) + return; + + m_currentReply = reply; + qCDebug(dcZigbeeController()) << "Sending request:" << reply->request().description(); + + m_interface->sendMessage(reply->request().message()); + reply->startTimer(reply->request().timeoutIntervall()); +} + +void ZigbeeBridgeController::onMessageReceived(const ZigbeeInterfaceMessage &message) +{ + // Check if we have a current reply + if (m_currentReply) { + if (message.messageType() == Zigbee::MessageTypeStatus) { + // We have a status message for the current reply + m_currentReply->setStatusMessage(message); + + // TODO: check if success, if not, finish reply + + } else if (message.messageType() == m_currentReply->request().expectedAdditionalMessageType()) { + m_currentReply->setAdditionalMessage(message); + } + + // Check if request is complete + if (m_currentReply->isComplete()) { + m_currentReply->setFinished(); + // Note: the request class has to take care about the reply object + m_currentReply = nullptr; + + if (!m_replyQueue.isEmpty()) + sendMessage(m_replyQueue.dequeue()); + + return; + } + } + + // Not a reply message + emit messageReceived(message); +} + +void ZigbeeBridgeController::onReplyTimeout() +{ + m_currentReply->setFinished(); + m_currentReply = nullptr; + + if (!m_replyQueue.isEmpty()) + sendMessage(m_replyQueue.dequeue()); + +} + +ZigbeeInterfaceReply *ZigbeeBridgeController::sendRequest(const ZigbeeInterfaceRequest &request) +{ + // Create Reply + ZigbeeInterfaceReply *reply = new ZigbeeInterfaceReply(request); + connect(reply, &ZigbeeInterfaceReply::timeout, this, &ZigbeeBridgeController::onReplyTimeout); + + // If reply running, enqueue, else send request + if (m_currentReply) { + m_replyQueue.enqueue(reply); + } else { + sendMessage(reply); + } + + return reply; +} diff --git a/zigbeebridgecontroller.h b/zigbeebridgecontroller.h new file mode 100644 index 0000000..7a3eb53 --- /dev/null +++ b/zigbeebridgecontroller.h @@ -0,0 +1,42 @@ +#ifndef ZIGBEEBRIDGECONTROLLER_H +#define ZIGBEEBRIDGECONTROLLER_H + +#include +#include + +#include "zigbee.h" +#include "interface/zigbeeinterface.h" +#include "interface/zigbeeinterfacereply.h" +#include "interface/zigbeeinterfacerequest.h" +#include "interface/zigbeeinterfacemessage.h" + +class ZigbeeBridgeController : public QObject +{ + Q_OBJECT +public: + explicit ZigbeeBridgeController(const QString &serialPort, QObject *parent = nullptr); + + bool available() const; + +private: + ZigbeeInterface *m_interface = nullptr; + ZigbeeInterfaceReply *m_currentReply = nullptr; + + QQueue m_replyQueue; + + void sendMessage(ZigbeeInterfaceReply *reply); + +signals: + void messageReceived(const ZigbeeInterfaceMessage &message); + +private slots: + void onMessageReceived(const ZigbeeInterfaceMessage &message); + void onReplyTimeout(); + +public slots: + ZigbeeInterfaceReply *sendRequest(const ZigbeeInterfaceRequest &request); + + +}; + +#endif // ZIGBEEBRIDGECONTROLLER_H diff --git a/zigbeemanager.cpp b/zigbeemanager.cpp deleted file mode 100644 index d05f98c..0000000 --- a/zigbeemanager.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "zigbeemanager.h" -#include "loggingcategory.h" - -ZigbeeManager::ZigbeeManager(const QString &serialPort, QObject *parent) : - QObject(parent), - m_serialPort(serialPort) -{ - m_interface = new ZigbeeInterface(this); - - connect(m_interface, &ZigbeeInterface::messageReceived, this, &ZigbeeManager::onMessageReceived); - - - if (!m_interface->enable(m_serialPort)) { - qCWarning(dcZigbee()) << "Could not enable ZigbeeInterface on" << m_serialPort; - return; - } - - initController(); - -} - -QString ZigbeeManager::serialPort() const -{ - return m_serialPort; -} - -void ZigbeeManager::setSerialPort(const QString &serialPort) -{ - if (m_serialPort == serialPort) - return; - - m_serialPort = serialPort; - m_interface->disable(); - m_interface->enable(m_serialPort); -} - -void ZigbeeManager::initController() -{ - ZigbeeInterfaceMessage message; - message.setMessageType(Zigbee::DataManagerAvailableResponse); - message.setData(QByteArray::fromRawData("\x00\x00", 2)); - - m_interface->sendMessage(message); -} - -void ZigbeeManager::onMessageReceived(const ZigbeeInterfaceMessage &message) -{ - qCDebug(dcZigbee()) << message; -} diff --git a/zigbeemanager.h b/zigbeemanager.h deleted file mode 100644 index 1c70885..0000000 --- a/zigbeemanager.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef ZIGBEEMANAGER_H -#define ZIGBEEMANAGER_H - -#include - -#include "zigbee.h" -#include "zigbeeinterface.h" -#include "zigbeeinterfacemessage.h" - -class ZigbeeManager : public QObject -{ - Q_OBJECT -public: - explicit ZigbeeManager(const QString &serialPort = "/dev/ttyS0", QObject *parent = nullptr); - - QString serialPort() const; - void setSerialPort(const QString &serialPort); - -private: - ZigbeeInterface *m_interface; - QString m_serialPort; - - // Controller methods - void initController(); - -signals: - -private slots: - void onMessageReceived(const ZigbeeInterfaceMessage &message); - -public slots: - -}; - -#endif // ZIGBEEMANAGER_H diff --git a/zigbeenetworkmanager.cpp b/zigbeenetworkmanager.cpp new file mode 100644 index 0000000..abbcbac --- /dev/null +++ b/zigbeenetworkmanager.cpp @@ -0,0 +1,776 @@ +#include "zigbeenetworkmanager.h" +#include "loggingcategory.h" +#include "zigbeeutils.h" + +#include +#include + +ZigbeeNetworkManager::ZigbeeNetworkManager(const QString &serialPort, QObject *parent) : + ZigbeeNode(parent), + m_serialPort(serialPort) +{ + // TODO: load PAN id and serial port + reset(); + +} + +QString ZigbeeNetworkManager::serialPort() const +{ + return m_serialPort; +} + +void ZigbeeNetworkManager::setSerialPort(const QString &serialPort) +{ + if (m_serialPort == serialPort) + return; + + m_serialPort = serialPort; + reset(); +} + +QString ZigbeeNetworkManager::controllerVersion() const +{ + return m_controllerVersion; +} + +void ZigbeeNetworkManager::reset() +{ + if (m_controller) { + delete m_controller; + m_controller = nullptr; + } + + //m_extendedPanId = generateRandomPanId(); + m_extendedPanId = 1180461015847120384; + qCDebug(dcZigbee()) << "PAN ID" << m_extendedPanId; + + m_controller = new ZigbeeBridgeController(m_serialPort, this); + connect(m_controller, &ZigbeeBridgeController::messageReceived, this, &ZigbeeNetworkManager::onMessageReceived); + + if (m_controller->available()) { + qCDebug(dcZigbee()) << "Bridge controller started successfully on" << m_serialPort; + } else { + qCWarning(dcZigbee()) << "The zigbee controller is not available"; + } + + // Call init methods + erasePersistentData(); + //reset(); + getVersion(); + setExtendedPanId(m_extendedPanId); + setChannelMask(0); + setDeviceType(NodeTypeCoordinator); + startNetwork(); + //startScan(); + getPermitJoiningStatus(); + permitJoining(); + getPermitJoiningStatus(); +} + +quint64 ZigbeeNetworkManager::generateRandomPanId() +{ + srand(static_cast(QDateTime::currentMSecsSinceEpoch() / 1000)); + srand(qrand()); + return (ULLONG_MAX - 0) * (qrand()/(double)RAND_MAX); +} + +void ZigbeeNetworkManager::resetController() +{ + ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeReset)); + request.setDescription("Reset controller"); + + ZigbeeInterfaceReply *reply = m_controller->sendRequest(request); + connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onResetControllerFinished); +} + +void ZigbeeNetworkManager::erasePersistentData() +{ + ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeErasePersistentData)); + request.setDescription("Erase persistent data"); + + ZigbeeInterfaceReply *reply = m_controller->sendRequest(request); + connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onErasePersistentDataFinished); +} + +void ZigbeeNetworkManager::sendDataManagerAvailableResponse() +{ + ZigbeeInterfaceMessage message; + message.setMessageType(Zigbee::MessageTypeDataManagerAvailableResponse); + message.setData(QByteArray::fromRawData("\x00\x00", 2)); + + ZigbeeInterfaceRequest request(message); + request.setDescription("Data manager available response"); + + ZigbeeInterfaceReply *reply = m_controller->sendRequest(request); + connect(reply, &ZigbeeInterfaceReply::finished, reply, &ZigbeeInterfaceReply::deleteLater); +} + +void ZigbeeNetworkManager::getVersion() +{ + ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeGetVersion)); + request.setDescription("Get version"); + request.setExpectedAdditionalMessageType(Zigbee::MessageTypeVersionList); + + ZigbeeInterfaceReply *reply = m_controller->sendRequest(request); + connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onGetVersionFinished); +} + +void ZigbeeNetworkManager::setExtendedPanId(const quint64 &panId) +{ + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + stream << panId; + + ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeSetExtendetPanId, data)); + request.setDescription("Set extended PAN ID"); + + ZigbeeInterfaceReply *reply = m_controller->sendRequest(request); + connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onSetExtendedPanIdFinished); +} + +void ZigbeeNetworkManager::setChannelMask(const quint32 &channelMask) +{ + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + stream << channelMask; + + ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeSetChannelMask, data)); + request.setDescription("Set channel mask"); + + ZigbeeInterfaceReply *reply = m_controller->sendRequest(request); + connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onSetChannelMaskFinished); +} + +void ZigbeeNetworkManager::setDeviceType(const NodeType &deviceType) +{ + quint8 deviceTypeValue = static_cast(deviceType); + + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + stream << deviceTypeValue; + + ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeSetDeviceType, data)); + request.setDescription("Set device type"); + + ZigbeeInterfaceReply *reply = m_controller->sendRequest(request); + connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onSetDeviceTypeFinished); +} + +void ZigbeeNetworkManager::startNetwork() +{ + ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeStartNetwork)); + request.setDescription("Start network"); + request.setExpectedAdditionalMessageType(Zigbee::MessageTypeNetworkJoinedFormed); + request.setTimoutIntervall(12000); + + ZigbeeInterfaceReply *reply = m_controller->sendRequest(request); + connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onStartNetworkFinished); +} + +void ZigbeeNetworkManager::startScan() +{ + ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeStartScan)); + request.setDescription("Start scan"); + request.setExpectedAdditionalMessageType(Zigbee::MessageTypeNetworkJoinedFormed); + request.setTimoutIntervall(12000); + + ZigbeeInterfaceReply *reply = m_controller->sendRequest(request); + connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onStartScanFinished); +} + +void ZigbeeNetworkManager::permitJoining(quint16 targetAddress, const quint8 advertisingIntervall) +{ + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + stream << targetAddress; + stream << advertisingIntervall; + + ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypePermitJoiningRequest, data)); + request.setDescription("Permit joining request"); + + ZigbeeInterfaceReply *reply = m_controller->sendRequest(request); + connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onPermitJoiningFinished); +} + +void ZigbeeNetworkManager::getPermitJoiningStatus() +{ + ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeGetPermitJoining)); + request.setDescription("Get permit joining status"); + request.setExpectedAdditionalMessageType(Zigbee::MessageTypeGetPermitJoiningResponse); + request.setTimoutIntervall(1000); + + ZigbeeInterfaceReply *reply = m_controller->sendRequest(request); + connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onGetPermitJoiningStatusFinished); +} + +void ZigbeeNetworkManager::requestNodeDescription(const quint16 &shortAddress) +{ + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + stream << shortAddress; + + ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeNodeDescriptorRequest, data)); + request.setExpectedAdditionalMessageType(Zigbee::MessageTypeNodeDescriptorRsponse); + request.setDescription("Node descriptor request"); + request.setTimoutIntervall(10000); + + ZigbeeInterfaceReply *reply = m_controller->sendRequest(request); + connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onRequestNodeDescriptionFinished); +} + +void ZigbeeNetworkManager::requestSimpleNodeDescription(const quint16 &shortAddress, const quint8 &endpoint) +{ + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + stream << shortAddress; + stream << endpoint; + + ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeSimpleDescriptorRequest, data)); + request.setExpectedAdditionalMessageType(Zigbee::MessageTypeSimpleDescriptorResponse); + request.setDescription("Simple node descriptor request"); + request.setTimoutIntervall(10000); + + ZigbeeInterfaceReply *reply = m_controller->sendRequest(request); + connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onRequestSimpleNodeDescriptionFinished); +} + +void ZigbeeNetworkManager::onResetControllerFinished() +{ + ZigbeeInterfaceReply *reply = static_cast(sender()); + reply->deleteLater(); + + if (reply->status() != ZigbeeInterfaceReply::Success) { + qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); + return; + } + + qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; +} + +void ZigbeeNetworkManager::onErasePersistentDataFinished() +{ + ZigbeeInterfaceReply *reply = static_cast(sender()); + reply->deleteLater(); + + if (reply->status() != ZigbeeInterfaceReply::Success) { + qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); + return; + } + + qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; +} + +void ZigbeeNetworkManager::onGetVersionFinished() +{ + ZigbeeInterfaceReply *reply = static_cast(sender()); + reply->deleteLater(); + + if (reply->status() != ZigbeeInterfaceReply::Success) { + qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); + return; + } + + if (reply->additionalMessage().data().count() != 4) { + qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << ":" << "Invalid payload size"; + return; + } + + qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; + + // Parse major version + quint16 majorVersion = reply->additionalMessage().data().at(0); + majorVersion <<= 8; + majorVersion |= reply->additionalMessage().data().at(1); + + // Parse minor version + quint16 minorVersion = reply->additionalMessage().data().at(2); + minorVersion <<= 8; + minorVersion |= reply->additionalMessage().data().at(3); + + m_controllerVersion = QString("%1.%2").arg(majorVersion).arg(minorVersion); + qCDebug(dcZigbee()) << "Version:" << m_controllerVersion; +} + +void ZigbeeNetworkManager::onSetExtendedPanIdFinished() +{ + ZigbeeInterfaceReply *reply = static_cast(sender()); + reply->deleteLater(); + + if (reply->status() != ZigbeeInterfaceReply::Success) { + qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); + return; + } + + qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; +} + +void ZigbeeNetworkManager::onSetChannelMaskFinished() +{ + ZigbeeInterfaceReply *reply = static_cast(sender()); + reply->deleteLater(); + + if (reply->status() != ZigbeeInterfaceReply::Success) { + qCWarning(dcZigbee()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); + return; + } + + qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; +} + +void ZigbeeNetworkManager::onSetDeviceTypeFinished() +{ + ZigbeeInterfaceReply *reply = static_cast(sender()); + reply->deleteLater(); + + if (reply->status() != ZigbeeInterfaceReply::Success) { + qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); + return; + } + + qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; +} + +void ZigbeeNetworkManager::onStartNetworkFinished() +{ + ZigbeeInterfaceReply *reply = static_cast(sender()); + reply->deleteLater(); + + if (reply->status() != ZigbeeInterfaceReply::Success) { + qCWarning(dcZigbee()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); + return; + } + + qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; + qCDebug(dcZigbeeController()) << reply->additionalMessage(); + + // Parse network status + quint8 networkStatus = static_cast(reply->additionalMessage().data().at(0)); + QString networkStatusString; + + if (networkStatus == 0) { + networkStatusString = "joined"; + } else if (networkStatus == 1) { + networkStatusString = "formed"; + } else if (networkStatus >= 128 && networkStatus <= 244) { + networkStatusString = "failed: Zigbee event code: " + QString::number(networkStatus); + } else { + networkStatusString = "unknown"; + } + + + // Parse short network address + quint16 shortAddress = reply->additionalMessage().data().at(1); + shortAddress <<= 8; + shortAddress |= reply->additionalMessage().data().at(2); + + // Parse extended network address + quint64 extendedAddress = reply->additionalMessage().data().at(3); + extendedAddress <<= 8; + extendedAddress |= reply->additionalMessage().data().at(4); + extendedAddress <<= 8; + extendedAddress |= reply->additionalMessage().data().at(5); + extendedAddress <<= 8; + extendedAddress |= reply->additionalMessage().data().at(6); + extendedAddress <<= 8; + extendedAddress |= reply->additionalMessage().data().at(7); + extendedAddress <<= 8; + extendedAddress |= reply->additionalMessage().data().at(8); + extendedAddress <<= 8; + extendedAddress |= reply->additionalMessage().data().at(9); + extendedAddress <<= 8; + extendedAddress |= reply->additionalMessage().data().at(10); + + // Parse network channel + quint8 channel = static_cast(reply->additionalMessage().data().at(11)); + + qCDebug(dcZigbee()) << "Network" << networkStatusString; + qCDebug(dcZigbee()) << " short address:" << ZigbeeUtils::convertUint16ToHexString(shortAddress); + qCDebug(dcZigbee()) << " extended address:" << ZigbeeUtils::convertUint64ToHexString(extendedAddress); + qCDebug(dcZigbee()) << " channel:" << channel; + + // Set the node information + setShortAddress(shortAddress); + setExtendedAddress(extendedAddress); + + // Request data + requestNodeDescription(shortAddress); + requestSimpleNodeDescription(shortAddress); + +} + +void ZigbeeNetworkManager::onStartScanFinished() +{ + ZigbeeInterfaceReply *reply = static_cast(sender()); + reply->deleteLater(); + + if (reply->status() != ZigbeeInterfaceReply::Success) { + qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); + return; + } + + qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; + qCDebug(dcZigbeeController()) << reply->additionalMessage(); +} + +void ZigbeeNetworkManager::onGetPermitJoiningStatusFinished() +{ + ZigbeeInterfaceReply *reply = static_cast(sender()); + reply->deleteLater(); + + if (reply->status() != ZigbeeInterfaceReply::Success) { + qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); + return; + } + + qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; + qCDebug(dcZigbeeController()) << reply->additionalMessage(); +} + +void ZigbeeNetworkManager::onPermitJoiningFinished() +{ + ZigbeeInterfaceReply *reply = static_cast(sender()); + reply->deleteLater(); + + if (reply->status() != ZigbeeInterfaceReply::Success) { + qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); + return; + } + + qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; + +} + +void ZigbeeNetworkManager::onRequestNodeDescriptionFinished() +{ + ZigbeeInterfaceReply *reply = static_cast(sender()); + reply->deleteLater(); + + if (reply->status() != ZigbeeInterfaceReply::Success) { + qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); + return; + } + + qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; + + quint8 sequenceNumber = static_cast(reply->additionalMessage().data().at(0)); + quint8 status = static_cast(reply->additionalMessage().data().at(1)); + + quint16 shortAddress = reply->additionalMessage().data().at(2); + shortAddress <<= 8; + shortAddress |= reply->additionalMessage().data().at(3); + + quint16 manufacturerCode = reply->additionalMessage().data().at(4); + manufacturerCode <<= 8; + manufacturerCode |= reply->additionalMessage().data().at(5); + + quint16 maximalRxSize = reply->additionalMessage().data().at(6); + maximalRxSize <<= 8; + maximalRxSize |= reply->additionalMessage().data().at(7); + + quint16 maximalTxSize = reply->additionalMessage().data().at(8); + maximalTxSize <<= 8; + maximalTxSize |= reply->additionalMessage().data().at(9); + + quint16 serverMask = 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 = reply->additionalMessage().data().at(15); + bitField <<= 8; + bitField |= reply->additionalMessage().data().at(16); + + // TODO: find note for short address and set data + + qCDebug(dcZigbee()) << "Node descriptor:"; + qCDebug(dcZigbee()) << " Sequence number:" << ZigbeeUtils::convertByteToHexString(sequenceNumber); + qCDebug(dcZigbee()) << " Status:" << ZigbeeUtils::convertByteToHexString(status); + qCDebug(dcZigbee()) << " Short address:" << ZigbeeUtils::convertUint16ToHexString(shortAddress); + qCDebug(dcZigbee()) << " Manufacturer code:" << ZigbeeUtils::convertUint16ToHexString(manufacturerCode); + qCDebug(dcZigbee()) << " Maximum Rx size:" << ZigbeeUtils::convertUint16ToHexString(maximalRxSize); + qCDebug(dcZigbee()) << " Maximum Tx size:" << ZigbeeUtils::convertUint16ToHexString(maximalTxSize); + qCDebug(dcZigbee()) << " Server makk:" << ZigbeeUtils::convertUint16ToHexString(serverMask); + qCDebug(dcZigbee()) << " Descriptor flag:" << ZigbeeUtils::convertByteToHexString(descriptorFlag); + qCDebug(dcZigbee()) << " MAC flags:" << ZigbeeUtils::convertByteToHexString(macFlags); + qCDebug(dcZigbee()) << " Maximum buffer size:" << ZigbeeUtils::convertByteToHexString(maxBufferSize); + qCDebug(dcZigbee()) << " Bit field:" << ZigbeeUtils::convertUint16ToHexString(bitField); + +} + +void ZigbeeNetworkManager::onRequestSimpleNodeDescriptionFinished() +{ + ZigbeeInterfaceReply *reply = static_cast(sender()); + reply->deleteLater(); + + if (reply->status() != ZigbeeInterfaceReply::Success) { + qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); + return; + } + + 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)); + + quint16 nwkAddress = reply->additionalMessage().data().at(2); + nwkAddress <<= 8; + nwkAddress |= reply->additionalMessage().data().at(3); + + quint8 length = static_cast(reply->additionalMessage().data().at(4)); + quint8 endPoint = static_cast(reply->additionalMessage().data().at(5)); + + quint16 profileId = reply->additionalMessage().data().at(6); + profileId <<= 8; + profileId |= reply->additionalMessage().data().at(7); + + quint16 deviceId = reply->additionalMessage().data().at(8); + deviceId <<= 8; + deviceId |= reply->additionalMessage().data().at(9); + + quint8 bitField = static_cast(reply->additionalMessage().data().at(10)); + + qCDebug(dcZigbee()) << "Node somple descriptor:"; + qCDebug(dcZigbee()) << " Sequence number:" << ZigbeeUtils::convertByteToHexString(sequenceNumber); + qCDebug(dcZigbee()) << " Status:" << ZigbeeUtils::convertByteToHexString(status); + qCDebug(dcZigbee()) << " Nwk address:" << ZigbeeUtils::convertUint16ToHexString(nwkAddress); + qCDebug(dcZigbee()) << " Lenght:" << ZigbeeUtils::convertByteToHexString(length); + qCDebug(dcZigbee()) << " End Point:" << ZigbeeUtils::convertByteToHexString(endPoint); + qCDebug(dcZigbee()) << " Profile:" << ZigbeeUtils::profileIdToString((Zigbee::ZigbeeProfile)profileId); + qCDebug(dcZigbee()) << " Device ID:" << ZigbeeUtils::convertUint16ToHexString(deviceId); + qCDebug(dcZigbee()) << " Bit field:" << ZigbeeUtils::convertByteToHexString(bitField); + + quint8 inputClusterCount = static_cast(reply->additionalMessage().data().at(10)); + + qCDebug(dcZigbee()) << " Input clusters:"; + QByteArray inputClusterListData = reply->additionalMessage().data().mid(11, inputClusterCount * 2); + for (int i = 0; i < inputClusterListData.count(); i+=2) { + quint16 clusterId = inputClusterListData.at(i); + clusterId <<= 8; + clusterId |= inputClusterListData .at(i+1); + + qCDebug(dcZigbee()) << " Cluster ID:" << ZigbeeUtils::convertUint16ToHexString(clusterId) << static_cast(clusterId); + } + + quint8 outputClusterCount = static_cast(reply->additionalMessage().data().at(12 + inputClusterCount * 2)); + + qCDebug(dcZigbee()) << " Output clusters:"; + QByteArray outputClusterListData = reply->additionalMessage().data().mid(12 + inputClusterCount * 2, outputClusterCount * 2); + for (int i = 0; i < outputClusterListData.count(); i+=2) { + quint16 clusterId = outputClusterListData.at(i); + clusterId <<= 8; + clusterId |= outputClusterListData .at(i+1); + + qCDebug(dcZigbee()) << " Cluster ID:" << ZigbeeUtils::convertUint16ToHexString(clusterId) << static_cast(clusterId); + } + +} + +void ZigbeeNetworkManager::processLoggingMessage(const ZigbeeInterfaceMessage &message) +{ + quint8 logLevel = static_cast(message.data().at(0)); + QString logMessage = QString::fromUtf8(message.data().right(message.data().count() - 1)); + + QString logLevelString; + switch (logLevel) { + case 0: + logLevelString = "Emergency:"; + break; + case 1: + logLevelString = "Alert:"; + break; + case 2: + logLevelString = "Critical:"; + break; + case 3: + logLevelString = "Error:"; + break; + case 4: + logLevelString = "Warning:"; + break; + case 5: + logLevelString = "Notice:"; + break; + case 6: + logLevelString = "Information:"; + break; + case 7: + logLevelString = "Debug:"; + break; + default: + logLevelString = "Unknown:"; + break; + } + + if (logLevel < 5) { + qCWarning(dcZigbeeController()).noquote() << "ControllerLog:" << logLevelString << logMessage; + } else { + qCDebug(dcZigbeeController()).noquote() << "ControllerLog:" << logLevelString << logMessage; + } +} + +void ZigbeeNetworkManager::processFactoryNewRestart(const ZigbeeInterfaceMessage &message) +{ + quint8 controllerStatus = static_cast(message.data().at(0)); + QString controllerStatusString; + switch (controllerStatus) { + case 0: + controllerStatusString = "startup"; + break; + case 2: + controllerStatusString = "NRF start"; + break; + case 6: + controllerStatusString = "running"; + break; + default: + break; + } + + qCDebug(dcZigbee()) << "Restart finished. Current controller state:" << controllerStatusString; +} + +void ZigbeeNetworkManager::processNodeClusterList(const ZigbeeInterfaceMessage &message) +{ + quint8 sourceEndpoint = static_cast(message.data().at(0)); + + quint16 profileId = static_cast(message.data().at(1)); + profileId <<= 8; + profileId |= static_cast(message.data().at(2)); + + qCDebug(dcZigbee()) << "Node cluster list received:"; + qCDebug(dcZigbee()) << " Souce endpoint:" << sourceEndpoint; + qCDebug(dcZigbee()) << " Profile:" << ZigbeeUtils::profileIdToString(static_cast(profileId)); + + + QByteArray clusterListData = message.data().right(message.data().count() - 3); + + for (int i = 0; i < clusterListData.count(); i+=2) { + quint16 clusterId = clusterListData.at(i); + clusterId <<= 8; + clusterId |= clusterListData .at(i+1); + + qCDebug(dcZigbee()) << " Cluster ID:" << ZigbeeUtils::clusterIdToString(static_cast(clusterId)) << static_cast(clusterId); + } +} + +void ZigbeeNetworkManager::processNodeAttributeList(const ZigbeeInterfaceMessage &message) +{ + quint8 sourceEndpoint = static_cast(message.data().at(0)); + + quint16 profileId = static_cast(message.data().at(1)); + profileId <<= 8; + profileId |= static_cast(message.data().at(2)); + + quint16 clusterId = static_cast(message.data().at(3)); + clusterId <<= 8; + clusterId |= static_cast(message.data().at(4)); + + + qCDebug(dcZigbee()) << "Node attribute list received:"; + qCDebug(dcZigbee()) << " Souce endpoint:" << sourceEndpoint; + qCDebug(dcZigbee()) << " Profile:" << ZigbeeUtils::profileIdToString(static_cast(profileId)); + qCDebug(dcZigbee()) << " Cluster ID:" << ZigbeeUtils::clusterIdToString(static_cast(clusterId)); + + QByteArray attributeListData = message.data().right(message.data().count() - 5); + + for (int i = 0; i < attributeListData.count(); i+=2) { + quint16 attribute = attributeListData.at(i); + attribute <<= 8; + attribute |= attributeListData .at(i+1); + + qCDebug(dcZigbee()) << " Attribute:" << ZigbeeUtils::convertUint16ToHexString(attribute); + } +} + +void ZigbeeNetworkManager::processNodeCommandIdList(const ZigbeeInterfaceMessage &message) +{ + quint8 sourceEndpoint = static_cast(message.data().at(0)); + + quint16 profileId = static_cast(message.data().at(1)); + profileId <<= 8; + profileId |= static_cast(message.data().at(2)); + + quint16 clusterId = static_cast(message.data().at(3)); + clusterId <<= 8; + clusterId |= static_cast(message.data().at(4)); + + qCDebug(dcZigbee()) << "Node command list received:"; + qCDebug(dcZigbee()) << " Souce endpoint:" << sourceEndpoint; + qCDebug(dcZigbee()) << " Profile:" << ZigbeeUtils::profileIdToString(static_cast(profileId)); + qCDebug(dcZigbee()) << " Cluster ID:" << ZigbeeUtils::clusterIdToString(static_cast(clusterId)); + + QByteArray commandListData = message.data().right(message.data().count() - 5); + + for (int i = 0; i < commandListData.count(); i++) { + quint8 attribute = commandListData.at(i); + qCDebug(dcZigbee()) << " Command:" << ZigbeeUtils::convertByteToHexString(attribute); + } +} + +void ZigbeeNetworkManager::processDeviceAnnounce(const ZigbeeInterfaceMessage &message) +{ + quint16 shortAddress = message.data().at(0); + shortAddress <<= 8; + shortAddress |= message.data().at(1); + + quint64 ieeeAddress = message.data().at(2); + ieeeAddress <<= 8; + ieeeAddress |= message.data().at(3); + ieeeAddress <<= 8; + ieeeAddress |= message.data().at(4); + ieeeAddress <<= 8; + ieeeAddress |= message.data().at(5); + ieeeAddress <<= 8; + ieeeAddress |= message.data().at(6); + ieeeAddress <<= 8; + ieeeAddress |= message.data().at(7); + ieeeAddress <<= 8; + ieeeAddress |= message.data().at(8); + ieeeAddress <<= 8; + ieeeAddress |= message.data().at(9); + + quint8 macCapability = message.data().at(10); + + qCDebug(dcZigbee()) << "Device announced:"; + qCDebug(dcZigbee()) << " Short address:" << ZigbeeUtils::convertUint16ToHexString(shortAddress); + qCDebug(dcZigbee()) << " Extended address:" << ZigbeeUtils::convertUint64ToHexString(ieeeAddress); + qCDebug(dcZigbee()) << " Mac capabilities:" << ZigbeeUtils::convertByteToHexString(macCapability); + + // TODO: parse mac capabilities + + requestNodeDescription(shortAddress); + requestSimpleNodeDescription(shortAddress); +} + +void ZigbeeNetworkManager::onMessageReceived(const ZigbeeInterfaceMessage &message) +{ + switch (message.messageType()) { + case Zigbee::MessageTypeLogging: + processLoggingMessage(message); + break; + case Zigbee::MessageTypeFactoryNewRestart: + processFactoryNewRestart(message); + break; + case Zigbee::MessageTypeNodeClusterList: + processNodeClusterList(message); + break; + case Zigbee::MessageTypeNodeAttributeList: + processNodeAttributeList(message); + break; + case Zigbee::MessageTypeNodeCommandIdList: + processNodeCommandIdList(message); + break; + case Zigbee::MessageTypeDeviceAnnounce: + processDeviceAnnounce(message); + break; + default: + qCDebug(dcZigbeeController()) << "Message received:" << message; + break; + } +} + + diff --git a/zigbeenetworkmanager.h b/zigbeenetworkmanager.h new file mode 100644 index 0000000..b6cf89a --- /dev/null +++ b/zigbeenetworkmanager.h @@ -0,0 +1,74 @@ +#ifndef ZIGBEEMANAGER_H +#define ZIGBEEMANAGER_H + +#include + +#include "zigbeenode.h" +#include "zigbeebridgecontroller.h" + +class ZigbeeNetworkManager : public ZigbeeNode +{ + Q_OBJECT +public: + explicit ZigbeeNetworkManager(const QString &serialPort = "/dev/ttyS0", QObject *parent = nullptr); + + QString serialPort() const; + void setSerialPort(const QString &serialPort); + + QString controllerVersion() const; + + void reset(); + +private: + ZigbeeBridgeController *m_controller = nullptr; + QString m_serialPort; + QString m_controllerVersion; + quint64 m_extendedPanId; + + quint64 generateRandomPanId(); + + // Controller methods + void resetController(); + void erasePersistentData(); + void sendDataManagerAvailableResponse(); + void getVersion(); + void setExtendedPanId(const quint64 &panId); + void setChannelMask(const quint32 &channelMask); + void setDeviceType(const NodeType &deviceType); + void startNetwork(); + void startScan(); + void permitJoining(quint16 targetAddress = 0xfffc, const quint8 advertisingIntervall = 254); + void getPermitJoiningStatus(); + void requestNodeDescription(const quint16 &shortAddress); + void requestSimpleNodeDescription(const quint16 &shortAddress, const quint8 &endpoint = 1); + +signals: + +private slots: + void onMessageReceived(const ZigbeeInterfaceMessage &message); + + // Controller methods finished slots + void onResetControllerFinished(); + void onErasePersistentDataFinished(); + void onGetVersionFinished(); + void onSetExtendedPanIdFinished(); + void onSetChannelMaskFinished(); + void onSetDeviceTypeFinished(); + void onStartNetworkFinished(); + void onStartScanFinished(); + void onGetPermitJoiningStatusFinished(); + void onPermitJoiningFinished(); + + void onRequestNodeDescriptionFinished(); + void onRequestSimpleNodeDescriptionFinished(); + + // Controler notifications + void processLoggingMessage(const ZigbeeInterfaceMessage &message); + void processFactoryNewRestart(const ZigbeeInterfaceMessage &message); + void processNodeClusterList(const ZigbeeInterfaceMessage &message); + void processNodeAttributeList(const ZigbeeInterfaceMessage &message); + void processNodeCommandIdList(const ZigbeeInterfaceMessage &message); + void processDeviceAnnounce(const ZigbeeInterfaceMessage &message); +}; + +#endif // ZIGBEEMANAGER_H diff --git a/zigbeenode.cpp b/zigbeenode.cpp new file mode 100644 index 0000000..fa75b5b --- /dev/null +++ b/zigbeenode.cpp @@ -0,0 +1,31 @@ +#include "zigbeenode.h" + +ZigbeeNode::ZigbeeNode(QObject *parent) : QObject(parent) +{ + +} + +quint16 ZigbeeNode::shortAddress() const +{ + return m_shortAddress; +} + +quint64 ZigbeeNode::extendedAddress() const +{ + return m_extendedAddress; +} + +ZigbeeNode::NodeType ZigbeeNode::nodeType() const +{ + return m_nodeType; +} + +void ZigbeeNode::setShortAddress(const quint16 &shortAddress) +{ + m_shortAddress = shortAddress; +} + +void ZigbeeNode::setExtendedAddress(const quint64 &extendedAddress) +{ + m_extendedAddress = extendedAddress; +} diff --git a/zigbeenode.h b/zigbeenode.h new file mode 100644 index 0000000..b8dfa73 --- /dev/null +++ b/zigbeenode.h @@ -0,0 +1,56 @@ +#ifndef ZIGBEENODE_H +#define ZIGBEENODE_H + +#include + +#include "zigbee.h" + +class ZigbeeNode : public QObject +{ + Q_OBJECT +public: + enum NodeType { + NodeTypeCoordinator = 0, + NodeTypeRouter = 1, + NodeTypeEndDevice = 2 + }; + Q_ENUM(NodeType) + + enum FrequencyBand { + FrequencyBand868Mhz, + FrequencyBand902Mhz, + FrequencyBand2400Mhz + }; + Q_ENUM(FrequencyBand) + + + + + explicit ZigbeeNode(QObject *parent = nullptr); + + quint16 shortAddress() const; + quint64 extendedAddress() const; + + // Information from node descriptor + NodeType nodeType() const; + FrequencyBand frequencyBand() const; + + bool canBeCoordinator() const; + +private: + quint16 m_shortAddress = 0; + quint64 m_extendedAddress = 0; + NodeType m_nodeType; + +protected: + void setShortAddress(const quint16 &shortAddress); + void setExtendedAddress(const quint64 &extendedAddress); + + +signals: + +public slots: + +}; + +#endif // ZIGBEENODE_H diff --git a/zigbeeutils.cpp b/zigbeeutils.cpp new file mode 100644 index 0000000..c424a86 --- /dev/null +++ b/zigbeeutils.cpp @@ -0,0 +1,74 @@ +#include "zigbeeutils.h" + +#include +#include + +QString ZigbeeUtils::convertByteToHexString(const quint8 &byte) +{ + QString hexString; + QString byteString = QString::number(byte, 16); + if (byteString.count() == 1) { + hexString = QString("0x0%1").arg(byteString); + } else { + hexString = QString("0x%1").arg(byteString); + } + return hexString.toStdString().data(); +} + +QString ZigbeeUtils::convertByteArrayToHexString(const QByteArray &byteArray) +{ + QString hexString; + for (int i = 0; i < byteArray.count(); i++) { + hexString.append(convertByteToHexString((quint8)byteArray.at(i))); + if (i != byteArray.count() -1) { + hexString.append(" "); + } + } + return hexString.toStdString().data(); +} + +QString ZigbeeUtils::convertUint16ToHexString(const quint16 &byte) +{ + quint8 msbByte = (byte >> 8) & 0xff; + quint8 lsbByte = (byte >> 0) & 0xff; + + return convertByteToHexString(msbByte) + convertByteToHexString(lsbByte).remove("0x"); +} + +QString ZigbeeUtils::convertUint64ToHexString(const quint64 &byte) +{ + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + stream << byte; + return QString("0x%1").arg(convertByteArrayToHexString(data).remove(" ").remove("0x")); +} + +QString ZigbeeUtils::messageTypeToString(const Zigbee::InterfaceMessageType &type) +{ + QMetaObject metaObject = Zigbee::staticMetaObject; + QMetaEnum metaEnum = metaObject.enumerator(metaObject.indexOfEnumerator("InterfaceMessageType")); + + QString enumString = metaEnum.valueToKey(type); + + return enumString.remove("Zigbee::InterfaceMessageType(MessageType").remove(")"); +} + +QString ZigbeeUtils::clusterIdToString(const Zigbee::ClusterId &clusterId) +{ + QMetaObject metaObject = Zigbee::staticMetaObject; + QMetaEnum metaEnum = metaObject.enumerator(metaObject.indexOfEnumerator("ClusterId")); + + QString enumString = metaEnum.valueToKey(clusterId); + + return enumString.remove("Zigbee::ClusterId(ClusterId").remove(")"); +} + +QString ZigbeeUtils::profileIdToString(const Zigbee::ZigbeeProfile &profileId) +{ + QMetaObject metaObject = Zigbee::staticMetaObject; + QMetaEnum metaEnum = metaObject.enumerator(metaObject.indexOfEnumerator("ZigbeeProfile")); + + QString enumString = metaEnum.valueToKey(profileId); + + return enumString.remove("Zigbee::ZigbeeProfile(ZigbeeProfile").remove(")"); +} diff --git a/zigbeeutils.h b/zigbeeutils.h new file mode 100644 index 0000000..fee574c --- /dev/null +++ b/zigbeeutils.h @@ -0,0 +1,26 @@ +#ifndef ZIGBEEUTILS_H +#define ZIGBEEUTILS_H + +#include + +#include "zigbee.h" + +class ZigbeeUtils +{ + Q_GADGET + +public: + // Debug utils + static QString convertByteToHexString(const quint8 &byte); + static QString convertByteArrayToHexString(const QByteArray &byteArray); + static QString convertUint16ToHexString(const quint16 &byte); + static QString convertUint64ToHexString(const quint64 &byte); + + // Enum prittify print methods + static QString messageTypeToString(const Zigbee::InterfaceMessageType &type); + static QString clusterIdToString(const Zigbee::ClusterId &clusterId); + static QString profileIdToString(const Zigbee::ZigbeeProfile &profileId); + +}; + +#endif // ZIGBEEUTILS_H