Add API to interact with bindings

This commit is contained in:
Michael Zanetti 2022-09-14 23:42:40 +02:00
parent acfd73c271
commit 33d3ad10e9
10 changed files with 299 additions and 71 deletions

View File

@ -147,7 +147,7 @@ public:
// Heating, Ventilation and Air-Conditioning (HVAC)
ClusterIdPumpConfigurationControl = 0x0200,
ClusterIdThermostat = 0x0201,
ClusterIdFanControll = 0x0202,
ClusterIdFanControl = 0x0202,
ClusterIdDehumiditationControl = 0x0203,
ClusterIdThermostatUserControl = 0x0204,

View File

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

View File

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

View File

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

View File

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

View File

@ -132,7 +132,7 @@ public:
void removeZigbeeNode(const ZigbeeAddress &address);
void refreshNeighborTable();
void refreshNeighborTables();
private:
QUuid m_networkUuid;

View File

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

View File

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

View File

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

View File

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