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