Implement basic cluster comands and commuication

This commit is contained in:
Simon Stürz 2020-05-25 22:25:49 +02:00
parent 029ca76bae
commit 4e897686bb
53 changed files with 1290 additions and 553 deletions

View File

@ -327,42 +327,70 @@ void ZigbeeNetworkDeconz::handleZigbeeDeviceProfileIndication(const DeconzApsDat
} }
node->deviceObject()->processApsDataIndication(indication.destinationEndpoint, indication.sourceEndpoint, indication.clusterId, indication.asdu, indication.lqi, indication.rssi); node->deviceObject()->processApsDataIndication(indication.destinationEndpoint, indication.sourceEndpoint, indication.clusterId, indication.asdu, indication.lqi, indication.rssi);
}
// foreach (ZigbeeNetworkReply *reply, m_pendingReplies.values()) { void ZigbeeNetworkDeconz::handleZigbeeLightLinkIndication(const DeconzApsDataIndication &indication)
// // Check if this is a reply if for a ZDO request {
// if (reply->request().profileId() == Zigbee::ZigbeeProfileDevice) { ZigbeeClusterLibrary::Frame frame = ZigbeeClusterLibrary::parseFrameData(indication.asdu);
// // We have a reply which is waiting for a ZDO response, lets check if they match //qCDebug(dcZigbeeNetwork()) << "ZCL ZLL" << indication << frame;
// // Check if this is the response to the sent request command
// if (indication.clusterId == (reply->request().clusterId() | 0x8000)) {
// // Now check if the id matches, if so set the ADPU as response to the reply, otherwise this is not the message for this reply
// ZigbeeDeviceProfile::Adpu deviceAdpu = ZigbeeDeviceProfile::parseAdpu(indication.asdu);
// if (deviceAdpu.transactionSequenceNumber == reply->request().requestId()) {
// // We found the correct reply
// // Set the response payload of the // Get the node
// qCDebug(dcZigbeeNetwork()) << "Indication response for ZDO request received" ZigbeeNode *node = getZigbeeNode(indication.sourceShortAddress);
// << static_cast<ZigbeeDeviceProfile::ZdoCommand>(reply->request().clusterId()) if (!node) {
// << "-->" qCWarning(dcZigbeeNetwork()) << "Received a ZCL indication for an unrecognized node. There is no such node in the system. Ignoring indication" << indication;
// << static_cast<ZigbeeDeviceProfile::ZdoCommand>(indication.clusterId) return;
// << deviceAdpu; }
// setReplyResponseData(reply, indication.asdu);
// return;
// }
// }
// }
// }
//qCWarning(dcZigbeeNetwork()) << "FIXME: Unhandled ZDO indication" << indication; // Get the endpoint
ZigbeeNodeEndpoint *endpoint = node->getEndpoint(indication.sourceEndpoint);
if (!endpoint) {
qCWarning(dcZigbeeNetwork()) << "Received a ZCL indication for an unrecognized endpoint. There is no such endpoint on" << node << ". Ignoring indication" << indication;
return;
}
// Get the cluster
ZigbeeCluster *cluster = endpoint->getOutputCluster(static_cast<Zigbee::ClusterId>(indication.clusterId));
if (!cluster) {
cluster = endpoint->getInputCluster(static_cast<Zigbee::ClusterId>(indication.clusterId));
if (!cluster) {
qCWarning(dcZigbeeNetwork()) << "Received a ZCL indication for an unrecognized cluster. There is no such cluster on" << node << endpoint << "in the system. Ignoring indication" << indication;
return;
}
}
cluster->processApsDataIndication(indication.asdu);
} }
void ZigbeeNetworkDeconz::handleZigbeeHomeAutomationIndication(const DeconzApsDataIndication &indication) void ZigbeeNetworkDeconz::handleZigbeeHomeAutomationIndication(const DeconzApsDataIndication &indication)
{ {
ZigbeeClusterLibrary::Frame frame = ZigbeeClusterLibrary::parseFrameData(static_cast<Zigbee::ClusterId>(indication.clusterId), indication.asdu); ZigbeeClusterLibrary::Frame frame = ZigbeeClusterLibrary::parseFrameData(indication.asdu);
qCDebug(dcZigbeeNetwork()) << "ZCL HA" << indication << frame; //qCDebug(dcZigbeeNetwork()) << "ZCL HA" << indication << frame;
// Get the node
ZigbeeNode *node = getZigbeeNode(indication.sourceShortAddress);
if (!node) {
qCWarning(dcZigbeeNetwork()) << "Received a ZCL indication for an unrecognized node. There is no such node in the system. Ignoring indication" << indication;
return;
}
// Get the endpoint
ZigbeeNodeEndpoint *endpoint = node->getEndpoint(indication.sourceEndpoint);
if (!endpoint) {
qCWarning(dcZigbeeNetwork()) << "Received a ZCL indication for an unrecognized endpoint. There is no such endpoint on" << node << ". Ignoring indication" << indication;
return;
}
// Get the cluster
ZigbeeCluster *cluster = endpoint->getOutputCluster(static_cast<Zigbee::ClusterId>(indication.clusterId));
if (!cluster) {
cluster = endpoint->getInputCluster(static_cast<Zigbee::ClusterId>(indication.clusterId));
if (!cluster) {
qCWarning(dcZigbeeNetwork()) << "Received a ZCL indication for an unrecognized cluster. There is no such cluster on" << node << endpoint << "in the system. Ignoring indication" << indication;
return;
}
}
cluster->processApsDataIndication(indication.asdu);
} }
void ZigbeeNetworkDeconz::setPermitJoiningInternal(bool permitJoining) void ZigbeeNetworkDeconz::setPermitJoiningInternal(bool permitJoining)

View File

@ -32,7 +32,7 @@
#include "zigbeenetwork.h" #include "zigbeenetwork.h"
#include "zigbeechannelmask.h" #include "zigbeechannelmask.h"
#include "zigbeeclusterlibrary.h" #include "zcl/zigbeeclusterlibrary.h"
#include "zigbeebridgecontrollerdeconz.h" #include "zigbeebridgecontrollerdeconz.h"
class ZigbeeNetworkDeconz : public ZigbeeNetwork class ZigbeeNetworkDeconz : public ZigbeeNetwork
@ -75,6 +75,7 @@ private:
void handleZigbeeDeviceProfileIndication(const DeconzApsDataIndication &indication); void handleZigbeeDeviceProfileIndication(const DeconzApsDataIndication &indication);
// ZZL // ZZL
void handleZigbeeLightLinkIndication(const DeconzApsDataIndication &indication);
// HA // HA
void handleZigbeeHomeAutomationIndication(const DeconzApsDataIndication &indication); void handleZigbeeHomeAutomationIndication(const DeconzApsDataIndication &indication);

View File

