add descriptor parsing and device types

This commit is contained in:
Simon Stürz 2017-10-20 15:08:00 +02:00
parent 3d0385363f
commit a3a9252092
28 changed files with 1968 additions and 296 deletions

View File

@ -4,5 +4,5 @@
Core::Core(QObject *parent) : Core::Core(QObject *parent) :
QObject(parent) QObject(parent)
{ {
m_manager = new ZigbeeManager("/dev/ttyS0", this); m_manager = new ZigbeeNetworkManager("/dev/ttyS0", this);
} }

4
core.h
View File

@ -3,7 +3,7 @@
#include <QObject> #include <QObject>
#include "zigbeemanager.h" #include "zigbeenetworkmanager.h"
class Core : public QObject class Core : public QObject
{ {
@ -12,7 +12,7 @@ public:
explicit Core(QObject *parent = nullptr); explicit Core(QObject *parent = nullptr);
private: private:
ZigbeeManager *m_manager; ZigbeeNetworkManager *m_manager;
signals: signals:

View File

@ -1,5 +1,6 @@
#include "zigbeeinterface.h" #include "zigbeeinterface.h"
#include "loggingcategory.h" #include "loggingcategory.h"
#include "zigbeeutils.h"
ZigbeeInterface::ZigbeeInterface(QObject *parent) : ZigbeeInterface::ZigbeeInterface(QObject *parent) :
QObject(parent), QObject(parent),
@ -9,11 +10,21 @@ ZigbeeInterface::ZigbeeInterface(QObject *parent) :
} }
ZigbeeInterface::~ZigbeeInterface()
{
disable();
}
bool ZigbeeInterface::available() const bool ZigbeeInterface::available() const
{ {
return m_serialPort->isOpen(); 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 ZigbeeInterface::calculateCrc(const quint16 &messageTypeValue, const quint16 &lenghtValue, const QByteArray &data)
{ {
quint8 crc = 0; quint8 crc = 0;
@ -35,16 +46,16 @@ void ZigbeeInterface::streamByte(quint8 byte, bool specialCharacter)
{ {
if (!specialCharacter && byte < 0x10) { if (!specialCharacter && byte < 0x10) {
// Byte stuffing ESC char before stuffed data byte // 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) { 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; byte ^= 0x10;
} }
qCDebug(dcZigbeeInterfaceTraffic()) << "[out]" << Zigbee::convertByteToHexString(byte); qCDebug(dcZigbeeInterfaceTraffic()) << "[out]" << ZigbeeUtils::convertByteToHexString(byte);
if (m_serialPort->write(QByteArray(1, (char)byte)) < 0) { 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(); m_serialPort->flush();
} }
@ -66,7 +77,7 @@ void ZigbeeInterface::onReadyRead()
for (int i = 0; i < data.length(); i++) { for (int i = 0; i < data.length(); i++) {
quint8 byte = static_cast<quint8>(data.at(i)); quint8 byte = static_cast<quint8>(data.at(i));
qCDebug(dcZigbeeInterfaceTraffic()) << "[ in]" << Zigbee::convertByteToHexString(byte); qCDebug(dcZigbeeInterfaceTraffic()) << "[ in]" << ZigbeeUtils::convertByteToHexString(byte);
switch (byte) { switch (byte) {
case 0x01: case 0x01:
@ -83,14 +94,17 @@ void ZigbeeInterface::onReadyRead()
break; break;
case 0x03: { case 0x03: {
Zigbee::InterfaceMessageType messageType = static_cast<Zigbee::InterfaceMessageType>(m_messageTypeValue); Zigbee::InterfaceMessageType messageType = static_cast<Zigbee::InterfaceMessageType>(m_messageTypeValue);
// Check message sanity
quint8 crc = calculateCrc(m_messageTypeValue, m_lengthValue, m_data); quint8 crc = calculateCrc(m_messageTypeValue, m_lengthValue, m_data);
if (crc != m_crcValue) { if (crc != m_crcValue) {
qCWarning(dcZigbeeInterface()) << "Invalid CRC value" << crc << "!=" << m_crcValue; qCWarning(dcZigbeeInterface()) << "Invalid CRC value" << crc << "!=" << m_crcValue;
} else if (m_data.count() != m_lengthValue) { } else if (m_data.count() != m_lengthValue) {
qCWarning(dcZigbeeInterface()) << "ERROR:s Invalid data length" << m_data.count() << "!=" << m_lengthValue; qCWarning(dcZigbeeInterface()) << "ERROR:s Invalid data length" << m_data.count() << "!=" << m_lengthValue;
} else { } else {
// We got a valid message
ZigbeeInterfaceMessage message(messageType, m_data); ZigbeeInterfaceMessage message(messageType, m_data);
qCDebug(dcZigbeeInterface()) << "<--" << message; qCDebug(dcZigbeeInterface()) << "<--" << message << "|" << "crc:" << ZigbeeUtils::convertByteToHexString(m_crcValue) << ", length:" << ZigbeeUtils::convertUint16ToHexString(m_lengthValue);
emit messageReceived(message); emit messageReceived(message);
} }
setReadingState(WaitForStart); setReadingState(WaitForStart);
@ -144,7 +158,9 @@ void ZigbeeInterface::onReadyRead()
void ZigbeeInterface::onError(const QSerialPort::SerialPortError &error) 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) bool ZigbeeInterface::enable(const QString &serialPort)
@ -194,7 +210,7 @@ void ZigbeeInterface::sendMessage(const ZigbeeInterfaceMessage &message)
quint8 crcValue = calculateCrc(messageTypeValue, lengthValue, message.data()); 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(0x01, true);
streamByte((messageTypeValue >> 8) & 0xff); streamByte((messageTypeValue >> 8) & 0xff);

View File

@ -23,8 +23,10 @@ public:
Q_ENUM(ReadingState) Q_ENUM(ReadingState)
explicit ZigbeeInterface(QObject *parent = nullptr); explicit ZigbeeInterface(QObject *parent = nullptr);
~ZigbeeInterface();
bool available() const; bool available() const;
QString serialPort() const;
private: private:
QSerialPort *m_serialPort; QSerialPort *m_serialPort;

View File

@ -1,4 +1,5 @@
#include "zigbeeinterfacemessage.h" #include "zigbeeinterfacemessage.h"
#include "zigbeeutils.h"
ZigbeeInterfaceMessage::ZigbeeInterfaceMessage() ZigbeeInterfaceMessage::ZigbeeInterfaceMessage()
{ {
@ -32,8 +33,13 @@ void ZigbeeInterfaceMessage::setData(const QByteArray &data)
m_data = data; m_data = data;
} }
bool ZigbeeInterfaceMessage::isValid() const
{
return m_messageType != Zigbee::MessageTypeNone;
}
QDebug operator<<(QDebug dbg, const ZigbeeInterfaceMessage &message) 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<quint16>(message.messageType())) << ")" << " | " << ZigbeeUtils::convertByteArrayToHexString(message.data());
return dbg.space(); return dbg.space();
} }

View File

@ -18,8 +18,10 @@ public:
QByteArray data() const; QByteArray data() const;
void setData(const QByteArray &data); void setData(const QByteArray &data);
bool isValid() const;
private: private:
Zigbee::InterfaceMessageType m_messageType; Zigbee::InterfaceMessageType m_messageType = Zigbee::MessageTypeNone;
QByteArray m_data; QByteArray m_data;
}; };

View File

@ -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<quint8>(statusMessage.data().at(0));
// Well known error
if (m_statusCode < 6) {
m_status = static_cast<Status>(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<quint8>(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();
}

View File

@ -0,0 +1,73 @@
#ifndef ZIGBEEINTERFACEREPLY_H
#define ZIGBEEINTERFACEREPLY_H
#include <QObject>
#include <QTimer>
#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

View File

@ -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;
}

View File

@ -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

View File

@ -2,4 +2,5 @@
Q_LOGGING_CATEGORY(dcZigbee, "Zigbee") Q_LOGGING_CATEGORY(dcZigbee, "Zigbee")
Q_LOGGING_CATEGORY(dcZigbeeInterface, "ZigbeeInterface") Q_LOGGING_CATEGORY(dcZigbeeInterface, "ZigbeeInterface")
Q_LOGGING_CATEGORY(dcZigbeeController, "ZigbeeController")
Q_LOGGING_CATEGORY(dcZigbeeInterfaceTraffic, "ZigbeeInterfaceTraffic") Q_LOGGING_CATEGORY(dcZigbeeInterfaceTraffic, "ZigbeeInterfaceTraffic")

View File

@ -6,6 +6,7 @@
Q_DECLARE_LOGGING_CATEGORY(dcZigbee) Q_DECLARE_LOGGING_CATEGORY(dcZigbee)
Q_DECLARE_LOGGING_CATEGORY(dcZigbeeInterface) Q_DECLARE_LOGGING_CATEGORY(dcZigbeeInterface)
Q_DECLARE_LOGGING_CATEGORY(dcZigbeeController)
Q_DECLARE_LOGGING_CATEGORY(dcZigbeeInterfaceTraffic) Q_DECLARE_LOGGING_CATEGORY(dcZigbeeInterfaceTraffic)
#endif // LOGGINGCATEGORY_H #endif // LOGGINGCATEGORY_H

View File

@ -58,9 +58,31 @@ int main(int argc, char *argv[])
QCoreApplication application(argc, 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 <simon.stuerz@guh.io>\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("Zigbee", true);
s_loggingFilters.insert("ZigbeeInterface", true); s_loggingFilters.insert("ZigbeeController", (debugLevel > 1));
s_loggingFilters.insert("ZigbeeInterfaceTraffic", false); s_loggingFilters.insert("ZigbeeInterface", (debugLevel > 2));
s_loggingFilters.insert("ZigbeeInterfaceTraffic", (debugLevel > 3));
QLoggingCategory::installFilter(loggingCategoryFilter); QLoggingCategory::installFilter(loggingCategoryFilter);

View File

@ -11,16 +11,28 @@ INSTALLS += target
SOURCES += main.cpp \ SOURCES += main.cpp \
core.cpp \ core.cpp \
zigbeeinterface.cpp \ interface/zigbeeinterface.cpp \
zigbeemanager.cpp \ interface/zigbeeinterfacemessage.cpp \
interface/zigbeeinterfacerequest.cpp \
interface/zigbeeinterfacereply.cpp \
zigbeenetworkmanager.cpp \
zigbee.cpp \ zigbee.cpp \
zigbeeinterfacemessage.cpp \ loggingcategory.cpp \
loggingcategory.cpp zigbeebridgecontroller.cpp \
zigbeeutils.cpp \
zigbeenode.cpp \
zigbeeaddress.cpp
HEADERS += \ HEADERS += \
core.h \ core.h \
zigbeeinterface.h \ interface/zigbeeinterface.h \
zigbeemanager.h \ interface/zigbeeinterfacemessage.h \
interface/zigbeeinterfacerequest.h \
interface/zigbeeinterfacereply.h \
zigbeenetworkmanager.h \
zigbee.h \ zigbee.h \
zigbeeinterfacemessage.h \ loggingcategory.h \
loggingcategory.h zigbeebridgecontroller.h \
zigbeeutils.h \
zigbeenode.h \
zigbeeaddress.h

View File

@ -1,33 +1,2 @@
#include "zigbee.h" #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");
}

474
zigbee.h
View File

@ -10,194 +10,352 @@ class Zigbee
Q_GADGET Q_GADGET
public: public:
enum ZigbeeProfile {
ZigbeeProfileHomeAutomation = 0x0104,
ZigbeeProfileLightLink = 0xC05E
};
Q_ENUM(ZigbeeProfile)
enum InterfaceMessageType { enum InterfaceMessageType {
/* Common Commands */ // Common Commands
Status = 0x8000, MessageTypeNone = 0x0000,
Logging = 0x8001, MessageTypeStatus = 0x8000,
MessageTypeLogging = 0x8001,
DataIndication = 0x8002, MessageTypeDataIndication = 0x8002,
NodeClusterList = 0x8003, MessageTypeNodeClusterList = 0x8003,
NodeAttributeList = 0x8004, MessageTypeNodeAttributeList = 0x8004,
NodeCommandIdList = 0x8005, MessageTypeNodeCommandIdList = 0x8005,
RestartProvisioned = 0x8006, MessageTypeRestartProvisioned = 0x8006,
RestartFactoryNew = 0x8007, MessageTypeFactoryNewRestart = 0x8007,
GetVersion = 0x0010, MessageTypeGetVersion = 0x0010,
VersionList = 0x8010, MessageTypeVersionList = 0x8010,
SetExtendetPanId = 0x0020, MessageTypeSetExtendetPanId = 0x0020,
SetChannelMask = 0x0021, MessageTypeSetChannelMask = 0x0021,
SetSecurity = 0x0022, MessageTypeSetSecurity = 0x0022,
SetDeviceType = 0x0023, MessageTypeSetDeviceType = 0x0023,
StartNetwork = 0x0024, MessageTypeStartNetwork = 0x0024,
StartScan = 0x0025, MessageTypeStartScan = 0x0025,
NetworkJoinedFormed = 0x8024, MessageTypeNetworkJoinedFormed = 0x8024,
NetworkRemoveDevice = 0x0026, MessageTypeNetworkRemoveDevice = 0x0026,
NetworkWhitelistEnable = 0x0027, MessageTypeNetworkWhitelistEnable = 0x0027,
AuthenticateDeviceRequest = 0x0028, MessageTypeAuthenticateDeviceRequest = 0x0028,
AuthenticateDeviceResponse = 0x8028, MessageTypeAuthenticateDeviceResponse = 0x8028,
OutOfBandCommisioningDataRequest = 0x0029, MessageTypeOutOfBandCommisioningDataRequest = 0x0029,
OutOfBandCommisioningDataResponse = 0x8029, MessageTypeOutOfBandCommisioningDataResponse = 0x8029,
Reset = 0x0011, MessageTypeReset = 0x0011,
ErasePersistentData = 0x0012, MessageTypeErasePersistentData = 0x0012,
ZllFactoryNew = 0x0013, MessageTypeZllFactoryNew = 0x0013,
GetPermitJoin = 0x0014, MessageTypeGetPermitJoining = 0x0014,
GetPermitJoinResponse = 0x8014, MessageTypeGetPermitJoiningResponse = 0x8014,
Bind = 0x0030, MessageTypeBind = 0x0030,
BindResponse = 0x8030, MessageTypeBindResponse = 0x8030,
Unbind = 0x0031, MessageTypeUnbind = 0x0031,
UnbindResponse = 0x8031, MessageTypeUnbindResponse = 0x8031,
NetworkAdressRequest = 0x0040, MessageTypeNetworkAdressRequest = 0x0040,
NetworkAdressResponse = 0x8040, MessageTypeNetworkAdressResponse = 0x8040,
IeeeAddressResponse = 0x0041, MessageTypeIeeeAddressResponse = 0x0041,
IeeeAddressRequest = 0x8041, MessageTypeIeeeAddressRequest = 0x8041,
NodeDescriptorRequest = 0x0042, MessageTypeNodeDescriptorRequest = 0x0042,
NodeDescriptorRsponse = 0x8042, MessageTypeNodeDescriptorRsponse = 0x8042,
SimpleDescriptorRequest = 0x0043, MessageTypeSimpleDescriptorRequest = 0x0043,
SimpleDescriptorResponse = 0x8043, MessageTypeSimpleDescriptorResponse = 0x8043,
PowerDescriptorRequest = 0x0044, MessageTypePowerDescriptorRequest = 0x0044,
PowerDescriptorResponse = 0x8044, MessageTypePowerDescriptorResponse = 0x8044,
ActiveEndpointRequest = 0x0045, MessageTypeActiveEndpointRequest = 0x0045,
ActiveEndpointResponse = 0x8045, MessageTypeActiveEndpointResponse = 0x8045,
MatchDescriptorRequest = 0x0046, MessageTypeMatchDescriptorRequest = 0x0046,
MatchDescriptorResponse = 0x8046, MessageTypeMatchDescriptorResponse = 0x8046,
ManagementLeaveRequest = 0x0047, MessageTypeManagementLeaveRequest = 0x0047,
ManagementLeaveResponse = 0x8047, MessageTypeManagementLeaveResponse = 0x8047,
LeaveIndication = 0x8048, MessageTypeLeaveIndication = 0x8048,
PermitJoiningRequest = 0x0049, MessageTypePermitJoiningRequest = 0x0049,
ManagementNetworkUpdateRequest = 0x004A, MessageTypeManagementNetworkUpdateRequest = 0x004A,
ManagementNetworkUpdateResponse = 0x804A, MessageTypeManagementNetworkUpdateResponse = 0x804A,
SystemServerDiscoveryRequest = 0x004B, MessageTypeSystemServerDiscoveryRequest = 0x004B,
SystemServerDiscoveryResponse = 0x804B, MessageTypeSystemServerDiscoveryResponse = 0x804B,
DeviceAnnounce = 0x004D, MessageTypeDeviceAnnounce = 0x004D,
ManagementLqiRequest = 0x004E, MessageTypeManagementLqiRequest = 0x004E,
ManagementLqiResponse = 0x804E, MessageTypeManagementLqiResponse = 0x804E,
/* Group Cluster */ // Group Cluster
AddGroupRequest = 0x0060, MessageTypeAddGroupRequest = 0x0060,
AddGroupResponse = 0x8060, MessageTypeAddGroupResponse = 0x8060,
ViewGroupRequest = 0x0061, MessageTypeViewGroupRequest = 0x0061,
ViewGroupResponse = 0x8061, MessageTypeViewGroupResponse = 0x8061,
GetGroupMembershipRequest = 0x0062, MessageTypeGetGroupMembershipRequest = 0x0062,
GetGroupMembershipResponse = 0x8062, MessageTypeGetGroupMembershipResponse = 0x8062,
RemoveGroupRequest = 0x0063, MessageTypeRemoveGroupRequest = 0x0063,
RemoveGroupResponse = 0x8063, MessageTypeRemoveGroupResponse = 0x8063,
RemoveAllGroups = 0x0064, MessageTypeRemoveAllGroups = 0x0064,
GroupIfIdentify = 0x0065, MessageTypeGroupIfIdentify = 0x0065,
/* Identify Cluster */ // Identify Cluster
IdentifySend = 0x0070, MessageTypeIdentifySend = 0x0070,
IdentifyQuery = 0x0071, MessageTypeIdentifyQuery = 0x0071,
/* Level Cluster */ // Level Cluster
MoveToLevel = 0x0080, MessageTypeMoveToLevel = 0x0080,
MoveToLevelOnOff = 0x0081, MessageTypeMoveToLevelOnOff = 0x0081,
MoveStep = 0x0082, MessageTypeMoveStep = 0x0082,
MoveStopMove = 0x0083, MessageTypeMoveStopMove = 0x0083,
MoveStopMoveOnOff = 0x0084, MessageTypeMoveStopMoveOnOff = 0x0084,
/* Scenes Cluster */ // Scenes Cluster
ViewScene = 0x00A0, MessageTypeViewScene = 0x00A0,
ViewSceneResponse = 0x80A0, MessageTypeViewSceneResponse = 0x80A0,
AddScene = 0x00A1, MessageTypeAddScene = 0x00A1,
AddSceneResponse = 0x80A1, MessageTypeAddSceneResponse = 0x80A1,
RemoveScene = 0x00A2, MessageTypeRemoveScene = 0x00A2,
RemoveSceneResponse = 0x80A2, MessageTypeRemoveSceneResponse = 0x80A2,
RemoveAllScenes = 0x00A3, MessageTypeRemoveAllScenes = 0x00A3,
RemoveAllScenesResponse = 0x80A3, MessageTypeRemoveAllScenesResponse = 0x80A3,
StoreScene = 0x00A4, MessageTypeStoreScene = 0x00A4,
StoreSceneResponse = 0x80A4, MessageTypeStoreSceneResponse = 0x80A4,
RecallScene = 0x00A5, MessageTypeRecallScene = 0x00A5,
SceneMembershipRequest = 0x00A6, MessageTypeSceneMembershipRequest = 0x00A6,
SceneMembershipResponse = 0x80A6, MessageTypeSceneMembershipResponse = 0x80A6,
/* Colour Cluster */ //Colour Cluster
MoveToHue = 0x00B0, MessageTypeMoveToHue = 0x00B0,
MoveHue = 0x00B1, MessageTypeMoveHue = 0x00B1,
StepHue = 0x00B2, MessageTypeStepHue = 0x00B2,
MoveToSaturation = 0x00B3, MessageTypeMoveToSaturation = 0x00B3,
MoveSaturation = 0x00B4, MessageTypeMoveSaturation = 0x00B4,
StepStaturation = 0x00B5, MessageTypeStepStaturation = 0x00B5,
MoveToHueSaturation = 0x00B6, MessageTypeMoveToHueSaturation = 0x00B6,
MoveToColor = 0x00B7, MessageTypeMoveToColor = 0x00B7,
MoveColor = 0x00B8, MessageTypeMoveColor = 0x00B8,
StepColor = 0x00B9, MessageTypeStepColor = 0x00B9,
/* ZLL Commands */ // ZLL Commands
/* Touchlink */ /* Touchlink */
InitiateTouchlink = 0x00D0, MessageTypeInitiateTouchlink = 0x00D0,
TouchlinkStatus = 0x00D1, MessageTypeTouchlinkStatus = 0x00D1,
TouchlinkFactoryReset = 0x00D2, MessageTypeTouchlinkFactoryReset = 0x00D2,
/* Identify Cluster */ // Identify Cluster
IdentifyTriggerEffect = 0x00E0, MessageTypeIdentifyTriggerEffect = 0x00E0,
/* On/Off Cluster */ // On/Off Cluster
CluserOnOff = 0x0092, MessageTypeCluserOnOff = 0x0092,
CluserOnOffTimed = 0x0093, MessageTypeCluserOnOffTimed = 0x0093,
CluserOnOffEffects = 0x0094, MessageTypeCluserOnOffEffects = 0x0094,
CluserOnOffUpdate = 0x8095, MessageTypeCluserOnOffUpdate = 0x8095,
/* Scenes Cluster */ // Scenes Cluster
AddEnhancedScene = 0x00A7, MessageTypeAddEnhancedScene = 0x00A7,
ViewEnhancedScene = 0x00A8, MessageTypeViewEnhancedScene = 0x00A8,
CopyScene = 0x00A9, MessageTypeCopyScene = 0x00A9,
/* Colour Cluster */ // Colour Cluster
EnhancedMoveToHue = 0x00BA, MessageTypeEnhancedMoveToHue = 0x00BA,
EnhancedMoveHue = 0x00BB, MessageTypeEnhancedMoveHue = 0x00BB,
EnhancedStepHue = 0x00BC, MessageTypeEnhancedStepHue = 0x00BC,
EnhancedMoveToHueSaturation = 0x00BD, MessageTypeEnhancedMoveToHueSaturation = 0x00BD,
ColourLoopSet = 0x00BE, MessageTypeColourLoopSet = 0x00BE,
StopMoveStep = 0x00BF, MessageTypeStopMoveStep = 0x00BF,
MoveToColorTemperature = 0x00C0, MessageTypeMoveToColorTemperature = 0x00C0,
MoveColorTemperature = 0x00C1, MessageTypeMoveColorTemperature = 0x00C1,
StepColorTemperature = 0x00C2, MessageTypeStepColorTemperature = 0x00C2,
/* ZHA Commands */ // ZHA Commands
/* Door Lock Cluster */ // Door Lock Cluster
LockUnlockDoor = 0x00F0, MessageTypeLockUnlockDoor = 0x00F0,
/* Attributes */ // Attributes
ReadAttributeRequest = 0x0100, MessageTypeReadAttributeRequest = 0x0100,
ReadAttributeResponse = 0x8100, MessageTypeReadAttributeResponse = 0x8100,
DefaultResponse = 0x8101, MessageTypeDefaultResponse = 0x8101,
AttributeReport = 0x8102, MessageTypeAttributeReport = 0x8102,
WriteAttributeRequest = 0x0110, MessageTypeWriteAttributeRequest = 0x0110,
WriteAttributeResponse = 0x8110, MessageTypeWriteAttributeResponse = 0x8110,
ConfigReportingRequest = 0x0120, MessageTypeConfigReportingRequest = 0x0120,
ConfigReportingResponse = 0x8120, MessageTypeConfigReportingResponse = 0x8120,
ReportAttributes = 0x8121, MessageTypeReportAttributes = 0x8121,
AttributeDiscoveryRequest = 0x0140, MessageTypeAttributeDiscoveryRequest = 0x0140,
AttributeDiscoveryResponse = 0x8140, MessageTypeAttributeDiscoveryResponse = 0x8140,
/* Persistant data manager messages */ /* Persistant data manager messages */
DataManagerAvailableRequest = 0x0300, MessageTypeDataManagerAvailableRequest = 0x0300,
DataManagerAvailableResponse = 0x8300, MessageTypeDataManagerAvailableResponse = 0x8300,
DataManagerSaveRecordRequest = 0x0200, MessageTypeDataManagerSaveRecordRequest = 0x0200,
DataManagerSaveRecordResponse = 0x8200, MessageTypeDataManagerSaveRecordResponse = 0x8200,
DataManagerLoadRecordRequest = 0x0201, MessageTypeDataManagerLoadRecordRequest = 0x0201,
DataManagerLoadRecordResponse = 0x8201, MessageTypeDataManagerLoadRecordResponse = 0x8201,
DataManagerDeleteAllRecordsRequest = 0x0202, MessageTypeDataManagerDeleteAllRecordsRequest = 0x0202,
DataManagerDeleteAllRecordsResponse = 0x8202, MessageTypeDataManagerDeleteAllRecordsResponse = 0x8202,
/* Appliance Statistics Cluster 0x0B03 */ /* Appliance Statistics Cluster 0x0B03 */
// http://www.nxp.com/documents/user_manual/JN-UG-3076.pdf // http://www.nxp.com/documents/user_manual/JN-UG-3076.pdf
StatisticsClusterLogMessage = 0x0301, // Was 0x0500, was 0x0301 MessageTypeStatisticsClusterLogMessage = 0x0301, // Was 0x0500, was 0x0301
StatisticsClusterLogMessageResponse = 0x8301, MessageTypeStatisticsClusterLogMessageResponse = 0x8301,
/* IAS Cluster */ /* IAS Cluster */
SendIasZoneEnroolResponse = 0x0400, MessageTypeSendIasZoneEnroolResponse = 0x0400,
IasZoneStatusChangeNotify = 0x8401, MessageTypeIasZoneStatusChangeNotify = 0x8401,
}; };
Q_ENUM(InterfaceMessageType) 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
// };
}; };

88
zigbeeaddress.cpp Normal file
View File

@ -0,0 +1,88 @@
#include "zigbeeaddress.h"
#include <QDebug>
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;
}

34
zigbeeaddress.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef ZIGBEEADDRESS_H
#define ZIGBEEADDRESS_H
#include <QByteArray>
#include <QString>
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

View File

@ -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;
}

42
zigbeebridgecontroller.h Normal file
View File

@ -0,0 +1,42 @@
#ifndef ZIGBEEBRIDGECONTROLLER_H
#define ZIGBEEBRIDGECONTROLLER_H
#include <QObject>
#include <QQueue>
#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<ZigbeeInterfaceReply *> 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

View File

@ -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;
}

View File

@ -1,35 +0,0 @@
#ifndef ZIGBEEMANAGER_H
#define ZIGBEEMANAGER_H
#include <QObject>
#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

776
zigbeenetworkmanager.cpp Normal file
View File

@ -0,0 +1,776 @@
#include "zigbeenetworkmanager.h"
#include "loggingcategory.h"
#include "zigbeeutils.h"
#include <QDateTime>
#include <QDataStream>
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<int>(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<quint8>(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<ZigbeeInterfaceReply *>(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<ZigbeeInterfaceReply *>(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<ZigbeeInterfaceReply *>(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<ZigbeeInterfaceReply *>(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<ZigbeeInterfaceReply *>(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<ZigbeeInterfaceReply *>(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<ZigbeeInterfaceReply *>(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<quint8>(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<quint8>(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<ZigbeeInterfaceReply *>(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<ZigbeeInterfaceReply *>(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<ZigbeeInterfaceReply *>(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<ZigbeeInterfaceReply *>(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<quint8>(reply->additionalMessage().data().at(0));
quint8 status = static_cast<quint8>(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<quint8>(reply->additionalMessage().data().at(12));
quint8 macFlags = static_cast<quint8>(reply->additionalMessage().data().at(13));
quint8 maxBufferSize = static_cast<quint8>(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<ZigbeeInterfaceReply *>(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<quint8>(reply->additionalMessage().data().at(0));
quint8 status = static_cast<quint8>(reply->additionalMessage().data().at(1));
quint16 nwkAddress = reply->additionalMessage().data().at(2);
nwkAddress <<= 8;
nwkAddress |= reply->additionalMessage().data().at(3);
quint8 length = static_cast<quint8>(reply->additionalMessage().data().at(4));
quint8 endPoint = static_cast<quint8>(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<quint8>(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<quint8>(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<Zigbee::ClusterId>(clusterId);
}
quint8 outputClusterCount = static_cast<quint8>(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<Zigbee::ClusterId>(clusterId);
}
}
void ZigbeeNetworkManager::processLoggingMessage(const ZigbeeInterfaceMessage &message)
{
quint8 logLevel = static_cast<quint8>(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<quint8>(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<quint8>(message.data().at(0));
quint16 profileId = static_cast<quint8>(message.data().at(1));
profileId <<= 8;
profileId |= static_cast<quint8>(message.data().at(2));
qCDebug(dcZigbee()) << "Node cluster list received:";
qCDebug(dcZigbee()) << " Souce endpoint:" << sourceEndpoint;
qCDebug(dcZigbee()) << " Profile:" << ZigbeeUtils::profileIdToString(static_cast<Zigbee::ZigbeeProfile>(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<Zigbee::ClusterId>(clusterId)) << static_cast<Zigbee::ClusterId>(clusterId);
}
}
void ZigbeeNetworkManager::processNodeAttributeList(const ZigbeeInterfaceMessage &message)
{
quint8 sourceEndpoint = static_cast<quint8>(message.data().at(0));
quint16 profileId = static_cast<quint8>(message.data().at(1));
profileId <<= 8;
profileId |= static_cast<quint8>(message.data().at(2));
quint16 clusterId = static_cast<quint8>(message.data().at(3));
clusterId <<= 8;
clusterId |= static_cast<quint8>(message.data().at(4));
qCDebug(dcZigbee()) << "Node attribute list received:";
qCDebug(dcZigbee()) << " Souce endpoint:" << sourceEndpoint;
qCDebug(dcZigbee()) << " Profile:" << ZigbeeUtils::profileIdToString(static_cast<Zigbee::ZigbeeProfile>(profileId));
qCDebug(dcZigbee()) << " Cluster ID:" << ZigbeeUtils::clusterIdToString(static_cast<Zigbee::ClusterId>(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<quint8>(message.data().at(0));
quint16 profileId = static_cast<quint8>(message.data().at(1));
profileId <<= 8;
profileId |= static_cast<quint8>(message.data().at(2));
quint16 clusterId = static_cast<quint8>(message.data().at(3));
clusterId <<= 8;
clusterId |= static_cast<quint8>(message.data().at(4));
qCDebug(dcZigbee()) << "Node command list received:";
qCDebug(dcZigbee()) << " Souce endpoint:" << sourceEndpoint;
qCDebug(dcZigbee()) << " Profile:" << ZigbeeUtils::profileIdToString(static_cast<Zigbee::ZigbeeProfile>(profileId));
qCDebug(dcZigbee()) << " Cluster ID:" << ZigbeeUtils::clusterIdToString(static_cast<Zigbee::ClusterId>(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;
}
}

74
zigbeenetworkmanager.h Normal file
View File

@ -0,0 +1,74 @@
#ifndef ZIGBEEMANAGER_H
#define ZIGBEEMANAGER_H
#include <QObject>
#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

31
zigbeenode.cpp Normal file
View File

@ -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;
}

56
zigbeenode.h Normal file
View File

@ -0,0 +1,56 @@
#ifndef ZIGBEENODE_H
#define ZIGBEENODE_H
#include <QObject>
#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

74
zigbeeutils.cpp Normal file
View File

@ -0,0 +1,74 @@
#include "zigbeeutils.h"
#include <QMetaEnum>
#include <QDataStream>
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(")");
}

26
zigbeeutils.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef ZIGBEEUTILS_H
#define ZIGBEEUTILS_H
#include <QObject>
#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