Implement power configuration cluster

This commit is contained in:
Simon Stürz 2020-06-13 21:18:01 +02:00
parent 0992028a8a
commit cdd7a1a1aa
33 changed files with 620 additions and 67375 deletions

View File

@ -9,8 +9,10 @@ Depending on your available hardware following gateway modules are supported
## NXP
* JN5168 (SoM)
* JN5169 (USB Stick)
> Note: the firmware erquires an entire rework and implement the APS layer
* ~~JN5168 (SoM)~~
* ~~JN5169 (USB Stick)~~
## deCONZ
@ -20,15 +22,11 @@ Depending on your available hardware following gateway modules are supported
* RaspBee II
## Structure
## Hidden base groups
Each sensor/remote cluster will be bound automatically to the sensors group in order to receive commands from the device. The lights group can be used to switch all lights within the network with one command.
- Sensors: `0xfff0`
- Light: `0xfff1`
ZigbeeNetwork
- ZDO handler
- ZCL handler
- ZHA handler
- ZLL handler
- GP handler
- [ZigbeeNode]
- [ZigbeeNodeEndpoints]
- Profile

File diff suppressed because one or more lines are too long

View File

@ -11,6 +11,7 @@ SOURCES += \
zcl/general/zigbeeclusteridentify.cpp \
zcl/general/zigbeeclusterlevelcontrol.cpp \
zcl/general/zigbeeclusteronoff.cpp \
zcl/general/zigbeeclusterpowerconfiguration.cpp \
zcl/lighting/zigbeeclustercolorcontrol.cpp \
zcl/measurement/zigbeeclusterilluminancemeasurment.cpp \
zcl/measurement/zigbeeclusteroccupancysensing.cpp \
@ -61,6 +62,7 @@ HEADERS += \
zcl/general/zigbeeclusteridentify.h \
zcl/general/zigbeeclusterlevelcontrol.h \
zcl/general/zigbeeclusteronoff.h \
zcl/general/zigbeeclusterpowerconfiguration.h \
zcl/lighting/zigbeeclustercolorcontrol.h \
zcl/measurement/zigbeeclusterilluminancemeasurment.h \
zcl/measurement/zigbeeclusteroccupancysensing.h \

View File

@ -31,7 +31,7 @@
#include <QDataStream>
ZigbeeClusterBasic::ZigbeeClusterBasic(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent) :
ZigbeeCluster(network, node, endpoint, Zigbee::ClusterIdBasic, direction, parent)
ZigbeeCluster(network, node, endpoint, ZigbeeClusterLibrary::ClusterIdBasic, direction, parent)
{
}

View File

@ -33,7 +33,7 @@
#include <QDataStream>
ZigbeeClusterIdentify::ZigbeeClusterIdentify(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, ZigbeeCluster::Direction direction, QObject *parent) :
ZigbeeCluster(network, node, endpoint, Zigbee::ClusterIdIdentify, direction, parent)
ZigbeeCluster(network, node, endpoint, ZigbeeClusterLibrary::ClusterIdIdentify, direction, parent)
{
}

View File

@ -33,7 +33,7 @@
#include <QDataStream>
ZigbeeClusterLevelControl::ZigbeeClusterLevelControl(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, ZigbeeCluster::Direction direction, QObject *parent) :
ZigbeeCluster(network, node, endpoint, Zigbee::ClusterIdLevelControl, direction, parent)
ZigbeeCluster(network, node, endpoint, ZigbeeClusterLibrary::ClusterIdLevelControl, direction, parent)
{
}

View File

@ -33,7 +33,7 @@
#include <QDataStream>
ZigbeeClusterOnOff::ZigbeeClusterOnOff(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent) :
ZigbeeCluster(network, node, endpoint, Zigbee::ClusterIdOnOff, direction, parent)
ZigbeeCluster(network, node, endpoint, ZigbeeClusterLibrary::ClusterIdOnOff, direction, parent)
{
}

View File

@ -0,0 +1,60 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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 "zigbeeclusterpowerconfiguration.h"
#include "loggingcategory.h"
#include <QDataStream>
ZigbeeClusterPowerConfiguration::ZigbeeClusterPowerConfiguration(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent) :
ZigbeeCluster(network, node, endpoint, ZigbeeClusterLibrary::ClusterIdPowerConfiguration, direction, parent)
{
}
void ZigbeeClusterPowerConfiguration::setAttribute(const ZigbeeClusterAttribute &attribute)
{
qCDebug(dcZigbeeCluster()) << "Update attribute" << m_node << m_endpoint << this << static_cast<Attribute>(attribute.id()) << attribute.dataType();
if (hasAttribute(attribute.id())) {
m_attributes[attribute.id()] = attribute;
emit attributeChanged(attribute);
} else {
m_attributes.insert(attribute.id(), attribute);
emit attributeChanged(attribute);
}
if (attribute.id() == AttributeBatteryPercentageRemaining) {
bool valueOk = false;
quint8 value = attribute.dataType().toUInt8(&valueOk);
if (valueOk) {
qCDebug(dcZigbeeCluster()) << "PowerConfiguration attribute changed on" << m_node << m_endpoint << this << value;
emit batteryPercentageChanged(value / 2.0);
} else {
qCWarning(dcZigbeeCluster()) << "Failed to parse attribute data" << m_node << m_endpoint << this << attribute;
}
}
}

View File

@ -0,0 +1,101 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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 ZIGBEECLUSTERPOWERCONFIGURATION_H
#define ZIGBEECLUSTERPOWERCONFIGURATION_H
#include <QObject>
#include "zcl/zigbeecluster.h"
class ZigbeeNode;
class ZigbeeNetwork;
class ZigbeeNodeEndpoint;
class ZigbeeNetworkReply;
class ZigbeeClusterPowerConfiguration : public ZigbeeCluster
{
Q_OBJECT
friend class ZigbeeNode;
friend class ZigbeeNetwork;
public:
enum Attribute {
// Mains information
AttributeMainsVoltage = 0x0000,
AttributeMainsFrequency = 0x0001,
// Mains settings
AttributeMainsAlarmMask = 0x0010,
AttributeMainsVoltageMinThreshold = 0x0011,
AttributeMainsVoltageMaxThreshold = 0x0012,
AttributeMainsVoltageDwellTripPoint = 0x0013,
// Battery information
AttributeBatteryVoltage = 0x0020,
AttributeBatteryPercentageRemaining = 0x0021,
// Battery settings
AttributeBatteryManufacturer = 0x0030,
AttributeBatterySize = 0x0031,
AttributeBatteryAHrRating = 0x0032,
AttributeBatteryQuality = 0x0033,
AttributeBatteryRatedVoltage = 0x0034,
AttributeBatteryAlarmMask = 0x0035,
AttributeBatteryVoltageMinThreshold = 0x0036,
AttributeBatteryVoltageThreshold1 = 0x0037,
AttributeBatteryVoltageThreshold2 = 0x0038,
AttributeBatteryVoltageThreshold3 = 0x0039,
AttributeBatteryPercentageMinThreshold = 0x003a,
AttributeBatteryPercentageThreshold1 = 0x003b,
AttributeBatteryPercentageThreshold2 = 0x003c,
AttributeBatteryPercentageThreshold3 = 0x003d,
AttributeBatteryAlarmState = 0x003e
};
Q_ENUM(Attribute)
enum MainsAlarmMask {
MainsAlarmMaskMainVoltageToLow = 0x01,
MainsAlarmMaskMainVoltageToHigh = 0x02,
MainsAlarmMaskMainPowerSupplyLost = 0x04
};
Q_ENUM(MainsAlarmMask)
Q_DECLARE_FLAGS(MainsAlarmMaskFlag, MainsAlarmMask)
explicit ZigbeeClusterPowerConfiguration(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent = nullptr);
private:
void setAttribute(const ZigbeeClusterAttribute &attribute) override;
signals:
void batteryPercentageChanged(double percentage);
};
Q_DECLARE_OPERATORS_FOR_FLAGS(ZigbeeClusterPowerConfiguration::MainsAlarmMaskFlag)
#endif // ZIGBEECLUSTERPOWERCONFIGURATION_H

View File

