Implement first ZDO call

This commit is contained in:
Simon Stürz 2020-04-08 21:34:05 +02:00
parent 4472700160
commit 118cdedc6a
22 changed files with 543 additions and 259 deletions

View File

@ -5,9 +5,16 @@ This repository contains the nymea-zigbee library and tools.
# Supported hardware
Currently the only supported hardware is from NXP, but the tool will be designed to support also other hardware modules.
Depending on your available hardware following gateway modules are supported
## NXP
* JN5168 (SoM)
* JN5169 (USB Stick)
## deCONZ
* ConBee
* RaspBee
* ConBee II
* RaspBee II

View File

@ -224,7 +224,7 @@ void ZigbeeInterfaceDeconz::sendPackage(const QByteArray &package)
qCWarning(dcZigbeeInterface()) << "Could not stream byte" << ZigbeeUtils::convertByteArrayToHexString(data);
}
m_serialPort->flush();
//m_serialPort->flush();
}
bool ZigbeeInterfaceDeconz::enable(const QString &serialPort, qint32 baudrate)

View File

@ -60,8 +60,18 @@ void ZigbeeInterfaceDeconzReply::abort()
ZigbeeInterfaceDeconzReply::ZigbeeInterfaceDeconzReply(Deconz::Command command, quint8 sequenceNumber, QObject *parent) :
QObject(parent),
m_timer(new QTimer(this)),
m_command(command),
m_sequenceNumber(sequenceNumber)
{
m_timer->setInterval(2000);
m_timer->setSingleShot(true);
connect(m_timer, &QTimer::timeout, this, &ZigbeeInterfaceDeconzReply::onTimeout);
}
void ZigbeeInterfaceDeconzReply::onTimeout()
{
m_timeout = true;
emit timeout();
emit finished();
}

View File

@ -29,6 +29,7 @@
#define ZIGBEEINTERFACEDECONZREPLY_H
#include <QObject>
#include <QTimer>
#include "deconz.h"
@ -47,12 +48,14 @@ public:
// Response content
Deconz::StatusCode statusCode() const;
bool timeout() const;
bool aborted() const;
void abort();
private:
explicit ZigbeeInterfaceDeconzReply(Deconz::Command command, quint8 sequenceNumber, QObject *parent = nullptr);
QTimer *m_timer = nullptr;
bool m_timeout = false;
bool m_aborted = false;
// Request content
@ -63,7 +66,11 @@ private:
Deconz::StatusCode m_statusCode = Deconz::StatusCodeError;
QByteArray m_responseData;
private slots:
void onTimeout();
signals:
void timeout();
void finished();
};

View File

