diff --git a/docs/deCONZ-Serial-Protocol-en.pdf b/docs/deCONZ-Serial-Protocol-en.pdf index d8e6bdc..c9eeea4 100644 Binary files a/docs/deCONZ-Serial-Protocol-en.pdf and b/docs/deCONZ-Serial-Protocol-en.pdf differ diff --git a/libnymea-zigbee/nxp/zigbeenodeendpointnxp.cpp b/libnymea-zigbee/nxp/zigbeenodeendpointnxp.cpp index f88e1a7..130bfa3 100644 --- a/libnymea-zigbee/nxp/zigbeenodeendpointnxp.cpp +++ b/libnymea-zigbee/nxp/zigbeenodeendpointnxp.cpp @@ -40,14 +40,16 @@ ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::readAttribute(ZigbeeCluster *cluster, { qCDebug(dcZigbeeNode()) << "Read" << node() << cluster << attributes; + ZigbeeNetworkReply *networkReply = createNetworkReply(); ZigbeeInterfaceReply *reply = m_controller->commandReadAttributeRequest(0x02, node()->shortAddress(), 0x01, endpointId(), cluster, attributes, false, node()->manufacturerCode()); - connect(reply, &ZigbeeInterfaceReply::finished, this, [this, reply](){ + connect(reply, &ZigbeeInterfaceReply::finished, this, [this, reply, networkReply](){ reply->deleteLater(); if (reply->status() != ZigbeeInterfaceReply::Success) { qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); + finishNetworkReply(networkReply, ZigbeeNetworkReply::ErrorUnknown); return; } @@ -56,9 +58,11 @@ ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::readAttribute(ZigbeeCluster *cluster, ZigbeeClusterAttributeReport report = ZigbeeUtils::parseAttributeReport(reply->additionalMessage().data()); setClusterAttribute(report.clusterId, ZigbeeClusterAttribute(report.attributeId, report.dataType, report.data)); } + + finishNetworkReply(networkReply); }); - return nullptr; + return networkReply; } ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::configureReporting(ZigbeeCluster *cluster, QList reportConfigurations) @@ -67,172 +71,195 @@ ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::configureReporting(ZigbeeCluster *clu // FIXME: check the report configuration and the direction field according to specs + ZigbeeNetworkReply *networkReply = createNetworkReply(); ZigbeeInterfaceReply *reply = m_controller->commandConfigureReportingRequest(0x02, node()->shortAddress(), 0x01, endpointId(), cluster, 0x00, false, node()->manufacturerCode(), reportConfigurations); - connect(reply, &ZigbeeInterfaceReply::finished, this, [reply](){ + connect(reply, &ZigbeeInterfaceReply::finished, this, [this, reply, networkReply](){ reply->deleteLater(); if (reply->status() != ZigbeeInterfaceReply::Success) { qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); + finishNetworkReply(networkReply, ZigbeeNetworkReply::ErrorUnknown); return; } qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; + finishNetworkReply(networkReply); }); - return nullptr; + return networkReply; } ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::identify(quint16 seconds) { qCDebug(dcZigbeeNode()) << "Identify" << node() << seconds << seconds; + ZigbeeNetworkReply *networkReply = createNetworkReply(); ZigbeeInterfaceReply *reply = m_controller->commandIdentify(0x02, node()->shortAddress(), 0x01, endpointId(), seconds); - connect(reply, &ZigbeeInterfaceReply::finished, this, [reply](){ + connect(reply, &ZigbeeInterfaceReply::finished, this, [this, reply, networkReply](){ reply->deleteLater(); if (reply->status() != ZigbeeInterfaceReply::Success) { qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); + finishNetworkReply(networkReply, ZigbeeNetworkReply::ErrorUnknown); return; } qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; + finishNetworkReply(networkReply); }); - return nullptr; + return networkReply; } ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::factoryReset() { qCDebug(dcZigbeeNode()) << "Factory reset" << this; + ZigbeeNetworkReply *networkReply = createNetworkReply(); ZigbeeInterfaceReply *reply = m_controller->commandFactoryResetNode(node()->shortAddress(), 0x01, endpointId()); - connect(reply, &ZigbeeInterfaceReply::finished, this, [reply](){ + connect(reply, &ZigbeeInterfaceReply::finished, this, [this, reply, networkReply](){ reply->deleteLater(); if (reply->status() != ZigbeeInterfaceReply::Success) { qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); + finishNetworkReply(networkReply, ZigbeeNetworkReply::ErrorUnknown); return; } qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; + finishNetworkReply(networkReply); }); - return nullptr; + return networkReply; } ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::bindGroup(Zigbee::ClusterId clusterId, quint16 destinationAddress, quint8 destinationEndpoint) { qCDebug(dcZigbeeNode()) << "Bind group" << node() << clusterId << ZigbeeUtils::convertUint16ToHexString(destinationAddress) << ZigbeeUtils::convertByteToHexString(destinationEndpoint); + ZigbeeNetworkReply *networkReply = createNetworkReply(); ZigbeeInterfaceReply *reply = m_controller->commandBindGroup(node()->extendedAddress(), endpointId(), clusterId, destinationAddress, destinationEndpoint); - connect(reply, &ZigbeeInterfaceReply::finished, this, [reply](){ + connect(reply, &ZigbeeInterfaceReply::finished, this, [this, reply, networkReply](){ reply->deleteLater(); if (reply->status() != ZigbeeInterfaceReply::Success) { qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); + finishNetworkReply(networkReply, ZigbeeNetworkReply::ErrorUnknown); return; } qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; qCDebug(dcZigbeeController()) << reply->additionalMessage().data(); + finishNetworkReply(networkReply); }); - return nullptr; + return networkReply; } ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::bindUnicast(Zigbee::ClusterId clusterId, const ZigbeeAddress &destinationAddress, quint8 destinationEndpoint) { qCDebug(dcZigbeeNode()) << "Bind unicast" << node() << clusterId << destinationAddress.toString() << ZigbeeUtils::convertByteToHexString(destinationEndpoint); + ZigbeeNetworkReply *networkReply = createNetworkReply(); ZigbeeInterfaceReply *reply = m_controller->commandBindUnicast(node()->extendedAddress(), endpointId(), clusterId, destinationAddress, destinationEndpoint); - connect(reply, &ZigbeeInterfaceReply::finished, this, [reply](){ + connect(reply, &ZigbeeInterfaceReply::finished, this, [this, reply, networkReply](){ reply->deleteLater(); if (reply->status() != ZigbeeInterfaceReply::Success) { qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); + finishNetworkReply(networkReply, ZigbeeNetworkReply::ErrorUnknown); return; } qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; qCDebug(dcZigbeeController()) << reply->additionalMessage().data(); + finishNetworkReply(networkReply); }); - return nullptr; + return networkReply; } ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::sendOnOffClusterCommand(ZigbeeCluster::OnOffClusterCommand command) { qCDebug(dcZigbeeNode()) << "Send on/off cluster command" << node() << command; - + ZigbeeNetworkReply *networkReply = createNetworkReply(); ZigbeeInterfaceReply *reply = m_controller->commandOnOffNoEffects(0x02, node()->shortAddress(), 0x01, endpointId(), command); - connect(reply, &ZigbeeInterfaceReply::finished, this, [reply](){ + connect(reply, &ZigbeeInterfaceReply::finished, this, [this, reply, networkReply](){ reply->deleteLater(); if (reply->status() != ZigbeeInterfaceReply::Success) { qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); + finishNetworkReply(networkReply, ZigbeeNetworkReply::ErrorUnknown); return; } qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; + finishNetworkReply(networkReply); }); - return nullptr; + return networkReply; } ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::addGroup(quint8 destinationEndpoint, quint16 groupAddress) { qCDebug(dcZigbeeNode()) << "Add group request" << node() << destinationEndpoint << groupAddress; - + ZigbeeNetworkReply *networkReply = createNetworkReply(); ZigbeeInterfaceReply *reply = m_controller->commandAddGroup(0x02, node()->shortAddress(), 0x01, endpointId(), groupAddress); - connect(reply, &ZigbeeInterfaceReply::finished, this, [reply](){ + connect(reply, &ZigbeeInterfaceReply::finished, this, [this, reply, networkReply](){ reply->deleteLater(); if (reply->status() != ZigbeeInterfaceReply::Success) { qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); + finishNetworkReply(networkReply, ZigbeeNetworkReply::ErrorUnknown); return; } qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; qCDebug(dcZigbeeController()) << reply->additionalMessage().data(); + finishNetworkReply(networkReply); }); - return nullptr; + return networkReply; } ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::sendLevelCommand(ZigbeeCluster::LevelClusterCommand command, quint8 level, bool triggersOnOff, quint16 transitionTime) { qCDebug(dcZigbeeNode()) << "Move to level request" << node() << command << level; - + ZigbeeNetworkReply *networkReply = createNetworkReply(); ZigbeeInterfaceReply *reply = m_controller->commandMoveToLevel(0x02, node()->shortAddress(), 0x01, endpointId(), triggersOnOff, level, transitionTime); - connect(reply, &ZigbeeInterfaceReply::finished, this, [reply](){ + connect(reply, &ZigbeeInterfaceReply::finished, this, [this, reply, networkReply](){ reply->deleteLater(); if (reply->status() != ZigbeeInterfaceReply::Success) { qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); + finishNetworkReply(networkReply, ZigbeeNetworkReply::ErrorUnknown); return; } qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; + finishNetworkReply(networkReply); }); - return nullptr; + return networkReply; } ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::sendMoveToColorTemperature(quint16 colourTemperature, quint16 transitionTime) { qCDebug(dcZigbeeNode()) << "Move to level request" << node() << colourTemperature << transitionTime; - + ZigbeeNetworkReply *networkReply = createNetworkReply(); ZigbeeInterfaceReply *reply = m_controller->commandMoveToColourTemperature(0x02, node()->shortAddress(), 0x01, endpointId(), colourTemperature, transitionTime); - connect(reply, &ZigbeeInterfaceReply::finished, this, [reply](){ + connect(reply, &ZigbeeInterfaceReply::finished, this, [this, reply, networkReply](){ reply->deleteLater(); if (reply->status() != ZigbeeInterfaceReply::Success) { qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); + finishNetworkReply(networkReply, ZigbeeNetworkReply::ErrorUnknown); return; } qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; + finishNetworkReply(networkReply); }); - return nullptr; + return networkReply; } ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::sendMoveToColor(double x, double y, quint16 transitionTime) @@ -242,76 +269,85 @@ ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::sendMoveToColor(double x, double y, q quint16 normalizedX = static_cast(qRound(x * 65536)); quint16 normalizedY = static_cast(qRound(y * 65536)); + ZigbeeNetworkReply *networkReply = createNetworkReply(); ZigbeeInterfaceReply *reply = m_controller->commandMoveToColor(0x02, node()->shortAddress(), 0x01, endpointId(), normalizedX, normalizedY, transitionTime); - connect(reply, &ZigbeeInterfaceReply::finished, this, [reply](){ + connect(reply, &ZigbeeInterfaceReply::finished, this, [this, reply, networkReply](){ reply->deleteLater(); if (reply->status() != ZigbeeInterfaceReply::Success) { qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); + finishNetworkReply(networkReply, ZigbeeNetworkReply::ErrorUnknown); return; } qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; + finishNetworkReply(networkReply); }); - return nullptr; + return networkReply; } ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::sendMoveToHueSaturation(quint8 hue, quint8 saturation, quint16 transitionTime) { qCDebug(dcZigbeeNode()) << "Move to hue saturation request" << node() << hue << saturation << transitionTime; - + ZigbeeNetworkReply *networkReply = createNetworkReply(); ZigbeeInterfaceReply *reply = m_controller->commandMoveToHueSaturation(0x02, node()->shortAddress(), 0x01, endpointId(), hue, saturation, transitionTime); - connect(reply, &ZigbeeInterfaceReply::finished, this, [reply](){ + connect(reply, &ZigbeeInterfaceReply::finished, this, [this, reply, networkReply](){ reply->deleteLater(); if (reply->status() != ZigbeeInterfaceReply::Success) { qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); + finishNetworkReply(networkReply, ZigbeeNetworkReply::ErrorUnknown); return; } qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; + finishNetworkReply(networkReply); }); - return nullptr; + return networkReply; } ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::sendMoveToHue(quint8 hue, quint16 transitionTime) { qCDebug(dcZigbeeNode()) << "Move to hue request" << node() << hue << transitionTime; - + ZigbeeNetworkReply *networkReply = createNetworkReply(); ZigbeeInterfaceReply *reply = m_controller->commandMoveToHue(0x02, node()->shortAddress(), 0x01, endpointId(), hue, transitionTime); - connect(reply, &ZigbeeInterfaceReply::finished, this, [reply](){ + connect(reply, &ZigbeeInterfaceReply::finished, this, [this, reply, networkReply](){ reply->deleteLater(); if (reply->status() != ZigbeeInterfaceReply::Success) { qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); + finishNetworkReply(networkReply, ZigbeeNetworkReply::ErrorUnknown); return; } qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; + finishNetworkReply(networkReply); }); - return nullptr; + return networkReply; } ZigbeeNetworkReply *ZigbeeNodeEndpointNxp::sendMoveToSaturation(quint8 saturation, quint16 transitionTime) { qCDebug(dcZigbeeNode()) << "Move to saturation request" << node() << saturation << transitionTime; - + ZigbeeNetworkReply *networkReply = createNetworkReply(); ZigbeeInterfaceReply *reply = m_controller->commandMoveToSaturation(0x02, node()->shortAddress(), 0x01, endpointId(), saturation, transitionTime); - connect(reply, &ZigbeeInterfaceReply::finished, this, [reply](){ + connect(reply, &ZigbeeInterfaceReply::finished, this, [this, reply, networkReply](){ reply->deleteLater(); if (reply->status() != ZigbeeInterfaceReply::Success) { qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage(); + finishNetworkReply(networkReply, ZigbeeNetworkReply::ErrorUnknown); return; } qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully"; + finishNetworkReply(networkReply); }); - return nullptr; + return networkReply; } void ZigbeeNodeEndpointNxp::setClusterAttribute(Zigbee::ClusterId clusterId, const ZigbeeClusterAttribute &attribute) diff --git a/libnymea-zigbee/zigbeenetworkreply.cpp b/libnymea-zigbee/zigbeenetworkreply.cpp index f7acee3..6fc65f2 100644 --- a/libnymea-zigbee/zigbeenetworkreply.cpp +++ b/libnymea-zigbee/zigbeenetworkreply.cpp @@ -27,6 +27,16 @@ #include "zigbeenetworkreply.h" +ZigbeeNetworkReply::Error ZigbeeNetworkReply::error() const +{ + return m_error; +} + +Zigbee::ZigbeeStatus ZigbeeNetworkReply::zigbeeStatus() const +{ + return m_zigbeeStatus; +} + ZigbeeNetworkReply::ZigbeeNetworkReply(QObject *parent) : QObject(parent) { diff --git a/libnymea-zigbee/zigbeenetworkreply.h b/libnymea-zigbee/zigbeenetworkreply.h index eeb8ec6..e5edd52 100644 --- a/libnymea-zigbee/zigbeenetworkreply.h +++ b/libnymea-zigbee/zigbeenetworkreply.h @@ -30,13 +30,34 @@ #include +#include "zigbee.h" + class ZigbeeNetworkReply : public QObject { Q_OBJECT + + friend class ZigbeeNodeEndpoint; + public: + enum Error { + ErrorNoError, + ErrorZigbeeStatusError, + ErrorNetworkOffline, + ErrorUnknown + }; + Q_ENUM(Error) + + Error error() const; + Zigbee::ZigbeeStatus zigbeeStatus() const; + +private: explicit ZigbeeNetworkReply(QObject *parent = nullptr); + bool m_finished = false; + Error m_error = ErrorNoError; + Zigbee::ZigbeeStatus m_zigbeeStatus = Zigbee::ZigbeeStatusSuccess; signals: + void finished(); }; diff --git a/libnymea-zigbee/zigbeenode.cpp b/libnymea-zigbee/zigbeenode.cpp index 1d614d8..c7328f2 100644 --- a/libnymea-zigbee/zigbeenode.cpp +++ b/libnymea-zigbee/zigbeenode.cpp @@ -463,30 +463,6 @@ void ZigbeeNode::startInitialization() qCWarning(dcZigbeeNode()) << "Start initialization is not implemented for this backend."; } -//void ZigbeeNode::setClusterAttribute(Zigbee::ClusterId clusterId, const ZigbeeClusterAttribute &attribute) -//{ -// qCDebug(dcZigbeeNode()) << this << "cluster attribute changed" << clusterId << attribute; -//// ZigbeeCluster *cluster = m_outputClusters.value(clusterId); - -//// // Note: create the cluster if not there yet -//// bool clusterCreated = false; -//// if (!cluster) { -//// cluster = new ZigbeeCluster(clusterId, this); -//// qCDebug(dcZigbeeNode()) << "Created cluster" << cluster; -//// connect(cluster, &ZigbeeCluster::attributeChanged, this, &ZigbeeNode::onClusterAttributeChanged); -//// m_outputClusters.insert(clusterId, cluster); -//// clusterCreated = true; -//// } - -//// // Set the attribute if valid -//// if (attribute.isValid()) -//// cluster->setAttribute(attribute); - -//// if (clusterCreated) -//// emit clusterAdded(cluster); - -//} - void ZigbeeNode::onClusterAttributeChanged(const ZigbeeClusterAttribute &attribute) { ZigbeeCluster *cluster = static_cast(sender()); @@ -529,23 +505,6 @@ void ZigbeeNode::onClusterAttributeChanged(const ZigbeeClusterAttribute &attribu // qCDebug(dcZigbeeNode()) << " 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) { diff --git a/libnymea-zigbee/zigbeenodeendpoint.cpp b/libnymea-zigbee/zigbeenodeendpoint.cpp index b7ac06f..e0dd517 100644 --- a/libnymea-zigbee/zigbeenodeendpoint.cpp +++ b/libnymea-zigbee/zigbeenodeendpoint.cpp @@ -155,6 +155,18 @@ void ZigbeeNodeEndpoint::addOutputCluster(ZigbeeCluster *cluster) m_outputClusters.insert(cluster->clusterId(), cluster); } +ZigbeeNetworkReply *ZigbeeNodeEndpoint::createNetworkReply() +{ + return new ZigbeeNetworkReply(this); +} + +void ZigbeeNodeEndpoint::finishNetworkReply(ZigbeeNetworkReply *reply, ZigbeeNetworkReply::Error error, Zigbee::ZigbeeStatus zigbeeStatus) +{ + reply->m_error = error; + reply->m_zigbeeStatus = zigbeeStatus; + reply->finished(); +} + ZigbeeCluster *ZigbeeNodeEndpoint::getOutputCluster(Zigbee::ClusterId clusterId) const { return m_outputClusters.value(clusterId); diff --git a/libnymea-zigbee/zigbeenodeendpoint.h b/libnymea-zigbee/zigbeenodeendpoint.h index 5ad8200..dea9207 100644 --- a/libnymea-zigbee/zigbeenodeendpoint.h +++ b/libnymea-zigbee/zigbeenodeendpoint.h @@ -130,6 +130,10 @@ protected: void addInputCluster(ZigbeeCluster *cluster); void addOutputCluster(ZigbeeCluster *cluster); + // Network reply methods + ZigbeeNetworkReply *createNetworkReply(); + void finishNetworkReply(ZigbeeNetworkReply *reply, ZigbeeNetworkReply::Error error = ZigbeeNetworkReply::ErrorNoError, Zigbee::ZigbeeStatus zigbeeStatus = Zigbee::ZigbeeStatusSuccess); + signals: void clusterAttributeChanged(ZigbeeCluster *cluster, const ZigbeeClusterAttribute &attribute); void manufacturerNameChanged(const QString &manufacturerName);