Introduce channel mask class and continue deCONZ UART protocol

This commit is contained in:
Simon Stürz 2020-04-05 16:13:30 +02:00
parent 95caaab5ce
commit 6449654820
14 changed files with 578 additions and 117 deletions

View File

@ -93,6 +93,16 @@ public:
};
Q_ENUM(NodeType)
enum SourceAddressMode {
SourceAddressModeNone = 0x00,
SourceAddressModeShortSourceAddress = 0x01,
SourceAddressModeAddLastHoppAddress = 0x02, // since 0x0108
SourceAddressModeIeeeSourceAddress = 0x03,
SourceAddressModeShortAndIeeeSourceAddress = 0x04 // since 0x010B
};
Q_ENUM(SourceAddressMode)
enum SecurityMode {
SecurityModeNoSecurity = 0x00,
SecurityModePreconfiguredNetworkKey = 0x01,
@ -109,4 +119,5 @@ public:
};
#endif // DECONZ_H

View File

@ -47,6 +47,17 @@ Deconz::StatusCode ZigbeeInterfaceDeconzReply::statusCode() const
return m_statusCode;
}
bool ZigbeeInterfaceDeconzReply::aborted() const
{
return m_aborted;
}
void ZigbeeInterfaceDeconzReply::abort()
{
m_aborted = true;
emit finished();
}
ZigbeeInterfaceDeconzReply::ZigbeeInterfaceDeconzReply(Deconz::Command command, quint8 sequenceNumber, QObject *parent) :
QObject(parent),
m_command(command),

View File

@ -47,9 +47,14 @@ public:
// Response content
Deconz::StatusCode statusCode() const;
bool aborted() const;
void abort();
private:
explicit ZigbeeInterfaceDeconzReply(Deconz::Command command, quint8 sequenceNumber, QObject *parent = nullptr);
bool m_aborted = false;
// Request content
Deconz::Command m_command;
quint8 m_sequenceNumber = 0;

View File

