Implement basic cluster fetching

pull/6/head
Simon Stürz 2020-03-17 14:24:11 +01:00
parent 9174ff39c4
commit b211dbf347
17 changed files with 266 additions and 158 deletions

View File

@ -143,7 +143,10 @@ void ZigbeeInterface::onReadyRead()
m_lengthValue = 0;
m_escapeDetected = false;
m_data.clear();
if (!m_unhandledBuffer.isEmpty()) {
qCDebug(dcZigbeeInterfaceTraffic()) << "Controller debug message:" << QString::fromUtf8(m_unhandledBuffer);
m_unhandledBuffer.clear();
}
setReadingState(WaitForTypeMsb);
break;
case 0x02:
@ -185,7 +188,8 @@ void ZigbeeInterface::onReadyRead()
// Read data bytes depending on the reading state
switch (m_readingState) {
case WaitForStart:
qCWarning(dcZigbeeInterfaceTraffic()) << "Wait for start but reviced data:" << byte;
qCDebug(dcZigbeeInterfaceTraffic()) << "Wait for start but reviced data:" << byte;
m_unhandledBuffer.append(static_cast<char>(byte));
break;
case WaitForTypeMsb:
m_messageTypeValue = byte;

View File

@ -60,6 +60,7 @@ private:
QTimer *m_reconnectTimer = nullptr;
QSerialPort *m_serialPort = nullptr;
QByteArray m_messageBuffer;
QByteArray m_unhandledBuffer;
bool m_available = false;
// Message parsing

View File

@ -381,6 +381,23 @@ ZigbeeInterfaceReply *ZigbeeBridgeControllerNxp::commandFactoryResetNode(quint16
return sendRequest(request);
}
ZigbeeInterfaceReply *ZigbeeBridgeControllerNxp::commandManagementLeaveRequest(quint16 shortAddress, const ZigbeeAddress &ieeeAddress, bool rejoin, bool removeChildren)
{
QByteArray data;
QDataStream stream(&data, QIODevice::WriteOnly);
stream << shortAddress;
stream << ieeeAddress.toUInt64();
stream << (rejoin ? static_cast<quint8>(0x01) : static_cast<quint8>(0x00));
stream << (removeChildren ? static_cast<quint8>(0x01) : static_cast<quint8>(0x00));
ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeManagementLeaveRequest, data));
request.setExpectedAdditionalMessageType(Zigbee::MessageTypeManagementLeaveResponse);
request.setDescription("Leave node request " + ZigbeeUtils::convertUint16ToHexString(shortAddress));
request.setTimoutIntervall(10000);
return sendRequest(request);
}
ZigbeeInterfaceReply *ZigbeeBridgeControllerNxp::commandReadAttributeRequest(quint8 addressMode, quint16 shortAddress, quint8 sourceEndpoint, quint8 destinationEndpoint, ZigbeeCluster *cluster, QList<quint16> attributes, bool manufacturerSpecific, quint16 manufacturerId)
{
// Address mode: TODO
@ -413,7 +430,9 @@ ZigbeeInterfaceReply *ZigbeeBridgeControllerNxp::commandReadAttributeRequest(qui
}
ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeReadAttributeRequest, data));
//request.setExpectedAdditionalMessageType(Zigbee::MessageTypeReadAttributeResponse);
if (attributes.count() == 1) {
request.setExpectedAdditionalMessageType(Zigbee::MessageTypeReadAttributeResponse);
}
request.setDescription("Read attribute request for " + ZigbeeUtils::convertUint16ToHexString(shortAddress));
request.setTimoutIntervall(12000);

View File

@ -78,6 +78,7 @@ public:
ZigbeeInterfaceReply *commandUserDescriptorRequest(quint16 shortAddress, quint16 address);
ZigbeeInterfaceReply *commandFactoryResetNode(quint16 shortAddress, quint8 sourceEndpoint, quint8 destinationEndpoint);
ZigbeeInterfaceReply *commandManagementLeaveRequest(quint16 shortAddress, const ZigbeeAddress &ieeeAddress, bool rejoin = false, bool removeChildren = false);
ZigbeeInterfaceReply *commandReadAttributeRequest(quint8 addressMode, quint16 shortAddress, quint8 sourceEndpoint, quint8 destinationEndpoint, ZigbeeCluster *cluster, QList<quint16> attributes, bool manufacturerSpecific, quint16 manufacturerId);
ZigbeeInterfaceReply *commandConfigureReportingRequest(quint8 addressMode, quint16 shortAddress, quint8 sourceEndpoint, quint8 destinationEndpoint, ZigbeeCluster *cluster, quint8 direction, bool manufacturerSpecific, quint16 manufacturerId, QList<ZigbeeClusterReportConfigurationRecord> reportConfigurations);

