Implement new clusters and restructure ZCL frame flow and implement attribute reports

This commit is contained in:
Simon Stürz 2020-05-28 18:03:33 +02:00
parent 7e5643a1cf
commit 69ed7e3496
26 changed files with 660 additions and 492 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,6 @@
#include "zigbeeclusterrelativehumiditymeasurement.h"
ZigbeeClusterRelativeHumidityMeasurement::ZigbeeClusterRelativeHumidityMeasurement(QObject *parent) : QObject(parent)
{
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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()) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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