@ -41,7 +41,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
connect(m_watchdogTimer, &QTimer::timeout, this, &ZigbeeBridgeControllerDeconz::onWatchdogTimerTimeout);
connect(m_watchdogTimer, &QTimer::timeout, this, &ZigbeeBridgeControllerDeconz::resetControllerWatchdog);
}
ZigbeeBridgeControllerDeconz::~ZigbeeBridgeControllerDeconz()
@ -49,6 +49,17 @@ ZigbeeBridgeControllerDeconz::~ZigbeeBridgeControllerDeconz()
qCDebug(dcZigbeeController()) << "Destroy controller";
}
DeconzNetworkConfiguration ZigbeeBridgeControllerDeconz::networkConfiguration() const
{
return m_networkConfiguration;
}
void ZigbeeBridgeControllerDeconz::setFirmwareVersionString(const QString &firmwareVersion)
{
m_firmwareVersion = firmwareVersion;
emit firmwareVersionChanged(m_firmwareVersion);
}
ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestVersion()
{
quint8 sequenceNumber = generateSequenceNumber();
@ -120,14 +131,15 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestWriteParameter(
quint8 sequenceNumber = generateSequenceNumber();
qCDebug(dcZigbeeController()) << "Request write parameter. SQN:" << sequenceNumber << parameter << ZigbeeUtils::convertByteArrayToHexString(data);
quint16 payloadLength = static_cast<quint16>(1 + data.length());
QByteArray message;
QDataStream stream(&message, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << static_cast<quint8>(Deconz::CommandWriteParameter);
stream << static_cast<quint8>(sequenceNumber);
stream << static_cast<quint8>(0); // Reserverd
stream << static_cast<quint16>(7 + 1 + data.length()); // Frame length 7 + 1 parameter + payload length
stream << static_cast<quint16>(1 + data.length()); // 1 parameter + payload length
stream << static_cast<quint16>(7 + payloadLength); // Frame length 7 + payload length
stream << static_cast<quint16>(payloadLength); // 1 parameter + parameter data length
stream << static_cast<quint8>(parameter);
for (int i = 0; i < data.length(); i++) {
stream << static_cast<quint8>(data.at(i));
@ -157,6 +169,31 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestChangeNetworkSt
return createReply(Deconz::CommandChangeNetworkState, sequenceNumber, this);
}
ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestReadReceivedDataIndication(Deconz::SourceAddressMode sourceAddressMode)
{
quint8 sequenceNumber = generateSequenceNumber();
qCDebug(dcZigbeeController()) << "Request read received data indication. SQN:" << sequenceNumber << ZigbeeUtils::convertByteToHexString(sourceAddressMode);
quint16 payloadLength = 0;
if (sourceAddressMode != Deconz::SourceAddressModeNone) {
payloadLength = 1;
}
QByteArray message;
QDataStream stream(&message, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << static_cast<quint8>(Deconz::CommandApsDataIndication);
stream << static_cast<quint8>(sequenceNumber);
stream << static_cast<quint8>(0); // Reserverd
stream << static_cast<quint16>(7 + payloadLength); // Frame length + payload length
stream << static_cast<quint16>(payloadLength); // payload length
if (payloadLength > 0)
stream << static_cast<quint8>(sourceAddressMode);
m_interface->sendPackage(message);
return createReply(Deconz::CommandApsDataIndication, sequenceNumber, this);
}
quint8 ZigbeeBridgeControllerDeconz::generateSequenceNumber()
{
@ -464,6 +501,7 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters(
m_watchdogTimer->stop();
// 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;
readNetworkParametersReply->finished();
return;
@ -491,10 +529,11 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters(
// Note: this value describes how much seconds are left until the watchdog triggers. Reset it right the way
if (watchdogTimeout < 15) {
onWatchdogTimerTimeout();
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;
readNetworkParametersReply->finished();
});
@ -515,20 +554,121 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters(
return readNetworkParametersReply;
}
ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::resetWatchdog()
DeconzDeviceState ZigbeeBridgeControllerDeconz::parseDeviceStateFlag(quint8 deviceStateFlag)
{
QByteArray parameterData;
QDataStream stream(&parameterData, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << m_watchdogTimeout;
return requestWriteParameter(Deconz::ParameterWatchdogTtl, parameterData);
DeconzDeviceState state;
state.networkState = static_cast<Deconz::NetworkState>(deviceStateFlag & 0x03);
state.aspDataConfirm = (deviceStateFlag & 0x04);
state.aspDataIndication = (deviceStateFlag & 0x08);
state.configurationChanged = (deviceStateFlag & 0x10);
state.aspDataRequestFreeSlots = (deviceStateFlag & 0x20);
return state;
}
void ZigbeeBridgeControllerDeconz::processDeviceState(DeconzDeviceState deviceState)
{
qCDebug(dcZigbeeController()) << "Device state changed notification:" << deviceState.networkState
<< "ASPDE-DATA.confirm:" << deviceState.aspDataConfirm
<< "ASPDE-DATA.indication:" << deviceState.aspDataIndication
<< "configuration changed:" << deviceState.configurationChanged
<< "ASPDE-DATA.request free slots:" << deviceState.aspDataRequestFreeSlots;
// Check if we have to fech new data
if (deviceState.aspDataConfirm) {
ZigbeeInterfaceDeconzReply *reply = requestReadReceivedDataIndication();
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){
if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Could not read data indication." << reply->statusCode();
// FIXME: set an appropriate error
return;
}
// ASP data indication received
QDataStream stream(reply->responseData());
stream.setByteOrder(QDataStream::LittleEndian);
quint16 payloadLenght = 0; quint8 deviceStateFlag = 0; quint8 destinationAddressModeFlag = 0;
quint16 destinationShortAddress = 0; quint64 destinationIeeeAddress = 0; quint8 destinationEndpoint = 0;
quint8 sourceAddressModeFlag = 0; quint16 sourceShortAddress = 0; quint64 sourceIeeeAddress = 0; quint8 sourceEndpoint = 0;
quint16 profileId = 0; quint16 clusterId = 0; quint16 asduLength = 0; QByteArray asdu; quint8 reserved = 0;
quint8 lqi = 0; qint8 rssi = 0;
stream >> payloadLenght >> deviceStateFlag >> destinationAddressModeFlag;
Zigbee::DestinationAddressMode destinationAddressMode = static_cast<Zigbee::DestinationAddressMode>(destinationAddressModeFlag);
if (destinationAddressMode == Zigbee::DestinationAddressModeGroup || destinationAddressMode == Zigbee::DestinationAddressModeShortAddress)
stream >> destinationShortAddress;
if (destinationAddressMode == Zigbee::DestinationAddressModeUnicastIeee)
stream >> destinationIeeeAddress;
stream >> destinationEndpoint >> sourceAddressModeFlag;
Zigbee::SourceAddressMode sourceAddressMode = static_cast<Zigbee::SourceAddressMode>(sourceAddressModeFlag);
if (sourceAddressMode == Zigbee::SourceAddressModeShortAddress || sourceAddressMode == Zigbee::SourceAddressModeShortAndIeeeAddress)
stream >> sourceShortAddress;
if (sourceAddressMode == Zigbee::SourceAddressModeIeeeAddress || sourceAddressMode == Zigbee::SourceAddressModeShortAndIeeeAddress)
stream >> sourceIeeeAddress;
stream >> sourceEndpoint >> profileId >> clusterId >> asduLength;
// Fill asdu data
for (int i = 0; i < asduLength; i++) {
quint8 byte = 0;
stream >> byte;
asdu.append(static_cast<char>(byte));
}
stream >> reserved >> reserved >> lqi >> reserved >> reserved >> reserved >> reserved >> rssi;
qCDebug(dcZigbeeController()) << "Data indication received:";
qCDebug(dcZigbeeController()) << " Destination address mode:" << destinationAddressMode;
if (destinationAddressMode == Zigbee::DestinationAddressModeGroup)
qCDebug(dcZigbeeController()) << " Destination address (group):" << ZigbeeUtils::convertUint16ToHexString(destinationShortAddress);
if (destinationAddressMode == Zigbee::DestinationAddressModeShortAddress)
qCDebug(dcZigbeeController()) << " Destination short address:" << ZigbeeUtils::convertUint16ToHexString(destinationShortAddress);
if (destinationAddressMode == Zigbee::DestinationAddressModeUnicastIeee)
qCDebug(dcZigbeeController()) << " Destination IEEE address:" << ZigbeeAddress(destinationIeeeAddress).toString();
qCDebug(dcZigbeeController()) << " Destination endpoint" << ZigbeeUtils::convertByteToHexString(destinationEndpoint);
qCDebug(dcZigbeeController()) << " Source address mode:" << sourceAddressMode;
if (sourceAddressMode == Zigbee::SourceAddressModeShortAddress || sourceAddressMode == Zigbee::SourceAddressModeShortAndIeeeAddress)
qCDebug(dcZigbeeController()) << " Source address:" << ZigbeeUtils::convertUint16ToHexString(sourceShortAddress);
if (sourceAddressMode == Zigbee::SourceAddressModeIeeeAddress || sourceAddressMode == Zigbee::SourceAddressModeShortAndIeeeAddress)
qCDebug(dcZigbeeController()) << " Source IEEE address:" << ZigbeeAddress(sourceIeeeAddress).toString();
qCDebug(dcZigbeeController()) << " Source endpoint:" << ZigbeeUtils::convertByteToHexString(sourceEndpoint);
qCDebug(dcZigbeeController()) << " Profile:" << static_cast<Zigbee::ZigbeeProfile>(profileId);
qCDebug(dcZigbeeController()) << " Cluster:" << static_cast<Zigbee::ClusterId>(clusterId);
qCDebug(dcZigbeeController()) << " ASDU:" << ZigbeeUtils::convertByteArrayToHexString(asdu);
qCDebug(dcZigbeeController()) << " LQI:" << lqi;
qCDebug(dcZigbeeController()) << " RSSI:" << rssi << "dBm";
processDeviceState(parseDeviceStateFlag(deviceStateFlag));
});
}
}
void ZigbeeBridgeControllerDeconz::onInterfaceAvailableChanged(bool available)
{
if (available) {
// FIXME: only start if the protocol version is >= 0x0108
m_watchdogTimer->start();
} else {
// Clean up any pending replies
foreach (quint8 id, m_pendingReplies.keys()) {
ZigbeeInterfaceDeconzReply *reply = m_pendingReplies.take(id);
reply->abort();
}
m_watchdogTimer->stop();
}
@ -539,20 +679,20 @@ void ZigbeeBridgeControllerDeconz::onInterfacePackageReceived(const QByteArray &
{
QDataStream stream(package);
stream.setByteOrder(QDataStream::LittleEndian);
quint8 command = 0; quint8 sequenceNumber = 0; quint8 status = 0; quint16 frameLength = 0;
stream >> command >> sequenceNumber >> status >> frameLength;
quint8 commandInt = 0; quint8 sequenceNumber = 0; quint8 statusInt = 0; quint16 frameLength = 0;
stream >> commandInt >> sequenceNumber >> statusInt >> frameLength;
qCDebug(dcZigbeeController()) << "Interface message received"
<< static_cast<Deconz::Command>(command)
<< "SQN:" << sequenceNumber
<< static_cast<Deconz::StatusCode>(status)
<< "Frame length:" << frameLength;
QByteArray data = package.right(package.length() - 5);
Deconz::Command command = static_cast<Deconz::Command>(commandInt);
Deconz::StatusCode status = static_cast<Deconz::StatusCode>(statusInt);
qCDebug(dcZigbeeController()) << "Interface message received" << command << "SQN:" << sequenceNumber
<< status << "Frame length:" << frameLength << ZigbeeUtils::convertByteArrayToHexString(data);
// Check if this is an interface response for a pending reply
if (m_pendingReplies.contains(sequenceNumber) && m_pendingReplies.value(sequenceNumber)->command() == command) {
ZigbeeInterfaceDeconzReply *reply = m_pendingReplies.take(sequenceNumber);
reply->m_responseData = package.right(package.length() - 5);
reply->m_statusCode = static_cast<Deconz::StatusCode>(status);
reply->m_responseData = data;
reply->m_statusCode = status;
reply->finished();
return;
}
@ -563,12 +703,28 @@ void ZigbeeBridgeControllerDeconz::onInterfacePackageReceived(const QByteArray &
m_sequenceNumber = sequenceNumber;
// No request for this data, lets check which notification and process the data
switch (command) {
case Deconz::CommandDeviceStateChanged: {
quint8 deviceStateFlag = 0;
stream >> deviceStateFlag;
processDeviceState(parseDeviceStateFlag(deviceStateFlag));
break;
}
default:
qCWarning(dcZigbeeController()) << "Unhandled interface package received" << command << "SQN:" << sequenceNumber
<< status << "Frame length:" << frameLength << ZigbeeUtils::convertByteArrayToHexString(data);
break;
}
}
void ZigbeeBridgeControllerDeconz::onWatchdogTimerTimeout()
void ZigbeeBridgeControllerDeconz::resetControllerWatchdog()
{
qCDebug(dcZigbeeController()) << "Reset application watchdog on the deCONZ controller";
ZigbeeInterfaceDeconzReply *reply = resetWatchdog();
QByteArray parameterData;
QDataStream stream(&parameterData, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << m_watchdogTimeout;
ZigbeeInterfaceDeconzReply *reply = requestWriteParameter(Deconz::ParameterWatchdogTtl, parameterData);
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [reply](){
if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Could not reset the application watchdog on the deCONZ controller." << reply->statusCode();

View File

@ -41,24 +41,37 @@
#include "interface/zigbeeinterfacedeconz.h"
#include "interface/zigbeeinterfacedeconzreply.h"
// This struct describes the current deCONZ network configuration parameters
typedef struct DeconzNetworkConfiguration {
ZigbeeAddress ieeeAddress; // R
quint16 panId; // R
quint16 shortAddress; // R
quint64 extendedPanId; // R
Deconz::NodeType nodeType; // RW
quint32 channelMask; // RW
quint64 apsExtendedPanId; // RW
quint16 panId = 0; // R
quint16 shortAddress = 0; // R
quint64 extendedPanId = 0; // R
Deconz::NodeType nodeType = Deconz::NodeTypeCoordinator; // RW
quint32 channelMask = 0; // RW
quint64 apsExtendedPanId = 0; // RW
ZigbeeAddress trustCenterAddress; // RW
Deconz::SecurityMode securityMode; // RW
Deconz::SecurityMode securityMode = Deconz::SecurityModeNoMasterButTrustCenterKey; // RW
ZigbeeNetworkKey networkKey; // RW
quint8 currentChannel; // R
quint16 protocolVersion; // R
quint8 networkUpdateId; // RW
quint32 watchdogTimeout; // RW
quint8 currentChannel = 0; // R
quint16 protocolVersion = 0; // R
quint8 networkUpdateId = 0; // RW
quint32 watchdogTimeout = 85; // RW
} DeconzNetworkConfiguration;
// This struct describes the deCONZ device state
typedef struct DeconzDeviceState {
Deconz::NetworkState networkState = Deconz::NetworkStateOffline;
bool aspDataConfirm = false;
bool aspDataIndication = false;
bool configurationChanged = false;
bool aspDataRequestFreeSlots = false;
} DeconzDeviceState;
class ZigbeeBridgeControllerDeconz : public ZigbeeBridgeController
{
Q_OBJECT
@ -69,11 +82,16 @@ public:
explicit ZigbeeBridgeControllerDeconz(QObject *parent = nullptr);
~ZigbeeBridgeControllerDeconz() override;
DeconzNetworkConfiguration networkConfiguration() const;
void setFirmwareVersionString(const QString &firmwareVersion);
ZigbeeInterfaceDeconzReply *requestVersion();
ZigbeeInterfaceDeconzReply *requestDeviceState();
ZigbeeInterfaceDeconzReply *requestReadParameter(Deconz::Parameter parameter);
ZigbeeInterfaceDeconzReply *requestWriteParameter(Deconz::Parameter parameter, const QByteArray &data);
ZigbeeInterfaceDeconzReply *requestStartJoinNetwork();
ZigbeeInterfaceDeconzReply *requestChangeNetworkState(Deconz::NetworkState networkState);
ZigbeeInterfaceDeconzReply *requestReadReceivedDataIndication(Deconz::SourceAddressMode sourceAddressMode = Deconz::SourceAddressModeShortSourceAddress);
private:
ZigbeeInterfaceDeconz *m_interface = nullptr;
@ -92,15 +110,18 @@ private:
// The data can be fetched from m_networkConfiguration on success.
ZigbeeInterfaceDeconzReply *readNetworkParameters();
ZigbeeInterfaceDeconzReply *resetWatchdog();
DeconzDeviceState parseDeviceStateFlag(quint8 deviceStateFlag);
void processDeviceState(DeconzDeviceState deviceState);
signals:
void networkConfigurationParameterChanged(const DeconzNetworkConfiguration &networkConfiguration);
private slots:
void onInterfaceAvailableChanged(bool available);
void onInterfacePackageReceived(const QByteArray &package);
void onWatchdogTimerTimeout();
void resetControllerWatchdog();
public slots:
bool enable(const QString &serialPort, qint32 baudrate);

View File

@ -29,6 +29,8 @@
#include "loggingcategory.h"
#include "zigbeeutils.h"
#include <QDataStream>
ZigbeeNetworkDeconz::ZigbeeNetworkDeconz(QObject *parent) :
ZigbeeNetwork(parent)
{
@ -58,65 +60,117 @@ void ZigbeeNetworkDeconz::setPermitJoiningInternal(bool permitJoining)
Q_UNUSED(permitJoining)
}
void ZigbeeNetworkDeconz::startNetworkInternally()
{
qCDebug(dcZigbeeNetwork()) << "Start network internally";
// Check if we have to create a pan ID and select the channel
if (extendedPanId() == 0) {
m_createNewNetwork = true;
setExtendedPanId(ZigbeeUtils::generateRandomPanId());
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);
}
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
// - Read the network state
// - If network running and we don't have configurations, write them
// - If network running and configurations match, we are done
// Read the firmware version
ZigbeeInterfaceDeconzReply *reply = m_controller->requestVersion();
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){
if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Request" << reply->command() << "finished with error" << reply->statusCode();
// FIXME: set an appropriate error
return;
}
qCDebug(dcZigbeeNetwork()) << "Version request finished" << reply->statusCode() << 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));
Deconz::Platform platform = static_cast<Deconz::Platform>(reply->responseData().at(1));
QString firmwareVersion = QString("%1.%2").arg(majorVersion).arg(minorVersion);
qCDebug(dcZigbeeNetwork()) << "Firmware version" << firmwareVersion << platform;
// Read all network parameters
ZigbeeInterfaceDeconzReply *reply = m_controller->readNetworkParameters();
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply, firmwareVersion](){
if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Could not read network parameters during network start up." << reply->statusCode();
// FIXME: set an appropriate error
return;
}
qCDebug(dcZigbeeNetwork()) << "Reading network parameters finished successfully.";
QString protocolVersion = QString("%1.%2").arg(m_controller->networkConfiguration().protocolVersion & 0xFF00)
.arg(m_controller->networkConfiguration().protocolVersion & 0x00FF);
m_controller->setFirmwareVersionString(QString("%1 - %2").arg(firmwareVersion).arg(protocolVersion));
ZigbeeInterfaceDeconzReply *reply = m_controller->requestDeviceState();
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){
if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Could not read device state during network start up." << reply->statusCode();
// FIXME: set an appropriate error
return;
}
qCDebug(dcZigbeeNetwork()) << "Read device state finished successfully";
QDataStream stream(reply->responseData());
});
if (m_createNewNetwork) {
// Write the configurations which need to be changed
// Initialize coordinator node
} else {
// Get the network state and start the network if required
}
});
});
}
void ZigbeeNetworkDeconz::onControllerAvailableChanged(bool available)
{
qCDebug(dcZigbeeNetwork()) << "Hardware controller is" << (available ? "now available" : "not available");
if (!available) {
// foreach (ZigbeeNode *node, nodes()) {
// qobject_cast<ZigbeeNodeNxp *>(node)->setConnected(false);
// }
setError(ErrorHardwareUnavailable);
m_permitJoining = false;
emit permitJoiningChanged(m_permitJoining);
//setStartingState(StartingStateNone);
setState(StateOffline);
} else {
m_error = ErrorNoError;
m_permitJoining = false;
emit permitJoiningChanged(m_permitJoining);
// Note: if we are factory resetting, erase also the data on the controller before resetting
// if (m_factoryResetting) {
// setStartingState(StartingStateErase);
// } else {
// setStartingState(StartingStateReset);
// }
setState(StateStarting);
// FIXME: do this in the startig state machine
ZigbeeInterfaceDeconzReply *reply = m_controller->requestVersion();
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){
if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Request" << reply->command() << "finished with error" << reply->statusCode();
// FIXME: set an appropriate error
return;
}
qCDebug(dcZigbeeNetwork()) << "Version request finished" << reply->statusCode() << 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));
Deconz::Platform platform = static_cast<Deconz::Platform>(reply->responseData().at(1));
QString firmwareVersion = QString("%1.%2").arg(majorVersion).arg(minorVersion);
qCDebug(dcZigbeeNetwork()) << "Firmware version" << firmwareVersion << platform;
// Read all network parameters
ZigbeeInterfaceDeconzReply *reply = m_controller->readNetworkParameters();
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){
if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Could not read network parameters during network start up." << reply->statusCode();
// FIXME: set an appropriate error
return;
}
qCDebug(dcZigbeeNetwork()) << "Reading network parameters finished successfully.";
});
});
startNetworkInternally();
}
}
@ -124,23 +178,6 @@ void ZigbeeNetworkDeconz::startNetwork()
{
loadNetwork();
// Check if we have to create a pan ID and select the channel
if (extendedPanId() == 0) {
setExtendedPanId(ZigbeeUtils::generateRandomPanId());
qCDebug(dcZigbeeNetwork()) << "Created new PAN ID:" << extendedPanId();
}
if (securityConfiguration().networkKey().isNull()) {
qCDebug(dcZigbeeNetwork()) << "Create a new network key";
ZigbeeNetworkKey key = ZigbeeNetworkKey::generateKey();
m_securityConfiguration.setNetworkKey(key);
}
qCDebug(dcZigbeeNetwork()) << "Using network link key" << securityConfiguration().networkKey();
qCDebug(dcZigbeeNetwork()) << "Using global trust center link key" << securityConfiguration().globalTrustCenterLinkKey();
// TODO: get desired channel, by default use all
if (!m_controller->enable(serialPortName(), serialBaudrate())) {
m_permitJoining = false;
emit permitJoiningChanged(m_permitJoining);
@ -157,15 +194,26 @@ void ZigbeeNetworkDeconz::startNetwork()
void ZigbeeNetworkDeconz::stopNetwork()
{
ZigbeeInterfaceDeconzReply *reply = m_controller->requestChangeNetworkState(Deconz::NetworkStateOffline);
setState(StateStopping);
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){
if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Could not leave network." << reply->statusCode();
// FIXME: set an appropriate error
return;
}
qCDebug(dcZigbeeNetwork()) << "Network left successfully";
setState(StateOffline);
});
}
void ZigbeeNetworkDeconz::reset()
{
// TODO
}
void ZigbeeNetworkDeconz::factoryResetNetwork()
{
// Wipe settings, and reconfigure network
}