@ -4,30 +4,22 @@ TARGET = nymea-zigbee1
TEMPLATE = lib TEMPLATE = lib
SOURCES += \ SOURCES += \
deconz/interface/zigbeeinterfacedeconz.cpp \ backends/deconz/interface/zigbeeinterfacedeconz.cpp \
deconz/interface/zigbeeinterfacedeconzreply.cpp \ backends/deconz/interface/zigbeeinterfacedeconzreply.cpp \
deconz/zigbeebridgecontrollerdeconz.cpp \ backends/deconz/zigbeebridgecontrollerdeconz.cpp \
deconz/zigbeenetworkdeconz.cpp \ backends/deconz/zigbeenetworkdeconz.cpp \
# nxp/interface/zigbeeinterface.cpp \ zcl/zigbeecluster.cpp \
# nxp/interface/zigbeeinterfacemessage.cpp \ zcl/zigbeeclusterattribute.cpp \
# nxp/interface/zigbeeinterfacerequest.cpp \ zcl/zigbeeclusterlibrary.cpp \
# nxp/interface/zigbeeinterfacereply.cpp \
# nxp/zigbeenetworknxp.cpp \
# nxp/zigbeebridgecontrollernxp.cpp \
# nxp/zigbeenodeendpointnxp.cpp \
# nxp/zigbeenodenxp.cpp \
zcl/zigbeeclusterbasic.cpp \
zcl/zigbeeclusterreply.cpp \ zcl/zigbeeclusterreply.cpp \
zcl/general/zigbeeclusterbasic.cpp \
zdo/zigbeedeviceobject.cpp \ zdo/zigbeedeviceobject.cpp \
zdo/zigbeedeviceobjectreply.cpp \ zdo/zigbeedeviceobjectreply.cpp \
zdo/zigbeedeviceprofile.cpp \ zdo/zigbeedeviceprofile.cpp \
zigbeeadpu.cpp \ zigbeeadpu.cpp \
zigbeebridgecontroller.cpp \ zigbeebridgecontroller.cpp \
zigbeechannelmask.cpp \ zigbeechannelmask.cpp \
zigbeecluster.cpp \ zigbeedatatype.cpp \
zigbeeclusterattribute.cpp \
zigbeeclusterlibrary.cpp \
zigbeeclusterlibraryreply.cpp \
zigbeemanufacturer.cpp \ zigbeemanufacturer.cpp \
zigbeenetwork.cpp \ zigbeenetwork.cpp \
zigbeenetworkdatabase.cpp \ zigbeenetworkdatabase.cpp \
@ -42,34 +34,33 @@ SOURCES += \
zigbeeutils.cpp \ zigbeeutils.cpp \
zigbeenode.cpp \ zigbeenode.cpp \
zigbeeaddress.cpp \ zigbeeaddress.cpp \
# nxp/interface/zigbeeinterface.cpp \
# nxp/interface/zigbeeinterfacemessage.cpp \
# nxp/interface/zigbeeinterfacerequest.cpp \
# nxp/interface/zigbeeinterfacereply.cpp \
# nxp/zigbeenetworknxp.cpp \
# nxp/zigbeebridgecontrollernxp.cpp \
# nxp/zigbeenodeendpointnxp.cpp \
# nxp/zigbeenodenxp.cpp \
HEADERS += \ HEADERS += \
deconz/interface/deconz.h \ backends/deconz/interface/deconz.h \
deconz/interface/zigbeeinterfacedeconz.h \ backends/deconz/interface/zigbeeinterfacedeconz.h \
deconz/interface/zigbeeinterfacedeconzreply.h \ backends/deconz/interface/zigbeeinterfacedeconzreply.h \
deconz/zigbeebridgecontrollerdeconz.h \ backends/deconz/zigbeebridgecontrollerdeconz.h \
deconz/zigbeenetworkdeconz.h \ backends/deconz/zigbeenetworkdeconz.h \
# nxp/interface/zigbeeinterface.h \ zcl/zigbeecluster.h \
# nxp/interface/zigbeeinterfacemessage.h \ zcl/zigbeeclusterattribute.h \
# nxp/interface/zigbeeinterfacerequest.h \ zcl/zigbeeclusterlibrary.h \
# nxp/interface/zigbeeinterfacereply.h \
# nxp/zigbeenetworknxp.h \
# nxp/zigbeebridgecontrollernxp.h \
# nxp/zigbeenodeendpointnxp.h \
# nxp/zigbeenodenxp.h \
zcl/zigbeeclusterbasic.h \
zcl/zigbeeclusterreply.h \ zcl/zigbeeclusterreply.h \
zcl/general/zigbeeclusterbasic.h \
zdo/zigbeedeviceobject.h \ zdo/zigbeedeviceobject.h \
zdo/zigbeedeviceobjectreply.h \ zdo/zigbeedeviceobjectreply.h \
zdo/zigbeedeviceprofile.h \ zdo/zigbeedeviceprofile.h \
zigbeeadpu.h \ zigbeeadpu.h \
zigbeebridgecontroller.h \ zigbeebridgecontroller.h \
zigbeechannelmask.h \ zigbeechannelmask.h \
zigbeecluster.h \ zigbeedatatype.h \
zigbeeclusterattribute.h \
zigbeeclusterlibrary.h \
zigbeeclusterlibraryreply.h \
zigbeemanufacturer.h \ zigbeemanufacturer.h \
zigbeenetwork.h \ zigbeenetwork.h \
zigbeenetworkdatabase.h \ zigbeenetworkdatabase.h \
@ -84,7 +75,14 @@ HEADERS += \
zigbeeutils.h \ zigbeeutils.h \
zigbeenode.h \ zigbeenode.h \
zigbeeaddress.h \ zigbeeaddress.h \
# nxp/interface/zigbeeinterface.h \
# nxp/interface/zigbeeinterfacemessage.h \
# nxp/interface/zigbeeinterfacerequest.h \
# nxp/interface/zigbeeinterfacereply.h \
# nxp/zigbeenetworknxp.h \
# nxp/zigbeebridgecontrollernxp.h \
# nxp/zigbeenodeendpointnxp.h \
# nxp/zigbeenodenxp.h \
# install header file with relative subdirectory # install header file with relative subdirectory
for (header, HEADERS) { for (header, HEADERS) {

View File

@ -34,5 +34,6 @@ Q_LOGGING_CATEGORY(dcZigbeeCluster, "ZigbeeCluster")
Q_LOGGING_CATEGORY(dcZigbeeInterface, "ZigbeeInterface") Q_LOGGING_CATEGORY(dcZigbeeInterface, "ZigbeeInterface")
Q_LOGGING_CATEGORY(dcZigbeeController, "ZigbeeController") Q_LOGGING_CATEGORY(dcZigbeeController, "ZigbeeController")
Q_LOGGING_CATEGORY(dcZigbeeDeviceObject, "ZigbeeDeviceObject") Q_LOGGING_CATEGORY(dcZigbeeDeviceObject, "ZigbeeDeviceObject")
Q_LOGGING_CATEGORY(dcZigbeeClusterLibrary, "ZigbeeClusterLibrary")
Q_LOGGING_CATEGORY(dcZigbeeNetworkDatabase, "ZigbeeNetworkDatabase") Q_LOGGING_CATEGORY(dcZigbeeNetworkDatabase, "ZigbeeNetworkDatabase")
Q_LOGGING_CATEGORY(dcZigbeeInterfaceTraffic, "ZigbeeInterfaceTraffic") Q_LOGGING_CATEGORY(dcZigbeeInterfaceTraffic, "ZigbeeInterfaceTraffic")

View File

@ -38,6 +38,7 @@ Q_DECLARE_LOGGING_CATEGORY(dcZigbeeCluster)
Q_DECLARE_LOGGING_CATEGORY(dcZigbeeInterface) Q_DECLARE_LOGGING_CATEGORY(dcZigbeeInterface)
Q_DECLARE_LOGGING_CATEGORY(dcZigbeeController) Q_DECLARE_LOGGING_CATEGORY(dcZigbeeController)
Q_DECLARE_LOGGING_CATEGORY(dcZigbeeDeviceObject) Q_DECLARE_LOGGING_CATEGORY(dcZigbeeDeviceObject)
Q_DECLARE_LOGGING_CATEGORY(dcZigbeeClusterLibrary)
Q_DECLARE_LOGGING_CATEGORY(dcZigbeeNetworkDatabase) Q_DECLARE_LOGGING_CATEGORY(dcZigbeeNetworkDatabase)
Q_DECLARE_LOGGING_CATEGORY(dcZigbeeInterfaceTraffic) Q_DECLARE_LOGGING_CATEGORY(dcZigbeeInterfaceTraffic)

View File

@ -0,0 +1,21 @@
#include "zigbeeclusterbasic.h"
#include "loggingcategory.h"
ZigbeeClusterBasic::ZigbeeClusterBasic(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent) :
ZigbeeCluster(network, node, endpoint, Zigbee::ClusterIdBasic, direction, parent)
{
}
void ZigbeeClusterBasic::setAttribute(const ZigbeeClusterAttribute &attribute)
{
if (hasAttribute(attribute.id())) {
qCDebug(dcZigbeeCluster()) << this << "update attribute" << static_cast<Attribute>(attribute.id()) << attribute.dataType() << attribute.data();
m_attributes[attribute.id()] = attribute;
emit attributeChanged(attribute);
} else {
qCDebug(dcZigbeeCluster()) << this << "add attribute" << static_cast<Attribute>(attribute.id()) << attribute.dataType() << attribute.data();
m_attributes.insert(attribute.id(), attribute);
emit attributeChanged(attribute);
}
}

View File

@ -3,13 +3,16 @@
#include <QObject> #include <QObject>
#include "zigbeecluster.h" #include "zcl/zigbeecluster.h"
class ZigbeeClusterBasic : public ZigbeeCluster class ZigbeeClusterBasic : public ZigbeeCluster
{ {
Q_OBJECT Q_OBJECT
public: public:
friend class ZigbeeNode;
friend class ZigbeeNetwork;
enum Attribute { enum Attribute {
AttributeZclVersion = 0x0000, // Mandatory AttributeZclVersion = 0x0000, // Mandatory
AttributeAppVersion = 0x0001, AttributeAppVersion = 0x0001,
@ -28,7 +31,7 @@ public:
}; };
Q_ENUM(Attribute) Q_ENUM(Attribute)
// From attribute 0x0007 power source // Enum for AttributePowerSource(0x0007)
enum AttributePowerSourceValue { enum AttributePowerSourceValue {
AttributePowerSourceValueUnknown = 0x00, AttributePowerSourceValueUnknown = 0x00,
AttributePowerSourceValueMainsSinglePhase = 0x01, AttributePowerSourceValueMainsSinglePhase = 0x01,
@ -40,9 +43,10 @@ public:
}; };
Q_ENUM(AttributePowerSourceValue) Q_ENUM(AttributePowerSourceValue)
explicit ZigbeeClusterBasic(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent = nullptr); explicit ZigbeeClusterBasic(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent = nullptr);
private:
void setAttribute(const ZigbeeClusterAttribute &attribute) override;
signals: signals:

View File

@ -74,14 +74,14 @@ bool ZigbeeCluster::hasAttribute(quint16 attributeId) const
return m_attributes.keys().contains(attributeId); return m_attributes.keys().contains(attributeId);
} }
ZigbeeClusterAttribute ZigbeeCluster::attribute(quint16 id) ZigbeeClusterAttribute ZigbeeCluster::attribute(quint16 attributeId)
{ {
return m_attributes.value(id); return m_attributes.value(attributeId);
} }
void ZigbeeCluster::setAttribute(const ZigbeeClusterAttribute &attribute) void ZigbeeCluster::setAttribute(const ZigbeeClusterAttribute &attribute)
{ {
if (hasAttribute(attribute.id())) { if (hasAttribute(attribute.id())) {
qCDebug(dcZigbeeCluster()) << this << "update attribute" << attribute; qCDebug(dcZigbeeCluster()) << this << "update attribute" << attribute;
m_attributes[attribute.id()] = attribute; m_attributes[attribute.id()] = attribute;
emit attributeChanged(attribute); emit attributeChanged(attribute);
@ -92,28 +92,31 @@ void ZigbeeCluster::setAttribute(const ZigbeeClusterAttribute &attribute)
} }
} }
ZigbeeNetworkReply *ZigbeeCluster::readAttributes(QList<quint16> attributes) ZigbeeClusterReply *ZigbeeCluster::readAttributes(QList<quint16> attributes)
{ {
qCDebug(dcZigbeeClusterLibrary()) << "Read attributes from" << m_node << m_endpoint << this << attributes;
// Build the request // Build the request
ZigbeeNetworkRequest request; ZigbeeNetworkRequest request = createGeneralRequest();
request.setRequestId(m_network->generateSequenceNumber());
request.setDestinationAddressMode(Zigbee::DestinationAddressModeShortAddress);
request.setDestinationShortAddress(static_cast<quint16>(m_node->shortAddress()));
request.setProfileId(m_endpoint->profile());
request.setClusterId(m_clusterId);
request.setSourceEndpoint(m_endpoint->endpointId());
request.setRadius(10);
// Build ZCL frame // 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; ZigbeeClusterLibrary::FrameControl frameControl;
frameControl.frameType = ZigbeeClusterLibrary::FrameTypeGlobal; // Note: for general commands always use global frameControl.frameType = ZigbeeClusterLibrary::FrameTypeGlobal;
frameControl.manufacturerSpecific = false;
if (m_direction == Direction::Input) {
frameControl.direction = ZigbeeClusterLibrary::DirectionClientToServer;
} else {
frameControl.direction = ZigbeeClusterLibrary::DirectionServerToClient;
}
frameControl.disableDefaultResponse = true; frameControl.disableDefaultResponse = true;
// ZCL header // ZCL header
ZigbeeClusterLibrary::Header header; ZigbeeClusterLibrary::Header header;
header.frameControl = frameControl; header.frameControl = frameControl;
header.command = ZigbeeClusterLibrary::CommandReadAttributes; header.command = ZigbeeClusterLibrary::CommandReadAttributes;
header.transactionSequenceNumber = m_network->generateTranactionSequenceNumber(); header.transactionSequenceNumber = m_transactionSequenceNumber++;
// ZCL payload // ZCL payload
QByteArray payload; QByteArray payload;
@ -125,15 +128,108 @@ ZigbeeNetworkReply *ZigbeeCluster::readAttributes(QList<quint16> attributes)
// Put them together // Put them together
ZigbeeClusterLibrary::Frame frame; ZigbeeClusterLibrary::Frame frame;
frame.clusterId = m_clusterId;
frame.header = header; frame.header = header;
frame.payload = payload; frame.payload = payload;
request.setTxOptions(Zigbee::ZigbeeTxOptions(Zigbee::ZigbeeTxOptionAckTransmission)); request.setTxOptions(Zigbee::ZigbeeTxOptions(Zigbee::ZigbeeTxOptionAckTransmission));
request.setAsdu(ZigbeeClusterLibrary::buildFrame(frame)); request.setAsdu(ZigbeeClusterLibrary::buildFrame(frame));
qCDebug(dcZigbeeCluster()) << "Send read attributes request" << m_node << m_endpoint << this << attributes; ZigbeeClusterReply *zclReply = createClusterReply(request, frame);
return m_network->sendRequest(request); 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 *ZigbeeCluster::createClusterReply(const ZigbeeNetworkRequest &request, ZigbeeClusterLibrary::Frame frame)
{
ZigbeeClusterReply *zclReply = new ZigbeeClusterReply(request, frame, this);
connect(zclReply, &ZigbeeClusterReply::finished, zclReply, &ZigbeeClusterReply::deleteLater);
zclReply->m_transactionSequenceNumber = frame.header.transactionSequenceNumber;
m_pendingReplies.insert(zclReply->transactionSequenceNumber(), zclReply);
return zclReply;
}
ZigbeeNetworkRequest ZigbeeCluster::createGeneralRequest()
{
// Build the request
ZigbeeNetworkRequest request;
request.setRequestId(m_network->generateSequenceNumber());
request.setDestinationAddressMode(Zigbee::DestinationAddressModeShortAddress);
request.setDestinationShortAddress(m_node->shortAddress());
request.setProfileId(Zigbee::ZigbeeProfileHomeAutomation); // Note: in Zigbee 3.0 this is the Application Profile (0x0104)
request.setClusterId(m_clusterId);
request.setSourceEndpoint(0x01);
request.setDestinationEndpoint(m_endpoint->endpointId());
request.setRadius(10);
request.setTxOptions(Zigbee::ZigbeeTxOptions(Zigbee::ZigbeeTxOptionAckTransmission));
return request;
}
bool ZigbeeCluster::verifyNetworkError(ZigbeeClusterReply *zclReply, ZigbeeNetworkReply *networkReply)
{
bool success = false;
switch (networkReply->error()) {
case ZigbeeNetworkReply::ErrorNoError:
// The request has been transported successfully to he destination, now
// wait for the expected indication or check if we already recieved it
zclReply->m_apsConfirmReceived = true;
zclReply->m_zigbeeApsStatus = networkReply->zigbeeApsStatus();
success = true;
break;
case ZigbeeNetworkReply::ErrorInterfaceError:
zclReply->m_error = ZigbeeClusterReply::ErrorInterfaceError;
break;
case ZigbeeNetworkReply::ErrorNetworkOffline:
zclReply->m_error = ZigbeeClusterReply::ErrorNetworkOffline;
break;
case ZigbeeNetworkReply::ErrorZigbeeApsStatusError:
zclReply->m_error = ZigbeeClusterReply::ErrorZigbeeApsStatusError;
zclReply->m_apsConfirmReceived = true;
zclReply->m_zigbeeApsStatus = networkReply->zigbeeApsStatus();
break;
}
return success;
}
void ZigbeeCluster::finishZclReply(ZigbeeClusterReply *zclReply)
{
m_pendingReplies.remove(zclReply->transactionSequenceNumber());
zclReply->finished();
}
void ZigbeeCluster::processApsDataIndication(QByteArray payload)
{
ZigbeeClusterLibrary::Frame frame = ZigbeeClusterLibrary::parseFrameData(payload);
qCDebug(dcZigbeeClusterLibrary()) << this << "received data indication" << frame;
if (m_pendingReplies.contains(frame.header.transactionSequenceNumber)) {
ZigbeeClusterReply *reply = m_pendingReplies.value(frame.header.transactionSequenceNumber);
reply->m_responseData = payload;
reply->m_responseFrame = frame;
reply->m_zclIndicationReceived = true;
if (reply->isComplete())
finishZclReply(reply);
}
} }
QDebug operator<<(QDebug debug, ZigbeeCluster *cluster) QDebug operator<<(QDebug debug, ZigbeeCluster *cluster)
@ -143,7 +239,6 @@ QDebug operator<<(QDebug debug, ZigbeeCluster *cluster)
<< cluster->clusterName() << ", " << cluster->clusterName() << ", "
<< cluster->direction() << cluster->direction()
<< ")"; << ")";
return debug.space(); return debug.space();
} }
@ -159,3 +254,4 @@ QDebug operator<<(QDebug debug, const ZigbeeClusterAttributeReport &attributeRep
return debug.space(); return debug.space();
} }