@ -33,7 +33,7 @@
#include <QDataStream>
ZigbeeClusterColorControl::ZigbeeClusterColorControl(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, ZigbeeCluster::Direction direction, QObject *parent) :
ZigbeeCluster(network, node, endpoint, Zigbee::ClusterIdColorControl, direction, parent)
ZigbeeCluster(network, node, endpoint, ZigbeeClusterLibrary::ClusterIdColorControl, direction, parent)
{
}
@ -105,6 +105,7 @@ ZigbeeClusterReply *ZigbeeClusterColorControl::commandMoveToHueAndSaturation(qui
ZigbeeClusterReply *ZigbeeClusterColorControl::commandMoveToColor(quint16 colorX, quint16 colorY, quint16 transitionTime)
{
qCDebug(dcZigbeeCluster()) << "Move to color" << colorX << colorY << transitionTime << "1/10 s";
QByteArray payload;
QDataStream stream(&payload, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
@ -132,6 +133,8 @@ ZigbeeClusterReply *ZigbeeClusterColorControl::commandStepColor(quint16 stepX, q
ZigbeeClusterReply *ZigbeeClusterColorControl::commandMoveToColorTemperature(quint16 colorTemperatureMireds, quint16 transitionTime)
{
qCDebug(dcZigbeeCluster()) << "Move to color temperature" << colorTemperatureMireds << transitionTime << "1/10 s";
QByteArray payload;
QDataStream stream(&payload, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);

View File

@ -31,7 +31,7 @@
#include "zigbeenetwork.h"
ZigbeeClusterIlluminanceMeasurment::ZigbeeClusterIlluminanceMeasurment(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent) :
ZigbeeCluster(network, node, endpoint, Zigbee::ClusterIdIlluminanceMeasurement, direction, parent)
ZigbeeCluster(network, node, endpoint, ZigbeeClusterLibrary::ClusterIdIlluminanceMeasurement, direction, parent)
{
}

View File

@ -31,7 +31,7 @@
#include "zigbeenetwork.h"
ZigbeeClusterOccupancySensing::ZigbeeClusterOccupancySensing(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent) :
ZigbeeCluster(network, node, endpoint, Zigbee::ClusterIdOccupancySensing, direction, parent)
ZigbeeCluster(network, node, endpoint, ZigbeeClusterLibrary::ClusterIdOccupancySensing, direction, parent)
{
}

View File

@ -31,7 +31,7 @@
#include "zigbeenetwork.h"
ZigbeeClusterRelativeHumidityMeasurement::ZigbeeClusterRelativeHumidityMeasurement(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent) :
ZigbeeCluster(network, node, endpoint, Zigbee::ClusterIdRelativeHumidityMeasurement, direction, parent)
ZigbeeCluster(network, node, endpoint, ZigbeeClusterLibrary::ClusterIdRelativeHumidityMeasurement, direction, parent)
{
}

View File

@ -31,7 +31,7 @@
#include "zigbeenetwork.h"
ZigbeeClusterTemperatureMeasurement::ZigbeeClusterTemperatureMeasurement(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Direction direction, QObject *parent) :
ZigbeeCluster(network, node, endpoint, Zigbee::ClusterIdTemperatureMeasurement, direction, parent)
ZigbeeCluster(network, node, endpoint, ZigbeeClusterLibrary::ClusterIdTemperatureMeasurement, direction, parent)
{
}

View File

@ -34,7 +34,7 @@
#include <QDataStream>
ZigbeeClusterIasZone::ZigbeeClusterIasZone(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, ZigbeeCluster::Direction direction, QObject *parent) :
ZigbeeCluster(network, node, endpoint, Zigbee::ClusterIdIasZone, direction, parent)
ZigbeeCluster(network, node, endpoint, ZigbeeClusterLibrary::ClusterIdIasZone, direction, parent)
{
}

View File

@ -35,7 +35,7 @@
#include <QDataStream>
ZigbeeCluster::ZigbeeCluster(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Zigbee::ClusterId clusterId, Direction direction, QObject *parent) :
ZigbeeCluster::ZigbeeCluster(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, ZigbeeClusterLibrary::ClusterId clusterId, Direction direction, QObject *parent) :
QObject(parent),
m_network(network),
m_node(node),
@ -61,14 +61,14 @@ ZigbeeCluster::Direction ZigbeeCluster::direction() const
return m_direction;
}
Zigbee::ClusterId ZigbeeCluster::clusterId() const
ZigbeeClusterLibrary::ClusterId ZigbeeCluster::clusterId() const
{
return m_clusterId;
}
QString ZigbeeCluster::clusterName() const
{
return ZigbeeUtils::clusterIdToString(static_cast<Zigbee::ClusterId>(m_clusterId));
return ZigbeeUtils::clusterIdToString(static_cast<ZigbeeClusterLibrary::ClusterId>(m_clusterId));
}
QList<ZigbeeClusterAttribute> ZigbeeCluster::attributes() const
@ -105,6 +105,32 @@ ZigbeeClusterReply *ZigbeeCluster::readAttributes(QList<quint16> attributes)
{
qCDebug(dcZigbeeCluster()) << "Read attributes from" << m_node << m_endpoint << this << attributes;
// ZCL payload
QByteArray payload;
QDataStream stream(&payload, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
foreach (quint16 attribute, attributes) {
stream << attribute;
}
return executeGlobalCommand(ZigbeeClusterLibrary::CommandReadAttributes, payload);
}
ZigbeeClusterReply *ZigbeeCluster::configureReporting(QList<ZigbeeClusterLibrary::AttributeReportingConfiguration> reportingConfigurations)
{
qCDebug(dcZigbeeCluster()) << "Configure reporting on" << m_node << m_endpoint << this << reportingConfigurations;
QByteArray payload;
foreach (const ZigbeeClusterLibrary::AttributeReportingConfiguration reportingConfiguration, reportingConfigurations) {
payload += ZigbeeClusterLibrary::buildAttributeReportingConfiguration(reportingConfiguration);
}
return executeGlobalCommand(ZigbeeClusterLibrary::CommandConfigureReporting, payload);
}
ZigbeeClusterReply *ZigbeeCluster::executeGlobalCommand(quint8 command, const QByteArray &payload)
{
// Build the request
ZigbeeNetworkRequest request = createGeneralRequest();
@ -124,17 +150,9 @@ ZigbeeClusterReply *ZigbeeCluster::readAttributes(QList<quint16> attributes)
// ZCL header
ZigbeeClusterLibrary::Header header;
header.frameControl = frameControl;
header.command = ZigbeeClusterLibrary::CommandReadAttributes;
header.command = command;
header.transactionSequenceNumber = m_transactionSequenceNumber++;
// ZCL payload
QByteArray payload;
QDataStream stream(&payload, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
foreach (quint16 attribute, attributes) {
stream << attribute;
}
// Put them together
ZigbeeClusterLibrary::Frame frame;
frame.header = header;
@ -163,6 +181,7 @@ ZigbeeClusterReply *ZigbeeCluster::readAttributes(QList<quint16> attributes)
return zclReply;
}
ZigbeeClusterReply *ZigbeeCluster::createClusterReply(const ZigbeeNetworkRequest &request, ZigbeeClusterLibrary::Frame frame)
{
ZigbeeClusterReply *zclReply = new ZigbeeClusterReply(request, frame, this);
@ -198,6 +217,7 @@ ZigbeeClusterReply *ZigbeeCluster::executeClusterCommand(quint8 command, const Q
request.setAsdu(ZigbeeClusterLibrary::buildFrame(frame));
ZigbeeClusterReply *zclReply = createClusterReply(request, frame);
qCDebug(dcZigbeeCluster()) << "Executing command" << ZigbeeUtils::convertByteToHexString(command) << ZigbeeUtils::convertByteArrayToHexString(payload);
ZigbeeNetworkReply *networkReply = m_network->sendRequest(request);
connect(networkReply, &ZigbeeNetworkReply::finished, this, [this, networkReply, zclReply](){
if (!verifyNetworkError(zclReply, networkReply)) {

View File

@ -48,9 +48,9 @@ struct ZigbeeClusterReportConfigurationRecord {
typedef struct ZigbeeClusterAttributeReport {
quint16 sourceAddress;
quint8 endpointId;
Zigbee::ClusterId clusterId;
ZigbeeClusterLibrary::ClusterId clusterId;
quint16 attributeId;
Zigbee::ZigbeeStatus attributeStatus;
ZigbeeClusterLibrary::Status attributeStatus;
Zigbee::DataType dataType;
QByteArray data;
} ZigbeeClusterAttributeReport;
@ -75,29 +75,14 @@ public:
};
Q_ENUM(Direction)
// // Power configuration cluster 0x0001
// enum PowerConfigurationAttribute {
// PowerConfigurationAttributeMainsInformation = 0x0000,
// PowerConfigurationAttributeMainsSettings = 0x0001,
// PowerConfigurationAttributeBatteryInformation = 0x0002,
// PowerConfigurationAttributeBatterySettings = 0x0003,
// PowerConfigurationAttributeBatterySource2Information = 0x0004,
// PowerConfigurationAttributeBattterySource2Settings = 0x0005,
// PowerConfigurationAttributeBatterySource3Information = 0x0006,
// PowerConfigurationAttributeBattterySource3Settings = 0x0007
// };
// Q_ENUM(PowerConfigurationAttribute)
explicit ZigbeeCluster(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Zigbee::ClusterId clusterId, Direction direction, QObject *parent = nullptr);
explicit ZigbeeCluster(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, ZigbeeClusterLibrary::ClusterId clusterId, Direction direction, QObject *parent = nullptr);
ZigbeeNode *node() const;
ZigbeeNodeEndpoint *endpoint() const;
Direction direction() const;
Zigbee::ClusterId clusterId() const;
ZigbeeClusterLibrary::ClusterId clusterId() const;
QString clusterName() const;
QList<ZigbeeClusterAttribute> attributes() const;
@ -106,13 +91,17 @@ public:
// ZCL global commands
ZigbeeClusterReply *readAttributes(QList<quint16> attributes);
ZigbeeClusterReply *configureReporting(QList<ZigbeeClusterLibrary::AttributeReportingConfiguration> reportingConfigurations);
protected:
ZigbeeNetwork *m_network = nullptr;
ZigbeeNode *m_node = nullptr;
ZigbeeNodeEndpoint *m_endpoint= nullptr;
Zigbee::ClusterId m_clusterId = Zigbee::ClusterIdUnknown;
ZigbeeClusterLibrary::ClusterId m_clusterId = ZigbeeClusterLibrary::ClusterIdUnknown;
Direction m_direction = Server;
QHash<quint16, ZigbeeClusterAttribute> m_attributes;
@ -120,6 +109,12 @@ protected:
ZigbeeNetworkRequest createGeneralRequest();
quint8 m_transactionSequenceNumber = 0;
QHash<quint8, ZigbeeClusterReply *> m_pendingReplies;
// Global commands
ZigbeeClusterReply *executeGlobalCommand(quint8 command, const QByteArray &payload = QByteArray());
// Cluster specific
ZigbeeClusterReply *createClusterReply(const ZigbeeNetworkRequest &request, ZigbeeClusterLibrary::Frame frame);
ZigbeeClusterReply *executeClusterCommand(quint8 command, const QByteArray &payload = QByteArray());
bool verifyNetworkError(ZigbeeClusterReply *zclReply, ZigbeeNetworkReply *networkReply);

View File

@ -112,11 +112,11 @@ QList<ZigbeeClusterLibrary::ReadAttributeStatusRecord> ZigbeeClusterLibrary::par
// Read attribute id and status
stream >> attributeId >> statusInt;
Zigbee::ZigbeeStatus status = static_cast<Zigbee::ZigbeeStatus>(statusInt);
ZigbeeClusterLibrary::Status status = static_cast<ZigbeeClusterLibrary::Status>(statusInt);
qCDebug(dcZigbeeClusterLibrary()) << "Parse:" << ZigbeeUtils::convertUint16ToHexString(attributeId) << status;
if (status != Zigbee::ZigbeeStatusSuccess) {
if (status != ZigbeeClusterLibrary::StatusSuccess) {
qCWarning(dcZigbeeCluster()) << "Attribute status record" << ZigbeeUtils::convertUint16ToHexString(attributeId) << "finished with error" << status;
// If not success, we are done and can continue with the next status record
continue;
@ -254,6 +254,45 @@ QByteArray ZigbeeClusterLibrary::buildFrame(const ZigbeeClusterLibrary::Frame &f
return buildHeader(frame.header) + frame.payload;
}
QByteArray ZigbeeClusterLibrary::buildAttributeReportingConfiguration(const ZigbeeClusterLibrary::AttributeReportingConfiguration &reportingConfiguration)
{
QByteArray payload;
QDataStream stream(&payload, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << static_cast<quint8>(reportingConfiguration.direction);
stream << reportingConfiguration.attributeId;
stream << static_cast<quint8>(reportingConfiguration.dataType);
stream << reportingConfiguration.minReportingInterval;
stream << reportingConfiguration.maxReportingInterval;
for (int i = 0; i < reportingConfiguration.reportableChange.count(); i++) {
stream << static_cast<quint8>(reportingConfiguration.reportableChange.at(i));
}
// Note: for reporting the timeoutPeriod is omitted
if (reportingConfiguration.direction == ReportingDirectionReceiving) {
stream << reportingConfiguration.timeoutPeriod;
}
return payload;
}
QList<ZigbeeClusterLibrary::AttributeReportingStatusRecord> ZigbeeClusterLibrary::parseAttributeReportingStatusRecords(const QByteArray &payload)
{
QList<ZigbeeClusterLibrary::AttributeReportingStatusRecord> statusRecords;
QDataStream stream(payload);
stream.setByteOrder(QDataStream::LittleEndian);
while (!stream.atEnd()) {
ZigbeeClusterLibrary::AttributeReportingStatusRecord statusRecord;
quint8 status; quint8 direction = 0;
stream >> status >> direction >> statusRecord.attributeId;
statusRecord.direction = static_cast<ReportingDirection>(direction);
statusRecord.status = static_cast<ZigbeeClusterLibrary::Status>(status);
statusRecords.append(statusRecord);
}
return statusRecords;
}
QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::FrameControl &frameControl)
{
debug.nospace() << "FrameControl(";
@ -305,3 +344,28 @@ QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::ReadAttributeStatusR
return debug.space();
}
QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::AttributeReportingConfiguration &attributeReportingConfiguration)
{
debug.nospace() << "AttributeReportingConfiguration("
<< attributeReportingConfiguration.direction << ", "
<< ZigbeeUtils::convertUint16ToHexString(attributeReportingConfiguration.attributeId) << ", "
<< attributeReportingConfiguration.dataType << ", "
<< "Min interval: " << attributeReportingConfiguration.minReportingInterval << "[s], "
<< "Max interval: " << attributeReportingConfiguration.maxReportingInterval << "[s], "
<< "Change: " << ZigbeeUtils::convertByteArrayToHexString(attributeReportingConfiguration.reportableChange) << ", "
<< "Timeout period: " << attributeReportingConfiguration.timeoutPeriod << "[s]"
<< ")";
return debug.space();
}
QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::AttributeReportingStatusRecord &attributeReportingStatusRecord)
{
debug.nospace() << "AttributeReportingConfiguration("
<< attributeReportingStatusRecord.status << ", "
<< attributeReportingStatusRecord.attributeId << ", "
<< attributeReportingStatusRecord.direction
<< ")";
return debug.space();
}

View File

@ -66,6 +66,133 @@ public:
};
Q_ENUM(Command)
enum Status {
StatusSuccess = 0x00,
StatusFailure = 0x01,
StatusNotAuthorized = 0x7e,
StatusReservedFieldNotZero = 0x7f,
StatusMalformedCommand = 0x80,
StatusUnsupportedClusterCommand = 0x81,
StatusUnsupportedGeneralCommand = 0x82,
StatusUnsupportedManufacturerClusterCommand = 0x83,
StatusUnsupportedManufacturerGeneralCommand = 0x84,
StatusInvalidField = 0x85,
StatusUnsupportedAttribute = 0x86,
StatusInvalidValue = 0x87,
StatusReadOnly = 0x88,
StatusInsufficientSpace = 0x89,
StatusDuplicateExists = 0x8a,
StatusNotFound = 0x8b,
StatusUnreportableAttribute = 0x8c,
StatusInvalidDataType = 0x8d,
StatusInvalidSector = 0x8e,
StatusWriteOnly = 0x8f,
StatusInconsistentStartupState = 0x90,
StatusDefinedOutOfBand = 0x91,
StatusInconsistent = 0x92,
StatusActionDenied = 0x93,
StatusTimeout = 0x94,
StatusAbort = 0x95,
StatusInvalidImage = 0x96,
StatusWaitForData = 0x97,
StatusNoImageAvailable = 0x98,
StatusRequireMoreImage = 0x99,
StatusNotificationPending = 0x9a,
StatusHardwareFailure = 0xc0,
StatusSoftwareFailure = 0xc1,
StatusCalibrationError = 0xc2,
StatusUnsupportedCluster = 0xc3
};
Q_ENUM(Status)
enum ClusterId {
// Basics
ClusterIdUnknown = 0xffff,
ClusterIdBasic = 0x0000,
ClusterIdPowerConfiguration = 0x0001,
ClusterIdDeviceTemperature = 0x0002,
ClusterIdIdentify = 0x0003,
ClusterIdGroups = 0x0004,
ClusterIdScenes = 0x0005,
ClusterIdOnOff = 0x0006,
ClusterIdOnOffCOnfiguration = 0x0007,
ClusterIdLevelControl = 0x0008,
ClusterIdAlarms = 0x0009,
ClusterIdTime = 0x000A,
ClusterIdRssiLocation = 0x000B,
ClusterIdAnalogInputBasic = 0x000C,
ClusterIdAnalogOutputBasic = 0x000D,
ClusterIdValueBasic = 0x000E,
ClusterIdBinaryInputBasic = 0x000F,
ClusterIdBinaryOutputBasic = 0x0010,
ClusterIdBinaryValueBasic = 0x0011,
ClusterIdMultiStateInputBasic = 0x0012,
ClusterIdMultiStateOutputBasic = 0x0013,
ClusterIdMultiStateValueBasic = 0x0014,
ClusterIdCommissoning = 0x0015,
// Over the air uppgrade (OTA)
ClusterIdOtaUpgrade = 0x0019,
// Poll controll
ClusterIdPollControl = 0x0020,
// Closures
ClusterIdShadeConfiguration = 0x0100,
// Door Lock
ClusterIdDoorLock = 0x0101,
// Heating, Ventilation and Air-Conditioning (HVAC)
ClusterIdPumpConfigurationControl = 0x0200,
ClusterIdThermostat = 0x0201,
ClusterIdFanControll = 0x0202,
ClusterIdDehumiditationControll = 0x0203,
ClusterIdThermostatUserControll = 0x0204,
// Lighting
ClusterIdColorControl = 0x0300,
ClusterIdBallastConfiguration = 0x0301,
// Sensing
ClusterIdIlluminanceMeasurement = 0x0400,
ClusterIdIlluminanceLevelSensing = 0x0401,
ClusterIdTemperatureMeasurement = 0x0402,
ClusterIdPressureMeasurement = 0x0403,
ClusterIdFlowMeasurement = 0x0404,
ClusterIdRelativeHumidityMeasurement = 0x0405,
ClusterIdOccupancySensing = 0x0406,
// Security and Safty
ClusterIdIasZone = 0x0500,
ClusterIdIasAce = 0x0501,
ClusterIdIasWd = 0x0502,
// Smart energy
ClusterIdPrice = 0x0700,
ClusterIdLoadControl = 0x0701,
ClusterIdSimpleMetering = 0x0702,
// ZLL
ClusterIdTouchlinkCommissioning = 0x1000,
// NXP Appliances
ClusterIdApplianceControl = 0x001B,
ClusterIdApplianceIdentification = 0x0B00,
ClusterIdApplianceEventsAlerts = 0x0B02,
ClusterIdApplianceStatistics = 0x0B03,
// Electrical Measurement
ClusterIdElectricalMeasurement = 0x0B04,
ClusterIdDiagnostics = 0x0B05,
// Zigbee green power
ClusterIdGreenPower = 0x0021
};
Q_ENUM(ClusterId)
enum GlobalAttribute {
GlobalAttributeClusterRevision = 0xfffd,
GlobalAttributeAttributeReportingStatus = 0xfffe
@ -91,6 +218,12 @@ public:
};
Q_ENUM(Direction)
enum ReportingDirection {
ReportingDirectionReporting = 0x00,
ReportingDirectionReceiving = 0x01
};
Q_ENUM(ReportingDirection)
typedef struct FrameControl {
FrameType frameType = FrameTypeGlobal;
bool manufacturerSpecific = false;
@ -114,10 +247,28 @@ public:
// Read attribute
typedef struct ReadAttributeStatusRecord {
quint16 attributeId;
Zigbee::ZigbeeStatus attributeStatus;
ZigbeeClusterLibrary::Status attributeStatus;
ZigbeeDataType dataType;
} ReadAttributeStatusRecord;
// Reporting attributes
typedef struct AttributeReportingConfiguration {
ReportingDirection direction = ReportingDirectionReporting;
quint16 attributeId = 0x0000;
Zigbee::DataType dataType = Zigbee::NoData;
quint16 minReportingInterval = 0x0000; // seconds
quint16 maxReportingInterval = 0x0000; // seconds
QByteArray reportableChange; // Data depending on the dataType
quint16 timeoutPeriod = 0x0000; // seconds, only used for direction receiving
} AttributeReportingConfiguration;
// Response of reporting configuration
typedef struct AttributeReportingStatusRecord {
ZigbeeClusterLibrary::Status status;
ReportingDirection direction = ReportingDirectionReporting;
quint16 attributeId = 0x0000;
} AttributeReportingStatusRecord;
// General parse/build methods
static quint8 buildFrameControlByte(const FrameControl &frameControl);
@ -132,12 +283,21 @@ public:
static Frame parseFrameData(const QByteArray &frameData);
static QByteArray buildFrame(const Frame &frame);
// AttributeReportingConfiguration
static QByteArray buildAttributeReportingConfiguration(const AttributeReportingConfiguration &reportingConfiguration);
// TODO: parseAttributeReportingConfiguration
static QList<AttributeReportingStatusRecord> parseAttributeReportingStatusRecords(const QByteArray &payload);
};
QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::FrameControl &frameControl);
QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::Header &header);
QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::Frame &frame);
QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::ReadAttributeStatusRecord &attributeStatusRecord);
QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::AttributeReportingConfiguration &attributeReportingConfiguration);
QDebug operator<<(QDebug debug, const ZigbeeClusterLibrary::AttributeReportingStatusRecord &attributeReportingStatusRecord);
#endif // ZIGBEECLUSTERLIBRARY_H

View File

@ -45,7 +45,7 @@ QDebug operator<<(QDebug debug, const Zigbee::ApsdeDataConfirm &confirm)
debug.nospace() << "Destination EP:" << ZigbeeUtils::convertByteToHexString(confirm.destinationEndpoint) << ", ";
debug.nospace() << "Source EP:" << ZigbeeUtils::convertByteToHexString(confirm.sourceEndpoint) << ", ";
debug.nospace() << static_cast<Zigbee::ZigbeeStatus>(confirm.zigbeeStatusCode);
debug.nospace() << static_cast<ZigbeeClusterLibrary::Status>(confirm.zigbeeStatusCode);
debug.nospace() << ")";
return debug.space();
@ -76,7 +76,7 @@ QDebug operator<<(QDebug debug, const Zigbee::ApsdeDataIndication &indication)
if (indication.profileId == static_cast<quint16>(Zigbee::ZigbeeProfileDevice)) {
debug.nospace() << static_cast<ZigbeeDeviceProfile::ZdoCommand>(indication.clusterId) << ", ";
} else {
debug.nospace() << static_cast<Zigbee::ClusterId>(indication.clusterId) << ", ";
debug.nospace() << static_cast<ZigbeeClusterLibrary::ClusterId>(indication.clusterId) << ", ";
}
debug.nospace() << "ASDU: " << ZigbeeUtils::convertByteArrayToHexString(indication.asdu) << ", ";

View File

@ -74,113 +74,6 @@ public:
Q_ENUM(ZigbeeChannel)
Q_DECLARE_FLAGS(ZigbeeChannels, ZigbeeChannel)
enum ClusterId {
// Basics
ClusterIdUnknown = 0xffff,
ClusterIdBasic = 0x0000,
ClusterIdPower = 0x0001,
ClusterIdDeviceTemperature = 0x0002,
ClusterIdIdentify = 0x0003,
ClusterIdGroups = 0x0004,
ClusterIdScenes = 0x0005,
ClusterIdOnOff = 0x0006,
ClusterIdOnOffCOnfiguration = 0x0007,
ClusterIdLevelControl = 0x0008,
ClusterIdAlarms = 0x0009,
ClusterIdTime = 0x000A,
ClusterIdRssiLocation = 0x000B,
ClusterIdAnalogInputBasic = 0x000C,
ClusterIdAnalogOutputBasic = 0x000D,
ClusterIdValueBasic = 0x000E,
ClusterIdBinaryInputBasic = 0x000F,
ClusterIdBinaryOutputBasic = 0x0010,
ClusterIdBinaryValueBasic = 0x0011,
ClusterIdMultiStateInputBasic = 0x0012,
ClusterIdMultiStateOutputBasic = 0x0013,
ClusterIdMultiStateValueBasic = 0x0014,
ClusterIdCommissoning = 0x0015,
// Over the air uppgrade (OTA)
ClusterIdOtaUpgrade = 0x0019,
// Poll controll
ClusterIdPollControl = 0x0020,
// Closures
ClusterIdShadeConfiguration = 0x0100,
// Door Lock
ClusterIdDoorLock = 0x0101,
// Heating, Ventilation and Air-Conditioning (HVAC)
ClusterIdPumpConfigurationControl = 0x0200,
ClusterIdThermostat = 0x0201,
ClusterIdFanControll = 0x0202,
ClusterIdDehumiditationControll = 0x0203,
ClusterIdThermostatUserControll = 0x0204,
// Lighting
ClusterIdColorControl = 0x0300,
ClusterIdBallastConfiguration = 0x0301,
// Sensing
ClusterIdIlluminanceMeasurement = 0x0400,
ClusterIdIlluminanceLevelSensing = 0x0401,
ClusterIdTemperatureMeasurement = 0x0402,
ClusterIdPressureMeasurement = 0x0403,
ClusterIdFlowMeasurement = 0x0404,
ClusterIdRelativeHumidityMeasurement = 0x0405,
ClusterIdOccupancySensing = 0x0406,
// Security and Safty
ClusterIdIasZone = 0x0500,
ClusterIdIasAce = 0x0501,
ClusterIdIasWd = 0x0502,
// Smart energy
ClusterIdPrice = 0x0700,
ClusterIdLoadControl = 0x0701,
ClusterIdSimpleMetering = 0x0702,
// ZLL
ClusterIdTouchlinkCommissioning = 0x1000,
// NXP Appliances
ClusterIdApplianceControl = 0x001B,
ClusterIdApplianceIdentification = 0x0B00,
ClusterIdApplianceEventsAlerts = 0x0B02,
ClusterIdApplianceStatistics = 0x0B03,
// Electrical Measurement
ClusterIdElectricalMeasurement = 0x0B04,
ClusterIdDiagnostics = 0x0B05,
// Zigbee green power
ClusterIdGreenPower = 0x0021
};
Q_ENUM(ClusterId)
enum ClusterAttributeBasic {
ClusterAttributeBasicZclVersion = 0x0000,
ClusterAttributeBasicApplicationVersion = 0x0001,
ClusterAttributeBasicStackVersion = 0x0002,
ClusterAttributeBasicHardwareVersion = 0x0003,
ClusterAttributeBasicManufacturerName = 0x0004,
ClusterAttributeBasicModelIdentifier = 0x0005,
ClusterAttributeBasicDataCode = 0x0006,
ClusterAttributeBasicPowerSource = 0x0007,
ClusterAttributeBasicLocationDescription = 0x0010,
ClusterAttributeBasicPhysicalEnvironment = 0x0011,
ClusterAttributeBasicDeviceEnabled = 0x0012,
ClusterAttributeBasicAlarmMask = 0x0013,
ClusterAttributeBasicDisableLocalConfig = 0x0014,
ClusterAttributeBasicSoftwareBuildId = 0x4000
};
Q_ENUM(ClusterAttributeBasic)
enum LightLinkDevice {
// Lightning devices
LightLinkDeviceOnOffLight = 0x0000,
@ -350,17 +243,6 @@ public:
};
Q_ENUM(SourceAddressMode)
enum ZigbeeZclStatus {
};
Q_ENUM(ZigbeeZclStatus)
enum ZigbeeZdpStatus {
};
Q_ENUM(ZigbeeZdpStatus)
enum ZigbeeTxOption {
ZigbeeTxOptionSecurityEnabled = 0x01,
ZigbeeTxOptionUseNetworkKey = 0x02,
@ -434,45 +316,6 @@ public:
};
Q_ENUM(ZigbeeApsStatus)
enum ZigbeeStatus {
ZigbeeStatusSuccess = 0x00,
ZigbeeStatusFailure = 0x01,
ZigbeeStatusNotAuthorized = 0x7e,
ZigbeeStatusReservedFieldNotZero = 0x7f,
ZigbeeStatusMalformedCommand = 0x80,
ZigbeeStatusUnsupportedClusterCommand = 0x81,
ZigbeeStatusUnsupportedGeneralCommand = 0x82,
ZigbeeStatusUnsupportedManufacturerClusterCommand = 0x83,
ZigbeeStatusUnsupportedManufacturerGeneralCommand = 0x84,
ZigbeeStatusInvalidField = 0x85,
ZigbeeStatusUnsupportedAttribute = 0x86,
ZigbeeStatusInvalidValue = 0x87,
ZigbeeStatusReadOnly = 0x88,
ZigbeeStatusInsufficientSpace = 0x89,
ZigbeeStatusDuplicateExists = 0x8a,
ZigbeeStatusNotFound = 0x8b,
ZigbeeStatusUnreportableAttribute = 0x8c,
ZigbeeStatusInvalidDataType = 0x8d,
ZigbeeStatusInvalidSector = 0x8e,
ZigbeeStatusWriteOnly = 0x8f,
ZigbeeStatusInconsistentStartupState = 0x90,
ZigbeeStatusDefinedOutOfBand = 0x91,
ZigbeeStatusInconsistent = 0x92,
ZigbeeStatusActionDenied = 0x93,
ZigbeeStatusTimeout = 0x94,
ZigbeeStatusAbort = 0x95,
ZigbeeStatusInvalidImage = 0x96,
ZigbeeStatusWaitForData = 0x97,
ZigbeeStatusNoImageAvailable = 0x98,
ZigbeeStatusRequireMoreImage = 0x99,
ZigbeeStatusNotificationPending = 0x9a,
ZigbeeStatusHardwareFailure = 0xc0,
ZigbeeStatusSoftwareFailure = 0xc1,
ZigbeeStatusCalibrationError = 0xc2,
ZigbeeStatusUnsupportedCluster = 0xc3
};
Q_ENUM(ZigbeeStatus)
// Basic struct for interface data.
typedef struct ApsdeDataConfirm {
quint8 requestId = 0;

View File

@ -303,7 +303,7 @@ void ZigbeeNetwork::saveNetwork()
{
qCDebug(dcZigbeeNetwork()) << "Save current network configuration to" << m_settingsFileName;
QSettings settings(m_settingsFileName, QSettings::IniFormat, this);
settings.beginGroup("Network");
settings.beginGroup("ZigbeeNetwork");
settings.setValue("panId", panId());
settings.setValue("channel", channel());
settings.setValue("networkKey", securityConfiguration().networkKey().toString());
@ -321,7 +321,7 @@ void ZigbeeNetwork::loadNetwork()
}
QSettings settings(m_settingsFileName, QSettings::IniFormat, this);
settings.beginGroup("Network");
settings.beginGroup("ZigbeeNetwork");
quint16 panId = static_cast<quint16>(settings.value("panId", 0).toUInt());
setPanId(panId);
setChannel(settings.value("channel", 0).toUInt());
@ -535,6 +535,7 @@ void ZigbeeNetwork::onNodeStateChanged(ZigbeeNode::State state)
ZigbeeNode *node = qobject_cast<ZigbeeNode *>(sender());
if (state == ZigbeeNode::StateInitialized && m_uninitializedNodes.contains(node)) {
m_uninitializedNodes.removeAll(node);
// Disconnect this slot since we don't need it any more
disconnect(node, &ZigbeeNode::stateChanged, this, &ZigbeeNetwork::onNodeStateChanged);
addNode(node);
}

View File

@ -55,6 +55,14 @@ ZigbeeNetworkDatabase::ZigbeeNetworkDatabase(ZigbeeNetwork *network, const QStri
}
}
ZigbeeNetworkDatabase::~ZigbeeNetworkDatabase()
{
if (m_db.isOpen()) {
qCDebug(dcZigbeeNetworkDatabase()) << "Closing database" << m_db.databaseName();
m_db.close();
}
}
QList<ZigbeeNode *> ZigbeeNetworkDatabase::loadNodes()
{
qCDebug(dcZigbeeNetworkDatabase()) << "Loading nodes from database" << m_db.databaseName();
@ -93,7 +101,7 @@ QList<ZigbeeNode *> ZigbeeNetworkDatabase::loadNodes()
.arg(endpointId);
QSqlQuery inputClustersQuery = m_db.exec(query);
while (inputClustersQuery.next()) {
Zigbee::ClusterId clusterId = static_cast<Zigbee::ClusterId>(inputClustersQuery.value("clusterId").toUInt());
ZigbeeClusterLibrary::ClusterId clusterId = static_cast<ZigbeeClusterLibrary::ClusterId>(inputClustersQuery.value("clusterId").toUInt());
ZigbeeCluster *cluster = endpoint->createCluster(clusterId, ZigbeeCluster::Server);
endpoint->addInputCluster(cluster);
@ -122,8 +130,8 @@ QList<ZigbeeNode *> ZigbeeNetworkDatabase::loadNodes()
}
// Set the basic cluster attributes if present
if (endpoint->hasInputCluster(Zigbee::ClusterIdBasic)) {
ZigbeeClusterBasic *basicCluster = endpoint->inputCluster<ZigbeeClusterBasic>(Zigbee::ClusterIdBasic);
if (endpoint->hasInputCluster(ZigbeeClusterLibrary::ClusterIdBasic)) {
ZigbeeClusterBasic *basicCluster = endpoint->inputCluster<ZigbeeClusterBasic>(ZigbeeClusterLibrary::ClusterIdBasic);
if (basicCluster->hasAttribute(ZigbeeClusterBasic::AttributeManufacturerName))
endpoint->m_manufacturerName = basicCluster->attribute(ZigbeeClusterBasic::AttributeManufacturerName).dataType().toString();
@ -142,7 +150,7 @@ QList<ZigbeeNode *> ZigbeeNetworkDatabase::loadNodes()
.arg(endpointId);
QSqlQuery outputClustersQuery = m_db.exec(query);
while (outputClustersQuery.next()) {
Zigbee::ClusterId clusterId = static_cast<Zigbee::ClusterId>(outputClustersQuery.value("clusterId").toUInt());
ZigbeeClusterLibrary::ClusterId clusterId = static_cast<ZigbeeClusterLibrary::ClusterId>(outputClustersQuery.value("clusterId").toUInt());
ZigbeeCluster *cluster = endpoint->createCluster(clusterId, ZigbeeCluster::Client);
qCDebug(dcZigbeeNetworkDatabase()) << "Loaded" << cluster;
endpoint->addOutputCluster(cluster);
@ -178,70 +186,89 @@ bool ZigbeeNetworkDatabase::initDatabase()
return false;
}
// Write pragmas
m_db.exec("PRAGMA foreign_keys = ON;");
// FIXME: check schema version
// FIXME: check schema version fro compatibility or migration
qCDebug(dcZigbeeNetworkDatabase()) << "Tables" << m_db.tables();
if (m_db.tables().isEmpty()) {
// Write pragmas
m_db.exec("PRAGMA foreign_keys = ON;");
m_db.exec(QString("PRAGMA schema_version = %1;").arg(DB_VERSION));
m_db.exec(QString("PRAGMA user_version = %1;").arg(DB_VERSION));
}
// Create nodes table
createTable("nodes",
"(ieeeAddress TEXT PRIMARY KEY, " // ieeeAddress to string
"shortAddress INTEGER NOT NULL, " // uint16
"nodeDescriptor BLOB NOT NULL, " // bytes as received from the node
"powerDescriptor INTEGER NOT NULL)"); // uint16
createIndices("ieeeAddressIndex", "nodes", "ieeeAddress");
if (!m_db.tables().contains("nodes")) {
createTable("nodes",
"(ieeeAddress TEXT PRIMARY KEY, " // ieeeAddress to string
"shortAddress INTEGER NOT NULL, " // uint16
"nodeDescriptor BLOB NOT NULL, " // bytes as received from the node
"powerDescriptor INTEGER NOT NULL)"); // uint16
createIndices("ieeeAddressIndex", "nodes", "ieeeAddress");
}
// Create endpoints table
createTable("endpoints",
"(id INTEGER PRIMARY KEY AUTOINCREMENT, " // for db relation
"ieeeAddress INTEGER NOT NULL, " // // reference to nodes.ieeeAddress
"endpointId INTEGER NOT NULL, " // uint8
"profileId INTEGER NOT NULL, " // uint16
"deviceId INTEGER NOT NULL, " // uint16
"deviceVersion INTEGER, " // uint8
"CONSTRAINT fk_ieeeAddress FOREIGN KEY(ieeeAddress) REFERENCES nodes(ieeeAddress) ON DELETE CASCADE)");
createIndices("endpointIndex", "endpoints", "ieeeAddress, endpointId");
if (!m_db.tables().contains("endpoints")) {
createTable("endpoints",
"(id INTEGER PRIMARY KEY AUTOINCREMENT, " // for db relation
"ieeeAddress INTEGER NOT NULL, " // // reference to nodes.ieeeAddress
"endpointId INTEGER NOT NULL, " // uint8
"profileId INTEGER NOT NULL, " // uint16
"deviceId INTEGER NOT NULL, " // uint16
"deviceVersion INTEGER, " // uint8
"CONSTRAINT fk_ieeeAddress FOREIGN KEY(ieeeAddress) REFERENCES nodes(ieeeAddress) ON DELETE CASCADE)");
createIndices("endpointIndex", "endpoints", "ieeeAddress, endpointId");
}
// Create server cluster table
createTable("serverClusters",
"(id INTEGER PRIMARY KEY AUTOINCREMENT, " // for db relation
"endpointId INTEGER NOT NULL, " // reference to endpoint.id
"clusterId INTEGER NOT NULL, " // uint16
"CONSTRAINT fk_endpoint FOREIGN KEY(endpointId) REFERENCES endpoints(id) ON DELETE CASCADE)");
createIndices("serverClusterIndex", "serverClusters", "endpointId, clusterId");
if (!m_db.tables().contains("serverClusters")) {
createTable("serverClusters",
"(id INTEGER PRIMARY KEY AUTOINCREMENT, " // for db relation
"endpointId INTEGER NOT NULL, " // reference to endpoint.id
"clusterId INTEGER NOT NULL, " // uint16
"CONSTRAINT fk_endpoint FOREIGN KEY(endpointId) REFERENCES endpoints(id) ON DELETE CASCADE)");
createIndices("serverClusterIndex", "serverClusters", "endpointId, clusterId");
}
// Create client cluster table
createTable("clientClusters",
"(id INTEGER PRIMARY KEY AUTOINCREMENT, " // for db relation
"endpointId INTEGER NOT NULL, " // reference to endpoint.id
"clusterId INTEGER NOT NULL, " // uint16
"CONSTRAINT fk_endpoint FOREIGN KEY(endpointId) REFERENCES endpoints(id) ON DELETE CASCADE)");
createIndices("clientClusterIndex", "clientClusters", "endpointId, clusterId");
if (!m_db.tables().contains("clientClusters")) {
createTable("clientClusters",
"(id INTEGER PRIMARY KEY AUTOINCREMENT, " // for db relation
"endpointId INTEGER NOT NULL, " // reference to endpoint.id
"clusterId INTEGER NOT NULL, " // uint16
"CONSTRAINT fk_endpoint FOREIGN KEY(endpointId) REFERENCES endpoints(id) ON DELETE CASCADE)");
createIndices("clientClusterIndex", "clientClusters", "endpointId, clusterId");
}
// Create cluster attributes table
createTable("attributes",
"(id INTEGER PRIMARY KEY AUTOINCREMENT, " // for db relation
"clusterId INTEGER NOT NULL, " // reference to serverClusters.id
"attributeId INTEGER NOT NULL, " // uint16
"dataType INTEGER NOT NULL, " // uint8
"data BLOB NOT NULL, " // raw data from attribute
"CONSTRAINT fk_cluster FOREIGN KEY(clusterId) REFERENCES serverClusters(id) ON DELETE CASCADE)");
createIndices("attributesIndex", "attributes", "clusterId, attributeId");
if (!m_db.tables().contains("attributes")) {
createTable("attributes",
"(id INTEGER PRIMARY KEY AUTOINCREMENT, " // for db relation
"clusterId INTEGER NOT NULL, " // reference to serverClusters.id
"attributeId INTEGER NOT NULL, " // uint16
"dataType INTEGER NOT NULL, " // uint8
"data BLOB NOT NULL, " // raw data from attribute
"CONSTRAINT fk_cluster FOREIGN KEY(clusterId) REFERENCES serverClusters(id) ON DELETE CASCADE)");
createIndices("attributesIndex", "attributes", "clusterId, attributeId");
}
return true;
}
void ZigbeeNetworkDatabase::createTable(const QString &tableName, const QString &schema)
{
m_db.exec(QString("CREATE TABLE IF NOT EXISTS %1 %2;").arg(tableName).arg(schema));
m_db.exec(QString("PRAGMA schema_version = %1;").arg(DB_VERSION));
m_db.exec(QString("PRAGMA user_version = %1;").arg(DB_VERSION));
qCDebug(dcZigbeeNetworkDatabase()) << "Creating table" << tableName << schema;
QString query = QString("CREATE TABLE IF NOT EXISTS %1 %2;").arg(tableName).arg(schema);
m_db.exec(query);
if (m_db.lastError().type() != QSqlError::NoError) {
qCWarning(dcZigbeeNetworkDatabase()) << "Could not create table in database." << query << m_db.lastError().databaseText() << m_db.lastError().driverText();
return;
}
}
void ZigbeeNetworkDatabase::createIndices(const QString &indexName, const QString &tableName, const QString &columns)
{
qCDebug(dcZigbeeNetworkDatabase()) << "Creating table indices" << indexName << tableName << columns;
m_db.exec(QString("CREATE UNIQUE INDEX IF NOT EXISTS %1 ON %2(%3);").arg(indexName).arg(tableName).arg(columns));
}
@ -256,9 +283,10 @@ bool ZigbeeNetworkDatabase::saveNodeEndpoint(ZigbeeNodeEndpoint *endpoint)
.arg(static_cast<quint16>(endpoint->deviceId()))
.arg(static_cast<quint8>(endpoint->deviceVersion()));
qCDebug(dcZigbeeNetworkDatabase()) << queryString;
m_db.exec(queryString);
if (m_db.lastError().type() != QSqlError::NoError) {
qCWarning(dcZigbeeNetworkDatabase()) << "Could not save node into database." << queryString << m_db.lastError().databaseText() << m_db.lastError().driverText();
qCWarning(dcZigbeeNetworkDatabase()) << "Could not save endpoint into database." << queryString << m_db.lastError().databaseText() << m_db.lastError().driverText();
return false;
}
@ -378,7 +406,7 @@ bool ZigbeeNetworkDatabase::removeNode(ZigbeeNode *node)
{
qCDebug(dcZigbeeNetworkDatabase()) << "Remove" << node;
// Note: cascade delete will clean up all other tables
QString queryString = QString("DELETE FROM nodes WHERE ieeeAddress = %1;").arg(node->extendedAddress().toString());
QString queryString = QString("DELETE FROM nodes WHERE ieeeAddress = \"%1\";").arg(node->extendedAddress().toString());
m_db.exec(queryString);
if (m_db.lastError().type() != QSqlError::NoError) {
qCWarning(dcZigbeeNetworkDatabase()) << "Could not remove node from database." << queryString << m_db.lastError().databaseText() << m_db.lastError().driverText();

View File

@ -44,6 +44,7 @@ class ZigbeeNetworkDatabase : public QObject
Q_OBJECT
public:
explicit ZigbeeNetworkDatabase(ZigbeeNetwork *network, const QString &databaseName, QObject *parent = nullptr);
~ZigbeeNetworkDatabase();
QList<ZigbeeNode *> loadNodes();
bool wipeDatabase();

View File

@ -32,6 +32,11 @@
#include <QDateTime>
QStringList ZigbeeNetworkManager::availableBackendTypes()
{
return {"deCONZ"};
}
ZigbeeNetwork *ZigbeeNetworkManager::createZigbeeNetwork(ZigbeeNetworkManager::BackendType backend, QObject *parent)
{
// Note: required for generating random PAN ID

View File

@ -25,8 +25,8 @@
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef ZIGBEEMANAGER_H
#define ZIGBEEMANAGER_H
#ifndef ZIGBEENETWORKMANAGER_H
#define ZIGBEENETWORKMANAGER_H
#include <QObject>
@ -39,7 +39,9 @@ public:
BackendTypeDeconz
};
static QStringList availableBackendTypes();
static ZigbeeNetwork *createZigbeeNetwork(BackendType backend, QObject *parent = nullptr);
};
#endif // ZIGBEEMANAGER_H
#endif // ZIGBEENETWORKMANAGER_H

View File

@ -151,7 +151,7 @@ QDebug operator<<(QDebug debug, const ZigbeeNetworkRequest &request)
if (request.profileId() == Zigbee::ZigbeeProfileDevice) {
debug.nospace() << static_cast<ZigbeeDeviceProfile::ZdoCommand>(request.clusterId()) << ", ";
} else {
debug.nospace() << static_cast<Zigbee::ClusterId>(request.clusterId()) << ", ";
debug.nospace() << static_cast<ZigbeeClusterLibrary::ClusterId>(request.clusterId()) << ", ";
}
if (request.destinationAddressMode() == Zigbee::DestinationAddressModeGroup)

View File

@ -280,7 +280,7 @@ void ZigbeeNode::initEndpoint(quint8 endpointId)
if (m_requestRetry < 3) {
m_requestRetry++;
qCDebug(dcZigbeeNode()) << "Retry to request simple descriptor from" << this << ZigbeeUtils::convertByteToHexString(endpointId) << m_requestRetry << "/" << "3 attempts.";
initEndpoints();
initEndpoint(endpointId);
} else {
qCWarning(dcZigbeeNode()) << "Failed to read simple descriptor from" << this << ZigbeeUtils::convertByteToHexString(endpointId) << "after 3 attempts. Giving up.";
m_requestRetry = 0;
@ -338,10 +338,10 @@ void ZigbeeNode::initEndpoint(quint8 endpointId)
for (int i = 0; i < inputClusterCount; i++) {
quint16 clusterId = 0;
stream >> clusterId;
if (!endpoint->hasInputCluster(static_cast<Zigbee::ClusterId>(clusterId))) {
endpoint->addInputCluster(endpoint->createCluster(static_cast<Zigbee::ClusterId>(clusterId), ZigbeeCluster::Server));
if (!endpoint->hasInputCluster(static_cast<ZigbeeClusterLibrary::ClusterId>(clusterId))) {
endpoint->addInputCluster(endpoint->createCluster(static_cast<ZigbeeClusterLibrary::ClusterId>(clusterId), ZigbeeCluster::Server));
}
qCDebug(dcZigbeeNode()) << " Cluster ID:" << ZigbeeUtils::convertUint16ToHexString(clusterId) << ZigbeeUtils::clusterIdToString(static_cast<Zigbee::ClusterId>(clusterId));
qCDebug(dcZigbeeNode()) << " Cluster ID:" << ZigbeeUtils::convertUint16ToHexString(clusterId) << ZigbeeUtils::clusterIdToString(static_cast<ZigbeeClusterLibrary::ClusterId>(clusterId));
}
// Parse and add client clusters
@ -350,10 +350,10 @@ void ZigbeeNode::initEndpoint(quint8 endpointId)
for (int i = 0; i < outputClusterCount; i++) {
quint16 clusterId = 0;
stream >> clusterId;
if (!endpoint->hasOutputCluster(static_cast<Zigbee::ClusterId>(clusterId))) {
endpoint->addOutputCluster(endpoint->createCluster(static_cast<Zigbee::ClusterId>(clusterId), ZigbeeCluster::Client));
if (!endpoint->hasOutputCluster(static_cast<ZigbeeClusterLibrary::ClusterId>(clusterId))) {
endpoint->addOutputCluster(endpoint->createCluster(static_cast<ZigbeeClusterLibrary::ClusterId>(clusterId), ZigbeeCluster::Client));
}
qCDebug(dcZigbeeNode()) << " Cluster ID:" << ZigbeeUtils::convertUint16ToHexString(clusterId) << ZigbeeUtils::clusterIdToString(static_cast<Zigbee::ClusterId>(clusterId));
qCDebug(dcZigbeeNode()) << " Cluster ID:" << ZigbeeUtils::convertUint16ToHexString(clusterId) << ZigbeeUtils::clusterIdToString(static_cast<ZigbeeClusterLibrary::ClusterId>(clusterId));
}
m_uninitializedEndpoints.removeAll(endpointId);
@ -380,7 +380,7 @@ void ZigbeeNode::initBasicCluster()
// Get the first endpoint which implements the basic cluster
ZigbeeNodeEndpoint *endpoint = nullptr;
foreach (ZigbeeNodeEndpoint *ep, endpoints()) {
if (ep->hasInputCluster(Zigbee::ClusterIdBasic)) {
if (ep->hasInputCluster(ZigbeeClusterLibrary::ClusterIdBasic)) {
endpoint = ep;
break;
}
@ -392,7 +392,7 @@ void ZigbeeNode::initBasicCluster()
return;
}
ZigbeeClusterBasic *basicCluster = endpoint->inputCluster<ZigbeeClusterBasic>(Zigbee::ClusterIdBasic);
ZigbeeClusterBasic *basicCluster = endpoint->inputCluster<ZigbeeClusterBasic>(ZigbeeClusterLibrary::ClusterIdBasic);
if (!basicCluster) {
qCWarning(dcZigbeeNode()) << "Could not find basic cluster on" << this << "Set the node to initialized anyways.";
// Set the device initialized any ways since this ist just for convinience
@ -527,6 +527,11 @@ void ZigbeeNode::readSoftwareBuildId(ZigbeeClusterBasic *basicCluster)
// Finished with reading basic cluster, the node is initialized.
// TODO: read other interesting cluster information
// Bind client clusters to the sensor group
// Configure reporting
setState(StateInitialized);
});
}

View File

@ -114,6 +114,9 @@ private:
void readModelIdentifier(ZigbeeClusterBasic *basicCluster);
void readSoftwareBuildId(ZigbeeClusterBasic *basicCluster);
signals:
void nodeInitializationFailed();
void stateChanged(State state);

View File

@ -97,12 +97,12 @@ QList<ZigbeeCluster *> ZigbeeNodeEndpoint::inputClusters() const
return m_inputClusters.values();
}
ZigbeeCluster *ZigbeeNodeEndpoint::getInputCluster(Zigbee::ClusterId clusterId) const
ZigbeeCluster *ZigbeeNodeEndpoint::getInputCluster(ZigbeeClusterLibrary::ClusterId clusterId) const
{
return m_inputClusters.value(clusterId);
}
bool ZigbeeNodeEndpoint::hasInputCluster(Zigbee::ClusterId clusterId) const
bool ZigbeeNodeEndpoint::hasInputCluster(ZigbeeClusterLibrary::ClusterId clusterId) const
{
return m_inputClusters.keys().contains(clusterId);
}
@ -112,12 +112,12 @@ QList<ZigbeeCluster *> ZigbeeNodeEndpoint::outputClusters() const
return m_outputClusters.values();
}
ZigbeeCluster *ZigbeeNodeEndpoint::getOutputCluster(Zigbee::ClusterId clusterId) const
ZigbeeCluster *ZigbeeNodeEndpoint::getOutputCluster(ZigbeeClusterLibrary::ClusterId clusterId) const
{
return m_outputClusters.value(clusterId);
}
bool ZigbeeNodeEndpoint::hasOutputCluster(Zigbee::ClusterId clusterId) const
bool ZigbeeNodeEndpoint::hasOutputCluster(ZigbeeClusterLibrary::ClusterId clusterId) const
{
return m_outputClusters.keys().contains(clusterId);
}
@ -163,48 +163,52 @@ void ZigbeeNodeEndpoint::setSoftwareBuildId(const QString &softwareBuildId)
emit softwareBuildIdChanged(m_softwareBuildId);
}
ZigbeeCluster *ZigbeeNodeEndpoint::createCluster(Zigbee::ClusterId clusterId, ZigbeeCluster::Direction direction)
ZigbeeCluster *ZigbeeNodeEndpoint::createCluster(ZigbeeClusterLibrary::ClusterId clusterId, ZigbeeCluster::Direction direction)
{
switch (clusterId) {
// General
case Zigbee::ClusterIdBasic:
case ZigbeeClusterLibrary::ClusterIdBasic:
return new ZigbeeClusterBasic(m_network, m_node, this, direction, this);
break;
case Zigbee::ClusterIdOnOff:
return new ZigbeeClusterOnOff(m_network, m_node, this, direction, this);
case ZigbeeClusterLibrary::ClusterIdPowerConfiguration:
return new ZigbeeClusterPowerConfiguration(m_network, m_node, this, direction, this);
break;
case Zigbee::ClusterIdIdentify:
case ZigbeeClusterLibrary::ClusterIdIdentify:
return new ZigbeeClusterIdentify(m_network, m_node, this, direction, this);
break;
case Zigbee::ClusterIdLevelControl:
case ZigbeeClusterLibrary::ClusterIdOnOff:
return new ZigbeeClusterOnOff(m_network, m_node, this, direction, this);
break;
case ZigbeeClusterLibrary::ClusterIdLevelControl:
return new ZigbeeClusterLevelControl(m_network, m_node, this, direction, this);
break;
// Measurement
case Zigbee::ClusterIdTemperatureMeasurement:
case ZigbeeClusterLibrary::ClusterIdIlluminanceMeasurement:
return new ZigbeeClusterIlluminanceMeasurment(m_network, m_node, this, direction, this);
break;
case ZigbeeClusterLibrary::ClusterIdTemperatureMeasurement:
return new ZigbeeClusterTemperatureMeasurement(m_network, m_node, this, direction, this);
break;
case Zigbee::ClusterIdRelativeHumidityMeasurement:
case ZigbeeClusterLibrary::ClusterIdRelativeHumidityMeasurement:
return new ZigbeeClusterRelativeHumidityMeasurement(m_network, m_node, this, direction, this);
break;
case Zigbee::ClusterIdOccupancySensing:
case ZigbeeClusterLibrary::ClusterIdOccupancySensing:
return new ZigbeeClusterOccupancySensing(m_network, m_node, this, direction, this);
break;
case Zigbee::ClusterIdIlluminanceMeasurement:
return new ZigbeeClusterIlluminanceMeasurment(m_network, m_node, this, direction, this);
break;
// Lighting
case Zigbee::ClusterIdColorControl:
case ZigbeeClusterLibrary::ClusterIdColorControl:
return new ZigbeeClusterColorControl(m_network, m_node, this, direction, this);
break;
// Security
case Zigbee::ClusterIdIasZone:
case ZigbeeClusterLibrary::ClusterIdIasZone:
return new ZigbeeClusterIasZone(m_network, m_node, this, direction, this);
break;
default:
// Return a default cluster since we have no special implementation for this cluster
// Return a default cluster since we have no special implementation for this cluster, allowing to use generic clusters operations
return new ZigbeeCluster(m_network, m_node, this, clusterId, direction, this);
}
}
@ -237,18 +241,18 @@ void ZigbeeNodeEndpoint::handleZigbeeClusterLibraryIndication(const Zigbee::Apsd
switch (frame.header.frameControl.direction) {
case ZigbeeClusterLibrary::DirectionClientToServer:
// Get the output/client cluster this indication is coming from
cluster = getOutputCluster(static_cast<Zigbee::ClusterId>(indication.clusterId));
cluster = getOutputCluster(static_cast<ZigbeeClusterLibrary::ClusterId>(indication.clusterId));
if (!cluster) {
cluster = createCluster(static_cast<Zigbee::ClusterId>(indication.clusterId), ZigbeeCluster::Client);
cluster = createCluster(static_cast<ZigbeeClusterLibrary::ClusterId>(indication.clusterId), ZigbeeCluster::Client);
qCWarning(dcZigbeeEndpoint()) << "Received a ZCL indication for a cluster which does not exist yet on" << m_node << this << "Creating" << cluster;
addOutputCluster(cluster);
}
break;
case ZigbeeClusterLibrary::DirectionServerToClient:
// Get the input/server cluster this indication is coming from
cluster = getInputCluster(static_cast<Zigbee::ClusterId>(indication.clusterId));
cluster = getInputCluster(static_cast<ZigbeeClusterLibrary::ClusterId>(indication.clusterId));
if (!cluster) {
cluster = createCluster(static_cast<Zigbee::ClusterId>(indication.clusterId), ZigbeeCluster::Server);
cluster = createCluster(static_cast<ZigbeeClusterLibrary::ClusterId>(indication.clusterId), ZigbeeCluster::Server);
qCWarning(dcZigbeeEndpoint()) << "Received a ZCL indication for a cluster which does not exist yet on" << m_node << this << "Creating" << cluster;
addInputCluster(cluster);
}

View File

@ -41,6 +41,7 @@
#include "zcl/general/zigbeeclusteronoff.h"
#include "zcl/general/zigbeeclusteridentify.h"
#include "zcl/general/zigbeeclusterlevelcontrol.h"
#include "zcl/general/zigbeeclusterpowerconfiguration.h"
#include "zcl/measurement/zigbeeclusteroccupancysensing.h"
#include "zcl/measurement/zigbeeclusterilluminancemeasurment.h"
@ -85,17 +86,17 @@ public:
// Server clusters
QList<ZigbeeCluster *> inputClusters() const;
ZigbeeCluster *getInputCluster(Zigbee::ClusterId clusterId) const;
bool hasInputCluster(Zigbee::ClusterId clusterId) const;
ZigbeeCluster *getInputCluster(ZigbeeClusterLibrary::ClusterId clusterId) const;
bool hasInputCluster(ZigbeeClusterLibrary::ClusterId clusterId) const;
// Client clusters
QList<ZigbeeCluster *> outputClusters() const;
ZigbeeCluster *getOutputCluster(Zigbee::ClusterId clusterId) const;
bool hasOutputCluster(Zigbee::ClusterId clusterId) const;
ZigbeeCluster *getOutputCluster(ZigbeeClusterLibrary::ClusterId clusterId) const;
bool hasOutputCluster(ZigbeeClusterLibrary::ClusterId clusterId) const;
// Convinience cast methods for getting a specific cluster object
template<typename T>
inline T* inputCluster(Zigbee::ClusterId clusterId)
inline T* inputCluster(ZigbeeClusterLibrary::ClusterId clusterId)
{
if (!hasInputCluster(clusterId))
return nullptr;
@ -104,7 +105,7 @@ public:
}
template<typename T>
inline T* outputCluster(Zigbee::ClusterId clusterId)
inline T* outputCluster(ZigbeeClusterLibrary::ClusterId clusterId)
{
if (!hasOutputCluster(clusterId))
return nullptr;
@ -124,8 +125,8 @@ private:
quint8 m_deviceVersion = 0;
bool m_initialized = false;
QHash<Zigbee::ClusterId, ZigbeeCluster *> m_inputClusters;
QHash<Zigbee::ClusterId, ZigbeeCluster *> m_outputClusters;
QHash<ZigbeeClusterLibrary::ClusterId, ZigbeeCluster *> m_inputClusters;
QHash<ZigbeeClusterLibrary::ClusterId, ZigbeeCluster *> m_outputClusters;
QString m_manufacturerName;
QString m_modelIdentifier;
@ -135,7 +136,7 @@ private:
void setModelIdentifier(const QString &modelIdentifier);
void setSoftwareBuildId(const QString &softwareBuildId);
ZigbeeCluster *createCluster(Zigbee::ClusterId clusterId, ZigbeeCluster::Direction direction);
ZigbeeCluster *createCluster(ZigbeeClusterLibrary::ClusterId clusterId, ZigbeeCluster::Direction direction);
void addInputCluster(ZigbeeCluster *cluster);
void addOutputCluster(ZigbeeCluster *cluster);

View File

@ -34,24 +34,7 @@
#include <math.h>
// Disabling 1000 to 2500 K, as we never want to go into full red for color temp
static QList<QColor> colorTemperatureScale = {
// QColor(255, 56, 0), // 1000 K
// QColor(255, 71, 0),
// QColor(255, 83, 0),
// QColor(255, 93, 0),
// QColor(255, 101, 0),
// QColor(255, 109, 0),
// QColor(255, 115, 0),
// QColor(255, 121, 0),
// QColor(255, 126, 0),
// QColor(255, 131, 0),
// QColor(255, 138, 18),
// QColor(255, 142, 33),
// QColor(255, 147, 44),
// QColor(255, 152, 54),
// QColor(255, 157, 63),
// QColor(255, 161, 72), // 2500K
QColor(255, 165, 79),
QColor(255, 169, 87),
QColor(255, 173, 94),
@ -260,23 +243,12 @@ QString ZigbeeUtils::convertUint64ToHexString(const quint64 &value)
return QString("0x%1").arg(convertByteArrayToHexString(data).remove(" ").remove("0x"));
}
//QString ZigbeeUtils::messageTypeToString(const Zigbee::InterfaceMessageType &type)
//{
// QMetaObject metaObject = Zigbee::staticMetaObject;
// QMetaEnum metaEnum = metaObject.enumerator(metaObject.indexOfEnumerator("InterfaceMessageType"));
// QString enumString = metaEnum.valueToKey(type);
// return enumString.remove("Zigbee::InterfaceMessageType(MessageType").remove(")");
//}
QString ZigbeeUtils::clusterIdToString(const Zigbee::ClusterId &clusterId)
QString ZigbeeUtils::clusterIdToString(const ZigbeeClusterLibrary::ClusterId &clusterId)
{
QMetaObject metaObject = Zigbee::staticMetaObject;
QMetaObject metaObject = ZigbeeClusterLibrary::staticMetaObject;
QMetaEnum metaEnum = metaObject.enumerator(metaObject.indexOfEnumerator("ClusterId"));
QString enumString = metaEnum.valueToKey(clusterId);
QString clusterName = enumString.remove("Zigbee::ClusterId(ClusterId").remove(")").append(QString("(%1)").arg(ZigbeeUtils::convertUint16ToHexString(clusterId)));
QString clusterName = enumString.remove("ZigbeeClusterLibrary::ClusterId(ClusterId").remove(")");
if (clusterName.isEmpty())
clusterName = "Unknown";
@ -287,9 +259,7 @@ QString ZigbeeUtils::profileIdToString(const Zigbee::ZigbeeProfile &profileId)
{
QMetaObject metaObject = Zigbee::staticMetaObject;
QMetaEnum metaEnum = metaObject.enumerator(metaObject.indexOfEnumerator("ZigbeeProfile"));
QString enumString = metaEnum.valueToKey(profileId);
return enumString.remove("Zigbee::ZigbeeProfile(ZigbeeProfile").remove(")");
}
@ -330,6 +300,12 @@ QPointF ZigbeeUtils::convertColorToXY(const QColor &color)
return QPointF(x, y);
}
QPoint ZigbeeUtils::convertColorToXYInt(const QColor &color)
{
QPointF xyColor = convertColorToXY(color);
return QPoint(qRound(xyColor.x() * 65536), qRound(xyColor.y() * 65536));
}
QColor ZigbeeUtils::convertXYToColor(const QPointF &xyColor)
{
// https://developers.meethue.com/develop/application-design-guidance/color-conversion-formulas-rgb-to-xy-and-back/
@ -429,81 +405,3 @@ QColor ZigbeeUtils::interpolateColorFromColorTemperature(int colorTemperature, i
// FIXME: interpolate between the selected index and the next color for more accuracy if required
return colorTemperatureScale.at(closestColorIndex);
}
ZigbeeClusterAttributeReport ZigbeeUtils::parseAttributeReport(const QByteArray &data)
{
QByteArray dataCopy = data;
quint8 sequenceNumber = 0;
quint16 sourceAddress = 0;
quint8 endpointId = 0;
quint16 clusterId = 0;
quint16 attributeId = 0;
quint8 attributeStatus = 0;
quint8 attributDataType = 0;
quint16 dataSize = 0;
QDataStream stream(&dataCopy, QIODevice::ReadOnly);
stream >> sequenceNumber >> sourceAddress >> endpointId >> clusterId >> attributeId >> attributeStatus >> attributDataType >> dataSize;
Zigbee::DataType dataType = static_cast<Zigbee::DataType>(attributDataType);
QByteArray attributeData = data.right(dataSize);
if (attributeData.length() != dataSize) {
//qCWarning(dcZigbeeNetwork()) << "HACK" << attributeData.length() << "!=" << dataSize;
// Note: the NXP firmware for JN5169 has a bug here and does not send the attributeStatus.
// Repars data without attribute status
sequenceNumber = 0;
sourceAddress = 0;
endpointId = 0;
clusterId = 0;
attributeId = 0;
attributeStatus = 0;
attributDataType = 0;
dataSize = 0;
QDataStream alternativeStream(&dataCopy, QIODevice::ReadOnly);
alternativeStream >> sequenceNumber >> sourceAddress >> endpointId >> clusterId >> attributeId >> attributDataType >> dataSize;
dataType = static_cast<Zigbee::DataType>(attributDataType);
attributeData = data.right(dataSize);
}
// qCDebug(dcZigbeeNetwork()) << "Attribute read response:";
// qCDebug(dcZigbeeNetwork()) << " SQN:" << ZigbeeUtils::convertByteToHexString(sequenceNumber);
// qCDebug(dcZigbeeNetwork()) << " Source address:" << ZigbeeUtils::convertUint16ToHexString(sourceAddress);
// qCDebug(dcZigbeeNetwork()) << " End point:" << ZigbeeUtils::convertByteToHexString(endpointId);
// qCDebug(dcZigbeeNetwork()) << " Cluster:" << ZigbeeUtils::clusterIdToString(static_cast<Zigbee::ClusterId>(clusterId));
// qCDebug(dcZigbeeNetwork()) << " Attribut id:" << ZigbeeUtils::convertUint16ToHexString(attributeId);
// qCDebug(dcZigbeeNetwork()) << " Attribut status:" << static_cast<Zigbee::ZigbeeStatus>(attributeStatus);
// qCDebug(dcZigbeeNetwork()) << " Attribut data type:" << dataType;
// qCDebug(dcZigbeeNetwork()) << " Attribut size:" << dataSize;
// qCDebug(dcZigbeeNetwork()) << " Data:" << ZigbeeUtils::convertByteArrayToHexString(attributeData);
// switch (dataType) {
// case Zigbee::CharString:
// qCDebug(dcZigbeeNetwork()) << " Data(converted)" << QString::fromUtf8(attributeData);
// break;
// case Zigbee::Bool:
// qCDebug(dcZigbeeNetwork()) << " Data(converted)" << static_cast<bool>(attributeData.at(0));
// break;
// default:
// break;
// }
// ZigbeeNodeNxp *node = qobject_cast<ZigbeeNodeNxp *>(getZigbeeNode(sourceAddress));
// if (!node) {
// qCWarning(dcZigbeeNode()) << "Received an attribute report from an unknown node. Ignoring data.";
// return;
// }
ZigbeeClusterAttributeReport attributeReport;
attributeReport.sourceAddress = sourceAddress;
attributeReport.endpointId = endpointId;
attributeReport.clusterId = static_cast<Zigbee::ClusterId>(clusterId);
attributeReport.attributeId = attributeId;
attributeReport.attributeStatus = static_cast<Zigbee::ZigbeeStatus>(attributeStatus);
attributeReport.dataType = dataType;
attributeReport.data = attributeData;
return attributeReport;
}

View File

@ -29,6 +29,7 @@
#define ZIGBEEUTILS_H
#include <QColor>
#include <QPoint>
#include <QObject>
#include <QString>
#include <QPointF>
@ -39,7 +40,6 @@
#include "zigbeedatatype.h"
#include "zcl/zigbeecluster.h"
class ZigbeeUtils
{
Q_GADGET
@ -63,7 +63,7 @@ public:
// Enum prittify print methods
//static QString messageTypeToString(const Zigbee::InterfaceMessageType &type);
static QString clusterIdToString(const Zigbee::ClusterId &clusterId);
static QString clusterIdToString(const ZigbeeClusterLibrary::ClusterId &clusterId);
static QString profileIdToString(const Zigbee::ZigbeeProfile &profileId);
// Generate random data
@ -71,14 +71,13 @@ public:
// Color converter
static QPointF convertColorToXY(const QColor &color);
static QPoint convertColorToXYInt(const QColor &color);
static QColor convertXYToColor(const QPointF &xyColor);
static QColor convertXYToColor(quint16 x, quint16 y);
// Color temperature interpolation
static QColor interpolateColorFromColorTemperature(int colorTemperature, int minValue, int maxValue);
static ZigbeeClusterAttributeReport parseAttributeReport(const QByteArray &data);
};
#endif // ZIGBEEUTILS_H