Introduce channel mask class and continue deCONZ UART protocol

This commit is contained in:
Simon Stürz 2020-04-05 16:13:30 +02:00
parent 95caaab5ce
commit 6449654820
14 changed files with 578 additions and 117 deletions

View File

@ -93,6 +93,16 @@ public:
}; };
Q_ENUM(NodeType) Q_ENUM(NodeType)
enum SourceAddressMode {
SourceAddressModeNone = 0x00,
SourceAddressModeShortSourceAddress = 0x01,
SourceAddressModeAddLastHoppAddress = 0x02, // since 0x0108
SourceAddressModeIeeeSourceAddress = 0x03,
SourceAddressModeShortAndIeeeSourceAddress = 0x04 // since 0x010B
};
Q_ENUM(SourceAddressMode)
enum SecurityMode { enum SecurityMode {
SecurityModeNoSecurity = 0x00, SecurityModeNoSecurity = 0x00,
SecurityModePreconfiguredNetworkKey = 0x01, SecurityModePreconfiguredNetworkKey = 0x01,
@ -109,4 +119,5 @@ public:
}; };
#endif // DECONZ_H #endif // DECONZ_H

View File

@ -47,6 +47,17 @@ Deconz::StatusCode ZigbeeInterfaceDeconzReply::statusCode() const
return m_statusCode; return m_statusCode;
} }
bool ZigbeeInterfaceDeconzReply::aborted() const
{
return m_aborted;
}
void ZigbeeInterfaceDeconzReply::abort()
{
m_aborted = true;
emit finished();
}
ZigbeeInterfaceDeconzReply::ZigbeeInterfaceDeconzReply(Deconz::Command command, quint8 sequenceNumber, QObject *parent) : ZigbeeInterfaceDeconzReply::ZigbeeInterfaceDeconzReply(Deconz::Command command, quint8 sequenceNumber, QObject *parent) :
QObject(parent), QObject(parent),
m_command(command), m_command(command),

View File

@ -47,9 +47,14 @@ public:
// Response content // Response content
Deconz::StatusCode statusCode() const; Deconz::StatusCode statusCode() const;
bool aborted() const;
void abort();
private: private:
explicit ZigbeeInterfaceDeconzReply(Deconz::Command command, quint8 sequenceNumber, QObject *parent = nullptr); explicit ZigbeeInterfaceDeconzReply(Deconz::Command command, quint8 sequenceNumber, QObject *parent = nullptr);
bool m_aborted = false;
// Request content // Request content
Deconz::Command m_command; Deconz::Command m_command;
quint8 m_sequenceNumber = 0; quint8 m_sequenceNumber = 0;

View File

