Add API to interact with bindings
This commit is contained in:
parent
acfd73c271
commit
33d3ad10e9
@ -147,7 +147,7 @@ public:
|
||||
// Heating, Ventilation and Air-Conditioning (HVAC)
|
||||
ClusterIdPumpConfigurationControl = 0x0200,
|
||||
ClusterIdThermostat = 0x0201,
|
||||
ClusterIdFanControll = 0x0202,
|
||||
ClusterIdFanControl = 0x0202,
|
||||
ClusterIdDehumiditationControl = 0x0203,
|
||||
ClusterIdThermostatUserControl = 0x0204,
|
||||
|
||||
|
||||
@ -427,9 +427,9 @@ ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestUnbind(const ZigbeeDevicePro
|
||||
stream << bindingRecord.clusterId;
|
||||
stream << static_cast<quint8>(bindingRecord.destinationAddressMode);
|
||||
if (bindingRecord.destinationAddressMode == Zigbee::DestinationAddressModeGroup) {
|
||||
stream << bindingRecord.destinationAddressShort;
|
||||
stream << bindingRecord.destinationShortAddress;
|
||||
} else {
|
||||
stream << bindingRecord.destinationAddress.toUInt64();
|
||||
stream << bindingRecord.destinationIeeeAddress.toUInt64();
|
||||
stream << bindingRecord.destinationEndpoint;
|
||||
}
|
||||
|
||||
|
||||
@ -174,14 +174,53 @@ ZigbeeDeviceProfile::PowerDescriptor ZigbeeDeviceProfile::parsePowerDescriptor(q
|
||||
return powerDescriptor;
|
||||
}
|
||||
|
||||
ZigbeeDeviceProfile::BindingTable ZigbeeDeviceProfile::parseBindingTable(const QByteArray &payload)
|
||||
{
|
||||
ZigbeeDeviceProfile::BindingTable table;
|
||||
|
||||
QDataStream stream(payload);
|
||||
stream.setByteOrder(QDataStream::LittleEndian);
|
||||
|
||||
quint8 sqn, status, listRecordCount;
|
||||
stream >> sqn >> status >> table.tableSize >> table.startIndex >> listRecordCount;
|
||||
|
||||
table.status = static_cast<Status>(status);
|
||||
|
||||
for (int i = 0; i < listRecordCount; i++) {
|
||||
ZigbeeDeviceProfile::BindingTableListRecord record;
|
||||
|
||||
quint64 sourceAddress;
|
||||
stream >> sourceAddress;
|
||||
record.sourceAddress = ZigbeeAddress(sourceAddress);
|
||||
|
||||
stream >> record.sourceEndpoint;
|
||||
stream >> record.clusterId;
|
||||
quint8 addressMode;
|
||||
stream >> addressMode;
|
||||
record.destinationAddressMode = static_cast<Zigbee::DestinationAddressMode>(addressMode);
|
||||
|
||||
if (addressMode == Zigbee::DestinationAddressModeGroup) {
|
||||
stream >> record.destinationShortAddress;
|
||||
} else if (addressMode == Zigbee::DestinationAddressModeIeeeAddress) {
|
||||
quint64 destinationAddressIeee;
|
||||
stream >> destinationAddressIeee >> record.destinationEndpoint;
|
||||
record.destinationIeeeAddress = ZigbeeAddress(destinationAddressIeee);
|
||||
}
|
||||
table.records.append(record);
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
ZigbeeDeviceProfile::NeighborTable ZigbeeDeviceProfile::parseNeighborTable(const QByteArray &payload)
|
||||
{
|
||||
ZigbeeDeviceProfile::NeighborTable table;
|
||||
QDataStream stream(payload);
|
||||
stream.setByteOrder(QDataStream::LittleEndian);
|
||||
|
||||
quint8 messageId; quint8 listRecordCount;
|
||||
stream >> messageId >> table.status >> table.tableSize >> table.startIndex >> listRecordCount;
|
||||
quint8 messageId, status, listRecordCount;
|
||||
stream >> messageId >> status >> table.tableSize >> table.startIndex >> listRecordCount;
|
||||
table.status = static_cast<Status>(status);
|
||||
for (int i = 0; i < listRecordCount; i++) {
|
||||
ZigbeeDeviceProfile::NeighborTableListRecord record;
|
||||
stream >> record.extendedPanId;
|
||||
@ -213,8 +252,9 @@ ZigbeeDeviceProfile::RoutingTable ZigbeeDeviceProfile::parseRoutingTable(const Q
|
||||
QDataStream stream(payload);
|
||||
stream.setByteOrder(QDataStream::LittleEndian);
|
||||
|
||||
quint8 messageId; quint8 listRecordCount;
|
||||
stream >> messageId >> table.status >> table.tableSize >> table.startIndex >> listRecordCount;
|
||||
quint8 messageId, status, listRecordCount;
|
||||
stream >> messageId >> status >> table.tableSize >> table.startIndex >> listRecordCount;
|
||||
table.status = static_cast<Status>(status);
|
||||
for (int i = 0; i < listRecordCount; i++) {
|
||||
ZigbeeDeviceProfile::RoutingTableListRecord record;
|
||||
stream >> record.destinationAddress;
|
||||
@ -323,10 +363,10 @@ QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::BindingTableListRecor
|
||||
debug.nospace() << "cluster: " << static_cast<ZigbeeClusterLibrary::ClusterId>(bindingTableListRecord.clusterId) << " --> ";
|
||||
switch (bindingTableListRecord.destinationAddressMode) {
|
||||
case Zigbee::DestinationAddressModeGroup:
|
||||
debug.nospace() << "destination address (group): " << ZigbeeUtils::convertUint16ToHexString(bindingTableListRecord.destinationAddressShort) << ") ";
|
||||
debug.nospace() << "destination address (group): " << ZigbeeUtils::convertUint16ToHexString(bindingTableListRecord.destinationShortAddress) << ") ";
|
||||
break;
|
||||
case Zigbee::DestinationAddressModeIeeeAddress:
|
||||
debug.nospace() << "destination address (unicast): " << bindingTableListRecord.destinationAddress.toString() << ", ";
|
||||
debug.nospace() << "destination address (unicast): " << bindingTableListRecord.destinationIeeeAddress.toString() << ", ";
|
||||
debug.nospace() << "destination endpoint: " << bindingTableListRecord.destinationEndpoint << ") ";
|
||||
break;
|
||||
default:
|
||||
|
||||
@ -292,12 +292,19 @@ public:
|
||||
ZigbeeAddress sourceAddress;
|
||||
quint8 sourceEndpoint;
|
||||
quint16 clusterId;
|
||||
Zigbee::DestinationAddressMode destinationAddressMode; // Note: group or unicast
|
||||
quint16 destinationAddressShort; // Only for destination address 0x01
|
||||
ZigbeeAddress destinationAddress; // Only for destination address 0x03
|
||||
quint8 destinationEndpoint; // Only for destination address 0x03
|
||||
Zigbee::DestinationAddressMode destinationAddressMode;
|
||||
quint16 destinationShortAddress;
|
||||
ZigbeeAddress destinationIeeeAddress;
|
||||
quint8 destinationEndpoint;
|
||||
} BindingTableListRecord;
|
||||
|
||||
typedef struct BindingTable {
|
||||
Status status;
|
||||
quint8 tableSize;
|
||||
quint8 startIndex;
|
||||
QList<BindingTableListRecord> records;
|
||||
} BindingTable;
|
||||
|
||||
typedef struct NeighborTableListRecord {
|
||||
quint64 extendedPanId;
|
||||
ZigbeeAddress ieeeAddress;
|
||||
@ -310,8 +317,8 @@ public:
|
||||
quint8 lqi;
|
||||
} NeighborTableListRecord;
|
||||
|
||||
typedef struct NeighborTable{
|
||||
quint8 status;
|
||||
typedef struct NeighborTable {
|
||||
Status status;
|
||||
quint8 tableSize;
|
||||
quint8 startIndex;
|
||||
QList<NeighborTableListRecord> records;
|
||||
@ -327,7 +334,7 @@ public:
|
||||
} RoutingTableListRecord;
|
||||
|
||||
typedef struct RoutingTable{
|
||||
quint8 status;
|
||||
Status status;
|
||||
quint8 tableSize;
|
||||
quint8 startIndex;
|
||||
QList<RoutingTableListRecord> records;
|
||||
@ -338,6 +345,7 @@ public:
|
||||
static ServerMask parseServerMask(quint16 serverMaskFlag);
|
||||
static DescriptorCapabilities parseDescriptorCapabilities(quint8 descriptorCapabilitiesFlag);
|
||||
static PowerDescriptor parsePowerDescriptor(quint16 powerDescriptorFlag);
|
||||
static BindingTable parseBindingTable(const QByteArray &payload);
|
||||
static NeighborTable parseNeighborTable(const QByteArray &payload);
|
||||
static RoutingTable parseRoutingTable(const QByteArray &payload);
|
||||
};
|
||||
|
||||
@ -330,7 +330,7 @@ void ZigbeeNetwork::removeZigbeeNode(const ZigbeeAddress &address)
|
||||
});
|
||||
}
|
||||
|
||||
void ZigbeeNetwork::refreshNeighborTable()
|
||||
void ZigbeeNetwork::refreshNeighborTables()
|
||||
{
|
||||
foreach (ZigbeeNode *node, m_nodes) {
|
||||
if (node->macCapabilities().receiverOnWhenIdle) {
|
||||
@ -415,6 +415,10 @@ void ZigbeeNetwork::addNodeInternally(ZigbeeNode *node)
|
||||
}
|
||||
});
|
||||
|
||||
connect(node, &ZigbeeNode::bindingTableRecordsChanged, this, [this, node](){
|
||||
m_database->updateNodeBindingTable(node);
|
||||
});
|
||||
|
||||
// Note: if a cluster shows up after initialization (out of spec devices), save the cluster and it's attributes
|
||||
foreach (ZigbeeNodeEndpoint *endpoint, node->endpoints()) {
|
||||
connect(endpoint, &ZigbeeNodeEndpoint::clusterAttributeChanged, this, &ZigbeeNetwork::onNodeClusterAttributeChanged);
|
||||
|
||||
@ -132,7 +132,7 @@ public:
|
||||
|
||||
void removeZigbeeNode(const ZigbeeAddress &address);
|
||||
|
||||
void refreshNeighborTable();
|
||||
void refreshNeighborTables();
|
||||
|
||||
private:
|
||||
QUuid m_networkUuid;
|
||||
|
||||
@ -180,6 +180,23 @@ QList<ZigbeeNode *> ZigbeeNetworkDatabase::loadNodes()
|
||||
node->m_endpoints.append(endpoint);
|
||||
node->setupEndpointInternal(endpoint);
|
||||
}
|
||||
|
||||
QSqlQuery bindingsQuery(m_db);
|
||||
bindingsQuery.prepare("SELECT * FROM bindings WHERE sourceAddress = ?;");
|
||||
bindingsQuery.addBindValue(node->extendedAddress().toString());
|
||||
bindingsQuery.exec();
|
||||
while (bindingsQuery.next()) {
|
||||
ZigbeeDeviceProfile::BindingTableListRecord record;
|
||||
record.sourceAddress = ZigbeeAddress(bindingsQuery.value("sourceAddress").toString());
|
||||
record.sourceEndpoint = bindingsQuery.value("sourceEndpointId").toUInt();
|
||||
record.clusterId = bindingsQuery.value("clusterId").toUInt();
|
||||
record.destinationAddressMode = static_cast<Zigbee::DestinationAddressMode>(bindingsQuery.value("destinationAddressMode").toUInt());
|
||||
record.destinationShortAddress = bindingsQuery.value("destinationShortAddress").toUInt();
|
||||
record.destinationIeeeAddress = ZigbeeAddress(bindingsQuery.value("destinationIeeeAddress").toString());
|
||||
record.destinationEndpoint = bindingsQuery.value("destinationEndpointId").toUInt();
|
||||
node->m_bindingTableRecords.append(record);
|
||||
}
|
||||
|
||||
nodes.append(node);
|
||||
}
|
||||
|
||||
@ -287,6 +304,18 @@ bool ZigbeeNetworkDatabase::initDatabase()
|
||||
createIndices("attributesIndex", "attributes", "clusterId, attributeId");
|
||||
}
|
||||
|
||||
if (!m_db.tables().contains("bindings")) {
|
||||
createTable("bindings", "(sourceAddress TEXT NOT NULL, "
|
||||
"sourceEndpointId INTEGER NOT NULL, "
|
||||
"clusterId INTEGER NOT NULL, "
|
||||
"destinationAddressMode INTEGER NOT NULL, "
|
||||
"destinationShortAddress INTEGER, "
|
||||
"destinationIeeeAddress TEXT, "
|
||||
"destinationEndpointId INTEGER, "
|
||||
"CONSTRAINT fk FOREIGN KEY(sourceAddress) REFERENCES nodes(ieeeAddress) ON DELETE CASCADE"
|
||||
")");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -436,6 +465,8 @@ bool ZigbeeNetworkDatabase::saveNode(ZigbeeNode *node)
|
||||
}
|
||||
}
|
||||
|
||||
updateNodeBindingTable(node);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -479,6 +510,37 @@ bool ZigbeeNetworkDatabase::updateNodeLastSeen(ZigbeeNode *node, const QDateTime
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ZigbeeNetworkDatabase::updateNodeBindingTable(ZigbeeNode *node)
|
||||
{
|
||||
bool error = false;
|
||||
QSqlQuery query(m_db);
|
||||
query.prepare("DELETE FROM bindings WHERE sourceAddress = ?");
|
||||
query.addBindValue(node->extendedAddress().toString());
|
||||
query.exec();
|
||||
if (query.lastError().type() != QSqlError::NoError) {
|
||||
qCWarning(dcZigbeeNetworkDatabase()) << "Error clearing old binding table:" << query.executedQuery() << query.lastError().databaseText() << query.lastError().driverText();
|
||||
error = true;
|
||||
}
|
||||
|
||||
foreach (const ZigbeeDeviceProfile::BindingTableListRecord &record, node->bindingTableRecords()) {
|
||||
QSqlQuery insertQuery(m_db);
|
||||
insertQuery.prepare("INSERT INTO bindings (sourceAddress, sourceEndpointId, clusterId, destinationAddressMode, destinationShortAddress, destinationIeeeAddress, destinationEndpointId) VALUES(?, ?, ?, ?, ?, ?, ?)");
|
||||
insertQuery.addBindValue(record.sourceAddress.toString());
|
||||
insertQuery.addBindValue(record.sourceEndpoint);
|
||||
insertQuery.addBindValue(record.clusterId);
|
||||
insertQuery.addBindValue(record.destinationAddressMode);
|
||||
insertQuery.addBindValue(record.destinationShortAddress);
|
||||
insertQuery.addBindValue(record.destinationIeeeAddress.toString());
|
||||
insertQuery.addBindValue(record.destinationEndpoint);
|
||||
insertQuery.exec();
|
||||
if (insertQuery.lastError().type() != QSqlError::NoError) {
|
||||
qCWarning(dcZigbeeNetworkDatabase()) << "Error inserting into binding table:" << query.executedQuery() << query.lastError().databaseText() << query.lastError().driverText();
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
bool ZigbeeNetworkDatabase::removeNode(ZigbeeNode *node)
|
||||
{
|
||||
qCDebug(dcZigbeeNetworkDatabase()) << "Remove" << node;
|
||||
|
||||
@ -73,6 +73,7 @@ public slots:
|
||||
bool updateNodeLqi(ZigbeeNode *node, quint8 lqi);
|
||||
bool updateNodeNetworkAddress(ZigbeeNode *node, quint16 networkAddress);
|
||||
bool updateNodeLastSeen(ZigbeeNode *node, const QDateTime &lastSeen);
|
||||
bool updateNodeBindingTable(ZigbeeNode *node);
|
||||
bool removeNode(ZigbeeNode *node);
|
||||
|
||||
};
|
||||
|
||||
@ -194,6 +194,7 @@ void ZigbeeNode::startInitialization()
|
||||
* - Simple descriptor request
|
||||
* - for each endpoint
|
||||
* - read basic cluster
|
||||
* - Read binding table
|
||||
*/
|
||||
|
||||
initNodeDescriptor();
|
||||
@ -225,52 +226,7 @@ ZigbeeReply *ZigbeeNode::removeAllBindings()
|
||||
ZigbeeReply *ZigbeeNode::readBindingTableEntries()
|
||||
{
|
||||
ZigbeeReply *reply = new ZigbeeReply(this);
|
||||
ZigbeeDeviceObjectReply *zdoReply = deviceObject()->requestMgmtBind();
|
||||
connect(zdoReply, &ZigbeeDeviceObjectReply::finished, this, [=](){
|
||||
if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) {
|
||||
qCWarning(dcZigbeeNode()) << "Failed to read binding table" << zdoReply->error();
|
||||
reply->finishReply(ZigbeeReply::ErrorZigbeeError);
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcZigbeeDeviceObject()) << "Bind table payload" << ZigbeeUtils::convertByteArrayToHexString(zdoReply->responseData());
|
||||
QByteArray response = zdoReply->responseData();
|
||||
QDataStream stream(&response, QIODevice::ReadOnly);
|
||||
stream.setByteOrder(QDataStream::LittleEndian);
|
||||
quint8 sqn; quint8 statusInt; quint8 entriesCount; quint8 startIndex; quint8 bindingTableListCount;
|
||||
stream >> sqn >> statusInt >> entriesCount >> startIndex >> bindingTableListCount;
|
||||
ZigbeeDeviceProfile::Status status = static_cast<ZigbeeDeviceProfile::Status>(statusInt);
|
||||
qCDebug(dcZigbeeDeviceObject()) << "SQN:" << sqn << status << "entries:" << entriesCount << "index:" << startIndex << "list count:" << bindingTableListCount;
|
||||
|
||||
m_bindingTableRecords.clear();
|
||||
for (int i = 0; i < bindingTableListCount; i++) {
|
||||
quint64 sourceAddress; quint8 addressMode;
|
||||
ZigbeeDeviceProfile::BindingTableListRecord record;
|
||||
stream >> sourceAddress;
|
||||
record.sourceAddress = ZigbeeAddress(sourceAddress);
|
||||
|
||||
stream >> record.sourceEndpoint >> record.clusterId >> addressMode;
|
||||
record.destinationAddressMode = static_cast<Zigbee::DestinationAddressMode>(addressMode);
|
||||
|
||||
if (addressMode == Zigbee::DestinationAddressModeGroup) {
|
||||
stream >> record.destinationAddressShort;
|
||||
} else if (addressMode == Zigbee::DestinationAddressModeIeeeAddress) {
|
||||
quint64 destinationAddressIeee;
|
||||
stream >> destinationAddressIeee >> record.destinationEndpoint;
|
||||
record.destinationAddress = ZigbeeAddress(destinationAddressIeee);
|
||||
} else {
|
||||
qCWarning(dcZigbeeDeviceObject()) << "Invalid destination address mode in binding table record.";
|
||||
break;
|
||||
}
|
||||
qCDebug(dcZigbeeDeviceObject()) << record;
|
||||
m_bindingTableRecords << record;
|
||||
}
|
||||
|
||||
// TODO: continue reading if there are more entries
|
||||
|
||||
emit bindingTableRecordsChanged();
|
||||
reply->finishReply();
|
||||
});
|
||||
readBindingTableChunk(reply, 0);
|
||||
return reply;
|
||||
}
|
||||
|
||||
@ -288,6 +244,63 @@ ZigbeeReply *ZigbeeNode::readRoutingTableEntries()
|
||||
return reply;
|
||||
}
|
||||
|
||||
ZigbeeReply *ZigbeeNode::addBinding(quint8 sourceEndpointId, quint16 clusterId, const ZigbeeAddress &destinationAddress, quint8 destinationEndpoint)
|
||||
{
|
||||
ZigbeeReply *reply = new ZigbeeReply(this);
|
||||
ZigbeeDeviceObjectReply *zdoReply = deviceObject()->requestBindIeeeAddress(sourceEndpointId, clusterId, destinationAddress, destinationEndpoint);
|
||||
connect(zdoReply, &ZigbeeDeviceObjectReply::finished, reply, [this, zdoReply, reply](){
|
||||
if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) {
|
||||
qCWarning(dcZigbeeNode()) << "Failed to configure binding on node" << this;
|
||||
reply->finishReply(ZigbeeReply::ErrorZigbeeError);
|
||||
return;
|
||||
}
|
||||
qCDebug(dcZigbeeNode) << "Binding added";
|
||||
|
||||
reply->finishReply();
|
||||
|
||||
readBindingTableEntries();
|
||||
});
|
||||
return reply;
|
||||
}
|
||||
|
||||
ZigbeeReply *ZigbeeNode::addBinding(quint8 sourceEndpointId, quint16 clusterId, quint16 destinationGroupAddress)
|
||||
{
|
||||
ZigbeeReply *reply = new ZigbeeReply(this);
|
||||
ZigbeeDeviceObjectReply *zdoReply = deviceObject()->requestBindGroupAddress(sourceEndpointId, clusterId, destinationGroupAddress);
|
||||
connect(zdoReply, &ZigbeeDeviceObjectReply::finished, reply, [this, zdoReply, reply](){
|
||||
if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) {
|
||||
qCWarning(dcZigbeeNode()) << "Failed to configure binding on node" << this;
|
||||
reply->finishReply(ZigbeeReply::ErrorZigbeeError);
|
||||
return;
|
||||
}
|
||||
qCDebug(dcZigbeeNode) << "Binding added";
|
||||
|
||||
reply->finishReply();
|
||||
|
||||
readBindingTableEntries();
|
||||
});
|
||||
return reply;
|
||||
}
|
||||
|
||||
ZigbeeReply *ZigbeeNode::removeBinding(const ZigbeeDeviceProfile::BindingTableListRecord &binding)
|
||||
{
|
||||
ZigbeeReply *reply = new ZigbeeReply(this);
|
||||
ZigbeeDeviceObjectReply *zdoReply = deviceObject()->requestUnbind(binding);
|
||||
connect(zdoReply, &ZigbeeDeviceObjectReply::finished, reply, [this, zdoReply, reply](){
|
||||
if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) {
|
||||
qCWarning(dcZigbeeNode()) << "Failed to rebinding binding on node" << this;
|
||||
reply->finishReply(ZigbeeReply::ErrorZigbeeError);
|
||||
return;
|
||||
}
|
||||
qCDebug(dcZigbeeNode) << "Binding removed";
|
||||
|
||||
reply->finishReply();
|
||||
|
||||
readBindingTableEntries();
|
||||
});
|
||||
return reply;
|
||||
}
|
||||
|
||||
void ZigbeeNode::initNodeDescriptor()
|
||||
{
|
||||
qCDebug(dcZigbeeNode()) << "Requesting node descriptor from" << this;
|
||||
@ -534,6 +547,90 @@ void ZigbeeNode::removeNextBinding(ZigbeeReply *reply)
|
||||
});
|
||||
}
|
||||
|
||||
void ZigbeeNode::readBindingTableChunk(ZigbeeReply *reply, quint8 startIndex)
|
||||
{
|
||||
ZigbeeDeviceObjectReply *zdoReply = deviceObject()->requestMgmtBind(startIndex);
|
||||
connect(zdoReply, &ZigbeeDeviceObjectReply::finished, reply, [=](){
|
||||
if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) {
|
||||
qCWarning(dcZigbeeNode()) << "Failed to read binding table" << zdoReply->error();
|
||||
if (zdoReply->zigbeeDeviceObjectStatus() == ZigbeeDeviceProfile::StatusNotSupported) {
|
||||
// Not an error, merely a valid response code that this optional request is not supported.
|
||||
reply->finishReply(ZigbeeReply::ErrorNoError);
|
||||
} else {
|
||||
reply->finishReply(ZigbeeReply::ErrorZigbeeError);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcZigbeeNetwork()) << "Binding table reply from" << ZigbeeUtils::convertUint16ToHexString(shortAddress()) << zdoReply->responseData().toHex();
|
||||
|
||||
ZigbeeDeviceProfile::BindingTable bindingTable = ZigbeeDeviceProfile::parseBindingTable(zdoReply->responseData());
|
||||
if (bindingTable.startIndex == 0) {
|
||||
m_bindingTable = bindingTable;
|
||||
} else if (m_bindingTable.records.count() == bindingTable.startIndex) {
|
||||
m_bindingTable.records.append(bindingTable.records);
|
||||
} else {
|
||||
qCWarning(dcZigbeeNode()) << "Error reading binding table. Indices not matching. Starting from scratch.";
|
||||
readBindingTableChunk(reply, 0);
|
||||
return;
|
||||
}
|
||||
if (m_bindingTable.records.count() < m_bindingTable.tableSize) {
|
||||
qCDebug(dcZigbeeNode) << "Binding table not complete yet (" << m_bindingTable.records.count() << "/" << m_bindingTable.tableSize << "). Fetching next chunk...";
|
||||
readBindingTableChunk(reply, m_bindingTable.records.count());
|
||||
return;
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
QMutableListIterator<ZigbeeDeviceProfile::BindingTableListRecord> it(m_bindingTableRecords);
|
||||
while (it.hasNext()) {
|
||||
ZigbeeDeviceProfile::BindingTableListRecord oldRecord = it.next();
|
||||
bool found = false;
|
||||
foreach (const ZigbeeDeviceProfile::BindingTableListRecord &record, m_bindingTable.records) {
|
||||
if (record.sourceAddress == oldRecord.sourceAddress
|
||||
&& record.sourceEndpoint == oldRecord.sourceEndpoint
|
||||
&& record.clusterId == oldRecord.clusterId
|
||||
&& record.destinationAddressMode == oldRecord.destinationAddressMode
|
||||
&& record.destinationShortAddress == oldRecord.destinationShortAddress
|
||||
&& record.destinationIeeeAddress == oldRecord.destinationIeeeAddress
|
||||
&& record.destinationEndpoint == oldRecord.destinationEndpoint) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
it.remove();
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (const ZigbeeDeviceProfile::BindingTableListRecord &record, m_bindingTable.records) {
|
||||
qCDebug(dcZigbeeNode()) << "Binding table record fetched" << record;
|
||||
bool found = false;
|
||||
foreach (const ZigbeeDeviceProfile::BindingTableListRecord &oldRecord, m_bindingTableRecords) {
|
||||
if (record.sourceAddress == oldRecord.sourceAddress
|
||||
&& record.sourceEndpoint == oldRecord.sourceEndpoint
|
||||
&& record.clusterId == oldRecord.clusterId
|
||||
&& record.destinationAddressMode == oldRecord.destinationAddressMode
|
||||
&& record.destinationShortAddress == oldRecord.destinationShortAddress
|
||||
&& record.destinationIeeeAddress == oldRecord.destinationIeeeAddress
|
||||
&& record.destinationEndpoint == oldRecord.destinationEndpoint) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
qCDebug(dcZigbeeNode()) << "New binding table record:" << record;
|
||||
m_bindingTableRecords.append(record);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
emit bindingTableRecordsChanged();
|
||||
}
|
||||
reply->finishReply(ZigbeeReply::ErrorNoError);
|
||||
});
|
||||
}
|
||||
|
||||
void ZigbeeNode::readNeighborTableChunk(ZigbeeReply *reply, quint8 startIndex)
|
||||
{
|
||||
ZigbeeDeviceObjectReply *zdoReply = deviceObject()->requestMgmtLqi(startIndex);
|
||||
@ -550,7 +647,7 @@ void ZigbeeNode::readNeighborTableChunk(ZigbeeReply *reply, quint8 startIndex)
|
||||
}
|
||||
|
||||
|
||||
// qCDebug(dcZigbeeNode()) << "LQI response:" << zdoReply->responseData().toHex();
|
||||
// qCDebug(dcZigbeeNode()) << "LQI neighbor table reply:" << zdoReply->responseData().toHex();
|
||||
ZigbeeDeviceProfile::NeighborTable neighborTable = ZigbeeDeviceProfile::parseNeighborTable(zdoReply->responseData());
|
||||
if (neighborTable.startIndex == 0) {
|
||||
m_neighborTable = neighborTable;
|
||||
@ -567,6 +664,7 @@ void ZigbeeNode::readNeighborTableChunk(ZigbeeReply *reply, quint8 startIndex)
|
||||
return;
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
QList<quint16> removedRecords = m_neighborTableRecords.keys();
|
||||
foreach(const ZigbeeDeviceProfile::NeighborTableListRecord &record, m_neighborTable.records) {
|
||||
qCDebug(dcZigbeeNode()).nospace() << "LQI neighbor for node: " << ZigbeeUtils::convertUint16ToHexString(m_shortAddress) << ": " << record;
|
||||
@ -578,15 +676,18 @@ void ZigbeeNode::readNeighborTableChunk(ZigbeeReply *reply, quint8 startIndex)
|
||||
|| existingRecord.depth != record.depth
|
||||
|| existingRecord.lqi != record.lqi) {
|
||||
m_neighborTableRecords[record.shortAddress] = record;
|
||||
emit neighborTableRecordsChanged();
|
||||
changed = true;
|
||||
}
|
||||
} else {
|
||||
m_neighborTableRecords.insert(record.shortAddress, record);
|
||||
emit neighborTableRecordsChanged();
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
foreach (quint16 removedRecord, removedRecords) {
|
||||
m_neighborTableRecords.remove(removedRecord);
|
||||
changed = true;
|
||||
}
|
||||
if (changed) {
|
||||
emit neighborTableRecordsChanged();
|
||||
}
|
||||
reply->finishReply(ZigbeeReply::ErrorNoError);
|
||||
@ -626,6 +727,7 @@ void ZigbeeNode::readRoutingTableChunk(ZigbeeReply *reply, quint8 startIndex)
|
||||
return;
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
QList<quint16> removedRecords = m_routingTableRecords.keys();
|
||||
foreach(const ZigbeeDeviceProfile::RoutingTableListRecord &record, m_routingTable.records) {
|
||||
qCDebug(dcZigbeeNode()).nospace() << "Route entry for node: " << ZigbeeUtils::convertUint16ToHexString(m_shortAddress) << ": " << record;
|
||||
@ -638,19 +740,21 @@ void ZigbeeNode::readRoutingTableChunk(ZigbeeReply *reply, quint8 startIndex)
|
||||
|| existingRecord.routeRecordRequired != record.routeRecordRequired
|
||||
|| existingRecord.nextHopAddress != record.nextHopAddress) {
|
||||
m_routingTableRecords[record.destinationAddress] = record;
|
||||
emit routingTableRecordsChanged();
|
||||
changed = true;
|
||||
}
|
||||
} else {
|
||||
m_routingTableRecords.insert(record.destinationAddress, record);
|
||||
emit routingTableRecordsChanged();
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
foreach (quint16 removedRecord, removedRecords) {
|
||||
m_routingTableRecords.remove(removedRecord);
|
||||
changed = true;
|
||||
}
|
||||
if (changed) {
|
||||
emit routingTableRecordsChanged();
|
||||
}
|
||||
reply->finishReply(ZigbeeReply::ErrorNoError);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@ -903,6 +1007,9 @@ void ZigbeeNode::readSoftwareBuildId(ZigbeeClusterBasic *basicCluster)
|
||||
|
||||
// Finished with reading basic cluster, the node is initialized.
|
||||
setState(StateInitialized);
|
||||
|
||||
// Reading binding table. If this fails, it's not critical, so setting the node to initialized before this is fine.
|
||||
readBindingTableEntries();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -98,11 +98,15 @@ public:
|
||||
// This method starts the node initialization phase (read descriptors and endpoints)
|
||||
void startInitialization();
|
||||
|
||||
ZigbeeReply *removeAllBindings();
|
||||
ZigbeeReply *readBindingTableEntries();
|
||||
ZigbeeReply *readLqiTableEntries();
|
||||
ZigbeeReply *readRoutingTableEntries();
|
||||
|
||||
ZigbeeReply *addBinding(quint8 sourceEndpointId, quint16 clusterId, const ZigbeeAddress &destinationAddress, quint8 destinationEndpoint);
|
||||
ZigbeeReply *addBinding(quint8 sourceEndpointId, quint16 clusterId, quint16 destinationGroupAddress);
|
||||
ZigbeeReply *removeBinding(const ZigbeeDeviceProfile::BindingTableListRecord &binding);
|
||||
ZigbeeReply *removeAllBindings();
|
||||
|
||||
private:
|
||||
ZigbeeNode(ZigbeeNetwork *network, quint16 shortAddress, const ZigbeeAddress &extendedAddress, QObject *parent = nullptr);
|
||||
|
||||
@ -132,6 +136,7 @@ private:
|
||||
QList<ZigbeeDeviceProfile::BindingTableListRecord> m_bindingTableRecords;
|
||||
QHash<quint16, ZigbeeDeviceProfile::NeighborTableListRecord> m_neighborTableRecords;
|
||||
QHash<quint16, ZigbeeDeviceProfile::RoutingTableListRecord> m_routingTableRecords;
|
||||
ZigbeeDeviceProfile::BindingTable m_bindingTable;
|
||||
ZigbeeDeviceProfile::NeighborTable m_neighborTable; // Used internally to sync the table from the device in chunks
|
||||
ZigbeeDeviceProfile::RoutingTable m_routingTable; // Used internally to sync the table from the device in chunks
|
||||
|
||||
@ -148,6 +153,7 @@ private:
|
||||
void initEndpoint(quint8 endpointId);
|
||||
|
||||
void removeNextBinding(ZigbeeReply *reply);
|
||||
void readBindingTableChunk(ZigbeeReply *reply, quint8 startIndex);
|
||||
void readNeighborTableChunk(ZigbeeReply *reply, quint8 startIndex);
|
||||
void readRoutingTableChunk(ZigbeeReply *reply, quint8 startIndex);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user