View File

@ -30,7 +30,7 @@
#include <QObject>
#include "zigbeenetwork.h"
#include "zigbeechannelmask.h"
#include "zigbeebridgecontrollerdeconz.h"
class ZigbeeNetworkDeconz : public ZigbeeNetwork
@ -44,11 +44,14 @@ public:
private:
ZigbeeBridgeControllerDeconz *m_controller = nullptr;
bool m_networkRunning = false;
bool m_createNewNetwork = false;
protected:
ZigbeeNode *createNode(QObject *parent) override;
void setPermitJoiningInternal(bool permitJoining) override;
void startNetworkInternally();
private slots:
void onControllerAvailableChanged(bool available);

View File

@ -17,6 +17,7 @@ SOURCES += \
nxp/zigbeenodeendpointnxp.cpp \
nxp/zigbeenodenxp.cpp \
zigbeebridgecontroller.cpp \
zigbeechannelmask.cpp \
zigbeecluster.cpp \
zigbeeclusterattribute.cpp \
zigbeemanufacturer.cpp \
@ -47,6 +48,7 @@ HEADERS += \
nxp/zigbeenodeendpointnxp.h \
nxp/zigbeenodenxp.h \
zigbeebridgecontroller.h \
zigbeechannelmask.h \
zigbeecluster.h \
zigbeeclusterattribute.h \
zigbeemanufacturer.h \