@ -41,7 +41,7 @@ ZigbeeBridgeControllerDeconz::ZigbeeBridgeControllerDeconz(QObject *parent) :
m_watchdogTimer = new QTimer(this); m_watchdogTimer = new QTimer(this);
m_watchdogTimer->setSingleShot(false); m_watchdogTimer->setSingleShot(false);
m_watchdogTimer->setInterval(m_watchdogResetTimout * 1000); // Set the watchdog to 85 seconds, reset every 60 s m_watchdogTimer->setInterval(m_watchdogResetTimout * 1000); // Set the watchdog to 85 seconds, reset every 60 s
connect(m_watchdogTimer, &QTimer::timeout, this, &ZigbeeBridgeControllerDeconz::onWatchdogTimerTimeout); connect(m_watchdogTimer, &QTimer::timeout, this, &ZigbeeBridgeControllerDeconz::resetControllerWatchdog);
} }
ZigbeeBridgeControllerDeconz::~ZigbeeBridgeControllerDeconz() ZigbeeBridgeControllerDeconz::~ZigbeeBridgeControllerDeconz()
@ -49,6 +49,17 @@ ZigbeeBridgeControllerDeconz::~ZigbeeBridgeControllerDeconz()
qCDebug(dcZigbeeController()) << "Destroy controller"; qCDebug(dcZigbeeController()) << "Destroy controller";
} }
DeconzNetworkConfiguration ZigbeeBridgeControllerDeconz::networkConfiguration() const
{
return m_networkConfiguration;
}
void ZigbeeBridgeControllerDeconz::setFirmwareVersionString(const QString &firmwareVersion)
{
m_firmwareVersion = firmwareVersion;
emit firmwareVersionChanged(m_firmwareVersion);
}
ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestVersion() ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestVersion()
{ {
quint8 sequenceNumber = generateSequenceNumber(); quint8 sequenceNumber = generateSequenceNumber();
@ -120,14 +131,15 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestWriteParameter(
quint8 sequenceNumber = generateSequenceNumber(); quint8 sequenceNumber = generateSequenceNumber();
qCDebug(dcZigbeeController()) << "Request write parameter. SQN:" << sequenceNumber << parameter << ZigbeeUtils::convertByteArrayToHexString(data); qCDebug(dcZigbeeController()) << "Request write parameter. SQN:" << sequenceNumber << parameter << ZigbeeUtils::convertByteArrayToHexString(data);
quint16 payloadLength = static_cast<quint16>(1 + data.length());
QByteArray message; QByteArray message;
QDataStream stream(&message, QIODevice::WriteOnly); QDataStream stream(&message, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian); stream.setByteOrder(QDataStream::LittleEndian);
stream << static_cast<quint8>(Deconz::CommandWriteParameter); stream << static_cast<quint8>(Deconz::CommandWriteParameter);
stream << static_cast<quint8>(sequenceNumber); stream << static_cast<quint8>(sequenceNumber);
stream << static_cast<quint8>(0); // Reserverd stream << static_cast<quint8>(0); // Reserverd
stream << static_cast<quint16>(7 + 1 + data.length()); // Frame length 7 + 1 parameter + payload length stream << static_cast<quint16>(7 + payloadLength); // Frame length 7 + payload length
stream << static_cast<quint16>(1 + data.length()); // 1 parameter + payload length stream << static_cast<quint16>(payloadLength); // 1 parameter + parameter data length
stream << static_cast<quint8>(parameter); stream << static_cast<quint8>(parameter);
for (int i = 0; i < data.length(); i++) { for (int i = 0; i < data.length(); i++) {
stream << static_cast<quint8>(data.at(i)); stream << static_cast<quint8>(data.at(i));
@ -157,6 +169,31 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestChangeNetworkSt
return createReply(Deconz::CommandChangeNetworkState, sequenceNumber, this); return createReply(Deconz::CommandChangeNetworkState, sequenceNumber, this);
} }
ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestReadReceivedDataIndication(Deconz::SourceAddressMode sourceAddressMode)
{
quint8 sequenceNumber = generateSequenceNumber();
qCDebug(dcZigbeeController()) << "Request read received data indication. SQN:" << sequenceNumber << ZigbeeUtils::convertByteToHexString(sourceAddressMode);
quint16 payloadLength = 0;
if (sourceAddressMode != Deconz::SourceAddressModeNone) {
payloadLength = 1;
}
QByteArray message;
QDataStream stream(&message, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << static_cast<quint8>(Deconz::CommandApsDataIndication);
stream << static_cast<quint8>(sequenceNumber);
stream << static_cast<quint8>(0); // Reserverd
stream << static_cast<quint16>(7 + payloadLength); // Frame length + payload length
stream << static_cast<quint16>(payloadLength); // payload length
if (payloadLength > 0)
stream << static_cast<quint8>(sourceAddressMode);
m_interface->sendPackage(message);
return createReply(Deconz::CommandApsDataIndication, sequenceNumber, this);
}
quint8 ZigbeeBridgeControllerDeconz::generateSequenceNumber() quint8 ZigbeeBridgeControllerDeconz::generateSequenceNumber()
{ {
@ -464,6 +501,7 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters(
m_watchdogTimer->stop(); m_watchdogTimer->stop();
// Finished reading all parameters. Finish the independent reply in order to indicate the process has finished // Finished reading all parameters. Finish the independent reply in order to indicate the process has finished
emit networkConfigurationParameterChanged(m_networkConfiguration);
readNetworkParametersReply->m_statusCode = Deconz::StatusCodeSuccess; readNetworkParametersReply->m_statusCode = Deconz::StatusCodeSuccess;
readNetworkParametersReply->finished(); readNetworkParametersReply->finished();
return; return;
@ -491,10 +529,11 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters(
// Note: this value describes how much seconds are left until the watchdog triggers. Reset it right the way // Note: this value describes how much seconds are left until the watchdog triggers. Reset it right the way
if (watchdogTimeout < 15) { if (watchdogTimeout < 15) {
onWatchdogTimerTimeout(); resetControllerWatchdog();
} }
// Finished reading all parameters. Finish the independent reply in order to indicate the process has finished // Finished reading all parameters. Finish the independent reply in order to indicate the process has finished
emit networkConfigurationParameterChanged(m_networkConfiguration);
readNetworkParametersReply->m_statusCode = Deconz::StatusCodeSuccess; readNetworkParametersReply->m_statusCode = Deconz::StatusCodeSuccess;
readNetworkParametersReply->finished(); readNetworkParametersReply->finished();
}); });
@ -515,20 +554,121 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters(
return readNetworkParametersReply; return readNetworkParametersReply;
} }
ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::resetWatchdog() DeconzDeviceState ZigbeeBridgeControllerDeconz::parseDeviceStateFlag(quint8 deviceStateFlag)
{ {
QByteArray parameterData; DeconzDeviceState state;
QDataStream stream(&parameterData, QIODevice::WriteOnly); state.networkState = static_cast<Deconz::NetworkState>(deviceStateFlag & 0x03);
stream.setByteOrder(QDataStream::LittleEndian); state.aspDataConfirm = (deviceStateFlag & 0x04);
stream << m_watchdogTimeout; state.aspDataIndication = (deviceStateFlag & 0x08);
return requestWriteParameter(Deconz::ParameterWatchdogTtl, parameterData); state.configurationChanged = (deviceStateFlag & 0x10);
state.aspDataRequestFreeSlots = (deviceStateFlag & 0x20);
return state;
}
void ZigbeeBridgeControllerDeconz::processDeviceState(DeconzDeviceState deviceState)
{
qCDebug(dcZigbeeController()) << "Device state changed notification:" << deviceState.networkState
<< "ASPDE-DATA.confirm:" << deviceState.aspDataConfirm
<< "ASPDE-DATA.indication:" << deviceState.aspDataIndication
<< "configuration changed:" << deviceState.configurationChanged
<< "ASPDE-DATA.request free slots:" << deviceState.aspDataRequestFreeSlots;
// Check if we have to fech new data
if (deviceState.aspDataConfirm) {
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
QDataStream stream(reply->responseData());
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;
stream >> payloadLenght >> deviceStateFlag >> destinationAddressModeFlag;
Zigbee::DestinationAddressMode destinationAddressMode = static_cast<Zigbee::DestinationAddressMode>(destinationAddressModeFlag);
if (destinationAddressMode == Zigbee::DestinationAddressModeGroup || destinationAddressMode == Zigbee::DestinationAddressModeShortAddress)
stream >> destinationShortAddress;
if (destinationAddressMode == Zigbee::DestinationAddressModeUnicastIeee)
stream >> destinationIeeeAddress;
stream >> destinationEndpoint >> sourceAddressModeFlag;
Zigbee::SourceAddressMode sourceAddressMode = static_cast<Zigbee::SourceAddressMode>(sourceAddressModeFlag);
if (sourceAddressMode == Zigbee::SourceAddressModeShortAddress || sourceAddressMode == Zigbee::SourceAddressModeShortAndIeeeAddress)
stream >> sourceShortAddress;
if (sourceAddressMode == Zigbee::SourceAddressModeIeeeAddress || sourceAddressMode == Zigbee::SourceAddressModeShortAndIeeeAddress)
stream >> sourceIeeeAddress;
stream >> sourceEndpoint >> profileId >> clusterId >> asduLength;
// Fill asdu data
for (int i = 0; i < asduLength; i++) {
quint8 byte = 0;
stream >> byte;
asdu.append(static_cast<char>(byte));
}
stream >> reserved >> reserved >> lqi >> reserved >> reserved >> reserved >> reserved >> rssi;
qCDebug(dcZigbeeController()) << "Data indication received:";
qCDebug(dcZigbeeController()) << " Destination address mode:" << destinationAddressMode;
if (destinationAddressMode == Zigbee::DestinationAddressModeGroup)
qCDebug(dcZigbeeController()) << " Destination address (group):" << ZigbeeUtils::convertUint16ToHexString(destinationShortAddress);
if (destinationAddressMode == Zigbee::DestinationAddressModeShortAddress)
qCDebug(dcZigbeeController()) << " Destination short address:" << ZigbeeUtils::convertUint16ToHexString(destinationShortAddress);
if (destinationAddressMode == Zigbee::DestinationAddressModeUnicastIeee)
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));
});
}
} }
void ZigbeeBridgeControllerDeconz::onInterfaceAvailableChanged(bool available) void ZigbeeBridgeControllerDeconz::onInterfaceAvailableChanged(bool available)
{ {
if (available) { if (available) {
// FIXME: only start if the protocol version is >= 0x0108
m_watchdogTimer->start(); m_watchdogTimer->start();
} else { } else {
// Clean up any pending replies
foreach (quint8 id, m_pendingReplies.keys()) {
ZigbeeInterfaceDeconzReply *reply = m_pendingReplies.take(id);
reply->abort();
}
m_watchdogTimer->stop(); m_watchdogTimer->stop();
} }
@ -539,20 +679,20 @@ void ZigbeeBridgeControllerDeconz::onInterfacePackageReceived(const QByteArray &
{ {
QDataStream stream(package); QDataStream stream(package);
stream.setByteOrder(QDataStream::LittleEndian); stream.setByteOrder(QDataStream::LittleEndian);
quint8 command = 0; quint8 sequenceNumber = 0; quint8 status = 0; quint16 frameLength = 0; quint8 commandInt = 0; quint8 sequenceNumber = 0; quint8 statusInt = 0; quint16 frameLength = 0;
stream >> command >> sequenceNumber >> status >> frameLength; stream >> commandInt >> sequenceNumber >> statusInt >> frameLength;
qCDebug(dcZigbeeController()) << "Interface message received" QByteArray data = package.right(package.length() - 5);
<< static_cast<Deconz::Command>(command) Deconz::Command command = static_cast<Deconz::Command>(commandInt);
<< "SQN:" << sequenceNumber Deconz::StatusCode status = static_cast<Deconz::StatusCode>(statusInt);
<< static_cast<Deconz::StatusCode>(status) qCDebug(dcZigbeeController()) << "Interface message received" << command << "SQN:" << sequenceNumber
<< "Frame length:" << frameLength; << status << "Frame length:" << frameLength << ZigbeeUtils::convertByteArrayToHexString(data);
// Check if this is an interface response for a pending reply // Check if this is an interface response for a pending reply
if (m_pendingReplies.contains(sequenceNumber) && m_pendingReplies.value(sequenceNumber)->command() == command) { if (m_pendingReplies.contains(sequenceNumber) && m_pendingReplies.value(sequenceNumber)->command() == command) {
ZigbeeInterfaceDeconzReply *reply = m_pendingReplies.take(sequenceNumber); ZigbeeInterfaceDeconzReply *reply = m_pendingReplies.take(sequenceNumber);
reply->m_responseData = package.right(package.length() - 5); reply->m_responseData = data;
reply->m_statusCode = static_cast<Deconz::StatusCode>(status); reply->m_statusCode = status;
reply->finished(); reply->finished();
return; return;
} }
@ -563,12 +703,28 @@ void ZigbeeBridgeControllerDeconz::onInterfacePackageReceived(const QByteArray &
m_sequenceNumber = sequenceNumber; m_sequenceNumber = sequenceNumber;
// No request for this data, lets check which notification and process the data // No request for this data, lets check which notification and process the data
switch (command) {
case Deconz::CommandDeviceStateChanged: {
quint8 deviceStateFlag = 0;
stream >> deviceStateFlag;
processDeviceState(parseDeviceStateFlag(deviceStateFlag));
break;
}
default:
qCWarning(dcZigbeeController()) << "Unhandled interface package received" << command << "SQN:" << sequenceNumber
<< status << "Frame length:" << frameLength << ZigbeeUtils::convertByteArrayToHexString(data);
break;
}
} }
void ZigbeeBridgeControllerDeconz::onWatchdogTimerTimeout() void ZigbeeBridgeControllerDeconz::resetControllerWatchdog()
{ {
qCDebug(dcZigbeeController()) << "Reset application watchdog on the deCONZ controller"; QByteArray parameterData;
ZigbeeInterfaceDeconzReply *reply = resetWatchdog(); QDataStream stream(&parameterData, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << m_watchdogTimeout;
ZigbeeInterfaceDeconzReply *reply = requestWriteParameter(Deconz::ParameterWatchdogTtl, parameterData);
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [reply](){ connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [reply](){
if (reply->statusCode() != Deconz::StatusCodeSuccess) { if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Could not reset the application watchdog on the deCONZ controller." << reply->statusCode(); qCWarning(dcZigbeeController()) << "Could not reset the application watchdog on the deCONZ controller." << reply->statusCode();

View File

@ -41,24 +41,37 @@
#include "interface/zigbeeinterfacedeconz.h" #include "interface/zigbeeinterfacedeconz.h"
#include "interface/zigbeeinterfacedeconzreply.h" #include "interface/zigbeeinterfacedeconzreply.h"
// This struct describes the current deCONZ network configuration parameters
typedef struct DeconzNetworkConfiguration { typedef struct DeconzNetworkConfiguration {
ZigbeeAddress ieeeAddress; // R ZigbeeAddress ieeeAddress; // R
quint16 panId; // R quint16 panId = 0; // R
quint16 shortAddress; // R quint16 shortAddress = 0; // R
quint64 extendedPanId; // R quint64 extendedPanId = 0; // R
Deconz::NodeType nodeType; // RW Deconz::NodeType nodeType = Deconz::NodeTypeCoordinator; // RW
quint32 channelMask; // RW quint32 channelMask = 0; // RW
quint64 apsExtendedPanId; // RW quint64 apsExtendedPanId = 0; // RW
ZigbeeAddress trustCenterAddress; // RW ZigbeeAddress trustCenterAddress; // RW
Deconz::SecurityMode securityMode; // RW Deconz::SecurityMode securityMode = Deconz::SecurityModeNoMasterButTrustCenterKey; // RW
ZigbeeNetworkKey networkKey; // RW ZigbeeNetworkKey networkKey; // RW
quint8 currentChannel; // R quint8 currentChannel = 0; // R
quint16 protocolVersion; // R quint16 protocolVersion = 0; // R
quint8 networkUpdateId; // RW quint8 networkUpdateId = 0; // RW
quint32 watchdogTimeout; // RW quint32 watchdogTimeout = 85; // RW
} DeconzNetworkConfiguration; } DeconzNetworkConfiguration;
// This struct describes the deCONZ device state
typedef struct DeconzDeviceState {
Deconz::NetworkState networkState = Deconz::NetworkStateOffline;
bool aspDataConfirm = false;
bool aspDataIndication = false;
bool configurationChanged = false;
bool aspDataRequestFreeSlots = false;
} DeconzDeviceState;
class ZigbeeBridgeControllerDeconz : public ZigbeeBridgeController class ZigbeeBridgeControllerDeconz : public ZigbeeBridgeController
{ {
Q_OBJECT Q_OBJECT
@ -69,11 +82,16 @@ public:
explicit ZigbeeBridgeControllerDeconz(QObject *parent = nullptr); explicit ZigbeeBridgeControllerDeconz(QObject *parent = nullptr);
~ZigbeeBridgeControllerDeconz() override; ~ZigbeeBridgeControllerDeconz() override;
DeconzNetworkConfiguration networkConfiguration() const;
void setFirmwareVersionString(const QString &firmwareVersion);
ZigbeeInterfaceDeconzReply *requestVersion(); ZigbeeInterfaceDeconzReply *requestVersion();
ZigbeeInterfaceDeconzReply *requestDeviceState(); ZigbeeInterfaceDeconzReply *requestDeviceState();
ZigbeeInterfaceDeconzReply *requestReadParameter(Deconz::Parameter parameter); ZigbeeInterfaceDeconzReply *requestReadParameter(Deconz::Parameter parameter);
ZigbeeInterfaceDeconzReply *requestWriteParameter(Deconz::Parameter parameter, const QByteArray &data); ZigbeeInterfaceDeconzReply *requestWriteParameter(Deconz::Parameter parameter, const QByteArray &data);
ZigbeeInterfaceDeconzReply *requestStartJoinNetwork(); ZigbeeInterfaceDeconzReply *requestChangeNetworkState(Deconz::NetworkState networkState);
ZigbeeInterfaceDeconzReply *requestReadReceivedDataIndication(Deconz::SourceAddressMode sourceAddressMode = Deconz::SourceAddressModeShortSourceAddress);
private: private:
ZigbeeInterfaceDeconz *m_interface = nullptr; ZigbeeInterfaceDeconz *m_interface = nullptr;
@ -92,15 +110,18 @@ private:
// The data can be fetched from m_networkConfiguration on success. // The data can be fetched from m_networkConfiguration on success.
ZigbeeInterfaceDeconzReply *readNetworkParameters(); ZigbeeInterfaceDeconzReply *readNetworkParameters();
ZigbeeInterfaceDeconzReply *resetWatchdog(); DeconzDeviceState parseDeviceStateFlag(quint8 deviceStateFlag);
void processDeviceState(DeconzDeviceState deviceState);
signals: signals:
void networkConfigurationParameterChanged(const DeconzNetworkConfiguration &networkConfiguration);
private slots: private slots:
void onInterfaceAvailableChanged(bool available); void onInterfaceAvailableChanged(bool available);
void onInterfacePackageReceived(const QByteArray &package); void onInterfacePackageReceived(const QByteArray &package);
void onWatchdogTimerTimeout(); void resetControllerWatchdog();
public slots: public slots:
bool enable(const QString &serialPort, qint32 baudrate); bool enable(const QString &serialPort, qint32 baudrate);

View File

@ -29,6 +29,8 @@
#include "loggingcategory.h" #include "loggingcategory.h"
#include "zigbeeutils.h" #include "zigbeeutils.h"
#include <QDataStream>
ZigbeeNetworkDeconz::ZigbeeNetworkDeconz(QObject *parent) : ZigbeeNetworkDeconz::ZigbeeNetworkDeconz(QObject *parent) :
ZigbeeNetwork(parent) ZigbeeNetwork(parent)
{ {
@ -58,65 +60,117 @@ void ZigbeeNetworkDeconz::setPermitJoiningInternal(bool permitJoining)
Q_UNUSED(permitJoining) Q_UNUSED(permitJoining)
} }
void ZigbeeNetworkDeconz::startNetworkInternally()
{
qCDebug(dcZigbeeNetwork()) << "Start network internally";
// Check if we have to create a pan ID and select the channel
if (extendedPanId() == 0) {
m_createNewNetwork = true;
setExtendedPanId(ZigbeeUtils::generateRandomPanId());
qCDebug(dcZigbeeNetwork()) << "Created new PAN ID:" << extendedPanId();
}
if (securityConfiguration().networkKey().isNull()) {
m_createNewNetwork = true;
qCDebug(dcZigbeeNetwork()) << "Create a new network key";
ZigbeeNetworkKey key = ZigbeeNetworkKey::generateKey();
m_securityConfiguration.setNetworkKey(key);
}
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
// - If network running and we don't have configurations, write them
// - If network running and configurations match, we are done
// Read the firmware version
ZigbeeInterfaceDeconzReply *reply = m_controller->requestVersion();
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){
if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Request" << reply->command() << "finished with error" << reply->statusCode();
// FIXME: set an appropriate error
return;
}
qCDebug(dcZigbeeNetwork()) << "Version request finished" << reply->statusCode() << ZigbeeUtils::convertByteArrayToHexString(reply->responseData());
// Note: version is an uint32 value, little endian, but we can read the individual bytes in reversed order
quint8 majorVersion = static_cast<quint8>(reply->responseData().at(3));
quint8 minorVersion = static_cast<quint8>(reply->responseData().at(2));
Deconz::Platform platform = static_cast<Deconz::Platform>(reply->responseData().at(1));
QString firmwareVersion = QString("%1.%2").arg(majorVersion).arg(minorVersion);
qCDebug(dcZigbeeNetwork()) << "Firmware version" << firmwareVersion << platform;
// Read all network parameters
ZigbeeInterfaceDeconzReply *reply = m_controller->readNetworkParameters();
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply, firmwareVersion](){
if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Could not read network parameters during network start up." << reply->statusCode();
// FIXME: set an appropriate error
return;
}
qCDebug(dcZigbeeNetwork()) << "Reading network parameters finished successfully.";
QString protocolVersion = QString("%1.%2").arg(m_controller->networkConfiguration().protocolVersion & 0xFF00)
.arg(m_controller->networkConfiguration().protocolVersion & 0x00FF);
m_controller->setFirmwareVersionString(QString("%1 - %2").arg(firmwareVersion).arg(protocolVersion));
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();
// FIXME: set an appropriate error
return;
}
qCDebug(dcZigbeeNetwork()) << "Read device state finished successfully";
QDataStream stream(reply->responseData());
});
if (m_createNewNetwork) {
// Write the configurations which need to be changed
// Initialize coordinator node
} else {
// Get the network state and start the network if required
}
});
});
}
void ZigbeeNetworkDeconz::onControllerAvailableChanged(bool available) void ZigbeeNetworkDeconz::onControllerAvailableChanged(bool available)
{ {
qCDebug(dcZigbeeNetwork()) << "Hardware controller is" << (available ? "now available" : "not available"); qCDebug(dcZigbeeNetwork()) << "Hardware controller is" << (available ? "now available" : "not available");
if (!available) { if (!available) {
// foreach (ZigbeeNode *node, nodes()) {
// qobject_cast<ZigbeeNodeNxp *>(node)->setConnected(false);
// }
setError(ErrorHardwareUnavailable); setError(ErrorHardwareUnavailable);
m_permitJoining = false; m_permitJoining = false;
emit permitJoiningChanged(m_permitJoining); emit permitJoiningChanged(m_permitJoining);
//setStartingState(StartingStateNone);
setState(StateOffline); setState(StateOffline);
} else { } else {
m_error = ErrorNoError; m_error = ErrorNoError;
m_permitJoining = false; m_permitJoining = false;
emit permitJoiningChanged(m_permitJoining); emit permitJoiningChanged(m_permitJoining);
// Note: if we are factory resetting, erase also the data on the controller before resetting
// if (m_factoryResetting) {
// setStartingState(StartingStateErase);
// } else {
// setStartingState(StartingStateReset);
// }
setState(StateStarting); setState(StateStarting);
startNetworkInternally();
// FIXME: do this in the startig state machine
ZigbeeInterfaceDeconzReply *reply = m_controller->requestVersion();
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){
if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Request" << reply->command() << "finished with error" << reply->statusCode();
// FIXME: set an appropriate error
return;
}
qCDebug(dcZigbeeNetwork()) << "Version request finished" << reply->statusCode() << ZigbeeUtils::convertByteArrayToHexString(reply->responseData());
// Note: version is an uint32 value, little endian, but we can read the individual bytes in reversed order
quint8 majorVersion = static_cast<quint8>(reply->responseData().at(3));
quint8 minorVersion = static_cast<quint8>(reply->responseData().at(2));
Deconz::Platform platform = static_cast<Deconz::Platform>(reply->responseData().at(1));
QString firmwareVersion = QString("%1.%2").arg(majorVersion).arg(minorVersion);
qCDebug(dcZigbeeNetwork()) << "Firmware version" << firmwareVersion << platform;
// Read all network parameters
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();
// FIXME: set an appropriate error
return;
}
qCDebug(dcZigbeeNetwork()) << "Reading network parameters finished successfully.";
});
});
} }
} }
@ -124,23 +178,6 @@ void ZigbeeNetworkDeconz::startNetwork()
{ {
loadNetwork(); loadNetwork();
// Check if we have to create a pan ID and select the channel
if (extendedPanId() == 0) {
setExtendedPanId(ZigbeeUtils::generateRandomPanId());
qCDebug(dcZigbeeNetwork()) << "Created new PAN ID:" << extendedPanId();
}
if (securityConfiguration().networkKey().isNull()) {
qCDebug(dcZigbeeNetwork()) << "Create a new network key";
ZigbeeNetworkKey key = ZigbeeNetworkKey::generateKey();
m_securityConfiguration.setNetworkKey(key);
}
qCDebug(dcZigbeeNetwork()) << "Using network link key" << securityConfiguration().networkKey();
qCDebug(dcZigbeeNetwork()) << "Using global trust center link key" << securityConfiguration().globalTrustCenterLinkKey();
// TODO: get desired channel, by default use all
if (!m_controller->enable(serialPortName(), serialBaudrate())) { if (!m_controller->enable(serialPortName(), serialBaudrate())) {
m_permitJoining = false; m_permitJoining = false;
emit permitJoiningChanged(m_permitJoining); emit permitJoiningChanged(m_permitJoining);
@ -157,15 +194,26 @@ void ZigbeeNetworkDeconz::startNetwork()
void ZigbeeNetworkDeconz::stopNetwork() void ZigbeeNetworkDeconz::stopNetwork()
{ {
ZigbeeInterfaceDeconzReply *reply = m_controller->requestChangeNetworkState(Deconz::NetworkStateOffline);
setState(StateStopping);
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){
if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Could not leave network." << reply->statusCode();
// FIXME: set an appropriate error
return;
}
qCDebug(dcZigbeeNetwork()) << "Network left successfully";
setState(StateOffline);
});
} }
void ZigbeeNetworkDeconz::reset() void ZigbeeNetworkDeconz::reset()
{ {
// TODO
} }
void ZigbeeNetworkDeconz::factoryResetNetwork() void ZigbeeNetworkDeconz::factoryResetNetwork()
{ {
// Wipe settings, and reconfigure network
} }

View File

@ -30,7 +30,7 @@
#include <QObject> #include <QObject>
#include "zigbeenetwork.h" #include "zigbeenetwork.h"
#include "zigbeechannelmask.h"
#include "zigbeebridgecontrollerdeconz.h" #include "zigbeebridgecontrollerdeconz.h"
class ZigbeeNetworkDeconz : public ZigbeeNetwork class ZigbeeNetworkDeconz : public ZigbeeNetwork
@ -44,11 +44,14 @@ public:
private: private:
ZigbeeBridgeControllerDeconz *m_controller = nullptr; ZigbeeBridgeControllerDeconz *m_controller = nullptr;
bool m_networkRunning = false; bool m_networkRunning = false;
bool m_createNewNetwork = false;
protected: protected:
ZigbeeNode *createNode(QObject *parent) override; ZigbeeNode *createNode(QObject *parent) override;
void setPermitJoiningInternal(bool permitJoining) override; void setPermitJoiningInternal(bool permitJoining) override;
void startNetworkInternally();
private slots: private slots:
void onControllerAvailableChanged(bool available); void onControllerAvailableChanged(bool available);

View File

@ -17,6 +17,7 @@ SOURCES += \
nxp/zigbeenodeendpointnxp.cpp \ nxp/zigbeenodeendpointnxp.cpp \
nxp/zigbeenodenxp.cpp \ nxp/zigbeenodenxp.cpp \
zigbeebridgecontroller.cpp \ zigbeebridgecontroller.cpp \
zigbeechannelmask.cpp \
zigbeecluster.cpp \ zigbeecluster.cpp \
zigbeeclusterattribute.cpp \ zigbeeclusterattribute.cpp \
zigbeemanufacturer.cpp \ zigbeemanufacturer.cpp \
@ -47,6 +48,7 @@ HEADERS += \
nxp/zigbeenodeendpointnxp.h \ nxp/zigbeenodeendpointnxp.h \
nxp/zigbeenodenxp.h \ nxp/zigbeenodenxp.h \
zigbeebridgecontroller.h \ zigbeebridgecontroller.h \
zigbeechannelmask.h \
zigbeecluster.h \ zigbeecluster.h \
zigbeeclusterattribute.h \ zigbeeclusterattribute.h \
zigbeemanufacturer.h \ zigbeemanufacturer.h \

View File

@ -53,21 +53,22 @@ public:
Q_ENUM(ZigbeeProfile) Q_ENUM(ZigbeeProfile)
enum ZigbeeChannel { enum ZigbeeChannel {
ZigbeeChannel11, ZigbeeChannel11 = 0x00000800,
ZigbeeChannel12, ZigbeeChannel12 = 0x00001000,
ZigbeeChannel13, ZigbeeChannel13 = 0x00002000,
ZigbeeChannel14, ZigbeeChannel14 = 0x00004000,
ZigbeeChannel15, ZigbeeChannel15 = 0x00008000,
ZigbeeChannel16, ZigbeeChannel16 = 0x00010000,
ZigbeeChannel17, ZigbeeChannel17 = 0x00020000,
ZigbeeChannel18, ZigbeeChannel18 = 0x00040000,
ZigbeeChannel19, ZigbeeChannel19 = 0x00080000,
ZigbeeChannel20, ZigbeeChannel20 = 0x00100000,
ZigbeeChannel21, ZigbeeChannel21 = 0x00200000,
ZigbeeChannel22, ZigbeeChannel22 = 0x00400000,
ZigbeeChannel23, ZigbeeChannel23 = 0x00800000,
ZigbeeChannel24, ZigbeeChannel24 = 0x01000000,
ZigbeeChannel25 ZigbeeChannel25 = 0x02000000,
ZigbeeChannel26 = 0x04000000
}; };
Q_ENUM(ZigbeeChannel) Q_ENUM(ZigbeeChannel)
Q_DECLARE_FLAGS(ZigbeeChannels, ZigbeeChannel) Q_DECLARE_FLAGS(ZigbeeChannels, ZigbeeChannel)
@ -540,6 +541,13 @@ public:
}; };
Q_ENUM(DestinationAddressMode) Q_ENUM(DestinationAddressMode)
enum SourceAddressMode {
SourceAddressModeShortAddress = 0x02,
SourceAddressModeIeeeAddress = 0x03,
SourceAddressModeShortAndIeeeAddress = 0x04
};
Q_ENUM(SourceAddressMode)
enum ZigbeeZclStatus { enum ZigbeeZclStatus {
}; };

