Implement new clusters and restructure ZCL frame flow and implement attribute reports
This commit is contained in:
parent
7e5643a1cf
commit
69ed7e3496
@ -814,7 +814,7 @@ void ZigbeeBridgeControllerDeconz::processDataIndication(const QByteArray &data)
|
||||
stream.setByteOrder(QDataStream::LittleEndian);
|
||||
quint16 payloadLenght = 0; quint8 deviceStateFlag = 0; quint8 reserved = 0; quint16 asduLength = 0;
|
||||
|
||||
DeconzApsDataIndication indication;
|
||||
Zigbee::ApsdeDataIndication indication;
|
||||
stream >> payloadLenght >> deviceStateFlag;
|
||||
stream >> indication.destinationAddressMode;
|
||||
Zigbee::DestinationAddressMode destinationAddressMode = static_cast<Zigbee::DestinationAddressMode>(indication.destinationAddressMode);
|
||||
@ -844,8 +844,8 @@ void ZigbeeBridgeControllerDeconz::processDataIndication(const QByteArray &data)
|
||||
stream >> reserved >> reserved >> indication.lqi >> reserved >> reserved >> reserved >> reserved >> indication.rssi;
|
||||
|
||||
// Print the information for debugging
|
||||
qCDebug(dcZigbeeAps()) << "APSDE-DATA.indication" << indication;
|
||||
qCDebug(dcZigbeeController()) << indication;
|
||||
qCDebug(dcZigbeeAps()) << "APSDE-DATA.indication" << indication;
|
||||
|
||||
emit apsDataIndicationReceived(indication);
|
||||
|
||||
@ -860,7 +860,7 @@ void ZigbeeBridgeControllerDeconz::processDataConfirm(const QByteArray &data)
|
||||
{
|
||||
QDataStream stream(data);
|
||||
stream.setByteOrder(QDataStream::LittleEndian);
|
||||
DeconzApsDataConfirm confirm;
|
||||
Zigbee::ApsdeDataConfirm confirm;
|
||||
quint16 payloadLenght = 0; quint8 deviceStateFlag = 0;
|
||||
stream >> payloadLenght >> deviceStateFlag;
|
||||
stream >> confirm.requestId >> confirm.destinationAddressMode;
|
||||
@ -1006,62 +1006,6 @@ QDebug operator<<(QDebug debug, const DeconzDeviceState &deviceState)
|
||||
return debug.space();
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const DeconzApsDataConfirm &confirm)
|
||||
{
|
||||
debug.nospace() << "APSDE-DATA.confirm(";
|
||||
debug.nospace() << "Request ID: " << confirm.requestId << ", ";
|
||||
|
||||
if (confirm.destinationAddressMode == Zigbee::DestinationAddressModeGroup)
|
||||
debug.nospace() << "Group address:" << ZigbeeUtils::convertUint16ToHexString(confirm.destinationShortAddress) << ", ";
|
||||
|
||||
if (confirm.destinationAddressMode == Zigbee::DestinationAddressModeShortAddress)
|
||||
debug.nospace() << "NWK address:" << ZigbeeUtils::convertUint16ToHexString(confirm.destinationShortAddress) << ", ";
|
||||
|
||||
if (confirm.destinationAddressMode == Zigbee::DestinationAddressModeIeeeAddress)
|
||||
debug.nospace() << "IEEE address:" << ZigbeeAddress(confirm.destinationIeeeAddress).toString() << ", ";
|
||||
|
||||
debug.nospace() << "Destination EP:" << ZigbeeUtils::convertByteToHexString(confirm.destinationEndpoint) << ", ";
|
||||
debug.nospace() << "Source EP:" << ZigbeeUtils::convertByteToHexString(confirm.sourceEndpoint) << ", ";
|
||||
debug.nospace() << static_cast<Zigbee::ZigbeeStatus>(confirm.zigbeeStatusCode);
|
||||
debug.nospace() << ")";
|
||||
|
||||
return debug.space();
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const DeconzApsDataIndication &indication)
|
||||
{
|
||||
debug.nospace() << "APSDE-DATA.indication(";
|
||||
if (indication.destinationAddressMode == Zigbee::DestinationAddressModeGroup)
|
||||
debug.nospace() << "Group address:" << ZigbeeUtils::convertUint16ToHexString(indication.destinationShortAddress) << ", ";
|
||||
|
||||
if (indication.destinationAddressMode == Zigbee::DestinationAddressModeShortAddress)
|
||||
debug.nospace() << "NWK address:" << ZigbeeUtils::convertUint16ToHexString(indication.destinationShortAddress) << ", ";
|
||||
|
||||
if (indication.destinationAddressMode == Zigbee::DestinationAddressModeIeeeAddress)
|
||||
debug.nospace() << "IEEE address:" << ZigbeeAddress(indication.destinationIeeeAddress).toString() << ", ";
|
||||
|
||||
debug.nospace() << "Destination EP:" << ZigbeeUtils::convertByteToHexString(indication.destinationEndpoint) << ", ";
|
||||
debug.nospace() << "Source EP:" << ZigbeeUtils::convertByteToHexString(indication.sourceEndpoint) << ", ";
|
||||
|
||||
if (indication.sourceAddressMode == Zigbee::SourceAddressModeShortAddress || indication.sourceAddressMode == Zigbee::SourceAddressModeShortAndIeeeAddress)
|
||||
debug.nospace() << "Source NWK address:" << ZigbeeUtils::convertUint16ToHexString(indication.sourceShortAddress) << ", ";
|
||||
|
||||
if (indication.sourceAddressMode == Zigbee::SourceAddressModeIeeeAddress || indication.sourceAddressMode == Zigbee::SourceAddressModeShortAndIeeeAddress)
|
||||
debug.nospace() << "Source IEEE address:" << ZigbeeAddress(indication.sourceIeeeAddress).toString() << ", ";
|
||||
|
||||
debug.nospace() << static_cast<Zigbee::ZigbeeProfile>(indication.profileId) << ", ";
|
||||
if (indication.profileId == static_cast<quint16>(Zigbee::ZigbeeProfileDevice)) {
|
||||
debug.nospace() << static_cast<ZigbeeDeviceProfile::ZdoCommand>(indication.clusterId) << ", ";
|
||||
} else {
|
||||
debug.nospace() << static_cast<Zigbee::ClusterId>(indication.clusterId) << ", ";
|
||||
}
|
||||
|
||||
debug.nospace() << "ASDU: " << ZigbeeUtils::convertByteArrayToHexString(indication.asdu) << ", ";
|
||||
debug.nospace() << "LQI: " << indication.lqi << ", ";
|
||||
debug.nospace() << "RSSI: " << indication.rssi << "dBm)";
|
||||
return debug.space();
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const DeconzNetworkConfiguration &configuration)
|
||||
{
|
||||
debug.nospace() << "Network configuration:" << endl;
|
||||
|
||||
@ -70,34 +70,6 @@ typedef struct DeconzDeviceState {
|
||||
bool apsDataRequestFreeSlots = false;
|
||||
} DeconzDeviceState;
|
||||
|
||||
|
||||
// Basic struct for interface data. Default Response
|
||||
typedef struct DeconzApsDataConfirm {
|
||||
quint8 requestId = 0;
|
||||
quint8 destinationAddressMode = Zigbee::DestinationAddressModeShortAddress;
|
||||
quint16 destinationShortAddress = 0;
|
||||
quint64 destinationIeeeAddress;
|
||||
quint8 destinationEndpoint = 0;
|
||||
quint8 sourceEndpoint = 0;
|
||||
quint8 zigbeeStatusCode = 0;
|
||||
} DeconzApsDataConfirm;
|
||||
|
||||
typedef struct DeconzApsDataIndication {
|
||||
quint8 destinationAddressMode = 0;
|
||||
quint16 destinationShortAddress = 0;
|
||||
quint64 destinationIeeeAddress = 0;
|
||||
quint8 destinationEndpoint = 0;
|
||||
quint8 sourceAddressMode = 0;
|
||||
quint16 sourceShortAddress = 0;
|
||||
quint64 sourceIeeeAddress = 0;
|
||||
quint8 sourceEndpoint = 0;
|
||||
quint16 profileId = 0;
|
||||
quint16 clusterId = 0;
|
||||
QByteArray asdu;
|
||||
quint8 lqi = 0;
|
||||
qint8 rssi = 0;
|
||||
} DeconzApsDataIndication;
|
||||
|
||||
class ZigbeeBridgeControllerDeconz : public ZigbeeBridgeController
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -164,8 +136,8 @@ signals:
|
||||
void networkStateChanged(Deconz::NetworkState networkState);
|
||||
void networkConfigurationParameterChanged(const DeconzNetworkConfiguration &networkConfiguration);
|
||||
|
||||
void apsDataConfirmReceived(const DeconzApsDataConfirm &confirm);
|
||||
void apsDataIndicationReceived(const DeconzApsDataIndication &indication);
|
||||
void apsDataConfirmReceived(const Zigbee::ApsdeDataConfirm &confirm);
|
||||
void apsDataIndicationReceived(const Zigbee::ApsdeDataIndication &indication);
|
||||
|
||||
private slots:
|
||||
void onInterfaceAvailableChanged(bool available);
|
||||
@ -179,8 +151,6 @@ public slots:
|
||||
};
|
||||
|
||||
QDebug operator<<(QDebug debug, const DeconzDeviceState &deviceState);
|
||||
QDebug operator<<(QDebug debug, const DeconzApsDataConfirm &confirm);
|
||||
QDebug operator<<(QDebug debug, const DeconzApsDataIndication &indication);
|
||||
QDebug operator<<(QDebug debug, const DeconzNetworkConfiguration &configuration);
|
||||
|
||||
|
||||
|
||||
@ -36,7 +36,6 @@ ZigbeeNetworkDeconz::ZigbeeNetworkDeconz(QObject *parent) :
|
||||
ZigbeeNetwork(parent)
|
||||
{
|
||||
m_controller = new ZigbeeBridgeControllerDeconz(this);
|
||||
//connect(m_controller, &ZigbeeBridgeControllerDeconz::messageReceived, this, &ZigbeeNetworkDeconz::onMessageReceived);
|
||||
connect(m_controller, &ZigbeeBridgeControllerDeconz::availableChanged, this, &ZigbeeNetworkDeconz::onControllerAvailableChanged);
|
||||
connect(m_controller, &ZigbeeBridgeControllerDeconz::apsDataConfirmReceived, this, &ZigbeeNetworkDeconz::onApsDataConfirmReceived);
|
||||
connect(m_controller, &ZigbeeBridgeControllerDeconz::apsDataIndicationReceived, this, &ZigbeeNetworkDeconz::onApsDataIndicationReceived);
|
||||
@ -54,11 +53,10 @@ ZigbeeNetworkDeconz::ZigbeeNetworkDeconz(QObject *parent) :
|
||||
|
||||
ZigbeeBridgeController *ZigbeeNetworkDeconz::bridgeController() const
|
||||
{
|
||||
if (m_controller) {
|
||||
return qobject_cast<ZigbeeBridgeController *>(m_controller);
|
||||
}
|
||||
if (!m_controller)
|
||||
return nullptr;
|
||||
|
||||
return nullptr;
|
||||
return qobject_cast<ZigbeeBridgeController *>(m_controller);
|
||||
}
|
||||
|
||||
ZigbeeNetworkReply *ZigbeeNetworkDeconz::sendRequest(const ZigbeeNetworkRequest &request)
|
||||
@ -308,8 +306,10 @@ void ZigbeeNetworkDeconz::setCreateNetworkState(ZigbeeNetworkDeconz::CreateNetwo
|
||||
}
|
||||
}
|
||||
|
||||
void ZigbeeNetworkDeconz::handleZigbeeDeviceProfileIndication(const DeconzApsDataIndication &indication)
|
||||
void ZigbeeNetworkDeconz::handleZigbeeDeviceProfileIndication(const Zigbee::ApsdeDataIndication &indication)
|
||||
{
|
||||
//qCDebug(dcZigbeeNetwork()) << "Handle ZDP indication" << indication;
|
||||
|
||||
// Check if this is a device announcement
|
||||
if (indication.clusterId == ZigbeeDeviceProfile::DeviceAnnounce) {
|
||||
QDataStream stream(indication.asdu);
|
||||
@ -320,77 +320,40 @@ void ZigbeeNetworkDeconz::handleZigbeeDeviceProfileIndication(const DeconzApsDat
|
||||
return;
|
||||
}
|
||||
|
||||
if (indication.destinationShortAddress == Zigbee::BroadcastAddressAllNodes ||
|
||||
indication.destinationShortAddress == Zigbee::BroadcastAddressAllRouters ||
|
||||
indication.destinationShortAddress == Zigbee::BroadcastAddressAllNonSleepingNodes) {
|
||||
qCDebug(dcZigbeeNetwork()) << "Received unhandled broadcast ZDO indication" << indication;
|
||||
|
||||
// FIXME: check what we can do with such messages like permit join
|
||||
return;
|
||||
}
|
||||
|
||||
ZigbeeNode *node = getZigbeeNode(indication.sourceShortAddress);
|
||||
if (!node) {
|
||||
qCWarning(dcZigbeeNetwork()) << "Received a ZDO indication for an unrecognized node. There is no such node in the system. Ignoring indication" << indication;
|
||||
// FIXME: check if we want to create it since the device definitly exists within the network
|
||||
return;
|
||||
}
|
||||
|
||||
node->deviceObject()->processApsDataIndication(indication.destinationEndpoint, indication.sourceEndpoint, indication.clusterId, indication.asdu, indication.lqi, indication.rssi);
|
||||
// Let the node device object handle this (ZDP)
|
||||
node->deviceObject()->processApsDataIndication(indication);
|
||||
}
|
||||
|
||||
void ZigbeeNetworkDeconz::handleZigbeeLightLinkIndication(const DeconzApsDataIndication &indication)
|
||||
void ZigbeeNetworkDeconz::handleZigbeeClusterLibraryIndication(const Zigbee::ApsdeDataIndication &indication)
|
||||
{
|
||||
ZigbeeClusterLibrary::Frame frame = ZigbeeClusterLibrary::parseFrameData(indication.asdu);
|
||||
//qCDebug(dcZigbeeNetwork()) << "ZCL ZLL" << indication << frame;
|
||||
//qCDebug(dcZigbeeNetwork()) << "Handle ZCL indication" << 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;
|
||||
// FIXME: maybe create and init the node, since it is in the network, but not recognized
|
||||
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::handleZigbeeHomeAutomationIndication(const DeconzApsDataIndication &indication)
|
||||
{
|
||||
ZigbeeClusterLibrary::Frame frame = ZigbeeClusterLibrary::parseFrameData(indication.asdu);
|
||||
//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);
|
||||
node->handleZigbeeClusterLibraryIndication(indication);
|
||||
}
|
||||
|
||||
void ZigbeeNetworkDeconz::setPermitJoiningInternal(bool permitJoining)
|
||||
@ -632,7 +595,7 @@ void ZigbeeNetworkDeconz::onPermitJoinRefreshTimout()
|
||||
setPermitJoiningInternal(true);
|
||||
}
|
||||
|
||||
void ZigbeeNetworkDeconz::onApsDataConfirmReceived(const DeconzApsDataConfirm &confirm)
|
||||
void ZigbeeNetworkDeconz::onApsDataConfirmReceived(const Zigbee::ApsdeDataConfirm &confirm)
|
||||
{
|
||||
ZigbeeNetworkReply *reply = m_pendingReplies.value(confirm.requestId);
|
||||
if (!reply) {
|
||||
@ -643,7 +606,7 @@ void ZigbeeNetworkDeconz::onApsDataConfirmReceived(const DeconzApsDataConfirm &c
|
||||
setReplyResponseError(reply, static_cast<Zigbee::ZigbeeApsStatus>(confirm.zigbeeStatusCode));
|
||||
}
|
||||
|
||||
void ZigbeeNetworkDeconz::onApsDataIndicationReceived(const DeconzApsDataIndication &indication)
|
||||
void ZigbeeNetworkDeconz::onApsDataIndicationReceived(const Zigbee::ApsdeDataIndication &indication)
|
||||
{
|
||||
// Check if this indocation is related to any pending reply
|
||||
if (indication.profileId == Zigbee::ZigbeeProfileDevice) {
|
||||
@ -651,18 +614,8 @@ void ZigbeeNetworkDeconz::onApsDataIndicationReceived(const DeconzApsDataIndicat
|
||||
return;
|
||||
}
|
||||
|
||||
if (indication.profileId == Zigbee::ZigbeeProfileLightLink) {
|
||||
|
||||
}
|
||||
|
||||
if (indication.profileId == Zigbee::ZigbeeProfileHomeAutomation) {
|
||||
handleZigbeeHomeAutomationIndication(indication);
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: handle it
|
||||
|
||||
qCDebug(dcZigbeeNetwork()) << "Unhandled indication" << indication;
|
||||
// Else let the node handle this indication
|
||||
handleZigbeeClusterLibraryIndication(indication);
|
||||
}
|
||||
|
||||
void ZigbeeNetworkDeconz::onDeviceAnnounced(quint16 shortAddress, ZigbeeAddress ieeeAddress, quint8 macCapabilities)
|
||||
|
||||
@ -72,15 +72,10 @@ private:
|
||||
void setCreateNetworkState(CreateNetworkState state);
|
||||
|
||||
// ZDO
|
||||
void handleZigbeeDeviceProfileIndication(const DeconzApsDataIndication &indication);
|
||||
void handleZigbeeDeviceProfileIndication(const Zigbee::ApsdeDataIndication &indication);
|
||||
|
||||
// ZZL
|
||||
void handleZigbeeLightLinkIndication(const DeconzApsDataIndication &indication);
|
||||
|
||||
// HA
|
||||
void handleZigbeeHomeAutomationIndication(const DeconzApsDataIndication &indication);
|
||||
|
||||
// GP
|
||||
// ZCL
|
||||
void handleZigbeeClusterLibraryIndication(const Zigbee::ApsdeDataIndication &indication);
|
||||
|
||||
protected:
|
||||
void setPermitJoiningInternal(bool permitJoining) override;
|
||||
@ -91,8 +86,8 @@ private slots:
|
||||
void onPollNetworkStateTimeout();
|
||||
void onPermitJoinRefreshTimout();
|
||||
|
||||
void onApsDataConfirmReceived(const DeconzApsDataConfirm &confirm);
|
||||
void onApsDataIndicationReceived(const DeconzApsDataIndication &indication);
|
||||
void onApsDataConfirmReceived(const Zigbee::ApsdeDataConfirm &confirm);
|
||||
void onApsDataIndicationReceived(const Zigbee::ApsdeDataIndication &indication);
|
||||
|
||||
void onDeviceAnnounced(quint16 shortAddress, ZigbeeAddress ieeeAddress, quint8 macCapabilities);
|
||||
|
||||
|
||||
@ -9,6 +9,8 @@ SOURCES += \
|
||||
backends/deconz/zigbeebridgecontrollerdeconz.cpp \
|
||||
backends/deconz/zigbeenetworkdeconz.cpp \
|
||||
zcl/general/zigbeeclusteronoff.cpp \
|
||||
zcl/measurement/zigbeeclusterrelativehumiditymeasurement.cpp \
|
||||
zcl/measurement/zigbeeclustertemperaturemeasurement.cpp \
|
||||
zcl/zigbeecluster.cpp \
|
||||
zcl/zigbeeclusterattribute.cpp \
|
||||
zcl/zigbeeclusterlibrary.cpp \
|
||||
@ -51,6 +53,8 @@ HEADERS += \
|
||||
backends/deconz/zigbeebridgecontrollerdeconz.h \
|
||||
backends/deconz/zigbeenetworkdeconz.h \
|
||||
zcl/general/zigbeeclusteronoff.h \
|
||||
zcl/measurement/zigbeeclusterrelativehumiditymeasurement.h \
|
||||
zcl/measurement/zigbeeclustertemperaturemeasurement.h \
|
||||
zcl/zigbeecluster.h \
|
||||
zcl/zigbeeclusterattribute.h \
|
||||
zcl/zigbeeclusterlibrary.h \
|
||||
|
||||
@ -27,10 +27,11 @@
|
||||
|
||||
#include "loggingcategory.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(dcZigbeeNetwork, "ZigbeeNetwork")
|
||||
Q_LOGGING_CATEGORY(dcZigbeeNode, "ZigbeeNode")
|
||||
Q_LOGGING_CATEGORY(dcZigbeeAps, "ZigbeeAps")
|
||||
Q_LOGGING_CATEGORY(dcZigbeeNode, "ZigbeeNode")
|
||||
Q_LOGGING_CATEGORY(dcZigbeeNetwork, "ZigbeeNetwork")
|
||||
Q_LOGGING_CATEGORY(dcZigbeeCluster, "ZigbeeCluster")
|
||||
Q_LOGGING_CATEGORY(dcZigbeeEndpoint, "ZigbeeEndpoint")
|
||||
Q_LOGGING_CATEGORY(dcZigbeeInterface, "ZigbeeInterface")
|
||||
Q_LOGGING_CATEGORY(dcZigbeeController, "ZigbeeController")
|
||||
Q_LOGGING_CATEGORY(dcZigbeeDeviceObject, "ZigbeeDeviceObject")
|
||||
|
||||
@ -31,10 +31,11 @@
|
||||
#include <QDebug>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcZigbeeNetwork)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcZigbeeNode)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcZigbeeAps)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcZigbeeNode)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcZigbeeNetwork)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcZigbeeCluster)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcZigbeeEndpoint)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcZigbeeInterface)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcZigbeeController)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcZigbeeDeviceObject)
|
||||
|
||||
@ -28,6 +28,8 @@
|
||||
#include "zigbeeclusterbasic.h"
|
||||
#include "loggingcategory.h"
|
||||
|
||||
#include <QDataStream>
|
||||
|
||||
ZigbeeClusterBasic::ZigbeeClusterBasic(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent) :
|
||||
ZigbeeCluster(network, node, endpoint, Zigbee::ClusterIdBasic, direction, parent)
|
||||
{
|
||||
|
||||
@ -199,6 +199,8 @@ public:
|
||||
|
||||
explicit ZigbeeClusterBasic(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent = nullptr);
|
||||
|
||||
// TODO: reset all clusters to factory defaults command 0x00, optional
|
||||
|
||||
private:
|
||||
void setAttribute(const ZigbeeClusterAttribute &attribute) override;
|
||||
|
||||
|
||||
@ -138,6 +138,56 @@ ZigbeeClusterReply *ZigbeeClusterOnOff::commandOn()
|
||||
return zclReply;
|
||||
}
|
||||
|
||||
ZigbeeClusterReply *ZigbeeClusterOnOff::commandToggle()
|
||||
{
|
||||
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::CommandToggle;
|
||||
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())) {
|
||||
@ -153,7 +203,39 @@ void ZigbeeClusterOnOff::setAttribute(const ZigbeeClusterAttribute &attribute)
|
||||
|
||||
void ZigbeeClusterOnOff::processDataIndication(ZigbeeClusterLibrary::Frame frame)
|
||||
{
|
||||
qCDebug(dcZigbeeCluster()) << "Processing cluster frame" << m_node << m_endpoint << this << frame;
|
||||
|
||||
// Increase the tsn for continuouse id increasing on both sides
|
||||
m_transactionSequenceNumber = frame.header.transactionSequenceNumber;
|
||||
|
||||
switch (m_direction) {
|
||||
case Client:
|
||||
// If the client cluster sends data to a server cluster (independent which), the command was executed on the device like button pressed
|
||||
if (frame.header.frameControl.direction == ZigbeeClusterLibrary::DirectionClientToServer) {
|
||||
// Read the payload which is
|
||||
Command command = static_cast<Command>(frame.header.command);
|
||||
qCDebug(dcZigbeeCluster()) << "Command sent from" << m_node << m_endpoint << this << command;
|
||||
switch (command) {
|
||||
case CommandOn:
|
||||
emit commandSent(CommandOn);
|
||||
break;
|
||||
case CommandOff:
|
||||
emit commandSent(CommandOff);
|
||||
break;
|
||||
case CommandToggle:
|
||||
emit commandSent(CommandToggle);
|
||||
break;
|
||||
default:
|
||||
qCWarning(dcZigbeeCluster()) << "Unhandled command sent from" << m_node << m_endpoint << this << command;
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case Server:
|
||||
// keep it unhandled if not parsed yet in order to warn about the handled indication
|
||||
break;
|
||||
}
|
||||
|
||||
qCWarning(dcZigbeeCluster()) << "Unhandled ZCL indication in" << m_node << m_endpoint << this << frame;
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@ public:
|
||||
|
||||
ZigbeeClusterReply *commandOff();
|
||||
ZigbeeClusterReply *commandOn();
|
||||
// ZigbeeClusterReply *commandToggle();
|
||||
ZigbeeClusterReply *commandToggle();
|
||||
// ZigbeeClusterReply *commandOffWithEffect();
|
||||
// ZigbeeClusterReply *commandOnWithRecallGlobalScene();
|
||||
// ZigbeeClusterReply *commandOnWithTimedOff();
|
||||
@ -79,8 +79,9 @@ private:
|
||||
protected:
|
||||
void processDataIndication(ZigbeeClusterLibrary::Frame frame) override;
|
||||
|
||||
|
||||
signals:
|
||||
// Note: these signals will only be emitted if the cluster is a client
|
||||
void commandSent(Command command);
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -0,0 +1,6 @@
|
||||
#include "zigbeeclusterrelativehumiditymeasurement.h"
|
||||
|
||||
ZigbeeClusterRelativeHumidityMeasurement::ZigbeeClusterRelativeHumidityMeasurement(QObject *parent) : QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
#ifndef ZIGBEECLUSTERRELATIVEHUMIDITYMEASUREMENT_H
|
||||
#define ZIGBEECLUSTERRELATIVEHUMIDITYMEASUREMENT_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class ZigbeeClusterRelativeHumidityMeasurement : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ZigbeeClusterRelativeHumidityMeasurement(QObject *parent = nullptr);
|
||||
|
||||
signals:
|
||||
|
||||
};
|
||||
|
||||
#endif // ZIGBEECLUSTERRELATIVEHUMIDITYMEASUREMENT_H
|
||||
@ -0,0 +1,60 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* 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 "zigbeeclustertemperaturemeasurement.h"
|
||||
#include "zigbeenetworkreply.h"
|
||||
#include "loggingcategory.h"
|
||||
#include "zigbeenetwork.h"
|
||||
|
||||
ZigbeeClusterTemperatureMeasurement::ZigbeeClusterTemperatureMeasurement(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent) :
|
||||
ZigbeeCluster(network, node, endpoint, Zigbee::ClusterIdTemperatureMeasurement, direction, parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ZigbeeClusterTemperatureMeasurement::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);
|
||||
}
|
||||
|
||||
if (attribute.id() == AttributeMeasuredValue) {
|
||||
bool valueOk = false;
|
||||
quint16 value = attribute.dataType().toUInt16(&valueOk);
|
||||
if (valueOk) {
|
||||
double temperature = value / 100.0;
|
||||
qCDebug(dcZigbeeCluster()) << "Temperature changed" << temperature << "°C";
|
||||
emit temperatureChanged(temperature);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* 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 ZIGBEECLUSTERTEMPERATUREMEASUREMENT_H
|
||||
#define ZIGBEECLUSTERTEMPERATUREMEASUREMENT_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "zcl/zigbeecluster.h"
|
||||
#include "zcl/zigbeeclusterreply.h"
|
||||
|
||||
class ZigbeeNode;
|
||||
class ZigbeeNetwork;
|
||||
class ZigbeeNodeEndpoint;
|
||||
class ZigbeeNetworkReply;
|
||||
|
||||
class ZigbeeClusterTemperatureMeasurement : public ZigbeeCluster
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
friend class ZigbeeNode;
|
||||
friend class ZigbeeNetwork;
|
||||
|
||||
public:
|
||||
enum Attribute {
|
||||
AttributeMeasuredValue = 0x0000,
|
||||
AttributeMinMeasuredValue = 0x0001,
|
||||
AttributeMaxMeasuredValue = 0x0002,
|
||||
AttributeTolerance = 0x0003
|
||||
};
|
||||
Q_ENUM(Attribute)
|
||||
|
||||
// No commands
|
||||
|
||||
explicit ZigbeeClusterTemperatureMeasurement(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent = nullptr);
|
||||
|
||||
private:
|
||||
void setAttribute(const ZigbeeClusterAttribute &attribute) override;
|
||||
|
||||
signals:
|
||||
void temperatureChanged(double temperature);
|
||||
|
||||
};
|
||||
|
||||
#endif // ZIGBEECLUSTERTEMPERATUREMEASUREMENT_H
|
||||
@ -105,7 +105,7 @@ ZigbeeClusterReply *ZigbeeCluster::readAttributes(QList<quint16> attributes)
|
||||
ZigbeeClusterLibrary::FrameControl frameControl;
|
||||
frameControl.frameType = ZigbeeClusterLibrary::FrameTypeGlobal;
|
||||
frameControl.manufacturerSpecific = false;
|
||||
if (m_direction == Direction::Input) {
|
||||
if (m_direction == Direction::Server) {
|
||||
frameControl.direction = ZigbeeClusterLibrary::DirectionClientToServer;
|
||||
} else {
|
||||
frameControl.direction = ZigbeeClusterLibrary::DirectionServerToClient;
|
||||
@ -222,15 +222,14 @@ void ZigbeeCluster::processDataIndication(ZigbeeClusterLibrary::Frame frame)
|
||||
qCWarning(dcZigbeeCluster()) << "Unhandled ZCL indication in" << m_node << m_endpoint << this << frame;
|
||||
}
|
||||
|
||||
void ZigbeeCluster::processApsDataIndication(QByteArray payload)
|
||||
void ZigbeeCluster::processApsDataIndication(const QByteArray &asdu, const ZigbeeClusterLibrary::Frame &frame)
|
||||
{
|
||||
ZigbeeClusterLibrary::Frame frame = ZigbeeClusterLibrary::parseFrameData(payload);
|
||||
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;
|
||||
reply->m_responseData = asdu;
|
||||
reply->m_responseFrame = frame;
|
||||
reply->m_zclIndicationReceived = true;
|
||||
|
||||
@ -240,6 +239,23 @@ void ZigbeeCluster::processApsDataIndication(QByteArray payload)
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for server clusters and if this is an attribute report
|
||||
if (m_direction == Server && frame.header.frameControl.frameType == ZigbeeClusterLibrary::FrameTypeGlobal) {
|
||||
ZigbeeClusterLibrary::Command globalCommand = static_cast<ZigbeeClusterLibrary::Command>(frame.header.command);
|
||||
if (globalCommand == ZigbeeClusterLibrary::CommandReportAttributes) {
|
||||
qCDebug(dcZigbeeCluster()) << "Received attributes report received" << this << frame;
|
||||
// Read the attribute reports and update/set the attributes
|
||||
QDataStream stream(frame.payload);
|
||||
stream.setByteOrder(QDataStream::LittleEndian);
|
||||
quint16 attributeId = 0; quint8 type = 0;
|
||||
stream >> attributeId >> type;
|
||||
ZigbeeDataType dataType = ZigbeeClusterLibrary::readDataType(&stream, static_cast<Zigbee::DataType>(type));
|
||||
setAttribute(ZigbeeClusterAttribute(attributeId, dataType));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Not for a reply, process the indication
|
||||
processDataIndication(frame);
|
||||
}
|
||||
|
||||
@ -69,8 +69,8 @@ class ZigbeeCluster : public QObject
|
||||
|
||||
public:
|
||||
enum Direction {
|
||||
Input,
|
||||
Output
|
||||
Server,
|
||||
Client
|
||||
};
|
||||
Q_ENUM(Direction)
|
||||
|
||||
@ -167,7 +167,7 @@ protected:
|
||||
ZigbeeNodeEndpoint *m_endpoint= nullptr;
|
||||
|
||||
Zigbee::ClusterId m_clusterId = Zigbee::ClusterIdUnknown;
|
||||
Direction m_direction = Input;
|
||||
Direction m_direction = Server;
|
||||
QHash<quint16, ZigbeeClusterAttribute> m_attributes;
|
||||
|
||||
ZigbeeNetworkRequest createGeneralRequest();
|
||||
@ -187,7 +187,7 @@ signals:
|
||||
void attributeChanged(const ZigbeeClusterAttribute &attribute);
|
||||
|
||||
public slots:
|
||||
void processApsDataIndication(QByteArray payload);
|
||||
void processApsDataIndication(const QByteArray &asdu, const ZigbeeClusterLibrary::Frame &frame);
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -335,19 +335,13 @@ void ZigbeeDeviceObject::finishZdoReply(ZigbeeDeviceObjectReply *zdoReply)
|
||||
zdoReply->finished();
|
||||
}
|
||||
|
||||
void ZigbeeDeviceObject::processApsDataIndication(quint8 destinationEndpoint, quint8 sourceEndpoint, quint16 clusterId, QByteArray payload, quint8 lqi, qint8 rssi)
|
||||
void ZigbeeDeviceObject::processApsDataIndication(const Zigbee::ApsdeDataIndication &indication)
|
||||
{
|
||||
Q_UNUSED(destinationEndpoint)
|
||||
Q_UNUSED(sourceEndpoint)
|
||||
Q_UNUSED(clusterId)
|
||||
Q_UNUSED(lqi)
|
||||
Q_UNUSED(rssi)
|
||||
|
||||
// Check if we have a waiting ZDO reply for this data
|
||||
ZigbeeDeviceProfile::Adpu asdu = ZigbeeDeviceProfile::parseAdpu(payload);
|
||||
ZigbeeDeviceProfile::Adpu asdu = ZigbeeDeviceProfile::parseAdpu(indication.asdu);
|
||||
ZigbeeDeviceObjectReply *zdoReply = m_pendingReplies.value(asdu.transactionSequenceNumber);
|
||||
if (zdoReply && clusterId == (zdoReply->request().clusterId() | 0x8000)) {
|
||||
zdoReply->m_responseData = payload;
|
||||
if (zdoReply && indication.clusterId == (zdoReply->request().clusterId() | 0x8000)) {
|
||||
zdoReply->m_responseData = indication.asdu;
|
||||
zdoReply->m_responseAdpu = asdu;
|
||||
zdoReply->m_zdpIndicationReceived = true;
|
||||
if (zdoReply->isComplete()) {
|
||||
|
||||
@ -47,6 +47,13 @@ public:
|
||||
ZigbeeDeviceObjectReply *requestPowerDescriptor();
|
||||
ZigbeeDeviceObjectReply *requestActiveEndpoints();
|
||||
ZigbeeDeviceObjectReply *requestSimpleDescriptor(quint8 endpointId);
|
||||
// TODO: implement other device and service discovery methods
|
||||
|
||||
// End device binding
|
||||
// ZigbeeDeviceObjectReply *requestBindGroup(quint16 clusterId, quint16 groupAddress, quint8 destinationEndpoint);
|
||||
// ZigbeeDeviceObjectReply *requestBindShortAddress();
|
||||
// ZigbeeDeviceObjectReply *requestBindIeeeAddress();
|
||||
|
||||
|
||||
// Management request
|
||||
ZigbeeDeviceObjectReply *requestMgmtLeaveNetwork(bool rejoin = false, bool removeChildren = false);
|
||||
@ -60,16 +67,14 @@ private:
|
||||
quint8 m_transactionSequenceNumber = 0;
|
||||
QHash<quint8, ZigbeeDeviceObjectReply *> m_pendingReplies;
|
||||
|
||||
// Helper methods
|
||||
// Helper methods for replies
|
||||
ZigbeeNetworkRequest buildZdoRequest(quint16 zdoRequest);
|
||||
ZigbeeDeviceObjectReply *createZigbeeDeviceObjectReply(const ZigbeeNetworkRequest &request, quint8 transactionSequenceNumber);
|
||||
bool verifyNetworkError(ZigbeeDeviceObjectReply *zdoReply, ZigbeeNetworkReply *networkReply);
|
||||
void finishZdoReply(ZigbeeDeviceObjectReply *zdoReply);
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
void processApsDataIndication(quint8 destinationEndpoint, quint8 sourceEndpoint, quint16 clusterId, QByteArray payload, quint8 lqi, qint8 rssi);
|
||||
void processApsDataIndication(const Zigbee::ApsdeDataIndication &indication);
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -26,4 +26,61 @@
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "zigbee.h"
|
||||
#include "zigbeeutils.h"
|
||||
#include "zdo/zigbeedeviceprofile.h"
|
||||
|
||||
QDebug operator<<(QDebug debug, const Zigbee::ApsdeDataConfirm &confirm)
|
||||
{
|
||||
debug.nospace() << "APSDE-DATA.confirm(";
|
||||
debug.nospace() << "Request ID: " << confirm.requestId << ", ";
|
||||
|
||||
if (confirm.destinationAddressMode == Zigbee::DestinationAddressModeGroup)
|
||||
debug.nospace() << "Group address:" << ZigbeeUtils::convertUint16ToHexString(confirm.destinationShortAddress) << ", ";
|
||||
|
||||
if (confirm.destinationAddressMode == Zigbee::DestinationAddressModeShortAddress)
|
||||
debug.nospace() << "NWK address:" << ZigbeeUtils::convertUint16ToHexString(confirm.destinationShortAddress) << ", ";
|
||||
|
||||
if (confirm.destinationAddressMode == Zigbee::DestinationAddressModeIeeeAddress)
|
||||
debug.nospace() << "IEEE address:" << ZigbeeAddress(confirm.destinationIeeeAddress).toString() << ", ";
|
||||
|
||||
debug.nospace() << "Destination EP:" << ZigbeeUtils::convertByteToHexString(confirm.destinationEndpoint) << ", ";
|
||||
debug.nospace() << "Source EP:" << ZigbeeUtils::convertByteToHexString(confirm.sourceEndpoint) << ", ";
|
||||
debug.nospace() << static_cast<Zigbee::ZigbeeStatus>(confirm.zigbeeStatusCode);
|
||||
debug.nospace() << ")";
|
||||
|
||||
return debug.space();
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const Zigbee::ApsdeDataIndication &indication)
|
||||
{
|
||||
debug.nospace() << "APSDE-DATA.indication(";
|
||||
if (indication.destinationAddressMode == Zigbee::DestinationAddressModeGroup)
|
||||
debug.nospace() << "Group address:" << ZigbeeUtils::convertUint16ToHexString(indication.destinationShortAddress) << ", ";
|
||||
|
||||
if (indication.destinationAddressMode == Zigbee::DestinationAddressModeShortAddress)
|
||||
debug.nospace() << "NWK address:" << ZigbeeUtils::convertUint16ToHexString(indication.destinationShortAddress) << ", ";
|
||||
|
||||
if (indication.destinationAddressMode == Zigbee::DestinationAddressModeIeeeAddress)
|
||||
debug.nospace() << "IEEE address:" << ZigbeeAddress(indication.destinationIeeeAddress).toString() << ", ";
|
||||
|
||||
debug.nospace() << "Destination EP:" << ZigbeeUtils::convertByteToHexString(indication.destinationEndpoint) << ", ";
|
||||
debug.nospace() << "Source EP:" << ZigbeeUtils::convertByteToHexString(indication.sourceEndpoint) << ", ";
|
||||
|
||||
if (indication.sourceAddressMode == Zigbee::SourceAddressModeShortAddress || indication.sourceAddressMode == Zigbee::SourceAddressModeShortAndIeeeAddress)
|
||||
debug.nospace() << "Source NWK address:" << ZigbeeUtils::convertUint16ToHexString(indication.sourceShortAddress) << ", ";
|
||||
|
||||
if (indication.sourceAddressMode == Zigbee::SourceAddressModeIeeeAddress || indication.sourceAddressMode == Zigbee::SourceAddressModeShortAndIeeeAddress)
|
||||
debug.nospace() << "Source IEEE address:" << ZigbeeAddress(indication.sourceIeeeAddress).toString() << ", ";
|
||||
|
||||
debug.nospace() << static_cast<Zigbee::ZigbeeProfile>(indication.profileId) << ", ";
|
||||
if (indication.profileId == static_cast<quint16>(Zigbee::ZigbeeProfileDevice)) {
|
||||
debug.nospace() << static_cast<ZigbeeDeviceProfile::ZdoCommand>(indication.clusterId) << ", ";
|
||||
} else {
|
||||
debug.nospace() << static_cast<Zigbee::ClusterId>(indication.clusterId) << ", ";
|
||||
}
|
||||
|
||||
debug.nospace() << "ASDU: " << ZigbeeUtils::convertByteArrayToHexString(indication.asdu) << ", ";
|
||||
debug.nospace() << "LQI: " << indication.lqi << ", ";
|
||||
debug.nospace() << "RSSI: " << indication.rssi << "dBm)";
|
||||
return debug.space();
|
||||
}
|
||||
|
||||
@ -74,212 +74,6 @@ public:
|
||||
Q_ENUM(ZigbeeChannel)
|
||||
Q_DECLARE_FLAGS(ZigbeeChannels, ZigbeeChannel)
|
||||
|
||||
// enum InterfaceMessageType {
|
||||
// // Common Commands
|
||||
// MessageTypeNone = 0x0000,
|
||||
// MessageTypeStatus = 0x8000,
|
||||
// MessageTypeLogging = 0x8001,
|
||||
|
||||
// MessageTypeDataIndication = 0x8002,
|
||||
|
||||
// MessageTypeNodeClusterList = 0x8003,
|
||||
// MessageTypeNodeAttributeList = 0x8004,
|
||||
// MessageTypeNodeCommandIdList = 0x8005,
|
||||
// MessageTypeRestartProvisioned = 0x8006,
|
||||
// MessageTypeFactoryNewRestart = 0x8007,
|
||||
// MessageTypeGetVersion = 0x0010,
|
||||
// MessageTypeVersionList = 0x8010,
|
||||
|
||||
// MessageTypeSetExtendetPanId = 0x0020,
|
||||
// MessageTypeSetChannelMask = 0x0021,
|
||||
// MessageTypeSetSecurity = 0x0022,
|
||||
// MessageTypeSetDeviceType = 0x0023,
|
||||
// MessageTypeStartNetwork = 0x0024,
|
||||
// MessageTypeStartScan = 0x0025,
|
||||
// MessageTypeNetworkJoinedFormed = 0x8024,
|
||||
// MessageTypeNetworkRemoveDevice = 0x0026,
|
||||
// MessageTypeNetworkWhitelistEnable = 0x0027,
|
||||
// MessageTypeAuthenticateDeviceRequest = 0x0028,
|
||||
// MessageTypeAuthenticateDeviceResponse = 0x8028,
|
||||
// MessageTypeOutOfBandCommisioningDataRequest = 0x0029,
|
||||
// MessageTypeOutOfBandCommisioningDataResponse = 0x8029,
|
||||
// MessageTypeUserDescriptorSet = 0x002B,
|
||||
// MessageTypeUserDescriptorNotify = 0x802B,
|
||||
// MessageTypeUserDescriptorRequest = 0x002C,
|
||||
// MessageTypeUserDescriptorResponse = 0x802C,
|
||||
|
||||
// MessageTypeReset = 0x0011,
|
||||
// MessageTypeErasePersistentData = 0x0012,
|
||||
// MessageTypeZllFactoryNew = 0x0013,
|
||||
// MessageTypeGetPermitJoining = 0x0014,
|
||||
// MessageTypeGetPermitJoiningResponse = 0x8014,
|
||||
// MessageTypeBind = 0x0030,
|
||||
// MessageTypeBindResponse = 0x8030,
|
||||
// MessageTypeUnbind = 0x0031,
|
||||
// MessageTypeBindGroup = 0x0032,
|
||||
// MessageTypeBindGroupResponse = 0x8032,
|
||||
// MessageTypeUnbindGroup = 0x0033,
|
||||
// MessageTypeUnbindGroupResponse = 0x8033,
|
||||
|
||||
// MessageTypeUnbindResponse = 0x8031,
|
||||
// MessageTypeComplexDescriptorRequest = 0x0034,
|
||||
// MessageTypeComplexDescriptorResponse = 0x8034,
|
||||
|
||||
// MessageTypeNetworkAdressRequest = 0x0040,
|
||||
// MessageTypeNetworkAdressResponse = 0x8040,
|
||||
// MessageTypeIeeeAddressResponse = 0x0041,
|
||||
// MessageTypeIeeeAddressRequest = 0x8041,
|
||||
// MessageTypeNodeDescriptorRequest = 0x0042,
|
||||
// MessageTypeNodeDescriptorRsponse = 0x8042,
|
||||
// MessageTypeSimpleDescriptorRequest = 0x0043,
|
||||
// MessageTypeSimpleDescriptorResponse = 0x8043,
|
||||
// MessageTypePowerDescriptorRequest = 0x0044,
|
||||
// MessageTypePowerDescriptorResponse = 0x8044,
|
||||
// MessageTypeActiveEndpointRequest = 0x0045,
|
||||
// MessageTypeActiveEndpointResponse = 0x8045,
|
||||
// MessageTypeMatchDescriptorRequest = 0x0046,
|
||||
// MessageTypeMatchDescriptorResponse = 0x8046,
|
||||
// MessageTypeManagementLeaveRequest = 0x0047,
|
||||
// MessageTypeManagementLeaveResponse = 0x8047,
|
||||
// MessageTypeLeaveIndication = 0x8048,
|
||||
// MessageTypePermitJoiningRequest = 0x0049,
|
||||
// MessageTypeManagementNetworkUpdateRequest = 0x004A,
|
||||
// MessageTypeManagementNetworkUpdateResponse = 0x804A,
|
||||
// MessageTypeSystemServerDiscoveryRequest = 0x004B,
|
||||
// MessageTypeSystemServerDiscoveryResponse = 0x804B,
|
||||
// MessageTypeDeviceAnnounce = 0x004D,
|
||||
// MessageTypeManagementLqiRequest = 0x004E,
|
||||
// MessageTypeManagementLqiResponse = 0x804E,
|
||||
|
||||
// // Basic cluster
|
||||
// MessageBasicResetFactoryDefaults = 0x0050,
|
||||
// MessageBasicResetFactoryDefaultsResponse = 0x8050,
|
||||
|
||||
// // Group Cluster
|
||||
// MessageTypeAddGroupRequest = 0x0060,
|
||||
// MessageTypeAddGroupResponse = 0x8060,
|
||||
// MessageTypeViewGroupRequest = 0x0061,
|
||||
// MessageTypeViewGroupResponse = 0x8061,
|
||||
// MessageTypeGetGroupMembershipRequest = 0x0062,
|
||||
// MessageTypeGetGroupMembershipResponse = 0x8062,
|
||||
// MessageTypeRemoveGroupRequest = 0x0063,
|
||||
// MessageTypeRemoveGroupResponse = 0x8063,
|
||||
// MessageTypeRemoveAllGroups = 0x0064,
|
||||
// MessageTypeGroupIfIdentify = 0x0065,
|
||||
|
||||
// // Identify Cluster
|
||||
// MessageTypeIdentifySend = 0x0070,
|
||||
// MessageTypeIdentifyQuery = 0x0071,
|
||||
|
||||
// // Level Cluster
|
||||
// MessageTypeMoveToLevel = 0x0080,
|
||||
// MessageTypeMoveToLevelOnOff = 0x0081,
|
||||
// MessageTypeMoveStep = 0x0082,
|
||||
// MessageTypeMoveStopMove = 0x0083,
|
||||
// MessageTypeMoveStopMoveOnOff = 0x0084,
|
||||
|
||||
// // Scenes Cluster
|
||||
// MessageTypeViewScene = 0x00A0,
|
||||
// MessageTypeViewSceneResponse = 0x80A0,
|
||||
// MessageTypeAddScene = 0x00A1,
|
||||
// MessageTypeAddSceneResponse = 0x80A1,
|
||||
// MessageTypeRemoveScene = 0x00A2,
|
||||
// MessageTypeRemoveSceneResponse = 0x80A2,
|
||||
// MessageTypeRemoveAllScenes = 0x00A3,
|
||||
// MessageTypeRemoveAllScenesResponse = 0x80A3,
|
||||
// MessageTypeStoreScene = 0x00A4,
|
||||
// MessageTypeStoreSceneResponse = 0x80A4,
|
||||
// MessageTypeRecallScene = 0x00A5,
|
||||
// MessageTypeSceneMembershipRequest = 0x00A6,
|
||||
// MessageTypeSceneMembershipResponse = 0x80A6,
|
||||
|
||||
// //Colour Cluster
|
||||
// MessageTypeMoveToHue = 0x00B0,
|
||||
// MessageTypeMoveHue = 0x00B1,
|
||||
// MessageTypeStepHue = 0x00B2,
|
||||
// MessageTypeMoveToSaturation = 0x00B3,
|
||||
// MessageTypeMoveSaturation = 0x00B4,
|
||||
// MessageTypeStepStaturation = 0x00B5,
|
||||
// MessageTypeMoveToHueSaturation = 0x00B6,
|
||||
// MessageTypeMoveToColor = 0x00B7,
|
||||
// MessageTypeMoveColor = 0x00B8,
|
||||
// MessageTypeStepColor = 0x00B9,
|
||||
|
||||
// // ZLL Commands
|
||||
// // Touchlink
|
||||
// MessageTypeInitiateTouchlink = 0x00D0,
|
||||
// MessageTypeTouchlinkStatus = 0x00D1,
|
||||
// MessageTypeTouchlinkFactoryReset = 0x00D2,
|
||||
|
||||
// // Identify Cluster
|
||||
// MessageTypeIdentifyTriggerEffect = 0x00E0,
|
||||
|
||||
// // On/Off Cluster
|
||||
// MessageTypeCluserOnOff = 0x0092,
|
||||
// MessageTypeCluserOnOffTimed = 0x0093,
|
||||
// MessageTypeCluserOnOffEffects = 0x0094,
|
||||
// MessageTypeCluserOnOffUpdate = 0x8095,
|
||||
|
||||
// // Scenes Cluster
|
||||
// MessageTypeAddEnhancedScene = 0x00A7,
|
||||
// MessageTypeViewEnhancedScene = 0x00A8,
|
||||
// MessageTypeCopyScene = 0x00A9,
|
||||
|
||||
// // Colour Cluster
|
||||
// MessageTypeEnhancedMoveToHue = 0x00BA,
|
||||
// MessageTypeEnhancedMoveHue = 0x00BB,
|
||||
// MessageTypeEnhancedStepHue = 0x00BC,
|
||||
// MessageTypeEnhancedMoveToHueSaturation = 0x00BD,
|
||||
// MessageTypeColourLoopSet = 0x00BE,
|
||||
// MessageTypeStopMoveStep = 0x00BF,
|
||||
// MessageTypeMoveToColorTemperature = 0x00C0,
|
||||
// MessageTypeMoveColorTemperature = 0x00C1,
|
||||
// MessageTypeStepColorTemperature = 0x00C2,
|
||||
|
||||
// // ZHA Commands
|
||||
// // Door Lock Cluster
|
||||
// MessageTypeLockUnlockDoor = 0x00F0,
|
||||
|
||||
// // Attributes
|
||||
// MessageTypeReadAttributeRequest = 0x0100,
|
||||
// MessageTypeReadAttributeResponse = 0x8100,
|
||||
// MessageTypeDefaultResponse = 0x8101,
|
||||
// MessageTypeAttributeReport = 0x8102,
|
||||
// MessageTypeWriteAttributeRequest = 0x0110,
|
||||
// MessageTypeWriteAttributeResponse = 0x8110,
|
||||
// MessageTypeConfigReportingRequest = 0x0120,
|
||||
// MessageTypeConfigReportingResponse = 0x8120,
|
||||
// MessageTypeReportAttributes = 0x8121,
|
||||
// MessageTypeAttributeDiscoveryRequest = 0x0140,
|
||||
// MessageTypeAttributeDiscoveryResponse = 0x8140,
|
||||
|
||||
// // Persistant data manager messages
|
||||
// MessageTypeDataManagerAvailableRequest = 0x0300,
|
||||
// MessageTypeDataManagerAvailableResponse = 0x8300,
|
||||
// MessageTypeDataManagerSaveRecordRequest = 0x0200,
|
||||
// MessageTypeDataManagerSaveRecordResponse = 0x8200,
|
||||
// MessageTypeDataManagerLoadRecordRequest = 0x0201,
|
||||
// MessageTypeDataManagerLoadRecordResponse = 0x8201,
|
||||
// MessageTypeDataManagerDeleteAllRecordsRequest = 0x0202,
|
||||
// MessageTypeDataManagerDeleteAllRecordsResponse = 0x8202,
|
||||
|
||||
// // Appliance Statistics Cluster 0x0B03
|
||||
// // http://www.nxp.com/documents/user_manual/JN-UG-3076.pdf
|
||||
// MessageTypeStatisticsClusterLogMessage = 0x0301, // Was 0x0500, was 0x0301
|
||||
// MessageTypeStatisticsClusterLogMessageResponse = 0x8301,
|
||||
|
||||
// // IAS Cluster
|
||||
// MessageTypeSendIasZoneEnroolResponse = 0x0400,
|
||||
// MessageTypeIasZoneStatusChangeNotify = 0x8401,
|
||||
|
||||
// // Extended utils
|
||||
// MessageTypeRawApsDataRequest = 0x0530,
|
||||
// MessageTypeRouterDiscoveryConfirm = 0x8701,
|
||||
// MessageTypeApsDataConfirmFail = 0x8702
|
||||
// };
|
||||
// Q_ENUM(InterfaceMessageType)
|
||||
|
||||
|
||||
enum ClusterId {
|
||||
// Basics
|
||||
ClusterIdUnknown = 0xffff,
|
||||
@ -677,6 +471,34 @@ public:
|
||||
};
|
||||
Q_ENUM(ZigbeeStatus)
|
||||
|
||||
// Basic struct for interface data.
|
||||
typedef struct ApsdeDataConfirm {
|
||||
quint8 requestId = 0;
|
||||
quint8 destinationAddressMode = Zigbee::DestinationAddressModeShortAddress;
|
||||
quint16 destinationShortAddress = 0;
|
||||
quint64 destinationIeeeAddress = 0;
|
||||
quint8 destinationEndpoint = 0;
|
||||
quint8 sourceEndpoint = 0;
|
||||
quint8 zigbeeStatusCode = 0;
|
||||
} ApsdeDataConfirm;
|
||||
|
||||
typedef struct ApsdeDataIndication {
|
||||
quint8 destinationAddressMode = 0;
|
||||
quint16 destinationShortAddress = 0;
|
||||
quint64 destinationIeeeAddress = 0;
|
||||
quint8 destinationEndpoint = 0;
|
||||
quint8 sourceAddressMode = 0;
|
||||
quint16 sourceShortAddress = 0;
|
||||
quint64 sourceIeeeAddress = 0;
|
||||
quint8 sourceEndpoint = 0;
|
||||
quint16 profileId = 0;
|
||||
quint16 clusterId = 0;
|
||||
QByteArray asdu;
|
||||
quint8 lqi = 0;
|
||||
qint8 rssi = 0;
|
||||
} ApsdeDataIndication;
|
||||
|
||||
|
||||
///* Manufacturer Codes */
|
||||
///* Codes less than 0x1000 were issued for RF4CE */
|
||||
//#define ZBEE_MFG_CODE_PANASONIC_RF4CE 0x0001
|
||||
@ -1371,6 +1193,9 @@ public:
|
||||
|
||||
};
|
||||
|
||||
QDebug operator<<(QDebug debug, const Zigbee::ApsdeDataConfirm &confirm);
|
||||
QDebug operator<<(QDebug debug, const Zigbee::ApsdeDataIndication &indication);
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(Zigbee::ZigbeeChannels)
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(Zigbee::ZigbeeTxOptions)
|
||||
|
||||
|
||||
@ -351,7 +351,7 @@ void ZigbeeNetwork::loadNetwork()
|
||||
for (int n = 0; n < inputClustersCount; n ++) {
|
||||
settings.setArrayIndex(n);
|
||||
Zigbee::ClusterId clusterId = static_cast<Zigbee::ClusterId>(settings.value("clusterId", 0).toUInt());
|
||||
ZigbeeCluster *cluster = endpoint->createCluster(clusterId, ZigbeeCluster::Input);
|
||||
ZigbeeCluster *cluster = endpoint->createCluster(clusterId, ZigbeeCluster::Server);
|
||||
//qCDebug(dcZigbeeNetwork()) << "Created" << cluster;
|
||||
endpoint->m_inputClusters.insert(clusterId, cluster);
|
||||
}
|
||||
@ -361,7 +361,7 @@ void ZigbeeNetwork::loadNetwork()
|
||||
for (int n = 0; n < outputClustersCount; n ++) {
|
||||
settings.setArrayIndex(n);
|
||||
Zigbee::ClusterId clusterId = static_cast<Zigbee::ClusterId>(settings.value("clusterId", 0).toUInt());
|
||||
ZigbeeCluster *cluster = endpoint->createCluster(clusterId, ZigbeeCluster::Output);
|
||||
ZigbeeCluster *cluster = endpoint->createCluster(clusterId, ZigbeeCluster::Client);
|
||||
//qCDebug(dcZigbeeNetwork()) << "Created" << cluster;
|
||||
endpoint->m_outputClusters.insert(clusterId, cluster);
|
||||
}
|
||||
|
||||
@ -396,24 +396,34 @@ void ZigbeeNode::startInitialization()
|
||||
|
||||
void ZigbeeNode::initNodeDescriptor()
|
||||
{
|
||||
qCDebug(dcZigbeeNode()) << "Requst node descriptor from" << this;
|
||||
ZigbeeDeviceObjectReply *reply = deviceObject()->requestNodeDescriptor();
|
||||
connect(reply, &ZigbeeDeviceObjectReply::finished, this, [this, reply](){
|
||||
if (reply->error() != ZigbeeDeviceObjectReply::ErrorNoError) {
|
||||
qCWarning(dcZigbeeNode()) << "Error occured during initialization of" << this << "Failed to read node descriptor" << reply->error();
|
||||
// FIXME: decide what to do, retry or stop initialization
|
||||
m_requestRetry++;
|
||||
if (m_requestRetry < 3) {
|
||||
qCDebug(dcZigbeeNode()) << "Retry to request node descriptor" << m_requestRetry << "/" << "3";
|
||||
initNodeDescriptor();
|
||||
} else {
|
||||
qCWarning(dcZigbeeNode()) << "Failed to read node descriptor from" << this << "after 3 attempts. Giving up.";
|
||||
m_requestRetry = 0;
|
||||
// FIXME: decide what to do, remove the node again from network
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// The request finished, but we received a ZDP error.
|
||||
if (reply->responseAdpu().status != ZigbeeDeviceProfile::StatusSuccess) {
|
||||
qCWarning(dcZigbeeNode()) << this << "failed to read node descriptor" << reply->responseAdpu().status;
|
||||
// FIXME: decide what to do, retry or stop initialization
|
||||
// FIXME: decide what to do, remove the node again from network
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcZigbeeNode()) << this << "reading node descriptor finished successfully.";
|
||||
m_requestRetry = 0;
|
||||
|
||||
// Parse and set the node descriptor FIXME: make it nicer using the data types
|
||||
|
||||
QDataStream stream(reply->responseAdpu().payload);
|
||||
stream.setByteOrder(QDataStream::LittleEndian);
|
||||
quint8 typeDescriptorFlag = 0; quint8 frequencyFlag = 0; quint8 macCapabilities = 0;
|
||||
@ -488,18 +498,27 @@ void ZigbeeNode::initPowerDescriptor()
|
||||
connect(reply, &ZigbeeDeviceObjectReply::finished, this, [this, reply](){
|
||||
if (reply->error() != ZigbeeDeviceObjectReply::ErrorNoError) {
|
||||
qCWarning(dcZigbeeNode()) << "Error occured during initialization of" << this << "Failed to read power descriptor" << reply->error();
|
||||
// FIXME: decide what to do, retry or stop initialization
|
||||
if (m_requestRetry < 3) {
|
||||
m_requestRetry++;
|
||||
qCDebug(dcZigbeeNode()) << "Retry to request power descriptor from" << this << m_requestRetry << "/" << "3 attempts.";
|
||||
initPowerDescriptor();
|
||||
} else {
|
||||
qCWarning(dcZigbeeNode()) << "Failed to read power descriptor from" << this << "after 3 attempts. Giving up.";
|
||||
m_requestRetry = 0;
|
||||
// FIXME: decide what to do, remove the node again from network or continue with active endpoint request
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ZigbeeDeviceProfile::Adpu adpu = reply->responseAdpu();
|
||||
if (adpu.status != ZigbeeDeviceProfile::StatusSuccess) {
|
||||
qCWarning(dcZigbeeNode()) << this << "failed to read node descriptor" << adpu.status;
|
||||
// FIXME: decide what to do, retry or stop initialization
|
||||
qCWarning(dcZigbeeNode()) << "Failed to read power descriptor from" << this << adpu.status;
|
||||
// FIXME: decide what to do, remove the node again from network or continue without powerdescriptor
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcZigbeeNode()) << this << "reading power descriptor finished successfully.";
|
||||
m_requestRetry = 0;
|
||||
|
||||
QDataStream stream(adpu.payload);
|
||||
stream.setByteOrder(QDataStream::LittleEndian);
|
||||
@ -518,17 +537,26 @@ void ZigbeeNode::initEndpoints()
|
||||
connect(reply, &ZigbeeDeviceObjectReply::finished, this, [this, reply](){
|
||||
if (reply->error() != ZigbeeDeviceObjectReply::ErrorNoError) {
|
||||
qCWarning(dcZigbeeNode()) << "Error occured during initialization of" << this << "Failed to read active endpoints" << reply->error();
|
||||
// FIXME: decide what to do, retry or stop initialization
|
||||
if (m_requestRetry < 3) {
|
||||
m_requestRetry++;
|
||||
qCDebug(dcZigbeeNode()) << "Retry to request active endpoints from" << this << m_requestRetry << "/" << "3 attempts.";
|
||||
initEndpoints();
|
||||
} else {
|
||||
qCWarning(dcZigbeeNode()) << "Failed to read active endpoints from" << this << "after 3 attempts. Giving up.";
|
||||
m_requestRetry = 0;
|
||||
// FIXME: decide what to do, remove the node again from network
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (reply->responseAdpu().status != ZigbeeDeviceProfile::StatusSuccess) {
|
||||
qCWarning(dcZigbeeNode()) << this << "failed to read active endpoints" << reply->responseAdpu().status;
|
||||
qCWarning(dcZigbeeNode()) << "Failed to read active endpoints" << reply->responseAdpu().status;
|
||||
// FIXME: decide what to do, retry or stop initialization
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcZigbeeNode()) << this << "reading active endpoints finished successfully.";
|
||||
m_requestRetry = 0;
|
||||
|
||||
QDataStream stream(reply->responseAdpu().payload);
|
||||
stream.setByteOrder(QDataStream::LittleEndian);
|
||||
@ -549,25 +577,31 @@ void ZigbeeNode::initEndpoints()
|
||||
// If there a no endpoints or all endpoints have already be initialized, continue with reading the basic cluster information
|
||||
if (m_uninitializedEndpoints.isEmpty()) {
|
||||
initBasicCluster();
|
||||
return;
|
||||
}
|
||||
|
||||
// Read simple descriptor for each uninitialized endpoint
|
||||
for (int i = 0; i < m_uninitializedEndpoints.count(); i++) {
|
||||
quint8 endpointId = m_uninitializedEndpoints.at(i);
|
||||
qCDebug(dcZigbeeNode()) << "Read simple descriptor of endpoint" << ZigbeeUtils::convertByteToHexString(endpointId);
|
||||
initEndpoint(endpointId);
|
||||
}
|
||||
// Start reading simple descriptors sequentially
|
||||
initEndpoint(m_uninitializedEndpoints.first());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void ZigbeeNode::initEndpoint(quint8 endpointId)
|
||||
{
|
||||
qCDebug(dcZigbeeNode()) << "Read simple descriptor of endpoint" << ZigbeeUtils::convertByteToHexString(endpointId);
|
||||
ZigbeeDeviceObjectReply *reply = deviceObject()->requestSimpleDescriptor(endpointId);
|
||||
connect(reply, &ZigbeeDeviceObjectReply::finished, this, [this, reply, endpointId](){
|
||||
if (reply->error() != ZigbeeDeviceObjectReply::ErrorNoError) {
|
||||
qCWarning(dcZigbeeNode()) << "Error occured during initialization of" << this << "Failed to read simple descriptor for endpoint" << endpointId << reply->error();
|
||||
// FIXME: decide what to do, retry or stop initialization
|
||||
if (m_requestRetry < 3) {
|
||||
m_requestRetry++;
|
||||
qCDebug(dcZigbeeNode()) << "Retry to request simple descriptor from" << this << ZigbeeUtils::convertByteToHexString(endpointId) << m_requestRetry << "/" << "3 attempts.";
|
||||
initEndpoints();
|
||||
} else {
|
||||
qCWarning(dcZigbeeNode()) << "Failed to read simple descriptor from" << this << ZigbeeUtils::convertByteToHexString(endpointId) << "after 3 attempts. Giving up.";
|
||||
m_requestRetry = 0;
|
||||
// FIXME: decide what to do, remove the node again from network
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -578,6 +612,7 @@ void ZigbeeNode::initEndpoint(quint8 endpointId)
|
||||
}
|
||||
|
||||
qCDebug(dcZigbeeNode()) << this << "reading simple descriptor for endpoint" << endpointId << "finished successfully.";
|
||||
m_requestRetry = 0;
|
||||
|
||||
quint8 length = 0; quint8 endpointId = 0; quint16 profileId = 0; quint16 deviceId = 0; quint8 deviceVersion = 0;
|
||||
quint8 inputClusterCount = 0; quint8 outputClusterCount = 0;
|
||||
@ -614,32 +649,34 @@ void ZigbeeNode::initEndpoint(quint8 endpointId)
|
||||
endpoint->setDeviceId(deviceId);
|
||||
endpoint->setDeviceVersion(deviceVersion);
|
||||
|
||||
// Parse and add server clusters
|
||||
qCDebug(dcZigbeeNode()) << " Input clusters: (" << inputClusterCount << ")";
|
||||
for (int i = 0; i < inputClusterCount; i++) {
|
||||
quint16 clusterId = 0;
|
||||
stream >> clusterId;
|
||||
if (!endpoint->hasInputCluster(static_cast<Zigbee::ClusterId>(clusterId))) {
|
||||
endpoint->addInputCluster(endpoint->createCluster(static_cast<Zigbee::ClusterId>(clusterId), ZigbeeCluster::Input));
|
||||
endpoint->addInputCluster(endpoint->createCluster(static_cast<Zigbee::ClusterId>(clusterId), ZigbeeCluster::Server));
|
||||
}
|
||||
qCDebug(dcZigbeeNode()) << " Cluster ID:" << ZigbeeUtils::convertUint16ToHexString(clusterId) << ZigbeeUtils::clusterIdToString(static_cast<Zigbee::ClusterId>(clusterId));
|
||||
}
|
||||
|
||||
// Parse and add client clusters
|
||||
stream >> outputClusterCount;
|
||||
|
||||
qCDebug(dcZigbeeNode()) << " Output clusters: (" << outputClusterCount << ")";
|
||||
for (int i = 0; i < outputClusterCount; i++) {
|
||||
quint16 clusterId = 0;
|
||||
stream >> clusterId;
|
||||
if (!endpoint->hasOutputCluster(static_cast<Zigbee::ClusterId>(clusterId))) {
|
||||
endpoint->addOutputCluster(endpoint->createCluster(static_cast<Zigbee::ClusterId>(clusterId), ZigbeeCluster::Output));
|
||||
endpoint->addOutputCluster(endpoint->createCluster(static_cast<Zigbee::ClusterId>(clusterId), ZigbeeCluster::Client));
|
||||
}
|
||||
qCDebug(dcZigbeeNode()) << " Cluster ID:" << ZigbeeUtils::convertUint16ToHexString(clusterId) << ZigbeeUtils::clusterIdToString(static_cast<Zigbee::ClusterId>(clusterId));
|
||||
}
|
||||
|
||||
m_uninitializedEndpoints.removeAll(endpointId);
|
||||
endpoint->m_initialized = true;
|
||||
|
||||
if (m_uninitializedEndpoints.isEmpty()) {
|
||||
|
||||
// Note: if we are initializing the coordinator, we can stop here
|
||||
if (m_shortAddress == 0) {
|
||||
setState(StateInitialized);
|
||||
return;
|
||||
@ -647,21 +684,49 @@ void ZigbeeNode::initEndpoint(quint8 endpointId)
|
||||
|
||||
// Continue with the basic cluster attributes
|
||||
initBasicCluster();
|
||||
} else {
|
||||
// Fetch next endpoint
|
||||
initEndpoint(m_uninitializedEndpoints.first());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ZigbeeNode::initBasicCluster()
|
||||
{
|
||||
// FIXME: check if we want to read from all endpoints the basic cluster information or only from the first
|
||||
ZigbeeClusterBasic *basicCluster = m_endpoints.first()->inputCluster<ZigbeeClusterBasic>(Zigbee::ClusterIdBasic);
|
||||
|
||||
if (!basicCluster) {
|
||||
qCWarning(dcZigbeeNode()) << this << "could not find basic server cluster";
|
||||
qCWarning(dcZigbeeNode()) << "Could not find basic cluster on" << this << "Set the node to initialized anyways.";
|
||||
// Set the device initialized any ways since this ist just for convinience
|
||||
setState(StateInitialized);
|
||||
return;
|
||||
}
|
||||
|
||||
// Start reading basic cluster attributes sequentially
|
||||
readManufacturerName(basicCluster);
|
||||
}
|
||||
|
||||
void ZigbeeNode::readManufacturerName(ZigbeeClusterBasic *basicCluster)
|
||||
{
|
||||
ZigbeeClusterBasic::Attribute attributeId = ZigbeeClusterBasic::AttributeManufacturerName;
|
||||
if (basicCluster->hasAttribute(attributeId)) {
|
||||
// Note: only read the basic cluster information if we don't have them already from an indication.
|
||||
// Some devices (Lumi/Aquara) send cluster information containing different payload than a read attribute returns.
|
||||
// This is bad device stack implementation, but we want to make it work either way without destroying the correct
|
||||
// workflow as specified by the stack.
|
||||
qCDebug(dcZigbeeNode()) << "The manufacturer name has already been set" << this << "Continue with model identifier";
|
||||
bool valueOk = false;
|
||||
QString manufacturerName = basicCluster->attribute(attributeId).dataType().toString(&valueOk);
|
||||
if (valueOk) {
|
||||
m_endpoints.first()->m_manufacturerName = manufacturerName;
|
||||
} else {
|
||||
qCWarning(dcZigbeeNode()) << "Could not convert manufacturer name attribute data to string" << basicCluster->attribute(attributeId).dataType();
|
||||
}
|
||||
|
||||
readModelIdentifier(basicCluster);
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcZigbeeNode()) << "Reading attribute" << attributeId;
|
||||
ZigbeeClusterReply *reply = basicCluster->readAttributes({static_cast<quint16>(attributeId)});
|
||||
connect(reply, &ZigbeeClusterReply::finished, this, [this, basicCluster, reply, attributeId](){
|
||||
@ -684,64 +749,106 @@ void ZigbeeNode::initBasicCluster()
|
||||
}
|
||||
}
|
||||
|
||||
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()) {
|
||||
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)});
|
||||
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()) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finished with reading basic cluster, the node is initialized. TODO: read other cluster information
|
||||
setState(StateInitialized);
|
||||
});
|
||||
});
|
||||
// Continue eiterh way with attribute reading
|
||||
readModelIdentifier(basicCluster);
|
||||
});
|
||||
}
|
||||
|
||||
void ZigbeeNode::onClusterAttributeChanged(const ZigbeeClusterAttribute &attribute)
|
||||
void ZigbeeNode::readModelIdentifier(ZigbeeClusterBasic *basicCluster)
|
||||
{
|
||||
ZigbeeCluster *cluster = static_cast<ZigbeeCluster *>(sender());
|
||||
qCDebug(dcZigbeeNode()) << "Cluster" << cluster << "attribute changed" << attribute;
|
||||
emit clusterAttributeChanged(cluster, attribute);
|
||||
ZigbeeClusterBasic::Attribute attributeId = ZigbeeClusterBasic::AttributeModelIdentifier;
|
||||
if (basicCluster->hasAttribute(attributeId)) {
|
||||
// Note: only read the basic cluster information if we don't have them already from an indication.
|
||||
// Some devices (Lumi/Aquara) send cluster information containing different payload than a read attribute returns.
|
||||
// This is bad device stack implementation, but we want to make it work either way without destroying the correct
|
||||
// workflow as specified by the stack.
|
||||
qCDebug(dcZigbeeNode()) << "The model identifier has already been set" << this << "Continue with software build ID.";
|
||||
bool valueOk = false;
|
||||
QString modelIdentifier = basicCluster->attribute(attributeId).dataType().toString(&valueOk);
|
||||
if (valueOk) {
|
||||
m_endpoints.first()->m_modelIdentifier= modelIdentifier;
|
||||
} else {
|
||||
qCWarning(dcZigbeeNode()) << "Could not convert model identifier attribute data to string" << basicCluster->attribute(attributeId).dataType();
|
||||
}
|
||||
|
||||
readSoftwareBuildId(basicCluster);
|
||||
return;
|
||||
}
|
||||
|
||||
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()) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Continue eiterh way with attribute reading
|
||||
readSoftwareBuildId(basicCluster);
|
||||
});
|
||||
}
|
||||
|
||||
void ZigbeeNode::readSoftwareBuildId(ZigbeeClusterBasic *basicCluster)
|
||||
{
|
||||
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()) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finished with reading basic cluster, the node is initialized.
|
||||
// TODO: read other interesting cluster information
|
||||
setState(StateInitialized);
|
||||
});
|
||||
}
|
||||
|
||||
void ZigbeeNode::handleZigbeeClusterLibraryIndication(const Zigbee::ApsdeDataIndication &indication)
|
||||
{
|
||||
qCDebug(dcZigbeeNode()) << "Processing ZCL indication" << indication;
|
||||
|
||||
// Get the endpoint
|
||||
ZigbeeNodeEndpoint *endpoint = getEndpoint(indication.sourceEndpoint);
|
||||
if (!endpoint) {
|
||||
qCWarning(dcZigbeeNetwork()) << "Received a ZCL indication for an unrecognized endpoint. There is no such endpoint on" << this << "Creating the uninitialized endpoint";
|
||||
// Create the uninitialized endpoint for now and fetch information later
|
||||
endpoint = new ZigbeeNodeEndpoint(m_network, this, indication.sourceEndpoint, this);
|
||||
endpoint->setProfile(static_cast<Zigbee::ZigbeeProfile>(indication.profileId));
|
||||
// Note: the endpoint is not initializd yet, but keep it anyways
|
||||
m_endpoints.append(endpoint);
|
||||
}
|
||||
|
||||
endpoint->handleZigbeeClusterLibraryIndication(indication);
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, ZigbeeNode *node)
|
||||
|
||||
@ -275,14 +275,19 @@ private:
|
||||
//virtual void setClusterAttributeReport(const ZigbeeClusterAttributeReport &report) = 0;
|
||||
|
||||
// Init methods
|
||||
int m_requestRetry = 0;
|
||||
QList<quint8> m_uninitializedEndpoints;
|
||||
void initNodeDescriptor();
|
||||
void initPowerDescriptor();
|
||||
void initEndpoints();
|
||||
void initEndpoint(quint8 endpointId);
|
||||
void initBasicCluster();
|
||||
|
||||
QList<quint8> m_uninitializedEndpoints;
|
||||
QList<quint16> m_uninitalizedBasicClusterAttributes;
|
||||
// For convinience and having base information about the first endpoint
|
||||
void initBasicCluster();
|
||||
void readManufacturerName(ZigbeeClusterBasic *basicCluster);
|
||||
void readModelIdentifier(ZigbeeClusterBasic *basicCluster);
|
||||
void readSoftwareBuildId(ZigbeeClusterBasic *basicCluster);
|
||||
|
||||
|
||||
signals:
|
||||
void stateChanged(State state);
|
||||
@ -290,8 +295,8 @@ signals:
|
||||
void clusterAdded(ZigbeeCluster *cluster);
|
||||
void clusterAttributeChanged(ZigbeeCluster *cluster, const ZigbeeClusterAttribute &attribute);
|
||||
|
||||
private slots:
|
||||
void onClusterAttributeChanged(const ZigbeeClusterAttribute &attribute);
|
||||
public slots:
|
||||
void handleZigbeeClusterLibraryIndication(const Zigbee::ApsdeDataIndication &indication);
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -72,6 +72,11 @@ void ZigbeeNodeEndpoint::setDeviceVersion(quint8 deviceVersion)
|
||||
m_deviceVersion = deviceVersion;
|
||||
}
|
||||
|
||||
bool ZigbeeNodeEndpoint::initialized() const
|
||||
{
|
||||
return m_initialized;
|
||||
}
|
||||
|
||||
QString ZigbeeNodeEndpoint::manufacturerName() const
|
||||
{
|
||||
return m_manufacturerName;
|
||||
@ -118,7 +123,12 @@ ZigbeeNodeEndpoint::ZigbeeNodeEndpoint(ZigbeeNetwork *network, ZigbeeNode *node,
|
||||
m_node(node),
|
||||
m_endpointId(endpointId)
|
||||
{
|
||||
qCDebug(dcZigbeeEndpoint()) << "Creating endpoint" << m_endpointId << "on" << m_node;
|
||||
}
|
||||
|
||||
ZigbeeNodeEndpoint::~ZigbeeNodeEndpoint()
|
||||
{
|
||||
qCDebug(dcZigbeeEndpoint()) << "Destroy endpoint" << m_endpointId << "on" << m_node;
|
||||
}
|
||||
|
||||
void ZigbeeNodeEndpoint::setManufacturerName(const QString &manufacturerName)
|
||||
@ -157,7 +167,11 @@ ZigbeeCluster *ZigbeeNodeEndpoint::createCluster(Zigbee::ClusterId clusterId, Zi
|
||||
case Zigbee::ClusterIdOnOff:
|
||||
return new ZigbeeClusterOnOff(m_network, m_node, this, direction, this);
|
||||
break;
|
||||
case Zigbee::ClusterIdTemperatureMeasurement:
|
||||
return new ZigbeeClusterTemperatureMeasurement(m_network, m_node, this, direction, this);
|
||||
break;
|
||||
default:
|
||||
// Return a default cluster since we have no special implementation for this cluster
|
||||
return new ZigbeeCluster(m_network, m_node, this, clusterId, direction, this);
|
||||
}
|
||||
}
|
||||
@ -172,6 +186,37 @@ void ZigbeeNodeEndpoint::addOutputCluster(ZigbeeCluster *cluster)
|
||||
m_outputClusters.insert(cluster->clusterId(), cluster);
|
||||
}
|
||||
|
||||
void ZigbeeNodeEndpoint::handleZigbeeClusterLibraryIndication(const Zigbee::ApsdeDataIndication &indication)
|
||||
{
|
||||
ZigbeeClusterLibrary::Frame frame = ZigbeeClusterLibrary::parseFrameData(indication.asdu);
|
||||
qCDebug(dcZigbeeEndpoint()) << "Processing ZCL indication" << this << indication << frame;
|
||||
|
||||
// Check which kind of cluster sent this inidication, server or client
|
||||
ZigbeeCluster *cluster = nullptr;
|
||||
switch (frame.header.frameControl.direction) {
|
||||
case ZigbeeClusterLibrary::DirectionClientToServer:
|
||||
// Get the output/client cluster this indication is coming from
|
||||
cluster = getOutputCluster(static_cast<Zigbee::ClusterId>(indication.clusterId));
|
||||
if (!cluster) {
|
||||
cluster = createCluster(static_cast<Zigbee::ClusterId>(indication.clusterId), ZigbeeCluster::Client);
|
||||
qCWarning(dcZigbeeEndpoint()) << "Received a ZCL indication for a cluster which does not exist yet on" << m_node << this << "Creating" << cluster;
|
||||
m_outputClusters.insert(cluster->clusterId(), cluster);
|
||||
}
|
||||
break;
|
||||
case ZigbeeClusterLibrary::DirectionServerToClient:
|
||||
// Get the input/server cluster this indication is coming from
|
||||
cluster = getInputCluster(static_cast<Zigbee::ClusterId>(indication.clusterId));
|
||||
if (!cluster) {
|
||||
cluster = createCluster(static_cast<Zigbee::ClusterId>(indication.clusterId), ZigbeeCluster::Server);
|
||||
qCWarning(dcZigbeeEndpoint()) << "Received a ZCL indication for a cluster which does not exist yet on" << m_node << this << "Creating" << cluster;
|
||||
m_inputClusters.insert(cluster->clusterId(), cluster);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
cluster->processApsDataIndication(indication.asdu, frame);
|
||||
}
|
||||
|
||||
ZigbeeCluster *ZigbeeNodeEndpoint::getOutputCluster(Zigbee::ClusterId clusterId) const
|
||||
{
|
||||
return m_outputClusters.value(clusterId);
|
||||
|
||||
@ -34,9 +34,11 @@
|
||||
#include "zigbeeaddress.h"
|
||||
#include "zigbeenetworkreply.h"
|
||||
|
||||
// Import all implemented cluster types
|
||||
#include "zcl/zigbeecluster.h"
|
||||
#include "zcl/general/zigbeeclusterbasic.h"
|
||||
#include "zcl/general/zigbeeclusteronoff.h"
|
||||
#include "zcl/measurement/zigbeeclustertemperaturemeasurement.h"
|
||||
|
||||
class ZigbeeNode;
|
||||
class ZigbeeNetwork;
|
||||
@ -62,17 +64,19 @@ public:
|
||||
quint8 deviceVersion() const;
|
||||
void setDeviceVersion(quint8 deviceVersion);
|
||||
|
||||
bool initialized() const;
|
||||
|
||||
// Basic cluster information
|
||||
QString manufacturerName() const;
|
||||
QString modelIdentifier() const;
|
||||
QString softwareBuildId() const;
|
||||
|
||||
// Input clusters
|
||||
// Server clusters
|
||||
QList<ZigbeeCluster *> inputClusters() const;
|
||||
ZigbeeCluster *getInputCluster(Zigbee::ClusterId clusterId) const;
|
||||
bool hasInputCluster(Zigbee::ClusterId clusterId) const;
|
||||
|
||||
// Output clusters
|
||||
// Client clusters
|
||||
QList<ZigbeeCluster *> outputClusters() const;
|
||||
ZigbeeCluster *getOutputCluster(Zigbee::ClusterId clusterId) const;
|
||||
bool hasOutputCluster(Zigbee::ClusterId clusterId) const;
|
||||
@ -97,6 +101,7 @@ public:
|
||||
|
||||
private:
|
||||
explicit ZigbeeNodeEndpoint(ZigbeeNetwork *network, ZigbeeNode *node, quint8 endpointId, QObject *parent = nullptr);
|
||||
~ZigbeeNodeEndpoint();
|
||||
|
||||
ZigbeeNetwork *m_network = nullptr;
|
||||
ZigbeeNode *m_node = nullptr;
|
||||
@ -104,6 +109,7 @@ private:
|
||||
Zigbee::ZigbeeProfile m_profile = Zigbee::ZigbeeProfileLightLink;
|
||||
quint16 m_deviceId = 0;
|
||||
quint8 m_deviceVersion = 0;
|
||||
bool m_initialized = false;
|
||||
|
||||
QHash<Zigbee::ClusterId, ZigbeeCluster *> m_inputClusters;
|
||||
QHash<Zigbee::ClusterId, ZigbeeCluster *> m_outputClusters;
|
||||
@ -124,6 +130,8 @@ private:
|
||||
void addInputCluster(ZigbeeCluster *cluster);
|
||||
void addOutputCluster(ZigbeeCluster *cluster);
|
||||
|
||||
void handleZigbeeClusterLibraryIndication(const Zigbee::ApsdeDataIndication &indication);
|
||||
|
||||
signals:
|
||||
void clusterAttributeChanged(ZigbeeCluster *cluster, const ZigbeeClusterAttribute &attribute);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user