From 92cdc4bc8477b8421b3e1e0d470b6c3e640e4d43 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Mon, 20 Jun 2022 23:25:23 +0200 Subject: [PATCH] Implement commands for metering cluster Main reasoning behind this is actually that Tuya devices seem to send a CancelMessage command every other minute and are currently spamming the log with unhandled ZCL indication warnings. Instead of just silencing the warning (which is very useful most of the times) I decided to complete the implementation of the metering cluster and actually make it a handled ZCL indication. --- .../zcl/smartenergy/zigbeeclustermetering.cpp | 50 +++++++++++++++++++ .../zcl/smartenergy/zigbeeclustermetering.h | 34 +++++++++++++ 2 files changed, 84 insertions(+) diff --git a/libnymea-zigbee/zcl/smartenergy/zigbeeclustermetering.cpp b/libnymea-zigbee/zcl/smartenergy/zigbeeclustermetering.cpp index cc5353c..65f4302 100644 --- a/libnymea-zigbee/zcl/smartenergy/zigbeeclustermetering.cpp +++ b/libnymea-zigbee/zcl/smartenergy/zigbeeclustermetering.cpp @@ -1,5 +1,6 @@ #include "zigbeeclustermetering.h" +#include #include Q_DECLARE_LOGGING_CATEGORY(dcZigbeeCluster) @@ -64,3 +65,52 @@ void ZigbeeClusterMetering::setAttribute(const ZigbeeClusterAttribute &attribute qCWarning(dcZigbeeCluster()) << "Unhandled attribute change:" << attribute; } } + +void ZigbeeClusterMetering::processDataIndication(ZigbeeClusterLibrary::Frame frame) +{ + switch (m_direction) { + case Client: + qCWarning(dcZigbeeCluster()) << "Metering: Unhandled ZCL indication in" << m_node << m_endpoint << this << frame; + break; + case Server: { + ServerCommand command = static_cast(frame.header.command); + switch (command) { + case CommandDisplayMessage: { + QDataStream stream(frame.payload); + stream.setByteOrder(QDataStream::LittleEndian); + quint32 messageId, time; + quint16 durationInMinutes; + quint8 messageControl; + stream >> messageId >> messageControl >> time >> durationInMinutes; + + char messageData[2048]; // At max 2KB, realistically we'll only ever see < 80B in here as larger messages require both ends to negotiate on a larger PDU, however, if the backend supports it, it *may* happen. + int messageLength = stream.readRawData(messageData, 2048); + QByteArray message(messageData, messageLength); + MessageTransmission messageTransmission = static_cast((messageControl & 0xC0) >> 6); + MessagePriority priority = static_cast((messageControl & 0x30) >> 4); + bool confirmationRequired = (messageControl & 0x01) == 1; + + qCWarning(dcZigbeeCluster()) << "Display message received!" << messageId << messageControl << time << durationInMinutes << message; + emit showMessage(messageId, message, time, durationInMinutes, messageTransmission, priority, confirmationRequired); + break; + } + case ClientCommandCancelMessage: { + QDataStream stream(frame.payload); + stream.setByteOrder(QDataStream::LittleEndian); + quint32 messageId; + quint8 messageControl; + stream >> messageId >> messageControl; + MessageTransmission messageTransmission = static_cast((messageControl & 0xC0) >> 6); + MessagePriority priority = static_cast((messageControl & 0x30) >> 4); + bool confirmationRequired = (messageControl & 0x01) == 1; + qCDebug(dcZigbeeCluster()) << "Metering: Cancel message command received" << messageId << messageControl; + emit cancelMessage(messageId, messageTransmission, priority, confirmationRequired); + break; + } + default: + qCDebug(dcZigbeeCluster()) << "Ignoring out of spec metering cluster ZCL indication:" << m_node << m_endpoint << this << frame; + } + break; + } + } +} diff --git a/libnymea-zigbee/zcl/smartenergy/zigbeeclustermetering.h b/libnymea-zigbee/zcl/smartenergy/zigbeeclustermetering.h index e8c1d0c..524c8db 100644 --- a/libnymea-zigbee/zcl/smartenergy/zigbeeclustermetering.h +++ b/libnymea-zigbee/zcl/smartenergy/zigbeeclustermetering.h @@ -555,6 +555,36 @@ public: }; Q_ENUM(AlarmCode) + enum ClientCommand { + CommandGetProfile = 0x00, + CommandRequestMirrorResponse = 0x01, + CommandMirrorRemoved = 0x02, + CommandRequestFastPollMode = 0x03 + }; + Q_ENUM(ClientCommand) + + enum ServerCommand { + CommandDisplayMessage = 0x00, + ClientCommandCancelMessage = 0x01 + }; + Q_ENUM(ServerCommand) + + enum MessageTransmission { + MessageTransmissionNormal = 0x0, + MessageTransmissionNormalAndAnonymousInterPan = 0x1, + MessageTransmissionAnonymousInterPan = 0x2 + }; + Q_ENUM(MessageTransmission) + + enum MessagePriority { + MessagePriorityLow = 0x0, + MessagePriorityMedium = 0x1, + MessagePriorityHigh = 0x2, + MessagePriorityCritical = 0x3 + }; + Q_ENUM(MessagePriority) + + explicit ZigbeeClusterMetering(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent = nullptr); // Used to refresh formatting attributes (multiplier/divisor) @@ -570,8 +600,12 @@ signals: void currentSummationDeliveredChanged(quint64 currentSummationDelivered); void instantaneousDemandChanged(qint32 instantaneousDemand); + void showMessage(quint32 messageId, const QString &message, quint32 time, quint16 durationInMinutes, MessageTransmission transmission, MessagePriority priority, bool confirmationRequired); + void cancelMessage(quint32 messageId, MessageTransmission transmission, MessagePriority priority, bool confirmationRequired); + private: void setAttribute(const ZigbeeClusterAttribute &attribute) override; + void processDataIndication(ZigbeeClusterLibrary::Frame frame) override; quint32 m_multiplier = 1; quint32 m_divisor = 1;