View File

@ -0,0 +1,106 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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 "zigbeechannelmask.h"
#include "zigbeeutils.h"
ZigbeeChannelMask::ZigbeeChannelMask()
{
}
ZigbeeChannelMask::ZigbeeChannelMask(quint32 channelMask) :
m_channelMask(channelMask)
{
}
ZigbeeChannelMask::ZigbeeChannelMask(Zigbee::ZigbeeChannels channels)
{
m_channelMask = static_cast<quint32>(channels);
}
quint32 ZigbeeChannelMask::toUInt32() const
{
return m_channelMask;
}
Zigbee::ZigbeeChannels ZigbeeChannelMask::channels() const
{
return static_cast<Zigbee::ZigbeeChannels>(m_channelMask);
}
bool ZigbeeChannelMask::isSet(Zigbee::ZigbeeChannel channel) const
{
return channels().testFlag(channel);
}
void ZigbeeChannelMask::setChannel(Zigbee::ZigbeeChannel channel)
{
// Set channel bit
m_channelMask |= 1 << channel;
}
void ZigbeeChannelMask::unsetChannel(Zigbee::ZigbeeChannel channel)
{
// Clear channel bit
m_channelMask &= ~(1 << channel);
}
ZigbeeChannelMask &ZigbeeChannelMask::operator=(const ZigbeeChannelMask &other)
{
m_channelMask = other.toUInt32();
return *this;
}
bool ZigbeeChannelMask::operator==(const ZigbeeChannelMask &other) const
{
return m_channelMask == other.toUInt32();
}
bool ZigbeeChannelMask::operator!=(const ZigbeeChannelMask &other) const
{
return !operator==(other);
}
QDebug operator<<(QDebug debug, const ZigbeeChannelMask &channelMaks)
{
debug.nospace() << "ChannelMask(" << ZigbeeUtils::convertUint32ToHexString(channelMaks.toUInt32());
debug.nospace() << ", [";
for (int i = 11; i <= 25; i++) {
if (channelMaks.isSet(static_cast<Zigbee::ZigbeeChannel>(i))) {
if (i < 25) {
debug.nospace() << i << ", ";
} else {
debug.nospace() << i;
}
}
}
debug.nospace() << "])";
return debug.space();
}