View File

@ -53,21 +53,22 @@ public:
Q_ENUM(ZigbeeProfile)
enum ZigbeeChannel {
ZigbeeChannel11,
ZigbeeChannel12,
ZigbeeChannel13,
ZigbeeChannel14,
ZigbeeChannel15,
ZigbeeChannel16,
ZigbeeChannel17,
ZigbeeChannel18,
ZigbeeChannel19,
ZigbeeChannel20,
ZigbeeChannel21,
ZigbeeChannel22,
ZigbeeChannel23,
ZigbeeChannel24,
ZigbeeChannel25
ZigbeeChannel11 = 0x00000800,
ZigbeeChannel12 = 0x00001000,
ZigbeeChannel13 = 0x00002000,
ZigbeeChannel14 = 0x00004000,
ZigbeeChannel15 = 0x00008000,
ZigbeeChannel16 = 0x00010000,
ZigbeeChannel17 = 0x00020000,
ZigbeeChannel18 = 0x00040000,
ZigbeeChannel19 = 0x00080000,
ZigbeeChannel20 = 0x00100000,
ZigbeeChannel21 = 0x00200000,
ZigbeeChannel22 = 0x00400000,
ZigbeeChannel23 = 0x00800000,
ZigbeeChannel24 = 0x01000000,
ZigbeeChannel25 = 0x02000000,
ZigbeeChannel26 = 0x04000000
};
Q_ENUM(ZigbeeChannel)
Q_DECLARE_FLAGS(ZigbeeChannels, ZigbeeChannel)
@ -540,6 +541,13 @@ public:
};
Q_ENUM(DestinationAddressMode)
enum SourceAddressMode {
SourceAddressModeShortAddress = 0x02,
SourceAddressModeIeeeAddress = 0x03,
SourceAddressModeShortAndIeeeAddress = 0x04
};
Q_ENUM(SourceAddressMode)
enum ZigbeeZclStatus {
};