@ -28,6 +28,7 @@
#include "zigbeebridgecontrollerdeconz.h"
#include "loggingcategory.h"
#include "zigbeeutils.h"
#include "zigbeedeviceprofile.h"
#include <QDataStream>
@ -219,7 +220,7 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestQuerySendDataCo
return createReply(Deconz::CommandApsDataConfirm, sequenceNumber, this);
}
ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendDataGroup(quint8 requestId, quint16 groupAddress, quint8 destinationEndpoint, Zigbee::ZigbeeProfile profileId, Zigbee::ClusterId clusterId, quint8 sourceEndpoint, const QByteArray &asdu, quint8 radius)
ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendDataGroup(quint8 requestId, quint16 groupAddress, quint8 destinationEndpoint, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, quint8 radius)
{
quint8 sequenceNumber = generateSequenceNumber();
qCDebug(dcZigbeeController()) << "Request enqueue send data to group" << ZigbeeUtils::convertUint16ToHexString(groupAddress)
@ -240,13 +241,13 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendData
stream << static_cast<quint8>(sequenceNumber);
stream << static_cast<quint8>(0); // Reserverd
stream << static_cast<quint16>(7 + payloadLength); // Frame length
stream << static_cast<quint16>(payloadLength);
stream << payloadLength;
stream << requestId;
stream << static_cast<quint8>(0); // Flags
stream << static_cast<quint8>(Zigbee::DestinationAddressModeGroup);
stream << groupAddress << destinationEndpoint;
stream << static_cast<quint16>(profileId);
stream << static_cast<quint16>(clusterId);
stream << profileId;
stream << clusterId;
stream << sourceEndpoint;
stream << static_cast<quint16>(asdu.length());
for (int i = 0; i < asdu.length(); i++) {
@ -260,7 +261,7 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendData
return createReply(Deconz::CommandApsDataRequest, sequenceNumber, this);
}
ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendDataShortAddress(quint8 requestId, quint16 shortAddress, quint8 destinationEndpoint, Zigbee::ZigbeeProfile profileId, Zigbee::ClusterId clusterId, quint8 sourceEndpoint, const QByteArray &asdu, quint8 radius)
ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendDataShortAddress(quint8 requestId, quint16 shortAddress, quint8 destinationEndpoint, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, quint8 radius)
{
quint8 sequenceNumber = generateSequenceNumber();
qCDebug(dcZigbeeController()) << "Request enqueue send data to short address" << ZigbeeUtils::convertUint16ToHexString(shortAddress)
@ -281,13 +282,13 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendData
stream << static_cast<quint8>(sequenceNumber);
stream << static_cast<quint8>(0); // Reserverd
stream << static_cast<quint16>(7 + payloadLength); // Frame length
stream << static_cast<quint16>(payloadLength);
stream << payloadLength;
stream << requestId;
stream << static_cast<quint8>(0); // Flags
stream << static_cast<quint8>(Zigbee::DestinationAddressModeShortAddress);
stream << shortAddress << destinationEndpoint;
stream << static_cast<quint16>(profileId);
stream << static_cast<quint16>(clusterId);
stream << profileId;
stream << clusterId;
stream << sourceEndpoint;
stream << static_cast<quint16>(asdu.length());
for (int i = 0; i < asdu.length(); i++) {
@ -301,7 +302,7 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendData
return createReply(Deconz::CommandApsDataRequest, sequenceNumber, this);
}
ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendDataIeeeAddress(quint8 requestId, ZigbeeAddress ieeeAddress, quint8 destinationEndpoint, Zigbee::ZigbeeProfile profileId, Zigbee::ClusterId clusterId, quint8 sourceEndpoint, const QByteArray &asdu, quint8 radius)
ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendDataIeeeAddress(quint8 requestId, ZigbeeAddress ieeeAddress, quint8 destinationEndpoint, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, quint8 radius)
{
quint8 sequenceNumber = generateSequenceNumber();
qCDebug(dcZigbeeController()) << "Request enqueue send data to IEEE address" << ieeeAddress.toString()
@ -322,13 +323,13 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendData
stream << static_cast<quint8>(sequenceNumber);
stream << static_cast<quint8>(0); // Reserverd
stream << static_cast<quint16>(7 + payloadLength); // Frame length
stream << static_cast<quint16>(payloadLength);
stream << payloadLength;
stream << requestId;
stream << static_cast<quint8>(0); // Flags
stream << static_cast<quint8>(Zigbee::DestinationAddressModeIeeeAddress);
stream << ieeeAddress.toUInt64() << destinationEndpoint;
stream << static_cast<quint16>(profileId);
stream << static_cast<quint16>(clusterId);
stream << profileId;
stream << clusterId;
stream << sourceEndpoint;
stream << static_cast<quint16>(asdu.length());
for (int i = 0; i < asdu.length(); i++) {
@ -342,6 +343,30 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendData
return createReply(Deconz::CommandApsDataRequest, sequenceNumber, this);
}
ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestSendRequest(const ZigbeeNetworkRequest &request)
{
ZigbeeInterfaceDeconzReply *interfaceReply = nullptr;
switch (request.destinationAddressMode()) {
case Zigbee::DestinationAddressModeGroup:
interfaceReply = requestEnqueueSendDataGroup(request.requestId(), request.destinationShortAddress(),
request.destinationEndpoint(), request.profileId(), request.clusterId(),
request.sourceEndpoint(), request.asdu(), request.radius());
break;
case Zigbee::DestinationAddressModeShortAddress:
interfaceReply = requestEnqueueSendDataShortAddress(request.requestId(), request.destinationShortAddress(),
request.destinationEndpoint(), request.profileId(), request.clusterId(),
request.sourceEndpoint(), request.asdu(), request.radius());
break;
case Zigbee::DestinationAddressModeIeeeAddress:
interfaceReply = requestEnqueueSendDataIeeeAddress(request.requestId(), request.destinationIeeeAddress(),
request.destinationEndpoint(), request.profileId(), request.clusterId(),
request.sourceEndpoint(), request.asdu(), request.radius());
break;
}
return interfaceReply;
}
quint8 ZigbeeBridgeControllerDeconz::generateSequenceNumber()
{
return m_sequenceNumber++;
@ -358,6 +383,9 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::createReply(Deconz::Co
// Add it to the pending list
m_pendingReplies.insert(sequenceNumber, reply);
// Fixme: start the timer once actually sent to the interface
reply->m_timer->start();
return reply;
}
@ -712,6 +740,38 @@ DeconzDeviceState ZigbeeBridgeControllerDeconz::parseDeviceStateFlag(quint8 devi
return state;
}
void ZigbeeBridgeControllerDeconz::readDataIndication()
{
ZigbeeInterfaceDeconzReply *reply = requestReadReceivedDataIndication();
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){
if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Could not read data indication." << reply->statusCode();
// FIXME: set an appropriate error
return;
}
// ASP data indication received, process the content
qCDebug(dcZigbeeController()) << "Reading data indication finished successfully";
processDataIndication(reply->responseData());
});
}
void ZigbeeBridgeControllerDeconz::readDataConfirm()
{
ZigbeeInterfaceDeconzReply *reply = requestQuerySendDataConfirm();
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){
if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Could not read data confirm." << reply->statusCode();
// FIXME: set an appropriate error
return;
}
// ASP data indication received, process the content
qCDebug(dcZigbeeController()) << "Reading data confirm finished successfully";
processDataConfirm(reply->responseData());
});
}
void ZigbeeBridgeControllerDeconz::processDeviceState(DeconzDeviceState deviceState)
{
qCDebug(dcZigbeeController()) << deviceState;
@ -732,36 +792,16 @@ void ZigbeeBridgeControllerDeconz::processDeviceState(DeconzDeviceState deviceSt
if (m_networkState != Deconz::NetworkStateConnected)
return;
// Note: read a data indication before a confirmation since the confirmation arrives after a related indication normally
// Check if we have to read a data indication message
if (deviceState.aspDataIndication) {
ZigbeeInterfaceDeconzReply *reply = requestReadReceivedDataIndication();
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){
if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Could not read data indication." << reply->statusCode();
// FIXME: set an appropriate error
return;
}
// ASP data indication received, process the content
qCDebug(dcZigbeeController()) << "Reading data indication finished successfully";
processDataIndication(reply->responseData());
});
readDataIndication();
}
// Check if we have a response to read for a request
if (deviceState.aspDataConfirm) {
ZigbeeInterfaceDeconzReply *reply = requestQuerySendDataConfirm();
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){
if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Could not read data indication." << reply->statusCode();
// FIXME: set an appropriate error
return;
}
// ASP data indication received, process the content
qCDebug(dcZigbeeController()) << "Reading data confirm finished successfully";
processDataConfirm(reply->responseData());
});
readDataConfirm();
}
}
@ -771,104 +811,76 @@ void ZigbeeBridgeControllerDeconz::processDataIndication(const QByteArray &data)
// ASP data indication
QDataStream stream(data);
stream.setByteOrder(QDataStream::LittleEndian);
quint16 payloadLenght = 0; quint8 deviceStateFlag = 0; quint8 destinationAddressModeFlag = 0;
quint16 destinationShortAddress = 0; quint64 destinationIeeeAddress = 0; quint8 destinationEndpoint = 0;
quint8 sourceAddressModeFlag = 0; quint16 sourceShortAddress = 0; quint64 sourceIeeeAddress = 0; quint8 sourceEndpoint = 0;
quint16 profileId = 0; quint16 clusterId = 0; quint16 asduLength = 0; QByteArray asdu; quint8 reserved = 0;
quint8 lqi = 0; qint8 rssi = 0;
quint16 payloadLenght = 0; quint8 deviceStateFlag = 0; quint8 reserved = 0; quint16 asduLength = 0;
stream >> payloadLenght >> deviceStateFlag >> destinationAddressModeFlag;
Zigbee::DestinationAddressMode destinationAddressMode = static_cast<Zigbee::DestinationAddressMode>(destinationAddressModeFlag);
DeconzApsDataIndication indication;
stream >> payloadLenght >> deviceStateFlag;
stream >> indication.destinationAddressMode;
Zigbee::DestinationAddressMode destinationAddressMode = static_cast<Zigbee::DestinationAddressMode>(indication.destinationAddressMode);
if (destinationAddressMode == Zigbee::DestinationAddressModeGroup || destinationAddressMode == Zigbee::DestinationAddressModeShortAddress)
stream >> destinationShortAddress;
stream >> indication.destinationShortAddress;
if (destinationAddressMode == Zigbee::DestinationAddressModeIeeeAddress)
stream >> destinationIeeeAddress;
stream >> indication.destinationIeeeAddress;
stream >> destinationEndpoint >> sourceAddressModeFlag;
stream >> indication.destinationEndpoint >> indication.sourceAddressMode;
Zigbee::SourceAddressMode sourceAddressMode = static_cast<Zigbee::SourceAddressMode>(sourceAddressModeFlag);
Zigbee::SourceAddressMode sourceAddressMode = static_cast<Zigbee::SourceAddressMode>(indication.sourceAddressMode);
if (sourceAddressMode == Zigbee::SourceAddressModeShortAddress || sourceAddressMode == Zigbee::SourceAddressModeShortAndIeeeAddress)
stream >> sourceShortAddress;
stream >> indication.sourceShortAddress;
if (sourceAddressMode == Zigbee::SourceAddressModeIeeeAddress || sourceAddressMode == Zigbee::SourceAddressModeShortAndIeeeAddress)
stream >> sourceIeeeAddress;
stream >> sourceEndpoint >> profileId >> clusterId >> asduLength;
stream >> indication.sourceIeeeAddress;
stream >> indication.sourceEndpoint >> indication.profileId >> indication.clusterId >> asduLength;
// Fill asdu data
for (int i = 0; i < asduLength; i++) {
quint8 byte = 0;
stream >> byte;
asdu.append(static_cast<char>(byte));
indication.asdu.append(static_cast<char>(byte));
}
stream >> reserved >> reserved >> lqi >> reserved >> reserved >> reserved >> reserved >> rssi;
stream >> reserved >> reserved >> indication.lqi >> reserved >> reserved >> reserved >> reserved >> indication.rssi;
// Print the information for debugging
qCDebug(dcZigbeeController()) << "Data indication received:";
qCDebug(dcZigbeeController()) << " Destination address mode:" << destinationAddressMode;
if (destinationAddressMode == Zigbee::DestinationAddressModeGroup)
qCDebug(dcZigbeeController()) << " Destination address (group):" << ZigbeeUtils::convertUint16ToHexString(destinationShortAddress);
qCDebug(dcZigbeeController()) << indication;
if (destinationAddressMode == Zigbee::DestinationAddressModeShortAddress)
qCDebug(dcZigbeeController()) << " Destination short address:" << ZigbeeUtils::convertUint16ToHexString(destinationShortAddress);
emit aspDataIndicationReceived(indication);
if (destinationAddressMode == Zigbee::DestinationAddressModeIeeeAddress)
qCDebug(dcZigbeeController()) << " Destination IEEE address:" << ZigbeeAddress(destinationIeeeAddress).toString();
qCDebug(dcZigbeeController()) << " Destination endpoint" << ZigbeeUtils::convertByteToHexString(destinationEndpoint);
qCDebug(dcZigbeeController()) << " Source address mode:" << sourceAddressMode;
if (sourceAddressMode == Zigbee::SourceAddressModeShortAddress || sourceAddressMode == Zigbee::SourceAddressModeShortAndIeeeAddress)
qCDebug(dcZigbeeController()) << " Source address:" << ZigbeeUtils::convertUint16ToHexString(sourceShortAddress);
if (sourceAddressMode == Zigbee::SourceAddressModeIeeeAddress || sourceAddressMode == Zigbee::SourceAddressModeShortAndIeeeAddress)
qCDebug(dcZigbeeController()) << " Source IEEE address:" << ZigbeeAddress(sourceIeeeAddress).toString();
qCDebug(dcZigbeeController()) << " Source endpoint:" << ZigbeeUtils::convertByteToHexString(sourceEndpoint);
qCDebug(dcZigbeeController()) << " Profile:" << static_cast<Zigbee::ZigbeeProfile>(profileId);
qCDebug(dcZigbeeController()) << " Cluster:" << static_cast<Zigbee::ClusterId>(clusterId);
qCDebug(dcZigbeeController()) << " ASDU:" << ZigbeeUtils::convertByteArrayToHexString(asdu);
qCDebug(dcZigbeeController()) << " LQI:" << lqi;
qCDebug(dcZigbeeController()) << " RSSI:" << rssi << "dBm";
processDeviceState(parseDeviceStateFlag(deviceStateFlag));
// Process the device state in order to check if we have to request another indication
DeconzDeviceState deviceState = parseDeviceStateFlag(deviceStateFlag);
if (deviceState.aspDataIndication) {
readDataIndication();
}
}
void ZigbeeBridgeControllerDeconz::processDataConfirm(const QByteArray &data)
{
QDataStream stream(data);
stream.setByteOrder(QDataStream::LittleEndian);
quint16 payloadLenght = 0; quint8 deviceStateFlag = 0; quint8 requestId = 0; quint8 destinationAddressMode = 0;
quint16 destinationShortAddress = 0; quint64 destinationIeeeAddress = 0; quint8 destinationEndpoint = 0;
quint8 sourceEndpoint = 0; quint8 zigbeeConfirmStatus = 0;
DeconzApsDataConfirm confirm;
quint16 payloadLenght = 0; quint8 deviceStateFlag = 0;
stream >> payloadLenght >> deviceStateFlag;
stream >> confirm.requestId >> confirm.destinationAddressMode;
stream >> payloadLenght >> deviceStateFlag >> requestId >> destinationAddressMode;
if (destinationAddressMode == Zigbee::DestinationAddressModeGroup || destinationAddressMode == Zigbee::DestinationAddressModeShortAddress)
stream >> destinationShortAddress;
if (confirm.destinationAddressMode == Zigbee::DestinationAddressModeGroup || confirm.destinationAddressMode == Zigbee::DestinationAddressModeShortAddress)
stream >> confirm.destinationShortAddress;
if (destinationAddressMode == Zigbee::DestinationAddressModeIeeeAddress)
stream >> destinationIeeeAddress;
if (confirm.destinationAddressMode == Zigbee::DestinationAddressModeIeeeAddress)
stream >> confirm.destinationIeeeAddress;
stream >> destinationEndpoint >> sourceEndpoint >> zigbeeConfirmStatus;
stream >> confirm.destinationEndpoint >> confirm.sourceEndpoint >> confirm.zigbeeStatusCode;
// Print the information for debugging
qCDebug(dcZigbeeController()) << "Data confirm received: Request" << requestId;
qCDebug(dcZigbeeController()) << " Destination address mode:" << destinationAddressMode;
if (destinationAddressMode == Zigbee::DestinationAddressModeGroup)
qCDebug(dcZigbeeController()) << " Destination address (group):" << ZigbeeUtils::convertUint16ToHexString(destinationShortAddress);
qCDebug(dcZigbeeController()) << confirm;
if (destinationAddressMode == Zigbee::DestinationAddressModeShortAddress)
qCDebug(dcZigbeeController()) << " Destination short address:" << ZigbeeUtils::convertUint16ToHexString(destinationShortAddress);
emit aspDataConfirmReceived(confirm);
if (destinationAddressMode == Zigbee::DestinationAddressModeIeeeAddress)
qCDebug(dcZigbeeController()) << " Destination IEEE address:" << ZigbeeAddress(destinationIeeeAddress).toString();
qCDebug(dcZigbeeController()) << " Destination endpoint" << ZigbeeUtils::convertByteToHexString(destinationEndpoint);
qCDebug(dcZigbeeController()) << " Source endpoint" << ZigbeeUtils::convertByteToHexString(sourceEndpoint);
qCDebug(dcZigbeeController()) << " Confirm status" << static_cast<Zigbee::ZigbeeStatus>(zigbeeConfirmStatus);
// Process the device state in order to check if we have to request another indication
DeconzDeviceState deviceState = parseDeviceStateFlag(deviceStateFlag);
if (deviceState.aspDataConfirm) {
readDataConfirm();
}
}
void ZigbeeBridgeControllerDeconz::onInterfaceAvailableChanged(bool available)
@ -899,8 +911,8 @@ void ZigbeeBridgeControllerDeconz::onInterfacePackageReceived(const QByteArray &
QByteArray data = package.right(package.length() - 5);
Deconz::Command command = static_cast<Deconz::Command>(commandInt);
Deconz::StatusCode status = static_cast<Deconz::StatusCode>(statusInt);
qCDebug(dcZigbeeController()) << "Interface message received" << command << "SQN:" << sequenceNumber
<< status << "Frame length:" << frameLength << ZigbeeUtils::convertByteArrayToHexString(data);
//qCDebug(dcZigbeeController()) << "Interface message received" << command << "SQN:" << sequenceNumber
// << status << "Frame length:" << frameLength << ZigbeeUtils::convertByteArrayToHexString(data);
// Check if this is an interface response for a pending reply
if (m_pendingReplies.contains(sequenceNumber) && m_pendingReplies.value(sequenceNumber)->command() == command) {
@ -982,3 +994,59 @@ QDebug operator<<(QDebug debug, const DeconzDeviceState &deviceState)
debug.nospace() << "CanSend=" << deviceState.aspDataRequestFreeSlots << ")";
return debug.space();
}
QDebug operator<<(QDebug debug, const DeconzApsDataConfirm &confirm)
{
debug.nospace() << "ASP.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() << "ASP.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

@ -35,6 +35,7 @@
#include "zigbee.h"
#include "zigbeeaddress.h"
#include "zigbeenetworkkey.h"
#include "zigbeenetworkrequest.h"
#include "zigbeebridgecontroller.h"
#include "interface/deconz.h"
@ -70,6 +71,34 @@ typedef struct DeconzDeviceState {
bool aspDataRequestFreeSlots = 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
@ -96,9 +125,11 @@ public:
ZigbeeInterfaceDeconzReply *requestQuerySendDataConfirm();
// Send data
ZigbeeInterfaceDeconzReply *requestEnqueueSendDataGroup(quint8 requestId, quint16 groupAddress, quint8 destinationEndpoint, Zigbee::ZigbeeProfile profileId, Zigbee::ClusterId clusterId, quint8 sourceEndpoint, const QByteArray &asdu, quint8 radius = 0);
ZigbeeInterfaceDeconzReply *requestEnqueueSendDataShortAddress(quint8 requestId, quint16 shortAddress, quint8 destinationEndpoint, Zigbee::ZigbeeProfile profileId, Zigbee::ClusterId clusterId, quint8 sourceEndpoint, const QByteArray &asdu, quint8 radius = 0);
ZigbeeInterfaceDeconzReply *requestEnqueueSendDataIeeeAddress(quint8 requestId, ZigbeeAddress ieeeAddress, quint8 destinationEndpoint, Zigbee::ZigbeeProfile profileId, Zigbee::ClusterId clusterId, quint8 sourceEndpoint, const QByteArray &asdu, quint8 radius = 0);
ZigbeeInterfaceDeconzReply *requestEnqueueSendDataGroup(quint8 requestId, quint16 groupAddress, quint8 destinationEndpoint, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, quint8 radius = 0);
ZigbeeInterfaceDeconzReply *requestEnqueueSendDataShortAddress(quint8 requestId, quint16 shortAddress, quint8 destinationEndpoint, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, quint8 radius = 0);
ZigbeeInterfaceDeconzReply *requestEnqueueSendDataIeeeAddress(quint8 requestId, ZigbeeAddress ieeeAddress, quint8 destinationEndpoint, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, quint8 radius = 0);
ZigbeeInterfaceDeconzReply *requestSendRequest(const ZigbeeNetworkRequest &request);
private:
ZigbeeInterfaceDeconz *m_interface = nullptr;
@ -122,8 +153,11 @@ private:
// Device state helper
DeconzDeviceState parseDeviceStateFlag(quint8 deviceStateFlag);
void processDeviceState(DeconzDeviceState deviceState);
void readDataIndication();
void readDataConfirm();
void processDeviceState(DeconzDeviceState deviceState);
void processDataIndication(const QByteArray &data);
void processDataConfirm(const QByteArray &data);
@ -131,6 +165,9 @@ signals:
void networkStateChanged(Deconz::NetworkState networkState);
void networkConfigurationParameterChanged(const DeconzNetworkConfiguration &networkConfiguration);
void aspDataConfirmReceived(const DeconzApsDataConfirm &confirm);
void aspDataIndicationReceived(const DeconzApsDataIndication &indication);
private slots:
void onInterfaceAvailableChanged(bool available);
void onInterfacePackageReceived(const QByteArray &package);
@ -143,6 +180,8 @@ public slots:
};
QDebug operator<<(QDebug debug, const DeconzDeviceState &deviceState);
QDebug operator<<(QDebug debug, const DeconzApsDataConfirm &confirm);
QDebug operator<<(QDebug debug, const DeconzApsDataIndication &indication);
#endif // ZIGBEEBRIDGECONTROLLERDECONZ_H

View File

@ -26,6 +26,7 @@
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "zigbeenetworkdeconz.h"
#include "zigbeedeviceprofile.h"
#include "loggingcategory.h"
#include "zigbeeutils.h"
@ -37,6 +38,8 @@ ZigbeeNetworkDeconz::ZigbeeNetworkDeconz(QObject *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::aspDataConfirmReceived, this, &ZigbeeNetworkDeconz::onAspDataConfirmReceived);
connect(m_controller, &ZigbeeBridgeControllerDeconz::aspDataIndicationReceived, this, &ZigbeeNetworkDeconz::onAspDataIndicationReceived);
m_pollNetworkStateTimer = new QTimer(this);
m_pollNetworkStateTimer->setInterval(1000);
@ -52,6 +55,30 @@ ZigbeeBridgeController *ZigbeeNetworkDeconz::bridgeController() const
return nullptr;
}
ZigbeeNetworkReply *ZigbeeNetworkDeconz::sendRequest(const ZigbeeNetworkRequest &request)
{
ZigbeeNetworkReply *reply = createNetworkReply(request);
// Send the request, and keep the reply until transposrt, zigbee trasmission and response arrived
m_pendingReplies.insert(request.requestId(), reply);
ZigbeeInterfaceDeconzReply *interfaceReply = m_controller->requestSendRequest(request);
connect(interfaceReply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply, interfaceReply](){
if (interfaceReply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Could send request to controller. SQN:" << interfaceReply->sequenceNumber() << interfaceReply->statusCode();
finishNetworkReply(reply, ZigbeeNetworkReply::ErrorInterfaceError);
return;
}
});
return reply;
}
quint8 ZigbeeNetworkDeconz::generateSequenceNumber()
{
return m_sequenceNumber++;
}
void ZigbeeNetworkDeconz::setCreateNetworkState(ZigbeeNetworkDeconz::CreateNetworkState state)
{
if (m_createState == state)
@ -68,11 +95,13 @@ void ZigbeeNetworkDeconz::setCreateNetworkState(ZigbeeNetworkDeconz::CreateNetwo
ZigbeeInterfaceDeconzReply *reply = m_controller->requestChangeNetworkState(Deconz::NetworkStateOffline);
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){
if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Could not stop network for creating a new one." << reply->statusCode();
qCWarning(dcZigbeeController()) << "Could not stop network for creating a new one. SQN:" << reply->sequenceNumber() << reply->statusCode();
// FIXME: set an appropriate error
return;
}
qCDebug(dcZigbeeNetwork()) << "Stop network finished successfully. SQN:" << reply->sequenceNumber();
// Start polling the device state, should be Online -> Leaving -> Offline
m_pollNetworkStateTimer->start();
});
@ -94,12 +123,12 @@ void ZigbeeNetworkDeconz::setCreateNetworkState(ZigbeeNetworkDeconz::CreateNetwo
ZigbeeInterfaceDeconzReply *reply = m_controller->requestWriteParameter(Deconz::ParameterNodeType, paramData);
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){
if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Could not write parameter" << Deconz::ParameterNodeType << Deconz::NodeTypeCoordinator << reply->statusCode();
qCWarning(dcZigbeeController()) << "Could not write parameter. SQN:" << reply->sequenceNumber() << Deconz::ParameterNodeType << Deconz::NodeTypeCoordinator << reply->statusCode();
// FIXME: set an appropriate error
return;
}
qCDebug(dcZigbeeNetwork()) << "Configured successfully bridge to" << Deconz::NodeTypeCoordinator;
qCDebug(dcZigbeeNetwork()) << "Configured successfully bridge to" << Deconz::NodeTypeCoordinator << "SQN:" << reply->sequenceNumber();
QByteArray paramData;
QDataStream stream(&paramData, QIODevice::WriteOnly);
@ -109,12 +138,12 @@ void ZigbeeNetworkDeconz::setCreateNetworkState(ZigbeeNetworkDeconz::CreateNetwo
ZigbeeInterfaceDeconzReply *reply = m_controller->requestWriteParameter(Deconz::ParameterChannelMask, paramData);
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){
if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Could not write parameter" << Deconz::ParameterChannelMask << reply->statusCode();
qCWarning(dcZigbeeController()) << "Could not write parameter. SQN:" << reply->sequenceNumber() << Deconz::ParameterChannelMask << reply->statusCode();
// FIXME: set an appropriate error
return;
}
qCDebug(dcZigbeeNetwork()) << "Configured channel mask successfully";
qCDebug(dcZigbeeNetwork()) << "Configured channel mask successfully. SQN:" << reply->sequenceNumber();
QByteArray paramData;
QDataStream stream(&paramData, QIODevice::WriteOnly);
@ -124,12 +153,12 @@ void ZigbeeNetworkDeconz::setCreateNetworkState(ZigbeeNetworkDeconz::CreateNetwo
ZigbeeInterfaceDeconzReply *reply = m_controller->requestWriteParameter(Deconz::ParameterApsExtendedPanId, paramData);
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){
if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Could not write parameter" << Deconz::ParameterApsExtendedPanId << reply->statusCode();
qCWarning(dcZigbeeController()) << "Could not write parameter. SQN:" << reply->sequenceNumber() << Deconz::ParameterApsExtendedPanId << reply->statusCode();
// FIXME: set an appropriate error
return;
}
qCDebug(dcZigbeeNetwork()) << "Configured APS extended PANID successfully";
qCDebug(dcZigbeeNetwork()) << "Configured APS extended PANID successfully. SQN:" << reply->sequenceNumber();
QByteArray paramData;
QDataStream stream(&paramData, QIODevice::WriteOnly);
@ -139,12 +168,12 @@ void ZigbeeNetworkDeconz::setCreateNetworkState(ZigbeeNetworkDeconz::CreateNetwo
ZigbeeInterfaceDeconzReply *reply = m_controller->requestWriteParameter(Deconz::ParameterTrustCenterAddress, paramData);
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){
if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Could not write parameter" << Deconz::ParameterTrustCenterAddress << reply->statusCode();
qCWarning(dcZigbeeController()) << "Could not write parameter. SQN:" << reply->sequenceNumber() << Deconz::ParameterTrustCenterAddress << reply->statusCode();
// FIXME: set an appropriate error
return;
}
qCDebug(dcZigbeeNetwork()) << "Configured trust center address successfully";
qCDebug(dcZigbeeNetwork()) << "Configured trust center address successfully. SQN:" << reply->sequenceNumber();
QByteArray paramData;
QDataStream stream(&paramData, QIODevice::WriteOnly);
@ -154,7 +183,7 @@ void ZigbeeNetworkDeconz::setCreateNetworkState(ZigbeeNetworkDeconz::CreateNetwo
ZigbeeInterfaceDeconzReply *reply = m_controller->requestWriteParameter(Deconz::ParameterSecurityMode, paramData);
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){
if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Could not write parameter" << Deconz::ParameterSecurityMode << reply->statusCode();
qCWarning(dcZigbeeController()) << "Could not write parameter. SQN:" << reply->sequenceNumber() << Deconz::ParameterSecurityMode << reply->statusCode();
// FIXME: set an appropriate error
return;
}
@ -166,12 +195,12 @@ void ZigbeeNetworkDeconz::setCreateNetworkState(ZigbeeNetworkDeconz::CreateNetwo
ZigbeeInterfaceDeconzReply *reply = m_controller->requestWriteParameter(Deconz::ParameterNetworkKey, securityConfiguration().networkKey().toByteArray());
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){
if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Could not write parameter" << Deconz::ParameterNetworkKey << reply->statusCode();
qCWarning(dcZigbeeController()) << "Could not write parameter. SQN:" << reply->sequenceNumber() << Deconz::ParameterNetworkKey << reply->statusCode();
// FIXME: set an appropriate error
// Note: writing the network key fails all the time...
//return;
} else {
qCDebug(dcZigbeeNetwork()) << "Configured network key successfully";
qCDebug(dcZigbeeNetwork()) << "Configured network key successfully. SQN:" << reply->sequenceNumber();
}
// Configuration finished, lets start the network
@ -188,11 +217,12 @@ void ZigbeeNetworkDeconz::setCreateNetworkState(ZigbeeNetworkDeconz::CreateNetwo
ZigbeeInterfaceDeconzReply *reply = m_controller->requestChangeNetworkState(Deconz::NetworkStateConnected);
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){
if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Could not start network for creating a new one." << reply->statusCode();
qCWarning(dcZigbeeController()) << "Could not start network for creating a new one. SQN:" << reply->sequenceNumber() << reply->statusCode();
// FIXME: set an appropriate error
return;
}
qCDebug(dcZigbeeNetwork()) << "Start network finished successfully. SQN:" << reply->sequenceNumber();
// Start polling the device state, should be Online -> Leaving -> Offline
m_pollNetworkStateTimer->start();
});
@ -203,12 +233,12 @@ void ZigbeeNetworkDeconz::setCreateNetworkState(ZigbeeNetworkDeconz::CreateNetwo
ZigbeeInterfaceDeconzReply *reply = m_controller->readNetworkParameters();
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){
if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Could not read network parameters during network start up." << reply->statusCode();
qCWarning(dcZigbeeController()) << "Could not read network parameters during network start up. SQN:" << reply->sequenceNumber() << reply->statusCode();
// FIXME: set an appropriate error
return;
}
qCDebug(dcZigbeeNetwork()) << "Reading network parameters finished successfully.";
qCDebug(dcZigbeeNetwork()) << "Reading network parameters finished successfully. SQN:" << reply->sequenceNumber();
setPanId(m_controller->networkConfiguration().panId);
setExtendedPanId(m_controller->networkConfiguration().extendedPanId);
@ -224,22 +254,53 @@ void ZigbeeNetworkDeconz::setCreateNetworkState(ZigbeeNetworkDeconz::CreateNetwo
coordinatorNode->setShortAddress(m_controller->networkConfiguration().shortAddress);
coordinatorNode->setExtendedAddress(m_controller->networkConfiguration().ieeeAddress);
addUnitializedNode(coordinatorNode);
coordinatorNode->startInitialization();
// TODO: done when when node initialized
m_coordinatorNode = coordinatorNode;
addNode(coordinatorNode);
setCreateNetworkState(CreateNetworkStateIdle);
setState(StateRunning);
//addUnitializedNode(coordinatorNode);
//coordinatorNode->startInitialization();
}
}
}
void ZigbeeNetworkDeconz::handleZigbeeDeviceProfileIndication(const DeconzApsDataIndication &indication)
{
if (indication.clusterId == ZigbeeDeviceProfile::DeviceAnnounce) {
QDataStream stream(indication.asdu);
stream.setByteOrder(QDataStream::LittleEndian);
quint8 sequenceNumber = 0; quint16 shortAddress = 0; quint64 ieeeAddress = 0; quint8 macFlag = 0;
stream >> sequenceNumber >> shortAddress >> ieeeAddress >> macFlag;
onDeviceAnnounced(shortAddress, ZigbeeAddress(ieeeAddress), macFlag);
return;
}
// Check if this is a response for a ZDO request
foreach (ZigbeeNetworkReply *reply, m_pendingReplies) {
if (reply->request().profileId() == Zigbee::ZigbeeProfileDevice) {
// We have a reply which is waiting for a ZDO response, lets check if they match
// Check if this is the response to the sent request command
if (indication.clusterId == (reply->request().clusterId() | 0x8000)) {
// Now check if the id matches, if so set the ADPU as response to the reply, otherwise this is not the message for this reply
ZigbeeDeviceProfileAdpu deviceAdpu = ZigbeeDeviceProfile::parseAdpu(indication.asdu);
if (deviceAdpu.sequenceNumber == reply->request().requestId() && deviceAdpu.addressOfInterest == reply->request().destinationShortAddress()) {
// We found the correct reply
// Set the response payload of the
qCDebug(dcZigbeeNetwork()) << "Indication response for ZDO request received"
<< static_cast<ZigbeeDeviceProfile::ZdoCommand>(reply->request().clusterId())
<< "-->"
<< static_cast<ZigbeeDeviceProfile::ZdoCommand>(indication.clusterId)
<< deviceAdpu;
setReplyResponseData(reply, indication.asdu);
return;
}
}
}
}
}
ZigbeeNode *ZigbeeNetworkDeconz::createNode(QObject *parent)
{
return new ZigbeeNodeDeconz(m_controller, parent);
return new ZigbeeNodeDeconz(this, parent);
}
void ZigbeeNetworkDeconz::setPermitJoiningInternal(bool permitJoining)
@ -271,7 +332,6 @@ void ZigbeeNetworkDeconz::startNetworkInternally()
qCDebug(dcZigbeeNetwork()) << "Using" << securityConfiguration().networkKey() << "network link key";
qCDebug(dcZigbeeNetwork()) << "Using" << securityConfiguration().globalTrustCenterLinkKey() << "global trust center link key";
// - Read the firmware version
// - Read the network configuration parameters
// - Read the network state
@ -313,12 +373,12 @@ void ZigbeeNetworkDeconz::startNetworkInternally()
ZigbeeInterfaceDeconzReply *reply = m_controller->requestDeviceState();
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){
if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Could not read device state during network start up." << reply->statusCode();
qCWarning(dcZigbeeController()) << "Could not read device state during network start up. SQN:" << reply->sequenceNumber() << reply->statusCode();
// FIXME: set an appropriate error
return;
}
qCDebug(dcZigbeeNetwork()) << "Read device state finished successfully";
qCDebug(dcZigbeeNetwork()) << "Read device state finished successfully. SQN:" << reply->sequenceNumber();
QDataStream stream(reply->responseData());
stream.setByteOrder(QDataStream::LittleEndian);
quint8 deviceStateFlag = 0;
@ -328,11 +388,19 @@ void ZigbeeNetworkDeconz::startNetworkInternally()
if (m_createNewNetwork) {
setCreateNetworkState(CreateNetworkStateStopNetwork);
// Set offline
// Write configurations
// Set online
// Read configurations
// Create and initialize coordinator node
// Done. Save network
} else {
// Get the network state and start the network if required
if (m_controller->networkState() == Deconz::NetworkStateConnected) {
qCDebug(dcZigbeeNetwork()) << "The network is already running.";
setState(StateRunning);
} else {
startNetwork();
}
}
});
@ -340,20 +408,6 @@ void ZigbeeNetworkDeconz::startNetworkInternally()
});
}
void ZigbeeNetworkDeconz::createNetwork()
{
// Set offline
setCreateNetworkState(CreateNetworkStateStopNetwork);
// Write configurations
// Set online
// Read configurations
// Create and initialize coordinator node
}
void ZigbeeNetworkDeconz::onControllerAvailableChanged(bool available)
{
qCDebug(dcZigbeeNetwork()) << "Hardware controller is" << (available ? "now available" : "not available");
@ -387,7 +441,7 @@ void ZigbeeNetworkDeconz::onPollNetworkStateTimeout()
return;
}
qCDebug(dcZigbeeNetwork()) << "Read device state finished successfully";
//qCDebug(dcZigbeeNetwork()) << "Read device state finished successfully";
QDataStream stream(reply->responseData());
stream.setByteOrder(QDataStream::LittleEndian);
quint8 deviceStateFlag = 0;
@ -409,12 +463,12 @@ void ZigbeeNetworkDeconz::onPollNetworkStateTimeout()
ZigbeeInterfaceDeconzReply *reply = m_controller->requestDeviceState();
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){
if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Could not read device state during network start up." << reply->statusCode();
qCWarning(dcZigbeeController()) << "Could not read device state during network start up. SQN:" << reply->sequenceNumber() << reply->statusCode();
// FIXME: set an appropriate error
return;
}
qCDebug(dcZigbeeNetwork()) << "Read device state finished successfully";
//qCDebug(dcZigbeeNetwork()) << "Read device state finished successfully. SQN:" << reply->sequenceNumber();
QDataStream stream(reply->responseData());
stream.setByteOrder(QDataStream::LittleEndian);
quint8 deviceStateFlag = 0;
@ -442,6 +496,42 @@ void ZigbeeNetworkDeconz::onPollNetworkStateTimeout()
}
}
void ZigbeeNetworkDeconz::onAspDataConfirmReceived(const DeconzApsDataConfirm &confirm)
{
qCDebug(dcZigbeeNetwork()) << confirm;
ZigbeeNetworkReply *reply = m_pendingReplies.take(confirm.requestId);
if (!reply) {
qCWarning(dcZigbeeNetwork()) << "Received confirmation but could not find any reply. Ignoring the confirmation";
return;
}
finishNetworkReply(reply, ZigbeeNetworkReply::ErrorNoError, static_cast<Zigbee::ZigbeeStatus>(confirm.zigbeeStatusCode));
}
void ZigbeeNetworkDeconz::onAspDataIndicationReceived(const DeconzApsDataIndication &indication)
{
qCDebug(dcZigbeeNetwork()) << indication;
// Check if this indocation is related to any pending reply
if (indication.profileId == Zigbee::ZigbeeProfileDevice) {
handleZigbeeDeviceProfileIndication(indication);
return;
}
// Find reply finish it
qCDebug(dcZigbeeNetwork()) << "Unhandled indication" << indication;
}
void ZigbeeNetworkDeconz::onDeviceAnnounced(quint16 shortAddress, ZigbeeAddress ieeeAddress, quint8 macCapabilities)
{
qCDebug(dcZigbeeNetwork()) << "Device announced" << ZigbeeUtils::convertUint16ToHexString(shortAddress) << ieeeAddress.toString() << ZigbeeUtils::convertByteToHexString(macCapabilities);
// Create node and initialize it
}
void ZigbeeNetworkDeconz::startNetwork()
{
loadNetwork();
@ -466,12 +556,12 @@ void ZigbeeNetworkDeconz::stopNetwork()
setState(StateStopping);
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){
if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Could not leave network." << reply->statusCode();
qCWarning(dcZigbeeController()) << "Could not leave network. SQN:" << reply->sequenceNumber() << reply->statusCode();
// FIXME: set an appropriate error
return;
}
qCDebug(dcZigbeeNetwork()) << "Network left successfully";
qCDebug(dcZigbeeNetwork()) << "Network left successfully. SQN:" << reply->sequenceNumber();
setState(StateOffline);
});
}

