From 0768903b07cf816b1b5a83ddb1c5b3b256dc72cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Tue, 24 Nov 2020 12:45:20 +0100 Subject: [PATCH] Add group cluster and fix reply handling in NXP backend --- .../backends/nxp/zigbeenetworknxp.cpp | 29 ++++- libnymea-zigbee/libnymea-zigbee.pro | 2 + .../zcl/general/zigbeeclusterbasic.h | 7 +- .../zcl/general/zigbeeclustergroups.cpp | 102 ++++++++++++++++++ .../zcl/general/zigbeeclustergroups.h | 72 +++++++++++++ libnymea-zigbee/zigbeenetwork.cpp | 1 + libnymea-zigbee/zigbeenode.cpp | 8 +- libnymea-zigbee/zigbeenodeendpoint.cpp | 2 + libnymea-zigbee/zigbeenodeendpoint.h | 1 + 9 files changed, 212 insertions(+), 12 deletions(-) create mode 100644 libnymea-zigbee/zcl/general/zigbeeclustergroups.cpp create mode 100644 libnymea-zigbee/zcl/general/zigbeeclustergroups.h diff --git a/libnymea-zigbee/backends/nxp/zigbeenetworknxp.cpp b/libnymea-zigbee/backends/nxp/zigbeenetworknxp.cpp index 9711a0f..35d714a 100644 --- a/libnymea-zigbee/backends/nxp/zigbeenetworknxp.cpp +++ b/libnymea-zigbee/backends/nxp/zigbeenetworknxp.cpp @@ -76,8 +76,14 @@ ZigbeeNetworkReply *ZigbeeNetworkNxp::sendRequest(const ZigbeeNetworkRequest &re { ZigbeeNetworkReply *reply = createNetworkReply(request); // Send the request, and keep the reply until transposrt, zigbee trasmission and response arrived - connect(reply, &ZigbeeNetworkReply::finished, this, [this, request](){ - m_pendingReplies.remove(request.requestId()); + connect(reply, &ZigbeeNetworkReply::finished, this, [this, reply](){ + if (!m_pendingReplies.values().contains(reply)) { + qCWarning(dcZigbeeNetwork()) << "Reply finished but not in the pending replies list" << reply; + return; + } + quint8 requestId = m_pendingReplies.key(reply); + m_pendingReplies.remove(requestId); + //qCWarning(dcZigbeeNetwork()) << "#### Removed network reply" << reply << "ID:" << requestId << "Current reply count" << m_pendingReplies.count(); }); // Finish the reply right the way if the network is offline @@ -104,8 +110,8 @@ ZigbeeNetworkReply *ZigbeeNetworkNxp::sendRequest(const ZigbeeNetworkRequest &re quint8 networkRequestId = interfaceReply->responseData().at(0); //qCDebug(dcZigbeeNetwork()) << "Request has network SQN" << networkRequestId; reply->request().setRequestId(networkRequestId); + //qCWarning(dcZigbeeNetwork()) << "#### Insert network reply" << reply << "ID:" << networkRequestId << "Current reply count" << m_pendingReplies.count(); m_pendingReplies.insert(networkRequestId, reply); - // The request has been sent successfully to the device, start the timeout timer now startWaitingReply(reply); }); @@ -404,6 +410,23 @@ void ZigbeeNetworkNxp::onControllerStateChanged(ZigbeeBridgeControllerNxp::Contr connect(coordinatorNode, &ZigbeeNode::stateChanged, this, [this, coordinatorNode](ZigbeeNode::State state){ if (state == ZigbeeNode::StateInitialized) { qCDebug(dcZigbeeNetwork()) << "Coordinator initialized successfully." << coordinatorNode; +// ZigbeeClusterGroups *groupsCluster = coordinatorNode->getEndpoint(0x01)->inputCluster(ZigbeeClusterLibrary::ClusterIdGroups); +// if (!groupsCluster) { +// qCWarning(dcZigbeeNetwork()) << "Failed to get groups cluster from coordinator. The coordinator will not be in default group 0x0000"; +// setState(StateRunning); +// setPermitJoining(0); +// return; +// } + +// ZigbeeClusterReply *reply = groupsCluster->addGroup(0x0000, "Default"); +// connect(reply, &ZigbeeClusterReply::finished, this, [=](){ +// if (reply->error() != ZigbeeClusterReply::ErrorNoError) { +// qCWarning(dcZigbeeNetwork()) << "Failed to add coordinator to default group 0x0000. The coordinator will not be in default group 0x0000"; +// } +// setState(StateRunning); +// setPermitJoining(0); +// }); + setState(StateRunning); setPermitJoining(0); return; diff --git a/libnymea-zigbee/libnymea-zigbee.pro b/libnymea-zigbee/libnymea-zigbee.pro index f9c783e..83805e9 100644 --- a/libnymea-zigbee/libnymea-zigbee.pro +++ b/libnymea-zigbee/libnymea-zigbee.pro @@ -17,6 +17,7 @@ SOURCES += \ backends/nxp/zigbeebridgecontrollernxp.cpp \ backends/nxp/zigbeenetworknxp.cpp \ zcl/closures/zigbeeclusterdoorlock.cpp \ + zcl/general/zigbeeclustergroups.cpp \ zcl/general/zigbeeclusteridentify.cpp \ zcl/general/zigbeeclusterlevelcontrol.cpp \ zcl/general/zigbeeclusteronoff.cpp \ @@ -72,6 +73,7 @@ HEADERS += \ backends/nxp/zigbeebridgecontrollernxp.h \ backends/nxp/zigbeenetworknxp.h \ zcl/closures/zigbeeclusterdoorlock.h \ + zcl/general/zigbeeclustergroups.h \ zcl/general/zigbeeclusteridentify.h \ zcl/general/zigbeeclusterlevelcontrol.h \ zcl/general/zigbeeclusteronoff.h \ diff --git a/libnymea-zigbee/zcl/general/zigbeeclusterbasic.h b/libnymea-zigbee/zcl/general/zigbeeclusterbasic.h index 899792b..494e51e 100644 --- a/libnymea-zigbee/zcl/general/zigbeeclusterbasic.h +++ b/libnymea-zigbee/zcl/general/zigbeeclusterbasic.h @@ -187,14 +187,14 @@ public: AlarmMaskGeneralHardwareFault = 0x01, AlarmMaskGeneralSoftwareFault = 0x02 }; - Q_ENUM(AlarmMask) + Q_FLAG(AlarmMask) Q_DECLARE_FLAGS(AlarmMasks, AlarmMask) enum DiableLocalConfig { DiableLocalConfigReset = 0x01, DiableLocalConfigDeviceConfiguration = 0x02 }; - Q_ENUM(DiableLocalConfig) + Q_FLAG(DiableLocalConfig) Q_DECLARE_FLAGS(DiableLocalConfigs, DiableLocalConfig) explicit ZigbeeClusterBasic(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent = nullptr); @@ -206,7 +206,4 @@ private: }; -Q_DECLARE_OPERATORS_FOR_FLAGS(ZigbeeClusterBasic::AlarmMasks) -Q_DECLARE_OPERATORS_FOR_FLAGS(ZigbeeClusterBasic::DiableLocalConfigs) - #endif // ZIGBEECLUSTERBASIC_H diff --git a/libnymea-zigbee/zcl/general/zigbeeclustergroups.cpp b/libnymea-zigbee/zcl/general/zigbeeclustergroups.cpp new file mode 100644 index 0000000..c75d668 --- /dev/null +++ b/libnymea-zigbee/zcl/general/zigbeeclustergroups.cpp @@ -0,0 +1,102 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea-zigbee. +* This project including source code and documentation is protected by copyright law, and +* remains the property of nymea GmbH. All rights, including reproduction, publication, +* editing and translation, are reserved. The use of this project is subject to the terms of a +* license agreement to be concluded with nymea GmbH in accordance with the terms +* of use of nymea GmbH, available under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the terms of the GNU +* Lesser General Public License as published by the Free Software Foundation; version 3. +* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +* See the GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License along with this project. +* If not, see . +* +* For any further details and any questions please contact us under contact@nymea.io +* or see our FAQ/Licensing Information on https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "zigbeeclustergroups.h" +#include "loggingcategory.h" + +#include + +ZigbeeClusterGroups::ZigbeeClusterGroups(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent) : + ZigbeeCluster(network, node, endpoint, ZigbeeClusterLibrary::ClusterIdGroups, direction, parent) +{ + +} + +ZigbeeClusterReply *ZigbeeClusterGroups::addGroup(quint16 groupId, const QString &groupName) +{ + QByteArray payload; + QDataStream stream(&payload, QIODevice::WriteOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream << groupId << static_cast(Zigbee::CharString); + for (int i = 0; i < groupName.length(); i++) { + stream << static_cast(groupName.toUtf8().at(i)); + } + return executeClusterCommand(ZigbeeClusterGroups::CommandAddGroup, payload); +} + +ZigbeeClusterReply *ZigbeeClusterGroups::viewGroup(quint16 groupId) +{ + QByteArray payload; + QDataStream stream(&payload, QIODevice::WriteOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream << groupId; + return executeClusterCommand(ZigbeeClusterGroups::CommandViewGroup, payload); +} + +ZigbeeClusterReply *ZigbeeClusterGroups::getGroupMembership(quint8 groupCount, const QList &groupList) +{ + QByteArray payload; + QDataStream stream(&payload, QIODevice::WriteOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream << groupCount; + for (int i = 0; i < groupList.length(); i++) { + stream << groupList.at(i); + } + return executeClusterCommand(ZigbeeClusterGroups::CommandGetGroupMembership, payload); +} + +ZigbeeClusterReply *ZigbeeClusterGroups::removeGroup(quint16 groupId) +{ + QByteArray payload; + QDataStream stream(&payload, QIODevice::WriteOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream << groupId; + return executeClusterCommand(ZigbeeClusterGroups::CommandRemoveGroup, payload); +} + +ZigbeeClusterReply *ZigbeeClusterGroups::removeAllGroups() +{ + return executeClusterCommand(ZigbeeClusterGroups::CommandRemoveAllGroups); +} + +ZigbeeClusterReply *ZigbeeClusterGroups::addGroupIfIdentifying(quint16 groupId, const QString &groupName) +{ + QByteArray payload; + QDataStream stream(&payload, QIODevice::WriteOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream << groupId << static_cast(Zigbee::CharString); + for (int i = 0; i < groupName.length(); i++) { + stream << static_cast(groupName.toUtf8().at(i)); + } + return executeClusterCommand(ZigbeeClusterGroups::CommandAddGroup, payload); +} + +void ZigbeeClusterGroups::setAttribute(const ZigbeeClusterAttribute &attribute) +{ + qCDebug(dcZigbeeCluster()) << "Update attribute" << m_node << m_endpoint << this << static_cast(attribute.id()) << attribute.dataType(); + updateOrAddAttribute(attribute); +} diff --git a/libnymea-zigbee/zcl/general/zigbeeclustergroups.h b/libnymea-zigbee/zcl/general/zigbeeclustergroups.h new file mode 100644 index 0000000..c8044c1 --- /dev/null +++ b/libnymea-zigbee/zcl/general/zigbeeclustergroups.h @@ -0,0 +1,72 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2020, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea-zigbee. +* This project including source code and documentation is protected by copyright law, and +* remains the property of nymea GmbH. All rights, including reproduction, publication, +* editing and translation, are reserved. The use of this project is subject to the terms of a +* license agreement to be concluded with nymea GmbH in accordance with the terms +* of use of nymea GmbH, available under https://nymea.io/license +* +* GNU Lesser General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the terms of the GNU +* Lesser General Public License as published by the Free Software Foundation; version 3. +* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +* See the GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License along with this project. +* If not, see . +* +* For any further details and any questions please contact us under contact@nymea.io +* or see our FAQ/Licensing Information on https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef ZIGBEECLUSTERGROUPS_H +#define ZIGBEECLUSTERGROUPS_H + +#include + +#include "zcl/zigbeecluster.h" + +class ZigbeeClusterGroups : public ZigbeeCluster +{ + Q_OBJECT +public: + enum Attribute { + // 1 supported, 0 not supported + AttributeNameSupport = 0x0000 + }; + Q_ENUM(Attribute) + + enum Command { + CommandAddGroup = 0x00, + CommandViewGroup = 0x01, + CommandGetGroupMembership = 0x02, + CommandRemoveGroup = 0x03, + CommandRemoveAllGroups = 0x04, + CommandAddGroupIfIdentifying = 0x05 + }; + Q_ENUM(Command) + + explicit ZigbeeClusterGroups(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent = nullptr); + + ZigbeeClusterReply *addGroup(quint16 groupId, const QString &groupName); + ZigbeeClusterReply *viewGroup(quint16 groupId); + ZigbeeClusterReply *getGroupMembership(quint8 groupCount, const QList &groupList); + ZigbeeClusterReply *removeGroup(quint16 groupId); + ZigbeeClusterReply *removeAllGroups(); + ZigbeeClusterReply *addGroupIfIdentifying(quint16 groupId, const QString &groupName); + +signals: + +private: + void setAttribute(const ZigbeeClusterAttribute &attribute) override; + + +}; + +#endif // ZIGBEECLUSTERGROUPS_H diff --git a/libnymea-zigbee/zigbeenetwork.cpp b/libnymea-zigbee/zigbeenetwork.cpp index 0a928bf..5512df2 100644 --- a/libnymea-zigbee/zigbeenetwork.cpp +++ b/libnymea-zigbee/zigbeenetwork.cpp @@ -611,6 +611,7 @@ void ZigbeeNetwork::setReplyResponseError(ZigbeeNetworkReply *reply, Zigbee::Zig void ZigbeeNetwork::finishNetworkReply(ZigbeeNetworkReply *reply, ZigbeeNetworkReply::Error error) { + qCDebug(dcZigbeeNetwork()) << "Finish network reply" << reply << error; reply->m_error = error; switch(reply->error()) { case ZigbeeNetworkReply::ErrorNoError: diff --git a/libnymea-zigbee/zigbeenode.cpp b/libnymea-zigbee/zigbeenode.cpp index e0b1333..89fdfd3 100644 --- a/libnymea-zigbee/zigbeenode.cpp +++ b/libnymea-zigbee/zigbeenode.cpp @@ -505,7 +505,7 @@ void ZigbeeNode::readManufacturerName(ZigbeeClusterBasic *basicCluster) if (m_requestRetry < 3) { m_requestRetry++; qCDebug(dcZigbeeNode()) << "Retry to read manufacturer name from" << this << basicCluster << m_requestRetry << "/" << "3 attempts."; - readManufacturerName(basicCluster); + QTimer::singleShot(1000, this, [=](){readManufacturerName(basicCluster);}); } else { qCWarning(dcZigbeeNode()) << "Failed to read manufacturer name from" << this << basicCluster << "after 3 attempts. Giving up and continue..."; m_requestRetry = 0; @@ -564,7 +564,7 @@ void ZigbeeNode::readModelIdentifier(ZigbeeClusterBasic *basicCluster) if (m_requestRetry < 3) { m_requestRetry++; qCDebug(dcZigbeeNode()) << "Retry to read model identifier from" << this << basicCluster << m_requestRetry << "/" << "3 attempts."; - readModelIdentifier(basicCluster); + QTimer::singleShot(1000, this, [=](){readModelIdentifier(basicCluster);}); } else { qCWarning(dcZigbeeNode()) << "Failed to read model identifier from" << this << basicCluster << "after 3 attempts. Giving up and continue..."; m_requestRetry = 0; @@ -605,11 +605,11 @@ void ZigbeeNode::readSoftwareBuildId(ZigbeeClusterBasic *basicCluster) if (m_requestRetry < 3) { m_requestRetry++; qCDebug(dcZigbeeNode()) << "Retry to read model identifier from" << this << basicCluster << m_requestRetry << "/" << "3 attempts."; - readModelIdentifier(basicCluster); + QTimer::singleShot(1000, this, [=](){readSoftwareBuildId(basicCluster);}); } else { qCWarning(dcZigbeeNode()) << "Failed to read model identifier from" << this << basicCluster << "after 3 attempts. Giving up and continue..."; m_requestRetry = 0; - readSoftwareBuildId(basicCluster); + setState(StateInitialized); } return; } diff --git a/libnymea-zigbee/zigbeenodeendpoint.cpp b/libnymea-zigbee/zigbeenodeendpoint.cpp index e610b19..32315f0 100644 --- a/libnymea-zigbee/zigbeenodeendpoint.cpp +++ b/libnymea-zigbee/zigbeenodeendpoint.cpp @@ -175,6 +175,8 @@ ZigbeeCluster *ZigbeeNodeEndpoint::createCluster(ZigbeeClusterLibrary::ClusterId return new ZigbeeClusterOnOff(m_network, m_node, this, direction, this); case ZigbeeClusterLibrary::ClusterIdLevelControl: return new ZigbeeClusterLevelControl(m_network, m_node, this, direction, this); + case ZigbeeClusterLibrary::ClusterIdGroups: + return new ZigbeeClusterGroups(m_network, m_node, this, direction, this); // Measurement case ZigbeeClusterLibrary::ClusterIdIlluminanceMeasurement: diff --git a/libnymea-zigbee/zigbeenodeendpoint.h b/libnymea-zigbee/zigbeenodeendpoint.h index 4e64da5..73d1a20 100644 --- a/libnymea-zigbee/zigbeenodeendpoint.h +++ b/libnymea-zigbee/zigbeenodeendpoint.h @@ -42,6 +42,7 @@ #include "zcl/general/zigbeeclusteridentify.h" #include "zcl/general/zigbeeclusterlevelcontrol.h" #include "zcl/general/zigbeeclusterpowerconfiguration.h" +#include "zcl/general/zigbeeclustergroups.h" #include "zcl/closures/zigbeeclusterdoorlock.h"