View File

@ -0,0 +1,106 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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 "zigbeechannelmask.h"
#include "zigbeeutils.h"
ZigbeeChannelMask::ZigbeeChannelMask()
{
}
ZigbeeChannelMask::ZigbeeChannelMask(quint32 channelMask) :
m_channelMask(channelMask)
{
}
ZigbeeChannelMask::ZigbeeChannelMask(Zigbee::ZigbeeChannels channels)
{
m_channelMask = static_cast<quint32>(channels);
}
quint32 ZigbeeChannelMask::toUInt32() const
{
return m_channelMask;
}
Zigbee::ZigbeeChannels ZigbeeChannelMask::channels() const
{
return static_cast<Zigbee::ZigbeeChannels>(m_channelMask);
}
bool ZigbeeChannelMask::isSet(Zigbee::ZigbeeChannel channel) const
{
return channels().testFlag(channel);
}
void ZigbeeChannelMask::setChannel(Zigbee::ZigbeeChannel channel)
{
// Set channel bit
m_channelMask |= 1 << channel;
}
void ZigbeeChannelMask::unsetChannel(Zigbee::ZigbeeChannel channel)
{
// Clear channel bit
m_channelMask &= ~(1 << channel);
}
ZigbeeChannelMask &ZigbeeChannelMask::operator=(const ZigbeeChannelMask &other)
{
m_channelMask = other.toUInt32();
return *this;
}
bool ZigbeeChannelMask::operator==(const ZigbeeChannelMask &other) const
{
return m_channelMask == other.toUInt32();
}
bool ZigbeeChannelMask::operator!=(const ZigbeeChannelMask &other) const
{
return !operator==(other);
}
QDebug operator<<(QDebug debug, const ZigbeeChannelMask &channelMaks)
{
debug.nospace() << "ChannelMask(" << ZigbeeUtils::convertUint32ToHexString(channelMaks.toUInt32());
debug.nospace() << ", [";
for (int i = 11; i <= 25; i++) {
if (channelMaks.isSet(static_cast<Zigbee::ZigbeeChannel>(i))) {
if (i < 25) {
debug.nospace() << i << ", ";
} else {
debug.nospace() << i;
}
}
}
debug.nospace() << "])";
return debug.space();
}

