More work on OnOff, LevelControl and ScenesCluster

This alignes the OnOff cluster with the LevelControl cluster in terms
of signal behavior: Previously, the OnOff cluster would fire a generic
commandSent() signal for some commands and specific signals for others.
Effectively forcing the user to connect multiple signals even if only
the command would be of interest.

The LevelControl cluster instead always fired a generic commandSent()
signal and *additionally* more specific signals for parsed parameters.

This changes the OnOff cluster to be in line with the LevelCluster
as and making the API a bit simpler to use when parameters are not of
interest.

Also it completes the specific parsing for all 3 clusters.
This commit is contained in:
Michael Zanetti 2021-11-08 00:58:55 +01:00
parent c5d9b119af
commit 6afc214202
6 changed files with 52 additions and 37 deletions

View File

@ -29,6 +29,7 @@
#include "zigbeenetworkreply.h" #include "zigbeenetworkreply.h"
#include "loggingcategory.h" #include "loggingcategory.h"
#include "zigbeenetwork.h" #include "zigbeenetwork.h"
#include "zigbeeutils.h"
#include <QDataStream> #include <QDataStream>
@ -56,12 +57,12 @@ ZigbeeClusterReply *ZigbeeClusterLevelControl::commandMove(ZigbeeClusterLevelCon
return executeClusterCommand(ZigbeeClusterLevelControl::CommandMove, payload); return executeClusterCommand(ZigbeeClusterLevelControl::CommandMove, payload);
} }
ZigbeeClusterReply *ZigbeeClusterLevelControl::commandStep(ZigbeeClusterLevelControl::FadeMode fadeMode, quint8 stepSize, quint16 transitionTime) ZigbeeClusterReply *ZigbeeClusterLevelControl::commandStep(StepMode stepMode, quint8 stepSize, quint16 transitionTime)
{ {
QByteArray payload; QByteArray payload;
QDataStream stream(&payload, QIODevice::WriteOnly); QDataStream stream(&payload, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian); stream.setByteOrder(QDataStream::LittleEndian);
stream << static_cast<quint8>(fadeMode) << stepSize << transitionTime; stream << static_cast<quint8>(stepMode) << stepSize << transitionTime;
return executeClusterCommand(ZigbeeClusterLevelControl::CommandStep, payload); return executeClusterCommand(ZigbeeClusterLevelControl::CommandStep, payload);
} }
@ -88,12 +89,12 @@ ZigbeeClusterReply *ZigbeeClusterLevelControl::commandMoveWithOnOff(ZigbeeCluste
return executeClusterCommand(ZigbeeClusterLevelControl::CommandMoveWithOnOff, payload); return executeClusterCommand(ZigbeeClusterLevelControl::CommandMoveWithOnOff, payload);
} }
ZigbeeClusterReply *ZigbeeClusterLevelControl::commandStepWithOnOff(ZigbeeClusterLevelControl::FadeMode fadeMode, quint8 stepSize, quint16 transitionTime) ZigbeeClusterReply *ZigbeeClusterLevelControl::commandStepWithOnOff(StepMode stepMode, quint8 stepSize, quint16 transitionTime)
{ {
QByteArray payload; QByteArray payload;
QDataStream stream(&payload, QIODevice::WriteOnly); QDataStream stream(&payload, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian); stream.setByteOrder(QDataStream::LittleEndian);
stream << static_cast<quint8>(fadeMode) << stepSize << transitionTime; stream << static_cast<quint8>(stepMode) << stepSize << transitionTime;
return executeClusterCommand(ZigbeeClusterLevelControl::CommandStepWithOnOff, payload); return executeClusterCommand(ZigbeeClusterLevelControl::CommandStepWithOnOff, payload);
} }
@ -128,8 +129,6 @@ void ZigbeeClusterLevelControl::setAttribute(const ZigbeeClusterAttribute &attri
void ZigbeeClusterLevelControl::processDataIndication(ZigbeeClusterLibrary::Frame frame) void ZigbeeClusterLevelControl::processDataIndication(ZigbeeClusterLibrary::Frame frame)
{ {
qCDebug(dcZigbeeCluster()) << "Processing cluster frame" << m_node << m_endpoint << this << frame;
// Increase the tsn for continuous id increasing on both sides // Increase the tsn for continuous id increasing on both sides
m_transactionSequenceNumber = frame.header.transactionSequenceNumber; m_transactionSequenceNumber = frame.header.transactionSequenceNumber;
@ -139,30 +138,48 @@ void ZigbeeClusterLevelControl::processDataIndication(ZigbeeClusterLibrary::Fram
if (frame.header.frameControl.direction == ZigbeeClusterLibrary::DirectionClientToServer) { if (frame.header.frameControl.direction == ZigbeeClusterLibrary::DirectionClientToServer) {
// Read the payload which is // Read the payload which is
Command command = static_cast<Command>(frame.header.command); Command command = static_cast<Command>(frame.header.command);
qCDebug(dcZigbeeCluster()) << "Command sent from" << m_node << m_endpoint << this << command;
emit commandSent(command, frame.payload); emit commandSent(command, frame.payload);
bool withOnOff = false;
switch (command) { switch (command) {
case CommandMoveToLevelWithOnOff:
case CommandMoveToLevel: {
QByteArray payload = frame.payload;
QDataStream payloadStream(&payload, QIODevice::ReadOnly);
payloadStream.setByteOrder(QDataStream::LittleEndian);
quint8 level; quint16 transitionTime;
payloadStream >> level >> transitionTime;
withOnOff = command == CommandMoveToLevelWithOnOff;
qCDebug(dcZigbeeCluster()).noquote().nospace() << "Command received from " << m_node << " " << m_endpoint << " " << this << " " << command << " withOnOff: " << withOnOff << " level: 0x" << QString::number(level, 16) << " transitionTime: 0x" << QString::number(transitionTime, 16);
emit commandMoveToLevelSent(withOnOff, level, transitionTime);
break;
}
case CommandStepWithOnOff:
case CommandStep: { case CommandStep: {
QByteArray payload = frame.payload; QByteArray payload = frame.payload;
QDataStream payloadStream(&payload, QIODevice::ReadOnly); QDataStream payloadStream(&payload, QIODevice::ReadOnly);
payloadStream.setByteOrder(QDataStream::LittleEndian); payloadStream.setByteOrder(QDataStream::LittleEndian);
quint8 fadeModeValue = 0; quint8 stepSize; quint16 transitionTime; quint8 stepModeValue = 0; quint8 stepSize; quint16 transitionTime;
payloadStream >> fadeModeValue >> stepSize >> transitionTime; payloadStream >> stepModeValue >> stepSize >> transitionTime;
emit commandStepSent(static_cast<FadeMode>(fadeModeValue), stepSize, transitionTime); withOnOff = command == CommandMoveToLevelWithOnOff;
qCDebug(dcZigbeeCluster()).noquote().nospace() << "Command received from " << m_node << " " << m_endpoint << " " << this << " " << command << " withOnOff: " << withOnOff << " stepModeValue: 0x" << QString::number(stepModeValue, 16) << " stepSize: 0x" << QString::number(stepSize, 16) << " transitionTime: 0x" << QString::number(transitionTime, 16);
emit commandStepSent(withOnOff, static_cast<StepMode>(stepModeValue), stepSize, transitionTime);
break; break;
} }
case CommandMoveWithOnOff:
case CommandMove: { case CommandMove: {
QByteArray payload = frame.payload; QByteArray payload = frame.payload;
QDataStream payloadStream(&payload, QIODevice::ReadOnly); QDataStream payloadStream(&payload, QIODevice::ReadOnly);
payloadStream.setByteOrder(QDataStream::LittleEndian); payloadStream.setByteOrder(QDataStream::LittleEndian);
quint8 moveModeValue = 0; quint8 rate;; quint8 moveModeValue = 0; quint8 rate;;
payloadStream >> moveModeValue >> rate; payloadStream >> moveModeValue >> rate;
emit commandMoveSent(static_cast<MoveMode>(moveModeValue), rate); withOnOff = command == CommandMoveToLevelWithOnOff;
qCDebug(dcZigbeeCluster()).noquote().nospace() << "Command received from " << m_node << " " << m_endpoint << " " << this << " " << command << " withOnOff:" << withOnOff << " moveModeValue: 0x" << QString::number(moveModeValue, 16) << " rate: 0x" << QString::number(rate, 16);
emit commandMoveSent(withOnOff, static_cast<MoveMode>(moveModeValue), rate);
break; break;
} }
default: default:
qCDebug(dcZigbeeCluster()) << "Command received without special implementation"; qCDebug(dcZigbeeCluster()).noquote().nospace() << "Command received from " << m_node << " " << m_endpoint << " " << this << " " << command << " payload: 0x" << ZigbeeUtils::convertByteArrayToHexString(frame.payload);
break; break;
} }

View File

@ -75,23 +75,23 @@ public:
}; };
Q_ENUM(MoveMode) Q_ENUM(MoveMode)
enum FadeMode { enum StepMode {
FadeModeUp = 0x00, StepModeUp = 0x00,
FadeModeDown = 0x01 StepModeDown = 0x01
}; };
Q_ENUM(FadeMode) Q_ENUM(StepMode)
explicit ZigbeeClusterLevelControl(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent = nullptr); explicit ZigbeeClusterLevelControl(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent = nullptr);
ZigbeeClusterReply *commandMoveToLevel(quint8 level, quint16 transitionTime = 0xffff); ZigbeeClusterReply *commandMoveToLevel(quint8 level, quint16 transitionTime = 0xffff);
ZigbeeClusterReply *commandMove(MoveMode moveMode, quint8 rate = 0xff); ZigbeeClusterReply *commandMove(MoveMode moveMode, quint8 rate = 0xff);
ZigbeeClusterReply *commandStep(FadeMode fadeMode, quint8 stepSize = 0x01, quint16 transitionTime = 0xffff); ZigbeeClusterReply *commandStep(StepMode stepMode, quint8 stepSize = 0x01, quint16 transitionTime = 0xffff);
ZigbeeClusterReply *commandStop(); ZigbeeClusterReply *commandStop();
// With on/off // With on/off
ZigbeeClusterReply *commandMoveToLevelWithOnOff(quint8 level, quint16 transitionTime = 0xffff); ZigbeeClusterReply *commandMoveToLevelWithOnOff(quint8 level, quint16 transitionTime = 0xffff);
ZigbeeClusterReply *commandMoveWithOnOff(MoveMode moveMode, quint8 rate = 0xff); ZigbeeClusterReply *commandMoveWithOnOff(MoveMode moveMode, quint8 rate = 0xff);
ZigbeeClusterReply *commandStepWithOnOff(FadeMode fadeMode, quint8 stepSize = 0x01, quint16 transitionTime = 0xffff); ZigbeeClusterReply *commandStepWithOnOff(StepMode stepMode, quint8 stepSize = 0x01, quint16 transitionTime = 0xffff);
ZigbeeClusterReply *commandStopWithOnOff(); ZigbeeClusterReply *commandStopWithOnOff();
quint8 currentLevel() const; quint8 currentLevel() const;
@ -107,8 +107,10 @@ protected:
signals: signals:
void currentLevelChanged(quint8 level); void currentLevelChanged(quint8 level);
void commandSent(ZigbeeClusterLevelControl::Command command, const QByteArray &parameter = QByteArray()); void commandSent(ZigbeeClusterLevelControl::Command command, const QByteArray &parameter = QByteArray());
void commandMoveSent(MoveMode moveMode, quint8 rate = 0xff); void commandMoveToLevelSent(bool withOnOff, quint8 level, quint16 transitionTime);
void commandStepSent(FadeMode fadeMode, quint8 stepSize, quint16 transitionTime); void commandMoveSent(bool withOnOff, MoveMode moveMode, quint8 rate = 0xff);
void commandStepSent(bool withOnOff, StepMode stepMode, quint8 stepSize, quint16 transitionTime);
void commandStopSent();
}; };

View File

@ -114,23 +114,15 @@ void ZigbeeClusterOnOff::processDataIndication(ZigbeeClusterLibrary::Frame frame
if (frame.header.frameControl.direction == ZigbeeClusterLibrary::DirectionClientToServer) { if (frame.header.frameControl.direction == ZigbeeClusterLibrary::DirectionClientToServer) {
// Read the payload which is // Read the payload which is
Command command = static_cast<Command>(frame.header.command); Command command = static_cast<Command>(frame.header.command);
qCDebug(dcZigbeeCluster()) << "Received" << command << "from" << m_node << m_endpoint << this; emit commandSent(command, frame.payload);
switch (command) { switch (command) {
case CommandOn:
emit commandSent(CommandOn);
break;
case CommandOff:
emit commandSent(CommandOff);
break;
case CommandToggle:
emit commandSent(CommandToggle);
break;
case CommandOffWithEffect: { case CommandOffWithEffect: {
QByteArray payload = frame.payload; QByteArray payload = frame.payload;
QDataStream payloadStream(&payload, QIODevice::ReadOnly); QDataStream payloadStream(&payload, QIODevice::ReadOnly);
payloadStream.setByteOrder(QDataStream::LittleEndian); payloadStream.setByteOrder(QDataStream::LittleEndian);
quint8 effectValue = 0; quint16 effectVariant; quint8 effectValue = 0; quint16 effectVariant;
payloadStream >> effectValue >> effectVariant; payloadStream >> effectValue >> effectVariant;
qCDebug(dcZigbeeCluster()) << "Command received from" << m_node << m_endpoint << this << command << "effect:" << effectValue << "effectVariant:" << effectVariant;
emit commandOffWithEffectSent(static_cast<Effect>(effectValue), effectVariant); emit commandOffWithEffectSent(static_cast<Effect>(effectValue), effectVariant);
break; break;
} }
@ -140,11 +132,12 @@ void ZigbeeClusterOnOff::processDataIndication(ZigbeeClusterLibrary::Frame frame
payloadStream.setByteOrder(QDataStream::LittleEndian); payloadStream.setByteOrder(QDataStream::LittleEndian);
quint8 acceptOnlyWhenOnInt = 0; quint16 onTime; quint16 offTime; quint8 acceptOnlyWhenOnInt = 0; quint16 onTime; quint16 offTime;
payloadStream >> acceptOnlyWhenOnInt >> onTime >> offTime; payloadStream >> acceptOnlyWhenOnInt >> onTime >> offTime;
qCDebug(dcZigbeeCluster()) << "Command received from" << m_node << m_endpoint << this << command << "accentOnlyWhenOnInt:" << acceptOnlyWhenOnInt << "onTime:" << onTime << "offTime:" << offTime;
emit commandOnWithTimedOffSent(static_cast<bool>(acceptOnlyWhenOnInt), onTime, offTime); emit commandOnWithTimedOffSent(static_cast<bool>(acceptOnlyWhenOnInt), onTime, offTime);
break; break;
} }
default: default:
qCWarning(dcZigbeeCluster()) << "Unhandled command sent from" << m_node << m_endpoint << this << command << ZigbeeUtils::convertByteArrayToHexString(frame.payload); qCDebug(dcZigbeeCluster()) << "Command received from" << m_node << m_endpoint << this << command << ZigbeeUtils::convertByteArrayToHexString(frame.payload);
break; break;
} }
} }

View File

@ -94,7 +94,7 @@ signals:
void powerChanged(bool power); void powerChanged(bool power);
// Client cluster signals // Client cluster signals
void commandSent(Command command); void commandSent(Command command, const QByteArray &parameters = QByteArray());
// On and off time is in 1/10 seconds // On and off time is in 1/10 seconds
void commandOnWithTimedOffSent(bool acceptOnlyWhenOn, quint16 onTime, quint16 offTime); void commandOnWithTimedOffSent(bool acceptOnlyWhenOn, quint16 onTime, quint16 offTime);

View File

@ -45,8 +45,6 @@ void ZigbeeClusterScenes::setAttribute(const ZigbeeClusterAttribute &attribute)
void ZigbeeClusterScenes::processDataIndication(ZigbeeClusterLibrary::Frame frame) void ZigbeeClusterScenes::processDataIndication(ZigbeeClusterLibrary::Frame frame)
{ {
qCDebug(dcZigbeeCluster()) << "Processing cluster frame" << m_node << m_endpoint << this << frame;
// Increase the tsn for continuous id increasing on both sides // Increase the tsn for continuous id increasing on both sides
m_transactionSequenceNumber = frame.header.transactionSequenceNumber; m_transactionSequenceNumber = frame.header.transactionSequenceNumber;
@ -54,8 +52,13 @@ void ZigbeeClusterScenes::processDataIndication(ZigbeeClusterLibrary::Frame fram
case Client: { case Client: {
// Read the payload which is // Read the payload which is
Command command = static_cast<Command>(frame.header.command); Command command = static_cast<Command>(frame.header.command);
qCDebug(dcZigbeeCluster()) << "Received" << command << "from" << m_node << m_endpoint << this << ZigbeeUtils::convertByteArrayToHexString(frame.payload); QByteArray payload = frame.payload;
emit commandSent(frame.header.command, frame.payload); QDataStream payloadStream(&payload, QIODevice::ReadOnly);
payloadStream.setByteOrder(QDataStream::LittleEndian);
quint16 groupId = 0; quint8 sceneId;
payloadStream >> groupId >> sceneId;
qCDebug(dcZigbeeCluster()).noquote() << "Received" << command << "for group" << "0x" + QString::number(groupId, 16) << "and scene" << sceneId << "from" << m_node << m_endpoint << this;
emit commandSent(command, groupId, sceneId);
break; break;
} }
case Server: case Server:

View File

@ -63,7 +63,7 @@ public:
explicit ZigbeeClusterScenes(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent = nullptr); explicit ZigbeeClusterScenes(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent = nullptr);
signals: signals:
void commandSent(quint8 command, const QByteArray &payload); void commandSent(Command command, quint16 groupId, quint8 sceneId);
private: private:
void setAttribute(const ZigbeeClusterAttribute &attribute) override; void setAttribute(const ZigbeeClusterAttribute &attribute) override;