View File

@ -53,7 +53,9 @@ public:
ZigbeeBridgeController *bridgeController() const override;
ZigbeeNetworkReply *sendRequest(const ZigbeeNetworkRequest &request) override;
quint8 generateSequenceNumber();
private:
ZigbeeBridgeControllerDeconz *m_controller = nullptr;
@ -61,9 +63,16 @@ private:
CreateNetworkState m_createState = CreateNetworkStateIdle;
bool m_createNewNetwork = false;
QHash<quint8, ZigbeeNetworkReply *> m_pendingReplies;
quint8 m_sequenceNumber = 0;
QTimer *m_pollNetworkStateTimer = nullptr;
void setCreateNetworkState(CreateNetworkState state);
void handleZigbeeDeviceProfileIndication(const DeconzApsDataIndication &indication);
protected:
ZigbeeNode *createNode(QObject *parent) override;
@ -71,12 +80,15 @@ protected:
void startNetworkInternally();
void createNetwork();
private slots:
void onControllerAvailableChanged(bool available);
void onPollNetworkStateTimeout();
void onAspDataConfirmReceived(const DeconzApsDataConfirm &confirm);
void onAspDataIndicationReceived(const DeconzApsDataIndication &indication);
void onDeviceAnnounced(quint16 shortAddress, ZigbeeAddress ieeeAddress, quint8 macCapabilities);
public slots:
void startNetwork() override;
void stopNetwork() override;

