nymea-zigbee/libnymea-zigbee/zigbeenetworkmanager.cpp

1299 lines
56 KiB
C++

#include "zigbeenetworkmanager.h"
#include "loggingcategory.h"
#include "zigbeeutils.h"
#include <QDateTime>
#include <QDataStream>
#include <QSettings>
ZigbeeNetworkManager::ZigbeeNetworkManager(QObject *parent) :
ZigbeeNetwork(ZigbeeNetwork::ControlerTypeNxp, parent)
{
}
QString ZigbeeNetworkManager::controllerFirmwareVersion() const
{
return m_controllerFirmwareVersion;
}
ZigbeeBridgeController *ZigbeeNetworkManager::controller() const
{
return m_controller;
}
bool ZigbeeNetworkManager::networkRunning() const
{
return state() == ZigbeeNetwork::StateRunning;
}
bool ZigbeeNetworkManager::permitJoining() const
{
return m_permitJoining;
}
void ZigbeeNetworkManager::setPermitJoining(bool permitJoining)
{
if (m_permitJoining == permitJoining)
return;
ZigbeeInterfaceReply *reply = m_controller->commandPermitJoin(0, (permitJoining ? 255 : 0));
connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onCommandPermitJoiningFinished);
}
void ZigbeeNetworkManager::setStartingState(ZigbeeNetworkManager::StartingState state)
{
if (m_startingState == state)
return;
m_startingState = state;
switch (m_startingState) {
case StartingStateNone:
break;
case StartingStateErase: {
m_networkRunning = false;
qCDebug(dcZigbeeNetwork()) << "Starting state changed: Erase persistant data";
ZigbeeInterfaceReply *reply = m_controller->commandErasePersistantData();
connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onCommandErasePersistentDataFinished);
break;
}
case StartingStateReset: {
m_networkRunning = false;
qCDebug(dcZigbeeNetwork()) << "Starting state changed: Reset controller";
ZigbeeInterfaceReply *reply = m_controller->commandResetController();
connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onCommandResetControllerFinished);
break;
}
case StartingStateGetVersion: {
qCDebug(dcZigbeeNetwork()) << "Starting state changed: Get controller version";
ZigbeeInterfaceReply *reply = m_controller->commandGetVersion();
connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onCommandGetVersionFinished);
break;
}
case StartingStateSetPanId: {
qCDebug(dcZigbeeNetwork()) << "Starting state changed: Set PAN ID";
if (extendedPanId() == 0) {
setExtendedPanId(ZigbeeUtils::generateRandomPanId());
}
ZigbeeInterfaceReply *reply = m_controller->commandSetExtendedPanId(extendedPanId());
connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onCommandSetExtendedPanIdFinished);
break;
}
case StartingStateSetChannel: {
// Create channel mask
// Note: normal number passed, that specific channel will be used || Bitfield: all channels would be 0x07FFF800
qCDebug(dcZigbeeNetwork()) << "Starting state changed: Set channel mask";
quint32 channelMask = 0;
if (channel() == 0) {
qCDebug(dcZigbeeNetwork()) << "Autoselect quitest channel for the zigbee network.";
} else {
channelMask |= 1 << (channel());
qCDebug(dcZigbeeNetwork()) << "Using channel" << channel() << "for the zigbee network.";
}
ZigbeeInterfaceReply *reply = m_controller->commandSetChannelMask(channelMask);
connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onCommandSetChannelMaskFinished);
break;
}
case StartingStateSetSecurity: {
qCDebug(dcZigbeeNetwork()) << "Starting state changed: Set security configuration";
ZigbeeInterfaceReply *reply = m_controller->commandSetSecurityStateAndKey(4, 0, 1, "5A6967426565416C6C69616E63653039");
connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onCommandSetSecurityFinished);
break;
}
case StartingStateSetNodeType: {
qCDebug(dcZigbeeNetwork()) << "Starting state changed: Set node type";
ZigbeeInterfaceReply *reply = m_controller->commandSetNodeType(ZigbeeNode::NodeTypeCoordinator);
connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onCommandSetNodeTypeFinished);
break;
}
case StartingStateStartNetwork: {
qCDebug(dcZigbeeNetwork()) << "Starting state changed: Starting network";
ZigbeeInterfaceReply *reply = m_controller->commandStartNetwork();
connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onCommandStartNetworkFinished);
break;
}
case StartingStateGetPermitJoinStatus: {
qCDebug(dcZigbeeNetwork()) << "Starting state changed: Get permit join status";
ZigbeeInterfaceReply *reply = m_controller->commandGetPermitJoinStatus();
connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onCommandGetPermitJoiningStatusFinished);
break;
}
case StartingStateReadeNodeDescriptor: {
qCDebug(dcZigbeeNetwork()) << "Starting state changed: Read coordinator node descriptor";
ZigbeeInterfaceReply *reply = m_controller->commandNodeDescriptorRequest(shortAddress());
connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onCommandNodeDescriptorRequestFinished);
break;
}
case StartingStateReadSimpleDescriptor: {
qCDebug(dcZigbeeNetwork()) << "Starting state changed: Read coordinator simple descriptor";
ZigbeeInterfaceReply *reply = m_controller->commandSimpleDescriptorRequest(shortAddress(), endPoint());
connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onCommandSimpleDescriptorRequestFinished);
break;
}
case StartingStateReadPowerDescriptor: {
qCDebug(dcZigbeeNetwork()) << "Starting state changed: Read coordinator power descriptor";
ZigbeeInterfaceReply *reply = m_controller->commandPowerDescriptorRequest(shortAddress());
connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onCommandPowerDescriptorRequestFinished);
break;
}
}
}
//void ZigbeeNetworkManager::requestMatchDescriptor(const quint16 &shortAddress, const Zigbee::ZigbeeProfile &profile)
//{
// // TargetAddress profile InputClusterCount InputClusterList OutputClusterCount OutputClusterList
// Q_UNUSED(profile)
// QByteArray data;
// QDataStream stream(&data, QIODevice::WriteOnly);
// stream << shortAddress;
// stream << static_cast<quint16>(0xFFFF);
// stream << static_cast<quint8>(0);
// stream << static_cast<quint16>(0);
// stream << static_cast<quint8>(0);
// stream << static_cast<quint16>(0);
// ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeMatchDescriptorRequest, data));
// request.setExpectedAdditionalMessageType(Zigbee::MessageTypeMatchDescriptorResponse);
// request.setDescription("Request match descriptors " + ZigbeeUtils::convertUint16ToHexString(shortAddress));
// request.setTimoutIntervall(5000);
// ZigbeeInterfaceReply *reply = controller()->sendRequest(request);
// connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onRequestMatchDescriptorFinished);
//}
void ZigbeeNetworkManager::onCommandResetControllerFinished()
{
ZigbeeInterfaceReply *reply = static_cast<ZigbeeInterfaceReply *>(sender());
reply->deleteLater();
if (reply->status() != ZigbeeInterfaceReply::Success) {
qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage();
return;
}
qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully";
}
void ZigbeeNetworkManager::onCommandSoftResetControllerFinished()
{
ZigbeeInterfaceReply *reply = static_cast<ZigbeeInterfaceReply *>(sender());
reply->deleteLater();
if (reply->status() != ZigbeeInterfaceReply::Success) {
qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage();
return;
}
qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully";
}
void ZigbeeNetworkManager::onCommandErasePersistentDataFinished()
{
ZigbeeInterfaceReply *reply = static_cast<ZigbeeInterfaceReply *>(sender());
reply->deleteLater();
if (reply->status() != ZigbeeInterfaceReply::Success) {
qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage();
return;
}
m_factoryResetting = false;
qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully";
if (m_startingState == StartingStateErase) {
setStartingState(StartingStateReset);
}
}
void ZigbeeNetworkManager::onCommandGetVersionFinished()
{
ZigbeeInterfaceReply *reply = static_cast<ZigbeeInterfaceReply *>(sender());
reply->deleteLater();
if (reply->status() != ZigbeeInterfaceReply::Success) {
qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage();
return;
}
if (reply->additionalMessage().data().count() != 4) {
qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << ":" << "Invalid payload size";
return;
}
qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully";
// Parse version
quint16 majorVersion = ZigbeeUtils::convertByteArrayToUint16(reply->additionalMessage().data().mid(0, 2));
quint16 minorVersion = ZigbeeUtils::convertByteArrayToUint16(reply->additionalMessage().data().mid(2, 2));
m_controllerFirmwareVersion = QString("%1.%2").arg(majorVersion).arg(minorVersion);
qCDebug(dcZigbeeNetwork()) << "Controller version:" << m_controllerFirmwareVersion;
if (m_startingState == StartingStateGetVersion) setStartingState(StartingStateSetPanId);
}
void ZigbeeNetworkManager::onCommandSetExtendedPanIdFinished()
{
ZigbeeInterfaceReply *reply = static_cast<ZigbeeInterfaceReply *>(sender());
reply->deleteLater();
if (reply->status() != ZigbeeInterfaceReply::Success) {
qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage();
return;
}
qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully";
if (m_startingState == StartingStateSetPanId) setStartingState(StartingStateSetChannel);
}
void ZigbeeNetworkManager::onCommandSetChannelMaskFinished()
{
ZigbeeInterfaceReply *reply = static_cast<ZigbeeInterfaceReply *>(sender());
reply->deleteLater();
if (reply->status() != ZigbeeInterfaceReply::Success) {
qCWarning(dcZigbeeNetwork()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage();
return;
}
qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully";
if (m_startingState == StartingStateSetChannel) setStartingState(StartingStateSetSecurity);
}
void ZigbeeNetworkManager::onCommandSetNodeTypeFinished()
{
ZigbeeInterfaceReply *reply = static_cast<ZigbeeInterfaceReply *>(sender());
reply->deleteLater();
if (reply->status() != ZigbeeInterfaceReply::Success) {
qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage();
return;
}
qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully";
if (m_startingState == StartingStateSetNodeType) setStartingState(StartingStateStartNetwork);
}
void ZigbeeNetworkManager::onCommandStartNetworkFinished()
{
ZigbeeInterfaceReply *reply = static_cast<ZigbeeInterfaceReply *>(sender());
reply->deleteLater();
if (reply->status() != ZigbeeInterfaceReply::Success) {
qCWarning(dcZigbeeNetwork()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage();
return;
}
qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully";
qCDebug(dcZigbeeController()) << reply->additionalMessage();
processNetworkFormed(reply->additionalMessage());
if (m_startingState == StartingStateStartNetwork) setStartingState(StartingStateGetPermitJoinStatus);
}
void ZigbeeNetworkManager::onCommandStartScanFinished()
{
ZigbeeInterfaceReply *reply = static_cast<ZigbeeInterfaceReply *>(sender());
reply->deleteLater();
if (reply->status() != ZigbeeInterfaceReply::Success) {
qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage();
return;
}
qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully";
qCDebug(dcZigbeeController()) << reply->additionalMessage();
processNetworkFormed(reply->additionalMessage());
}
void ZigbeeNetworkManager::onCommandGetPermitJoiningStatusFinished()
{
ZigbeeInterfaceReply *reply = static_cast<ZigbeeInterfaceReply *>(sender());
reply->deleteLater();
if (reply->status() != ZigbeeInterfaceReply::Success) {
qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage();
return;
}
qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully";
qCDebug(dcZigbeeController()) << reply->additionalMessage();
m_permitJoining = static_cast<bool>(reply->additionalMessage().data().at(0));
emit permitJoiningChanged(m_permitJoining);
if (m_startingState == StartingStateGetPermitJoinStatus) setStartingState(StartingStateReadeNodeDescriptor);
}
void ZigbeeNetworkManager::onCommandPermitJoiningFinished()
{
ZigbeeInterfaceReply *reply = static_cast<ZigbeeInterfaceReply *>(sender());
reply->deleteLater();
if (reply->status() != ZigbeeInterfaceReply::Success) {
qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage();
return;
}
qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully";
// Read the permit joining status back in order to update the state
ZigbeeInterfaceReply *getJoiningReply = m_controller->commandGetPermitJoinStatus();
connect(getJoiningReply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onCommandGetPermitJoiningStatusFinished);
}
void ZigbeeNetworkManager::onCommandRequestMatchDescriptorFinished()
{
ZigbeeInterfaceReply *reply = static_cast<ZigbeeInterfaceReply *>(sender());
reply->deleteLater();
if (reply->status() != ZigbeeInterfaceReply::Success) {
qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage();
return;
}
qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully";
qCDebug(dcZigbeeController()) << reply->additionalMessage();
}
void ZigbeeNetworkManager::onCommandSetSecurityFinished()
{
ZigbeeInterfaceReply *reply = static_cast<ZigbeeInterfaceReply *>(sender());
reply->deleteLater();
if (reply->status() != ZigbeeInterfaceReply::Success) {
qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage();
return;
}
qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully";
if (m_startingState == StartingStateSetSecurity) setStartingState(StartingStateSetNodeType);
}
void ZigbeeNetworkManager::onCommandNetworkAddressRequestFinished()
{
ZigbeeInterfaceReply *reply = static_cast<ZigbeeInterfaceReply *>(sender());
reply->deleteLater();
if (reply->status() != ZigbeeInterfaceReply::Success) {
qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage();
return;
}
qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully";
qCDebug(dcZigbeeController()) << reply->additionalMessage();
quint8 sequenceNumber = static_cast<quint8>(reply->additionalMessage().data().at(0));
quint8 statusCode = static_cast<quint8>(reply->additionalMessage().data().at(1));
quint64 ieeeAddress = ZigbeeUtils::convertByteArrayToUint64(reply->additionalMessage().data().mid(2, 8));
quint16 shortAddress = ZigbeeUtils::convertByteArrayToUint16(reply->additionalMessage().data().mid(10, 2));
quint8 deviceCount = static_cast<quint8>(reply->additionalMessage().data().at(12));
quint8 startIndex = static_cast<quint8>(reply->additionalMessage().data().at(13));
qCDebug(dcZigbeeNetwork()) << "Network address response:";
qCDebug(dcZigbeeNetwork()) << " SQN:" << sequenceNumber;
qCDebug(dcZigbeeNetwork()) << " Status:" << statusCode;
qCDebug(dcZigbeeNetwork()) << " Address:" << shortAddress << ZigbeeUtils::convertUint16ToHexString(shortAddress);
qCDebug(dcZigbeeNetwork()) << " Extended address:" << ZigbeeAddress(ieeeAddress);
qCDebug(dcZigbeeNetwork()) << " Deice count:" << deviceCount;
qCDebug(dcZigbeeNetwork()) << " Start index:" << startIndex;
}
void ZigbeeNetworkManager::onCommandAuthenticateDeviceFinished()
{
ZigbeeInterfaceReply *reply = static_cast<ZigbeeInterfaceReply *>(sender());
reply->deleteLater();
if (reply->status() != ZigbeeInterfaceReply::Success) {
qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage();
return;
}
qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully";
qCDebug(dcZigbeeController()) << reply->additionalMessage();
quint64 gatewayIeeeAddress = ZigbeeUtils::convertByteArrayToUint64(reply->additionalMessage().data().mid(0, 8));
QString encryptedKey = reply->additionalMessage().data().mid(8, 16).toHex();
QByteArray mic = reply->additionalMessage().data().mid(24, 4);
quint64 initiatorIeeeAddress = ZigbeeUtils::convertByteArrayToUint64(reply->additionalMessage().data().mid(28, 8));
quint8 activeKeySequenceNumber = static_cast<quint8>(reply->additionalMessage().data().at(36));
quint8 channel = static_cast<quint8>(reply->additionalMessage().data().at(37));
quint16 shortPan = ZigbeeUtils::convertByteArrayToUint16(reply->additionalMessage().data().mid(38, 2));
quint64 extendedPanId = ZigbeeUtils::convertByteArrayToUint64(reply->additionalMessage().data().mid(40, 8));
qCDebug(dcZigbeeNetwork()) << "Authentication response:";
qCDebug(dcZigbeeNetwork()) << " Gateways address:" << ZigbeeAddress(gatewayIeeeAddress);
qCDebug(dcZigbeeNetwork()) << " Key:" << encryptedKey;
qCDebug(dcZigbeeNetwork()) << " MIC:" << mic.toHex();
qCDebug(dcZigbeeNetwork()) << " Initiator address:" << ZigbeeAddress(initiatorIeeeAddress);
qCDebug(dcZigbeeNetwork()) << " Active key sequence number:" << activeKeySequenceNumber;
qCDebug(dcZigbeeNetwork()) << " Channel:" << channel;
qCDebug(dcZigbeeNetwork()) << " Short PAN ID:" << ZigbeeUtils::convertUint16ToHexString(shortPan);
qCDebug(dcZigbeeNetwork()) << " Extended PAN ID:" << extendedPanId << ZigbeeUtils::convertUint64ToHexString(extendedPanId);
}
void ZigbeeNetworkManager::processNetworkFormed(const ZigbeeInterfaceMessage &message)
{
// Parse network status
QByteArray data = message.data();
quint8 networkStatus = static_cast<quint8>(data.at(0));
QString networkStatusString;
bool success = false;
if (networkStatus == 0) {
networkStatusString = "joined";
success = true;
} else if (networkStatus == 1) {
networkStatusString = "created";
success = true;
} else if (networkStatus >= 128 && networkStatus <= 244) {
networkStatusString = "failed: Zigbee event code: " + QString::number(networkStatus);
} else {
networkStatusString = "unknown";
}
if (!success) {
qCWarning(dcZigbeeNetwork()) << "Forming network failed" << networkStatusString;
setError(ErrorZigbeeError);
setStartingState(StartingStateNone);
setState(StateDisconnected);
m_networkRunning = false;
return;
}
quint16 shortAddress = ZigbeeUtils::convertByteArrayToUint16(data.mid(1, 2));
quint64 extendedAddress = ZigbeeUtils::convertByteArrayToUint64(data.mid(3, 8));
// Parse network channel
quint8 channel = static_cast<quint8>(data.at(11));
qCDebug(dcZigbeeNetwork()).noquote() << "Network" << networkStatusString;
qCDebug(dcZigbeeNetwork()) << " Address:" << ZigbeeUtils::convertUint16ToHexString(shortAddress);
qCDebug(dcZigbeeNetwork()) << " Extended address:" << ZigbeeAddress(extendedAddress);
qCDebug(dcZigbeeNetwork()) << " Channel:" << channel;
qCDebug(dcZigbeeNetwork()) << " Extended PAN ID:" << extendedPanId();
qCDebug(dcZigbeeNetwork()) << " Permit joining:" << permitJoining();
m_networkRunning = true;
// Set the node information
setShortAddress(shortAddress);
setExtendedAddress(ZigbeeAddress(extendedAddress));
setChannel(channel);
if (!hasNode(this->shortAddress()))
addUnitializedNode(this);
}
void ZigbeeNetworkManager::onCommandEnableWhitelistFinished()
{
ZigbeeInterfaceReply *reply = static_cast<ZigbeeInterfaceReply *>(sender());
reply->deleteLater();
if (reply->status() != ZigbeeInterfaceReply::Success) {
qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage();
return;
}
qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully";
}
void ZigbeeNetworkManager::onCommandNodeDescriptorRequestFinished()
{
ZigbeeInterfaceReply *reply = static_cast<ZigbeeInterfaceReply *>(sender());
reply->deleteLater();
if (reply->status() != ZigbeeInterfaceReply::Success) {
qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage();
return;
}
qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully";
qCDebug(dcZigbeeController()) << reply->additionalMessage();
QByteArray data = reply->additionalMessage().data();
quint8 sequenceNumber = 0;
quint8 status = 0;
quint16 shortAddress = 0;
quint16 manufacturerCode = 0;
quint16 maximalRxSize = 0;
quint16 maximalTxSize = 0;
quint16 serverMask = 0;
quint8 descriptorFlag = 0;
quint8 macFlags = 0;
quint8 maxBufferSize = 0;
quint16 bitField = 0;
QDataStream stream(&data, QIODevice::ReadOnly);
stream >> sequenceNumber;
stream >> status;
stream >> shortAddress;
stream >> manufacturerCode;
stream >> maximalRxSize;
stream >> maximalTxSize;
stream >> serverMask;
stream >> descriptorFlag;
stream >> macFlags;
stream >> maxBufferSize;
stream >> bitField;
// Get node object
ZigbeeNode *node = getZigbeeNode(shortAddress);
if (!node) {
qCWarning(dcZigbeeNetwork()) << "Could not find node for address" << shortAddress << ZigbeeUtils::convertUint16ToHexString(shortAddress);
return;
}
// Set node data
node->setManufacturerCode(manufacturerCode);
node->setMaximumRxSize(maximalRxSize);
node->setMaximumTxSize(maximalTxSize);
node->setMaximumBufferSize(maxBufferSize);
node->setServerMask(serverMask);
node->setMacCapabilitiesFlag(macFlags);
node->setDescriptorFlag(descriptorFlag);
// Parse bit field
// 0-2 Bit = logical type, 0 = coordinator, 1 = router, 2 = end device
if (!ZigbeeUtils::checkBitUint16(bitField, 0) && !ZigbeeUtils::checkBitUint16(bitField, 1)) {
node->setNodeType(NodeTypeCoordinator);
} else if (!ZigbeeUtils::checkBitUint16(bitField, 0) && ZigbeeUtils::checkBitUint16(bitField, 1)) {
node->setNodeType(NodeTypeRouter);
} else if (ZigbeeUtils::checkBitUint16(bitField, 0) && !ZigbeeUtils::checkBitUint16(bitField, 1)) {
node->setNodeType(NodeTypeEndDevice);
}
node->setComplexDescriptorAvailable((bitField >> 3) & 0x0001);
node->setUserDescriptorAvailable((bitField >> 4) & 0x0001);
qCDebug(dcZigbeeNetwork()) << "Node descriptor:";
qCDebug(dcZigbeeNetwork()) << " Node type:" << node->nodeType();
qCDebug(dcZigbeeNetwork()) << " Sequence number:" << ZigbeeUtils::convertByteToHexString(sequenceNumber);
qCDebug(dcZigbeeNetwork()) << " Status:" << ZigbeeUtils::convertByteToHexString(status);
qCDebug(dcZigbeeNetwork()) << " Short address:" << ZigbeeUtils::convertUint16ToHexString(shortAddress);
qCDebug(dcZigbeeNetwork()) << " Manufacturer code:" << ZigbeeUtils::convertUint16ToHexString(manufacturerCode);
qCDebug(dcZigbeeNetwork()) << " Maximum Rx size:" << ZigbeeUtils::convertUint16ToHexString(node->maximumRxSize());
qCDebug(dcZigbeeNetwork()) << " Maximum Tx size:" << ZigbeeUtils::convertUint16ToHexString(node->maximumTxSize());
qCDebug(dcZigbeeNetwork()) << " Maximum buffer size:" << ZigbeeUtils::convertByteToHexString(node->maximumBufferSize());
qCDebug(dcZigbeeNetwork()) << " Server mask:" << ZigbeeUtils::convertUint16ToHexString(serverMask);
qCDebug(dcZigbeeNetwork()) << " Primary Trust center:" << node->isPrimaryTrustCenter();
qCDebug(dcZigbeeNetwork()) << " Backup Trust center:" << node->isBackupTrustCenter();
qCDebug(dcZigbeeNetwork()) << " Primary Binding cache:" << node->isPrimaryBindingCache();
qCDebug(dcZigbeeNetwork()) << " Backup Binding cache:" << node->isBackupBindingCache();
qCDebug(dcZigbeeNetwork()) << " Primary Discovery cache:" << node->isPrimaryDiscoveryCache();
qCDebug(dcZigbeeNetwork()) << " Backup Discovery cache:" << node->isBackupDiscoveryCache();
qCDebug(dcZigbeeNetwork()) << " Network Manager:" << node->isNetworkManager();
qCDebug(dcZigbeeNetwork()) << " Descriptor flag:" << ZigbeeUtils::convertByteToHexString(descriptorFlag);
qCDebug(dcZigbeeNetwork()) << " Extended active endpoint list available:" << node->extendedActiveEndpointListAvailable();
qCDebug(dcZigbeeNetwork()) << " Extended simple descriptor list available:" << node->extendedSimpleDescriptorListAvailable();
qCDebug(dcZigbeeNetwork()) << " MAC flags:" << ZigbeeUtils::convertByteToHexString(macFlags);
qCDebug(dcZigbeeNetwork()) << " Alternate PAN coordinator:" << node->alternatePanCoordinator();
qCDebug(dcZigbeeNetwork()) << " Device type:" << node->deviceType();
qCDebug(dcZigbeeNetwork()) << " Power source flag main power:" << node->powerSourceFlagMainPower();
qCDebug(dcZigbeeNetwork()) << " Receiver on when idle:" << node->receiverOnWhenIdle();
qCDebug(dcZigbeeNetwork()) << " Security capability:" << node->securityCapability();
qCDebug(dcZigbeeNetwork()) << " Allocate address:" << node->allocateAddress();
qCDebug(dcZigbeeNetwork()) << " Bit field:" << ZigbeeUtils::convertUint16ToHexString(bitField);
qCDebug(dcZigbeeNetwork()) << " Complex desciptor available:" << node->complexDescriptorAvailable();
qCDebug(dcZigbeeNetwork()) << " User desciptor available:" << node->userDescriptorAvailable();
if (m_startingState == StartingStateReadeNodeDescriptor) setStartingState(StartingStateReadSimpleDescriptor);
}
void ZigbeeNetworkManager::onCommandSimpleDescriptorRequestFinished()
{
ZigbeeInterfaceReply *reply = static_cast<ZigbeeInterfaceReply *>(sender());
reply->deleteLater();
if (reply->status() != ZigbeeInterfaceReply::Success) {
qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage();
return;
}
qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully";
qCDebug(dcZigbeeController()) << reply->additionalMessage();
QByteArray data = reply->additionalMessage().data();
quint8 sequenceNumber = 0;
quint8 status = 0;
quint16 shortAddress = 0;
quint8 length = 0;
quint8 endPoint = 0;
quint16 profileId = 0;
quint16 deviceId = 0;
quint8 bitField = 0;
quint8 inputClusterCount = 0;
quint8 outputClusterCount = 0;
QDataStream stream(&data, QIODevice::ReadOnly);
stream >> sequenceNumber;
stream >> status;
stream >> shortAddress;
stream >> length;
if (length == 0) {
qCWarning(dcZigbeeNetwork()) << "Simple node descriptior has a length of 0.";
return;
}
stream >> endPoint;
stream >> profileId;
stream >> deviceId;
stream >> bitField;
qCDebug(dcZigbeeNetwork()) << "Node simple descriptor:";
qCDebug(dcZigbeeNetwork()) << " Sequence number:" << ZigbeeUtils::convertByteToHexString(sequenceNumber);
qCDebug(dcZigbeeNetwork()) << " Status:" << ZigbeeUtils::convertByteToHexString(status);
qCDebug(dcZigbeeNetwork()) << " Nwk address:" << ZigbeeUtils::convertUint16ToHexString(shortAddress);
qCDebug(dcZigbeeNetwork()) << " Lenght:" << ZigbeeUtils::convertByteToHexString(length);
qCDebug(dcZigbeeNetwork()) << " End Point:" << ZigbeeUtils::convertByteToHexString(endPoint);
qCDebug(dcZigbeeNetwork()) << " Profile:" << ZigbeeUtils::profileIdToString(static_cast<Zigbee::ZigbeeProfile>(profileId));
if (profileId == Zigbee::ZigbeeProfileLightLink) {
qCDebug(dcZigbeeNetwork()) << " Device ID:" << ZigbeeUtils::convertUint16ToHexString(deviceId) << static_cast<Zigbee::LightLinkDevice>(deviceId);
} else {
qCDebug(dcZigbeeNetwork()) << " Device ID:" << ZigbeeUtils::convertUint16ToHexString(deviceId) << static_cast<Zigbee::HomeAutomationDevice>(deviceId);
}
qCDebug(dcZigbeeNetwork()) << " Bit field:" << ZigbeeUtils::convertByteToHexString(bitField);
stream >> inputClusterCount;
qCDebug(dcZigbeeNetwork()) << " Input clusters: (" << inputClusterCount << ")";
for (int i = 0; i < inputClusterCount; i+=1) {
quint16 clusterId = 0;
stream >> clusterId;
qCDebug(dcZigbeeNetwork()) << " Cluster ID:" << ZigbeeUtils::convertUint16ToHexString(clusterId) << ZigbeeUtils::clusterIdToString(static_cast<Zigbee::ClusterId>(clusterId));
}
stream >> outputClusterCount;
qCDebug(dcZigbeeNetwork()) << " Output clusters: (" << outputClusterCount << ")";
for (int i = 0; i < outputClusterCount; i+=1) {
if (stream.atEnd()) {
qCWarning(dcZigbeeNode()) << "Data stream already at the end but more data expected. Looks like the firmware doesn't provide more data.";
break;
}
quint16 clusterId = 0;
stream >> clusterId;
qCDebug(dcZigbeeNetwork()) << " Cluster ID:" << ZigbeeUtils::convertUint16ToHexString(clusterId) << ZigbeeUtils::clusterIdToString(static_cast<Zigbee::ClusterId>(clusterId));
}
// Get node object
ZigbeeNode *node = getZigbeeNode(shortAddress);
if (!node) {
qCWarning(dcZigbeeNetwork()) << "Could not find node for address" << shortAddress << ZigbeeUtils::convertUint16ToHexString(shortAddress);
if (m_startingState == StartingStateReadSimpleDescriptor) setStartingState(StartingStateReadPowerDescriptor);
return;
}
// Set node data
node->setEndPoint(endPoint);
node->setZigbeeProfile(static_cast<Zigbee::ZigbeeProfile>(profileId));
node->setDeviceId(deviceId);
if (m_startingState == StartingStateReadSimpleDescriptor) setStartingState(StartingStateReadPowerDescriptor);
}
void ZigbeeNetworkManager::onCommandPowerDescriptorRequestFinished()
{
ZigbeeInterfaceReply *reply = static_cast<ZigbeeInterfaceReply *>(sender());
reply->deleteLater();
// Note: get the short address from the request data
QByteArray requestData = reply->request().message().data();
quint16 shortAddress;
QDataStream stream(&requestData, QIODevice::ReadOnly);
stream >> shortAddress;
if (reply->status() != ZigbeeInterfaceReply::Success) {
qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage();
// Note: the power descriptor is the last request from the initialization
ZigbeeNode *node = getZigbeeNode(shortAddress);
if (!node) {
qCWarning(dcZigbeeNetwork()) << "Could not find node for address" << shortAddress << ZigbeeUtils::convertUint16ToHexString(shortAddress);
if (m_startingState == StartingStateReadSimpleDescriptor) setStartingState(StartingStateReadPowerDescriptor);
return;
}
if (node->state() != ZigbeeNode::StateInitialized) {
node->setState(ZigbeeNode::StateInitialized);
}
return;
}
qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully";
qCDebug(dcZigbeeController()) << reply->additionalMessage();
quint8 sequenceNumber = static_cast<quint8>(reply->additionalMessage().data().at(0));
quint8 status = static_cast<quint8>(reply->additionalMessage().data().at(1));
quint16 bitField = static_cast<quint16>(reply->additionalMessage().data().at(2));
bitField <<= 8;
bitField |= static_cast<quint8>(reply->additionalMessage().data().at(3));
// Bit 0 - 3 Power mode
// 0000: Receiver configured according to “Receiver on when idle” MAC flag in the Node Descriptor
// 0001: Receiver switched on periodically
// 0010: Receiver switched on when stimulated, e.g. by pressing a button
ZigbeeNode::PowerMode powerMode = PowerModeAlwaysOn;
if (!ZigbeeUtils::checkBitUint16(bitField, 0) && !ZigbeeUtils::checkBitUint16(bitField, 1)) {
powerMode = PowerModeAlwaysOn;
} else if (ZigbeeUtils::checkBitUint16(bitField, 0) && !ZigbeeUtils::checkBitUint16(bitField, 1)) {
powerMode = PowerModeOnPeriodically;
} else if (!ZigbeeUtils::checkBitUint16(bitField, 0) && ZigbeeUtils::checkBitUint16(bitField, 1)) {
powerMode = PowerModeOnWhenStimulated;
}
// Bit 4 - 7 Available power sources
// Bit 0: Permanent mains supply
// Bit 1: Rechargeable battery
// Bit 2: Disposable battery
// Bit 4: Reserved
QList<ZigbeeNode::PowerSource> availablePowerSources;
if (ZigbeeUtils::checkBitUint16(bitField, 4)) {
availablePowerSources.append(PowerSourcePermanentMainSupply);
} else if (ZigbeeUtils::checkBitUint16(bitField, 5)) {
availablePowerSources.append(PowerSourceRecharchableBattery);
} else if (ZigbeeUtils::checkBitUint16(bitField, 6)) {
availablePowerSources.append(PowerSourceDisposableBattery);
}
// Bit 8 - 11 Active source: according to the same schema as available power sources
ZigbeeNode::PowerSource powerSource = PowerSourcePermanentMainSupply;
if (ZigbeeUtils::checkBitUint16(bitField, 8)) {
powerSource = PowerSourcePermanentMainSupply;
} else if (ZigbeeUtils::checkBitUint16(bitField, 9)) {
powerSource = PowerSourceRecharchableBattery;
} else if (ZigbeeUtils::checkBitUint16(bitField, 10)) {
powerSource = PowerSourceDisposableBattery;
}
// Bit 12 - 15: Battery level if available
// 0000: Critically low
// 0100: Approximately 33%
// 1000: Approximately 66%
// 1100: Approximately 100% (near fully charged)
ZigbeeNode::PowerLevel powerLevel = PowerLevelCriticalLow;
if (!ZigbeeUtils::checkBitUint16(bitField, 14) && !ZigbeeUtils::checkBitUint16(bitField, 15)) {
powerLevel = PowerLevelCriticalLow;
} else if (ZigbeeUtils::checkBitUint16(bitField, 14) && !ZigbeeUtils::checkBitUint16(bitField, 15)) {
powerLevel = PowerLevelLow;
} else if (!ZigbeeUtils::checkBitUint16(bitField, 14) && ZigbeeUtils::checkBitUint16(bitField, 15)) {
powerLevel = PowerLevelOk;
} else if (ZigbeeUtils::checkBitUint16(bitField, 14) && ZigbeeUtils::checkBitUint16(bitField, 15)) {
powerLevel = PowerLevelFull;
}
qCDebug(dcZigbeeNetwork()) << "Node power descriptor:";
qCDebug(dcZigbeeNetwork()) << " Sequence number:" << ZigbeeUtils::convertByteToHexString(sequenceNumber);
qCDebug(dcZigbeeNetwork()) << " Status:" << ZigbeeUtils::convertByteToHexString(status);
qCDebug(dcZigbeeNetwork()) << " Bitfiled:" << ZigbeeUtils::convertUint16ToHexString(bitField);
qCDebug(dcZigbeeNetwork()) << " Power mode:" << m_powerMode;
qCDebug(dcZigbeeNetwork()) << " Available power sources:";
foreach (const PowerSource &source, availablePowerSources) {
qCDebug(dcZigbeeNetwork()) << " " << source;
}
qCDebug(dcZigbeeNetwork()) << " Power source:" << powerSource;
qCDebug(dcZigbeeNetwork()) << " Power level:" << powerLevel;
// Get node object
ZigbeeNode *node = getZigbeeNode(shortAddress);
if (!node) {
qCWarning(dcZigbeeNetwork()) << "Could not find node for address" << shortAddress << ZigbeeUtils::convertUint16ToHexString(shortAddress);
if (m_startingState == StartingStateReadSimpleDescriptor) setStartingState(StartingStateReadPowerDescriptor);
return;
}
// Set node data
node->setPowerMode(powerMode);
node->setPowerSource(powerSource);
node->setAvailablePowerSources(availablePowerSources);
node->setPowerLevel(powerLevel);
// Note: the power descriptor is the last request from the initialization
if (node->state() != ZigbeeNode::StateInitialized) {
node->setState(ZigbeeNode::StateInitialized);
}
// Note: the power descriptor request for the coordinator is the last step from the network init process
if (m_startingState == StartingStateReadPowerDescriptor) {
setStartingState(StartingStateNone);
setState(StateRunning);
foreach (ZigbeeNode *node, nodes()) {
node->setConnected(true);
}
}
}
void ZigbeeNetworkManager::onCommandInitiateTouchLinkFinished()
{
ZigbeeInterfaceReply *reply = static_cast<ZigbeeInterfaceReply *>(sender());
reply->deleteLater();
if (reply->status() != ZigbeeInterfaceReply::Success) {
qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage();
return;
}
qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully";
}
void ZigbeeNetworkManager::onCommandTouchLinkFactoryResetFinished()
{
ZigbeeInterfaceReply *reply = static_cast<ZigbeeInterfaceReply *>(sender());
reply->deleteLater();
if (reply->status() != ZigbeeInterfaceReply::Success) {
qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage();
return;
}
qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully";
}
void ZigbeeNetworkManager::onCommandRequestLinkQualityFinished()
{
ZigbeeInterfaceReply *reply = static_cast<ZigbeeInterfaceReply *>(sender());
reply->deleteLater();
if (reply->status() != ZigbeeInterfaceReply::Success) {
qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage();
return;
}
qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully";
qCDebug(dcZigbeeController()) << reply->additionalMessage();
quint8 sequenceNumber = static_cast<quint8>(reply->additionalMessage().data().at(0));
quint8 statusCode = static_cast<quint8>(reply->additionalMessage().data().at(1));
quint8 neighborTableEntries = static_cast<quint8>(reply->additionalMessage().data().at(2));
quint8 neighborTableListCount = static_cast<quint8>(reply->additionalMessage().data().at(3));
quint8 startIndex = static_cast<quint8>(reply->additionalMessage().data().at(4));
qCDebug(dcZigbeeNetwork()) << "LQI response:";
qCDebug(dcZigbeeNetwork()) << " SQN:" << ZigbeeUtils::convertByteToHexString(sequenceNumber);
qCDebug(dcZigbeeNetwork()) << " Status:" << ZigbeeUtils::convertByteToHexString(statusCode);
qCDebug(dcZigbeeNetwork()) << " Neighbor table entries:" << neighborTableEntries;
qCDebug(dcZigbeeNetwork()) << " Neighbor table list count:" << neighborTableListCount;
qCDebug(dcZigbeeNetwork()) << " Start index:" << startIndex;
int offset = 5;
// Note: according to docs, if the table has no neigbors the list will be empty
if (neighborTableEntries == 0) {
qCDebug(dcZigbeeNetwork()) << " There are no neigbors";
return;
}
for (int i = startIndex; i < neighborTableListCount; i++) {
quint16 shortAddress = ZigbeeUtils::convertByteArrayToUint16(reply->additionalMessage().data().mid(offset, 2));
quint64 panId = ZigbeeUtils::convertByteArrayToUint64(reply->additionalMessage().data().mid(offset + 2, 8));
quint64 ieeeAddress = ZigbeeUtils::convertByteArrayToUint64(reply->additionalMessage().data().mid(offset + 10, 8));
quint8 depth = static_cast<quint8>(reply->additionalMessage().data().at(offset + 18));
quint8 linkQuality = static_cast<quint8>(reply->additionalMessage().data().at(offset + 19));
quint8 bitMap = static_cast<quint8>(reply->additionalMessage().data().at(offset + 20));
offset += 21;
qCDebug(dcZigbeeNetwork()) << " Neighbor:" << i;
qCDebug(dcZigbeeNetwork()) << " Address:" << ZigbeeUtils::convertUint16ToHexString(shortAddress);
qCDebug(dcZigbeeNetwork()) << " PAN id:" << panId;
qCDebug(dcZigbeeNetwork()) << " Extended address:" << ZigbeeAddress(ieeeAddress);
qCDebug(dcZigbeeNetwork()) << " Depth:" << depth;
qCDebug(dcZigbeeNetwork()) << " Link quality:" << linkQuality;
qCDebug(dcZigbeeNetwork()) << " BitMap:" << ZigbeeUtils::convertByteToHexString(bitMap);
foreach (ZigbeeNode *node, nodes()) {
if (node->extendedAddress() == ZigbeeAddress(ieeeAddress)) {
node->setShortAddress(shortAddress);
}
}
}
}
void ZigbeeNetworkManager::processLoggingMessage(const ZigbeeInterfaceMessage &message)
{
quint8 logLevel = static_cast<quint8>(message.data().at(0));
QString logMessage = QString::fromUtf8(message.data().right(message.data().count() - 1));
QString logLevelString;
switch (logLevel) {
case 0:
logLevelString = "Emergency:";
break;
case 1:
logLevelString = "Alert:";
break;
case 2:
logLevelString = "Critical:";
break;
case 3:
logLevelString = "Error:";
break;
case 4:
logLevelString = "Warning:";
break;
case 5:
logLevelString = "Notice:";
break;
case 6:
logLevelString = "Information:";
break;
case 7:
logLevelString = "Debug:";
break;
default:
logLevelString = "Unknown:";
break;
}
if (logLevel < 5) {
qCWarning(dcZigbeeController()).noquote() << "ControllerLog:" << logLevelString << logMessage;
} else {
qCDebug(dcZigbeeController()).noquote() << "ControllerLog:" << logLevelString << logMessage;
}
}
void ZigbeeNetworkManager::processFactoryNewRestart(const ZigbeeInterfaceMessage &message)
{
quint8 controllerStatus = static_cast<quint8>(message.data().at(0));
QString controllerStatusString;
switch (controllerStatus) {
case 0:
controllerStatusString = "startup";
break;
case 2:
controllerStatusString = "NRF start";
break;
case 6:
controllerStatusString = "running";
break;
default:
break;
}
qCDebug(dcZigbeeNetwork()) << "Restart finished. Current controller state:" << controllerStatusString;
if (m_startingState == StartingStateReset) setStartingState(StartingStateGetVersion);
}
void ZigbeeNetworkManager::processNodeClusterList(const ZigbeeInterfaceMessage &message)
{
quint8 sourceEndpoint = static_cast<quint8>(message.data().at(0));
quint16 profileId = static_cast<quint8>(message.data().at(1));
profileId <<= 8;
profileId |= static_cast<quint8>(message.data().at(2));
qCDebug(dcZigbeeController()) << "Node cluster list received:";
qCDebug(dcZigbeeController()) << " Souce endpoint:" << sourceEndpoint;
qCDebug(dcZigbeeController()) << " Profile:" << ZigbeeUtils::profileIdToString(static_cast<Zigbee::ZigbeeProfile>(profileId));
QByteArray clusterListData = message.data().right(message.data().count() - 3);
for (int i = 0; i < clusterListData.count(); i += 2) {
quint16 clusterId = static_cast<quint16>(clusterListData.at(i));
clusterId <<= 8;
clusterId |= clusterListData .at(i + 1);
qCDebug(dcZigbeeController()) << " Cluster ID:" << ZigbeeUtils::clusterIdToString(static_cast<Zigbee::ClusterId>(clusterId));
}
}
void ZigbeeNetworkManager::processNodeAttributeList(const ZigbeeInterfaceMessage &message)
{
quint8 sourceEndpoint = static_cast<quint8>(message.data().at(0));
quint16 profileId = ZigbeeUtils::convertByteArrayToUint16(message.data().mid(1, 2));
quint16 clusterId = ZigbeeUtils::convertByteArrayToUint16(message.data().mid(3, 2));
qCDebug(dcZigbeeController()) << "Node attribute list received:";
qCDebug(dcZigbeeController()) << " Souce endpoint:" << sourceEndpoint;
qCDebug(dcZigbeeController()) << " Profile:" << ZigbeeUtils::profileIdToString(static_cast<Zigbee::ZigbeeProfile>(profileId));
qCDebug(dcZigbeeController()) << " Cluster ID:" << ZigbeeUtils::clusterIdToString(static_cast<Zigbee::ClusterId>(clusterId));
QByteArray attributeListData = message.data().right(message.data().count() - 5);
for (int i = 0; i < attributeListData.count(); i += 2) {
quint16 attribute = ZigbeeUtils::convertByteArrayToUint16(attributeListData.mid(i, 2));
qCDebug(dcZigbeeController()) << " Attribute:" << ZigbeeUtils::convertUint16ToHexString(attribute);
}
}
void ZigbeeNetworkManager::processNodeCommandIdList(const ZigbeeInterfaceMessage &message)
{
quint8 sourceEndpoint = static_cast<quint8>(message.data().at(0));
quint16 profileId = ZigbeeUtils::convertByteArrayToUint16(message.data().mid(1, 2));
quint16 clusterId = ZigbeeUtils::convertByteArrayToUint16(message.data().mid(3, 2));
qCDebug(dcZigbeeController()) << "Node command list received:";
qCDebug(dcZigbeeController()) << " Souce endpoint:" << sourceEndpoint;
qCDebug(dcZigbeeController()) << " Profile:" << ZigbeeUtils::profileIdToString(static_cast<Zigbee::ZigbeeProfile>(profileId));
qCDebug(dcZigbeeController()) << " Cluster ID:" << ZigbeeUtils::clusterIdToString(static_cast<Zigbee::ClusterId>(clusterId));
QByteArray commandListData = message.data().right(message.data().count() - 5);
for (int i = 0; i < commandListData.count(); i++) {
quint8 attribute = static_cast<quint8>(commandListData.at(i));
qCDebug(dcZigbeeController()) << " Command:" << ZigbeeUtils::convertByteToHexString(attribute);
}
}
void ZigbeeNetworkManager::processDeviceAnnounce(const ZigbeeInterfaceMessage &message)
{
quint16 shortAddress = ZigbeeUtils::convertByteArrayToUint16(message.data().mid(0, 2));
quint64 ieeeAddress = ZigbeeUtils::convertByteArrayToUint64(message.data().mid(2, 8));
quint8 macCapabilitiesFlag = static_cast<quint8>(message.data().at(10));
qCDebug(dcZigbeeNetwork()) << "Device announced:";
qCDebug(dcZigbeeNetwork()) << " Address:" << ZigbeeUtils::convertUint16ToHexString(shortAddress);
qCDebug(dcZigbeeNetwork()) << " Extended address:" << ZigbeeAddress(ieeeAddress);
qCDebug(dcZigbeeNetwork()) << " Mac capabilities:" << ZigbeeUtils::convertByteToHexString(macCapabilitiesFlag);
ZigbeeNode *node = createNode();
node->setShortAddress(shortAddress);
node->setExtendedAddress(ZigbeeAddress(ieeeAddress));
node->setMacCapabilitiesFlag(macCapabilitiesFlag);
qCDebug(dcZigbeeNetwork()) << " Node:" << node;
// FIXME: check if node already added, and if we have to update it
addUnitializedNode(node);
node->setState(StateInitializing);
ZigbeeInterfaceReply *reply = nullptr;
reply = m_controller->commandAuthenticateDevice(node->extendedAddress(), securityConfiguration().globalTrustCenterLinkKey());
connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onCommandAuthenticateDeviceFinished);
reply = m_controller->commandNodeDescriptorRequest(node->shortAddress());
connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onCommandNodeDescriptorRequestFinished);
reply = m_controller->commandSimpleDescriptorRequest(node->shortAddress(), endPoint());
connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onCommandSimpleDescriptorRequestFinished);
reply = m_controller->commandPowerDescriptorRequest(node->shortAddress());
connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onCommandPowerDescriptorRequestFinished);
}
void ZigbeeNetworkManager::processAttributeReport(const ZigbeeInterfaceMessage &message)
{
quint8 sequenceNumber = static_cast<quint8>(message.data().at(0));
quint16 sourceAddress = ZigbeeUtils::convertByteArrayToUint16(message.data().mid(1, 2));
quint8 endPoint = static_cast<quint8>(message.data().at(3));
quint16 clusterId = ZigbeeUtils::convertByteArrayToUint16(message.data().mid(4, 2));
quint16 attributeId = ZigbeeUtils::convertByteArrayToUint16(message.data().mid(6, 2));
quint8 attributDataType = static_cast<quint8>(message.data().at(8));
quint16 attributeSize = ZigbeeUtils::convertByteArrayToUint16(message.data().mid(9, 2));
QByteArray data = message.data().mid(11);
Zigbee::DataType dataType = static_cast<Zigbee::DataType>(attributDataType);
qCDebug(dcZigbeeNetwork()) << "Attribute report:";
qCDebug(dcZigbeeNetwork()) << " SQN:" << ZigbeeUtils::convertByteToHexString(sequenceNumber);
qCDebug(dcZigbeeNetwork()) << " Source address:" << ZigbeeUtils::convertUint16ToHexString(sourceAddress);
qCDebug(dcZigbeeNetwork()) << " End point:" << ZigbeeUtils::convertByteToHexString(endPoint);
qCDebug(dcZigbeeNetwork()) << " Cluster:" << ZigbeeUtils::clusterIdToString(static_cast<Zigbee::ClusterId>(clusterId));
qCDebug(dcZigbeeNetwork()) << " Attribut id:" << ZigbeeUtils::convertUint16ToHexString(attributeId);
qCDebug(dcZigbeeNetwork()) << " Attribut data type:" << dataType;
qCDebug(dcZigbeeNetwork()) << " Attribut size:" << attributeSize;
qCDebug(dcZigbeeNetwork()) << " Data:" << ZigbeeUtils::convertByteArrayToHexString(data);
switch (dataType) {
case Zigbee::CharString:
qCDebug(dcZigbeeNetwork()) << " Data(converted)" << QString::fromUtf8(data);
break;
case Zigbee::Bool:
qCDebug(dcZigbeeNetwork()) << " Data(converted)" << static_cast<bool>(data.at(0));
break;
default:
break;
}
ZigbeeNode *node = getZigbeeNode(sourceAddress);
if (!node) {
qCWarning(dcZigbeeNode()) << "Received an attribute report from an unknown node. Ignoring data.";
return;
}
node->setClusterAttribute(static_cast<Zigbee::ClusterId>(clusterId), ZigbeeClusterAttribute(attributeId, dataType, data));
}
void ZigbeeNetworkManager::processLeaveIndication(const ZigbeeInterfaceMessage &message)
{
QByteArray data = message.data();
quint64 extendedAddress = 0;
bool rejoining = 0;
QDataStream stream(&data, QIODevice::ReadOnly);
stream >> extendedAddress;
stream >> rejoining;
ZigbeeAddress address(extendedAddress);
qCDebug(dcZigbeeNetwork()) << "Node leaving indication:" << address.toString() << "rejoining:" << rejoining;
ZigbeeNode *node = getZigbeeNode(address);
if (node) removeNode(node);
}
void ZigbeeNetworkManager::processRestartProvisioned(const ZigbeeInterfaceMessage &message)
{
if (message.data().isEmpty())
return;
quint8 status = static_cast<quint8>(message.data().at(0));
switch (status) {
case 0:
qCDebug(dcZigbeeNetwork()) << "Restart provisioned: start up";
break;
case 2:
qCDebug(dcZigbeeNetwork()) << "Restart provisioned: NFN start";
break;
case 6:
qCDebug(dcZigbeeNetwork()) << "Restart provisioned: running";
break;
default:
qCDebug(dcZigbeeNetwork()) << "Restart provisioned: unknown";
break;
}
if (m_startingState == StartingStateReset) {
if (m_networkRunning) {
qCDebug(dcZigbeeNetwork()) << "Reset finished. Network already running. No need to set it up";
setStartingState(StartingStateGetPermitJoinStatus);
} else {
qCDebug(dcZigbeeNetwork()) << "Reset finished. Set up network";
setStartingState(StartingStateGetVersion);
}
}
}
void ZigbeeNetworkManager::startNetwork()
{
qCDebug(dcZigbeeNetwork()) << "Start network...";
if (m_controller) {
qCDebug(dcZigbeeNetwork()) << "Clean up old controller...";
delete m_controller;
m_controller = nullptr;
}
qCDebug(dcZigbeeNetwork()) << "Create new controller...";
m_controller = new ZigbeeBridgeController(this);
connect(m_controller, &ZigbeeBridgeController::messageReceived, this, &ZigbeeNetworkManager::onMessageReceived);
connect(m_controller, &ZigbeeBridgeController::availableChanged, this, &ZigbeeNetworkManager::onControllerAvailableChanged);
if (state() == StateUninitialized)
loadNetwork();
if (!m_controller->enable(serialPortName(), serialBaudrate())) {
setState(StateDisconnected);
setStartingState(StartingStateNone);
setError(ErrorHardwareUnavailable);
} else {
// Reset
setStartingState(StartingStateReset);
setState(StateStarting);
}
}
void ZigbeeNetworkManager::stopNetwork()
{
qCDebug(dcZigbeeNetwork()) << "Stopping network...";
setState(StateStopping);
if (m_controller) {
delete m_controller;
m_controller = nullptr;
}
setStartingState(StartingStateNone);
setState(StateDisconnected);
setError(ErrorNoError);
}
void ZigbeeNetworkManager::onMessageReceived(const ZigbeeInterfaceMessage &message)
{
switch (message.messageType()) {
case Zigbee::MessageTypeLogging:
processLoggingMessage(message);
break;
case Zigbee::MessageTypeFactoryNewRestart:
processFactoryNewRestart(message);
break;
case Zigbee::MessageTypeNodeClusterList:
processNodeClusterList(message);
break;
case Zigbee::MessageTypeNodeAttributeList:
processNodeAttributeList(message);
break;
case Zigbee::MessageTypeNodeCommandIdList:
processNodeCommandIdList(message);
break;
case Zigbee::MessageTypeDeviceAnnounce:
processDeviceAnnounce(message);
break;
case Zigbee::MessageTypeAttributeReport:
processAttributeReport(message);
break;
case Zigbee::MessageTypeLeaveIndication:
processLeaveIndication(message);
break;
case Zigbee::MessageTypeNetworkJoinedFormed:
processNetworkFormed(message);
break;
case Zigbee::MessageTypeRestartProvisioned:
processRestartProvisioned(message);
break;
default:
qCWarning(dcZigbeeController()) << "Unhandled message received:" << message;
break;
}
}
void ZigbeeNetworkManager::onControllerAvailableChanged(bool available)
{
qCDebug(dcZigbeeNetwork()) << "Hardware controller is" << (available ? "now available" : "not available");
if (m_factoryResetting && !available) {
setStartingState(StartingStateReset);
return;
}
if (!available) {
foreach (ZigbeeNode *node, nodes()) {
node->setConnected(false);
}
setError(ErrorHardwareUnavailable);
setState(StateDisconnected);
setStartingState(StartingStateNone);
} else {
setError(ErrorNoError);
setState(StateStarting);
setStartingState(StartingStateReset);
}
}
void ZigbeeNetworkManager::factoryResetNetwork()
{
qCDebug(dcZigbeeNetwork()) << "Factory reset network and forget all information. This cannot be undone.";
clearSettings();
setState(StateStarting);
setStartingState(StartingStateErase);
}