View File

@ -0,0 +1,213 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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 ZIGBEECLUSTER_H
#define ZIGBEECLUSTER_H
#include <QObject>
#include "zigbee.h"
#include "zigbeeclusterreply.h"
#include "zigbeeclusterlibrary.h"
#include "zigbeeclusterattribute.h"
struct ZigbeeClusterReportConfigurationRecord {
quint8 direction;
Zigbee::DataType dataType;
quint16 attributeId;
quint16 minInterval;
quint16 maxInterval;
quint16 timeout;
quint8 change;
};
typedef struct ZigbeeClusterAttributeReport {
quint16 sourceAddress;
quint8 endpointId;
Zigbee::ClusterId clusterId;
quint16 attributeId;
Zigbee::ZigbeeStatus attributeStatus;
Zigbee::DataType dataType;
QByteArray data;
} ZigbeeClusterAttributeReport;
class ZigbeeNode;
class ZigbeeNetwork;
class ZigbeeNodeEndpoint;
class ZigbeeNetworkReply;
class ZigbeeCluster : public QObject
{
Q_OBJECT
friend class ZigbeeNode;
friend class ZigbeeNetwork;
public:
enum Direction {
Input,
Output
};
Q_ENUM(Direction)
// // Power configuration cluster 0x0001
// enum PowerConfigurationAttribute {
// PowerConfigurationAttributeMainsInformation = 0x0000,
// PowerConfigurationAttributeMainsSettings = 0x0001,
// PowerConfigurationAttributeBatteryInformation = 0x0002,
// PowerConfigurationAttributeBatterySettings = 0x0003,
// PowerConfigurationAttributeBatterySource2Information = 0x0004,
// PowerConfigurationAttributeBattterySource2Settings = 0x0005,
// PowerConfigurationAttributeBatterySource3Information = 0x0006,
// PowerConfigurationAttributeBattterySource3Settings = 0x0007
// };
// 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
// enum LevelClusterAttribute {
// LevelClusterAttributeCurrentLevel = 0x0000,
// LevelClusterAttributeRemainingTime = 0x0001,
// LevelClusterAttributeOnOffTransitionTime = 0x0010,
// LevelClusterAttributeOnLevel = 0x0011,
// LevelClusterAttributeOnTransitionTime = 0x0012,
// LevelClusterAttributeOffTransitionTime = 0x0013,
// LevelClusterAttributeDefaultMoveRate = 0x0014
// };
// Q_ENUM(LevelClusterAttribute)
// enum LevelClusterCommand {
// LevelClusterCommandMoveToLevel = 0x00,
// LevelClusterCommandMove = 0x01,
// LevelClusterCommandStep = 0x02,
// LevelClusterCommandStop = 0x03,
// LevelClusterCommandMoveToLevelWithOnOff = 0x04,
// LevelClusterCommandMoveWithOnOff = 0x05,
// LevelClusterCommandStepWithOnOff = 0x06,
// LevelClusterCommandStopWithOnOff = 0x07,
// };
// Q_ENUM(LevelClusterCommand)
// // Color cluster 0x0300
// enum ColorControlClusterAttribute {
// ColorControlClusterAttributeCurrentHue = 0x0000,
// ColorControlClusterAttributeCurrentSaturation = 0x0001,
// ColorControlClusterAttributeRemainingTime = 0x0002,
// ColorControlClusterAttributeCurrentX = 0x0003,
// ColorControlClusterAttributeCurrentY = 0x0004,
// ColorControlClusterAttributeDriftCompensation = 0x0005,
// ColorControlClusterAttributeCompensationText = 0x0006,
// ColorControlClusterAttributeColorTemperatureMireds = 0x0007,
// ColorControlClusterAttributeColorMode = 0x0008,
// ColorControlClusterAttributeEnhancedCurrentHue = 0x4000,
// ColorControlClusterAttributeEnhancedColorMode = 0x4001,
// ColorControlClusterAttributeColorLoopActive = 0x4002,
// ColorControlClusterAttributeColorLoopDirection = 0x4003,
// ColorControlClusterAttributeColorLoopTime = 0x4004,
// ColorControlClusterAttributeColorLoopStartEnhancedHue = 0x4005,
// ColorControlClusterAttributeColorLoopStoredEnhancedHue = 0x4006,
// ColorControlClusterAttributeColorCapabilities = 0x400a,
// ColorControlClusterAttributeColorTempPhysicalMinMireds = 0x400b,
// ColorControlClusterAttributeColorTempPhysicalMaxMireds = 0x400c
// };
// Q_ENUM(ColorControlClusterAttribute)
// enum ColorControlClusterColorMode {
// ColorControlClusterColorModeHueSaturation = 0x00,
// ColorControlClusterColorModeXY = 0x01,
// ColorControlClusterColorModeColorTemperatureMired = 0x02
// };
// Q_ENUM(ColorControlClusterColorMode)
explicit ZigbeeCluster(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Zigbee::ClusterId clusterId, Direction direction, QObject *parent = nullptr);
Direction direction() const;
Zigbee::ClusterId clusterId() const;
QString clusterName() const;
QList<ZigbeeClusterAttribute> attributes() const;
bool hasAttribute(quint16 attributeId) const;
ZigbeeClusterAttribute attribute(quint16 attributeId);
// ZCL global commands
ZigbeeClusterReply *readAttributes(QList<quint16> attributes);
protected:
ZigbeeNetwork *m_network = nullptr;
ZigbeeNode *m_node = nullptr;
ZigbeeNodeEndpoint *m_endpoint= nullptr;
Zigbee::ClusterId m_clusterId = Zigbee::ClusterIdUnknown;
Direction m_direction = Input;
QHash<quint16, ZigbeeClusterAttribute> m_attributes;
ZigbeeNetworkRequest createGeneralRequest();
quint8 m_transactionSequenceNumber = 0;
QHash<quint8, ZigbeeClusterReply *> m_pendingReplies;
ZigbeeClusterReply *createClusterReply(const ZigbeeNetworkRequest &request, ZigbeeClusterLibrary::Frame frame);
bool verifyNetworkError(ZigbeeClusterReply *zclReply, ZigbeeNetworkReply *networkReply);
void finishZclReply(ZigbeeClusterReply *zclReply);
private:
virtual void setAttribute(const ZigbeeClusterAttribute &attribute);
signals:
void attributeChanged(const ZigbeeClusterAttribute &attribute);
public slots:
void processApsDataIndication(QByteArray payload);
};
QDebug operator<<(QDebug debug, ZigbeeCluster *cluster);
QDebug operator<<(QDebug debug, const ZigbeeClusterAttributeReport &attributeReport);
#endif // ZIGBEECLUSTER_H

View File

@ -1,7 +0,0 @@
#include "zigbeeclusterbasic.h"
ZigbeeClusterBasic::ZigbeeClusterBasic(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent) :
ZigbeeCluster(network, node, endpoint, Zigbee::ClusterIdBasic, direction, parent)
{
}

View File

