Add on off cluster and use zigbedatatype for atributes

This commit is contained in:
Simon Stürz 2020-05-27 21:56:41 +02:00
parent ab560a9c55
commit 7e5643a1cf
20 changed files with 607 additions and 148 deletions

View File

@ -250,7 +250,7 @@ bool ZigbeeInterfaceDeconz::enable(const QString &serialPort, qint32 baudrate)
connect(m_serialPort, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(onError(QSerialPort::SerialPortError)));
if (!m_serialPort->open(QSerialPort::ReadWrite)) {
qCWarning(dcZigbeeInterface()) << "Could not open serial port" << serialPort << baudrate;
qCWarning(dcZigbeeInterface()) << "Could not open serial port" << serialPort << baudrate << m_serialPort->errorString();
m_reconnectTimer->start();
return false;
}

View File

@ -8,6 +8,7 @@ SOURCES += \
backends/deconz/interface/zigbeeinterfacedeconzreply.cpp \
backends/deconz/zigbeebridgecontrollerdeconz.cpp \
backends/deconz/zigbeenetworkdeconz.cpp \
zcl/general/zigbeeclusteronoff.cpp \
zcl/zigbeecluster.cpp \
zcl/zigbeeclusterattribute.cpp \
zcl/zigbeeclusterlibrary.cpp \
@ -49,6 +50,7 @@ HEADERS += \
backends/deconz/interface/zigbeeinterfacedeconzreply.h \
backends/deconz/zigbeebridgecontrollerdeconz.h \
backends/deconz/zigbeenetworkdeconz.h \
zcl/general/zigbeeclusteronoff.h \
zcl/zigbeecluster.h \
zcl/zigbeeclusterattribute.h \
zcl/zigbeeclusterlibrary.h \

View File