View File

@ -0,0 +1,71 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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 ZIGBEECHANNELMASK_H
#define ZIGBEECHANNELMASK_H
#include <QDebug>
#include <QObject>
#include "zigbee.h"
class ZigbeeChannelMask
{
Q_GADGET
public:
enum ChannelConfiguration {
ChannelConfigurationNoChannel = 0x00000000,
ChannelConfigurationPrimaryLightLink = 0x02108800,
ChannelConfigurationAllChannels = 0x07fff800
};
Q_ENUM(ChannelConfiguration)
ZigbeeChannelMask();
ZigbeeChannelMask(quint32 channelMask);
ZigbeeChannelMask(Zigbee::ZigbeeChannels channels);
quint32 toUInt32() const;
Zigbee::ZigbeeChannels channels() const;
bool isSet(Zigbee::ZigbeeChannel channel) const;
void setChannel(Zigbee::ZigbeeChannel channel);
void unsetChannel(Zigbee::ZigbeeChannel channel);
ZigbeeChannelMask &operator=(const ZigbeeChannelMask &other);
bool operator==(const ZigbeeChannelMask &other) const;
bool operator!=(const ZigbeeChannelMask &other) const;
private:
quint32 m_channelMask = 0;
};
QDebug operator<<(QDebug debug, const ZigbeeChannelMask &channelMaks);
#endif // ZIGBEECHANNELMASK_H

