From 8f1043ba9fd2dcf33b350759d13a118a33fa63ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Sun, 14 Jun 2020 14:39:10 +0200 Subject: [PATCH] Implement lqi updating of nodes and finishe on/off and level cluster --- .../backends/deconz/zigbeenetworkdeconz.cpp | 8 +-- .../zcl/general/zigbeeclusterlevelcontrol.cpp | 8 +-- .../zcl/general/zigbeeclusterlevelcontrol.h | 2 + .../zcl/general/zigbeeclusteronoff.cpp | 23 ++++++++ .../zcl/general/zigbeeclusteronoff.h | 12 +++- libnymea-zigbee/zdo/zigbeedeviceobject.cpp | 52 +++++++++++++++++ libnymea-zigbee/zdo/zigbeedeviceobject.h | 2 +- libnymea-zigbee/zigbeenetwork.cpp | 56 ++----------------- libnymea-zigbee/zigbeenetwork.h | 3 +- libnymea-zigbee/zigbeenode.cpp | 23 ++++++++ libnymea-zigbee/zigbeenode.h | 5 +- 11 files changed, 125 insertions(+), 69 deletions(-) diff --git a/libnymea-zigbee/backends/deconz/zigbeenetworkdeconz.cpp b/libnymea-zigbee/backends/deconz/zigbeenetworkdeconz.cpp index c5889c8..4006326 100644 --- a/libnymea-zigbee/backends/deconz/zigbeenetworkdeconz.cpp +++ b/libnymea-zigbee/backends/deconz/zigbeenetworkdeconz.cpp @@ -341,8 +341,8 @@ void ZigbeeNetworkDeconz::handleZigbeeDeviceProfileIndication(const Zigbee::Apsd return; } - // Let the node device object handle this (ZDP) - node->deviceObject()->processApsDataIndication(indication); + // Let the node handle this indication + handleNodeIndication(node, indication); } void ZigbeeNetworkDeconz::handleZigbeeClusterLibraryIndication(const Zigbee::ApsdeDataIndication &indication) @@ -358,8 +358,8 @@ void ZigbeeNetworkDeconz::handleZigbeeClusterLibraryIndication(const Zigbee::Aps // FIXME: maybe remove this node since we might have removed it but it did not respond, or we not explicitly allowed it to join. return; } - - node->handleZigbeeClusterLibraryIndication(indication); + // Let the node handle this indication + handleNodeIndication(node, indication); } void ZigbeeNetworkDeconz::setPermitJoiningInternal(bool permitJoining) diff --git a/libnymea-zigbee/zcl/general/zigbeeclusterlevelcontrol.cpp b/libnymea-zigbee/zcl/general/zigbeeclusterlevelcontrol.cpp index 89c4cef..b10c9c5 100644 --- a/libnymea-zigbee/zcl/general/zigbeeclusterlevelcontrol.cpp +++ b/libnymea-zigbee/zcl/general/zigbeeclusterlevelcontrol.cpp @@ -140,17 +140,11 @@ void ZigbeeClusterLevelControl::processDataIndication(ZigbeeClusterLibrary::Fram // Read the payload which is Command command = static_cast(frame.header.command); qCDebug(dcZigbeeCluster()) << "Command sent from" << m_node << m_endpoint << this << command; - switch (command) { - default: - qCWarning(dcZigbeeCluster()) << "Unhandled command sent from" << m_node << m_endpoint << this << command; - break; - } + emit commandSent(command, frame.payload); } break; case Server: qCWarning(dcZigbeeCluster()) << "Unhandled ZCL indication in" << m_node << m_endpoint << this << frame; break; } - - } diff --git a/libnymea-zigbee/zcl/general/zigbeeclusterlevelcontrol.h b/libnymea-zigbee/zcl/general/zigbeeclusterlevelcontrol.h index 820d757..83e2957 100644 --- a/libnymea-zigbee/zcl/general/zigbeeclusterlevelcontrol.h +++ b/libnymea-zigbee/zcl/general/zigbeeclusterlevelcontrol.h @@ -102,6 +102,8 @@ protected: signals: void currentLevelChanged(quint8 level); + void commandSent(ZigbeeClusterLevelControl::Command command, const QByteArray ¶meter = QByteArray()); + }; diff --git a/libnymea-zigbee/zcl/general/zigbeeclusteronoff.cpp b/libnymea-zigbee/zcl/general/zigbeeclusteronoff.cpp index 78c36f3..e2ddfd6 100644 --- a/libnymea-zigbee/zcl/general/zigbeeclusteronoff.cpp +++ b/libnymea-zigbee/zcl/general/zigbeeclusteronoff.cpp @@ -53,6 +53,29 @@ ZigbeeClusterReply *ZigbeeClusterOnOff::commandToggle() return executeClusterCommand(ZigbeeClusterOnOff::CommandToggle); } +ZigbeeClusterReply *ZigbeeClusterOnOff::commandOffWithEffect(ZigbeeClusterOnOff::Effect effect, quint8 effectVariant) +{ + QByteArray payload; + QDataStream stream(&payload, QIODevice::WriteOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream << static_cast(effect) << effectVariant; + return executeClusterCommand(ZigbeeClusterOnOff::CommandOffWithEffect, payload); +} + +ZigbeeClusterReply *ZigbeeClusterOnOff::commandOnWithRecallGlobalScene() +{ + return executeClusterCommand(ZigbeeClusterOnOff::CommandOnWithRecallGlobalScene); +} + +ZigbeeClusterReply *ZigbeeClusterOnOff::commandOnWithTimedOff(bool acceptOnlyWhenOn, quint16 onTime, quint16 offWaitTime) +{ + QByteArray payload; + QDataStream stream(&payload, QIODevice::WriteOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream << static_cast(acceptOnlyWhenOn) << onTime << offWaitTime; + return executeClusterCommand(ZigbeeClusterOnOff::CommandOnWithTimedOff, payload); +} + void ZigbeeClusterOnOff::setAttribute(const ZigbeeClusterAttribute &attribute) { qCDebug(dcZigbeeCluster()) << "Update attribute" << m_node << m_endpoint << this << static_cast(attribute.id()) << attribute.dataType(); diff --git a/libnymea-zigbee/zcl/general/zigbeeclusteronoff.h b/libnymea-zigbee/zcl/general/zigbeeclusteronoff.h index f9dc783..a3c6b09 100644 --- a/libnymea-zigbee/zcl/general/zigbeeclusteronoff.h +++ b/libnymea-zigbee/zcl/general/zigbeeclusteronoff.h @@ -64,14 +64,20 @@ public: }; Q_ENUM(Command) + enum Effect { + EffectDelayedAllOff = 0x00, + EffectDyingLight = 0x01 + }; + Q_ENUM(Effect) + explicit ZigbeeClusterOnOff(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent = nullptr); ZigbeeClusterReply *commandOff(); ZigbeeClusterReply *commandOn(); ZigbeeClusterReply *commandToggle(); -// ZigbeeClusterReply *commandOffWithEffect(); -// ZigbeeClusterReply *commandOnWithRecallGlobalScene(); -// ZigbeeClusterReply *commandOnWithTimedOff(); + ZigbeeClusterReply *commandOffWithEffect(Effect effect, quint8 effectVariant = 0x00); + ZigbeeClusterReply *commandOnWithRecallGlobalScene(); + ZigbeeClusterReply *commandOnWithTimedOff(bool acceptOnlyWhenOn, quint16 onTime, quint16 offWaitTime); private: void setAttribute(const ZigbeeClusterAttribute &attribute) override; diff --git a/libnymea-zigbee/zdo/zigbeedeviceobject.cpp b/libnymea-zigbee/zdo/zigbeedeviceobject.cpp index 5954a0e..b5b28b2 100644 --- a/libnymea-zigbee/zdo/zigbeedeviceobject.cpp +++ b/libnymea-zigbee/zdo/zigbeedeviceobject.cpp @@ -224,6 +224,58 @@ ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestSimpleDescriptor(quint8 endp return zdoReply; } +ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestBindIeeeAddress(quint8 sourceEndpointId, quint16 clusterId, const ZigbeeAddress &destinationIeeeAddress, quint8 destinationEndpointId) +{ + qCDebug(dcZigbeeDeviceObject()) << "Request bind ieee address from" << m_node << "endpoint" << clusterId << "to" << destinationIeeeAddress.toString() << destinationEndpointId; + + // Build APS request + ZigbeeNetworkRequest request = buildZdoRequest(ZigbeeDeviceProfile::BindRequest); + + // Generate a new transaction sequence number for this device object + quint8 transactionSequenceNumber = m_transactionSequenceNumber++; + + // Build ZDO frame + QByteArray asdu; + QDataStream stream(&asdu, QIODevice::WriteOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream << transactionSequenceNumber; + stream << m_node->extendedAddress().toUInt64(); + stream << sourceEndpointId; + stream << clusterId; + stream << static_cast(Zigbee::DestinationAddressModeIeeeAddress); + stream << destinationIeeeAddress.toUInt64(); + stream << destinationEndpointId; + + // Set the ZDO frame as APS request payload + request.setAsdu(asdu); + + // Create the device object reply and wait for the response indication + ZigbeeDeviceObjectReply *zdoReply = createZigbeeDeviceObjectReply(request, transactionSequenceNumber); + + // Send the request, on finished read the confirm information + ZigbeeNetworkReply *networkReply = m_network->sendRequest(request); + connect(networkReply, &ZigbeeNetworkReply::finished, this, [this, networkReply, zdoReply](){ + if (!verifyNetworkError(zdoReply, networkReply)) { + qCWarning(dcZigbeeDeviceObject()) << "Failed to send request" + << static_cast(networkReply->request().clusterId()) + << m_node << networkReply->error() + << networkReply->zigbeeApsStatus(); + finishZdoReply(zdoReply); + return; + } + + // The request was successfully sent to the device + // Now check if the expected indication response received already + if (zdoReply->isComplete()) { + finishZdoReply(zdoReply); + return; + } + // We received the confirmation but not yet the indication + }); + + return zdoReply; +} + ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestMgmtLeaveNetwork(bool rejoin, bool removeChildren) { qCDebug(dcZigbeeDeviceObject()) << "Request management leave network from" << m_node << "rejoin" << rejoin << "remove children" << removeChildren; diff --git a/libnymea-zigbee/zdo/zigbeedeviceobject.h b/libnymea-zigbee/zdo/zigbeedeviceobject.h index 9606542..ba938e0 100644 --- a/libnymea-zigbee/zdo/zigbeedeviceobject.h +++ b/libnymea-zigbee/zdo/zigbeedeviceobject.h @@ -52,7 +52,7 @@ public: // End device binding // ZigbeeDeviceObjectReply *requestBindGroup(quint16 clusterId, quint16 groupAddress, quint8 destinationEndpoint); // ZigbeeDeviceObjectReply *requestBindShortAddress(); -// ZigbeeDeviceObjectReply *requestBindIeeeAddress(); + ZigbeeDeviceObjectReply *requestBindIeeeAddress(quint8 sourceEndpointId, quint16 clusterId, const ZigbeeAddress &destinationIeeeAddress, quint8 destinationEndpointId); // Management request diff --git a/libnymea-zigbee/zigbeenetwork.cpp b/libnymea-zigbee/zigbeenetwork.cpp index 3025d89..8123be5 100644 --- a/libnymea-zigbee/zigbeenetwork.cpp +++ b/libnymea-zigbee/zigbeenetwork.cpp @@ -366,57 +366,6 @@ void ZigbeeNetwork::clearSettings() m_nodeType = ZigbeeDeviceProfile::NodeTypeCoordinator; } -void ZigbeeNetwork::saveNode(ZigbeeNode *node) -{ - QSettings settings(m_settingsFileName, QSettings::IniFormat, this); - settings.beginGroup("Nodes"); - - // Save this node - settings.beginGroup(node->extendedAddress().toString()); - settings.setValue("nwkAddress", node->shortAddress()); - - // Node descriptor - settings.setValue("nodeDescriptorRaw", node->nodeDescriptor().descriptorRawData); - - // Power descriptor - settings.setValue("powerDescriptorFlag", node->powerDescriptor().powerDescriptoFlag); - - settings.beginWriteArray("endpoints"); - for (int i = 0; i < node->endpoints().count(); i++) { - ZigbeeNodeEndpoint *endpoint = node->endpoints().at(i); - settings.setArrayIndex(i); - settings.setValue("id", endpoint->endpointId()); - 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++) { - ZigbeeCluster *cluster = endpoint->inputClusters().at(n); - settings.setArrayIndex(n); - settings.setValue("clusterId", cluster->clusterId()); - } - settings.endArray(); // inputClusters - - settings.beginWriteArray("outputClusters"); - for (int n = 0; n < endpoint->outputClusters().count(); n++) { - ZigbeeCluster *cluster = endpoint->outputClusters().at(n); - settings.setArrayIndex(n); - settings.setValue("clusterId", cluster->clusterId()); - } - settings.endArray(); // outputClusters - } - - settings.endArray(); // endpoints - - settings.endGroup(); // Node ieee address - - settings.endGroup(); // Nodes -} - void ZigbeeNetwork::addNode(ZigbeeNode *node) { qCDebug(dcZigbeeNetwork()) << "Add node" << node; @@ -483,6 +432,11 @@ bool ZigbeeNetwork::networkConfigurationAvailable() const return m_extendedPanId != 0 && m_channel != 0 && m_coordinatorNode; } +void ZigbeeNetwork::handleNodeIndication(ZigbeeNode *node, const Zigbee::ApsdeDataIndication indication) +{ + node->handleDataIndication(indication); +} + ZigbeeNetworkReply *ZigbeeNetwork::createNetworkReply(const ZigbeeNetworkRequest &request) { ZigbeeNetworkReply *reply = new ZigbeeNetworkReply(request, this); diff --git a/libnymea-zigbee/zigbeenetwork.h b/libnymea-zigbee/zigbeenetwork.h index 32b2e7f..db872e6 100644 --- a/libnymea-zigbee/zigbeenetwork.h +++ b/libnymea-zigbee/zigbeenetwork.h @@ -155,7 +155,6 @@ protected: void saveNetwork(); void loadNetwork(); void clearSettings(); - void saveNode(ZigbeeNode *node); void addNode(ZigbeeNode *node); void addUnitializedNode(ZigbeeNode *node); @@ -166,6 +165,8 @@ protected: bool networkConfigurationAvailable() const; + void handleNodeIndication(ZigbeeNode *node, const Zigbee::ApsdeDataIndication indication); + // Network reply methods ZigbeeNetworkReply *createNetworkReply(const ZigbeeNetworkRequest &request = ZigbeeNetworkRequest()); void setReplyResponseError(ZigbeeNetworkReply *reply, Zigbee::ZigbeeApsStatus zigbeeApsStatus = Zigbee::ZigbeeApsStatusSuccess); diff --git a/libnymea-zigbee/zigbeenode.cpp b/libnymea-zigbee/zigbeenode.cpp index 470f71d..4a77fe6 100644 --- a/libnymea-zigbee/zigbeenode.cpp +++ b/libnymea-zigbee/zigbeenode.cpp @@ -87,6 +87,11 @@ ZigbeeNodeEndpoint *ZigbeeNode::getEndpoint(quint8 endpointId) const return nullptr; } +quint8 ZigbeeNode::lqi() const +{ + return m_lqi; +} + ZigbeeDeviceProfile::NodeDescriptor ZigbeeNode::nodeDescriptor() const { return m_nodeDescriptor; @@ -536,6 +541,24 @@ void ZigbeeNode::readSoftwareBuildId(ZigbeeClusterBasic *basicCluster) }); } +void ZigbeeNode::handleDataIndication(const Zigbee::ApsdeDataIndication &indication) +{ + if (indication.lqi != m_lqi) { + m_lqi = indication.lqi; + emit lqiChanged(m_lqi); + } + + + // Check if this indocation is related to any pending reply + if (indication.profileId == Zigbee::ZigbeeProfileDevice) { + deviceObject()->processApsDataIndication(indication); + return; + } + + // Else let the node handle this indication + handleZigbeeClusterLibraryIndication(indication); +} + void ZigbeeNode::handleZigbeeClusterLibraryIndication(const Zigbee::ApsdeDataIndication &indication) { qCDebug(dcZigbeeNode()) << "Processing ZCL indication" << indication; diff --git a/libnymea-zigbee/zigbeenode.h b/libnymea-zigbee/zigbeenode.h index cd9148c..3c8e7b6 100644 --- a/libnymea-zigbee/zigbeenode.h +++ b/libnymea-zigbee/zigbeenode.h @@ -65,6 +65,8 @@ public: bool hasEndpoint(quint8 endpointId) const; ZigbeeNodeEndpoint *getEndpoint(quint8 endpointId) const; + quint8 lqi() const; + // Information from descriptors ZigbeeDeviceProfile::NodeDescriptor nodeDescriptor() const; ZigbeeDeviceProfile::MacCapabilities macCapabilities() const; @@ -114,8 +116,7 @@ private: void readModelIdentifier(ZigbeeClusterBasic *basicCluster); void readSoftwareBuildId(ZigbeeClusterBasic *basicCluster); - - + void handleDataIndication(const Zigbee::ApsdeDataIndication &indication); signals: void nodeInitializationFailed();