View File

@ -26,10 +26,15 @@
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "zigbeenodedeconz.h"
#include "zigbeedeviceprofile.h"
#include "zigbeenetworkdeconz.h"
#include "loggingcategory.h"
ZigbeeNodeDeconz::ZigbeeNodeDeconz(ZigbeeBridgeControllerDeconz *controller, QObject *parent) :
#include <QDataStream>
ZigbeeNodeDeconz::ZigbeeNodeDeconz(ZigbeeNetworkDeconz *network, QObject *parent) :
ZigbeeNode(parent),
m_controller(controller)
m_network(network)
{
}
@ -47,6 +52,40 @@ void ZigbeeNodeDeconz::setClusterAttributeReport(const ZigbeeClusterAttributeRep
void ZigbeeNodeDeconz::startInitialization()
{
setState(StateInitializing);
// Get the node descriptor
ZigbeeNetworkRequest request;
request.setRequestId(m_network->generateSequenceNumber());
request.setDestinationAddressMode(Zigbee::DestinationAddressModeShortAddress);
request.setDestinationShortAddress(shortAddress());
request.setDestinationEndpoint(0); // ZDO
request.setProfileId(Zigbee::ZigbeeProfileDevice); // ZDP
request.setClusterId(ZigbeeDeviceProfile::NodeDescriptorRequest);
request.setSourceEndpoint(0); // ZDO
// Build ASDU
QByteArray asdu;
QDataStream stream(&asdu, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << request.requestId() << request.destinationShortAddress();
request.setAsdu(asdu);
ZigbeeNetworkReply *reply = m_network->sendRequest(request);
connect(reply, &ZigbeeNetworkReply::finished, this, [this, reply](){
// TODO: check reply error
ZigbeeDeviceProfileAdpu adpu = ZigbeeDeviceProfile::parseAdpu(reply->responseData());
qCDebug(dcZigbeeNode()) << "Node descriptor request finished" << adpu;
setNodeDescriptorRawData(reply->responseData());
QDataStream stream(adpu.payload);
stream.setByteOrder(QDataStream::LittleEndian);
});
/* Node initialisation steps (sequentially)
* - Node descriptor
* - Power descriptor

View File

@ -32,7 +32,8 @@
#include "zigbee.h"
#include "zigbeenode.h"
#include "zigbeebridgecontrollerdeconz.h"
class ZigbeeNetworkDeconz;
class ZigbeeNodeDeconz : public ZigbeeNode
{
@ -41,12 +42,12 @@ class ZigbeeNodeDeconz : public ZigbeeNode
friend class ZigbeeNetworkDeconz;
public:
explicit ZigbeeNodeDeconz(ZigbeeBridgeControllerDeconz *controller, QObject *parent = nullptr);
explicit ZigbeeNodeDeconz(ZigbeeNetworkDeconz *network, QObject *parent = nullptr);
void leaveNetworkRequest(bool rejoin = false, bool removeChildren = false) override;
private:
ZigbeeBridgeControllerDeconz *m_controller = nullptr;
ZigbeeNetworkDeconz *m_network = nullptr;
void setClusterAttributeReport(const ZigbeeClusterAttributeReport &report) override;

View File

@ -23,7 +23,6 @@ SOURCES += \
zigbeechannelmask.cpp \
zigbeecluster.cpp \
zigbeeclusterattribute.cpp \
zigbeedeviceobject.cpp \
zigbeedeviceprofile.cpp \
zigbeemanufacturer.cpp \
zigbeenetwork.cpp \
@ -60,7 +59,6 @@ HEADERS += \
zigbeechannelmask.h \
zigbeecluster.h \
zigbeeclusterattribute.h \
zigbeedeviceobject.h \
zigbeedeviceprofile.h \
zigbeemanufacturer.h \
zigbeenetwork.h \

View File

@ -265,6 +265,13 @@ void ZigbeeNetworkNxp::readPermitJoinStatus()
});
}
ZigbeeNetworkReply *ZigbeeNetworkNxp::sendRequest(const ZigbeeNetworkRequest &request)
{
Q_UNUSED(request)
qCCritical(dcZigbeeNetwork()) << "Cannot send request. Not implemented for this backend";
return nullptr;
}
ZigbeeNode *ZigbeeNetworkNxp::createNode(QObject *parent)
{
return new ZigbeeNodeNxp(m_controller, parent);

View File

@ -62,6 +62,9 @@ private:
void readControllerVersion();
void readPermitJoinStatus();
ZigbeeNetworkReply *sendRequest(const ZigbeeNetworkRequest &request) override;
protected:
ZigbeeNode *createNode(QObject *parent) override;
void setPermitJoiningInternal(bool permitJoining) override;

View File

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

View File

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

View File

@ -26,3 +26,28 @@
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "zigbeedeviceprofile.h"
#include "zigbeeutils.h"
#include <QDataStream>
ZigbeeDeviceProfileAdpu ZigbeeDeviceProfile::parseAdpu(const QByteArray &adpu)
{
QDataStream stream(adpu);
stream.setByteOrder(QDataStream::LittleEndian);
ZigbeeDeviceProfileAdpu deviceAdpu;
quint8 statusFlag = 0;
stream >> deviceAdpu.sequenceNumber >> statusFlag >> deviceAdpu.addressOfInterest;
deviceAdpu.status = static_cast<Zigbee::ZigbeeStatus>(statusFlag);
deviceAdpu.payload = adpu.right(adpu.length() - 4);
return deviceAdpu;
}
QDebug operator<<(QDebug debug, const ZigbeeDeviceProfileAdpu &deviceAdpu)
{
debug.nospace() << "DeviceAdpu(SQN: " << deviceAdpu.sequenceNumber << ", ";
debug.nospace() << deviceAdpu.status << ", ";
debug.nospace() << ZigbeeUtils::convertUint16ToHexString(deviceAdpu.addressOfInterest) << ", ";
debug.nospace() << ZigbeeUtils::convertByteArrayToHexString(deviceAdpu.payload) << ")";
return debug.space();
}

View File

@ -28,14 +28,23 @@
#ifndef ZIGBEEDEVICEPROFILE_H
#define ZIGBEEDEVICEPROFILE_H
#include <QDebug>
#include <QObject>
#include "zigbee.h"
typedef struct ZigbeeDeviceProfileAdpu {
quint8 sequenceNumber = 0;
Zigbee::ZigbeeStatus status = Zigbee::ZigbeeStatusSuccess;
quint16 addressOfInterest = 0;
QByteArray payload;
} ZigbeeDeviceProfileAdpu;
class ZigbeeDeviceProfile
{
Q_GADGET
public:
enum ZdoCommand {
/* Requests */
/*Device and service discovery */
@ -136,9 +145,11 @@ public:
MgmtCacheResponse = 0x8037,
MgmtNetworkUpdateResponse = 0x8038
};
Q_ENUM(ZdoCommand)
static ZigbeeDeviceProfileAdpu parseAdpu(const QByteArray &adpu);
};
QDebug operator<<(QDebug debug, const ZigbeeDeviceProfileAdpu &deviceAdpu);
#endif // ZIGBEEDEVICEPROFILE_H

View File

@ -378,6 +378,9 @@ void ZigbeeNetwork::saveNode(ZigbeeNode *node)
settings.beginGroup(node->extendedAddress().toString());
settings.setValue("nwkAddress", node->shortAddress());
settings.setValue("macCapabilitiesFlag", node->m_macCapabilitiesFlag);
settings.setValue("manufacturerCode", node->m_manufacturerCode);
settings.setValue("nodeDescriptorRawData", node->m_nodeDescriptorRawData);
settings.setValue("powerDescriptorFlag", node->m_powerDescriptorFlag);
@ -491,6 +494,26 @@ bool ZigbeeNetwork::networkConfigurationAvailable() const
return m_extendedPanId != 0 && m_channel != 0;
}
ZigbeeNetworkReply *ZigbeeNetwork::createNetworkReply(const ZigbeeNetworkRequest &request)
{
ZigbeeNetworkReply *reply = new ZigbeeNetworkReply(request, this);
// Make sure the reply will be deleted
connect(reply, &ZigbeeNetworkReply::finished, reply, &ZigbeeNetworkReply::deleteLater);
return reply;
}
void ZigbeeNetwork::setReplyResponseData(ZigbeeNetworkReply *reply, const QByteArray &responseData)
{
reply->m_responseData = responseData;
}
void ZigbeeNetwork::finishNetworkReply(ZigbeeNetworkReply *reply, ZigbeeNetworkReply::Error error, Zigbee::ZigbeeStatus zigbeeStatus)
{
reply->m_error = error;
reply->m_zigbeeStatus = zigbeeStatus;
reply->finished();
}
void ZigbeeNetwork::onNodeStateChanged(ZigbeeNode::State state)
{
ZigbeeNode *node = qobject_cast<ZigbeeNode *>(sender());

View File

@ -94,6 +94,8 @@ public:
bool permitJoining() const;
void setPermitJoining(bool permitJoining);
// Network nodes
QList<ZigbeeNode *> nodes() const;
ZigbeeNode *coordinatorNode() const;
@ -150,6 +152,13 @@ protected:
bool networkConfigurationAvailable() const;
virtual ZigbeeNetworkReply *sendRequest(const ZigbeeNetworkRequest &request) = 0;
// Network reply methods
ZigbeeNetworkReply *createNetworkReply(const ZigbeeNetworkRequest &request = ZigbeeNetworkRequest());
void setReplyResponseData(ZigbeeNetworkReply *reply, const QByteArray &responseData);
void finishNetworkReply(ZigbeeNetworkReply *reply, ZigbeeNetworkReply::Error error = ZigbeeNetworkReply::ErrorNoError, Zigbee::ZigbeeStatus zigbeeStatus = Zigbee::ZigbeeStatusSuccess);
signals:
void settingsFileNameChanged(const QString &settingsFileName);
void serialPortNameChanged(const QString &serialPortName);

View File

@ -42,6 +42,11 @@ Zigbee::ZigbeeStatus ZigbeeNetworkReply::zigbeeStatus() const
return m_zigbeeStatus;
}
QByteArray ZigbeeNetworkReply::responseData() const
{
return m_responseData;
}
ZigbeeNetworkReply::ZigbeeNetworkReply(const ZigbeeNetworkRequest &request, QObject *parent) :
QObject(parent),
m_request(request)

