From 3e8da8ee26fd845c9ed2eb0124023c96173dfeb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Wed, 15 Apr 2020 17:59:01 +0200 Subject: [PATCH] Add ZCL basics --- .../interface/zigbeeinterfacedeconz.cpp | 28 +- .../deconz/interface/zigbeeinterfacedeconz.h | 1 + .../deconz/zigbeebridgecontrollerdeconz.cpp | 20 +- .../deconz/zigbeebridgecontrollerdeconz.h | 4 +- .../deconz/zigbeenetworkdeconz.cpp | 59 ++-- libnymea-zigbee/deconz/zigbeenetworkdeconz.h | 6 + libnymea-zigbee/deconz/zigbeenodedeconz.cpp | 278 ++++++++++-------- libnymea-zigbee/deconz/zigbeenodedeconz.h | 7 + .../deconz/zigbeenodeendpointdeconz.cpp | 26 +- .../deconz/zigbeenodeendpointdeconz.h | 22 +- libnymea-zigbee/libnymea-zigbee.pro | 2 + libnymea-zigbee/zigbeeclusterlibrary.cpp | 176 +++++++++++ libnymea-zigbee/zigbeeclusterlibrary.h | 112 +++++++ libnymea-zigbee/zigbeenodeendpoint.h | 24 +- libnymea-zigbee/zigbeeutils.h | 9 - 15 files changed, 590 insertions(+), 184 deletions(-) create mode 100644 libnymea-zigbee/zigbeeclusterlibrary.cpp create mode 100644 libnymea-zigbee/zigbeeclusterlibrary.h diff --git a/libnymea-zigbee/deconz/interface/zigbeeinterfacedeconz.cpp b/libnymea-zigbee/deconz/interface/zigbeeinterfacedeconz.cpp index 94c6ee0..50e428b 100644 --- a/libnymea-zigbee/deconz/interface/zigbeeinterfacedeconz.cpp +++ b/libnymea-zigbee/deconz/interface/zigbeeinterfacedeconz.cpp @@ -38,7 +38,7 @@ ZigbeeInterfaceDeconz::ZigbeeInterfaceDeconz(QObject *parent) : QObject(parent) { m_reconnectTimer = new QTimer(this); m_reconnectTimer->setSingleShot(true); - m_reconnectTimer->setInterval(2000); + m_reconnectTimer->setInterval(5000); connect(m_reconnectTimer, &QTimer::timeout, this, &ZigbeeInterfaceDeconz::onReconnectTimeout); } @@ -122,11 +122,12 @@ void ZigbeeInterfaceDeconz::setAvailable(bool available) if (m_available == available) return; + // Clear the data buffer in any case + if (m_available) + m_dataBuffer.clear(); + m_available = available; emit availableChanged(m_available); - - // Clear the data buffer in any case - m_dataBuffer.clear(); } void ZigbeeInterfaceDeconz::onReconnectTimeout() @@ -137,7 +138,6 @@ void ZigbeeInterfaceDeconz::onReconnectTimeout() m_reconnectTimer->start(); } else { qCDebug(dcZigbeeInterface()) << "Interface reconnected successfully on" << m_serialPort->portName() << m_serialPort->baudRate(); - m_serialPort->clear(); setAvailable(true); } } @@ -232,13 +232,13 @@ void ZigbeeInterfaceDeconz::sendPackage(const QByteArray &package) bool ZigbeeInterfaceDeconz::enable(const QString &serialPort, qint32 baudrate) { + qCDebug(dcZigbeeInterface()) << "Start UART interface " << serialPort << baudrate; + if (m_serialPort) { delete m_serialPort; m_serialPort = nullptr; } - setAvailable(false); - m_serialPort = new QSerialPort(serialPort, this); m_serialPort->setBaudRate(baudrate); m_serialPort->setDataBits(QSerialPort::Data8); @@ -260,6 +260,20 @@ bool ZigbeeInterfaceDeconz::enable(const QString &serialPort, qint32 baudrate) return true; } +void ZigbeeInterfaceDeconz::reconnectController() +{ + if (!m_serialPort) + return; + + if (m_serialPort->isOpen()) + m_serialPort->close(); + + delete m_serialPort; + m_serialPort = nullptr; + setAvailable(false); + m_reconnectTimer->start(); +} + void ZigbeeInterfaceDeconz::disable() { if (!m_serialPort) diff --git a/libnymea-zigbee/deconz/interface/zigbeeinterfacedeconz.h b/libnymea-zigbee/deconz/interface/zigbeeinterfacedeconz.h index 58af5b7..fdef08e 100644 --- a/libnymea-zigbee/deconz/interface/zigbeeinterfacedeconz.h +++ b/libnymea-zigbee/deconz/interface/zigbeeinterfacedeconz.h @@ -75,6 +75,7 @@ public slots: void sendPackage(const QByteArray &package); bool enable(const QString &serialPort = "/dev/ttyS0", qint32 baudrate = 115200); + void reconnectController(); void disable(); }; diff --git a/libnymea-zigbee/deconz/zigbeebridgecontrollerdeconz.cpp b/libnymea-zigbee/deconz/zigbeebridgecontrollerdeconz.cpp index 84b5604..a9a34f4 100644 --- a/libnymea-zigbee/deconz/zigbeebridgecontrollerdeconz.cpp +++ b/libnymea-zigbee/deconz/zigbeebridgecontrollerdeconz.cpp @@ -42,7 +42,7 @@ ZigbeeBridgeControllerDeconz::ZigbeeBridgeControllerDeconz(QObject *parent) : m_watchdogTimer = new QTimer(this); m_watchdogTimer->setSingleShot(false); - m_watchdogTimer->setInterval(m_watchdogResetTimout * 1000); // Set the watchdog to 85 seconds, reset every 60 s + m_watchdogTimer->setInterval(m_watchdogResetTimout * 1000); connect(m_watchdogTimer, &QTimer::timeout, this, &ZigbeeBridgeControllerDeconz::resetControllerWatchdog); } @@ -684,6 +684,9 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters( return; } + // Reset the watchdog in any case + resetControllerWatchdog(); + // Read watchdog timeout ZigbeeInterfaceDeconzReply *replyWatchdogTimeout = requestReadParameter(Deconz::ParameterWatchdogTtl); connect(replyWatchdogTimeout, &ZigbeeInterfaceDeconzReply::finished, this, [this, readNetworkParametersReply, replyWatchdogTimeout](){ @@ -704,11 +707,6 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters( << "finished successfully"; qCDebug(dcZigbeeController()) << "Watchdog timeout:" << m_networkConfiguration.watchdogTimeout; - // Note: this value describes how much seconds are left until the watchdog triggers. Reset it right the way - if (watchdogTimeout < 15) { - resetControllerWatchdog(); - } - // Finished reading all parameters. Finish the independent reply in order to indicate the process has finished emit networkConfigurationParameterChanged(m_networkConfiguration); readNetworkParametersReply->m_statusCode = Deconz::StatusCodeSuccess; @@ -887,10 +885,8 @@ void ZigbeeBridgeControllerDeconz::processDataConfirm(const QByteArray &data) void ZigbeeBridgeControllerDeconz::onInterfaceAvailableChanged(bool available) { - if (available) { - // FIXME: only start if the protocol version is >= 0x0108 - m_watchdogTimer->start(); - } else { + qCDebug(dcZigbeeController()) << "Interface available changed" << available; + if (!available) { // Clean up any pending replies foreach (quint8 id, m_pendingReplies.keys()) { ZigbeeInterfaceDeconzReply *reply = m_pendingReplies.take(id); @@ -940,6 +936,7 @@ void ZigbeeBridgeControllerDeconz::onInterfacePackageReceived(const QByteArray & } case Deconz::CommandMacPoll: { // FIXME: parse the data and print info + break; } case Deconz::CommandSimplifiedBeacon: { @@ -961,12 +958,13 @@ void ZigbeeBridgeControllerDeconz::resetControllerWatchdog() stream.setByteOrder(QDataStream::LittleEndian); stream << m_watchdogTimeout; ZigbeeInterfaceDeconzReply *reply = requestWriteParameter(Deconz::ParameterWatchdogTtl, parameterData); - connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [reply](){ + connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){ if (reply->statusCode() != Deconz::StatusCodeSuccess) { qCWarning(dcZigbeeController()) << "Could not reset the application watchdog on the deCONZ controller." << reply->statusCode(); return; } qCDebug(dcZigbeeController()) << "Reset application watchdog on the deCONZ controller successfully"; + m_watchdogTimer->start(); }); } diff --git a/libnymea-zigbee/deconz/zigbeebridgecontrollerdeconz.h b/libnymea-zigbee/deconz/zigbeebridgecontrollerdeconz.h index cbf72a8..510aada 100644 --- a/libnymea-zigbee/deconz/zigbeebridgecontrollerdeconz.h +++ b/libnymea-zigbee/deconz/zigbeebridgecontrollerdeconz.h @@ -134,8 +134,8 @@ public: private: ZigbeeInterfaceDeconz *m_interface = nullptr; quint8 m_sequenceNumber = 0; - quint32 m_watchdogTimeout = 85; - int m_watchdogResetTimout = 60; + quint32 m_watchdogTimeout = 300; + int m_watchdogResetTimout = 280; QHash m_pendingReplies; DeconzNetworkConfiguration m_networkConfiguration; Deconz::NetworkState m_networkState = Deconz::NetworkStateOffline; diff --git a/libnymea-zigbee/deconz/zigbeenetworkdeconz.cpp b/libnymea-zigbee/deconz/zigbeenetworkdeconz.cpp index dafa300..547bf7a 100644 --- a/libnymea-zigbee/deconz/zigbeenetworkdeconz.cpp +++ b/libnymea-zigbee/deconz/zigbeenetworkdeconz.cpp @@ -54,8 +54,9 @@ ZigbeeNetworkDeconz::ZigbeeNetworkDeconz(QObject *parent) : ZigbeeBridgeController *ZigbeeNetworkDeconz::bridgeController() const { - if (m_controller) + if (m_controller) { return qobject_cast(m_controller); + } return nullptr; } @@ -222,7 +223,7 @@ void ZigbeeNetworkDeconz::setCreateNetworkState(ZigbeeNetworkDeconz::CreateNetwo return; } - qCDebug(dcZigbeeNetwork()) << "Configured security mode successfully"; + qCDebug(dcZigbeeNetwork()) << "Configured security mode successfully. SQN:" << reply->sequenceNumber(); qCDebug(dcZigbeeNetwork()) << "Configure network key" << securityConfiguration().networkKey().toString(); @@ -279,11 +280,16 @@ void ZigbeeNetworkDeconz::setCreateNetworkState(ZigbeeNetworkDeconz::CreateNetwo setChannel(m_controller->networkConfiguration().currentChannel); setCreateNetworkState(CreateNetworkStateInitializeCoordinatorNode); - }); break; } case CreateNetworkStateInitializeCoordinatorNode: { + if (m_coordinatorNode) { + qCDebug(dcZigbeeNetwork()) << "We already have the coordinator node. Network starting done."; + setState(StateRunning); + return; + } + ZigbeeNodeDeconz *coordinatorNode = qobject_cast(createNode(this)); coordinatorNode->setShortAddress(m_controller->networkConfiguration().shortAddress); coordinatorNode->setExtendedAddress(m_controller->networkConfiguration().ieeeAddress); @@ -340,7 +346,7 @@ void ZigbeeNetworkDeconz::handleZigbeeDeviceProfileIndication(const DeconzApsDat } } - qCWarning(dcZigbeeNetwork()) << "Unhandled ZDO indication" << indication; + qCWarning(dcZigbeeNetwork()) << "FIXME: Unhandled ZDO indication" << indication; } ZigbeeNode *ZigbeeNetworkDeconz::createNode(QObject *parent) @@ -400,10 +406,9 @@ void ZigbeeNetworkDeconz::setPermitJoiningInternal(bool permitJoining) void ZigbeeNetworkDeconz::startNetworkInternally() { - qCDebug(dcZigbeeNetwork()) << "Start network internally"; + qCDebug(dcZigbeeNetwork()) << "Start zigbee network internally"; m_createNewNetwork = false; - // Check if we have to create a pan ID and select the channel if (panId() == 0) { m_createNewNetwork = true; @@ -411,15 +416,17 @@ void ZigbeeNetworkDeconz::startNetworkInternally() //qCDebug(dcZigbeeNetwork()) << "Created new PAN ID:" << extendedPanId(); } - if (securityConfiguration().networkKey().isNull()) { - m_createNewNetwork = true; - qCDebug(dcZigbeeNetwork()) << "Create a new network key"; - ZigbeeNetworkKey key = ZigbeeNetworkKey::generateKey(); - m_securityConfiguration.setNetworkKey(key); - } + // Note: we cannot read or write the network key here. - qCDebug(dcZigbeeNetwork()) << "Using" << securityConfiguration().networkKey() << "network link key"; - qCDebug(dcZigbeeNetwork()) << "Using" << securityConfiguration().globalTrustCenterLinkKey() << "global trust center link key"; + // if (securityConfiguration().networkKey().isNull()) { + // m_createNewNetwork = true; + // qCDebug(dcZigbeeNetwork()) << "Create a new network key"; + // ZigbeeNetworkKey key = ZigbeeNetworkKey::generateKey(); + // m_securityConfiguration.setNetworkKey(key); + // } + + //qCDebug(dcZigbeeNetwork()) << "Using" << securityConfiguration().networkKey() << "network link key"; + //qCDebug(dcZigbeeNetwork()) << "Using" << securityConfiguration().globalTrustCenterLinkKey() << "global trust center link key"; // - Read the firmware version // - Read the network configuration parameters @@ -429,6 +436,7 @@ void ZigbeeNetworkDeconz::startNetworkInternally() // - If network running and configurations match, we are done // Read the firmware version + qCDebug(dcZigbeeNetwork()) << "Reading current firmware version..."; ZigbeeInterfaceDeconzReply *reply = m_controller->requestVersion(); connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){ if (reply->statusCode() != Deconz::StatusCodeSuccess) { @@ -436,7 +444,7 @@ void ZigbeeNetworkDeconz::startNetworkInternally() // FIXME: set an appropriate error return; } - qCDebug(dcZigbeeNetwork()) << "Version request finished" << reply->statusCode() << ZigbeeUtils::convertByteArrayToHexString(reply->responseData()); + qCDebug(dcZigbeeNetwork()) << "Version request finished successfully" << ZigbeeUtils::convertByteArrayToHexString(reply->responseData()); // Note: version is an uint32 value, little endian, but we can read the individual bytes in reversed order quint8 majorVersion = static_cast(reply->responseData().at(3)); quint8 minorVersion = static_cast(reply->responseData().at(2)); @@ -445,6 +453,7 @@ void ZigbeeNetworkDeconz::startNetworkInternally() qCDebug(dcZigbeeNetwork()) << "Firmware version" << firmwareVersion << platform; // Read all network parameters + qCDebug(dcZigbeeNetwork()) << "Start reading controller network parameters..."; ZigbeeInterfaceDeconzReply *reply = m_controller->readNetworkParameters(); connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply, firmwareVersion](){ if (reply->statusCode() != Deconz::StatusCodeSuccess) { @@ -456,11 +465,12 @@ void ZigbeeNetworkDeconz::startNetworkInternally() qCDebug(dcZigbeeNetwork()) << "Reading network parameters finished successfully."; QString protocolVersion = QString("%1.%2").arg(m_controller->networkConfiguration().protocolVersion >> 8 & 0xFF) .arg(m_controller->networkConfiguration().protocolVersion & 0xFF); + qCDebug(dcZigbeeNetwork()) << "Controller API protocol version" << ZigbeeUtils::convertUint16ToHexString(m_controller->networkConfiguration().protocolVersion) << protocolVersion; m_controller->setFirmwareVersionString(QString("%1 - %2").arg(firmwareVersion).arg(protocolVersion)); qCDebug(dcZigbeeNetwork()) << m_controller->networkConfiguration(); - + qCDebug(dcZigbeeNetwork()) << "Reading current network state"; ZigbeeInterfaceDeconzReply *reply = m_controller->requestDeviceState(); connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){ if (reply->statusCode() != Deconz::StatusCodeSuccess) { @@ -469,29 +479,34 @@ void ZigbeeNetworkDeconz::startNetworkInternally() return; } - qCDebug(dcZigbeeNetwork()) << "Read device state finished successfully. SQN:" << reply->sequenceNumber(); QDataStream stream(reply->responseData()); stream.setByteOrder(QDataStream::LittleEndian); quint8 deviceStateFlag = 0; stream >> deviceStateFlag; + DeconzDeviceState deviceState = m_controller->parseDeviceStateFlag(deviceStateFlag); + qCDebug(dcZigbeeNetwork()) << deviceState; + // Update the device state in the controller - m_controller->processDeviceState(m_controller->parseDeviceStateFlag(deviceStateFlag)); + m_controller->processDeviceState(deviceState); if (m_createNewNetwork) { - setCreateNetworkState(CreateNetworkStateStopNetwork); // Set offline // Write configurations // Set online // Read configurations // Create and initialize coordinator node // Done. Save network + setCreateNetworkState(CreateNetworkStateStopNetwork); } else { // Get the network state and start the network if required if (m_controller->networkState() == Deconz::NetworkStateConnected) { qCDebug(dcZigbeeNetwork()) << "The network is already running."; setState(StateRunning); + } else if (m_controller->networkState() == Deconz::NetworkStateOffline) { + qCDebug(dcZigbeeNetwork()) << "The network is offline. Lets start it"; + setCreateNetworkState(CreateNetworkStateStartNetwork); } else { - startNetwork(); + // The network is not running yet, lets wait for the state changed } } }); @@ -501,9 +516,8 @@ void ZigbeeNetworkDeconz::startNetworkInternally() void ZigbeeNetworkDeconz::onControllerAvailableChanged(bool available) { - qCDebug(dcZigbeeNetwork()) << "Hardware controller is" << (available ? "now available" : "not available"); - if (!available) { + qCWarning(dcZigbeeNetwork()) << "Hardware controller is not available any more."; setError(ErrorHardwareUnavailable); m_permitJoining = false; emit permitJoiningChanged(m_permitJoining); @@ -513,6 +527,7 @@ void ZigbeeNetworkDeconz::onControllerAvailableChanged(bool available) m_permitJoining = false; emit permitJoiningChanged(m_permitJoining); setState(StateStarting); + qCDebug(dcZigbeeNetwork()) << "Hardware controller is now available."; startNetworkInternally(); } } diff --git a/libnymea-zigbee/deconz/zigbeenetworkdeconz.h b/libnymea-zigbee/deconz/zigbeenetworkdeconz.h index 8db4e02..a3ae5a5 100644 --- a/libnymea-zigbee/deconz/zigbeenetworkdeconz.h +++ b/libnymea-zigbee/deconz/zigbeenetworkdeconz.h @@ -74,8 +74,14 @@ private: QTimer *m_pollNetworkStateTimer = nullptr; void setCreateNetworkState(CreateNetworkState state); + // ZDO void handleZigbeeDeviceProfileIndication(const DeconzApsDataIndication &indication); + // ZZL + + // HA + + // GP protected: ZigbeeNode *createNode(QObject *parent) override; diff --git a/libnymea-zigbee/deconz/zigbeenodedeconz.cpp b/libnymea-zigbee/deconz/zigbeenodedeconz.cpp index 78f6ae2..609748d 100644 --- a/libnymea-zigbee/deconz/zigbeenodedeconz.cpp +++ b/libnymea-zigbee/deconz/zigbeenodedeconz.cpp @@ -29,6 +29,8 @@ #include "zigbeenodedeconz.h" #include "zigbeedeviceprofile.h" #include "zigbeenetworkdeconz.h" +#include "zigbeenodeendpointdeconz.h" + #include "loggingcategory.h" #include @@ -142,26 +144,8 @@ void ZigbeeNodeDeconz::leaveNetworkRequest(bool rejoin, bool removeChildren) Q_UNUSED(removeChildren) } -void ZigbeeNodeDeconz::setClusterAttributeReport(const ZigbeeClusterAttributeReport &report) +void ZigbeeNodeDeconz::initNodeDescriptor() { - Q_UNUSED(report) -} - -void ZigbeeNodeDeconz::startInitialization() -{ - setState(StateInitializing); - - /* Node initialisation steps (sequentially) - * - Node descriptor - * - Power descriptor - * - Active endpoints - * - for each endpoint do: - * - Simple descriptor request - * - for each endpoint - * - read basic cluster - */ - - ZigbeeNetworkReply *reply = requestNodeDescriptor(); connect(reply, &ZigbeeNetworkReply::finished, this, [this, reply](){ // TODO: check reply error @@ -231,123 +215,181 @@ void ZigbeeNodeDeconz::startInitialization() qCDebug(dcZigbeeNode()) << " Allocate address:" << allocateAddress(); - // Power descriptor - ZigbeeNetworkReply *reply = requestPowerDescriptor(); - connect(reply, &ZigbeeNetworkReply::finished, this, [this, reply](){ - // TODO: check reply error + // Continue with the power descriptor + initPowerDescriptor(); + }); +} - ZigbeeDeviceProfileAdpu adpu = ZigbeeDeviceProfile::parseAdpu(reply->responseData()); - qCDebug(dcZigbeeNode()) << "Power descriptor request finished" << this << adpu; - QDataStream stream(adpu.payload); - stream.setByteOrder(QDataStream::LittleEndian); - quint16 powerDescriptorFlag = 0; - stream >> powerDescriptorFlag; - setPowerDescriptorFlag(powerDescriptorFlag); +void ZigbeeNodeDeconz::initPowerDescriptor() +{ + ZigbeeNetworkReply *reply = requestPowerDescriptor(); + connect(reply, &ZigbeeNetworkReply::finished, this, [this, reply](){ + // TODO: check reply error + ZigbeeDeviceProfileAdpu adpu = ZigbeeDeviceProfile::parseAdpu(reply->responseData()); + qCDebug(dcZigbeeNode()) << "Power descriptor request finished" << this << adpu; + QDataStream stream(adpu.payload); + stream.setByteOrder(QDataStream::LittleEndian); + quint16 powerDescriptorFlag = 0; + stream >> powerDescriptorFlag; + setPowerDescriptorFlag(powerDescriptorFlag); - ZigbeeNetworkReply *reply = requestActiveEndpoints(); - connect(reply, &ZigbeeNetworkReply::finished, this, [this, reply](){ + // Continue with endpoint fetching + initEndpoints(); + }); +} + +void ZigbeeNodeDeconz::initEndpoints() +{ + ZigbeeNetworkReply *reply = requestActiveEndpoints(); + connect(reply, &ZigbeeNetworkReply::finished, this, [this, reply](){ + // TODO: check reply error + + ZigbeeDeviceProfileAdpu adpu = ZigbeeDeviceProfile::parseAdpu(reply->responseData()); + qCDebug(dcZigbeeNode()) << "Active endpoints request finished" << this << adpu; + QDataStream stream(adpu.payload); + stream.setByteOrder(QDataStream::LittleEndian); + quint8 endpointCount = 0; + m_uninitializedEndpoints.clear(); + stream >> endpointCount; + for (int i = 0; i < endpointCount; i++) { + quint8 endpoint = 0; + stream >> endpoint; + m_uninitializedEndpoints.append(endpoint); + } + + qCDebug(dcZigbeeNode()) << "Endpoints" << endpointCount; + for (int i = 0; i < m_uninitializedEndpoints.count(); i++) { + qCDebug(dcZigbeeNode()) << " -" << ZigbeeUtils::convertByteToHexString(m_uninitializedEndpoints.at(i)); + } + + // Read simple descriptor for each endpoint + if (m_uninitializedEndpoints.isEmpty()) { + initBasicCluster(); + } + + for (int i = 0; i < m_uninitializedEndpoints.count(); i++) { + quint8 endpointId = m_uninitializedEndpoints.at(i); + qCDebug(dcZigbeeNode()) << "Read simple descriptor of endpoint" << ZigbeeUtils::convertByteToHexString(endpointId); + ZigbeeNetworkReply *reply = requestSimpleDescriptor(endpointId); + connect(reply, &ZigbeeNetworkReply::finished, this, [this, reply, endpointId](){ // TODO: check reply error - ZigbeeDeviceProfileAdpu adpu = ZigbeeDeviceProfile::parseAdpu(reply->responseData()); - qCDebug(dcZigbeeNode()) << "Active endpoints request finished" << this << adpu; + qCDebug(dcZigbeeNode()) << "Simple descriptor request finished" << this << endpointId << adpu; + QDataStream stream(adpu.payload); stream.setByteOrder(QDataStream::LittleEndian); - quint8 endpointCount = 0; - m_uninitializedEndpoints.clear(); - stream >> endpointCount; - for (int i = 0; i < endpointCount; i++) { - quint8 endpoint = 0; - stream >> endpoint; - m_uninitializedEndpoints.append(endpoint); + quint8 length = 0; + quint8 endpointId = 0; + quint16 profileId = 0; + quint16 deviceId = 0; + quint8 deviceVersion = 0; + quint8 inputClusterCount = 0; + quint8 outputClusterCount = 0; + + QList inputClusters; + QList outputClusters; + + stream >> length >> endpointId >> profileId >> deviceId >> deviceVersion >> inputClusterCount; + + qCDebug(dcZigbeeNode()) << "Node endpoint simple descriptor:"; + qCDebug(dcZigbeeNode()) << " Lenght:" << ZigbeeUtils::convertByteToHexString(length); + qCDebug(dcZigbeeNode()) << " End Point:" << ZigbeeUtils::convertByteToHexString(endpointId); + qCDebug(dcZigbeeNode()) << " Profile:" << ZigbeeUtils::profileIdToString(static_cast(profileId)); + if (profileId == Zigbee::ZigbeeProfileLightLink) { + qCDebug(dcZigbeeNode()) << " Device ID:" << ZigbeeUtils::convertUint16ToHexString(deviceId) << static_cast(deviceId); + } else if (profileId == Zigbee::ZigbeeProfileHomeAutomation) { + qCDebug(dcZigbeeNode()) << " Device ID:" << ZigbeeUtils::convertUint16ToHexString(deviceId) << static_cast(deviceId); + } else if (profileId == Zigbee::ZigbeeProfileGreenPower) { + qCDebug(dcZigbeeNode()) << " Device ID:" << ZigbeeUtils::convertUint16ToHexString(deviceId) << static_cast(deviceId); } - qCDebug(dcZigbeeNode()) << "Endpoints" << endpointCount; - for (int i = 0; i < m_uninitializedEndpoints.count(); i++) { - qCDebug(dcZigbeeNode()) << " -" << ZigbeeUtils::convertByteToHexString(m_uninitializedEndpoints.at(i)); + qCDebug(dcZigbeeNode()) << " Device version:" << ZigbeeUtils::convertByteToHexString(deviceVersion); + + // Create endpoint + ZigbeeNodeEndpointDeconz *endpoint = nullptr; + if (!hasEndpoint(endpointId)) { + ZigbeeNodeEndpointDeconz *endpoint = qobject_cast(createNodeEndpoint(endpointId, this)); + m_endpoints.append(endpoint); + } else { + endpoint = qobject_cast(getEndpoint(endpointId)); + } + endpoint->setProfile(static_cast(profileId)); + endpoint->setDeviceId(deviceId); + endpoint->setDeviceVersion(deviceVersion); + + qCDebug(dcZigbeeNode()) << " Input clusters: (" << inputClusterCount << ")"; + for (int i = 0; i < inputClusterCount; i++) { + quint16 clusterId = 0; + stream >> clusterId; + if (!endpoint->hasInputCluster(static_cast(clusterId))) { + endpoint->addInputCluster(new ZigbeeCluster(static_cast(clusterId), ZigbeeCluster::Input, endpoint)); + } + qCDebug(dcZigbeeNode()) << " Cluster ID:" << ZigbeeUtils::convertUint16ToHexString(clusterId) << ZigbeeUtils::clusterIdToString(static_cast(clusterId)); + + } + stream >> outputClusterCount; + + qCDebug(dcZigbeeNode()) << " Output clusters: (" << outputClusterCount << ")"; + for (int i = 0; i < outputClusterCount; i++) { + quint16 clusterId = 0; + stream >> clusterId; + if (!endpoint->hasOutputCluster(static_cast(clusterId))) { + endpoint->addOutputCluster(new ZigbeeCluster(static_cast(clusterId), ZigbeeCluster::Output, endpoint)); + } + qCDebug(dcZigbeeNode()) << " Cluster ID:" << ZigbeeUtils::convertUint16ToHexString(clusterId) << ZigbeeUtils::clusterIdToString(static_cast(clusterId)); } - // Read simple descriptor for each endpoint + m_uninitializedEndpoints.removeAll(endpointId); + if (m_uninitializedEndpoints.isEmpty()) { - setState(StateInitialized); - } - - for (int i = 0; i < m_uninitializedEndpoints.count(); i++) { - quint8 endpointId = m_uninitializedEndpoints.at(i); - qCDebug(dcZigbeeNode()) << "Read simple descriptor of endpoint" << ZigbeeUtils::convertByteToHexString(endpointId); - ZigbeeNetworkReply *reply = requestSimpleDescriptor(endpointId); - connect(reply, &ZigbeeNetworkReply::finished, this, [this, reply, endpointId](){ - // TODO: check reply error - ZigbeeDeviceProfileAdpu adpu = ZigbeeDeviceProfile::parseAdpu(reply->responseData()); - qCDebug(dcZigbeeNode()) << "Simple descriptor request finished" << this << endpointId << adpu; - - QDataStream stream(adpu.payload); - stream.setByteOrder(QDataStream::LittleEndian); - quint8 length = 0; - quint8 endpoint = 0; - quint16 profileId = 0; - quint16 deviceId = 0; - quint8 deviceVersion = 0; - quint8 inputClusterCount = 0; - quint8 outputClusterCount = 0; - - QList inputClusters; - QList outputClusters; - - stream >> length >> endpoint >> profileId >> deviceId >> deviceVersion >> inputClusterCount; - - qCDebug(dcZigbeeNode()) << "Node endpoint simple descriptor:"; - qCDebug(dcZigbeeNode()) << " Lenght:" << ZigbeeUtils::convertByteToHexString(length); - qCDebug(dcZigbeeNode()) << " End Point:" << ZigbeeUtils::convertByteToHexString(endpoint); - qCDebug(dcZigbeeNode()) << " Profile:" << ZigbeeUtils::profileIdToString(static_cast(profileId)); - if (profileId == Zigbee::ZigbeeProfileLightLink) { - qCDebug(dcZigbeeNode()) << " Device ID:" << ZigbeeUtils::convertUint16ToHexString(deviceId) << static_cast(deviceId); - } else if (profileId == Zigbee::ZigbeeProfileHomeAutomation) { - qCDebug(dcZigbeeNode()) << " Device ID:" << ZigbeeUtils::convertUint16ToHexString(deviceId) << static_cast(deviceId); - } else if (profileId == Zigbee::ZigbeeProfileGreenPower) { - qCDebug(dcZigbeeNode()) << " Device ID:" << ZigbeeUtils::convertUint16ToHexString(deviceId) << static_cast(deviceId); - } - - qCDebug(dcZigbeeNode()) << " Device version:" << ZigbeeUtils::convertByteToHexString(deviceVersion); - - - qCDebug(dcZigbeeNode()) << " Input clusters: (" << inputClusterCount << ")"; - - for (int i = 0; i < inputClusterCount; i++) { - quint16 clusterId = 0; - stream >> clusterId; - inputClusters.append(clusterId); - qCDebug(dcZigbeeNode()) << " Cluster ID:" << ZigbeeUtils::convertUint16ToHexString(clusterId) << ZigbeeUtils::clusterIdToString(static_cast(clusterId)); - - } - stream >> outputClusterCount; - - qCDebug(dcZigbeeNode()) << " Output clusters: (" << outputClusterCount << ")"; - for (int i = 0; i < outputClusterCount; i++) { - quint16 clusterId = 0; - stream >> clusterId; - outputClusters.append(clusterId); - qCDebug(dcZigbeeNode()) << " Cluster ID:" << ZigbeeUtils::convertUint16ToHexString(clusterId) << ZigbeeUtils::clusterIdToString(static_cast(clusterId)); - } - - m_uninitializedEndpoints.removeAll(endpointId); - - // Create endpoint - - - if (m_uninitializedEndpoints.isEmpty()) { - setState(StateInitialized); - } - }); + // Continue with the basic cluster attributes + initBasicCluster(); } }); + } + }); +} + +void ZigbeeNodeDeconz::initBasicCluster() +{ + + + + setState(StateInitialized); + +} + +void ZigbeeNodeDeconz::setClusterAttributeReport(const ZigbeeClusterAttributeReport &report) +{ + Q_UNUSED(report) +} + +void ZigbeeNodeDeconz::startInitialization() +{ + setState(StateInitializing); + + /* Node initialisation steps (sequentially) + * - Node descriptor + * - Power descriptor + * - Active endpoints + * - for each endpoint do: + * - Simple descriptor request + * - for each endpoint + * - read basic cluster + */ + + initNodeDescriptor(); + + /* + + }); }); + */ } ZigbeeNodeEndpoint *ZigbeeNodeDeconz::createNodeEndpoint(quint8 endpointId, QObject *parent) { - Q_UNUSED(endpointId) - Q_UNUSED(parent) - return nullptr; + return qobject_cast(new ZigbeeNodeEndpointDeconz(m_network, this, endpointId, parent)); } diff --git a/libnymea-zigbee/deconz/zigbeenodedeconz.h b/libnymea-zigbee/deconz/zigbeenodedeconz.h index 704a144..ce6692d 100644 --- a/libnymea-zigbee/deconz/zigbeenodedeconz.h +++ b/libnymea-zigbee/deconz/zigbeenodedeconz.h @@ -33,6 +33,7 @@ #include "zigbee.h" #include "zigbeenode.h" +class ZigbeeNodeEndpoint; class ZigbeeNetworkDeconz; class ZigbeeNodeDeconz : public ZigbeeNode @@ -54,6 +55,12 @@ public: private: ZigbeeNetworkDeconz *m_network = nullptr; + // Init methods + void initNodeDescriptor(); + void initPowerDescriptor(); + void initEndpoints(); + void initBasicCluster(); + QList m_uninitializedEndpoints; QList m_uninitalizedBasicClusterAttributes; diff --git a/libnymea-zigbee/deconz/zigbeenodeendpointdeconz.cpp b/libnymea-zigbee/deconz/zigbeenodeendpointdeconz.cpp index d7f6ba3..98b3675 100644 --- a/libnymea-zigbee/deconz/zigbeenodeendpointdeconz.cpp +++ b/libnymea-zigbee/deconz/zigbeenodeendpointdeconz.cpp @@ -26,8 +26,32 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "zigbeenodeendpointdeconz.h" +#include "zigbeenodeendpoint.h" -ZigbeeNodeEndpointDeconz::ZigbeeNodeEndpointDeconz(QObject *parent) : QObject(parent) +ZigbeeNodeEndpointDeconz::ZigbeeNodeEndpointDeconz(ZigbeeNetworkDeconz *network, ZigbeeNode *node, quint8 endpointId, QObject *parent) : + ZigbeeNodeEndpoint(node, endpointId, parent), + m_network(network), + m_node(node) { } + +ZigbeeNetworkReply *ZigbeeNodeEndpointDeconz::readAttribute(ZigbeeCluster *cluster, QList attributes) +{ + Q_UNUSED(cluster) + Q_UNUSED(attributes) + return nullptr; +} + +ZigbeeNetworkReply *ZigbeeNodeEndpointDeconz::configureReporting(ZigbeeCluster *cluster, QList reportConfigurations) +{ + Q_UNUSED(cluster) + Q_UNUSED(reportConfigurations) + return nullptr; +} + +void ZigbeeNodeEndpointDeconz::setClusterAttribute(Zigbee::ClusterId clusterId, const ZigbeeClusterAttribute &attribute) +{ + Q_UNUSED(clusterId) + Q_UNUSED(attribute) +} diff --git a/libnymea-zigbee/deconz/zigbeenodeendpointdeconz.h b/libnymea-zigbee/deconz/zigbeenodeendpointdeconz.h index 92dd2eb..fde0f21 100644 --- a/libnymea-zigbee/deconz/zigbeenodeendpointdeconz.h +++ b/libnymea-zigbee/deconz/zigbeenodeendpointdeconz.h @@ -29,12 +29,30 @@ #define ZIGBEENODEENDPOINTDECONZ_H #include +#include "zigbeenodeendpoint.h" -class ZigbeeNodeEndpointDeconz : public QObject +class ZigbeeNodeDeconz; +class ZigbeeNetworkDeconz; + +class ZigbeeNodeEndpointDeconz : public ZigbeeNodeEndpoint { Q_OBJECT + + friend class ZigbeeNodeDeconz; + public: - explicit ZigbeeNodeEndpointDeconz(QObject *parent = nullptr); + explicit ZigbeeNodeEndpointDeconz(ZigbeeNetworkDeconz *network, ZigbeeNode *node, quint8 endpointId, QObject *parent = nullptr); + + ZigbeeNetworkReply *readAttribute(ZigbeeCluster *cluster, QList attributes) override; + ZigbeeNetworkReply *configureReporting(ZigbeeCluster *cluster, QList reportConfigurations) override; + +protected: + // Cluster commands + void setClusterAttribute(Zigbee::ClusterId clusterId, const ZigbeeClusterAttribute &attribute = ZigbeeClusterAttribute()) override; + +private: + ZigbeeNetworkDeconz *m_network = nullptr; + ZigbeeNode *m_node = nullptr; signals: diff --git a/libnymea-zigbee/libnymea-zigbee.pro b/libnymea-zigbee/libnymea-zigbee.pro index 0a4776a..b04e37d 100644 --- a/libnymea-zigbee/libnymea-zigbee.pro +++ b/libnymea-zigbee/libnymea-zigbee.pro @@ -23,6 +23,7 @@ SOURCES += \ zigbeechannelmask.cpp \ zigbeecluster.cpp \ zigbeeclusterattribute.cpp \ + zigbeeclusterlibrary.cpp \ zigbeedeviceprofile.cpp \ zigbeemanufacturer.cpp \ zigbeenetwork.cpp \ @@ -59,6 +60,7 @@ HEADERS += \ zigbeechannelmask.h \ zigbeecluster.h \ zigbeeclusterattribute.h \ + zigbeeclusterlibrary.h \ zigbeedeviceprofile.h \ zigbeemanufacturer.h \ zigbeenetwork.h \ diff --git a/libnymea-zigbee/zigbeeclusterlibrary.cpp b/libnymea-zigbee/zigbeeclusterlibrary.cpp new file mode 100644 index 0000000..e0fb7b2 --- /dev/null +++ b/libnymea-zigbee/zigbeeclusterlibrary.cpp @@ -0,0 +1,176 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* 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 "zigbeeclusterlibrary.h" +#include "zigbeeutils.h" + +#include + +quint8 ZigbeeClusterLibrary::buildFrameControlByte(const ZigbeeClusterLibrary::FrameControl &frameControl) +{ + quint8 byte = 0x00; + + // Bit 0-1 + byte |= FrameTypeClusterSpecific; + + // Bit 2 + if (frameControl.manufacturerSpecific) + byte |= 0x01 << 2; + + // Bit 3 + if (frameControl.direction == DirectionServerToClient) + byte |= 0x01 << 3; + + // Bit 4 + if (frameControl.disableDefaultResponse) + byte |= 0x01 << 4; + + return byte; +} + +ZigbeeClusterLibrary::FrameControl ZigbeeClusterLibrary::parseFrameControlByte(quint8 frameControlByte) +{ + FrameControl frameControl; + + if (ZigbeeUtils::checkBitUint8(frameControlByte, 0)) { + frameControl.frameType = FrameTypeClusterSpecific; + } else { + frameControl.frameType = FrameTypeGlobal; + } + + frameControl.manufacturerSpecific = ZigbeeUtils::checkBitUint8(frameControlByte, 2); + + if (ZigbeeUtils::checkBitUint8(frameControlByte, 3)) { + frameControl.direction = DirectionServerToClient; + } else { + frameControl.direction = DirectionClientToServer; + } + + frameControl.disableDefaultResponse = ZigbeeUtils::checkBitUint8(frameControlByte, 4); + + return frameControl; +} + +QByteArray ZigbeeClusterLibrary::buildHeader(const ZigbeeClusterLibrary::Header &header) +{ + QByteArray headerData; + QDataStream stream(&headerData, QIODevice::WriteOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream << buildFrameControlByte(header.frameControl); + + // Include manufacturer only if the frame control indicates manufacturer specific + if (header.frameControl.manufacturerSpecific) { + stream << header.manufacturerCode; + } + + stream << header.transactionSequenceNumber; + stream << static_cast(header.command); + + return headerData; +} + +ZigbeeClusterLibrary::Frame ZigbeeClusterLibrary::parseFrameData(const QByteArray &frameData) +{ + QDataStream stream(frameData); + stream.setByteOrder(QDataStream::LittleEndian); + + // Read the header and then the payload + quint8 offset = 0; + quint8 frameControlByte = 0; + quint8 commandByte = 0; + + Header header; + stream >> frameControlByte; + offset += 1; + + header.frameControl = parseFrameControlByte(frameControlByte); + if (header.frameControl.manufacturerSpecific) { + stream >> header.manufacturerCode; + offset += 2; + } + + stream >> header.transactionSequenceNumber; + offset += 1; + + + stream >> commandByte; + offset += 1; + + header.command = static_cast(commandByte); + offset += 1; + + Frame frame; + frame.header = header; + frame.payload = frameData.right(frameData.length() - offset); + return frame; +} + +QByteArray ZigbeeClusterLibrary::buildFrame(const ZigbeeClusterLibrary::Frame &frame) +{ + return buildHeader(frame.header) + frame.payload; +} + +QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::FrameControl &frameControl) +{ + debug.nospace() << "FrameControl("; + if (frameControl.frameType == ZigbeeClusterLibrary::FrameTypeGlobal) { + debug.nospace() << "Frame Type: Global" << ", "; + } else { + debug.nospace() << "Frame Type: Cluster specific" << ", "; + } + + debug.nospace() << "Manufacturer specific: " << (frameControl.manufacturerSpecific ? "1" : "0") << ", "; + debug.nospace() << "Direction: "; + if (frameControl.direction == ZigbeeClusterLibrary::DirectionClientToServer) { + debug.nospace() << "Client to server, "; + } else { + debug.nospace() << "Server to client, "; + } + + debug.nospace() << "Disable default response: " << (frameControl.disableDefaultResponse ? "1" : "0") << ")"; + return debug.space(); +} + +QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::Header &header) +{ + debug.nospace() << "Header("; + debug.nospace() << header.frameControl; + if (header.frameControl.manufacturerSpecific) { + debug.nospace() << "Manufacturer code: " << ZigbeeUtils::convertUint16ToHexString(header.manufacturerCode) << ", "; + } + debug.nospace() << "TSN:" << header.transactionSequenceNumber << ", "; + debug.nospace() << header.command << ")"; + return debug.space(); +} + +QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::Frame &frame) +{ + debug.nospace() << "Zigbee Cluster Library Frame("; + debug.nospace() << frame.header; + debug.nospace() << ZigbeeUtils::convertByteArrayToHexString(frame.payload) << ")"; + return debug.space(); +} diff --git a/libnymea-zigbee/zigbeeclusterlibrary.h b/libnymea-zigbee/zigbeeclusterlibrary.h new file mode 100644 index 0000000..792426b --- /dev/null +++ b/libnymea-zigbee/zigbeeclusterlibrary.h @@ -0,0 +1,112 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* 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 ZIGBEECLUSTERLIBRARY_H +#define ZIGBEECLUSTERLIBRARY_H + +#include +#include + +class ZigbeeClusterLibrary +{ + Q_GADGET +public: + enum Command { + CommandReadAttributes = 0x00, + CommandReadAttributesResponse = 0x01, + CommandWriteAttributes = 0x02, + CommandWriteAttributesUndivided = 0x03, + CommandWriteAttributesResponse = 0x04, + CommandWriteAttributesNoResponse = 0x05, + CommandConfigureReporting = 0x06, + CommandConfigureReportingResponse = 0x07, + CommandReadReportingConfiguration = 0x08, + CommandReadReportingConfigurationResponse = 0x09, + CommandReportAttributes = 0x0a, + CommandDefaultResponse = 0x0b, + CommandDiscoverAttributes = 0x0c, + CommandDiscoverAttributesResponse = 0x0d, + CommandReadAttributesStructured = 0x0e, + CommandWriteAttributesStructured = 0x0f, + CommandWriteAttributesStructuredResponse = 0x10, + CommandDiscoverCommandsReceived = 0x11, + CommandDiscoverCommandsReceivedResponse = 0x12, + CommandDiscoverCommandsGenerated = 0x13, + CommandDiscoverCommandsGeneratedResponse = 0x14, + CommandDiscoverAttributesExtended = 0x15, + CommandDiscoverAttributesExtendedResponse = 0x16 + }; + Q_ENUM(Command) + + // Frame control field + enum FrameType { + FrameTypeGlobal = 0x00, + FrameTypeClusterSpecific = 0x01 + }; + Q_ENUM(FrameType) + + enum Direction { + DirectionClientToServer = 0x00, + DirectionServerToClient = 0x01 + }; + Q_ENUM(Direction) + + typedef struct FrameControl { + FrameType frameType = FrameTypeClusterSpecific; + bool manufacturerSpecific = false; + Direction direction = DirectionClientToServer; + bool disableDefaultResponse = false; + } FrameControl; + + typedef struct Header { + FrameControl frameControl; + quint16 manufacturerCode = 0; + quint8 transactionSequenceNumber = 0; + Command command; + } ZclHeader; + + typedef struct Frame { + Header header; + QByteArray payload; + } Frame; + + // General parse/build methods + static quint8 buildFrameControlByte(const FrameControl &frameControl); + static FrameControl parseFrameControlByte(quint8 frameControlByte); + + static QByteArray buildHeader(const Header &header); + + static Frame parseFrameData(const QByteArray &frameData); + static QByteArray buildFrame(const Frame &frame); +}; + +QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::FrameControl &frameControl); +QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::Header &header); +QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::Frame &frame); + + +#endif // ZIGBEECLUSTERLIBRARY_H diff --git a/libnymea-zigbee/zigbeenodeendpoint.h b/libnymea-zigbee/zigbeenodeendpoint.h index a289bf6..4b1a0b2 100644 --- a/libnymea-zigbee/zigbeenodeendpoint.h +++ b/libnymea-zigbee/zigbeenodeendpoint.h @@ -78,30 +78,30 @@ public: virtual ZigbeeNetworkReply *configureReporting(ZigbeeCluster *cluster, QList reportConfigurations) = 0; // Identify - virtual ZigbeeNetworkReply *identify(quint16 seconds) = 0; + virtual ZigbeeNetworkReply *identify(quint16 seconds); // Reset - virtual ZigbeeNetworkReply *factoryReset() = 0; + virtual ZigbeeNetworkReply *factoryReset(); // Binding - virtual ZigbeeNetworkReply *bindGroup(Zigbee::ClusterId clusterId, quint16 destinationAddress, quint8 destinationEndpoint) = 0; - virtual ZigbeeNetworkReply *bindUnicast(Zigbee::ClusterId clusterId, const ZigbeeAddress &destinationAddress, quint8 destinationEndpoint) = 0; + virtual ZigbeeNetworkReply *bindGroup(Zigbee::ClusterId clusterId, quint16 destinationAddress, quint8 destinationEndpoint); + virtual ZigbeeNetworkReply *bindUnicast(Zigbee::ClusterId clusterId, const ZigbeeAddress &destinationAddress, quint8 destinationEndpoint); // Cluster commands - virtual ZigbeeNetworkReply *sendOnOffClusterCommand(ZigbeeCluster::OnOffClusterCommand command) = 0; + virtual ZigbeeNetworkReply *sendOnOffClusterCommand(ZigbeeCluster::OnOffClusterCommand command); // Group commands - virtual ZigbeeNetworkReply *addGroup(quint8 destinationEndpoint, quint16 groupAddress) = 0; + virtual ZigbeeNetworkReply *addGroup(quint8 destinationEndpoint, quint16 groupAddress); // Level commands - virtual ZigbeeNetworkReply *sendLevelCommand(ZigbeeCluster::LevelClusterCommand command, quint8 level, bool triggersOnOff, quint16 transitionTime) = 0; + virtual ZigbeeNetworkReply *sendLevelCommand(ZigbeeCluster::LevelClusterCommand command, quint8 level, bool triggersOnOff, quint16 transitionTime); // Color commands - virtual ZigbeeNetworkReply *sendMoveToColorTemperature(quint16 colourTemperature, quint16 transitionTime) = 0; - virtual ZigbeeNetworkReply *sendMoveToColor(double x, double y, quint16 transitionTime) = 0; - virtual ZigbeeNetworkReply *sendMoveToHueSaturation(quint8 hue, quint8 saturation, quint16 transitionTime) = 0; - virtual ZigbeeNetworkReply *sendMoveToHue(quint8 hue, quint16 transitionTime) = 0; - virtual ZigbeeNetworkReply *sendMoveToSaturation(quint8 saturation, quint16 transitionTime) = 0; + virtual ZigbeeNetworkReply *sendMoveToColorTemperature(quint16 colourTemperature, quint16 transitionTime); + virtual ZigbeeNetworkReply *sendMoveToColor(double x, double y, quint16 transitionTime); + virtual ZigbeeNetworkReply *sendMoveToHueSaturation(quint8 hue, quint8 saturation, quint16 transitionTime); + virtual ZigbeeNetworkReply *sendMoveToHue(quint8 hue, quint16 transitionTime); + virtual ZigbeeNetworkReply *sendMoveToSaturation(quint8 saturation, quint16 transitionTime); private: ZigbeeNode *m_node = nullptr; diff --git a/libnymea-zigbee/zigbeeutils.h b/libnymea-zigbee/zigbeeutils.h index bd39d62..2460f97 100644 --- a/libnymea-zigbee/zigbeeutils.h +++ b/libnymea-zigbee/zigbeeutils.h @@ -38,15 +38,6 @@ #include "zigbee.h" #include "zigbeecluster.h" -template inline TYPE ZigbeeBit(const TYPE & x) -{ - return TYPE(1) << x; -} - -template inline bool ZigbeeIsBitSet(const TYPE & x, const TYPE & y) -{ - return (x & y) != 0; -} class ZigbeeUtils {