From 0255dfd7923a41bfcaea768bc088249bf0ff773c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Wed, 17 Jan 2018 16:39:06 +0100 Subject: [PATCH] Add more node data and improve serial parsing --- libqtzigbee/zigbee.h | 27 +- libqtzigbee/zigbeenetworkmanager.cpp | 441 +++++++++++++++++++-------- libqtzigbee/zigbeenetworkmanager.h | 22 +- libqtzigbee/zigbeenode.cpp | 297 +++++++++++++++++- libqtzigbee/zigbeenode.h | 77 ++++- libqtzigbee/zigbeeutils.cpp | 39 ++- libqtzigbee/zigbeeutils.h | 4 + zigbee-cli/core.cpp | 98 +++++- zigbee-cli/core.h | 3 + 9 files changed, 855 insertions(+), 153 deletions(-) diff --git a/libqtzigbee/zigbee.h b/libqtzigbee/zigbee.h index 0c0a326..93bfa33 100644 --- a/libqtzigbee/zigbee.h +++ b/libqtzigbee/zigbee.h @@ -46,6 +46,10 @@ public: MessageTypeAuthenticateDeviceResponse = 0x8028, MessageTypeOutOfBandCommisioningDataRequest = 0x0029, MessageTypeOutOfBandCommisioningDataResponse = 0x8029, + MessageTypeUserDescriptorSet = 0x002B, + MessageTypeUserDescriptorNotify = 0x802B, + MessageTypeUserDescriptorRequest = 0x002C, + MessageTypeUserDescriptorResponse = 0x802C, MessageTypeReset = 0x0011, MessageTypeErasePersistentData = 0x0012, @@ -56,6 +60,8 @@ public: MessageTypeBindResponse = 0x8030, MessageTypeUnbind = 0x0031, MessageTypeUnbindResponse = 0x8031, + MessageTypeComplexDescriptorRequest = 0x0034, + MessageTypeComplexDescriptorResponse = 0x8034, MessageTypeNetworkAdressRequest = 0x0040, MessageTypeNetworkAdressResponse = 0x8040, @@ -400,6 +406,25 @@ public: }; Q_ENUM(DataType) + enum Manufacturer { + // RF4CE + PanasonicRF4CE = 0x0001, + SonyRF4CE = 0x0002, + SamsungRF4CE = 0x0003, + PhilipsRF4CE = 0x0004, + FreescaleRF4CE = 0x0005, + OkiSemiRF4CE = 0x0006, + TiRF4CE = 0x0007, + + // Manufacturer Codes for non RF4CE devices + Cirronet = 0x1000, + Chipcon = 0x1001, + Ember = 0x1003, + + }; + Q_ENUM(Manufacturer) + + ///* Manufacturer Codes */ ///* Codes less than 0x1000 were issued for RF4CE */ //#define ZBEE_MFG_CODE_PANASONIC_RF4CE 0x0001 @@ -412,7 +437,7 @@ public: ///* Manufacturer Codes for non RF4CE devices */ //#define ZBEE_MFG_CODE_CIRRONET 0x1000 - //#define ZBEE_MFG_CODE_CHIPCON 0x1001 + //#define ZBEE_MFG_CODE_CHIPCON //#define ZBEE_MFG_CODE_EMBER 0x1002 //#define ZBEE_MFG_CODE_NTS 0x1003 //#define ZBEE_MFG_CODE_FREESCALE 0x1004 diff --git a/libqtzigbee/zigbeenetworkmanager.cpp b/libqtzigbee/zigbeenetworkmanager.cpp index 3ef9b8f..3789ede 100644 --- a/libqtzigbee/zigbeenetworkmanager.cpp +++ b/libqtzigbee/zigbeenetworkmanager.cpp @@ -7,7 +7,8 @@ #include ZigbeeNetworkManager::ZigbeeNetworkManager(const int &channel, const QString &serialPort, QObject *parent) : - ZigbeeNode(new ZigbeeBridgeController(serialPort, parent), parent) + ZigbeeNode(new ZigbeeBridgeController(serialPort, parent), parent), + m_serialPort(serialPort) { connect(controller(), &ZigbeeBridgeController::messageReceived, this, &ZigbeeNetworkManager::onMessageReceived); @@ -20,7 +21,8 @@ ZigbeeNetworkManager::ZigbeeNetworkManager(const int &channel, const QString &se QSettings settings; settings.beginGroup("Network"); - m_extendedPanId = static_cast(settings.value("panId", 0).toUInt()); + m_extendedPanId = static_cast(settings.value("panId", 0).toLongLong()); + qCDebug(dcZigbee()) << "Loading saved pan id" << m_extendedPanId; if (m_extendedPanId == 0) { m_extendedPanId = generateRandomPanId(); settings.setValue("panId", m_extendedPanId); @@ -39,24 +41,19 @@ ZigbeeNetworkManager::ZigbeeNetworkManager(const int &channel, const QString &se qCDebug(dcZigbee()) << "Using channel" << channel << "for the zigbee network."; } -// // Call init methods -// erasePersistentData(); -// //resetController(); -// getVersion(); -// setExtendedPanId(m_extendedPanId); -// setChannelMask(channelMask); -// setDeviceType(NodeTypeCoordinator); -// startNetwork(); + loadNetwork(); + resetController(); + getVersion(); + init(); +} +bool ZigbeeNetworkManager::isAvailable() const +{ + if (m_controller){ + return m_controller->available(); + } -// //startScan(); -// //getPermitJoiningStatus(); -// enableWhitelist(); -// permitJoining(); -// //getPermitJoiningStatus(); - -// //startScan(); -// initiateTouchLink(); + return false; } QString ZigbeeNetworkManager::controllerVersion() const @@ -64,6 +61,11 @@ QString ZigbeeNetworkManager::controllerVersion() const return m_controllerVersion; } +QString ZigbeeNetworkManager::serialPort() const +{ + return m_serialPort; +} + QList ZigbeeNetworkManager::nodeList() const { return m_nodeList; @@ -102,28 +104,8 @@ void ZigbeeNetworkManager::parseNetworkFormed(const QByteArray &data) networkStatusString = "unknown"; } - - // Parse short network address - quint16 shortAddress = data.at(1); - shortAddress <<= 8; - shortAddress |= data.at(2); - - // Parse extended network address - quint64 extendedAddress = data.at(3); - extendedAddress <<= 8; - extendedAddress |= data.at(4); - extendedAddress <<= 8; - extendedAddress |= data.at(5); - extendedAddress <<= 8; - extendedAddress |= data.at(6); - extendedAddress <<= 8; - extendedAddress |= data.at(7); - extendedAddress <<= 8; - extendedAddress |= data.at(8); - extendedAddress <<= 8; - extendedAddress |= data.at(9); - extendedAddress <<= 8; - extendedAddress |= data.at(10); + quint16 shortAddress = ZigbeeUtils::convertByteArrayToUint16(data.mid(1, 2)); + quint64 extendedAddress = ZigbeeUtils::convertByteArrayToUint64(data.mid(3, 8)); // Parse network channel quint8 channel = static_cast(data.at(11)); @@ -219,7 +201,7 @@ void ZigbeeNetworkManager::setExtendedPanId(const quint64 &panId) stream << panId; ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeSetExtendetPanId, data)); - request.setDescription("Set extended PAN ID " + QString::number(panId) + " " + ZigbeeUtils::convertUint64ToHexString(panId)); + request.setDescription("Set extended PAN id " + QString::number(panId) + " " + ZigbeeUtils::convertUint64ToHexString(panId)); ZigbeeInterfaceReply *reply = controller()->sendRequest(request); connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onSetExtendedPanIdFinished); @@ -227,6 +209,10 @@ void ZigbeeNetworkManager::setExtendedPanId(const quint64 &panId) void ZigbeeNetworkManager::setChannelMask(const quint32 &channelMask) { + // Note: 10 < value < 27 -> using sinle channel value + // 0x07fff800 select from all channels 11 - 26 + // 0x2108800 primary zigbee light link channels 11, 15, 20, 25 + QByteArray data; QDataStream stream(&data, QIODevice::WriteOnly); stream << channelMask; @@ -285,12 +271,13 @@ void ZigbeeNetworkManager::startScan() connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onStartScanFinished); } -void ZigbeeNetworkManager::permitJoining(quint16 targetAddress, const quint8 advertisingIntervall) +void ZigbeeNetworkManager::permitJoining(quint16 targetAddress, const quint8 advertisingIntervall, bool tcSignificance) { QByteArray data; QDataStream stream(&data, QIODevice::WriteOnly); stream << targetAddress; stream << advertisingIntervall; + stream << static_cast(tcSignificance); ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypePermitJoiningRequest, data)); request.setDescription("Permit joining request on " + ZigbeeUtils::convertUint16ToHexString(targetAddress) + " for " + QString::number(advertisingIntervall) + "[s]"); @@ -299,6 +286,23 @@ void ZigbeeNetworkManager::permitJoining(quint16 targetAddress, const quint8 adv connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onPermitJoiningFinished); } +void ZigbeeNetworkManager::requestLinkQuality() +{ + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + stream << shortAddress(); + stream << static_cast(0); + + ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeManagementLqiRequest, data)); + request.setExpectedAdditionalMessageType(Zigbee::MessageTypeManagementLqiResponse); + request.setDescription("Request link quality request for " + ZigbeeUtils::convertUint16ToHexString(shortAddress())); + request.setTimoutIntervall(5000); + + ZigbeeInterfaceReply *reply = controller()->sendRequest(request); + connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onRequestLinkQualityFinished); + +} + void ZigbeeNetworkManager::getPermitJoiningStatus() { ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeGetPermitJoining)); @@ -337,25 +341,40 @@ void ZigbeeNetworkManager::touchLinkFactoryReset() connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onTouchLinkFactoryResetFinished); } - -void ZigbeeNetworkManager::requestMatchDescriptor(const quint16 &shortAddress, const Zigbee::ZigbeeProfile &profile) +void ZigbeeNetworkManager::networkAddressRequest(quint16 targetAddress, quint64 extendedAddress) { + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + stream << targetAddress; + stream << extendedAddress; + stream << static_cast(1); + stream << static_cast(0); + + ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeNetworkAdressRequest, data)); + request.setDescription("Network address request on " + ZigbeeUtils::convertUint16ToHexString(targetAddress)); + request.setExpectedAdditionalMessageType(Zigbee::MessageTypeNetworkAdressResponse); + request.setTimoutIntervall(1000); + + ZigbeeInterfaceReply *reply = controller()->sendRequest(request); + connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onNetworkAddressRequestFinished); +} + + +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(profile); - - // TODO: check what this does - -// // Input clusters -// stream << static_cast(7); -// stream << static_cast(Zigbee::ClusterIdBasic); -// stream << static_cast(Zigbee::ClusterIdIdentify); -// stream << static_cast(Zigbee::ClusterIdGroups); -// stream << static_cast(Zigbee::ClusterIdScenes); -// stream << static_cast(Zigbee::ClusterIdOnOff); -// stream << static_cast(Zigbee::ClusterIdLevelControl); -// stream << static_cast(Zigbee::ClusterIdColorControl); + stream << static_cast(0xFFFF); + stream << static_cast(0); + stream << static_cast(0); + stream << static_cast(0); + stream << static_cast(0); ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeMatchDescriptorRequest, data)); request.setExpectedAdditionalMessageType(Zigbee::MessageTypeMatchDescriptorResponse); @@ -366,6 +385,50 @@ void ZigbeeNetworkManager::requestMatchDescriptor(const quint16 &shortAddress, c connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onRequestMatchDescriptorFinished); } +void ZigbeeNetworkManager::setInitialSecurity(quint8 keyState, quint8 keySequence, quint8 keyType, const QString &key) +{ + // Note: calls ZPS_vAplSecSetInitialSecurityState + + // Key state: + // ZPS_ZDO_PRECONFIGURED_LINK_KEY = 3 + // This key will be used to encrypt the network key. This is the master or manufacturer key + + // ZPS_ZDO_ZLL_LINK_KEY = 4 + // This key will be generated by the trust center. + + // Key Type: + // ZPS_APS_UNIQUE_LINK_KEY = + + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + stream << keyState; + stream << keySequence; + stream << keyType; + stream << QByteArray::fromHex(key.toUtf8()); + + ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeSetSecurity, data)); + request.setDescription("Set security configuration"); + + ZigbeeInterfaceReply *reply = controller()->sendRequest(request); + connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onSetSecurityFinished); +} + +void ZigbeeNetworkManager::authenticateDevice(const ZigbeeAddress &ieeeAddress) +{ + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + stream << ieeeAddress.toUInt64(); + stream << QByteArray::fromHex("5A6967426565416C6C69616E63653039"); + + ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeAuthenticateDeviceRequest, data)); + request.setExpectedAdditionalMessageType(Zigbee::MessageTypeAuthenticateDeviceResponse); + request.setDescription(QString("Authenticate device %1").arg(ieeeAddress.toString())); + request.setTimoutIntervall(5000); + + ZigbeeInterfaceReply *reply = controller()->sendRequest(request); + connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onAuthenticateDeviceFinished); +} + void ZigbeeNetworkManager::onResetControllerFinished() { ZigbeeInterfaceReply *reply = static_cast(sender()); @@ -409,15 +472,9 @@ void ZigbeeNetworkManager::onGetVersionFinished() qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; - // Parse major version - quint16 majorVersion = reply->additionalMessage().data().at(0); - majorVersion <<= 8; - majorVersion |= reply->additionalMessage().data().at(1); - - // Parse minor version - quint16 minorVersion = reply->additionalMessage().data().at(2); - minorVersion <<= 8; - minorVersion |= reply->additionalMessage().data().at(3); + // Parse version + quint16 majorVersion = ZigbeeUtils::convertByteArrayToUint16(reply->additionalMessage().data().mid(0, 2)); + quint16 minorVersion = ZigbeeUtils::convertByteArrayToUint16(reply->additionalMessage().data().mid(2, 2)); m_controllerVersion = QString("%1.%2").arg(majorVersion).arg(minorVersion); qCDebug(dcZigbee()) << "Version:" << m_controllerVersion; @@ -540,6 +597,83 @@ void ZigbeeNetworkManager::onRequestMatchDescriptorFinished() qCDebug(dcZigbeeController()) << reply->additionalMessage(); } +void ZigbeeNetworkManager::onSetSecurityFinished() +{ + ZigbeeInterfaceReply *reply = static_cast(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::onNetworkAddressRequestFinished() +{ + ZigbeeInterfaceReply *reply = static_cast(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(reply->additionalMessage().data().at(0)); + quint8 statusCode = static_cast(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(reply->additionalMessage().data().at(12)); + quint8 startIndex = static_cast(reply->additionalMessage().data().at(13)); + + qCDebug(dcZigbee()) << "Network address response:"; + qCDebug(dcZigbee()) << " SQN:" << sequenceNumber; + qCDebug(dcZigbee()) << " Status:" << statusCode; + qCDebug(dcZigbee()) << " Address:" << shortAddress << ZigbeeUtils::convertUint16ToHexString(shortAddress); + qCDebug(dcZigbee()) << " Extended address:" << ZigbeeAddress(ieeeAddress); + qCDebug(dcZigbee()) << " Deice count:" << deviceCount; + qCDebug(dcZigbee()) << " Start index:" << startIndex; + +} + +void ZigbeeNetworkManager::onAuthenticateDeviceFinished() +{ + ZigbeeInterfaceReply *reply = static_cast(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(reply->additionalMessage().data().at(36)); + quint8 channel = static_cast(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(dcZigbee()) << "Authentication response:"; + qCDebug(dcZigbee()) << " Gateways address:" << ZigbeeAddress(gatewayIeeeAddress); + qCDebug(dcZigbee()) << " Key:" << encryptedKey; + qCDebug(dcZigbee()) << " MIC:" << mic.toHex(); + qCDebug(dcZigbee()) << " Initiator address:" << ZigbeeAddress(initiatorIeeeAddress); + qCDebug(dcZigbee()) << " Active key sequence number:" << activeKeySequenceNumber; + qCDebug(dcZigbee()) << " Channel:" << channel; + qCDebug(dcZigbee()) << " Short PAN ID:" << ZigbeeUtils::convertUint16ToHexString(shortPan); + qCDebug(dcZigbee()) << " Extended PAN ID:" << extendedPanId << ZigbeeUtils::convertUint64ToHexString(extendedPanId); +} + void ZigbeeNetworkManager::onEnableWhitelistFinished() { ZigbeeInterfaceReply *reply = static_cast(sender()); @@ -579,6 +713,68 @@ void ZigbeeNetworkManager::onTouchLinkFactoryResetFinished() qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; } +void ZigbeeNetworkManager::onRequestLinkQualityFinished() +{ + ZigbeeInterfaceReply *reply = static_cast(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(reply->additionalMessage().data().at(0)); + quint8 statusCode = static_cast(reply->additionalMessage().data().at(1)); + quint8 neighborTableEntries = static_cast(reply->additionalMessage().data().at(2)); + quint8 neighborTableListCount = static_cast(reply->additionalMessage().data().at(3)); + quint8 startIndex = static_cast(reply->additionalMessage().data().at(4)); + + qCDebug(dcZigbee()) << "LQI response:"; + qCDebug(dcZigbee()) << " SQN:" << ZigbeeUtils::convertByteToHexString(sequenceNumber); + qCDebug(dcZigbee()) << " Status:" << ZigbeeUtils::convertByteToHexString(statusCode); + qCDebug(dcZigbee()) << " Neighbor table entries:" << neighborTableEntries; + qCDebug(dcZigbee()) << " Neighbor table list count:" << neighborTableListCount; + qCDebug(dcZigbee()) << " 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(dcZigbee()) << " 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(reply->additionalMessage().data().at(offset + 18)); + quint8 linkQuality = static_cast(reply->additionalMessage().data().at(offset + 19)); + quint8 bitMap = static_cast(reply->additionalMessage().data().at(offset + 20)); + + offset += 21; + + qCDebug(dcZigbee()) << " Neighbor:" << i; + qCDebug(dcZigbee()) << " Address:" << ZigbeeUtils::convertUint16ToHexString(shortAddress); + qCDebug(dcZigbee()) << " PAN id:" << panId; + qCDebug(dcZigbee()) << " Extended address:" << ZigbeeAddress(ieeeAddress); + qCDebug(dcZigbee()) << " Depth:" << depth; + qCDebug(dcZigbee()) << " Link quality:" << linkQuality; + qCDebug(dcZigbee()) << " BitMap:" << ZigbeeUtils::convertByteToHexString(bitMap); + + foreach (ZigbeeNode *node, m_nodeList) { + if (node->extendedAddress() == ZigbeeAddress(ieeeAddress)) { + node->setShortAddress(shortAddress); + } + } + + } +} + void ZigbeeNetworkManager::processLoggingMessage(const ZigbeeInterfaceMessage &message) { quint8 logLevel = static_cast(message.data().at(0)); @@ -658,10 +854,10 @@ void ZigbeeNetworkManager::processNodeClusterList(const ZigbeeInterfaceMessage & QByteArray clusterListData = message.data().right(message.data().count() - 3); - for (int i = 0; i < clusterListData.count(); i+=2) { + for (int i = 0; i < clusterListData.count(); i += 2) { quint16 clusterId = clusterListData.at(i); clusterId <<= 8; - clusterId |= clusterListData .at(i+1); + clusterId |= clusterListData .at(i + 1); qCDebug(dcZigbeeController()) << " Cluster ID:" << ZigbeeUtils::clusterIdToString(static_cast(clusterId)); } @@ -671,14 +867,8 @@ void ZigbeeNetworkManager::processNodeAttributeList(const ZigbeeInterfaceMessage { quint8 sourceEndpoint = static_cast(message.data().at(0)); - quint16 profileId = static_cast(message.data().at(1)); - profileId <<= 8; - profileId |= static_cast(message.data().at(2)); - - quint16 clusterId = static_cast(message.data().at(3)); - clusterId <<= 8; - clusterId |= static_cast(message.data().at(4)); - + 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; @@ -687,11 +877,8 @@ void ZigbeeNetworkManager::processNodeAttributeList(const ZigbeeInterfaceMessage QByteArray attributeListData = message.data().right(message.data().count() - 5); - for (int i = 0; i < attributeListData.count(); i+=2) { - quint16 attribute = attributeListData.at(i); - attribute <<= 8; - attribute |= attributeListData .at(i+1); - + for (int i = 0; i < attributeListData.count(); i += 2) { + quint16 attribute = ZigbeeUtils::convertByteArrayToUint16(attributeListData.mid(i, 2)); qCDebug(dcZigbeeController()) << " Attribute:" << ZigbeeUtils::convertUint16ToHexString(attribute); } } @@ -700,13 +887,8 @@ void ZigbeeNetworkManager::processNodeCommandIdList(const ZigbeeInterfaceMessage { quint8 sourceEndpoint = static_cast(message.data().at(0)); - quint16 profileId = static_cast(message.data().at(1)); - profileId <<= 8; - profileId |= static_cast(message.data().at(2)); - - quint16 clusterId = static_cast(message.data().at(3)); - clusterId <<= 8; - clusterId |= static_cast(message.data().at(4)); + 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; @@ -723,27 +905,10 @@ void ZigbeeNetworkManager::processNodeCommandIdList(const ZigbeeInterfaceMessage void ZigbeeNetworkManager::processDeviceAnnounce(const ZigbeeInterfaceMessage &message) { - quint16 shortAddress = message.data().at(0); - shortAddress <<= 8; - shortAddress |= message.data().at(1); + quint16 shortAddress = ZigbeeUtils::convertByteArrayToUint16(message.data().mid(0, 2)); + quint64 ieeeAddress = ZigbeeUtils::convertByteArrayToUint64(message.data().mid(2, 8)); - quint64 ieeeAddress = message.data().at(2); - ieeeAddress <<= 8; - ieeeAddress |= message.data().at(3); - ieeeAddress <<= 8; - ieeeAddress |= message.data().at(4); - ieeeAddress <<= 8; - ieeeAddress |= message.data().at(5); - ieeeAddress <<= 8; - ieeeAddress |= message.data().at(6); - ieeeAddress <<= 8; - ieeeAddress |= message.data().at(7); - ieeeAddress <<= 8; - ieeeAddress |= message.data().at(8); - ieeeAddress <<= 8; - ieeeAddress |= message.data().at(9); - - quint8 macCapability = message.data().at(10); + quint8 macCapability = static_cast(message.data().at(10)); qCDebug(dcZigbee()) << "Device announced:"; qCDebug(dcZigbee()) << " Address:" << ZigbeeUtils::convertUint16ToHexString(shortAddress); @@ -764,28 +929,14 @@ void ZigbeeNetworkManager::processDeviceAnnounce(const ZigbeeInterfaceMessage &m void ZigbeeNetworkManager::processAttributeReport(const ZigbeeInterfaceMessage &message) { quint8 sequenceNumber = static_cast(message.data().at(0)); - - quint16 sourceAddress = message.data().at(1); - sourceAddress <<= 8; - sourceAddress |= message.data().at(2); - + quint16 sourceAddress = ZigbeeUtils::convertByteArrayToUint16(message.data().mid(1, 2)); quint8 endPoint = static_cast(message.data().at(3)); - - quint16 clusterId = message.data().at(4); - clusterId <<= 8; - clusterId |= message.data().at(5); - - quint16 attributeId = message.data().at(6); - attributeId <<= 8; - attributeId |= message.data().at(7); - + quint16 clusterId = ZigbeeUtils::convertByteArrayToUint16(message.data().mid(4, 2)); + quint16 attributeId = ZigbeeUtils::convertByteArrayToUint16(message.data().mid(6, 2)); quint8 attributStatus = static_cast(message.data().at(8)); quint8 attributDataType = static_cast(message.data().at(9)); - quint16 attributeSize = message.data().at(10); - attributeSize <<= 8; - attributeSize |= message.data().at(11); - + quint16 attributeSize = ZigbeeUtils::convertByteArrayToUint16(message.data().mid(10, 2)); QByteArray data = message.data().mid(12); Zigbee::DataType dataType = static_cast(attributDataType); @@ -805,7 +956,7 @@ void ZigbeeNetworkManager::processAttributeReport(const ZigbeeInterfaceMessage & qCDebug(dcZigbee()) << " Data(converted)" << QString::fromUtf8(data); break; case Zigbee::Bool: - qCDebug(dcZigbee()) << " Data(converted)" << QVariant(data.toInt()).toBool(); + qCDebug(dcZigbee()) << " Data(converted)" << QVariant(data).toBool(); break; default: break; @@ -817,17 +968,37 @@ void ZigbeeNetworkManager::processAttributeReport(const ZigbeeInterfaceMessage & void ZigbeeNetworkManager::processLeaveIndication(const ZigbeeInterfaceMessage &message) { - quint16 shortAddress = message.data().at(0); - shortAddress <<= 8; - shortAddress |= message.data().at(1); - - quint8 rejoining = message.data().at(2); + quint16 shortAddress = ZigbeeUtils::convertByteArrayToUint16(message.data().mid(0, 2)); + quint8 rejoining = static_cast(message.data().at(2)); qCDebug(dcZigbee()) << "Node leaving:" << ZigbeeUtils::convertUint16ToHexString(shortAddress) << rejoining; // TODO: remove node } +void ZigbeeNetworkManager::processRestartProvisioned(const ZigbeeInterfaceMessage &message) +{ + if (message.data().isEmpty()) + return; + + quint8 status = static_cast(message.data().at(0)); + switch (status) { + case 0: + qCDebug(dcZigbee()) << "Restart provisioned: start up"; + break; + case 2: + qCDebug(dcZigbee()) << "Restart provisioned: NFN start"; + break; + case 6: + qCDebug(dcZigbee()) << "Restart provisioned: running"; + break; + default: + qCDebug(dcZigbee()) << "Restart provisioned: unknown"; + break; + } + +} + void ZigbeeNetworkManager::onMessageReceived(const ZigbeeInterfaceMessage &message) { switch (message.messageType()) { @@ -855,6 +1026,12 @@ void ZigbeeNetworkManager::onMessageReceived(const ZigbeeInterfaceMessage &messa case Zigbee::MessageTypeLeaveIndication: processLeaveIndication(message); break; + case Zigbee::MessageTypeNetworkJoinedFormed: + parseNetworkFormed(message.data()); + break; + case Zigbee::MessageTypeRestartProvisioned: + processRestartProvisioned(message); + break; default: qCDebug(dcZigbeeController()) << "Message received:" << message; break; diff --git a/libqtzigbee/zigbeenetworkmanager.h b/libqtzigbee/zigbeenetworkmanager.h index e5c0ba3..8b53923 100644 --- a/libqtzigbee/zigbeenetworkmanager.h +++ b/libqtzigbee/zigbeenetworkmanager.h @@ -13,9 +13,13 @@ class ZigbeeNetworkManager : public ZigbeeNode public: explicit ZigbeeNetworkManager(const int &channel = 0, const QString &serialPort = "/dev/ttyS0", QObject *parent = nullptr); + bool isAvailable() const; + QString controllerVersion() const; + QString serialPort() const; QList nodeList() const; quint64 extendedPanId() const; + bool networkRunning() const; // Controller methods @@ -24,12 +28,14 @@ public: void sendDataManagerAvailableResponse(); void getVersion(); void setExtendedPanId(const quint64 &panId); - void setChannelMask(const quint32 &channelMask); + void setChannelMask(const quint32 &channelMask = 0x07fff800); void setDeviceType(const NodeType &deviceType); void startNetwork(); void startScan(); - void permitJoining(quint16 targetAddress = 0xfffc, const quint8 advertisingIntervall = 254); + void permitJoining(quint16 targetAddress = 0xfffc, const quint8 advertisingIntervall = 180, bool tcSignificance = false); + + void requestLinkQuality(); void getPermitJoiningStatus(); void enableWhitelist(); @@ -37,10 +43,17 @@ public: void initiateTouchLink(); void touchLinkFactoryReset(); + void networkAddressRequest(quint16 targetAddress, quint64 extendedAddress); + void requestMatchDescriptor(const quint16 &shortAddress, const Zigbee::ZigbeeProfile &profile); + void setInitialSecurity(quint8 keyState, quint8 keySequence, quint8 keyType, const QString &key); + + void authenticateDevice(const ZigbeeAddress &ieeeAddress); + private: ZigbeeBridgeController *m_controller = nullptr; + QString m_serialPort; QString m_controllerVersion; quint64 m_extendedPanId = 0; @@ -76,8 +89,12 @@ private slots: void onInitiateTouchLinkFinished(); void onTouchLinkFactoryResetFinished(); + void onRequestLinkQualityFinished(); void onRequestMatchDescriptorFinished(); + void onSetSecurityFinished(); + void onNetworkAddressRequestFinished(); + void onAuthenticateDeviceFinished(); // Process controller notifications/messages void processLoggingMessage(const ZigbeeInterfaceMessage &message); @@ -88,6 +105,7 @@ private slots: void processDeviceAnnounce(const ZigbeeInterfaceMessage &message); void processAttributeReport(const ZigbeeInterfaceMessage &message); void processLeaveIndication(const ZigbeeInterfaceMessage &message); + void processRestartProvisioned(const ZigbeeInterfaceMessage &message); }; #endif // ZIGBEEMANAGER_H diff --git a/libqtzigbee/zigbeenode.cpp b/libqtzigbee/zigbeenode.cpp index dc2cfca..ffbaf5a 100644 --- a/libqtzigbee/zigbeenode.cpp +++ b/libqtzigbee/zigbeenode.cpp @@ -14,11 +14,56 @@ ZigbeeAddress ZigbeeNode::extendedAddress() const return m_extendedAddress; } +int ZigbeeNode::endPoint() const +{ + return m_endPoint; +} + ZigbeeNode::NodeType ZigbeeNode::nodeType() const { return m_nodeType; } +ZigbeeNode::FrequencyBand ZigbeeNode::frequencyBand() const +{ + return m_frequencyBand; +} + +ZigbeeNode::Relationship ZigbeeNode::relationShip() const +{ + return m_relationShip; +} + +Zigbee::ZigbeeProfile ZigbeeNode::profile() const +{ + return m_profile; +} + +quint16 ZigbeeNode::manufacturerCode() const +{ + return m_manufacturerCode; +} + +ZigbeeNode::PowerMode ZigbeeNode::powerMode() const +{ + return m_powerMode; +} + +ZigbeeNode::PowerSource ZigbeeNode::powerSource() const +{ + return m_powerSource; +} + +QList ZigbeeNode::availablePowerSources() const +{ + return m_availablePowerSources; +} + +ZigbeeNode::PowerLevel ZigbeeNode::powerLevel() const +{ + return m_powerLevel; +} + void ZigbeeNode::init() { requestNodeDescription(); @@ -26,6 +71,40 @@ void ZigbeeNode::init() requestPowerDescriptor(); } + +void ZigbeeNode::identify() +{ +// QByteArray data; +// QDataStream stream(&data, QIODevice::WriteOnly); +// stream << m_shortAddress; +// stream << static_cast(0); + +// ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeManagementLqiRequest, data)); +// request.setExpectedAdditionalMessageType(Zigbee::MessageTypeManagementLqiResponse); +// request.setDescription("Node link quality request for " + ZigbeeUtils::convertUint16ToHexString(m_shortAddress)); +// request.setTimoutIntervall(10000); + +// ZigbeeInterfaceReply *reply = controller()->sendRequest(request); + // connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNode::onRequestLinkQuality); +} + +void ZigbeeNode::toggle(int addressMode) +{ + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + stream << static_cast(addressMode); // adress mode + stream << m_shortAddress; + stream << static_cast(1); // source endpoint + stream << static_cast(1); // destination endpoint + stream << static_cast(2); // command toggle + + ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeCluserOnOff, data)); + request.setDescription("Toggle request for " + ZigbeeUtils::convertUint16ToHexString(m_shortAddress)); + + ZigbeeInterfaceReply *reply = controller()->sendRequest(request); + connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNode::onToggleFinished); +} + void ZigbeeNode::requestNodeDescription() { QByteArray data; @@ -35,7 +114,7 @@ void ZigbeeNode::requestNodeDescription() ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeNodeDescriptorRequest, data)); request.setExpectedAdditionalMessageType(Zigbee::MessageTypeNodeDescriptorRsponse); request.setDescription("Node descriptor request for " + ZigbeeUtils::convertUint16ToHexString(m_shortAddress)); - request.setTimoutIntervall(10000); + request.setTimoutIntervall(5000); ZigbeeInterfaceReply *reply = controller()->sendRequest(request); connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNode::onRequestNodeDescriptionFinished); @@ -51,7 +130,7 @@ void ZigbeeNode::requestSimpleNodeDescription() ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeSimpleDescriptorRequest, data)); request.setExpectedAdditionalMessageType(Zigbee::MessageTypeSimpleDescriptorResponse); request.setDescription("Simple node descriptor request for " + ZigbeeUtils::convertUint16ToHexString(m_shortAddress)); - request.setTimoutIntervall(10000); + request.setTimoutIntervall(5000); ZigbeeInterfaceReply *reply = controller()->sendRequest(request); connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNode::onRequestSimpleNodeDescriptionFinished); @@ -66,12 +145,28 @@ void ZigbeeNode::requestPowerDescriptor() ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypePowerDescriptorRequest, data)); request.setExpectedAdditionalMessageType(Zigbee::MessageTypePowerDescriptorResponse); request.setDescription("Node power descriptor request for " + ZigbeeUtils::convertUint16ToHexString(m_shortAddress)); - request.setTimoutIntervall(10000); + request.setTimoutIntervall(5000); ZigbeeInterfaceReply *reply = controller()->sendRequest(request); connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNode::onRequestPowerDescriptorFinished); } +void ZigbeeNode::requestUserDescriptor() +{ + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + stream << m_shortAddress; + stream << static_cast(0); + + ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeUserDescriptorRequest, data)); + request.setExpectedAdditionalMessageType(Zigbee::MessageTypeUserDescriptorResponse); + request.setDescription("Node user descriptor request for " + ZigbeeUtils::convertUint16ToHexString(m_shortAddress)); + request.setTimoutIntervall(5000); + + ZigbeeInterfaceReply *reply = controller()->sendRequest(request); + connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNode::onRequestUserDescriptorFinished); +} + ZigbeeNode::ZigbeeNode(ZigbeeBridgeController *controller, QObject *parent) : QObject(parent), m_controller(controller) @@ -137,20 +232,86 @@ void ZigbeeNode::onRequestNodeDescriptionFinished() bitField <<= 8; bitField |= reply->additionalMessage().data().at(16); - // TODO: find node for short address and set data + // Set node data + m_manufacturerCode = manufacturerCode; + m_maximalRxSize = maximalRxSize; + m_maximalTxSize = maximalTxSize; + + // Parse server mask + m_isPrimaryTrustCenter = ((serverMask >> 0) & 0x0001); + m_isBackupTrustCenter = ((serverMask >> 1) & 0x0001); + m_isPrimaryBindingCache = ((serverMask >> 2) & 0x0001); + m_isBackupBindingCache = ((serverMask >> 3) & 0x0001); + m_isPrimaryDiscoveryCache = ((serverMask >> 4) & 0x0001); + m_isBackupDiscoveryCache = ((serverMask >> 5) & 0x0001); + m_isNetworkManager = ((serverMask >> 6) & 0x0001); + + // Parse desciptor flag + bool extendedActiveEndpointListAvailable = ((descriptorFlag >> 0) & 0x01); + bool extendedSimpleDescriptorListAvailable = ((descriptorFlag >> 1) & 0x01); + + // Parse MAC capabilities + m_receiverOnWhenIdle = ((macFlags >> 3) & 0x01); + m_securityCapability = ((macFlags >> 6) & 0x01); + + // Parse bit field + bool isCoordinator = ((bitField >> 0) & 0x0001); + bool isRouter = ((bitField >> 1) & 0x0001); + bool isEndDevice = ((bitField >> 2) & 0x0001); + bool complexDescriptorAvailable = ((bitField >> 3) & 0x0001); + bool userDescriptorAvailable = ((bitField >> 4) & 0x0001); + + if (isCoordinator && !isRouter && !isEndDevice) { + m_nodeType = NodeTypeCoordinator; + } else if (!isCoordinator && isRouter && !isEndDevice) { + m_nodeType = NodeTypeRouter; + } else if (!isCoordinator && !isRouter && isEndDevice) { + m_nodeType = NodeTypeEndDevice; + } else { + if (m_isNetworkManager) { + m_nodeType = NodeTypeCoordinator; + } else { + m_nodeType = NodeTypeEndDevice; + } + } + + // Note: Frequency always 2,4 GHz qCDebug(dcZigbee()) << "Node descriptor:"; + qCDebug(dcZigbee()) << " Node type:" << m_nodeType; qCDebug(dcZigbee()) << " Sequence number:" << ZigbeeUtils::convertByteToHexString(sequenceNumber); qCDebug(dcZigbee()) << " Status:" << ZigbeeUtils::convertByteToHexString(status); qCDebug(dcZigbee()) << " Short address:" << ZigbeeUtils::convertUint16ToHexString(shortAddress); qCDebug(dcZigbee()) << " Manufacturer code:" << ZigbeeUtils::convertUint16ToHexString(manufacturerCode); - qCDebug(dcZigbee()) << " Maximum Rx size:" << ZigbeeUtils::convertUint16ToHexString(maximalRxSize); - qCDebug(dcZigbee()) << " Maximum Tx size:" << ZigbeeUtils::convertUint16ToHexString(maximalTxSize); - qCDebug(dcZigbee()) << " Server makk:" << ZigbeeUtils::convertUint16ToHexString(serverMask); + qCDebug(dcZigbee()) << " Maximum Rx size:" << ZigbeeUtils::convertUint16ToHexString(m_maximalRxSize); + qCDebug(dcZigbee()) << " Maximum Tx size:" << ZigbeeUtils::convertUint16ToHexString(m_maximalTxSize); + qCDebug(dcZigbee()) << " Server mask:" << ZigbeeUtils::convertUint16ToHexString(serverMask); + qCDebug(dcZigbee()) << " Primary Trust center:" << m_isPrimaryTrustCenter; + qCDebug(dcZigbee()) << " Backup Trust center:" << m_isBackupTrustCenter; + qCDebug(dcZigbee()) << " Primary Binding cache:" << m_isPrimaryBindingCache; + qCDebug(dcZigbee()) << " Backup Binding cache:" << m_isBackupBindingCache; + qCDebug(dcZigbee()) << " Primary Discovery cache:" << m_isPrimaryDiscoveryCache; + qCDebug(dcZigbee()) << " Backup Discovery cache:" << m_isBackupDiscoveryCache; + qCDebug(dcZigbee()) << " Network Manager:" << m_isNetworkManager; qCDebug(dcZigbee()) << " Descriptor flag:" << ZigbeeUtils::convertByteToHexString(descriptorFlag); + qCDebug(dcZigbee()) << " Extended active endpoint list available:" << extendedActiveEndpointListAvailable; + qCDebug(dcZigbee()) << " Extended simple descriptor list available:" << extendedSimpleDescriptorListAvailable; qCDebug(dcZigbee()) << " MAC flags:" << ZigbeeUtils::convertByteToHexString(macFlags); + qCDebug(dcZigbee()) << " Receiver on when idle:" << m_receiverOnWhenIdle; + qCDebug(dcZigbee()) << " Security capability:" << m_securityCapability; qCDebug(dcZigbee()) << " Maximum buffer size:" << ZigbeeUtils::convertByteToHexString(maxBufferSize); qCDebug(dcZigbee()) << " Bit field:" << ZigbeeUtils::convertUint16ToHexString(bitField); + qCDebug(dcZigbee()) << " Is coordinator:" << isCoordinator; + qCDebug(dcZigbee()) << " Is router:" << isRouter; + qCDebug(dcZigbee()) << " Is end device:" << isEndDevice; + qCDebug(dcZigbee()) << " Complex desciptor available:" << complexDescriptorAvailable; + qCDebug(dcZigbee()) << " User desciptor available:" << userDescriptorAvailable; + + + if (userDescriptorAvailable) { + requestUserDescriptor(); + } + } void ZigbeeNode::onRequestSimpleNodeDescriptionFinished() @@ -174,6 +335,12 @@ void ZigbeeNode::onRequestSimpleNodeDescriptionFinished() nwkAddress |= reply->additionalMessage().data().at(3); quint8 length = static_cast(reply->additionalMessage().data().at(4)); + + if (length == 0) { + qCWarning(dcZigbee()) << "Length 0"; + return; + } + quint8 endPoint = static_cast(reply->additionalMessage().data().at(5)); quint16 profileId = reply->additionalMessage().data().at(6); @@ -248,14 +415,128 @@ void ZigbeeNode::onRequestPowerDescriptorFinished() bitField <<= 8; bitField |= 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 + + if (!ZigbeeUtils::checkBitUint16(bitField, 0) && !ZigbeeUtils::checkBitUint16(bitField, 1)) { + m_powerMode = PowerModeAlwaysOn; + } else if (ZigbeeUtils::checkBitUint16(bitField, 0) && !ZigbeeUtils::checkBitUint16(bitField, 1)) { + m_powerMode = PowerModeOnPeriodically; + } else if (!ZigbeeUtils::checkBitUint16(bitField, 0) && ZigbeeUtils::checkBitUint16(bitField, 1)) { + m_powerMode = PowerModeOnWhenStimulated; + } + + // Bit 4 - 7 Available power sources + // Bit 0: Permanent mains supply + // Bit 1: Rechargeable battery + // Bit 2: Disposable battery + // Bit 4: Reserved + + if (ZigbeeUtils::checkBitUint16(bitField, 4)) { + m_availablePowerSources.append(PowerSourcePermanentMainSupply); + } else if (ZigbeeUtils::checkBitUint16(bitField, 5)) { + m_availablePowerSources.append(PowerSourceRecharchableBattery); + } else if (ZigbeeUtils::checkBitUint16(bitField, 6)) { + m_availablePowerSources.append(PowerSourceDisposableBattery); + } + + // Bit 8 - 11 Active source: according to the same schema as available power sources + if (ZigbeeUtils::checkBitUint16(bitField, 8)) { + m_powerSource = PowerSourcePermanentMainSupply; + } else if (ZigbeeUtils::checkBitUint16(bitField, 9)) { + m_powerSource = PowerSourceRecharchableBattery; + } else if (ZigbeeUtils::checkBitUint16(bitField, 10)) { + m_powerSource = PowerSourceDisposableBattery; + } + + // Bit 12 - 15: Battery level if available + // 0000: Critically low + // 0100: Approximately 33% + // 1000: Approximately 66% + // 1100: Approximately 100% (near fully charged) + + if (!ZigbeeUtils::checkBitUint16(bitField, 14) && !ZigbeeUtils::checkBitUint16(bitField, 15)) { + m_powerLevel = PowerLevelCriticalLow; + } else if (ZigbeeUtils::checkBitUint16(bitField, 14) && !ZigbeeUtils::checkBitUint16(bitField, 15)) { + m_powerLevel = PowerLevelLow; + } else if (!ZigbeeUtils::checkBitUint16(bitField, 14) && ZigbeeUtils::checkBitUint16(bitField, 15)) { + m_powerLevel = PowerLevelOk; + } else if (ZigbeeUtils::checkBitUint16(bitField, 14) && ZigbeeUtils::checkBitUint16(bitField, 15)) { + m_powerLevel = PowerLevelFull; + } + qCDebug(dcZigbee()) << "Node power descriptor:"; qCDebug(dcZigbee()) << " Sequence number:" << ZigbeeUtils::convertByteToHexString(sequenceNumber); qCDebug(dcZigbee()) << " Status:" << ZigbeeUtils::convertByteToHexString(status); qCDebug(dcZigbee()) << " Bitfiled:" << ZigbeeUtils::convertUint16ToHexString(bitField); + qCDebug(dcZigbee()) << " Power mode:" << m_powerMode; + qCDebug(dcZigbee()) << " Available power sources:"; + foreach (const PowerSource &source, m_availablePowerSources) { + qCDebug(dcZigbee()) << " " << source; + } + qCDebug(dcZigbee()) << " Power source:" << m_powerSource; + qCDebug(dcZigbee()) << " Power level:" << m_powerLevel; + +} + +void ZigbeeNode::onRequestUserDescriptorFinished() +{ + ZigbeeInterfaceReply *reply = static_cast(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(reply->additionalMessage().data().at(0)); + quint8 status = static_cast(reply->additionalMessage().data().at(1)); + + quint16 nwkAddress = reply->additionalMessage().data().at(2); + nwkAddress <<= 8; + nwkAddress |= reply->additionalMessage().data().at(3); + + quint8 length = static_cast(reply->additionalMessage().data().at(4)); + + QByteArray data; + if (length > 0) { + data = reply->additionalMessage().data().mid(5, length); + } + + qCDebug(dcZigbee()) << "User descriptor:"; + qCDebug(dcZigbee()) << " Sequence number:" << ZigbeeUtils::convertByteToHexString(sequenceNumber); + qCDebug(dcZigbee()) << " Status:" << ZigbeeUtils::convertByteToHexString(status); + qCDebug(dcZigbee()) << " Attribute address:" << ZigbeeUtils::convertUint16ToHexString(nwkAddress); + qCDebug(dcZigbee()) << " Lenght:" << ZigbeeUtils::convertByteToHexString(length); + qCDebug(dcZigbee()) << " Data:" << data; +} + +void ZigbeeNode::onToggleFinished() +{ + ZigbeeInterfaceReply *reply = static_cast(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 ZigbeeNode::onIdentifyFinished() +{ + } QDebug operator<<(QDebug debug, ZigbeeNode *node) { - debug.nospace().noquote() << "Node(" << node->shortAddress() << ", " << node->extendedAddress().toString() << ") "; + debug.nospace().noquote() << "Node(" << ZigbeeUtils::convertUint16ToHexString(node->shortAddress()) << " | " << node->extendedAddress().toString() << ") "; + debug.nospace().noquote() << " " << ZigbeeUtils::convertUint16ToHexString(node->shortAddress()) << " | " << node->extendedAddress().toString() << ") "; return debug; } diff --git a/libqtzigbee/zigbeenode.h b/libqtzigbee/zigbeenode.h index daa6972..6f18668 100644 --- a/libqtzigbee/zigbeenode.h +++ b/libqtzigbee/zigbeenode.h @@ -30,28 +30,97 @@ public: }; Q_ENUM(FrequencyBand) + enum Relationship { + Parent, + Child, + Sibling + }; + Q_ENUM(Relationship) + + enum PowerMode { + PowerModeAlwaysOn, + PowerModeOnPeriodically, + PowerModeOnWhenStimulated + }; + Q_ENUM(PowerMode) + + enum PowerSource { + PowerSourcePermanentMainSupply, + PowerSourceRecharchableBattery, + PowerSourceDisposableBattery + }; + Q_ENUM(PowerSource) + + enum PowerLevel { + PowerLevelCriticalLow, + PowerLevelLow, + PowerLevelOk, + PowerLevelFull + }; + Q_ENUM(PowerLevel) + quint16 shortAddress() const; ZigbeeAddress extendedAddress() const; + int endPoint() const; // Information from node descriptor NodeType nodeType() const; FrequencyBand frequencyBand() const; + Relationship relationShip() const; + Zigbee::ZigbeeProfile profile() const; + quint16 manufacturerCode() const; - bool canBeCoordinator() const; + PowerMode powerMode() const; + PowerSource powerSource() const; + QList availablePowerSources() const; + PowerLevel powerLevel() const; + // Node specific zigbee commands void init(); + void identify(); + void toggle(int addressMode); private: ZigbeeBridgeController *m_controller; quint16 m_shortAddress = 0; ZigbeeAddress m_extendedAddress; - NodeType m_nodeType; + int m_endPoint = 0; + + NodeType m_nodeType = NodeTypeRouter; + FrequencyBand m_frequencyBand = FrequencyBand2400Mhz; + Relationship m_relationShip = Parent; + + Zigbee::ZigbeeProfile m_profile; + quint16 m_manufacturerCode = 0; + + quint16 m_maximalRxSize = 0; + quint16 m_maximalTxSize = 0; + + bool m_isPrimaryTrustCenter = false; + bool m_isBackupTrustCenter = false; + bool m_isPrimaryBindingCache = false; + bool m_isBackupBindingCache = false; + bool m_isPrimaryDiscoveryCache = false; + bool m_isBackupDiscoveryCache = false; + bool m_isNetworkManager = false; + + // Power information + PowerMode m_powerMode; + PowerSource m_powerSource; + QList m_availablePowerSources; + PowerLevel m_powerLevel; + + // Mac capabilities + bool m_receiverOnWhenIdle = false; + bool m_securityCapability = false; void requestNodeDescription(); void requestSimpleNodeDescription(); void requestPowerDescriptor(); + void requestUserDescriptor(); + void saveToSettings(); protected: ZigbeeNode(ZigbeeBridgeController *controller, QObject *parent = nullptr); @@ -67,7 +136,9 @@ private slots: void onRequestNodeDescriptionFinished(); void onRequestSimpleNodeDescriptionFinished(); void onRequestPowerDescriptorFinished(); - + void onRequestUserDescriptorFinished(); + void onToggleFinished(); + void onIdentifyFinished(); public slots: diff --git a/libqtzigbee/zigbeeutils.cpp b/libqtzigbee/zigbeeutils.cpp index 7b464dd..3796011 100644 --- a/libqtzigbee/zigbeeutils.cpp +++ b/libqtzigbee/zigbeeutils.cpp @@ -27,6 +27,43 @@ QByteArray ZigbeeUtils::convertBitArrayToByteArray(const QBitArray &bitArray) return byteArray; } +bool ZigbeeUtils::checkBitUint16(const quint16 &value, const int &bitNumber) +{ + return value & (1 << bitNumber); +} + +quint16 ZigbeeUtils::convertByteArrayToUint16(const QByteArray &data) +{ + Q_ASSERT_X(data.count() == 2, "converting data", "Invalid byte array size for converting to quint16"); + + quint16 value = static_cast(data.at(0)); + value <<= 8; + value |= static_cast(data.at(1)); + return value; +} + +quint64 ZigbeeUtils::convertByteArrayToUint64(const QByteArray &data) +{ + Q_ASSERT_X(data.count() == 8, "converting data", "Invalid byte array size for converting to quint64"); + + quint64 value = static_cast(data.at(0)); + value <<= 8; + value |= static_cast(data.at(1)); + value <<= 8; + value |= static_cast(data.at(2)); + value <<= 8; + value |= static_cast(data.at(3)); + value <<= 8; + value |= static_cast(data.at(4)); + value <<= 8; + value |= static_cast(data.at(5)); + value <<= 8; + value |= static_cast(data.at(6)); + value <<= 8; + value |= static_cast(data.at(7)); + return value; +} + QString ZigbeeUtils::convertByteToHexString(const quint8 &byte) { QString hexString(QStringLiteral("0x%1")); @@ -39,7 +76,7 @@ QString ZigbeeUtils::convertByteArrayToHexString(const QByteArray &byteArray) QString hexString; for (int i = 0; i < byteArray.count(); i++) { hexString.append(convertByteToHexString((quint8)byteArray.at(i))); - if (i != byteArray.count() -1) { + if (i != byteArray.count() - 1) { hexString.append(" "); } } diff --git a/libqtzigbee/zigbeeutils.h b/libqtzigbee/zigbeeutils.h index 19104f7..fdf30f9 100644 --- a/libqtzigbee/zigbeeutils.h +++ b/libqtzigbee/zigbeeutils.h @@ -16,6 +16,10 @@ public: // Data utils QBitArray convertByteArrayToBitArray(const QByteArray &byteArray); QByteArray convertBitArrayToByteArray(const QBitArray &bitArray); + static bool checkBitUint16(const quint16 &value, const int &bitNumber); + + static quint16 convertByteArrayToUint16(const QByteArray &data); + static quint64 convertByteArrayToUint64(const QByteArray &data); // Debug utils static QString convertByteToHexString(const quint8 &byte); diff --git a/zigbee-cli/core.cpp b/zigbee-cli/core.cpp index faf019d..41a29d7 100644 --- a/zigbee-cli/core.cpp +++ b/zigbee-cli/core.cpp @@ -1,11 +1,16 @@ #include "core.h" #include "loggingcategory.h" -#include "QCoreApplication" +#include "zigbeeutils.h" + +#include Core::Core(const QString &serialPort, const int &channel, QObject *parent) : QObject(parent), m_serialPort(serialPort) { + m_channelMask = 0; + m_channelMask |= 1 << (channel); + m_manager = new ZigbeeNetworkManager(channel, m_serialPort, this); // Set commands @@ -20,11 +25,18 @@ Core::Core(const QString &serialPort, const int &channel, QObject *parent) : m_commands.append(TerminalCommand("reset", "Reset the zigbee controller")); m_commands.append(TerminalCommand("scan", "Start scanning for zigbee networks")); m_commands.append(TerminalCommand("version", "Print the version of the zigbee controll bridge firmware")); + m_commands.append(TerminalCommand("network-info", "Print all information of the zigbee network.")); m_commands.append(TerminalCommand("list-nodes", "List all nodes and information of the current network")); m_commands.append(TerminalCommand("permit-join", "Permit nodes to join the network")); m_commands.append(TerminalCommand("touch-link", "Initiate touch link pairing")); m_commands.append(TerminalCommand("reset-touchlink", "Touch link factory reset")); m_commands.append(TerminalCommand("whitelist", "Enable the white list joining")); + m_commands.append(TerminalCommand("address-request", "Request network address on host node")); + m_commands.append(TerminalCommand("matchdescriptor", "Request match descriptors")); + m_commands.append(TerminalCommand("init-node", "Request simple descriptors")); + m_commands.append(TerminalCommand("lqi", "Request link quality")); + m_commands.append(TerminalCommand("toggle", "Request to toggle")); + m_commands.append(TerminalCommand("authenticate", "Authenticate device with given IEEE address")); TerminalCommander::instance()->setCommands(m_commands); TerminalCommander::instance()->start(); @@ -33,6 +45,17 @@ Core::Core(const QString &serialPort, const int &channel, QObject *parent) : connect(TerminalCommander::instance(), &TerminalCommander::finished, QCoreApplication::instance(), &QCoreApplication::quit); } +ZigbeeNode *Core::findNode(const QString &shortAddressString) +{ + foreach (ZigbeeNode *node, m_manager->nodeList()) { + if (ZigbeeUtils::convertUint16ToHexString(node->shortAddress()) == shortAddressString) { + return node; + } + } + + return nullptr; +} + void Core::onCommandReceived(const QStringList &tokens) { TerminalCommand command; @@ -68,30 +91,93 @@ void Core::onCommandReceived(const QStringList &tokens) m_manager->erasePersistentData(); m_manager->getVersion(); m_manager->setExtendedPanId(m_manager->extendedPanId()); - m_manager->setChannelMask(0); + m_manager->setChannelMask(0x2108800); m_manager->setDeviceType(nodeType); + // Note: this is the leaked philips ZLL master key + m_manager->setInitialSecurity(3, 0, 1, "9F5595F10257C8A469CBF42BC93FEE31"); + m_manager->setInitialSecurity(4, 0, 1, "5A6967426565416C6C69616E63653039"); } else if (command.command() == "start") { m_manager->startNetwork(); } else if (command.command() == "version") { m_manager->getVersion(); + } else if (command.command() == "network-info") { + qCDebug(dcZigbee()).nospace().noquote() << "Network controller: " << m_manager->serialPort() << ", Bridge version: " << m_manager->controllerVersion(); } else if (command.command() == "scan") { m_manager->startScan(); } else if (command.command() == "reset") { m_manager->resetController(); } else if (command.command() == "permit-join") { - m_manager->permitJoining(); + m_manager->permitJoining(0xfffc, 180, true); } else if (command.command() == "touch-link") { m_manager->initiateTouchLink(); - } else if (command.command() == "touch-link-reset") { + } else if (command.command() == "reset-touchlink") { m_manager->touchLinkFactoryReset(); - } else if (command.command() == "enable-whitelist") { + } else if (command.command() == "whitelist") { m_manager->enableWhitelist(); + } else if (command.command() == "address-request") { + if (tokens.count() == 1) { + m_manager->networkAddressRequest(0, m_manager->extendedAddress().toUInt64()); + return; + } + ZigbeeNode *node = findNode(tokens.at(1)); + if (!node) { + qCWarning(dcZigbee()) << "Could not find node with short address tokens.at(1)"; + return; + } + + m_manager->networkAddressRequest(node->shortAddress(), node->extendedAddress().toUInt64()); + + } else if (command.command() == "toggle") { + if (tokens.count() < 3) { + qCWarning(dcZigbee()) << "Please specify also the node short address and adressMode"; + return; + } + + + ZigbeeNode *node = findNode(tokens.at(1)); + if (!node) { + qCWarning(dcZigbee()) << "Could not find node with short address tokens.at(1)"; + return; + } + + int addressMode = QString(tokens.at(2)).toInt(); + + node->toggle(addressMode); + + } else if (command.command() == "lqi") { + m_manager->requestLinkQuality(); + } else if (command.command() == "init-node") { + if (tokens.count() == 1) { + qCWarning(dcZigbee()) << "Please specify also the node short address"; + return; + } + + ZigbeeNode *node = findNode(tokens.at(1)); + if (!node) { + qCWarning(dcZigbee()) << "Could not find node with short address tokens.at(1)"; + return; + } + + node->init(); + + } else if (command.command() == "matchdescriptor") { + m_manager->requestMatchDescriptor(0x9004, Zigbee::ZigbeeProfileLightLink); + //m_manager->requestMatchDescriptor(0xFFFD, Zigbee::ZigbeeProfileLightLink); + + } else if (command.command() == "authenticate") { + if (tokens.count() == 1) { + qCWarning(dcZigbee()) << "Please specify also the node short address"; + return; + } + + m_manager->authenticateDevice(ZigbeeAddress(tokens.at(1))); + } else if (command.command() == "list-nodes") { qCDebug(dcZigbee()) << "--> Host:" << m_manager; foreach (ZigbeeNode *node, m_manager->nodeList()) { - qCDebug(dcZigbee()) << "-->" << node; + qCDebug(dcZigbee()) << " -->" << node; } } diff --git a/zigbee-cli/core.h b/zigbee-cli/core.h index 10fda3c..38098bc 100644 --- a/zigbee-cli/core.h +++ b/zigbee-cli/core.h @@ -21,6 +21,9 @@ private: ZigbeeNetworkManager *m_manager = nullptr; QList m_commands; QString m_serialPort; + quint32 m_channelMask = 0; + + ZigbeeNode *findNode(const QString &shortAddressString); signals: