Add ZCL basics
This commit is contained in:
parent
882023198a
commit
3e8da8ee26
@ -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)
|
||||
|
||||
@ -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();
|
||||
|
||||
};
|
||||
|
||||
@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -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<quint8, ZigbeeInterfaceDeconzReply *> m_pendingReplies;
|
||||
DeconzNetworkConfiguration m_networkConfiguration;
|
||||
Deconz::NetworkState m_networkState = Deconz::NetworkStateOffline;
|
||||
|
||||
@ -54,8 +54,9 @@ ZigbeeNetworkDeconz::ZigbeeNetworkDeconz(QObject *parent) :
|
||||
|
||||
ZigbeeBridgeController *ZigbeeNetworkDeconz::bridgeController() const
|
||||
{
|
||||
if (m_controller)
|
||||
if (m_controller) {
|
||||
return qobject_cast<ZigbeeBridgeController *>(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<ZigbeeNodeDeconz *>(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<quint8>(reply->responseData().at(3));
|
||||
quint8 minorVersion = static_cast<quint8>(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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -29,6 +29,8 @@
|
||||
#include "zigbeenodedeconz.h"
|
||||
#include "zigbeedeviceprofile.h"
|
||||
#include "zigbeenetworkdeconz.h"
|
||||
#include "zigbeenodeendpointdeconz.h"
|
||||
|
||||
#include "loggingcategory.h"
|
||||
|
||||
#include <QDataStream>
|
||||
@ -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<quint16> inputClusters;
|
||||
QList<quint16> 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<Zigbee::ZigbeeProfile>(profileId));
|
||||
if (profileId == Zigbee::ZigbeeProfileLightLink) {
|
||||
qCDebug(dcZigbeeNode()) << " Device ID:" << ZigbeeUtils::convertUint16ToHexString(deviceId) << static_cast<Zigbee::LightLinkDevice>(deviceId);
|
||||
} else if (profileId == Zigbee::ZigbeeProfileHomeAutomation) {
|
||||
qCDebug(dcZigbeeNode()) << " Device ID:" << ZigbeeUtils::convertUint16ToHexString(deviceId) << static_cast<Zigbee::HomeAutomationDevice>(deviceId);
|
||||
} else if (profileId == Zigbee::ZigbeeProfileGreenPower) {
|
||||
qCDebug(dcZigbeeNode()) << " Device ID:" << ZigbeeUtils::convertUint16ToHexString(deviceId) << static_cast<Zigbee::GreenPowerDevice>(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<ZigbeeNodeEndpointDeconz *>(createNodeEndpoint(endpointId, this));
|
||||
m_endpoints.append(endpoint);
|
||||
} else {
|
||||
endpoint = qobject_cast<ZigbeeNodeEndpointDeconz *>(getEndpoint(endpointId));
|
||||
}
|
||||
endpoint->setProfile(static_cast<Zigbee::ZigbeeProfile>(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<Zigbee::ClusterId>(clusterId))) {
|
||||
endpoint->addInputCluster(new ZigbeeCluster(static_cast<Zigbee::ClusterId>(clusterId), ZigbeeCluster::Input, endpoint));
|
||||
}
|
||||
qCDebug(dcZigbeeNode()) << " Cluster ID:" << ZigbeeUtils::convertUint16ToHexString(clusterId) << ZigbeeUtils::clusterIdToString(static_cast<Zigbee::ClusterId>(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<Zigbee::ClusterId>(clusterId))) {
|
||||
endpoint->addOutputCluster(new ZigbeeCluster(static_cast<Zigbee::ClusterId>(clusterId), ZigbeeCluster::Output, endpoint));
|
||||
}
|
||||
qCDebug(dcZigbeeNode()) << " Cluster ID:" << ZigbeeUtils::convertUint16ToHexString(clusterId) << ZigbeeUtils::clusterIdToString(static_cast<Zigbee::ClusterId>(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<quint16> inputClusters;
|
||||
QList<quint16> 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<Zigbee::ZigbeeProfile>(profileId));
|
||||
if (profileId == Zigbee::ZigbeeProfileLightLink) {
|
||||
qCDebug(dcZigbeeNode()) << " Device ID:" << ZigbeeUtils::convertUint16ToHexString(deviceId) << static_cast<Zigbee::LightLinkDevice>(deviceId);
|
||||
} else if (profileId == Zigbee::ZigbeeProfileHomeAutomation) {
|
||||
qCDebug(dcZigbeeNode()) << " Device ID:" << ZigbeeUtils::convertUint16ToHexString(deviceId) << static_cast<Zigbee::HomeAutomationDevice>(deviceId);
|
||||
} else if (profileId == Zigbee::ZigbeeProfileGreenPower) {
|
||||
qCDebug(dcZigbeeNode()) << " Device ID:" << ZigbeeUtils::convertUint16ToHexString(deviceId) << static_cast<Zigbee::GreenPowerDevice>(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<Zigbee::ClusterId>(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<Zigbee::ClusterId>(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<ZigbeeNodeEndpoint *>(new ZigbeeNodeEndpointDeconz(m_network, this, endpointId, parent));
|
||||
}
|
||||
|
||||
@ -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<quint8> m_uninitializedEndpoints;
|
||||
QList<quint16> m_uninitalizedBasicClusterAttributes;
|
||||
|
||||
|
||||
@ -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<quint16> attributes)
|
||||
{
|
||||
Q_UNUSED(cluster)
|
||||
Q_UNUSED(attributes)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ZigbeeNetworkReply *ZigbeeNodeEndpointDeconz::configureReporting(ZigbeeCluster *cluster, QList<ZigbeeClusterReportConfigurationRecord> reportConfigurations)
|
||||
{
|
||||
Q_UNUSED(cluster)
|
||||
Q_UNUSED(reportConfigurations)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ZigbeeNodeEndpointDeconz::setClusterAttribute(Zigbee::ClusterId clusterId, const ZigbeeClusterAttribute &attribute)
|
||||
{
|
||||
Q_UNUSED(clusterId)
|
||||
Q_UNUSED(attribute)
|
||||
}
|
||||
|
||||
@ -29,12 +29,30 @@
|
||||
#define ZIGBEENODEENDPOINTDECONZ_H
|
||||
|
||||
#include <QObject>
|
||||
#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<quint16> attributes) override;
|
||||
ZigbeeNetworkReply *configureReporting(ZigbeeCluster *cluster, QList<ZigbeeClusterReportConfigurationRecord> 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:
|
||||
|
||||
|
||||
@ -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 \
|
||||
|
||||
176
libnymea-zigbee/zigbeeclusterlibrary.cpp
Normal file
176
libnymea-zigbee/zigbeeclusterlibrary.cpp
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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 <QDataStream>
|
||||
|
||||
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<quint8>(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<Command>(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();
|
||||
}
|
||||
112
libnymea-zigbee/zigbeeclusterlibrary.h
Normal file
112
libnymea-zigbee/zigbeeclusterlibrary.h
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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 <QObject>
|
||||
#include <QDebug>
|
||||
|
||||
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
|
||||
@ -78,30 +78,30 @@ public:
|
||||
virtual ZigbeeNetworkReply *configureReporting(ZigbeeCluster *cluster, QList<ZigbeeClusterReportConfigurationRecord> 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;
|
||||
|
||||
@ -38,15 +38,6 @@
|
||||
#include "zigbee.h"
|
||||
#include "zigbeecluster.h"
|
||||
|
||||
template<class TYPE> inline TYPE ZigbeeBit(const TYPE & x)
|
||||
{
|
||||
return TYPE(1) << x;
|
||||
}
|
||||
|
||||
template<class TYPE> inline bool ZigbeeIsBitSet(const TYPE & x, const TYPE & y)
|
||||
{
|
||||
return (x & y) != 0;
|
||||
}
|
||||
|
||||
class ZigbeeUtils
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user