@ -26,6 +26,7 @@
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "zigbeeclusterlibrary.h" #include "zigbeeclusterlibrary.h"
#include "loggingcategory.h"
#include "zigbeeutils.h" #include "zigbeeutils.h"
#include <QDataStream> #include <QDataStream>
@ -35,7 +36,7 @@ quint8 ZigbeeClusterLibrary::buildFrameControlByte(const ZigbeeClusterLibrary::F
quint8 byte = 0x00; quint8 byte = 0x00;
// Bit 0-1 // Bit 0-1
byte |= FrameTypeClusterSpecific; byte |= frameControl.frameType;
// Bit 2 // Bit 2
if (frameControl.manufacturerSpecific) if (frameControl.manufacturerSpecific)
@ -93,7 +94,95 @@ QByteArray ZigbeeClusterLibrary::buildHeader(const ZigbeeClusterLibrary::Header
return headerData; return headerData;
} }
ZigbeeClusterLibrary::Frame ZigbeeClusterLibrary::parseFrameData(Zigbee::ClusterId clusterId, const QByteArray &frameData) QList<ZigbeeClusterLibrary::ReadAttributeStatusRecord> ZigbeeClusterLibrary::parseAttributeStatusRecords(const QByteArray &payload)
{
// Read attribute status records
QList<ReadAttributeStatusRecord> attributeStatusRecords;
qCDebug(dcZigbeeClusterLibrary()) << "Parse attribute status records from" << ZigbeeUtils::convertByteArrayToHexString(payload);
QDataStream stream(payload);
stream.setByteOrder(QDataStream::LittleEndian);
quint16 attributeId; quint8 statusInt; quint8 dataTypeInt; quint16 numberOfElenemts; quint8 elementType;
QByteArray data;
while (!stream.atEnd()) {
// Reset variables
attributeId = 0; statusInt = 0; dataTypeInt = 0; numberOfElenemts = 0; elementType = 0;
data.clear();
// Read attribute id and status
stream >> attributeId >> statusInt;
Zigbee::ZigbeeStatus status = static_cast<Zigbee::ZigbeeStatus>(statusInt);
qCDebug(dcZigbeeClusterLibrary()) << "Parse:" << ZigbeeUtils::convertUint16ToHexString(attributeId) << status;
if (status != Zigbee::ZigbeeStatusSuccess) {
qCWarning(dcZigbeeCluster()) << "Attribute status record" << ZigbeeUtils::convertUint16ToHexString(attributeId) << "finished with error" << status;
// If not success, we are done and can continue with the next status record
continue;
} else {
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 {
// Normal data type
quint8 length = 0;
stream >> length;
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);
}
}
ReadAttributeStatusRecord attributeRecord;
attributeRecord.attributeId = attributeId;
attributeRecord.attributeStatus = status;
attributeRecord.dataType = dataType;
attributeRecord.data = data;
qCDebug(dcZigbeeClusterLibrary()) << attributeRecord;
attributeStatusRecords.append(attributeRecord);
}
}
return attributeStatusRecords;
}
ZigbeeClusterLibrary::Frame ZigbeeClusterLibrary::parseFrameData(const QByteArray &frameData)
{ {
QDataStream stream(frameData); QDataStream stream(frameData);
stream.setByteOrder(QDataStream::LittleEndian); stream.setByteOrder(QDataStream::LittleEndian);
@ -117,15 +206,12 @@ ZigbeeClusterLibrary::Frame ZigbeeClusterLibrary::parseFrameData(Zigbee::Cluster
offset += 1; offset += 1;
stream >> commandByte; stream >> commandByte;
offset += 1;
header.command = static_cast<Command>(commandByte); header.command = static_cast<Command>(commandByte);
offset += 1; offset += 1;
Frame frame; Frame frame;
frame.clusterId = clusterId;
frame.header = header; frame.header = header;
frame.payload = frameData.right(frameData.length() - offset - 1); frame.payload = frameData.right(frameData.length() - offset);
return frame; return frame;
} }
@ -169,8 +255,20 @@ QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::Header &header)
QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::Frame &frame) QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::Frame &frame)
{ {
debug.nospace() << "Zigbee Cluster Library Frame(" << frame.clusterId << ", "; debug.nospace() << "Frame(";
debug.nospace() << frame.header; debug.nospace() << frame.header;
debug.nospace() << ZigbeeUtils::convertByteArrayToHexString(frame.payload) << ")"; debug.nospace() << ZigbeeUtils::convertByteArrayToHexString(frame.payload) << ")";
return debug.space(); return debug.space();
} }
QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::ReadAttributeStatusRecord &attributeStatusRecord)
{
debug.nospace().noquote() << "ReadAttributeStatusRecord("
<< ZigbeeUtils::convertUint16ToHexString(attributeStatusRecord.attributeId) << ", "
<< attributeStatusRecord.attributeStatus << ", "
<< attributeStatusRecord.dataType << ", "
<< attributeStatusRecord.data
<< ")";
return debug.space();
}

View File

@ -65,6 +65,18 @@ public:
}; };
Q_ENUM(Command) Q_ENUM(Command)
enum GlobalAttribute {
GlobalAttributeClusterRevision = 0xfffd,
GlobalAttributeAttributeReportingStatus = 0xfffe
};
Q_ENUM(GlobalAttribute)
enum AttributeReportingStatus {
AttributeReportingStatusPending = 0x00,
AttributeReportingStatusComplete = 0x01
};
Q_ENUM(AttributeReportingStatus)
// Frame control field // Frame control field
enum FrameType { enum FrameType {
FrameTypeGlobal = 0x00, FrameTypeGlobal = 0x00,
@ -79,7 +91,7 @@ public:
Q_ENUM(Direction) Q_ENUM(Direction)
typedef struct FrameControl { typedef struct FrameControl {
FrameType frameType = FrameTypeClusterSpecific; FrameType frameType = FrameTypeGlobal;
bool manufacturerSpecific = false; bool manufacturerSpecific = false;
Direction direction = DirectionClientToServer; Direction direction = DirectionClientToServer;
bool disableDefaultResponse = false; bool disableDefaultResponse = false;
@ -93,24 +105,39 @@ public:
} ZclHeader; } ZclHeader;
typedef struct Frame { typedef struct Frame {
Zigbee::ClusterId clusterId;
Header header; Header header;
QByteArray payload; QByteArray payload;
} Frame; } Frame;
// Read attribute
typedef struct ReadAttributeStatusRecord {
quint16 attributeId;
Zigbee::ZigbeeStatus attributeStatus;
Zigbee::DataType dataType;
QByteArray data;
} ReadAttributeStatusRecord;
// General parse/build methods // General parse/build methods
static quint8 buildFrameControlByte(const FrameControl &frameControl); static quint8 buildFrameControlByte(const FrameControl &frameControl);
static FrameControl parseFrameControlByte(quint8 frameControlByte); static FrameControl parseFrameControlByte(quint8 frameControlByte);
static QByteArray buildHeader(const Header &header); static QByteArray buildHeader(const Header &header);
static Frame parseFrameData(Zigbee::ClusterId clusterId, const QByteArray &frameData); static QList<ReadAttributeStatusRecord> parseAttributeStatusRecords(const QByteArray &payload);
//static QByteArray readAttributeData(const QDataStream &stream, Zigbee::DataType dataType);
static Frame parseFrameData(const QByteArray &frameData);
static QByteArray buildFrame(const Frame &frame); static QByteArray buildFrame(const Frame &frame);
}; };
QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::FrameControl &frameControl); QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::FrameControl &frameControl);
QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::Header &header); QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::Header &header);
QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::Frame &frame); QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::Frame &frame);
QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::ReadAttributeStatusRecord &attributeStatusRecord);
#endif // ZIGBEECLUSTERLIBRARY_H #endif // ZIGBEECLUSTERLIBRARY_H

View File

@ -1,6 +1,44 @@
#include "zigbeeclusterreply.h" #include "zigbeeclusterreply.h"
ZigbeeClusterReply::ZigbeeClusterReply(QObject *parent) : QObject(parent) ZigbeeClusterReply::Error ZigbeeClusterReply::error() const
{
return m_error;
}
ZigbeeNetworkRequest ZigbeeClusterReply::request() const
{
return m_request;
}
ZigbeeClusterLibrary::Frame ZigbeeClusterReply::requestFrame() const
{
return m_requestFrame;
}
quint8 ZigbeeClusterReply::transactionSequenceNumber() const
{
return m_transactionSequenceNumber;
}
QByteArray ZigbeeClusterReply::responseData() const
{
return m_responseData;
}
ZigbeeClusterLibrary::Frame ZigbeeClusterReply::responseFrame() const
{
return m_responseFrame;
}
bool ZigbeeClusterReply::isComplete() const
{
return m_apsConfirmReceived && m_zclIndicationReceived;
}
ZigbeeClusterReply::ZigbeeClusterReply(const ZigbeeNetworkRequest &request, ZigbeeClusterLibrary::Frame requestFrame, QObject *parent) :
QObject(parent),
m_request(request),
m_requestFrame(requestFrame)
{ {
} }

View File

@ -3,13 +3,59 @@
#include <QObject> #include <QObject>
#include "zigbeenetworkrequest.h"
#include "zigbeeclusterlibrary.h"
class ZigbeeClusterReply : public QObject class ZigbeeClusterReply : public QObject
{ {
Q_OBJECT Q_OBJECT
friend class ZigbeeCluster;
public: public:
explicit ZigbeeClusterReply(QObject *parent = nullptr); enum Error {
ErrorNoError, // All OK, no error occured, the message was transported successfully
ErrorTimeout, // The request timeouted
ErrorZigbeeApsStatusError, // An APS transport error occured. See zigbeeApsStatus()
ErrorInterfaceError, // A transport interface error occured. Could not communicate with the hardware.
ErrorNetworkOffline // The network is offline. Cannot send any requests
};
Q_ENUM(Error)
Error error() const;
ZigbeeNetworkRequest request() const;
ZigbeeClusterLibrary::Frame requestFrame() const;
quint8 transactionSequenceNumber() const;
QByteArray responseData() const;
ZigbeeClusterLibrary::Frame responseFrame() const;
bool isComplete() const;
private:
explicit ZigbeeClusterReply(const ZigbeeNetworkRequest &request, ZigbeeClusterLibrary::Frame requestFrame, QObject *parent = nullptr);
Error m_error = ErrorNoError;
// Request
quint8 m_transactionSequenceNumber = 0;
ZigbeeNetworkRequest m_request;
ZigbeeClusterLibrary::Frame m_requestFrame;
// Response
bool m_apsConfirmReceived = false;
Zigbee::ZigbeeApsStatus m_zigbeeApsStatus = Zigbee::ZigbeeApsStatusSuccess;
ZigbeeClusterLibrary::Command m_expectedResponse;
bool m_zclIndicationReceived = false;
QByteArray m_responseData;
ZigbeeClusterLibrary::Frame m_responseFrame;
signals: signals:
void finished();
}; };

View File

