Add ZCL basics

This commit is contained in:
Simon Stürz 2020-04-15 17:59:01 +02:00
parent 882023198a
commit 3e8da8ee26
15 changed files with 590 additions and 184 deletions

View File

@ -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)

View File

@ -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();
};

View File

@ -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();
});
}

View File

@ -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;

View File

@ -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();
}
}

View File

@ -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;

View File

@ -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));
}

View File

@ -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;

View File

@ -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)
}

View File

@ -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:

View File

@ -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 \

View 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();
}

View 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

View File

@ -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;

View File

@ -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
{