@ -1,3 +1,30 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea-zigbee.
* This project including source code and documentation is protected by copyright law, and
* remains the property of nymea GmbH. All rights, including reproduction, publication,
* editing and translation, are reserved. The use of this project is subject to the terms of a
* license agreement to be concluded with nymea GmbH in accordance with the terms
* of use of nymea GmbH, available under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation; version 3.
* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this project.
* If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under contact@nymea.io
* or see our FAQ/Licensing Information on https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "zigbeeclusterbasic.h"
#include "loggingcategory.h"
@ -10,11 +37,11 @@ ZigbeeClusterBasic::ZigbeeClusterBasic(ZigbeeNetwork *network, ZigbeeNode *node,
void ZigbeeClusterBasic::setAttribute(const ZigbeeClusterAttribute &attribute)
{
if (hasAttribute(attribute.id())) {
qCDebug(dcZigbeeCluster()) << this << "update attribute" << static_cast<Attribute>(attribute.id()) << attribute.dataType() << attribute.data();
qCDebug(dcZigbeeCluster()) << "Update attribute" << this << static_cast<Attribute>(attribute.id()) << attribute.dataType();
m_attributes[attribute.id()] = attribute;
emit attributeChanged(attribute);
} else {
qCDebug(dcZigbeeCluster()) << this << "add attribute" << static_cast<Attribute>(attribute.id()) << attribute.dataType() << attribute.data();
qCDebug(dcZigbeeCluster()) << "Add attribute" << this << static_cast<Attribute>(attribute.id()) << attribute.dataType();
m_attributes.insert(attribute.id(), attribute);
emit attributeChanged(attribute);
}

View File

@ -1,3 +1,30 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea-zigbee.
* This project including source code and documentation is protected by copyright law, and
* remains the property of nymea GmbH. All rights, including reproduction, publication,
* editing and translation, are reserved. The use of this project is subject to the terms of a
* license agreement to be concluded with nymea GmbH in accordance with the terms
* of use of nymea GmbH, available under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation; version 3.
* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this project.
* If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under contact@nymea.io
* or see our FAQ/Licensing Information on https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef ZIGBEECLUSTERBASIC_H
#define ZIGBEECLUSTERBASIC_H
@ -172,13 +199,9 @@ public:
explicit ZigbeeClusterBasic(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent = nullptr);
private:
void setAttribute(const ZigbeeClusterAttribute &attribute) override;
signals:
};
Q_DECLARE_OPERATORS_FOR_FLAGS(ZigbeeClusterBasic::AlarmMasks)

View File

@ -0,0 +1,159 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea-zigbee.
* This project including source code and documentation is protected by copyright law, and
* remains the property of nymea GmbH. All rights, including reproduction, publication,
* editing and translation, are reserved. The use of this project is subject to the terms of a
* license agreement to be concluded with nymea GmbH in accordance with the terms
* of use of nymea GmbH, available under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation; version 3.
* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this project.
* If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under contact@nymea.io
* or see our FAQ/Licensing Information on https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "zigbeeclusteronoff.h"
#include "zigbeenetworkreply.h"
#include "loggingcategory.h"
#include "zigbeenetwork.h"
#include <QDataStream>
ZigbeeClusterOnOff::ZigbeeClusterOnOff(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent) :
ZigbeeCluster(network, node, endpoint, Zigbee::ClusterIdOnOff, direction, parent)
{
}
ZigbeeClusterReply *ZigbeeClusterOnOff::commandOff()
{
ZigbeeNetworkRequest request = createGeneralRequest();
// Build ZCL frame
// Note: for basic commands the frame control files has to be zero accoring to spec ZCL 2.4.1.1
ZigbeeClusterLibrary::FrameControl frameControl;
frameControl.frameType = ZigbeeClusterLibrary::FrameTypeClusterSpecific;
frameControl.manufacturerSpecific = false;
frameControl.direction = ZigbeeClusterLibrary::DirectionClientToServer;
frameControl.disableDefaultResponse = false;
// ZCL header
ZigbeeClusterLibrary::Header header;
header.frameControl = frameControl;
header.command = ZigbeeClusterOnOff::CommandOff;
header.transactionSequenceNumber = m_transactionSequenceNumber++;
// there is no ZCL payload
// Put them together
ZigbeeClusterLibrary::Frame frame;
frame.header = header;
request.setTxOptions(Zigbee::ZigbeeTxOptions(Zigbee::ZigbeeTxOptionAckTransmission));
request.setAsdu(ZigbeeClusterLibrary::buildFrame(frame));
ZigbeeClusterReply *zclReply = createClusterReply(request, frame);
ZigbeeNetworkReply *networkReply = m_network->sendRequest(request);
connect(networkReply, &ZigbeeNetworkReply::finished, this, [this, networkReply, zclReply](){
if (!verifyNetworkError(zclReply, networkReply)) {
qCWarning(dcZigbeeClusterLibrary()) << "Failed to send request"
<< m_node << networkReply->error()
<< networkReply->zigbeeApsStatus();
finishZclReply(zclReply);
return;
}
// The request was successfully sent to the device
// Now check if the expected indication response received already
if (zclReply->isComplete()) {
finishZclReply(zclReply);
return;
}
});
return zclReply;
}
ZigbeeClusterReply *ZigbeeClusterOnOff::commandOn()
{
ZigbeeNetworkRequest request = createGeneralRequest();
// Build ZCL frame
// Note: for basic commands the frame control files has to be zero accoring to spec ZCL 2.4.1.1
ZigbeeClusterLibrary::FrameControl frameControl;
frameControl.frameType = ZigbeeClusterLibrary::FrameTypeClusterSpecific;
frameControl.manufacturerSpecific = false;
frameControl.direction = ZigbeeClusterLibrary::DirectionClientToServer;
frameControl.disableDefaultResponse = false;
// ZCL header
ZigbeeClusterLibrary::Header header;
header.frameControl = frameControl;
header.command = ZigbeeClusterOnOff::CommandOn;
header.transactionSequenceNumber = m_transactionSequenceNumber++;
// There is no ZCL payload
// Put them together
ZigbeeClusterLibrary::Frame frame;
frame.header = header;
request.setTxOptions(Zigbee::ZigbeeTxOptions(Zigbee::ZigbeeTxOptionAckTransmission));
request.setAsdu(ZigbeeClusterLibrary::buildFrame(frame));
ZigbeeClusterReply *zclReply = createClusterReply(request, frame);
ZigbeeNetworkReply *networkReply = m_network->sendRequest(request);
connect(networkReply, &ZigbeeNetworkReply::finished, this, [this, networkReply, zclReply](){
if (!verifyNetworkError(zclReply, networkReply)) {
qCWarning(dcZigbeeClusterLibrary()) << "Failed to send request"
<< m_node << networkReply->error()
<< networkReply->zigbeeApsStatus();
finishZclReply(zclReply);
return;
}
// The request was successfully sent to the device
// Now check if the expected indication response received already
if (zclReply->isComplete()) {
finishZclReply(zclReply);
return;
}
});
return zclReply;
}
void ZigbeeClusterOnOff::setAttribute(const ZigbeeClusterAttribute &attribute)
{
if (hasAttribute(attribute.id())) {
qCDebug(dcZigbeeCluster()) << "Update attribute" << this << static_cast<Attribute>(attribute.id()) << attribute.dataType();
m_attributes[attribute.id()] = attribute;
emit attributeChanged(attribute);
} else {
qCDebug(dcZigbeeCluster()) << "Add attribute" << this << static_cast<Attribute>(attribute.id()) << attribute.dataType();
m_attributes.insert(attribute.id(), attribute);
emit attributeChanged(attribute);
}
}
void ZigbeeClusterOnOff::processDataIndication(ZigbeeClusterLibrary::Frame frame)
{
// Increase the tsn for continuouse id increasing on both sides
m_transactionSequenceNumber = frame.header.transactionSequenceNumber;
qCWarning(dcZigbeeCluster()) << "Unhandled ZCL indication in" << m_node << m_endpoint << this << frame;
}

View File

@ -0,0 +1,87 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea-zigbee.
* This project including source code and documentation is protected by copyright law, and
* remains the property of nymea GmbH. All rights, including reproduction, publication,
* editing and translation, are reserved. The use of this project is subject to the terms of a
* license agreement to be concluded with nymea GmbH in accordance with the terms
* of use of nymea GmbH, available under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation; version 3.
* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this project.
* If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under contact@nymea.io
* or see our FAQ/Licensing Information on https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef ZIGBEECLUSTERONOFF_H
#define ZIGBEECLUSTERONOFF_H
#include <QObject>
#include "zcl/zigbeecluster.h"
#include "zcl/zigbeeclusterreply.h"
class ZigbeeNode;
class ZigbeeNetwork;
class ZigbeeNodeEndpoint;
class ZigbeeNetworkReply;
class ZigbeeClusterOnOff : public ZigbeeCluster
{
Q_OBJECT
friend class ZigbeeNode;
friend class ZigbeeNetwork;
public:
enum Attribute {
AttributeOnOff = 0x0000,
AttributeGlobalSceneControl = 0x4000,
AttributeOnTime = 0x4001,
AttributeOffTime = 0x4002
};
Q_ENUM(Attribute)
enum Command {
CommandOff = 0x00,
CommandOn = 0x01,
CommandToggle = 0x02,
CommandOffWithEffect = 0x40,
CommandOnWithRecallGlobalScene = 0x41,
CommandOnWithTimedOff = 0x42
};
Q_ENUM(Command)
explicit ZigbeeClusterOnOff(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent = nullptr);
ZigbeeClusterReply *commandOff();
ZigbeeClusterReply *commandOn();
// ZigbeeClusterReply *commandToggle();
// ZigbeeClusterReply *commandOffWithEffect();
// ZigbeeClusterReply *commandOnWithRecallGlobalScene();
// ZigbeeClusterReply *commandOnWithTimedOff();
private:
void setAttribute(const ZigbeeClusterAttribute &attribute) override;
protected:
void processDataIndication(ZigbeeClusterLibrary::Frame frame) override;
signals:
};
#endif // ZIGBEECLUSTERONOFF_H

View File

@ -82,11 +82,11 @@ ZigbeeClusterAttribute ZigbeeCluster::attribute(quint16 attributeId)
void ZigbeeCluster::setAttribute(const ZigbeeClusterAttribute &attribute)
{
if (hasAttribute(attribute.id())) {
qCDebug(dcZigbeeCluster()) << this << "update attribute" << attribute;
qCDebug(dcZigbeeCluster()) << "Update attribute" << this << attribute;
m_attributes[attribute.id()] = attribute;
emit attributeChanged(attribute);
} else {
qCDebug(dcZigbeeCluster()) << this << "add attribute" << attribute;
qCDebug(dcZigbeeCluster()) << "Add attribute" << this << attribute;
m_attributes.insert(attribute.id(), attribute);
emit attributeChanged(attribute);
}
@ -94,7 +94,7 @@ void ZigbeeCluster::setAttribute(const ZigbeeClusterAttribute &attribute)
ZigbeeClusterReply *ZigbeeCluster::readAttributes(QList<quint16> attributes)
{
qCDebug(dcZigbeeClusterLibrary()) << "Read attributes from" << m_node << m_endpoint << this << attributes;
qCDebug(dcZigbeeCluster()) << "Read attributes from" << m_node << m_endpoint << this << attributes;
// Build the request
ZigbeeNetworkRequest request = createGeneralRequest();
@ -139,7 +139,7 @@ ZigbeeClusterReply *ZigbeeCluster::readAttributes(QList<quint16> attributes)
connect(networkReply, &ZigbeeNetworkReply::finished, this, [this, networkReply, zclReply](){
if (!verifyNetworkError(zclReply, networkReply)) {
qCWarning(dcZigbeeClusterLibrary()) << "Failed to send request"
<< m_node << networkReply->error()
<< m_node << m_endpoint << this << networkReply->error()
<< networkReply->zigbeeApsStatus();
finishZclReply(zclReply);
return;
@ -211,14 +211,23 @@ bool ZigbeeCluster::verifyNetworkError(ZigbeeClusterReply *zclReply, ZigbeeNetwo
void ZigbeeCluster::finishZclReply(ZigbeeClusterReply *zclReply)
{
m_pendingReplies.remove(zclReply->transactionSequenceNumber());
qCDebug(dcZigbeeCluster()) << "ZigbeeClusterReply finished" << zclReply->request() << zclReply->requestFrame() << zclReply->responseFrame();
zclReply->finished();
}
void ZigbeeCluster::processDataIndication(ZigbeeClusterLibrary::Frame frame)
{
// Increase the tsn for continuouse id increasing on both sides
m_transactionSequenceNumber = frame.header.transactionSequenceNumber;
qCWarning(dcZigbeeCluster()) << "Unhandled ZCL indication in" << m_node << m_endpoint << this << frame;
}
void ZigbeeCluster::processApsDataIndication(QByteArray payload)
{
ZigbeeClusterLibrary::Frame frame = ZigbeeClusterLibrary::parseFrameData(payload);
qCDebug(dcZigbeeClusterLibrary()) << this << "received data indication" << frame;
qCDebug(dcZigbeeCluster()) << "Received data indication" << this << frame;
// Check if this indication is for a pending reply
if (m_pendingReplies.contains(frame.header.transactionSequenceNumber)) {
ZigbeeClusterReply *reply = m_pendingReplies.value(frame.header.transactionSequenceNumber);
reply->m_responseData = payload;
@ -231,8 +240,8 @@ void ZigbeeCluster::processApsDataIndication(QByteArray payload)
return;
}
// FIXME: increase transaction sequence number
qCWarning(dcZigbeeNode()) << m_node << m_endpoint << this << "Unhandled ZCL indication" << ZigbeeUtils::convertByteArrayToHexString(payload);
// Not for a reply, process the indication
processDataIndication(frame);
}
QDebug operator<<(QDebug debug, ZigbeeCluster *cluster)
@ -252,7 +261,7 @@ QDebug operator<<(QDebug debug, const ZigbeeClusterAttributeReport &attributeRep
<< attributeReport.attributeId << ", "
<< attributeReport.attributeStatus << ", "
<< attributeReport.dataType << ", "
<< attributeReport.data << ", "
<< ZigbeeUtils::convertByteArrayToHexString(attributeReport.data)
<< ")";
return debug.space();

View File

@ -89,23 +89,6 @@ public:
// };
// Q_ENUM(PowerConfigurationAttribute)
// // On Off Cluster 0x0006
// enum OnOffClusterAttribute {
// OnOffClusterAttributeOnOff = 0x0000,
// OnOffClusterAttributeGlobalSceneControl = 0x4000,
// OnOffClusterAttributeOnTime = 0x4001,
// OnOffClusterAttributeOffWaitTime = 0x4002
// };
// Q_ENUM(OnOffClusterAttribute)
// enum OnOffClusterCommand {
// OnOffClusterCommandOff = 0x00,
// OnOffClusterCommandOn = 0x01,
// OnOffClusterCommandToggle = 0x02
// };
// Q_ENUM(OnOffClusterCommand)
// // Level cluster 0x0008
@ -195,6 +178,8 @@ protected:
bool verifyNetworkError(ZigbeeClusterReply *zclReply, ZigbeeNetworkReply *networkReply);
void finishZclReply(ZigbeeClusterReply *zclReply);
virtual void processDataIndication(ZigbeeClusterLibrary::Frame frame);
private:
virtual void setAttribute(const ZigbeeClusterAttribute &attribute);
@ -204,7 +189,6 @@ signals:
public slots:
void processApsDataIndication(QByteArray payload);
};
QDebug operator<<(QDebug debug, ZigbeeCluster *cluster);

View File

@ -33,10 +33,9 @@ ZigbeeClusterAttribute::ZigbeeClusterAttribute()
}
ZigbeeClusterAttribute::ZigbeeClusterAttribute(quint16 id, Zigbee::DataType dataType, QByteArray data):
ZigbeeClusterAttribute::ZigbeeClusterAttribute(quint16 id, const ZigbeeDataType &dataType):
m_id(id),
m_dataType(dataType),
m_data(data)
m_dataType(dataType)
{
}
@ -45,7 +44,6 @@ ZigbeeClusterAttribute::ZigbeeClusterAttribute(const ZigbeeClusterAttribute &oth
{
m_id = other.id();
m_dataType = other.dataType();
m_data = other.data();
}
quint16 ZigbeeClusterAttribute::id() const
@ -53,29 +51,21 @@ quint16 ZigbeeClusterAttribute::id() const
return m_id;
}
Zigbee::DataType ZigbeeClusterAttribute::dataType() const
ZigbeeDataType ZigbeeClusterAttribute::dataType() const
{
return m_dataType;
}
QByteArray ZigbeeClusterAttribute::data() const
{
return m_data;
}
ZigbeeClusterAttribute &ZigbeeClusterAttribute::operator=(const ZigbeeClusterAttribute &other)
{
m_id = other.id();
m_dataType = other.dataType();
m_data = other.data();
return *this;
}
bool ZigbeeClusterAttribute::operator==(const ZigbeeClusterAttribute &other) const
{
return m_id == other.id() &&
m_dataType == other.dataType() &&
m_data == other.data();
return m_id == other.id() && m_dataType == other.dataType();
}
bool ZigbeeClusterAttribute::operator!=(const ZigbeeClusterAttribute &other) const
@ -85,16 +75,14 @@ bool ZigbeeClusterAttribute::operator!=(const ZigbeeClusterAttribute &other) con
bool ZigbeeClusterAttribute::isValid() const
{
return m_id != 0 ||
m_dataType != Zigbee::NoData ||
!m_data.isNull();
return m_id != 0xffff && m_dataType.isValid();
}
QDebug operator<<(QDebug debug, const ZigbeeClusterAttribute &attribute)
{
debug.nospace().noquote() << "ZigbeeClusterAttribute("
<< ZigbeeUtils::convertUint16ToHexString(attribute.id()) << ", "
<< attribute.dataType() << ", "
<< ZigbeeUtils::convertByteArrayToHexString(attribute.data()) << ")";
<< attribute.dataType()
<< ")";
return debug.space();
}

View File

@ -31,17 +31,17 @@
#include <QDebug>
#include "zigbee.h"
#include "zigbeedatatype.h"
class ZigbeeClusterAttribute
{
public:
ZigbeeClusterAttribute();
ZigbeeClusterAttribute(quint16 id, Zigbee::DataType dataType, QByteArray data);
ZigbeeClusterAttribute(quint16 id, const ZigbeeDataType &dataType);
ZigbeeClusterAttribute(const ZigbeeClusterAttribute &other);
quint16 id() const;
Zigbee::DataType dataType() const;
QByteArray data() const;
ZigbeeDataType dataType() const;
ZigbeeClusterAttribute &operator=(const ZigbeeClusterAttribute &other);
bool operator==(const ZigbeeClusterAttribute &other) const;
@ -50,9 +50,8 @@ public:
bool isValid() const;
private:
quint16 m_id = 0;
Zigbee::DataType m_dataType = Zigbee::NoData;
QByteArray m_data;
quint16 m_id = 0xffff;
ZigbeeDataType m_dataType;
};
QDebug operator<<(QDebug debug, const ZigbeeClusterAttribute &attribute);

View File

@ -104,13 +104,11 @@ QList<ZigbeeClusterLibrary::ReadAttributeStatusRecord> ZigbeeClusterLibrary::par
QDataStream stream(payload);
stream.setByteOrder(QDataStream::LittleEndian);
quint16 attributeId; quint8 statusInt; quint8 dataTypeInt; quint16 numberOfElenemts; quint8 elementType;
QByteArray data;
quint16 attributeId; quint8 statusInt; quint8 dataTypeInt;
while (!stream.atEnd()) {
// Reset variables
attributeId = 0; statusInt = 0; dataTypeInt = 0; numberOfElenemts = 0; elementType = 0;
data.clear();
attributeId = 0; statusInt = 0; dataTypeInt = 0;
// Read attribute id and status
stream >> attributeId >> statusInt;
@ -126,72 +124,15 @@ QList<ZigbeeClusterLibrary::ReadAttributeStatusRecord> ZigbeeClusterLibrary::par
stream >> dataTypeInt;
Zigbee::DataType dataType = static_cast<Zigbee::DataType>(dataTypeInt);
qCDebug(dcZigbeeClusterLibrary()) << "Parse:" << dataType;
// Parse data depending on the type
if (dataType == Zigbee::Array || dataType == Zigbee::Set || dataType == Zigbee::Bag) {
stream >> elementType >> numberOfElenemts;
qCDebug(dcZigbeeClusterLibrary()) << "Parse (array, set, bag): Element type" << ZigbeeUtils::convertByteToHexString(elementType) << "Number of elements:" << numberOfElenemts;
if (numberOfElenemts == 0xffff) {
qCWarning(dcZigbeeCluster()) << "ZigbeeStatusRecord contains invalid elements" << ZigbeeUtils::convertUint16ToHexString(attributeId) << dataType;
continue;
} else {
for (int i = 0; i < numberOfElenemts; i++) {
quint8 element = 0;
stream >> element;
data.append(element);
}
}
} else if (dataType == Zigbee::Structure) {
stream >> numberOfElenemts;
qCDebug(dcZigbeeClusterLibrary()) << "Parse (structure)" << "Number of elements:" << numberOfElenemts;
if (numberOfElenemts == 0xffff) {
qCWarning(dcZigbeeCluster()) << "ZigbeeStatusRecord contains invalid elements" << ZigbeeUtils::convertUint16ToHexString(attributeId) << dataType;
continue;
} else {
stream >> elementType;
qCDebug(dcZigbeeClusterLibrary()) << "Parse (structure)" << "Element type:" << ZigbeeUtils::convertByteToHexString(elementType);
for (int i = 0; i < numberOfElenemts; i++) {
quint8 element = 0;
stream >> element;
//qCDebug(dcZigbeeClusterLibrary()) << "Parse (structure)" << "Element value:" << ZigbeeUtils::convertByteToHexString(element);
data.append(element);
}
}
} else if (dataType == Zigbee::OctetString || dataType == Zigbee::CharString) {
quint8 length = 0;
stream >> length;
qCDebug(dcZigbeeClusterLibrary()) << "Parse (octet string, character string)" << "Length:" << length;
for (int i = 0; i < length; i++) {
quint8 element = 0;
stream >> element;
data.append(element);
}
} else if (dataType == Zigbee::LongOctetString || dataType == Zigbee::LongCharString) {
quint16 length = 0;
stream >> length;
qCDebug(dcZigbeeClusterLibrary()) << "Parse (long octet string, long character string)" << "Length:" << length;
for (int i = 0; i < length; i++) {
quint8 element = 0;
stream >> element;
data.append(element);
}
} else {
// Normal data type
int length = ZigbeeDataType::typeLength(dataType);
qCDebug(dcZigbeeClusterLibrary()) << "Parse (normal data type)" << "Number of elements:" << length;
for (int i = 0; i < length; i++) {
quint8 element = 0;
stream >> element;
data.append(element);
}
}
qCDebug(dcZigbeeClusterLibrary()) << "Parse data type:" << dataType;
ZigbeeDataType type = readDataType(&stream, dataType);
if (!type.isValid())
continue;
ReadAttributeStatusRecord attributeRecord;
attributeRecord.attributeId = attributeId;
attributeRecord.attributeStatus = status;
attributeRecord.dataType = dataType;
attributeRecord.data = data;
attributeRecord.dataType = type;
qCDebug(dcZigbeeClusterLibrary()) << attributeRecord;
attributeStatusRecords.append(attributeRecord);
}
@ -200,6 +141,82 @@ QList<ZigbeeClusterLibrary::ReadAttributeStatusRecord> ZigbeeClusterLibrary::par
return attributeStatusRecords;
}
ZigbeeDataType ZigbeeClusterLibrary::readDataType(QDataStream *stream, Zigbee::DataType dataType)
{
QByteArray data; quint16 numberOfElenemts = 0; quint8 elementType = 0;
QDataStream dataStream(&data, QIODevice::WriteOnly);
dataStream.setByteOrder(QDataStream::LittleEndian);
// Parse data depending on the type
if (dataType == Zigbee::Array || dataType == Zigbee::Set || dataType == Zigbee::Bag) {
*stream >> elementType >> numberOfElenemts;
dataStream << elementType << numberOfElenemts;
qCDebug(dcZigbeeClusterLibrary()) << "Parse (array, set, bag): Element type" << ZigbeeUtils::convertByteToHexString(elementType) << "Number of elements:" << numberOfElenemts;
if (numberOfElenemts == 0xffff) {
qCWarning(dcZigbeeClusterLibrary()) << "ZigbeeStatusRecord contains invalid data elements" << dataType;
return ZigbeeDataType(dataType);
} else {
for (int i = 0; i < numberOfElenemts; i++) {
quint8 element = 0;
*stream >> element;
dataStream << element;
}
}
} else if (dataType == Zigbee::Structure) {
*stream >> numberOfElenemts;
dataStream << numberOfElenemts;
qCDebug(dcZigbeeClusterLibrary()) << "Parse (structure)" << "Number of elements:" << numberOfElenemts;
if (numberOfElenemts == 0xffff) {
qCWarning(dcZigbeeClusterLibrary()) << "ZigbeeStatusRecord contains invalid data elements" << dataType;
return ZigbeeDataType(dataType);
} else {
*stream >> elementType;
qCDebug(dcZigbeeClusterLibrary()) << "Parse (structure)" << "Element type:" << ZigbeeUtils::convertByteToHexString(elementType);
for (int i = 0; i < numberOfElenemts; i++) {
quint8 element = 0;
*stream >> element;
//qCDebug(dcZigbeeClusterLibrary()) << "Parse (structure)" << "Element value:" << ZigbeeUtils::convertByteToHexString(element);
dataStream << element;
}
}
} else if (dataType == Zigbee::OctetString || dataType == Zigbee::CharString) {
quint8 length = 0;
*stream >> length;
dataStream << length;
qCDebug(dcZigbeeClusterLibrary()) << "Parse (octet string, character string)" << "Length:" << length;
for (int i = 0; i < length; i++) {
quint8 element = 0;
*stream >> element;
dataStream << element;
}
} else if (dataType == Zigbee::LongOctetString || dataType == Zigbee::LongCharString) {
quint16 length = 0;
*stream >> length;
dataStream << length;
qCDebug(dcZigbeeClusterLibrary()) << "Parse (long octet string, long character string)" << "Length:" << length;
for (int i = 0; i < length; i++) {
quint8 element = 0;
*stream >> element;
data.append(element);
dataStream << element;
}
} else {
// Normal data type
int length = ZigbeeDataType::typeLength(dataType);
qCDebug(dcZigbeeClusterLibrary()) << "Parse (normal data type)" << "Number of elements:" << length;
for (int i = 0; i < length; i++) {
quint8 element = 0;
*stream >> element;
dataStream << element;
}
}
qCDebug(dcZigbeeClusterLibrary()) << "Parsed data:" << ZigbeeUtils::convertByteArrayToHexString(data);
return ZigbeeDataType(dataType, data);
}
ZigbeeClusterLibrary::Frame ZigbeeClusterLibrary::parseFrameData(const QByteArray &frameData)
{
QDataStream stream(frameData);
@ -281,12 +298,11 @@ QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::Frame &frame)
QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::ReadAttributeStatusRecord &attributeStatusRecord)
{
debug.nospace().noquote() << "ReadAttributeStatusRecord("
<< ZigbeeUtils::convertUint16ToHexString(attributeStatusRecord.attributeId) << ", "
<< attributeStatusRecord.attributeStatus << ", "
<< attributeStatusRecord.dataType << ", "
<< attributeStatusRecord.data
<< ")";
debug.nospace() << "ReadAttributeStatusRecord("
<< ZigbeeUtils::convertUint16ToHexString(attributeStatusRecord.attributeId) << ", "
<< attributeStatusRecord.attributeStatus << ", "
<< attributeStatusRecord.dataType
<< ")";
return debug.space();
}

View File

@ -32,6 +32,7 @@
#include <QDebug>
#include "zigbee.h"
#include "zigbeedatatype.h"
class ZigbeeClusterLibrary
{
@ -101,7 +102,7 @@ public:
FrameControl frameControl;
quint16 manufacturerCode = 0;
quint8 transactionSequenceNumber = 0;
Command command;
quint8 command;
} ZclHeader;
typedef struct Frame {
@ -114,8 +115,7 @@ public:
typedef struct ReadAttributeStatusRecord {
quint16 attributeId;
Zigbee::ZigbeeStatus attributeStatus;
Zigbee::DataType dataType;
QByteArray data;
ZigbeeDataType dataType;
} ReadAttributeStatusRecord;
@ -128,7 +128,7 @@ public:
static QList<ReadAttributeStatusRecord> parseAttributeStatusRecords(const QByteArray &payload);
//static QByteArray readAttributeData(const QDataStream &stream, Zigbee::DataType dataType);
static ZigbeeDataType readDataType(QDataStream *stream, Zigbee::DataType dataType);
static Frame parseFrameData(const QByteArray &frameData);
static QByteArray buildFrame(const Frame &frame);

View File

@ -1,3 +1,30 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea-zigbee.
* This project including source code and documentation is protected by copyright law, and
* remains the property of nymea GmbH. All rights, including reproduction, publication,
* editing and translation, are reserved. The use of this project is subject to the terms of a
* license agreement to be concluded with nymea GmbH in accordance with the terms
* of use of nymea GmbH, available under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation; version 3.
* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this project.
* If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under contact@nymea.io
* or see our FAQ/Licensing Information on https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "zigbeeclusterreply.h"
ZigbeeClusterReply::Error ZigbeeClusterReply::error() const

View File

@ -1,3 +1,30 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea-zigbee.
* This project including source code and documentation is protected by copyright law, and
* remains the property of nymea GmbH. All rights, including reproduction, publication,
* editing and translation, are reserved. The use of this project is subject to the terms of a
* license agreement to be concluded with nymea GmbH in accordance with the terms
* of use of nymea GmbH, available under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation; version 3.
* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this project.
* If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under contact@nymea.io
* or see our FAQ/Licensing Information on https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef ZIGBEECLUSTERREPLY_H
#define ZIGBEECLUSTERREPLY_H

View File

@ -356,5 +356,5 @@ void ZigbeeDeviceObject::processApsDataIndication(quint8 destinationEndpoint, qu
return;
}
qCWarning(dcZigbeeDeviceObject()) << m_node << "unhandled ZDO indication";
qCWarning(dcZigbeeDeviceObject()) << "Unhandled ZDO indication" << m_node << asdu;
}

View File

@ -1,16 +1,46 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea-zigbee.
* This project including source code and documentation is protected by copyright law, and
* remains the property of nymea GmbH. All rights, including reproduction, publication,
* editing and translation, are reserved. The use of this project is subject to the terms of a
* license agreement to be concluded with nymea GmbH in accordance with the terms
* of use of nymea GmbH, available under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation; version 3.
* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this project.
* If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under contact@nymea.io
* or see our FAQ/Licensing Information on https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "zigbeedatatype.h"
#include "zigbeeutils.h"
#include <QtGlobal>
#include <QDataStream>
ZigbeeDataType::ZigbeeDataType()
{
setDataType(Zigbee::NoData);
}
ZigbeeDataType::ZigbeeDataType(Zigbee::DataType dataType, const QByteArray &data):
m_data(data)
{
setDataType(dataType);
// TODO: verify data length and consistency
@ -414,6 +444,12 @@ int ZigbeeDataType::dataLength() const
return typeLength(m_dataType);
}
bool ZigbeeDataType::isValid() const
{
// FIXME: implement validate data depending on the type
return m_dataType != Zigbee::NoData && !m_data.isNull();
}
int ZigbeeDataType::typeLength(Zigbee::DataType dataType)
{
int length = 0;
@ -597,6 +633,23 @@ int ZigbeeDataType::typeLength(Zigbee::DataType dataType)
return length;
}
ZigbeeDataType &ZigbeeDataType::operator=(const ZigbeeDataType &other)
{
setDataType(other.dataType());
m_data = other.data();
return *this;
}
bool ZigbeeDataType::operator==(const ZigbeeDataType &other) const
{
return m_dataType == other.dataType() && m_data == other.data();
}
bool ZigbeeDataType::operator!=(const ZigbeeDataType &other) const
{
return !operator==(other);
}
void ZigbeeDataType::setDataType(Zigbee::DataType dataType)
{
m_dataType = dataType;
@ -885,6 +938,7 @@ void ZigbeeDataType::setDataType(Zigbee::DataType dataType)
QDebug operator<<(QDebug debug, const ZigbeeDataType &dataType)
{
// FIXME: print data depending on the datatype
debug.nospace() << "ZigbeeDataType(" << dataType.name();
debug.nospace() << ", " << ZigbeeUtils::convertByteArrayToHexString(dataType.data());
debug.nospace() << ")";

View File

@ -1,3 +1,30 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea-zigbee.
* This project including source code and documentation is protected by copyright law, and
* remains the property of nymea GmbH. All rights, including reproduction, publication,
* editing and translation, are reserved. The use of this project is subject to the terms of a
* license agreement to be concluded with nymea GmbH in accordance with the terms
* of use of nymea GmbH, available under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation; version 3.
* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this project.
* If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under contact@nymea.io
* or see our FAQ/Licensing Information on https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef ZIGBEEDATATYPE_H
#define ZIGBEEDATATYPE_H
@ -7,6 +34,7 @@
class ZigbeeDataType
{
public:
ZigbeeDataType();
ZigbeeDataType(Zigbee::DataType dataType, const QByteArray &data = QByteArray());
ZigbeeDataType(quint8 value, Zigbee::DataType dataType = Zigbee::Uint8);
@ -32,8 +60,14 @@ public:
QByteArray data() const;
int dataLength() const;
bool isValid() const;
static int typeLength(Zigbee::DataType dataType);
ZigbeeDataType &operator=(const ZigbeeDataType &other);
bool operator==(const ZigbeeDataType &other) const;
bool operator!=(const ZigbeeDataType &other) const;
private:
Zigbee::DataType m_dataType = Zigbee::NoData;
QByteArray m_data;

View File

@ -671,9 +671,16 @@ void ZigbeeNode::initBasicCluster()
qCDebug(dcZigbeeNode()) << "Reading basic cluster attributes finished successfully";
QList<ZigbeeClusterLibrary::ReadAttributeStatusRecord> attributeStatusRecords = ZigbeeClusterLibrary::parseAttributeStatusRecords(reply->responseFrame().payload);
if (!attributeStatusRecords.isEmpty()) {
qCDebug(dcZigbeeNode()) << attributeStatusRecords.first();
basicCluster->setAttribute(ZigbeeClusterAttribute(static_cast<quint16>(attributeId), attributeStatusRecords.first().dataType, attributeStatusRecords.first().data));
m_endpoints.first()->m_manufacturerName = QString::fromUtf8(attributeStatusRecords.first().data);
ZigbeeClusterLibrary::ReadAttributeStatusRecord attributeStatusRecord = attributeStatusRecords.first();
qCDebug(dcZigbeeNode()) << attributeStatusRecord;
basicCluster->setAttribute(ZigbeeClusterAttribute(static_cast<quint16>(attributeId), attributeStatusRecord.dataType));
bool valueOk = false;
QString manufacturerName = attributeStatusRecord.dataType.toString(&valueOk);
if (valueOk) {
m_endpoints.first()->m_manufacturerName = manufacturerName;
} else {
qCWarning(dcZigbeeNode()) << "Could not convert manufacturer name attribute data to string" << attributeStatusRecord.dataType;
}
}
}
@ -687,13 +694,19 @@ void ZigbeeNode::initBasicCluster()
qCDebug(dcZigbeeNode()) << "Reading basic cluster attributes finished successfully";
QList<ZigbeeClusterLibrary::ReadAttributeStatusRecord> attributeStatusRecords = ZigbeeClusterLibrary::parseAttributeStatusRecords(reply->responseFrame().payload);
if (!attributeStatusRecords.isEmpty()) {
qCDebug(dcZigbeeNode()) << attributeStatusRecords.first();
basicCluster->setAttribute(ZigbeeClusterAttribute(static_cast<quint16>(attributeId), attributeStatusRecords.first().dataType, attributeStatusRecords.first().data));
m_endpoints.first()->m_modelIdentifier = QString::fromUtf8(attributeStatusRecords.first().data);
ZigbeeClusterLibrary::ReadAttributeStatusRecord attributeStatusRecord = attributeStatusRecords.first();
qCDebug(dcZigbeeNode()) << attributeStatusRecord;
basicCluster->setAttribute(ZigbeeClusterAttribute(static_cast<quint16>(attributeId), attributeStatusRecord.dataType));
bool valueOk = false;
QString modelIdentifier = attributeStatusRecord.dataType.toString(&valueOk);
if (valueOk) {
m_endpoints.first()->m_modelIdentifier = modelIdentifier;
} else {
qCWarning(dcZigbeeNode()) << "Could not convert model identifier attribute data to string" << attributeStatusRecord.dataType;
}
}
}
ZigbeeClusterBasic::Attribute attributeId = ZigbeeClusterBasic::AttributeSwBuildId;
qCDebug(dcZigbeeNode()) << "Reading attribute" << attributeId;
ZigbeeClusterReply *reply = basicCluster->readAttributes({static_cast<quint16>(attributeId)});
@ -704,9 +717,16 @@ void ZigbeeNode::initBasicCluster()
qCDebug(dcZigbeeNode()) << "Reading basic cluster attributes finished successfully";
QList<ZigbeeClusterLibrary::ReadAttributeStatusRecord> attributeStatusRecords = ZigbeeClusterLibrary::parseAttributeStatusRecords(reply->responseFrame().payload);
if (!attributeStatusRecords.isEmpty()) {
qCDebug(dcZigbeeNode()) << attributeStatusRecords.first();
basicCluster->setAttribute(ZigbeeClusterAttribute(static_cast<quint16>(attributeId), attributeStatusRecords.first().dataType, attributeStatusRecords.first().data));
m_endpoints.first()->m_softwareBuildId = QString::fromUtf8(attributeStatusRecords.first().data);
ZigbeeClusterLibrary::ReadAttributeStatusRecord attributeStatusRecord = attributeStatusRecords.first();
qCDebug(dcZigbeeNode()) << attributeStatusRecord;
basicCluster->setAttribute(ZigbeeClusterAttribute(static_cast<quint16>(attributeId), attributeStatusRecord.dataType));
bool valueOk = false;
QString softwareBuildId = attributeStatusRecord.dataType.toString(&valueOk);
if (valueOk) {
m_endpoints.first()->m_softwareBuildId = softwareBuildId;
} else {
qCWarning(dcZigbeeNode()) << "Could not convert software build id attribute data to string" << attributeStatusRecord.dataType;
}
}
}
@ -715,7 +735,6 @@ void ZigbeeNode::initBasicCluster()
});
});
});
}
void ZigbeeNode::onClusterAttributeChanged(const ZigbeeClusterAttribute &attribute)

View File

@ -154,6 +154,9 @@ ZigbeeCluster *ZigbeeNodeEndpoint::createCluster(Zigbee::ClusterId clusterId, Zi
case Zigbee::ClusterIdBasic:
return new ZigbeeClusterBasic(m_network, m_node, this, direction, this);
break;
case Zigbee::ClusterIdOnOff:
return new ZigbeeClusterOnOff(m_network, m_node, this, direction, this);
break;
default:
return new ZigbeeCluster(m_network, m_node, this, clusterId, direction, this);
}

View File

@ -36,6 +36,7 @@
#include "zcl/zigbeecluster.h"
#include "zcl/general/zigbeeclusterbasic.h"
#include "zcl/general/zigbeeclusteronoff.h"
class ZigbeeNode;
class ZigbeeNetwork;