@ -41,7 +41,7 @@ class ZigbeeDeviceObjectReply : public QObject
public: public:
enum Error { enum Error {
ErrorNoError, // All OK, no error occured ErrorNoError, // All OK, no error occured, the message was transported successfully
ErrorTimeout, // The request timeouted ErrorTimeout, // The request timeouted
ErrorZigbeeApsStatusError, // An APS transport error occured. See zigbeeApsStatus() ErrorZigbeeApsStatusError, // An APS transport error occured. See zigbeeApsStatus()
ErrorInterfaceError, // A transport interface error occured. Could not communicate with the hardware. ErrorInterfaceError, // A transport interface error occured. Could not communicate with the hardware.
@ -81,7 +81,6 @@ private:
QByteArray m_responseData; QByteArray m_responseData;
ZigbeeDeviceProfile::Adpu m_responseAdpu; ZigbeeDeviceProfile::Adpu m_responseAdpu;
signals: signals:
void finished(); void finished();

View File

@ -1,234 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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 ZIGBEECLUSTER_H
#define ZIGBEECLUSTER_H
#include <QObject>
#include "zigbee.h"
#include "zigbeeclusterlibrary.h"
#include "zigbeeclusterattribute.h"
struct ZigbeeClusterReportConfigurationRecord {
quint8 direction;
Zigbee::DataType dataType;
quint16 attributeId;
quint16 minInterval;
quint16 maxInterval;
quint16 timeout;
quint8 change;
};
typedef struct ZigbeeClusterAttributeReport {
quint16 sourceAddress;
quint8 endpointId;
Zigbee::ClusterId clusterId;
quint16 attributeId;
Zigbee::ZigbeeStatus attributeStatus;
Zigbee::DataType dataType;
QByteArray data;
} ZigbeeClusterAttributeReport;
class ZigbeeNode;
class ZigbeeNetwork;
class ZigbeeNodeEndpoint;
class ZigbeeNetworkReply;
class ZigbeeCluster : public QObject
{
Q_OBJECT
friend class ZigbeeNode;
public:
enum Direction {
Input,
Output
};
Q_ENUM(Direction)
// Basic clustr 0x0000
// Attributes from the basic cluster 0x0000
enum BasicAttribute {
BasicAttributeZclVersion = 0x0000, // Mandatory
BasicAttributeAppVersion = 0x0001,
BasicAttributeStackVersion = 0x0002,
BasicAttributeHardwareVersion = 0x0003,
BasicAttributeManufacturerName = 0x0004,
BasicAttributeModelIdentifier = 0x0005,
BasicAttributeDateCode = 0x0006, // ISO 8601 YYYYMMDD
BasicAttributePowerSource = 0x0007, // Mandatory
BasicAttributeLocationDescription = 0x0010,
BasicAttributePhysicalEnvironment = 0x0011,
BasicAttributeDeviceEnabled = 0x0012, // 0: disabled, 1: enabled
BasicAttributeAlarmMask = 0x0013,
BasicAttributeDisableLocalConfig = 0x0014,
BasicAttributeSwBuildId = 0x4000
};
Q_ENUM(BasicAttribute)
// From the Basic cluster attribute 0x0007 power source
enum BasicAttributePowerSourceValue {
BasicAttributePowerSourceValueUnknown = 0x00,
BasicAttributePowerSourceValueMainsSinglePhase = 0x01,
BasicAttributePowerSourceValueMainsThreePhase = 0x02,
BasicAttributePowerSourceValueBattery = 0x03,
BasicAttributePowerSourceValueDcSource = 0x04,
BasicAttributePowerSourceValueEmergencyMainsConstantlyPowered = 0x05,
BasicAttributePowerSourceValueEmergencyMainsTransferSwitch = 0x06
};
Q_ENUM(BasicAttributePowerSourceValue)
// Power configuration cluster 0x0001
enum PowerConfigurationAttribute {
PowerConfigurationAttributeMainsInformation = 0x0000,
PowerConfigurationAttributeMainsSettings = 0x0001,
PowerConfigurationAttributeBatteryInformation = 0x0002,
PowerConfigurationAttributeBatterySettings = 0x0003,
PowerConfigurationAttributeBatterySource2Information = 0x0004,
PowerConfigurationAttributeBattterySource2Settings = 0x0005,
PowerConfigurationAttributeBatterySource3Information = 0x0006,
PowerConfigurationAttributeBattterySource3Settings = 0x0007
};
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
enum LevelClusterAttribute {
LevelClusterAttributeCurrentLevel = 0x0000,
LevelClusterAttributeRemainingTime = 0x0001,
LevelClusterAttributeOnOffTransitionTime = 0x0010,
LevelClusterAttributeOnLevel = 0x0011,
LevelClusterAttributeOnTransitionTime = 0x0012,
LevelClusterAttributeOffTransitionTime = 0x0013,
LevelClusterAttributeDefaultMoveRate = 0x0014
};
Q_ENUM(LevelClusterAttribute)
enum LevelClusterCommand {
LevelClusterCommandMoveToLevel = 0x00,
LevelClusterCommandMove = 0x01,
LevelClusterCommandStep = 0x02,
LevelClusterCommandStop = 0x03,
LevelClusterCommandMoveToLevelWithOnOff = 0x04,
LevelClusterCommandMoveWithOnOff = 0x05,
LevelClusterCommandStepWithOnOff = 0x06,
LevelClusterCommandStopWithOnOff = 0x07,
};
Q_ENUM(LevelClusterCommand)
// Color cluster 0x0300
enum ColorControlClusterAttribute {
ColorControlClusterAttributeCurrentHue = 0x0000,
ColorControlClusterAttributeCurrentSaturation = 0x0001,
ColorControlClusterAttributeRemainingTime = 0x0002,
ColorControlClusterAttributeCurrentX = 0x0003,
ColorControlClusterAttributeCurrentY = 0x0004,
ColorControlClusterAttributeDriftCompensation = 0x0005,
ColorControlClusterAttributeCompensationText = 0x0006,
ColorControlClusterAttributeColorTemperatureMireds = 0x0007,
ColorControlClusterAttributeColorMode = 0x0008,
ColorControlClusterAttributeEnhancedCurrentHue = 0x4000,
ColorControlClusterAttributeEnhancedColorMode = 0x4001,
ColorControlClusterAttributeColorLoopActive = 0x4002,
ColorControlClusterAttributeColorLoopDirection = 0x4003,
ColorControlClusterAttributeColorLoopTime = 0x4004,
ColorControlClusterAttributeColorLoopStartEnhancedHue = 0x4005,
ColorControlClusterAttributeColorLoopStoredEnhancedHue = 0x4006,
ColorControlClusterAttributeColorCapabilities = 0x400a,
ColorControlClusterAttributeColorTempPhysicalMinMireds = 0x400b,
ColorControlClusterAttributeColorTempPhysicalMaxMireds = 0x400c
};
Q_ENUM(ColorControlClusterAttribute)
enum ColorControlClusterColorMode {
ColorControlClusterColorModeHueSaturation = 0x00,
ColorControlClusterColorModeXY = 0x01,
ColorControlClusterColorModeColorTemperatureMired = 0x02
};
Q_ENUM(ColorControlClusterColorMode)
explicit ZigbeeCluster(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Zigbee::ClusterId clusterId, Direction direction, QObject *parent = nullptr);
Direction direction() const;
Zigbee::ClusterId clusterId() const;
QString clusterName() const;
QList<ZigbeeClusterAttribute> attributes() const;
bool hasAttribute(quint16 attributeId) const;
ZigbeeClusterAttribute attribute(quint16 id);
// FIXME: this should not be public
void setAttribute(const ZigbeeClusterAttribute &attribute);
// ZCL global commands
ZigbeeNetworkReply *readAttributes(QList<quint16> attributes);
protected:
ZigbeeNetwork *m_network = nullptr;
ZigbeeNode *m_node = nullptr;
ZigbeeNodeEndpoint *m_endpoint= nullptr;
Zigbee::ClusterId m_clusterId = Zigbee::ClusterIdUnknown;
Direction m_direction = Input;
QHash<quint16, ZigbeeClusterAttribute> m_attributes;
signals:
void attributeChanged(const ZigbeeClusterAttribute &attribute);
};
QDebug operator<<(QDebug debug, ZigbeeCluster *cluster);
QDebug operator<<(QDebug debug, const ZigbeeClusterAttributeReport &attributeReport);
#endif // ZIGBEECLUSTER_H

View File

@ -1,24 +0,0 @@
#include "zigbeeclusterlibraryreply.h"
ZigbeeNetworkRequest ZigbeeClusterLibraryReply::request() const
{
return m_request;
}
ZigbeeClusterLibrary::Frame ZigbeeClusterLibraryReply::requestFrame() const
{
return m_requestFrame;
}
quint8 ZigbeeClusterLibraryReply::transactionSequenceNumber() const
{
return m_requestFrame.header.transactionSequenceNumber;
}
ZigbeeClusterLibraryReply::ZigbeeClusterLibraryReply(const ZigbeeNetworkRequest &request, ZigbeeClusterLibrary::Frame requestFrame, QObject *parent) :
QObject(parent),
m_request(request),
m_requestFrame(requestFrame)
{
}

View File

@ -1,35 +0,0 @@
#ifndef ZIGBEECLUSTERLIBRARYREPLY_H
#define ZIGBEECLUSTERLIBRARYREPLY_H
#include <QObject>
#include "zigbeenetworkrequest.h"
#include "zigbeeclusterlibrary.h"
class ZigbeeClusterLibraryReply : public QObject
{
Q_OBJECT
friend class ZigbeeCluster;
public:
ZigbeeNetworkRequest request() const;
ZigbeeClusterLibrary::Frame requestFrame() const;
quint8 transactionSequenceNumber() const;
private:
explicit ZigbeeClusterLibraryReply(const ZigbeeNetworkRequest &request, ZigbeeClusterLibrary::Frame requestFrame, QObject *parent = nullptr);
ZigbeeNetworkRequest m_request;
ZigbeeClusterLibrary::Frame m_requestFrame;
ZigbeeClusterLibrary::Command m_expectedResponse;
ZigbeeClusterLibrary::Frame m_responseFrame;
signals:
void finished();
};
#endif // ZIGBEECLUSTERLIBRARYREPLY_H

View File

@ -0,0 +1,480 @@
#include "zigbeedatatype.h"
ZigbeeDataType::ZigbeeDataType(Zigbee::DataType dataType, const QByteArray &data):
m_dataType(dataType),
m_data(data)
{
switch (dataType) {
case Zigbee::NoData:
m_name = "No data";
m_className = "Null";
m_typeLength = typeLength(m_dataType);
m_data.clear();
break;
case Zigbee::Data8:
m_name = "8-bit data";
m_className = "General data discrete";
m_typeLength = typeLength(m_dataType);
break;
case Zigbee::Data16:
m_name = "16-bit data";
m_className = "General data discrete";
m_typeLength = typeLength(m_dataType);
break;
case Zigbee::Data24:
m_name = "24-bit data";
m_className = "General data discrete";
m_typeLength = typeLength(m_dataType);
break;
case Zigbee::Data32:
m_name = "32-bit data";
m_className = "General data discrete";
m_typeLength = typeLength(m_dataType);
break;
case Zigbee::Data40:
m_name = "40-bit data";
m_className = "General data discrete";
m_typeLength = typeLength(m_dataType);
break;
case Zigbee::Data48:
m_name = "48-bit data";
m_className = "General data discrete";
m_typeLength = typeLength(m_dataType);
break;
case Zigbee::Data56:
m_name = "56-bit data";
m_className = "General data discrete";
m_typeLength = typeLength(m_dataType);
break;
case Zigbee::Data64:
m_name = "64-bit data";
m_className = "General data discrete";
m_typeLength = typeLength(m_dataType);
break;
case Zigbee::Bool:
m_name = "Bool";
m_className = "Logical discrete";
m_typeLength = typeLength(m_dataType);
break;
case Zigbee::BitMap8:
m_name = "8-bit bitmap";
m_className = "Bitmap discrete";
m_typeLength = typeLength(m_dataType);
break;
case Zigbee::BitMap16:
m_name = "16-bit bitmap";
m_className = "Bitmap discrete";
m_typeLength = typeLength(m_dataType);
break;
case Zigbee::BitMap24:
m_name = "24-bit bitmap";
m_className = "Bitmap discrete";
m_typeLength = typeLength(m_dataType);
break;
case Zigbee::BitMap32:
m_name = "32-bit bitmap";
m_className = "Bitmap discrete";
m_typeLength = typeLength(m_dataType);
break;
case Zigbee::BitMap40:
m_name = "40-bit bitmap";
m_className = "Bitmap discrete";
m_typeLength = typeLength(m_dataType);
break;
case Zigbee::BitMap48:
m_name = "48-bit bitmap";
m_className = "Bitmap discrete";
m_typeLength = typeLength(m_dataType);
break;
case Zigbee::BitMap56:
m_name = "56-bit bitmap";
m_className = "Bitmap discrete";
m_typeLength = typeLength(m_dataType);
break;
case Zigbee::BitMap64:
m_name = "64-bit bitmap";
m_className = "Bitmap discrete";
m_typeLength = typeLength(m_dataType);
break;
case Zigbee::Uint8:
m_name = "Unsigned 8-bit integer";
m_className = "Unsigned integer analog";
m_typeLength = typeLength(m_dataType);
break;
case Zigbee::Uint16:
break;
case Zigbee::Uint24:
break;
case Zigbee::Uint32:
break;
case Zigbee::Uint40:
break;
case Zigbee::Uint48:
break;
case Zigbee::Uint56:
break;
case Zigbee::Uint64:
break;
case Zigbee::Int8:
break;
case Zigbee::Int16:
break;
case Zigbee::Int24:
break;
case Zigbee::Int32:
break;
case Zigbee::Int40:
break;
case Zigbee::Int48:
break;
case Zigbee::Int56:
break;
case Zigbee::Int64:
break;
case Zigbee::Enum8:
break;
case Zigbee::Enum16:
break;
case Zigbee::FloatSemi:
break;
case Zigbee::FloatSingle:
break;
case Zigbee::FloatDouble:
break;
case Zigbee::OctetString:
break;
case Zigbee::CharString:
break;
case Zigbee::LongOctetString:
break;
case Zigbee::LongCharString:
break;
case Zigbee::Array:
break;
case Zigbee::Structure:
break;
case Zigbee::Set:
break;
case Zigbee::Bag:
break;
case Zigbee::TimeOfDay:
break;
case Zigbee::Date:
break;
case Zigbee::UtcTime:
break;
case Zigbee::Cluster:
break;
case Zigbee::Attribute:
break;
case Zigbee::BacnetId:
break;
case Zigbee::IeeeAddress:
break;
case Zigbee::BitKey128:
break;
case Zigbee::Unknown:
break;
}
/*
switch (dataType) {
case Zigbee::NoData:
break;
case Zigbee::Data8:
break;
case Zigbee::Data16:
break;
case Zigbee::Data24:
break;
case Zigbee::Data32:
break;
case Zigbee::Data40:
break;
case Zigbee::Data48:
break;
case Zigbee::Data56:
break;
case Zigbee::Data64:
break;
case Zigbee::Bool:
break;
case Zigbee::BitMap8:
break;
case Zigbee::BitMap16:
break;
case Zigbee::BitMap24:
break;
case Zigbee::BitMap32:
break;
case Zigbee::BitMap40:
break;
case Zigbee::BitMap48:
break;
case Zigbee::BitMap56:
break;
case Zigbee::BitMap64:
break;
case Zigbee::Uint8:
break;
case Zigbee::Uint16:
break;
case Zigbee::Uint24:
break;
case Zigbee::Uint32:
break;
case Zigbee::Uint40:
break;
case Zigbee::Uint48:
break;
case Zigbee::Uint56:
break;
case Zigbee::Uint64:
break;
case Zigbee::Int8:
break;
case Zigbee::Int16:
break;
case Zigbee::Int24:
break;
case Zigbee::Int32:
break;
case Zigbee::Int40:
break;
case Zigbee::Int48:
break;
case Zigbee::Int56:
break;
case Zigbee::Int64:
break;
case Zigbee::Enum8:
break;
case Zigbee::Enum16:
break;
case Zigbee::FloatSemi:
break;
case Zigbee::FloatSingle:
break;
case Zigbee::FloatDouble:
break;
case Zigbee::OctetString:
break;
case Zigbee::CharString:
break;
case Zigbee::LongOctetString:
break;
case Zigbee::LongCharString:
break;
case Zigbee::Array:
break;
case Zigbee::Structure:
break;
case Zigbee::Set:
break;
case Zigbee::Bag:
break;
case Zigbee::TimeOfDay:
break;
case Zigbee::Date:
break;
case Zigbee::UtcTime:
break;
case Zigbee::Cluster:
break;
case Zigbee::Attribute:
break;
case Zigbee::BacnetId:
break;
case Zigbee::IeeeAddress:
break;
case Zigbee::BitKey128:
break;
case Zigbee::Unknown:
break;
}
*/
}
int ZigbeeDataType::typeLength(Zigbee::DataType dataType)
{
int length = 0;
switch (dataType) {
case Zigbee::NoData:
break;
case Zigbee::Data8:
length = 1;
break;
case Zigbee::Data16:
length = 2;
break;
case Zigbee::Data24:
length = 3;
break;
case Zigbee::Data32:
length = 4;
break;
case Zigbee::Data40:
length = 5;
break;
case Zigbee::Data48:
length = 6;
break;
case Zigbee::Data56:
length = 7;
break;
case Zigbee::Data64:
length = 8;
break;
case Zigbee::Bool:
length = 1;
break;
case Zigbee::BitMap8:
length = 1;
break;
case Zigbee::BitMap16:
length = 2;
break;
case Zigbee::BitMap24:
length = 3;
break;
case Zigbee::BitMap32:
length = 4;
break;
case Zigbee::BitMap40:
length = 5;
break;
case Zigbee::BitMap48:
length = 6;
break;
case Zigbee::BitMap56:
length = 7;
break;
case Zigbee::BitMap64:
length = 8;
break;
case Zigbee::Uint8:
length = 1;
break;
case Zigbee::Uint16:
length = 2;
break;
case Zigbee::Uint24:
length = 3;
break;
case Zigbee::Uint32:
length = 4;
break;
case Zigbee::Uint40:
length = 5;
break;
case Zigbee::Uint48:
length = 6;
break;
case Zigbee::Uint56:
length = 7;
break;
case Zigbee::Uint64:
length = 8;
break;
case Zigbee::Int8:
length = 1;
break;
case Zigbee::Int16:
length = 2;
break;
case Zigbee::Int24:
length = 3;
break;
case Zigbee::Int32:
length = 4;
break;
case Zigbee::Int40:
length = 5;
break;
case Zigbee::Int48:
length = 6;
break;
case Zigbee::Int56:
length = 7;
break;
case Zigbee::Int64:
length = 8;
break;
case Zigbee::Enum8:
length = 1;
break;
case Zigbee::Enum16:
length = 2;
break;
case Zigbee::FloatSemi:
length = 2;
break;
case Zigbee::FloatSingle:
length = 4;
break;
case Zigbee::FloatDouble:
length = 8;
break;
case Zigbee::OctetString:
// first byte is length
length = -1;
break;
case Zigbee::CharString:
// first byte is length
length = -1;
break;
case Zigbee::LongOctetString:
// first 2 byte is length
length = -1;
break;
case Zigbee::LongCharString:
// first 2 byte is length
length = -1;
break;
case Zigbee::Array:
// 2 + sum of lengths of content
length = -1;
break;
case Zigbee::Structure:
// 2 + sum of lengths of content
length = -1;
break;
case Zigbee::Set:
// 2 + sum of lengths of content
length = -1;
break;
case Zigbee::Bag:
// 2 + sum of lengths of content
length = -1;
break;
case Zigbee::TimeOfDay:
length = 4;
break;
case Zigbee::Date:
length = 4;
break;
case Zigbee::UtcTime:
length = 4;
break;
case Zigbee::Cluster:
length = 2;
break;
case Zigbee::Attribute:
length = 2;
break;
case Zigbee::BacnetId:
length = 4;
break;
case Zigbee::IeeeAddress:
length = 8;
break;
case Zigbee::BitKey128:
length = 16;
break;
case Zigbee::Unknown:
break;
}
return length;
}

View File

@ -0,0 +1,26 @@
#ifndef ZIGBEEDATATYPE_H
#define ZIGBEEDATATYPE_H
#include "zigbee.h"
class ZigbeeDataType
{
public:
ZigbeeDataType(Zigbee::DataType dataType, const QByteArray &data = QByteArray());
Zigbee::DataType dataType() const;
QString name() const;
QString className() const;
QByteArray data() const;
static int typeLength(Zigbee::DataType dataType);
private:
Zigbee::DataType m_dataType = Zigbee::NoData;
QByteArray m_data;
QString m_name = "Unknown";
QString m_className = "Null";
int m_typeLength = 0;
};
#endif // ZIGBEEDATATYPE_H

View File

@ -351,7 +351,7 @@ void ZigbeeNetwork::loadNetwork()
for (int n = 0; n < inputClustersCount; n ++) { for (int n = 0; n < inputClustersCount; n ++) {
settings.setArrayIndex(n); settings.setArrayIndex(n);
Zigbee::ClusterId clusterId = static_cast<Zigbee::ClusterId>(settings.value("clusterId", 0).toUInt()); Zigbee::ClusterId clusterId = static_cast<Zigbee::ClusterId>(settings.value("clusterId", 0).toUInt());
ZigbeeCluster *cluster = new ZigbeeCluster(this, node, endpoint, clusterId, ZigbeeCluster::Input, endpoint); ZigbeeCluster *cluster = endpoint->createCluster(clusterId, ZigbeeCluster::Input);
//qCDebug(dcZigbeeNetwork()) << "Created" << cluster; //qCDebug(dcZigbeeNetwork()) << "Created" << cluster;
endpoint->m_inputClusters.insert(clusterId, cluster); endpoint->m_inputClusters.insert(clusterId, cluster);
} }
@ -361,7 +361,7 @@ void ZigbeeNetwork::loadNetwork()
for (int n = 0; n < outputClustersCount; n ++) { for (int n = 0; n < outputClustersCount; n ++) {
settings.setArrayIndex(n); settings.setArrayIndex(n);
Zigbee::ClusterId clusterId = static_cast<Zigbee::ClusterId>(settings.value("clusterId", 0).toUInt()); Zigbee::ClusterId clusterId = static_cast<Zigbee::ClusterId>(settings.value("clusterId", 0).toUInt());
ZigbeeCluster *cluster = new ZigbeeCluster(this, node, endpoint, clusterId, ZigbeeCluster::Output, endpoint); ZigbeeCluster *cluster = endpoint->createCluster(clusterId, ZigbeeCluster::Output);
//qCDebug(dcZigbeeNetwork()) << "Created" << cluster; //qCDebug(dcZigbeeNetwork()) << "Created" << cluster;
endpoint->m_outputClusters.insert(clusterId, cluster); endpoint->m_outputClusters.insert(clusterId, cluster);
} }

View File

@ -28,7 +28,7 @@
#include "zigbeenetworkmanager.h" #include "zigbeenetworkmanager.h"
#include "loggingcategory.h" #include "loggingcategory.h"
#include "deconz/zigbeenetworkdeconz.h" #include "backends/deconz/zigbeenetworkdeconz.h"
#include <QDateTime> #include <QDateTime>

View File

@ -257,91 +257,6 @@ void ZigbeeNode::setExtendedAddress(const ZigbeeAddress &extendedAddress)
m_extendedAddress = extendedAddress; m_extendedAddress = extendedAddress;
} }
//void ZigbeeNode::setNodeDescriptorRawData(const QByteArray nodeDescriptorRawData)
//{
// m_nodeDescriptorRawData = nodeDescriptorRawData;
// // Parse the raw data
// quint8 sequenceNumber = 0;
// quint8 status = 0;
// quint16 shortAddress = 0;
// quint16 manufacturerCode = 0;
// quint16 maximalRxSize = 0;
// quint16 maximalTxSize = 0;
// quint16 serverMask = 0;
// quint8 descriptorFlag = 0;
// quint8 macFlags = 0;
// quint8 maxBufferSize = 0;
// quint16 bitField = 0;
// QDataStream stream(&m_nodeDescriptorRawData, QIODevice::ReadOnly);
// stream >> sequenceNumber;
// stream >> status;
// stream >> shortAddress;
// stream >> manufacturerCode;
// stream >> maximalRxSize;
// stream >> maximalTxSize;
// stream >> serverMask;
// stream >> descriptorFlag;
// stream >> macFlags;
// stream >> maxBufferSize;
// stream >> bitField;
// // Set node data
// m_manufacturerCode = manufacturerCode;
// m_maximumRxSize = maximalRxSize;
// m_maximumTxSize = maximalTxSize;
// m_maximumBufferSize = maxBufferSize;
// setServerMask(serverMask);
// setMacCapabilitiesFlag(macFlags);
// setDescriptorFlag(descriptorFlag);
// // Parse bit field
// // 0-2 Bit = logical type, 0 = coordinator, 1 = router, 2 = end device
// if (!ZigbeeUtils::checkBitUint16(bitField, 0) && !ZigbeeUtils::checkBitUint16(bitField, 1)) {
// m_nodeType = NodeTypeCoordinator;
// } else if (!ZigbeeUtils::checkBitUint16(bitField, 0) && ZigbeeUtils::checkBitUint16(bitField, 1)) {
// m_nodeType = NodeTypeRouter;
// } else if (ZigbeeUtils::checkBitUint16(bitField, 0) && !ZigbeeUtils::checkBitUint16(bitField, 1)) {
// m_nodeType = NodeTypeEndDevice;
// }
// m_complexDescriptorAvailable = (bitField >> 3) & 0x0001;
// m_userDescriptorAvailable = (bitField >> 4) & 0x0001;
// qCDebug(dcZigbeeNode()) << "Node descriptor:";
// qCDebug(dcZigbeeNode()) << " Sequence number:" << ZigbeeUtils::convertByteToHexString(sequenceNumber);
// qCDebug(dcZigbeeNode()) << " Status:" << ZigbeeUtils::convertByteToHexString(status);
// qCDebug(dcZigbeeNode()) << " Node type:" << nodeType();
// qCDebug(dcZigbeeNode()) << " Short address:" << ZigbeeUtils::convertUint16ToHexString(shortAddress);
// qCDebug(dcZigbeeNode()) << " Manufacturer code:" << ZigbeeUtils::convertUint16ToHexString(manufacturerCode);
// qCDebug(dcZigbeeNode()) << " Maximum Rx size:" << ZigbeeUtils::convertUint16ToHexString(maximumRxSize());
// qCDebug(dcZigbeeNode()) << " Maximum Tx size:" << ZigbeeUtils::convertUint16ToHexString(maximumTxSize());
// qCDebug(dcZigbeeNode()) << " Maximum buffer size:" << ZigbeeUtils::convertByteToHexString(maximumBufferSize());
// qCDebug(dcZigbeeNode()) << " Server mask:" << ZigbeeUtils::convertUint16ToHexString(serverMask);
// qCDebug(dcZigbeeNode()) << " Primary Trust center:" << isPrimaryTrustCenter();
// qCDebug(dcZigbeeNode()) << " Backup Trust center:" << isBackupTrustCenter();
// qCDebug(dcZigbeeNode()) << " Primary Binding cache:" << isPrimaryBindingCache();
// qCDebug(dcZigbeeNode()) << " Backup Binding cache:" << isBackupBindingCache();
// qCDebug(dcZigbeeNode()) << " Primary Discovery cache:" << isPrimaryDiscoveryCache();
// qCDebug(dcZigbeeNode()) << " Backup Discovery cache:" << isBackupDiscoveryCache();
// qCDebug(dcZigbeeNode()) << " Network Manager:" << isNetworkManager();
// qCDebug(dcZigbeeNode()) << " Descriptor flag:" << ZigbeeUtils::convertByteToHexString(descriptorFlag);
// qCDebug(dcZigbeeNode()) << " Extended active endpoint list available:" << extendedActiveEndpointListAvailable();
// qCDebug(dcZigbeeNode()) << " Extended simple descriptor list available:" << extendedSimpleDescriptorListAvailable();
// qCDebug(dcZigbeeNode()) << " MAC flags:" << ZigbeeUtils::convertByteToHexString(macFlags);
// qCDebug(dcZigbeeNode()) << " Alternate PAN coordinator:" << alternatePanCoordinator();
// qCDebug(dcZigbeeNode()) << " Device type:" << deviceType();
// qCDebug(dcZigbeeNode()) << " Power source flag main power:" << powerSourceFlagMainPower();
// qCDebug(dcZigbeeNode()) << " Receiver on when idle:" << receiverOnWhenIdle();
// qCDebug(dcZigbeeNode()) << " Security capability:" << securityCapability();
// qCDebug(dcZigbeeNode()) << " Allocate address:" << allocateAddress();
// qCDebug(dcZigbeeNode()) << " Bit field:" << ZigbeeUtils::convertUint16ToHexString(bitField);
// qCDebug(dcZigbeeNode()) << " Complex desciptor available:" << complexDescriptorAvailable();
// qCDebug(dcZigbeeNode()) << " User desciptor available:" << userDescriptorAvailable();
//}
quint16 ZigbeeNode::serverMask() const quint16 ZigbeeNode::serverMask() const
{ {
return m_serverMask; return m_serverMask;
@ -704,11 +619,11 @@ void ZigbeeNode::initEndpoint(quint8 endpointId)
quint16 clusterId = 0; quint16 clusterId = 0;
stream >> clusterId; stream >> clusterId;
if (!endpoint->hasInputCluster(static_cast<Zigbee::ClusterId>(clusterId))) { if (!endpoint->hasInputCluster(static_cast<Zigbee::ClusterId>(clusterId))) {
endpoint->addInputCluster(new ZigbeeCluster(m_network, this, endpoint, static_cast<Zigbee::ClusterId>(clusterId), ZigbeeCluster::Input, endpoint)); endpoint->addInputCluster(endpoint->createCluster(static_cast<Zigbee::ClusterId>(clusterId), ZigbeeCluster::Input));
} }
qCDebug(dcZigbeeNode()) << " Cluster ID:" << ZigbeeUtils::convertUint16ToHexString(clusterId) << ZigbeeUtils::clusterIdToString(static_cast<Zigbee::ClusterId>(clusterId)); qCDebug(dcZigbeeNode()) << " Cluster ID:" << ZigbeeUtils::convertUint16ToHexString(clusterId) << ZigbeeUtils::clusterIdToString(static_cast<Zigbee::ClusterId>(clusterId));
} }
stream >> outputClusterCount; stream >> outputClusterCount;
qCDebug(dcZigbeeNode()) << " Output clusters: (" << outputClusterCount << ")"; qCDebug(dcZigbeeNode()) << " Output clusters: (" << outputClusterCount << ")";
@ -716,7 +631,7 @@ void ZigbeeNode::initEndpoint(quint8 endpointId)
quint16 clusterId = 0; quint16 clusterId = 0;
stream >> clusterId; stream >> clusterId;
if (!endpoint->hasOutputCluster(static_cast<Zigbee::ClusterId>(clusterId))) { if (!endpoint->hasOutputCluster(static_cast<Zigbee::ClusterId>(clusterId))) {
endpoint->addOutputCluster(new ZigbeeCluster(m_network, this, endpoint, static_cast<Zigbee::ClusterId>(clusterId), ZigbeeCluster::Output, endpoint)); endpoint->addOutputCluster(endpoint->createCluster(static_cast<Zigbee::ClusterId>(clusterId), ZigbeeCluster::Output));
} }
qCDebug(dcZigbeeNode()) << " Cluster ID:" << ZigbeeUtils::convertUint16ToHexString(clusterId) << ZigbeeUtils::clusterIdToString(static_cast<Zigbee::ClusterId>(clusterId)); qCDebug(dcZigbeeNode()) << " Cluster ID:" << ZigbeeUtils::convertUint16ToHexString(clusterId) << ZigbeeUtils::clusterIdToString(static_cast<Zigbee::ClusterId>(clusterId));
} }
@ -725,19 +640,81 @@ void ZigbeeNode::initEndpoint(quint8 endpointId)
if (m_uninitializedEndpoints.isEmpty()) { if (m_uninitializedEndpoints.isEmpty()) {
//if (m_shortAddress == 0) { if (m_shortAddress == 0) {
setState(StateInitialized); setState(StateInitialized);
return; return;
//} }
// Continue with the basic cluster attributes // Continue with the basic cluster attributes
//initBasicCluster(); initBasicCluster();
} }
}); });
} }
void ZigbeeNode::initBasicCluster() void ZigbeeNode::initBasicCluster()
{ {
ZigbeeClusterBasic *basicCluster = m_endpoints.first()->inputCluster<ZigbeeClusterBasic>(Zigbee::ClusterIdBasic);
if (!basicCluster) {
qCWarning(dcZigbeeNode()) << this << "could not find basic server cluster";
setState(StateInitialized);
return;
}
ZigbeeClusterBasic::Attribute attributeId = ZigbeeClusterBasic::AttributeManufacturerName;
qCDebug(dcZigbeeNode()) << "Reading attribute" << attributeId;
ZigbeeClusterReply *reply = basicCluster->readAttributes({static_cast<quint16>(attributeId)});
connect(reply, &ZigbeeClusterReply::finished, this, [this, basicCluster, reply, attributeId](){
if (reply->error() != ZigbeeClusterReply::ErrorNoError) {
qCWarning(dcZigbeeNode()) << "Error occured during initialization of" << this << "Failed to read basic cluster attribute" << attributeId << reply->error();
} else {
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);
}
}
ZigbeeClusterBasic::Attribute attributeId = ZigbeeClusterBasic::AttributeModelIdentifier;
qCDebug(dcZigbeeNode()) << "Reading attribute" << attributeId;
ZigbeeClusterReply *reply = basicCluster->readAttributes({static_cast<quint16>(attributeId)});
connect(reply, &ZigbeeClusterReply::finished, this, [this, basicCluster, reply, attributeId](){
if (reply->error() != ZigbeeClusterReply::ErrorNoError) {
qCWarning(dcZigbeeNode()) << "Error occured during initialization of" << this << "Failed to read basic cluster attribute" << attributeId << reply->error();
} else {
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);
}
}
ZigbeeClusterBasic::Attribute attributeId = ZigbeeClusterBasic::AttributeSwBuildId;
qCDebug(dcZigbeeNode()) << "Reading attribute" << attributeId;
ZigbeeClusterReply *reply = basicCluster->readAttributes({static_cast<quint16>(attributeId)});
connect(reply, &ZigbeeClusterReply::finished, this, [this, basicCluster, reply, attributeId](){
if (reply->error() != ZigbeeClusterReply::ErrorNoError) {
qCWarning(dcZigbeeNode()) << "Error occured during initialization of" << this << "Failed to read basic cluster attribute" << attributeId << reply->error();
} else {
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);
}
}
// Finished with reading basic cluster, the node is initialized. TODO: read other cluster information
setState(StateInitialized);
});
});
});
} }
@ -748,41 +725,6 @@ void ZigbeeNode::onClusterAttributeChanged(const ZigbeeClusterAttribute &attribu
emit clusterAttributeChanged(cluster, attribute); emit clusterAttributeChanged(cluster, attribute);
} }
//void ZigbeeNode::onRequestUserDescriptorFinished()
//{
// 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 = static_cast<quint16>(reply->additionalMessage().data().at(2));
// nwkAddress <<= 8;
// nwkAddress |= reply->additionalMessage().data().at(3);
// quint8 length = static_cast<quint8>(reply->additionalMessage().data().at(4));
// QByteArray data;
// if (length > 0) {
// data = reply->additionalMessage().data().mid(5, length);
// }
// qCDebug(dcZigbeeNode()) << "User descriptor:";
// qCDebug(dcZigbeeNode()) << " Sequence number:" << ZigbeeUtils::convertByteToHexString(sequenceNumber);
// qCDebug(dcZigbeeNode()) << " Status:" << ZigbeeUtils::convertByteToHexString(status);
// qCDebug(dcZigbeeNode()) << " Attribute address:" << ZigbeeUtils::convertUint16ToHexString(nwkAddress);
// qCDebug(dcZigbeeNode()) << " Lenght:" << ZigbeeUtils::convertByteToHexString(length);
// qCDebug(dcZigbeeNode()) << " Data:" << data;
//}
QDebug operator<<(QDebug debug, ZigbeeNode *node) QDebug operator<<(QDebug debug, ZigbeeNode *node)
{ {
debug.nospace().noquote() << "ZigbeeNode(" << ZigbeeUtils::convertUint16ToHexString(node->shortAddress()); debug.nospace().noquote() << "ZigbeeNode(" << ZigbeeUtils::convertUint16ToHexString(node->shortAddress());

View File

@ -284,7 +284,6 @@ private:
QList<quint8> m_uninitializedEndpoints; QList<quint8> m_uninitializedEndpoints;
QList<quint16> m_uninitalizedBasicClusterAttributes; QList<quint16> m_uninitalizedBasicClusterAttributes;
signals: signals:
void stateChanged(State state); void stateChanged(State state);
void connectedChanged(bool connected); void connectedChanged(bool connected);

View File

@ -30,6 +30,8 @@
#include "zigbeenode.h" #include "zigbeenode.h"
#include "loggingcategory.h" #include "loggingcategory.h"
#include "zcl/general/zigbeeclusterbasic.h"
quint8 ZigbeeNodeEndpoint::endpointId() const quint8 ZigbeeNodeEndpoint::endpointId() const
{ {
return m_endpointId; return m_endpointId;
@ -146,6 +148,17 @@ void ZigbeeNodeEndpoint::setSoftwareBuildId(const QString &softwareBuildId)
emit softwareBuildIdChanged(m_softwareBuildId); emit softwareBuildIdChanged(m_softwareBuildId);
} }
ZigbeeCluster *ZigbeeNodeEndpoint::createCluster(Zigbee::ClusterId clusterId, ZigbeeCluster::Direction direction)
{
switch (clusterId) {
case Zigbee::ClusterIdBasic:
return new ZigbeeClusterBasic(m_network, m_node, this, direction, this);
break;
default:
return new ZigbeeCluster(m_network, m_node, this, clusterId, direction, this);
}
}
void ZigbeeNodeEndpoint::addInputCluster(ZigbeeCluster *cluster) void ZigbeeNodeEndpoint::addInputCluster(ZigbeeCluster *cluster)
{ {
m_inputClusters.insert(cluster->clusterId(), cluster); m_inputClusters.insert(cluster->clusterId(), cluster);
@ -156,21 +169,6 @@ void ZigbeeNodeEndpoint::addOutputCluster(ZigbeeCluster *cluster)
m_outputClusters.insert(cluster->clusterId(), cluster); m_outputClusters.insert(cluster->clusterId(), cluster);
} }
ZigbeeNetworkReply *ZigbeeNodeEndpoint::createNetworkReply(const ZigbeeNetworkRequest &request)
{
ZigbeeNetworkReply *reply = new ZigbeeNetworkReply(request, this);
// Make sure the reply will be deleted
connect(reply, &ZigbeeNetworkReply::finished, reply, &ZigbeeNetworkReply::deleteLater);
return reply;
}
void ZigbeeNodeEndpoint::finishNetworkReply(ZigbeeNetworkReply *reply, ZigbeeNetworkReply::Error error, Zigbee::ZigbeeApsStatus zigbeeApsStatus)
{
reply->m_error = error;
reply->m_zigbeeApsStatus = zigbeeApsStatus;
reply->finished();
}
ZigbeeCluster *ZigbeeNodeEndpoint::getOutputCluster(Zigbee::ClusterId clusterId) const ZigbeeCluster *ZigbeeNodeEndpoint::getOutputCluster(Zigbee::ClusterId clusterId) const
{ {
return m_outputClusters.value(clusterId); return m_outputClusters.value(clusterId);

View File

@ -32,9 +32,11 @@
#include "zigbee.h" #include "zigbee.h"
#include "zigbeeaddress.h" #include "zigbeeaddress.h"
#include "zigbeecluster.h"
#include "zigbeenetworkreply.h" #include "zigbeenetworkreply.h"
#include "zcl/zigbeecluster.h"
#include "zcl/general/zigbeeclusterbasic.h"
class ZigbeeNode; class ZigbeeNode;
class ZigbeeNetwork; class ZigbeeNetwork;
@ -74,6 +76,24 @@ public:
ZigbeeCluster *getOutputCluster(Zigbee::ClusterId clusterId) const; ZigbeeCluster *getOutputCluster(Zigbee::ClusterId clusterId) const;
bool hasOutputCluster(Zigbee::ClusterId clusterId) const; bool hasOutputCluster(Zigbee::ClusterId clusterId) const;
template<typename T>
inline T* inputCluster(Zigbee::ClusterId clusterId)
{
if (!hasInputCluster(clusterId))
return nullptr;
return qobject_cast<T *>(getInputCluster(clusterId));
}
template<typename T>
inline T* outputCluster(Zigbee::ClusterId clusterId)
{
if (!hasOutputCluster(clusterId))
return nullptr;
return qobject_cast<T *>(getOutputCluster(clusterId));
}
private: private:
explicit ZigbeeNodeEndpoint(ZigbeeNetwork *network, ZigbeeNode *node, quint8 endpointId, QObject *parent = nullptr); explicit ZigbeeNodeEndpoint(ZigbeeNetwork *network, ZigbeeNode *node, quint8 endpointId, QObject *parent = nullptr);
@ -84,7 +104,6 @@ private:
quint16 m_deviceId = 0; quint16 m_deviceId = 0;
quint8 m_deviceVersion = 0; quint8 m_deviceVersion = 0;
QHash<Zigbee::ClusterId, ZigbeeCluster *> m_inputClusters; QHash<Zigbee::ClusterId, ZigbeeCluster *> m_inputClusters;
QHash<Zigbee::ClusterId, ZigbeeCluster *> m_outputClusters; QHash<Zigbee::ClusterId, ZigbeeCluster *> m_outputClusters;
@ -99,15 +118,14 @@ private:
// Cluster commands // Cluster commands
//virtual void setClusterAttribute(Zigbee::ClusterId clusterId, const ZigbeeClusterAttribute &attribute = ZigbeeClusterAttribute()) = 0; //virtual void setClusterAttribute(Zigbee::ClusterId clusterId, const ZigbeeClusterAttribute &attribute = ZigbeeClusterAttribute()) = 0;
ZigbeeCluster *createCluster(Zigbee::ClusterId clusterId, ZigbeeCluster::Direction direction);
void addInputCluster(ZigbeeCluster *cluster); void addInputCluster(ZigbeeCluster *cluster);
void addOutputCluster(ZigbeeCluster *cluster); void addOutputCluster(ZigbeeCluster *cluster);
// Network reply methods
ZigbeeNetworkReply *createNetworkReply(const ZigbeeNetworkRequest &request = ZigbeeNetworkRequest());
void finishNetworkReply(ZigbeeNetworkReply *reply, ZigbeeNetworkReply::Error error = ZigbeeNetworkReply::ErrorNoError, Zigbee::ZigbeeApsStatus zigbeeApsStatus = Zigbee::ZigbeeApsStatusSuccess);
signals: signals:
void clusterAttributeChanged(ZigbeeCluster *cluster, const ZigbeeClusterAttribute &attribute); void clusterAttributeChanged(ZigbeeCluster *cluster, const ZigbeeClusterAttribute &attribute);
void manufacturerNameChanged(const QString &manufacturerName); void manufacturerNameChanged(const QString &manufacturerName);
void modelIdentifierChanged(const QString &modelIdentifier); void modelIdentifierChanged(const QString &modelIdentifier);
void softwareBuildIdChanged(const QString &softwareBuildId); void softwareBuildIdChanged(const QString &softwareBuildId);

View File

@ -276,8 +276,11 @@ QString ZigbeeUtils::clusterIdToString(const Zigbee::ClusterId &clusterId)
QMetaEnum metaEnum = metaObject.enumerator(metaObject.indexOfEnumerator("ClusterId")); QMetaEnum metaEnum = metaObject.enumerator(metaObject.indexOfEnumerator("ClusterId"));
QString enumString = metaEnum.valueToKey(clusterId); QString enumString = metaEnum.valueToKey(clusterId);
QString clusterName = enumString.remove("Zigbee::ClusterId(ClusterId").remove(")").append(QString("(%1)").arg(ZigbeeUtils::convertUint16ToHexString(clusterId)));
if (clusterName.isEmpty())
clusterName = "Unknown";
return enumString.remove("Zigbee::ClusterId(ClusterId").remove(")").append(QString("(%1)").arg(ZigbeeUtils::convertUint16ToHexString(clusterId))); return clusterName;
} }
QString ZigbeeUtils::profileIdToString(const Zigbee::ZigbeeProfile &profileId) QString ZigbeeUtils::profileIdToString(const Zigbee::ZigbeeProfile &profileId)

View File

@ -36,7 +36,7 @@
#include <QBitArray> #include <QBitArray>
#include "zigbee.h" #include "zigbee.h"
#include "zigbeecluster.h" #include "zcl/zigbeecluster.h"
class ZigbeeUtils class ZigbeeUtils