View File

@ -0,0 +1,71 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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 ZIGBEECHANNELMASK_H
#define ZIGBEECHANNELMASK_H
#include <QDebug>
#include <QObject>
#include "zigbee.h"
class ZigbeeChannelMask
{
Q_GADGET
public:
enum ChannelConfiguration {
ChannelConfigurationNoChannel = 0x00000000,
ChannelConfigurationPrimaryLightLink = 0x02108800,
ChannelConfigurationAllChannels = 0x07fff800
};
Q_ENUM(ChannelConfiguration)
ZigbeeChannelMask();
ZigbeeChannelMask(quint32 channelMask);
ZigbeeChannelMask(Zigbee::ZigbeeChannels channels);
quint32 toUInt32() const;
Zigbee::ZigbeeChannels channels() const;
bool isSet(Zigbee::ZigbeeChannel channel) const;
void setChannel(Zigbee::ZigbeeChannel channel);
void unsetChannel(Zigbee::ZigbeeChannel channel);
ZigbeeChannelMask &operator=(const ZigbeeChannelMask &other);
bool operator==(const ZigbeeChannelMask &other) const;
bool operator!=(const ZigbeeChannelMask &other) const;
private:
quint32 m_channelMask = 0;
};
QDebug operator<<(QDebug debug, const ZigbeeChannelMask &channelMaks);
#endif // ZIGBEECHANNELMASK_H