View File

@ -37,20 +37,25 @@ class ZigbeeNetworkReply : public QObject
{
Q_OBJECT
friend class ZigbeeNetwork;
friend class ZigbeeNodeEndpoint;
public:
enum Error {
ErrorNoError,
ErrorZigbeeStatusError,
ErrorInterfaceError,
ErrorNetworkOffline,
ErrorNetworkNotImplemented,
ErrorUnknown
};
Q_ENUM(Error)
Error error() const;
ZigbeeNetworkRequest request() const;
Zigbee::ZigbeeStatus zigbeeStatus() const;
QByteArray responseData() const;
private:
explicit ZigbeeNetworkReply(const ZigbeeNetworkRequest &request, QObject *parent = nullptr);
@ -59,6 +64,7 @@ private:
bool m_finished = false;
Error m_error = ErrorNoError;
Zigbee::ZigbeeStatus m_zigbeeStatus = Zigbee::ZigbeeStatusSuccess;
QByteArray m_responseData;
signals:
void finished();

View File

@ -156,20 +156,6 @@ private:
quint16 m_shortAddress = 0;
ZigbeeAddress m_extendedAddress;
// Node descriptor information
QByteArray m_nodeDescriptorRawData;
NodeType m_nodeType = NodeTypeRouter;
FrequencyBand m_frequencyBand = FrequencyBand2400Mhz;
Relationship m_relationship = Parent;
quint16 m_manufacturerCode = 0;
bool m_complexDescriptorAvailable = false;
bool m_userDescriptorAvailable = false;
quint16 m_maximumRxSize = 0;
quint16 m_maximumTxSize = 0;
quint8 m_maximumBufferSize = 0;
// Server Mask
quint16 m_serverMask = 0;
bool m_isPrimaryTrustCenter = false;
@ -208,6 +194,20 @@ protected:
QList<ZigbeeNodeEndpoint *> m_endpoints;
// Node descriptor information
QByteArray m_nodeDescriptorRawData;
NodeType m_nodeType = NodeTypeRouter;
FrequencyBand m_frequencyBand = FrequencyBand2400Mhz;
Relationship m_relationship = Parent;
quint16 m_manufacturerCode = 0;
bool m_complexDescriptorAvailable = false;
bool m_userDescriptorAvailable = false;
quint16 m_maximumRxSize = 0;
quint16 m_maximumTxSize = 0;
quint8 m_maximumBufferSize = 0;
void setState(State state);
void setConnected(bool connected);