diff --git a/docs/JN-AN-1216-ZigBee-3-0-IoT-ControlBridge-UserGuide.pdf b/docs/JN-AN-1216-ZigBee-3-0-IoT-ControlBridge-UserGuide.pdf old mode 100644 new mode 100755 index c4d59a3..9d4edb4 Binary files a/docs/JN-AN-1216-ZigBee-3-0-IoT-ControlBridge-UserGuide.pdf and b/docs/JN-AN-1216-ZigBee-3-0-IoT-ControlBridge-UserGuide.pdf differ diff --git a/libnymea-zigbee/loggingcategory.cpp b/libnymea-zigbee/loggingcategory.cpp index b75d9c1..188adcb 100644 --- a/libnymea-zigbee/loggingcategory.cpp +++ b/libnymea-zigbee/loggingcategory.cpp @@ -29,6 +29,7 @@ Q_LOGGING_CATEGORY(dcZigbeeNetwork, "ZigbeeNetwork") Q_LOGGING_CATEGORY(dcZigbeeNode, "ZigbeeNode") +Q_LOGGING_CATEGORY(dcZigbeeCluster, "ZigbeeCluster") Q_LOGGING_CATEGORY(dcZigbeeInterface, "ZigbeeInterface") Q_LOGGING_CATEGORY(dcZigbeeController, "ZigbeeController") Q_LOGGING_CATEGORY(dcZigbeeInterfaceTraffic, "ZigbeeInterfaceTraffic") diff --git a/libnymea-zigbee/loggingcategory.h b/libnymea-zigbee/loggingcategory.h index caac9f0..0245617 100644 --- a/libnymea-zigbee/loggingcategory.h +++ b/libnymea-zigbee/loggingcategory.h @@ -33,6 +33,7 @@ Q_DECLARE_LOGGING_CATEGORY(dcZigbeeNetwork) Q_DECLARE_LOGGING_CATEGORY(dcZigbeeNode) +Q_DECLARE_LOGGING_CATEGORY(dcZigbeeCluster) Q_DECLARE_LOGGING_CATEGORY(dcZigbeeInterface) Q_DECLARE_LOGGING_CATEGORY(dcZigbeeController) Q_DECLARE_LOGGING_CATEGORY(dcZigbeeInterfaceTraffic) diff --git a/libnymea-zigbee/nxp/zigbeebridgecontrollernxp.cpp b/libnymea-zigbee/nxp/zigbeebridgecontrollernxp.cpp index 324099c..373819b 100644 --- a/libnymea-zigbee/nxp/zigbeebridgecontrollernxp.cpp +++ b/libnymea-zigbee/nxp/zigbeebridgecontrollernxp.cpp @@ -199,7 +199,7 @@ ZigbeeInterfaceReply *ZigbeeBridgeControllerNxp::commandActiveEndpointsRequest(q ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeActiveEndpointRequest, data)); request.setDescription("Get active endpoints"); request.setExpectedAdditionalMessageType(Zigbee::MessageTypeActiveEndpointResponse); - request.setTimoutIntervall(5000); + request.setTimoutIntervall(12000); return sendRequest(request); } @@ -214,7 +214,7 @@ ZigbeeInterfaceReply *ZigbeeBridgeControllerNxp::commandRequestLinkQuality(quint ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeManagementLqiRequest, data)); request.setExpectedAdditionalMessageType(Zigbee::MessageTypeManagementLqiResponse); request.setDescription("Request link quality request for " + ZigbeeUtils::convertUint16ToHexString(shortAddress)); - request.setTimoutIntervall(5000); + request.setTimoutIntervall(12000); return sendRequest(request); } @@ -301,7 +301,7 @@ ZigbeeInterfaceReply *ZigbeeBridgeControllerNxp::commandAuthenticateDevice(const ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeAuthenticateDeviceRequest, data)); request.setExpectedAdditionalMessageType(Zigbee::MessageTypeAuthenticateDeviceResponse); request.setDescription(QString("Authenticate device %1").arg(ieeeAddress.toString())); - request.setTimoutIntervall(2000); + request.setTimoutIntervall(12000); return sendRequest(request); } @@ -315,7 +315,7 @@ ZigbeeInterfaceReply *ZigbeeBridgeControllerNxp::commandNodeDescriptorRequest(qu ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeNodeDescriptorRequest, data)); request.setExpectedAdditionalMessageType(Zigbee::MessageTypeNodeDescriptorRsponse); request.setDescription("Node descriptor request for " + ZigbeeUtils::convertUint16ToHexString(shortAddress)); - request.setTimoutIntervall(5000); + request.setTimoutIntervall(12000); return sendRequest(request); } @@ -330,7 +330,7 @@ ZigbeeInterfaceReply *ZigbeeBridgeControllerNxp::commandSimpleDescriptorRequest( ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeSimpleDescriptorRequest, data)); request.setExpectedAdditionalMessageType(Zigbee::MessageTypeSimpleDescriptorResponse); request.setDescription("Simple node descriptor request for " + ZigbeeUtils::convertUint16ToHexString(shortAddress) + " endpoint " + QString::number(endpoint)); - request.setTimoutIntervall(10000); + request.setTimoutIntervall(12000); return sendRequest(request); } @@ -344,7 +344,7 @@ ZigbeeInterfaceReply *ZigbeeBridgeControllerNxp::commandPowerDescriptorRequest(q ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypePowerDescriptorRequest, data)); request.setExpectedAdditionalMessageType(Zigbee::MessageTypePowerDescriptorResponse); request.setDescription("Node power descriptor request for " + ZigbeeUtils::convertUint16ToHexString(shortAddress)); - request.setTimoutIntervall(5000); + request.setTimoutIntervall(12000); return sendRequest(request); } @@ -359,7 +359,24 @@ ZigbeeInterfaceReply *ZigbeeBridgeControllerNxp::commandUserDescriptorRequest(qu ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeUserDescriptorRequest, data)); request.setExpectedAdditionalMessageType(Zigbee::MessageTypeUserDescriptorResponse); request.setDescription("Node user descriptor request for " + ZigbeeUtils::convertUint16ToHexString(shortAddress) + " " + ZigbeeUtils::convertUint16ToHexString(address)); - request.setTimoutIntervall(5000); + request.setTimoutIntervall(12000); + + return sendRequest(request); +} + +ZigbeeInterfaceReply *ZigbeeBridgeControllerNxp::commandFactoryResetNode(quint16 shortAddress, quint8 sourceEndpoint, quint8 destinationEndpoint) +{ + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + stream << static_cast(0x02); + stream << shortAddress; + stream << sourceEndpoint; + stream << destinationEndpoint; + + ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageBasicResetFactoryDefaults, data)); + request.setExpectedAdditionalMessageType(Zigbee::MessageBasicResetFactoryDefaultsResponse); + request.setDescription("Factory reset node " + ZigbeeUtils::convertUint16ToHexString(shortAddress) + " " + ZigbeeUtils::convertByteToHexString(destinationEndpoint)); + request.setTimoutIntervall(12000); return sendRequest(request); } @@ -398,44 +415,41 @@ ZigbeeInterfaceReply *ZigbeeBridgeControllerNxp::commandReadAttributeRequest(qui ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeReadAttributeRequest, data)); //request.setExpectedAdditionalMessageType(Zigbee::MessageTypeReadAttributeResponse); request.setDescription("Read attribute request for " + ZigbeeUtils::convertUint16ToHexString(shortAddress)); - request.setTimoutIntervall(5000); + request.setTimoutIntervall(12000); return sendRequest(request); } -ZigbeeInterfaceReply *ZigbeeBridgeControllerNxp::commandConfigureReportingRequest(quint8 addressMode, quint16 shortAddress, quint8 sourceEndpoint, quint8 destinationEndpoint, ZigbeeCluster *cluster, QList attributes, bool manufacturerSpecific, quint16 manufacturerId) +ZigbeeInterfaceReply *ZigbeeBridgeControllerNxp::commandConfigureReportingRequest(quint8 addressMode, quint16 shortAddress, quint8 sourceEndpoint, quint8 destinationEndpoint, ZigbeeCluster *cluster, quint8 direction, bool manufacturerSpecific, quint16 manufacturerId, QList reportConfigurations) { QByteArray data; QDataStream stream(&data, QIODevice::WriteOnly); - stream << addressMode; - stream << shortAddress; - stream << sourceEndpoint; - stream << destinationEndpoint; - stream << static_cast(cluster->clusterId()); + stream << addressMode; // [0] + stream << shortAddress; // [1] [2] + stream << sourceEndpoint; // [3] + stream << destinationEndpoint; // [4] + stream << static_cast(cluster->clusterId()); // [5] [6] // 0: server -> client, 1: client -> server - if (cluster->direction() == ZigbeeCluster::Input) { - stream << static_cast(1); - } else { - stream << static_cast(0); - } - - if (manufacturerSpecific) { - stream << static_cast(1); - } else { - stream << static_cast(0); - } - - stream << manufacturerId; - stream << static_cast(attributes.count()); - for (int i = 0; i < attributes.count(); i++) { - stream << attributes.at(i); + stream << direction;// [7] + stream << static_cast(manufacturerSpecific); // [8] + stream << manufacturerId; // [9] [10] + stream << static_cast(reportConfigurations.count()); // [11] + for (int i = 0; i < reportConfigurations.count(); i++) { + // Configuration report + stream << reportConfigurations.at(i).direction; + stream << static_cast(reportConfigurations.at(i).dataType); + stream << reportConfigurations.at(i).attributeId; + stream << reportConfigurations.at(i).minInterval; + stream << reportConfigurations.at(i).maxInterval; + stream << reportConfigurations.at(i).timeout; + stream << reportConfigurations.at(i).change; } ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeConfigReportingRequest, data)); request.setExpectedAdditionalMessageType(Zigbee::MessageTypeConfigReportingResponse); request.setDescription("Configure reporting request for " + ZigbeeUtils::convertUint16ToHexString(shortAddress)); - request.setTimoutIntervall(5000); + request.setTimoutIntervall(12000); return sendRequest(request); } @@ -452,7 +466,7 @@ ZigbeeInterfaceReply *ZigbeeBridgeControllerNxp::commandIdentify(quint8 addressM ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeIdentifySend, data)); request.setDescription("Identify request for " + ZigbeeUtils::convertUint16ToHexString(shortAddress)); - request.setTimoutIntervall(5000); + request.setTimoutIntervall(12000); return sendRequest(request); } diff --git a/libnymea-zigbee/nxp/zigbeebridgecontrollernxp.h b/libnymea-zigbee/nxp/zigbeebridgecontrollernxp.h index 21e1a52..e5e8385 100644 --- a/libnymea-zigbee/nxp/zigbeebridgecontrollernxp.h +++ b/libnymea-zigbee/nxp/zigbeebridgecontrollernxp.h @@ -77,8 +77,10 @@ public: ZigbeeInterfaceReply *commandPowerDescriptorRequest(quint16 shortAddress); ZigbeeInterfaceReply *commandUserDescriptorRequest(quint16 shortAddress, quint16 address); + ZigbeeInterfaceReply *commandFactoryResetNode(quint16 shortAddress, quint8 sourceEndpoint, quint8 destinationEndpoint); + ZigbeeInterfaceReply *commandReadAttributeRequest(quint8 addressMode, quint16 shortAddress, quint8 sourceEndpoint, quint8 destinationEndpoint, ZigbeeCluster *cluster, QList attributes, bool manufacturerSpecific, quint16 manufacturerId); - ZigbeeInterfaceReply *commandConfigureReportingRequest(quint8 addressMode, quint16 shortAddress, quint8 sourceEndpoint, quint8 destinationEndpoint, ZigbeeCluster *cluster, QList attributes, bool manufacturerSpecific, quint16 manufacturerId); + ZigbeeInterfaceReply *commandConfigureReportingRequest(quint8 addressMode, quint16 shortAddress, quint8 sourceEndpoint, quint8 destinationEndpoint, ZigbeeCluster *cluster, quint8 direction, bool manufacturerSpecific, quint16 manufacturerId, QList reportConfigurations); ZigbeeInterfaceReply *commandIdentify(quint8 addressMode, quint16 shortAddress, quint8 sourceEndpoint, quint8 destinationEndpoint, quint16 duration); ZigbeeInterfaceReply *commandBindGroup(const ZigbeeAddress &sourceAddress, quint8 sourceEndpoint, quint16 clusterId, quint16 destinationAddress, quint8 destinationEndpoint); diff --git a/libnymea-zigbee/nxp/zigbeenetworknxp.cpp b/libnymea-zigbee/nxp/zigbeenetworknxp.cpp index 2662074..21588d7 100644 --- a/libnymea-zigbee/nxp/zigbeenetworknxp.cpp +++ b/libnymea-zigbee/nxp/zigbeenetworknxp.cpp @@ -131,7 +131,7 @@ void ZigbeeNetworkNxp::setStartingState(ZigbeeNetworkNxp::StartingState state) if (channel() == 0) { qCDebug(dcZigbeeNetwork()) << "Autoselect quitest channel for the zigbee network. FIXME: currently hardcoded to 13 due to firmware error."; quint32 channelMask = 0; - channelMask |= 1 << (13); + channelMask |= 1 << (14); reply = m_controller->commandSetChannelMask(channelMask); } else { quint32 channelMask = 0; @@ -675,19 +675,22 @@ void ZigbeeNetworkNxp::processAttributeReport(const ZigbeeInterfaceMessage &mess break; } - ZigbeeNode *node = getZigbeeNode(sourceAddress); + ZigbeeNodeNxp *node = qobject_cast(getZigbeeNode(sourceAddress)); if (!node) { qCWarning(dcZigbeeNode()) << "Received an attribute report from an unknown node. Ignoring data."; return; } - ZigbeeNodeEndpointNxp *endpoint = qobject_cast(node->getEndpoint(endpointId)); - if (!endpoint) { - qCWarning(dcZigbeeNetwork()) << "There is no endpoint for this attribute report."; - return; - } + ZigbeeClusterAttributeReport attributeReport; + attributeReport.sourceAddress = sourceAddress; + attributeReport.endpointId = endpointId; + attributeReport.clusterId = static_cast(clusterId); + attributeReport.attributeId = attributeId; + attributeReport.attributeStatus = static_cast(attributeStatus); + attributeReport.dataType = dataType; + attributeReport.data = attributeData; - endpoint->setClusterAttribute(static_cast(clusterId), ZigbeeClusterAttribute(attributeId, dataType, attributeData)); + node->setClusterAttributeReport(attributeReport); } void ZigbeeNetworkNxp::processReadAttributeResponse(const ZigbeeInterfaceMessage &message) @@ -751,19 +754,23 @@ void ZigbeeNetworkNxp::processReadAttributeResponse(const ZigbeeInterfaceMessage break; } - ZigbeeNode *node = getZigbeeNode(sourceAddress); + + ZigbeeNodeNxp *node = qobject_cast(getZigbeeNode(sourceAddress)); if (!node) { qCWarning(dcZigbeeNode()) << "Received an attribute report from an unknown node. Ignoring data."; return; } - ZigbeeNodeEndpointNxp *endpoint = qobject_cast(node->getEndpoint(endpointId)); - if (!endpoint) { - qCWarning(dcZigbeeNetwork()) << "There is no endpoint for this attribute report."; - return; - } + ZigbeeClusterAttributeReport attributeReport; + attributeReport.sourceAddress = sourceAddress; + attributeReport.endpointId = endpointId; + attributeReport.clusterId = static_cast(clusterId); + attributeReport.attributeId = attributeId; + attributeReport.attributeStatus = static_cast(attributeStatus); + attributeReport.dataType = dataType; + attributeReport.data = attributeData; - endpoint->setClusterAttribute(static_cast(clusterId), ZigbeeClusterAttribute(attributeId, dataType, attributeData)); + node->setClusterAttributeReport(attributeReport); } void ZigbeeNetworkNxp::processLeaveIndication(const ZigbeeInterfaceMessage &message) diff --git a/libnymea-zigbee/nxp/zigbeenodeendpointnxp.cpp b/libnymea-zigbee/nxp/zigbeenodeendpointnxp.cpp index 98ee6de..045a963 100644 --- a/libnymea-zigbee/nxp/zigbeenodeendpointnxp.cpp +++ b/libnymea-zigbee/nxp/zigbeenodeendpointnxp.cpp @@ -57,12 +57,12 @@ ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::readAttribute(ZigbeeCluster *cluster, return nullptr; } -ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::configureReporting(ZigbeeCluster *cluster, QList attributes) +ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::configureReporting(ZigbeeCluster *cluster, QList reportConfigurations) { - qCDebug(dcZigbeeNode()) << "Configure reporting" << node() << cluster << attributes; - ZigbeeInterfaceReply *reply = m_controller->commandConfigureReportingRequest(0x00, node()->shortAddress(), 0x01, - endpointId(), cluster, attributes, - false, node()->manufacturerCode()); + qCDebug(dcZigbeeNode()) << "Configure reporting" << node(); + ZigbeeInterfaceReply *reply = m_controller->commandConfigureReportingRequest(0x02, node()->shortAddress(), 0x01, + endpointId(), cluster, 0x01, + false, node()->manufacturerCode(), reportConfigurations); connect(reply, &ZigbeeInterfaceReply::finished, this, [reply](){ reply->deleteLater(); @@ -95,6 +95,24 @@ ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::identify(quint16 seconds) return nullptr; } +ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::factoryReset() +{ + qCDebug(dcZigbeeNode()) << "Factory reset" << this; + ZigbeeInterfaceReply *reply = m_controller->commandFactoryResetNode(node()->shortAddress(), 0x01, endpointId()); + connect(reply, &ZigbeeInterfaceReply::finished, this, [reply](){ + 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"; + }); + + return nullptr; +} + ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::bindGroup(Zigbee::ClusterId clusterId, quint16 destinationAddress, quint8 destinationEndpoint) { qCDebug(dcZigbeeNode()) << "Bind group" << node() << clusterId << ZigbeeUtils::convertUint16ToHexString(destinationAddress) << ZigbeeUtils::convertByteToHexString(destinationEndpoint); @@ -291,10 +309,31 @@ ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::sendMoveToSaturation(quint8 saturatio void ZigbeeNodeEndpointNxp::setClusterAttribute(Zigbee::ClusterId clusterId, const ZigbeeClusterAttribute &attribute) { - foreach (ZigbeeCluster *cluster, m_inputClusters) { - if (cluster->clusterId() == clusterId) { - cluster->setAttribute(attribute); - emit clusterAttributeChanged(cluster, attribute); - } + // Check if this cluster is an input cluster + if (hasInputCluster(clusterId)) { + ZigbeeCluster *cluster = getInputCluster(clusterId); + cluster->setAttribute(attribute); + emit clusterAttributeChanged(cluster, attribute); + return; } + + // Check if this cluster is an output cluster + if (hasOutputCluster(clusterId)) { + ZigbeeCluster *cluster = getOutputCluster(clusterId); + cluster->setAttribute(attribute); + emit clusterAttributeChanged(cluster, attribute); + return; + } + + // There is no cluster yet. Create it as output cluster if this is not the basic cluster + ZigbeeCluster *cluster = nullptr; + if (clusterId == Zigbee::ClusterIdBasic) { + cluster = new ZigbeeCluster(clusterId, ZigbeeCluster::Input, this); + addInputCluster(cluster); + } else { + cluster = new ZigbeeCluster(clusterId, ZigbeeCluster::Output, this); + addOutputCluster(cluster); + } + cluster->setAttribute(attribute); + emit clusterAttributeChanged(cluster, attribute); } diff --git a/libnymea-zigbee/nxp/zigbeenodeendpointnxp.h b/libnymea-zigbee/nxp/zigbeenodeendpointnxp.h index 07e20bd..0f9ff7d 100644 --- a/libnymea-zigbee/nxp/zigbeenodeendpointnxp.h +++ b/libnymea-zigbee/nxp/zigbeenodeendpointnxp.h @@ -44,8 +44,10 @@ public: explicit ZigbeeNodeEndpointNxp(ZigbeeBridgeControllerNxp *controller, ZigbeeNode *node, quint8 endpoint, QObject *parent = nullptr); ZigbeeNetworkReply *readAttribute(ZigbeeCluster *cluster, QList attributes) override; - ZigbeeNetworkReply *configureReporting(ZigbeeCluster *cluster, QList attributes) override; + ZigbeeNetworkReply *configureReporting(ZigbeeCluster *cluster, QList reportConfigurations) override; + ZigbeeNetworkReply *identify(quint16 seconds) override; + ZigbeeNetworkReply *factoryReset() override; ZigbeeNetworkReply *bindGroup(Zigbee::ClusterId clusterId, quint16 destinationAddress, quint8 destinationEndpoint) override; ZigbeeNetworkReply *bindUnicast(Zigbee::ClusterId clusterId, const ZigbeeAddress &destinationAddress, quint8 destinationEndpoint) override; ZigbeeNetworkReply *sendOnOffClusterCommand(ZigbeeCluster::OnOffClusterCommand command) override; diff --git a/libnymea-zigbee/nxp/zigbeenodenxp.cpp b/libnymea-zigbee/nxp/zigbeenodenxp.cpp index 839fbe9..3adee74 100644 --- a/libnymea-zigbee/nxp/zigbeenodenxp.cpp +++ b/libnymea-zigbee/nxp/zigbeenodenxp.cpp @@ -177,7 +177,12 @@ void ZigbeeNodeNxp::setInitState(ZigbeeNodeNxp::InitState initState) quint8 deviceVersion = (bitField >> 4); qCDebug(dcZigbeeNetwork()) << " Bit field:" << ZigbeeUtils::convertByteToHexString(bitField) << deviceVersion; - ZigbeeNodeEndpointNxp *endpoint = new ZigbeeNodeEndpointNxp(m_controller, this, endpointId, this); + ZigbeeNodeEndpointNxp *endpoint = qobject_cast(getEndpoint(endpointId)); + if (!endpoint) { + endpoint = new ZigbeeNodeEndpointNxp(m_controller, this, endpointId, this); + m_endpoints.append(endpoint); + } + endpoint->setProfile(static_cast(profileId)); endpoint->setDeviceId(deviceId); endpoint->setDeviceVersion(deviceVersion); @@ -187,7 +192,9 @@ void ZigbeeNodeNxp::setInitState(ZigbeeNodeNxp::InitState initState) for (int i = 0; i < inputClusterCount; i+=1) { quint16 clusterId = 0; stream >> clusterId; - endpoint->addInputCluster(new ZigbeeCluster(static_cast(clusterId), ZigbeeCluster::Input, endpoint)); + if (!endpoint->hasInputCluster(static_cast(clusterId))) { + endpoint->addInputCluster(new ZigbeeCluster(static_cast(clusterId), ZigbeeCluster::Input, endpoint)); + } qCDebug(dcZigbeeNetwork()) << " Cluster ID:" << ZigbeeUtils::convertUint16ToHexString(clusterId) << ZigbeeUtils::clusterIdToString(static_cast(clusterId)); } @@ -201,11 +208,11 @@ void ZigbeeNodeNxp::setInitState(ZigbeeNodeNxp::InitState initState) quint16 clusterId = 0; stream >> clusterId; - endpoint->addOutputCluster(new ZigbeeCluster(static_cast(clusterId), ZigbeeCluster::Output, endpoint)); + if (!endpoint->hasInputCluster(static_cast(clusterId))) { + endpoint->addOutputCluster(new ZigbeeCluster(static_cast(clusterId), ZigbeeCluster::Output, endpoint)); + } qCDebug(dcZigbeeNetwork()) << " Cluster ID:" << ZigbeeUtils::convertUint16ToHexString(clusterId) << ZigbeeUtils::clusterIdToString(static_cast(clusterId)); } - - m_endpoints.append(endpoint); } m_uninitializedEndpoints.removeAll(endpointId); @@ -217,23 +224,16 @@ void ZigbeeNodeNxp::setInitState(ZigbeeNodeNxp::InitState initState) } break; case InitStateReadClusterAttributes: - if (shortAddress() == 0x0000) { - qCDebug(dcZigbeeNode()) << "No need to read the endpoint baisc clusters of the coordinator node"; - setState(StateInitialized); - break; - } - +// 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) { // Read basic cluster qCDebug(dcZigbeeNode()) << "Read basic cluster for endpoint" << endpoint; - QList attributes; - attributes.append(ZigbeeCluster::BasicAttributeZclVersion); - attributes.append(ZigbeeCluster::BasicAttributeManufacturerName); - attributes.append(ZigbeeCluster::BasicAttributeModelIdentifier); - attributes.append(ZigbeeCluster::BasicAttributePowerSource); - attributes.append(ZigbeeCluster::BasicAttributeSwBuildId); ZigbeeCluster *basicCluster = endpoint->getInputCluster(Zigbee::ClusterIdBasic); if (!basicCluster) { @@ -242,46 +242,29 @@ void ZigbeeNodeNxp::setInitState(ZigbeeNodeNxp::InitState initState) return; } + QList attributes; + attributes.append(ZigbeeCluster::BasicAttributeZclVersion); + attributes.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. + if (!basicCluster->hasAttribute(ZigbeeCluster::BasicAttributeModelIdentifier)) + attributes.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()); + 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(); } - - // QList attributes; - // attributes.append(ZigbeeCluster::BasicAttributePowerSource); - // attributes.append(ZigbeeCluster::BasicAttributeLocationDescription); - // attributes.append(ZigbeeCluster::BasicAttributePhysicalEnvironment); - // attributes.append(ZigbeeCluster::BasicAttributeDeviceEnabled); - // attributes.append(ZigbeeCluster::BasicAttributeAlarmMask); - // attributes.append(ZigbeeCluster::BasicAttributeDisableLocalConfig); - // attributes.append(ZigbeeCluster::BasicAttributeSwBuildId); - - - // ZigbeeInterfaceReply *reply2 = m_controller->commandReadAttributeRequest(0x02, shortAddress(), - // 0x01, endpoint->endpointId(), - // basicCluster, - // attributes, - // false, - // manufacturerCode()); - // connect(reply, &ZigbeeInterfaceReply::finished, this, [this, reply2, endpoint](){ - // reply2->deleteLater(); - // if (reply2->status() != ZigbeeInterfaceReply::Success) { - // qCWarning(dcZigbeeController()) << "Could not" << reply2->request().description() << reply2->status() << reply2->statusErrorMessage(); - // } - - // }); - - - - qCDebug(dcZigbeeNode()) << "Reading basic cluster attributes finished successfully for" << endpoint; qCDebug(dcZigbeeNode()) << "The device should response with multiple attribute read notifications."; setState(StateInitialized); @@ -303,3 +286,20 @@ ZigbeeNodeEndpoint *ZigbeeNodeNxp::createNodeEndpoint(quint8 endpointId, QObject { return new ZigbeeNodeEndpointNxp(m_controller, this, endpointId, parent); } + +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; + return; + } + + ZigbeeNodeEndpointNxp *endpoint = qobject_cast(getEndpoint(report.endpointId)); + if (!endpoint) { + qCDebug(dcZigbeeNetwork()) << "Recived attribute report but there is no endpoint on this node yet. Create it..."; + endpoint = new ZigbeeNodeEndpointNxp(m_controller, this, report.endpointId, this); + m_endpoints.append(endpoint); + } + + endpoint->setClusterAttribute(report.clusterId, ZigbeeClusterAttribute(report.attributeId, report.dataType, report.data)); +} diff --git a/libnymea-zigbee/nxp/zigbeenodenxp.h b/libnymea-zigbee/nxp/zigbeenodenxp.h index f38e7a2..0b1709d 100644 --- a/libnymea-zigbee/nxp/zigbeenodenxp.h +++ b/libnymea-zigbee/nxp/zigbeenodenxp.h @@ -31,6 +31,7 @@ #include #include "zigbeenode.h" +#include "zigbeenodeendpointnxp.h" #include "zigbeebridgecontrollernxp.h" class ZigbeeNodeNxp : public ZigbeeNode @@ -59,6 +60,7 @@ private: QList m_uninitializedEndpoints; void setInitState(InitState initState); + void setClusterAttributeReport(const ZigbeeClusterAttributeReport &report) override; protected: void startInitialization() override; diff --git a/libnymea-zigbee/zigbeecluster.cpp b/libnymea-zigbee/zigbeecluster.cpp index a526b47..ef9a34c 100644 --- a/libnymea-zigbee/zigbeecluster.cpp +++ b/libnymea-zigbee/zigbeecluster.cpp @@ -27,13 +27,14 @@ #include "zigbeeutils.h" #include "zigbeecluster.h" +#include "loggingcategory.h" ZigbeeCluster::ZigbeeCluster(Zigbee::ClusterId clusterId, Direction direction, QObject *parent) : QObject(parent), m_clusterId(clusterId), m_direction(direction) { - + qCDebug(dcZigbeeCluster()) << "Create cluster" << ZigbeeUtils::convertUint16ToHexString(clusterId) << direction; } ZigbeeCluster::Direction ZigbeeCluster::direction() const @@ -71,10 +72,12 @@ ZigbeeClusterAttribute ZigbeeCluster::attribute(quint16 id) void ZigbeeCluster::setAttribute(const ZigbeeClusterAttribute &attribute) { - if (hasAttribute(attribute.id())) { + if (hasAttribute(attribute.id())) { + qCDebug(dcZigbeeCluster()) << this << "update attribute" << attribute; m_attributes[attribute.id()] = attribute; emit attributeChanged(attribute); } else { + qCDebug(dcZigbeeCluster()) << this << "add attribute" << attribute; m_attributes.insert(attribute.id(), attribute); emit attributeChanged(attribute); } diff --git a/libnymea-zigbee/zigbeecluster.h b/libnymea-zigbee/zigbeecluster.h index cf93c82..bcfaa26 100644 --- a/libnymea-zigbee/zigbeecluster.h +++ b/libnymea-zigbee/zigbeecluster.h @@ -33,6 +33,27 @@ #include "zigbee.h" #include "zigbeeclusterattribute.h" +struct ZigbeeClusterReportConfigurationRecord { + quint8 direction; + Zigbee::DataType dataType; + quint16 attributeId; + quint16 minInterval; + quint16 maxInterval; + quint16 timeout; + quint8 change; +}; + +struct ZigbeeClusterAttributeReport { + quint16 sourceAddress; + quint8 endpointId; + Zigbee::ClusterId clusterId; + quint16 attributeId; + Zigbee::ZigbeeStatus attributeStatus; + Zigbee::DataType dataType; + QByteArray data; +}; + + class ZigbeeCluster : public QObject { Q_OBJECT diff --git a/libnymea-zigbee/zigbeenode.h b/libnymea-zigbee/zigbeenode.h index f3e68a1..e07f9c8 100644 --- a/libnymea-zigbee/zigbeenode.h +++ b/libnymea-zigbee/zigbeenode.h @@ -199,6 +199,8 @@ private: bool m_extendedActiveEndpointListAvailable = false; bool m_extendedSimpleDescriptorListAvailable = false; + virtual void setClusterAttributeReport(const ZigbeeClusterAttributeReport &report) = 0; + protected: ZigbeeNode(QObject *parent = nullptr); diff --git a/libnymea-zigbee/zigbeenodeendpoint.h b/libnymea-zigbee/zigbeenodeendpoint.h index d9fcd4a..5ad8200 100644 --- a/libnymea-zigbee/zigbeenodeendpoint.h +++ b/libnymea-zigbee/zigbeenodeendpoint.h @@ -75,11 +75,14 @@ public: // Attribute methods virtual ZigbeeNetworkReply *readAttribute(ZigbeeCluster *cluster, QList attributes) = 0; - virtual ZigbeeNetworkReply *configureReporting(ZigbeeCluster *cluster, QList attributes) = 0; + virtual ZigbeeNetworkReply *configureReporting(ZigbeeCluster *cluster, QList reportConfigurations) = 0; // Identify virtual ZigbeeNetworkReply *identify(quint16 seconds) = 0; + // Reset + virtual ZigbeeNetworkReply *factoryReset() = 0; + // Binding virtual ZigbeeNetworkReply *bindGroup(Zigbee::ClusterId clusterId, quint16 destinationAddress, quint8 destinationEndpoint) = 0; virtual ZigbeeNetworkReply *bindUnicast(Zigbee::ClusterId clusterId, const ZigbeeAddress &destinationAddress, quint8 destinationEndpoint) = 0; diff --git a/libnymea-zigbee/zigbeeutils.cpp b/libnymea-zigbee/zigbeeutils.cpp index 13bb957..51bb0f2 100644 --- a/libnymea-zigbee/zigbeeutils.cpp +++ b/libnymea-zigbee/zigbeeutils.cpp @@ -416,8 +416,7 @@ QColor ZigbeeUtils::interpolateColorFromColorTemperature(int colorTemperature, i int intervalSize = maxValue - minValue; int intervalPosition = colorTemperature - minValue; double percentage = intervalPosition * 1.0 / intervalSize; - //qWarning() << "Interpolate color" << intervalSize << intervalPosition << percentage; int closestColorIndex = qRound((colorTemperatureScale.count() - 1) * (1.0 - percentage)); - //qWarning() << "Colors size" << colorTemperatureScale.count() << "Color position according to percentage" << closestColorIndex; + // FIXME: interpolate between the selected index and the next color for more accuracy if required return colorTemperatureScale.at(closestColorIndex); }