View File

@ -115,6 +115,20 @@ void ZigbeeNetwork::setChannel(quint32 channel)
emit channelChanged(m_channel); emit channelChanged(m_channel);
} }
ZigbeeChannelMask ZigbeeNetwork::channelMask() const
{
return m_channelMask;
}
void ZigbeeNetwork::setChannelMask(const ZigbeeChannelMask &channelMask)
{
if (m_channelMask == channelMask)
return;
m_channelMask = channelMask;
emit channelMaskChanged(m_channelMask);
}
ZigbeeSecurityConfiguration ZigbeeNetwork::securityConfiguration() const ZigbeeSecurityConfiguration ZigbeeNetwork::securityConfiguration() const
{ {
return m_securityConfiguration; return m_securityConfiguration;

View File

@ -32,6 +32,7 @@
#include <QSettings> #include <QSettings>
#include "zigbeenode.h" #include "zigbeenode.h"
#include "zigbeechannelmask.h"
#include "zigbeebridgecontroller.h" #include "zigbeebridgecontroller.h"
#include "zigbeesecurityconfiguration.h" #include "zigbeesecurityconfiguration.h"
@ -81,6 +82,9 @@ public:
quint32 channel() const; quint32 channel() const;
void setChannel(quint32 channel); void setChannel(quint32 channel);
ZigbeeChannelMask channelMask() const;
void setChannelMask(const ZigbeeChannelMask &channelMask);
ZigbeeSecurityConfiguration securityConfiguration() const; ZigbeeSecurityConfiguration securityConfiguration() const;
void setSecurityConfiguration(const ZigbeeSecurityConfiguration &securityConfiguration); void setSecurityConfiguration(const ZigbeeSecurityConfiguration &securityConfiguration);
@ -107,6 +111,7 @@ private:
// Network configurations // Network configurations
quint64 m_extendedPanId = 0; quint64 m_extendedPanId = 0;
quint32 m_channel = 0; quint32 m_channel = 0;
ZigbeeChannelMask m_channelMask = ZigbeeChannelMask(ZigbeeChannelMask::ChannelConfigurationAllChannels);
ZigbeeNode::NodeType m_nodeType = ZigbeeNode::NodeTypeCoordinator; ZigbeeNode::NodeType m_nodeType = ZigbeeNode::NodeTypeCoordinator;
QString m_settingsFileName = "/etc/nymea/nymea-zigbee.conf"; QString m_settingsFileName = "/etc/nymea/nymea-zigbee.conf";
@ -148,6 +153,7 @@ signals:
void extendedPanIdChanged(quint64 extendedPanId); void extendedPanIdChanged(quint64 extendedPanId);
void channelChanged(uint channel); void channelChanged(uint channel);
void channelMaskChanged(const ZigbeeChannelMask &channelMask);
void securityConfigurationChanged(const ZigbeeSecurityConfiguration &securityConfiguration); void securityConfigurationChanged(const ZigbeeSecurityConfiguration &securityConfiguration);
void nodeAdded(ZigbeeNode *node); void nodeAdded(ZigbeeNode *node);

View File

@ -505,7 +505,6 @@ void ZigbeeNode::onClusterAttributeChanged(const ZigbeeClusterAttribute &attribu
// qCDebug(dcZigbeeNode()) << " Data:" << data; // qCDebug(dcZigbeeNode()) << " Data:" << data;
//} //}
QDebug operator<<(QDebug debug, ZigbeeNode *node) QDebug operator<<(QDebug debug, ZigbeeNode *node)
{ {
debug.nospace().noquote() << "ZigbeeNode(" << ZigbeeUtils::convertUint16ToHexString(node->shortAddress()); debug.nospace().noquote() << "ZigbeeNode(" << ZigbeeUtils::convertUint16ToHexString(node->shortAddress());