View File

@ -115,6 +115,20 @@ void ZigbeeNetwork::setChannel(quint32 channel)
emit channelChanged(m_channel);
}
ZigbeeChannelMask ZigbeeNetwork::channelMask() const
{
return m_channelMask;
}
void ZigbeeNetwork::setChannelMask(const ZigbeeChannelMask &channelMask)
{
if (m_channelMask == channelMask)
return;
m_channelMask = channelMask;
emit channelMaskChanged(m_channelMask);
}
ZigbeeSecurityConfiguration ZigbeeNetwork::securityConfiguration() const
{
return m_securityConfiguration;

View File

@ -32,6 +32,7 @@
#include <QSettings>
#include "zigbeenode.h"
#include "zigbeechannelmask.h"
#include "zigbeebridgecontroller.h"
#include "zigbeesecurityconfiguration.h"
@ -81,6 +82,9 @@ public:
quint32 channel() const;
void setChannel(quint32 channel);
ZigbeeChannelMask channelMask() const;
void setChannelMask(const ZigbeeChannelMask &channelMask);
ZigbeeSecurityConfiguration securityConfiguration() const;
void setSecurityConfiguration(const ZigbeeSecurityConfiguration &securityConfiguration);
@ -107,6 +111,7 @@ private:
// Network configurations
quint64 m_extendedPanId = 0;
quint32 m_channel = 0;
ZigbeeChannelMask m_channelMask = ZigbeeChannelMask(ZigbeeChannelMask::ChannelConfigurationAllChannels);
ZigbeeNode::NodeType m_nodeType = ZigbeeNode::NodeTypeCoordinator;
QString m_settingsFileName = "/etc/nymea/nymea-zigbee.conf";
@ -148,6 +153,7 @@ signals:
void extendedPanIdChanged(quint64 extendedPanId);
void channelChanged(uint channel);
void channelMaskChanged(const ZigbeeChannelMask &channelMask);
void securityConfigurationChanged(const ZigbeeSecurityConfiguration &securityConfiguration);
void nodeAdded(ZigbeeNode *node);

View File

@ -505,7 +505,6 @@ void ZigbeeNode::onClusterAttributeChanged(const ZigbeeClusterAttribute &attribu
// qCDebug(dcZigbeeNode()) << " Data:" << data;
//}
QDebug operator<<(QDebug debug, ZigbeeNode *node)
{
debug.nospace().noquote() << "ZigbeeNode(" << ZigbeeUtils::convertUint16ToHexString(node->shortAddress());