* window_covering.cpp
*
*
*
*
* ZigBee Home Automation Public Application Profile Document 05-3520-29
* Chapter 9.3 Window Covering Cluster
*
* Cluster Id 0x0102 Window Covering Cluster
* Attributes
* 0x0000 enum8, WindowCoveringType
* 0x0003 unsinged16, CurrentPositionLift
* 0x0004 unsinged16, CurrentPositionTilt
* 0x0008 unsinged8, CurrentPositionLiftPercentage
* 0x0009 unsinged8, CurrentPositionTiltPercentage
* 0x000A bitmap8, OperationalStatus (This attribute contains two bits which will be set while the motor is active)
* 0x0011 unsinged16, InstalledClosedLimitLift (Specifies a bound for the bottom position (lift height), in centimeters)
* 0x0013 unsinged16, InstalledClosedLimitTilt (Specifies a bound for the closed position (tilt angle), in units of 0.1°)
* 0x0017 bitmap8, Mode (bit0=if the motor direction is reversed, bit1=the device is in calibration, bit2=maintenance mode)
*
* Commands
* 0x00 Move up/open, Move upwards, towards the fully open position.
* 0x01 Move down/close, Move downwards, towards the fully closed position.
* 0x02 Stop, Stop all motion.
* 0x04 Go to Lift Value, Moves to the specified lift value. Unsigned 16-bit integer.
* 0x05 Go to Lift Percentage, Moves to the specified lift percentage. Unsigned 8-bit integer.
* 0x07 Go to Tilt Value, Move to the specified tilt value. Unsigned 16-bit integer.
* 0x08 Go to Tilt Percentage, Move to the specified tilt percentage. Unsigned 8-bit integer.
*
*
* Ubisys Shutter Control J1
*
* http://www.ubisys.de/downloads/ubisys-j1-technical-reference.pdf
* page 18, chapter 7.2.5.1. Calibration
*
* Step 1
* In order to calibrate the device, first choose the appropriate device type.
* 0 = Roller Shade Lift only, ..., 6 = Shutter Tilt only, ..., 8 Tilt Blind Lift & Tilt
* Write attribute 0x10F2:0x0000 (“WindowCoveringType”) accordingly.
* Step 2
* Prepare calibration by setting these values:
* Write attribute 0x10F2:0x0010 (“InstalledOpenLimitLift”) as 0x0000 = 0cm.
* Write attribute 0x10F2:0x0011 (“InstalledClosedLimitLift”) as 0x00F0 = 240cm.
* Write attribute 0x10F2:0x0012 (“InstalledOpenLimitTilt”) as 0x0000 = 0°.
* Write attribute 0x10F2:0x0013 (“InstalledClosedLimitTilt”) as 0x0384 = 90.0°.
* Write attribute 0x10F2:0x1001 (“LiftToTiltTransitionSteps”) as 0xFFFF = invalid.
* Write attribute 0x10F2:0x1002 (“TotalSteps”) as 0xFFFF = invalid.
* Write attribute 0x10F2:0x1003 (“LiftToTiltTransitionSteps2”) as 0xFFFF = invalid.
* Write attribute 0x10F2:0x1004 (“TotalSteps2”) as 0xFFFF = invalid
* Step 3
* Enter calibration mode:
* Write attribute 0x0017 (“Mode”) as 0x02.
* Step 4
* Send the "move down" command and "stop" after a few centimeters.
* Step 5
* Send the “move up” command. When the device reaches its top position,
* J1 will recognize the upper bound.
* Step 6
* After J1 has reached the top position and the motor has stopped, send the “move down” command.
* Step 7
* After J1 has reached the lower bound and the motor has stopped, send the “move up” command.
* J1 will search for the upper bound. Once the top position is reached,
* calibration of the total steps in both directions is complete.
* Step 8
* In case of a tilt blind set attribute 0x10F2:0x1001 and 0x10F2:0x1003 to the time it takes for a lift-to tilt
* transition (down) or a tilt-to-lift transition (up), respectively. Otherwise proceed with the next step.
* Step 9
* To leave calibration mode, clear bit #1 in the Mode attribute, e.g. write attribute 0x0017 as 0x00.
*/
#include "de_web_plugin_private.h"
#include "product_match.h"
int calibrationStep = 0;
int operationalStatus = 0;
\param ind the APS level data indication containing the ZCL packet
\param zclFrame the actual ZCL frame which holds the Window Covering command or attribute
*/
void DeRestPluginPrivate::handleWindowCoveringClusterIndication(const deCONZ::ApsDataIndication &ind, deCONZ::ZclFrame &zclFrame)
{
Q_UNUSED(ind);
LightNode *lightNode = getLightNodeForAddress(ind.srcAddress(), ind.srcEndpoint());
if (!lightNode)
{
return;
}
deCONZ::NumericUnion numericValue{};
quint16 attrid = 0x0000;
quint8 attrTypeId = 0x00;
quint8 attrValue = 0x00;
quint8 status = 0x00;
QDataStream stream(zclFrame.payload());
stream.setByteOrder(QDataStream::LittleEndian);
NodeValue::UpdateType updateType = NodeValue::UpdateInvalid;
if (zclFrame.isProfileWideCommand() && zclFrame.commandId() == deCONZ::ZclReadAttributesResponseId)
{
updateType = NodeValue::UpdateByZclRead;
}
else if (zclFrame.isProfileWideCommand() && zclFrame.commandId() == deCONZ::ZclReportAttributesId)
{
updateType = NodeValue::UpdateByZclReport;
}
const QString modelId = lightNode->modelId();
if (updateType != NodeValue::UpdateInvalid)
{
while (!stream.atEnd())
{
stream >> attrid;
if (updateType == NodeValue::UpdateByZclRead)
{
stream >> status;
if (status != 0)
{
return;
}
}
stream >> attrTypeId;
switch (attrTypeId)
{
case deCONZ::Zcl8BitData:
case deCONZ::ZclBoolean:
case deCONZ::Zcl8BitBitMap:
case deCONZ::Zcl8BitUint:
case deCONZ::Zcl8BitInt:
case deCONZ::Zcl8BitEnum:
stream >> attrValue;
break;
case deCONZ::Zcl16BitData:
case deCONZ::Zcl16BitBitMap:
case deCONZ::Zcl16BitUint:
case deCONZ::Zcl16BitInt:
case deCONZ::Zcl16BitEnum:
quint16 attrVal16;
stream >> attrVal16;
break;
default:
return;
}
if (attrid == 0x0008)
{
numericValue.u8 = attrValue;
lightNode->setZclValue(updateType, ind.srcEndpoint(), WINDOW_COVERING_CLUSTER_ID, attrid, numericValue);
quint8 lift = attrValue;
if (modelId.startsWith(QLatin1String("lumi.curtain")) ||
modelId == QLatin1String("D10110") ||
modelId == QLatin1String("Motor Controller"))
{
lift = 100 - lift;
}
else if (modelId == QLatin1String("Shutter SW with level control") ||
modelId == QLatin1String("Shutter switch with neutral"))
{
bool bStatus = false;
uint nHex = lightNode->swBuildId().toUInt(&bStatus,16);
if (bStatus && (nHex < 28))
{
lift = 100 - lift;
}
}
else if (R_GetProductId(lightNode) == QLatin1String("11830304 Switch") ||
R_GetProductId(lightNode) == QLatin1String("Zigbee curtain switch") ||
R_GetProductId(lightNode) == QLatin1String("Zigbee dual curtain switch") ||
R_GetProductId(lightNode) == QLatin1String("Covering Switch ESW-2ZAD-EU") ||
R_GetProductId(lightNode) == QLatin1String("QS-Zigbee-C01 Module"))
{
lift = 100 - lift;
}
bool open = lift < 100;
if (lightNode->setValue(RStateLift, lift))
{
pushZclValueDb(lightNode->address().ext(), lightNode->haEndpoint().endpoint(), WINDOW_COVERING_CLUSTER_ID, attrid, attrValue);
}
lightNode->setValue(RStateOpen, open);
quint8 level = lift * 254 / 100;
bool on = level > 0;
lightNode->setValue(RStateBri, level);
lightNode->setValue(RStateOn, on);
}
else if (attrid == 0x0009)
{
numericValue.u8 = attrValue;
lightNode->setZclValue(updateType, ind.srcEndpoint(), WINDOW_COVERING_CLUSTER_ID, attrid, numericValue);
quint8 tilt = attrValue;
if (lightNode->setValue(RStateTilt, tilt))
{
pushZclValueDb(lightNode->address().ext(), lightNode->haEndpoint().endpoint(), WINDOW_COVERING_CLUSTER_ID, attrid, attrValue);
}
quint8 sat = attrValue * 254 / 100;
lightNode->setValue(RStateSat, sat);
}
else if (attrid == 0x000A)
{
if (calibrationStep != 0 && ind.srcAddress().ext() == calibrationTask.req.dstAddress().ext())
{
operationalStatus = attrValue;
}
}
else if (attrid == 0x0000)
{
Sensor *sensor = getSensorNodeForAddressAndEndpoint(ind.srcAddress(), 0x02);
if (sensor)
{
ResourceItem *item = sensor->item(RConfigWindowCoveringType);
if (item)
{
item->setValue(attrValue);
sensor->setNeedSaveDatabase(true);
queSaveDb(DB_SENSORS, DB_SHORT_SAVE_DELAY);
}
}
}
}
}
}
\param task - the task item
\param cmdId - moveUp/Down/stop/moveTo/moveToPct
\param pos - position centimeter
\param pct - position percent
\return true - on success
false - on error
*/
bool DeRestPluginPrivate::addTaskWindowCovering(TaskItem &task, uint8_t cmd, uint16_t pos, uint8_t pct)
{
task.taskType = TaskWindowCovering;
task.req.setClusterId(WINDOW_COVERING_CLUSTER_ID);
task.req.setProfileId(HA_PROFILE_ID);
task.zclFrame.payload().clear();
task.zclFrame.setSequenceNumber(zclSeq++);
task.zclFrame.setCommandId(cmd);
task.zclFrame.setFrameControl(deCONZ::ZclFCClusterCommand |
deCONZ::ZclFCDirectionClientToServer |
deCONZ::ZclFCDisableDefaultResponse);
if (cmd == 0x04 || cmd == 0x05 || cmd == 0x07 || cmd == 0x08)
{
QDataStream stream(&task.zclFrame.payload(), QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
if (cmd == 0x04 || cmd == 0x07)
{
stream << pos;
}
if (cmd == 0x05 || cmd == 0x08)
{
stream << pct;
}
}
{
task.req.asdu().clear();
QDataStream stream(&task.req.asdu(), QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
task.zclFrame.writeToStream(stream);
}
return addTask(task);
}
bool DeRestPluginPrivate::addTaskWindowCoveringSetAttr(TaskItem &task, uint16_t mfrCode, uint16_t attrId, uint8_t attrType, uint16_t attrValue)
{
DBG_Printf(DBG_INFO, "addTaskWindowCoveringSetAttr: mfrCode = 0x%04x, attrId = 0x%04x, attrType = 0x%02x, attrValue = 0x%04x\n", mfrCode, attrId, attrType, attrValue);
task.taskType = TaskWindowCovering;
task.req.setDstEndpoint(0x01);
task.req.setClusterId(WINDOW_COVERING_CLUSTER_ID);
task.req.setProfileId(HA_PROFILE_ID);
task.zclFrame.payload().clear();
task.zclFrame.setSequenceNumber(zclSeq++);
task.zclFrame.setCommandId(deCONZ::ZclWriteAttributesId);
task.zclFrame.setFrameControl(deCONZ::ZclFCProfileCommand |
deCONZ::ZclFCDirectionClientToServer |
deCONZ::ZclFCDisableDefaultResponse);
if (mfrCode != 0x0000)
{
task.zclFrame.setFrameControl(task.zclFrame.frameControl() | deCONZ::ZclFCManufacturerSpecific);
task.zclFrame.setManufacturerCode(mfrCode);
}
{
QDataStream stream(&task.zclFrame.payload(), QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << (quint16) attrId;
stream << (quint8) attrType;
if (attrType == deCONZ::Zcl8BitEnum || attrType == deCONZ::Zcl8BitBitMap || attrType == deCONZ::Zcl8BitUint)
{
stream << (quint8) attrValue;
}
else if (attrType == deCONZ::Zcl16BitUint)
{
stream << (quint16) attrValue;
}
else
{
DBG_Printf(DBG_INFO, "unsupported attribute type 0x%04x\n", attrType);
return false;
}
}
{
task.req.asdu().clear();
QDataStream stream(&task.req.asdu(), QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
task.zclFrame.writeToStream(stream);
}
return addTask(task);
}
static void copyTaskReq(TaskItem &a, TaskItem &b)
{
b.req.dstAddress() = a.req.dstAddress();
b.req.setDstAddressMode(a.req.dstAddressMode());
b.req.setSrcEndpoint(a.req.srcEndpoint());
b.req.setDstEndpoint(a.req.dstEndpoint());
b.req.setRadius(a.req.radius());
b.req.setTxOptions(a.req.txOptions());
b.req.setSendDelay(a.req.sendDelay());
b.transitionTime = a.transitionTime;
b.lightNode = a.lightNode;
b.taskType = TaskWindowCovering;
b.req.setClusterId(WINDOW_COVERING_CLUSTER_ID);
b.req.setProfileId(HA_PROFILE_ID);
b.zclFrame.payload().clear();
}
* Creates Binding from Target Device Endpoint to Coordinator Endpoint
* and configures Reporting on Cluster 0x0102 Attributes 0x0008, 0x0009, 0x000A.
* and starts calibration on ubisys J1
*
* Value WindowCoveringType Capabilities
* 0 Roller Shade = Lift only
* 1 Roller Shade two motors = Lift only
* 2 Roller Shade exterior = Lift only
* 3 Roller Shade two motors ext = Lift only
* 4 Drapery = Lift only
* 5 Awning = Lift only
* 6 Shutter = Tilt only
* 7 Tilt Blind Lift only = Tilt only
* 8 Tilt Blind lift & tilt = Lift & Tilt
* 9 Projector Screen = Lift only
\param task - the task item
\parma deviceType - WindowCoveringType 0-9
\return true - on success
false - on error
*/
bool DeRestPluginPrivate::addTaskWindowCoveringCalibrate(TaskItem &taskRef, int WindowCoveringType)
{
LightNode *lightNode = getLightNodeForAddress(taskRef.req.dstAddress(), 0x01);
if (lightNode)
{
if (WindowCoveringType == 6 || WindowCoveringType == 7 || WindowCoveringType == 8)
{
lightNode->addItem(DataTypeUInt8, RStateSat);
}
else
{
lightNode->removeItem(RStateSat);
}
lightNode->setNeedSaveDatabase(true);
saveDatabaseItems |= DB_LIGHTS;
}
Sensor *sensor = getSensorNodeForAddressAndEndpoint(taskRef.req.dstAddress(), 0x02);
if (!sensor || !sensor->modelId().startsWith(QLatin1String("J1")))
{
return false;
}
taskRef.req.setDstEndpoint(0x01);
TaskItem task;
copyTaskReq(taskRef, task);
copyTaskReq(taskRef, calibrationTask);
BindingTask bt;
bt.state = BindingTask::StateIdle;
bt.action = BindingTask::ActionBind;
bt.restNode = sensor;
Binding &bnd = bt.binding;
bnd.srcAddress = task.req.dstAddress().ext();
bnd.dstAddrMode = deCONZ::ApsExtAddress;
bnd.srcEndpoint = task.req.srcEndpoint();
bnd.clusterId = WINDOW_COVERING_CLUSTER_ID;
bnd.dstAddress.ext = apsCtrl->getParameter(deCONZ::ParamMacAddress);
bnd.dstEndpoint = endpoint();
if (bnd.dstEndpoint > 0)
{
DBG_Printf(DBG_INFO_L2, "create binding for attribute reporting of cluster 0x%04X\n", WINDOW_COVERING_CLUSTER_ID);
queueBindingTask(bt);
}
else
{
return false;
}
if (!bindingTimer->isActive())
{
bindingTimer->start();
}
ConfigureReportingRequest rq;
rq.zclSeqNum = zclSeq++;
rq.dataType = deCONZ::Zcl8BitUint;
rq.attributeId = 0x0008;
rq.minInterval = 1;
rq.maxInterval = 600;
rq.reportableChange8bit = 1;
ConfigureReportingRequest rq2;
rq2.dataType = deCONZ::Zcl8BitUint;
rq2.attributeId = 0x0009;
rq2.minInterval = 1;
rq2.maxInterval = 600;
rq2.reportableChange8bit = 1;
ConfigureReportingRequest rq3;
rq3.dataType = deCONZ::Zcl8BitBitMap;
rq3.attributeId = 0x000A;
rq3.minInterval = 1;
rq3.maxInterval = 600;
std::vector<ConfigureReportingRequest> out = {rq, rq2, rq3};
DBG_Printf(DBG_INFO, "ubisys addTaskWindowCoveringCalibrate task4 deviceType = %d\n", WindowCoveringType);
TaskItem task2;
copyTaskReq(taskRef, task2);
task2.zclFrame.setSequenceNumber(zclSeq++);
task2.zclFrame.setCommandId(deCONZ::ZclConfigureReportingId);
task2.zclFrame.setFrameControl(deCONZ::ZclFCProfileCommand |
deCONZ::ZclFCDirectionClientToServer |
deCONZ::ZclFCDisableDefaultResponse);
{
QDataStream stream(&task2.zclFrame.payload(), QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
for (const ConfigureReportingRequest &rq : out)
{
stream << rq.direction;
stream << rq.attributeId;
stream << rq.dataType;
stream << rq.minInterval;
stream << rq.maxInterval;
if (rq.reportableChange16bit != 0xFFFF)
{
stream << rq.reportableChange16bit;
}
else if (rq.reportableChange8bit != 0xFF)
{
stream << rq.reportableChange8bit;
}
else if (rq.reportableChange24bit != 0xFFFFFF)
{
stream << (qint8) (rq.reportableChange24bit & 0xFF);
stream << (qint8) ((rq.reportableChange24bit >> 8) & 0xFF);
stream << (qint8) ((rq.reportableChange24bit >> 16) & 0xFF);
}
else if (rq.reportableChange48bit != 0xFFFFFFFF)
{
stream << (qint8) (rq.reportableChange48bit & 0xFF);
stream << (qint8) ((rq.reportableChange48bit >> 8) & 0xFF);
stream << (qint8) ((rq.reportableChange48bit >> 16) & 0xFF);
stream << (qint8) ((rq.reportableChange48bit >> 24) & 0xFF);
stream << (qint8) 0x00;
stream << (qint8) 0x00;
}
DBG_Printf(DBG_INFO_L2, "configure reporting for 0x%016llX, attribute 0x%04X/0x%04X\n", bt.restNode->address().ext(), bt.binding.clusterId, rq.attributeId);
}
}
{
task2.req.asdu().clear();
QDataStream stream(&task2.req.asdu(), QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
task2.zclFrame.writeToStream(stream);
}
if (!addTask(task2))
{
return false;
}
TaskItem task3;
copyTaskReq(taskRef, task3);
task3.zclFrame.setSequenceNumber(zclSeq++);
task3.zclFrame.setCommandId(deCONZ::ZclWriteAttributesId);
task3.zclFrame.setFrameControl(deCONZ::ZclFCProfileCommand |
deCONZ::ZclFCManufacturerSpecific |
deCONZ::ZclFCDirectionClientToServer |
deCONZ::ZclFCDisableDefaultResponse);
task3.zclFrame.setManufacturerCode(0x10F2);
{
QDataStream stream(&task3.zclFrame.payload(), QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
quint8 attrTypeId = deCONZ::Zcl16BitUint;
stream << (quint16) 0x0000;
stream << (quint8) deCONZ::Zcl8BitEnum;
stream << (quint8) WindowCoveringType;
stream << (quint16) 0x0010;
stream << attrTypeId;
stream << (quint16) 0x0000;
stream << (quint16) 0x0011;
stream << attrTypeId;
stream << (quint16) 0x00F0;
stream << (quint16) 0x0012;
stream << attrTypeId;
stream << (quint16) 0x0000;
stream << (quint16) 0x0013;
stream << attrTypeId;
stream << (quint16) 0x0384;
stream << (quint16) 0x1001;
stream << attrTypeId;
stream << (quint16) 0xFFFF;
stream << (quint16) 0x1002;
stream << attrTypeId;
stream << (quint16) 0xFFFF;
stream << (quint16) 0x1003;
stream << attrTypeId;
stream << (quint16) 0xFFFF;
stream << (quint16) 0x1004;
stream << attrTypeId;
stream << (quint16) 0xFFFF;
}
{
task3.req.asdu().clear();
QDataStream stream(&task3.req.asdu(), QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
task3.zclFrame.writeToStream(stream);
}
if (!addTask(task3))
{
return false;
}
TaskItem task4;
copyTaskReq(taskRef, task4);
task4.zclFrame.setSequenceNumber(zclSeq++);
task4.zclFrame.setCommandId(deCONZ::ZclWriteAttributesId);
task4.zclFrame.setFrameControl(deCONZ::ZclFCProfileCommand |
deCONZ::ZclFCDirectionClientToServer |
deCONZ::ZclFCDisableDefaultResponse);
{
QDataStream stream(&task4.zclFrame.payload(), QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << (quint16) 0x0017;
stream << (quint8) deCONZ::Zcl8BitBitMap;
stream << (quint8) 0x02;
}
{
task4.req.asdu().clear();
QDataStream stream(&task4.req.asdu(), QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
task4.zclFrame.writeToStream(stream);
}
if (!addTask(task4))
{
return false;
}
calibrationStep = 3;
QTimer::singleShot(2000, this, SLOT(calibrateWindowCoveringNextStep()));
return true;
}
void DeRestPluginPrivate::calibrateWindowCoveringNextStep()
{
TaskItem task;
copyTaskReq(calibrationTask, task);
DBG_Printf(DBG_INFO, "ubisys NextStep calibrationStep = %d, task=" FMT_MAC " calibrationTask = " FMT_MAC "\n",
calibrationStep,
(unsigned long long)task.req.dstAddress().ext(),
(unsigned long long)calibrationTask.req.dstAddress().ext());
switch(calibrationStep)
{
case 3:
calibrationStep = 4;
QTimer::singleShot(2000, this, SLOT(calibrateWindowCoveringNextStep()));
addTaskWindowCovering(task, 0x01 , 0, 0);
break;
case 4:
calibrationStep = 5;
QTimer::singleShot(4000, this, SLOT(calibrateWindowCoveringNextStep()));
addTaskWindowCovering(task, 0x00 , 0, 0);
break;
case 5:
if (operationalStatus == 0)
{
calibrationStep = 6;
addTaskWindowCovering(task, 0x01 , 0, 0);
}
QTimer::singleShot(30000, this, SLOT(calibrateWindowCoveringNextStep()));
break;
case 6:
if (operationalStatus == 0)
{
calibrationStep = 7;
addTaskWindowCovering(task, 0x00 , 0, 0);
}
QTimer::singleShot(30000, this, SLOT(calibrateWindowCoveringNextStep()));
break;
case 7:
if (operationalStatus == 0)
{
calibrationStep = 8;
}
QTimer::singleShot(30000, this, SLOT(calibrateWindowCoveringNextStep()));
break;
case 8:
if (operationalStatus == 0)
{
calibrationStep = 0;
task.zclFrame.setSequenceNumber(zclSeq++);
task.zclFrame.setCommandId(deCONZ::ZclWriteAttributesId);
task.zclFrame.setFrameControl(deCONZ::ZclFCProfileCommand |
deCONZ::ZclFCDirectionClientToServer |
deCONZ::ZclFCDisableDefaultResponse);
{
QDataStream stream(&task.zclFrame.payload(), QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << (quint16) 0x0017;
stream << (quint8) deCONZ::Zcl8BitBitMap;
stream << (quint8) 0x00;
}
{
task.req.asdu().clear();
QDataStream stream(&task.req.asdu(), QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
task.zclFrame.writeToStream(stream);
}
addTask(task);
}
break;
default:
{
}
break;
}
}