View File

@ -695,81 +695,14 @@ void ZigbeeNetworkNxp::processAttributeReport(const ZigbeeInterfaceMessage &mess
void ZigbeeNetworkNxp::processReadAttributeResponse(const ZigbeeInterfaceMessage &message)
{
QByteArray data = message.data();
ZigbeeClusterAttributeReport attributeReport = ZigbeeUtils::parseAttributeReport(message.data());
quint8 sequenceNumber = 0;
quint16 sourceAddress = 0;
quint8 endpointId = 0;
quint16 clusterId = 0;
quint16 attributeId = 0;
quint8 attributeStatus = 0;
quint8 attributDataType = 0;
quint16 dataSize = 0;
QDataStream stream(&data, QIODevice::ReadOnly);
stream >> sequenceNumber >> sourceAddress >> endpointId >> clusterId >> attributeId >> attributeStatus >> attributDataType >> dataSize;
Zigbee::DataType dataType = static_cast<Zigbee::DataType>(attributDataType);
QByteArray attributeData = data.right(dataSize);
if (attributeData.length() != dataSize) {
qCWarning(dcZigbeeNetwork()) << "HACK" << attributeData.length() << "!=" << dataSize;
// Note: the NXP firmware for JN5169 has a bug here and does not send the attributeStatus.
// Repars data without attribute status
sequenceNumber = 0;
sourceAddress = 0;
endpointId = 0;
clusterId = 0;
attributeId = 0;
attributeStatus = 0;
attributDataType = 0;
dataSize = 0;
QDataStream alternativeStream(&data, QIODevice::ReadOnly);
alternativeStream >> sequenceNumber >> sourceAddress >> endpointId >> clusterId >> attributeId >> attributDataType >> dataSize;
dataType = static_cast<Zigbee::DataType>(attributDataType);
attributeData = data.right(dataSize);
}
qCDebug(dcZigbeeNetwork()) << "Attribute read response:";
qCDebug(dcZigbeeNetwork()) << " SQN:" << ZigbeeUtils::convertByteToHexString(sequenceNumber);
qCDebug(dcZigbeeNetwork()) << " Source address:" << ZigbeeUtils::convertUint16ToHexString(sourceAddress);
qCDebug(dcZigbeeNetwork()) << " End point:" << ZigbeeUtils::convertByteToHexString(endpointId);
qCDebug(dcZigbeeNetwork()) << " Cluster:" << ZigbeeUtils::clusterIdToString(static_cast<Zigbee::ClusterId>(clusterId));
qCDebug(dcZigbeeNetwork()) << " Attribut id:" << ZigbeeUtils::convertUint16ToHexString(attributeId);
qCDebug(dcZigbeeNetwork()) << " Attribut status:" << static_cast<Zigbee::ZigbeeStatus>(attributeStatus);
qCDebug(dcZigbeeNetwork()) << " Attribut data type:" << dataType;
qCDebug(dcZigbeeNetwork()) << " Attribut size:" << dataSize;
qCDebug(dcZigbeeNetwork()) << " Data:" << ZigbeeUtils::convertByteArrayToHexString(attributeData);
switch (dataType) {
case Zigbee::CharString:
qCDebug(dcZigbeeNetwork()) << " Data(converted)" << QString::fromUtf8(attributeData);
break;
case Zigbee::Bool:
qCDebug(dcZigbeeNetwork()) << " Data(converted)" << static_cast<bool>(attributeData.at(0));
break;
default:
break;
}
ZigbeeNodeNxp *node = qobject_cast<ZigbeeNodeNxp *>(getZigbeeNode(sourceAddress));
ZigbeeNodeNxp *node = qobject_cast<ZigbeeNodeNxp *>(getZigbeeNode(attributeReport.sourceAddress));
if (!node) {
qCWarning(dcZigbeeNode()) << "Received an attribute report from an unknown node. Ignoring data.";
return;
}
ZigbeeClusterAttributeReport attributeReport;
attributeReport.sourceAddress = sourceAddress;
attributeReport.endpointId = endpointId;
attributeReport.clusterId = static_cast<Zigbee::ClusterId>(clusterId);
attributeReport.attributeId = attributeId;
attributeReport.attributeStatus = static_cast<Zigbee::ZigbeeStatus>(attributeStatus);
attributeReport.dataType = dataType;
attributeReport.data = attributeData;
node->setClusterAttributeReport(attributeReport);
}

View File

@ -43,7 +43,7 @@ ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::readAttribute(ZigbeeCluster *cluster,
ZigbeeInterfaceReply *reply = m_controller->commandReadAttributeRequest(0x02, node()->shortAddress(), 0x01,
endpointId(), cluster, attributes,
false, node()->manufacturerCode());
connect(reply, &ZigbeeInterfaceReply::finished, this, [reply](){
connect(reply, &ZigbeeInterfaceReply::finished, this, [this, reply](){
reply->deleteLater();
if (reply->status() != ZigbeeInterfaceReply::Success) {
@ -52,6 +52,10 @@ ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::readAttribute(ZigbeeCluster *cluster,
}
qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully";
if (!reply->additionalMessage().data().isEmpty()) {
ZigbeeClusterAttributeReport report = ZigbeeUtils::parseAttributeReport(reply->additionalMessage().data());
setClusterAttribute(report.clusterId, ZigbeeClusterAttribute(report.attributeId, report.dataType, report.data));
}
});
return nullptr;
@ -60,8 +64,11 @@ ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::readAttribute(ZigbeeCluster *cluster,
ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::configureReporting(ZigbeeCluster *cluster, QList<ZigbeeClusterReportConfigurationRecord> reportConfigurations)
{
qCDebug(dcZigbeeNode()) << "Configure reporting" << node();
// FIXME: check the report configuration and the direction field according to specs
ZigbeeInterfaceReply *reply = m_controller->commandConfigureReportingRequest(0x02, node()->shortAddress(), 0x01,
endpointId(), cluster, 0x01,
endpointId(), cluster, 0x00,
false, node()->manufacturerCode(), reportConfigurations);
connect(reply, &ZigbeeInterfaceReply::finished, this, [reply](){
reply->deleteLater();
@ -336,4 +343,5 @@ void ZigbeeNodeEndpointNxp::setClusterAttribute(Zigbee::ClusterId clusterId, con
}
cluster->setAttribute(attribute);
emit clusterAttributeChanged(cluster, attribute);
}

View File

@ -51,6 +51,22 @@ ZigbeeNodeNxp::ZigbeeNodeNxp(ZigbeeBridgeControllerNxp *controller, QObject *par
}
void ZigbeeNodeNxp::leaveNetworkRequest(bool rejoin, bool removeChildren)
{
ZigbeeInterfaceReply *reply = m_controller->commandManagementLeaveRequest(shortAddress(), extendedAddress(), rejoin, removeChildren);
connect(reply, &ZigbeeInterfaceReply::finished, this, [reply](){
reply->deleteLater();
if (reply->status() != ZigbeeInterfaceReply::Success) {
qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage();
// TODO: check error handling
//return;
}
qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully";
});
}
void ZigbeeNodeNxp::setInitState(ZigbeeNodeNxp::InitState initState)
{
m_initState = initState;
@ -170,8 +186,10 @@ void ZigbeeNodeNxp::setInitState(ZigbeeNodeNxp::InitState initState)
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 {
} else if (profileId == Zigbee::ZigbeeProfileHomeAutomation) {
qCDebug(dcZigbeeNetwork()) << " Device ID:" << ZigbeeUtils::convertUint16ToHexString(deviceId) << static_cast<Zigbee::HomeAutomationDevice>(deviceId);
} else if (profileId == Zigbee::ZigbeeProfileGreenPower) {
qCDebug(dcZigbeeNetwork()) << " Device ID:" << ZigbeeUtils::convertUint16ToHexString(deviceId) << static_cast<Zigbee::GreenPowerDevice>(deviceId);
}
quint8 deviceVersion = (bitField >> 4);
@ -208,7 +226,7 @@ void ZigbeeNodeNxp::setInitState(ZigbeeNodeNxp::InitState initState)
quint16 clusterId = 0;
stream >> clusterId;
if (!endpoint->hasInputCluster(static_cast<Zigbee::ClusterId>(clusterId))) {
if (!endpoint->hasOutputCluster(static_cast<Zigbee::ClusterId>(clusterId))) {
endpoint->addOutputCluster(new ZigbeeCluster(static_cast<Zigbee::ClusterId>(clusterId), ZigbeeCluster::Output, endpoint));
}
qCDebug(dcZigbeeNetwork()) << " Cluster ID:" << ZigbeeUtils::convertUint16ToHexString(clusterId) << ZigbeeUtils::clusterIdToString(static_cast<Zigbee::ClusterId>(clusterId));
@ -218,20 +236,22 @@ void ZigbeeNodeNxp::setInitState(ZigbeeNodeNxp::InitState initState)
m_uninitializedEndpoints.removeAll(endpointId);
if (m_uninitializedEndpoints.isEmpty()) {
qCDebug(dcZigbeeNode()) << "All endpoints fetched.";
setInitState(InitStateReadClusterAttributes);
setInitState(InitStateReadBasicClusterAttributes);
}
});
}
break;
case InitStateReadClusterAttributes:
// if (shortAddress() == 0x0000) {
// qCDebug(dcZigbeeNode()) << "No need to read the endpoint baisc clusters of the coordinator node";
// setState(StateInitialized);
// break;
// }
case InitStateReadBasicClusterAttributes:
if (shortAddress() == 0x0000) {
qCDebug(dcZigbeeNode()) << "No need to read the endpoint baisc clusters of the coordinator node";
setState(StateInitialized);
break;
}
foreach (ZigbeeNodeEndpoint *endpoint, m_endpoints) {
ZigbeeNodeEndpointNxp *endpointNxp = qobject_cast<ZigbeeNodeEndpointNxp *>(endpoint);
// Read basic cluster
qCDebug(dcZigbeeNode()) << "Read basic cluster for endpoint" << endpoint;
@ -242,34 +262,53 @@ void ZigbeeNodeNxp::setInitState(ZigbeeNodeNxp::InitState initState)
return;
}
QList<quint16> attributes;
attributes.append(ZigbeeCluster::BasicAttributeZclVersion);
attributes.append(ZigbeeCluster::BasicAttributeManufacturerName);
m_uninitializedEndpoints.clear();
m_uninitalizedBasicClusterAttributes.append(ZigbeeCluster::BasicAttributeZclVersion);
m_uninitalizedBasicClusterAttributes.append(ZigbeeCluster::BasicAttributeManufacturerName);
// Note: some devices inform about the model identifier trough attribute report and the cluster contains different information
// Read the model identifier only if we don't have it yet. This is out of spec.
// Read the model identifier only if we don't have it yet. This is out of spec but required for some strange endpoints.
if (!basicCluster->hasAttribute(ZigbeeCluster::BasicAttributeModelIdentifier))
attributes.append(ZigbeeCluster::BasicAttributeModelIdentifier);
m_uninitalizedBasicClusterAttributes.append(ZigbeeCluster::BasicAttributeModelIdentifier);
attributes.append(ZigbeeCluster::BasicAttributePowerSource);
attributes.append(ZigbeeCluster::BasicAttributeSwBuildId);
ZigbeeInterfaceReply *reply = m_controller->commandReadAttributeRequest(0x02, shortAddress(),
0x01, endpoint->endpointId(),
basicCluster,
attributes,
false,
manufacturerCode());
m_uninitalizedBasicClusterAttributes.append(ZigbeeCluster::BasicAttributePowerSource);
m_uninitalizedBasicClusterAttributes.append(ZigbeeCluster::BasicAttributeSwBuildId);
connect(reply, &ZigbeeInterfaceReply::finished, this, [this, reply, endpoint](){
reply->deleteLater();
if (reply->status() != ZigbeeInterfaceReply::Success) {
qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage();
}
// Note: for having smoother flow request each attribute sequentially, not all at once
qCDebug(dcZigbeeNode()) << "Reading basic cluster attributes finished successfully for" << endpoint;
qCDebug(dcZigbeeNode()) << "The device should response with multiple attribute read notifications.";
setState(StateInitialized);
});
for (int i = 0; i < m_uninitalizedBasicClusterAttributes.count(); i++) {
quint16 attributeId = m_uninitalizedBasicClusterAttributes.at(i);
ZigbeeInterfaceReply *reply = m_controller->commandReadAttributeRequest(0x02, shortAddress(),
0x01, endpoint->endpointId(),
basicCluster,
{ attributeId },
false,
manufacturerCode());
connect(reply, &ZigbeeInterfaceReply::finished, this, [this, reply, endpointNxp, attributeId](){
reply->deleteLater();
if (reply->status() != ZigbeeInterfaceReply::Success) {
qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage();
} else {
ZigbeeClusterAttributeReport report = ZigbeeUtils::parseAttributeReport(reply->additionalMessage().data());
qCDebug(dcZigbeeNode()) << "Reading basic cluster attributes finished successfully for" << endpointNxp << report;
if (report.attributeStatus == Zigbee::ZigbeeStatusSuccess) {
if (attributeId == ZigbeeCluster::BasicAttributeManufacturerName) {
endpointNxp->setManufacturerName(QString::fromUtf8(report.data));
} else if (attributeId == ZigbeeCluster::BasicAttributeModelIdentifier) {
endpointNxp->setModelIdentifier(QString::fromUtf8(report.data));
} else if (attributeId == ZigbeeCluster::BasicAttributeSwBuildId) {
endpointNxp->setSoftwareBuildId(QString::fromUtf8(report.data));
}
}
}
m_uninitalizedBasicClusterAttributes.removeAll(attributeId);
if (m_uninitalizedBasicClusterAttributes.isEmpty()) {
setState(StateInitialized);
}
});
}
break;
}
}
@ -290,7 +329,7 @@ ZigbeeNodeEndpoint *ZigbeeNodeNxp::createNodeEndpoint(quint8 endpointId, QObject
void ZigbeeNodeNxp::setClusterAttributeReport(const ZigbeeClusterAttributeReport &report)
{
if (report.attributeStatus != Zigbee::ZigbeeStatusSuccess) {
qCWarning(dcZigbeeNode()) << this << "Got incalid status report" << report.endpointId << report.clusterId << report.attributeId << report.attributeStatus;
qCWarning(dcZigbeeNode()) << this << "Got incalid status report" << report;
return;
}

View File

@ -47,17 +47,20 @@ public:
InitStatePowerDescriptor,
InitStateActiveEndpoints,
InitStateSimpleDescriptors,
InitStateReadClusterAttributes
InitStateReadBasicClusterAttributes
};
Q_ENUM(InitState)
explicit ZigbeeNodeNxp(ZigbeeBridgeControllerNxp *controller, QObject *parent = nullptr);
void leaveNetworkRequest(bool rejoin = false, bool removeChildren = false) override;
private:
ZigbeeBridgeControllerNxp *m_controller = nullptr;
InitState m_initState = InitStateNone;
QList<quint8> m_uninitializedEndpoints;
QList<quint16> m_uninitalizedBasicClusterAttributes;
void setInitState(InitState initState);
void setClusterAttributeReport(const ZigbeeClusterAttributeReport &report) override;

View File

@ -47,7 +47,8 @@ public:
ZigbeeProfileTelecomAutomation = 0x0107,
ZigbeeProfilePersonalHomeHospitalCare = 0x0108,
ZigbeeProfileAdvancedMetering = 0x0109,
ZigbeeProfileLightLink = 0xC05E
ZigbeeProfileLightLink = 0xC05E,
ZigbeeProfileGreenPower = 0xA1E0
};
Q_ENUM(ZigbeeProfile)
@ -306,6 +307,10 @@ public:
// Over the air uppgrade (OTA)
ClusterIdOtaUpgrade = 0x0019,
// Poll controll
ClusterIdPollControl = 0x0020,
// Closures
ClusterIdShadeConfiguration = 0x0100,
@ -342,9 +347,6 @@ public:
ClusterIdLoadControl = 0x0701,
ClusterIdSimpleMetering = 0x0702,
// Electrical Measurement
ClusterIdElectricalMeasurement = 0x0B04,
// ZLL
ClusterIdTouchlinkCommissioning = 0x1000,
@ -352,7 +354,15 @@ public:
ClusterIdApplianceControl = 0x001B,
ClusterIdApplianceIdentification = 0x0B00,
ClusterIdApplianceEventsAlerts = 0x0B02,
ClusterIdApplianceStatistics = 0x0B03
ClusterIdApplianceStatistics = 0x0B03,
// Electrical Measurement
ClusterIdElectricalMeasurement = 0x0B04,
ClusterIdDiagnostics = 0x0B05,
// Zigbee green power
ClusterIdGreenPower = 0x0021
};
Q_ENUM(ClusterId)
@ -451,6 +461,18 @@ public:
};
Q_ENUM(HomeAutomationDevice)
enum GreenPowerDevice {
GreenPowerDeviceProxy = 0x0060,
GreenPowerDeviceProxyMinimum = 0x0061,
GreenPowerDeviceProxyTargetPlus = 0x0062,
GreenPowerDeviceProxyTarget = 0x0063,
GreenPowerDeviceProxyCommissioningTool = 0x0064,
GreenPowerDeviceProxyCombo = 0x0065,
GreenPowerDeviceProxyComboMinimum = 0x0066
};
Q_ENUM(GreenPowerDevice)
enum DataType {
NoData = 0x00,
Data8 = 0x08,

View File

@ -93,3 +93,16 @@ QDebug operator<<(QDebug debug, ZigbeeCluster *cluster)
return debug.space();
}
QDebug operator<<(QDebug debug, const ZigbeeClusterAttributeReport &attributeReport)
{
debug.nospace().noquote() << "AttributeReport("
<< attributeReport.clusterId << ", "
<< attributeReport.attributeId << ", "
<< attributeReport.attributeStatus << ", "
<< attributeReport.dataType << ", "
<< attributeReport.data << ", "
<< ")";
return debug.space();
}

View File

@ -43,7 +43,7 @@ struct ZigbeeClusterReportConfigurationRecord {
quint8 change;
};
struct ZigbeeClusterAttributeReport {
typedef struct ZigbeeClusterAttributeReport {
quint16 sourceAddress;
quint8 endpointId;
Zigbee::ClusterId clusterId;
@ -51,8 +51,7 @@ struct ZigbeeClusterAttributeReport {
Zigbee::ZigbeeStatus attributeStatus;
Zigbee::DataType dataType;
QByteArray data;
};
} ZigbeeClusterAttributeReport;
class ZigbeeCluster : public QObject
{
@ -218,5 +217,6 @@ signals:
};
QDebug operator<<(QDebug debug, ZigbeeCluster *cluster);
QDebug operator<<(QDebug debug, const ZigbeeClusterAttributeReport &attributeReport);
#endif // ZIGBEECLUSTER_H

View File

@ -280,7 +280,9 @@ void ZigbeeNetwork::loadNetwork()
endpoint->m_profile = static_cast<Zigbee::ZigbeeProfile>(settings.value("profile", 0).toUInt());
endpoint->m_deviceId = static_cast<quint16>(settings.value("deviceId", 0).toUInt());
endpoint->m_deviceVersion = static_cast<quint8>(settings.value("deviceId", 0).toUInt());
//qCDebug(dcZigbeeNetwork()) << "Created" << endpoint;
endpoint->m_manufacturerName = settings.value("manufacturerName").toString();
endpoint->m_modelIdentifier = settings.value("modelIdentifier").toString();
endpoint->m_softwareBuildId = settings.value("softwareBuildId").toString();
int inputClustersCount = settings.beginReadArray("inputClusters");
for (int n = 0; n < inputClustersCount; n ++) {
@ -339,11 +341,6 @@ void ZigbeeNetwork::saveNode(ZigbeeNode *node)
QSettings settings(m_settingsFileName, QSettings::IniFormat, this);
settings.beginGroup("Nodes");
// Clear settings for this node before storing it
// settings.beginGroup(node->extendedAddress().toString());
// settings.remove("");
// settings.endGroup();
// Save this node
settings.beginGroup(node->extendedAddress().toString());
settings.setValue("nwkAddress", node->shortAddress());
@ -359,6 +356,9 @@ void ZigbeeNetwork::saveNode(ZigbeeNode *node)
settings.setValue("profile", endpoint->profile());
settings.setValue("deviceId", endpoint->deviceId());
settings.setValue("deviceVersion", endpoint->deviceVersion());
settings.setValue("manufacturerName", endpoint->manufacturerName());
settings.setValue("modelIdentifier", endpoint->modelIdentifier());
settings.setValue("softwareBuildId", endpoint->softwareBuildId());
settings.beginWriteArray("inputClusters");
for (int n = 0; n < endpoint->inputClusters().count(); n++) {

View File

@ -238,42 +238,6 @@ void ZigbeeNode::setConnected(bool connected)
emit connectedChanged(m_connected);
}
//void ZigbeeNode::identify()
//{
// QByteArray data;
// QDataStream stream(&data, QIODevice::WriteOnly);
// stream << m_shortAddress;
// stream << static_cast<quint8>(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<quint8>(addressMode); // adress mode
// stream << m_shortAddress;
// stream << static_cast<quint8>(1); // source endpoint
// stream << static_cast<quint8>(1); // destination endpoint
// stream << static_cast<quint8>(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::setShortAddress(const quint16 &shortAddress)
{
m_shortAddress = shortAddress;
@ -587,6 +551,7 @@ QDebug operator<<(QDebug debug, ZigbeeNode *node)
{
debug.nospace().noquote() << "ZigbeeNode(" << ZigbeeUtils::convertUint16ToHexString(node->shortAddress());
debug.nospace().noquote() << ", " << node->extendedAddress().toString();
debug.nospace().noquote() << ", " << node->nodeType();
debug.nospace().noquote() << ")";
return debug.space();
}

View File

@ -147,6 +147,8 @@ public:
QList<PowerSource> availablePowerSources() const;
PowerLevel powerLevel() const;
virtual void leaveNetworkRequest(bool rejoin = false, bool removeChildren = false) = 0;
private:
bool m_connected = false;
State m_state = StateUninitialized;

View File

@ -70,6 +70,21 @@ void ZigbeeNodeEndpoint::setDeviceVersion(quint8 deviceVersion)
m_deviceVersion = deviceVersion;
}
QString ZigbeeNodeEndpoint::manufacturerName() const
{
return m_manufacturerName;
}
QString ZigbeeNodeEndpoint::modelIdentifier() const
{
return m_modelIdentifier;
}
QString ZigbeeNodeEndpoint::softwareBuildId() const
{
return m_softwareBuildId;
}
QList<ZigbeeCluster *> ZigbeeNodeEndpoint::inputClusters() const
{
return m_inputClusters.values();
@ -151,8 +166,10 @@ QDebug operator<<(QDebug debug, ZigbeeNodeEndpoint *endpoint)
debug.nospace().noquote() << ", " << endpoint->profile();
if (endpoint->profile() == Zigbee::ZigbeeProfileLightLink) {
debug.nospace().noquote() << ", " << static_cast<Zigbee::LightLinkDevice>(endpoint->deviceId());
} else {
} else if (endpoint->profile() == Zigbee::ZigbeeProfileHomeAutomation) {
debug.nospace().noquote() << ", " << static_cast<Zigbee::HomeAutomationDevice>(endpoint->deviceId());
} else if (endpoint->profile() == Zigbee::ZigbeeProfileGreenPower) {
debug.nospace().noquote() << ", " << static_cast<Zigbee::GreenPowerDevice>(endpoint->deviceId());
}
debug.nospace().noquote() << ")";

View File

@ -420,3 +420,81 @@ QColor ZigbeeUtils::interpolateColorFromColorTemperature(int colorTemperature, i
// FIXME: interpolate between the selected index and the next color for more accuracy if required
return colorTemperatureScale.at(closestColorIndex);
}
ZigbeeClusterAttributeReport ZigbeeUtils::parseAttributeReport(const QByteArray &data)
{
QByteArray dataCopy = data;
quint8 sequenceNumber = 0;
quint16 sourceAddress = 0;
quint8 endpointId = 0;
quint16 clusterId = 0;
quint16 attributeId = 0;
quint8 attributeStatus = 0;
quint8 attributDataType = 0;
quint16 dataSize = 0;
QDataStream stream(&dataCopy, QIODevice::ReadOnly);
stream >> sequenceNumber >> sourceAddress >> endpointId >> clusterId >> attributeId >> attributeStatus >> attributDataType >> dataSize;
Zigbee::DataType dataType = static_cast<Zigbee::DataType>(attributDataType);
QByteArray attributeData = data.right(dataSize);
if (attributeData.length() != dataSize) {
//qCWarning(dcZigbeeNetwork()) << "HACK" << attributeData.length() << "!=" << dataSize;
// Note: the NXP firmware for JN5169 has a bug here and does not send the attributeStatus.
// Repars data without attribute status
sequenceNumber = 0;
sourceAddress = 0;
endpointId = 0;
clusterId = 0;
attributeId = 0;
attributeStatus = 0;
attributDataType = 0;
dataSize = 0;
QDataStream alternativeStream(&dataCopy, QIODevice::ReadOnly);
alternativeStream >> sequenceNumber >> sourceAddress >> endpointId >> clusterId >> attributeId >> attributDataType >> dataSize;
dataType = static_cast<Zigbee::DataType>(attributDataType);
attributeData = data.right(dataSize);
}
// qCDebug(dcZigbeeNetwork()) << "Attribute read response:";
// qCDebug(dcZigbeeNetwork()) << " SQN:" << ZigbeeUtils::convertByteToHexString(sequenceNumber);
// qCDebug(dcZigbeeNetwork()) << " Source address:" << ZigbeeUtils::convertUint16ToHexString(sourceAddress);
// qCDebug(dcZigbeeNetwork()) << " End point:" << ZigbeeUtils::convertByteToHexString(endpointId);
// qCDebug(dcZigbeeNetwork()) << " Cluster:" << ZigbeeUtils::clusterIdToString(static_cast<Zigbee::ClusterId>(clusterId));
// qCDebug(dcZigbeeNetwork()) << " Attribut id:" << ZigbeeUtils::convertUint16ToHexString(attributeId);
// qCDebug(dcZigbeeNetwork()) << " Attribut status:" << static_cast<Zigbee::ZigbeeStatus>(attributeStatus);
// qCDebug(dcZigbeeNetwork()) << " Attribut data type:" << dataType;
// qCDebug(dcZigbeeNetwork()) << " Attribut size:" << dataSize;
// qCDebug(dcZigbeeNetwork()) << " Data:" << ZigbeeUtils::convertByteArrayToHexString(attributeData);
// switch (dataType) {
// case Zigbee::CharString:
// qCDebug(dcZigbeeNetwork()) << " Data(converted)" << QString::fromUtf8(attributeData);
// break;
// case Zigbee::Bool:
// qCDebug(dcZigbeeNetwork()) << " Data(converted)" << static_cast<bool>(attributeData.at(0));
// break;
// default:
// break;
// }
// ZigbeeNodeNxp *node = qobject_cast<ZigbeeNodeNxp *>(getZigbeeNode(sourceAddress));
// if (!node) {
// qCWarning(dcZigbeeNode()) << "Received an attribute report from an unknown node. Ignoring data.";
// return;
// }
ZigbeeClusterAttributeReport attributeReport;
attributeReport.sourceAddress = sourceAddress;
attributeReport.endpointId = endpointId;
attributeReport.clusterId = static_cast<Zigbee::ClusterId>(clusterId);
attributeReport.attributeId = attributeId;
attributeReport.attributeStatus = static_cast<Zigbee::ZigbeeStatus>(attributeStatus);
attributeReport.dataType = dataType;
attributeReport.data = attributeData;
return attributeReport;
}

View File

@ -36,6 +36,7 @@
#include <QBitArray>
#include "zigbee.h"
#include "zigbeecluster.h"
class ZigbeeUtils
{
@ -73,6 +74,8 @@ public:
// Color temperature interpolation
static QColor interpolateColorFromColorTemperature(int colorTemperature, int minValue, int maxValue);
static ZigbeeClusterAttributeReport parseAttributeReport(const QByteArray &data);
};
#endif // ZIGBEEUTILS_H