nymea-zigbee/libnymea-zigbee/zigbeenetwork.cpp

489 lines
16 KiB
C++

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* 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 "zigbeeutils.h"
#include "zigbeenetwork.h"
#include "loggingcategory.h"
ZigbeeNetwork::ZigbeeNetwork(QObject *parent) :
QObject(parent)
{
}
ZigbeeNetwork::State ZigbeeNetwork::state() const
{
return m_state;
}
ZigbeeNetwork::Error ZigbeeNetwork::error() const
{
return m_error;
}
QString ZigbeeNetwork::settingsFilenName() const
{
return m_settingsFileName;
}
void ZigbeeNetwork::setSettingsFileName(const QString &settingsFileName)
{
if (m_settingsFileName == settingsFileName)
return;
m_settingsFileName = settingsFileName;
emit settingsFileNameChanged(m_settingsFileName);
}
QString ZigbeeNetwork::serialPortName() const
{
return m_serialPortName;
}
void ZigbeeNetwork::setSerialPortName(const QString &serialPortName)
{
if (m_serialPortName == serialPortName)
return;
m_serialPortName = serialPortName;
emit serialPortNameChanged(m_serialPortName);
}
qint32 ZigbeeNetwork::serialBaudrate() const
{
return m_serialBaudrate;
}
void ZigbeeNetwork::setSerialBaudrate(qint32 baudrate)
{
if (m_serialBaudrate == baudrate)
return;
m_serialBaudrate = baudrate;
emit serialBaudrateChanged(m_serialBaudrate);
}
quint64 ZigbeeNetwork::extendedPanId() const
{
return m_extendedPanId;
}
void ZigbeeNetwork::setExtendedPanId(quint64 extendedPanId)
{
if (m_extendedPanId == extendedPanId)
return;
m_extendedPanId = extendedPanId;
emit extendedPanIdChanged(m_extendedPanId);
}
quint32 ZigbeeNetwork::channel() const
{
return m_channel;
}
void ZigbeeNetwork::setChannel(quint32 channel)
{
if (m_channel == channel)
return;
m_channel = channel;
emit channelChanged(m_channel);
}
ZigbeeSecurityConfiguration ZigbeeNetwork::securityConfiguration() const
{
return m_securityConfiguration;
}
void ZigbeeNetwork::setSecurityConfiguration(const ZigbeeSecurityConfiguration &securityConfiguration)
{
if (m_securityConfiguration == securityConfiguration)
return;
m_securityConfiguration = securityConfiguration;
emit securityConfigurationChanged(m_securityConfiguration);
}
bool ZigbeeNetwork::permitJoining() const
{
return m_permitJoining;
}
void ZigbeeNetwork::setPermitJoining(bool permitJoining)
{
if (m_permitJoining == permitJoining)
return;
qCDebug(dcZigbeeNetwork()) << "Permit joining changed to" << permitJoining;
m_permitJoining = permitJoining;
emit permitJoiningChanged(m_permitJoining);
}
QList<ZigbeeNode *> ZigbeeNetwork::nodes() const
{
return m_nodes;
}
ZigbeeNode *ZigbeeNetwork::coordinatorNode() const
{
return getZigbeeNode(0);
}
ZigbeeNode *ZigbeeNetwork::getZigbeeNode(quint16 shortAddress) const
{
foreach (ZigbeeNode *node, m_uninitializedNodes) {
if (node->shortAddress() == shortAddress) {
return node;
}
}
foreach (ZigbeeNode *node, m_nodes) {
if (node->shortAddress() == shortAddress) {
return node;
}
}
return nullptr;
}
ZigbeeNode *ZigbeeNetwork::getZigbeeNode(const ZigbeeAddress &address) const
{
foreach (ZigbeeNode *node, m_uninitializedNodes) {
if (node->extendedAddress() == address) {
return node;
}
}
foreach (ZigbeeNode *node, m_nodes) {
if (node->extendedAddress() == address) {
return node;
}
}
return nullptr;
}
bool ZigbeeNetwork::hasNode(quint16 shortAddress) const
{
return getZigbeeNode(shortAddress) != nullptr;
}
bool ZigbeeNetwork::hasNode(const ZigbeeAddress &address) const
{
return getZigbeeNode(address) != nullptr;
}
void ZigbeeNetwork::addNodeInternally(ZigbeeNode *node)
{
if (m_nodes.contains(node)) {
qCWarning(dcZigbeeNetwork()) << "The node" << node << "has already been added.";
return;
}
node->setConnected(state() == StateRunning);
m_nodes.append(node);
emit nodeAdded(node);
}
void ZigbeeNetwork::removeNodeInternally(ZigbeeNode *node)
{
if (!m_nodes.contains(node)) {
qCWarning(dcZigbeeNetwork()) << "Try to remove node" << node << "but not in the node list.";
return;
}
m_nodes.removeAll(node);
emit nodeRemoved(node);
node->deleteLater();
}
void ZigbeeNetwork::saveNetwork()
{
qCDebug(dcZigbeeNetwork()) << "Save current network configuration to" << m_settingsFileName;
QSettings settings(m_settingsFileName, QSettings::IniFormat, this);
settings.beginGroup("Network");
settings.setValue("panId", extendedPanId());
settings.setValue("channel", channel());
settings.endGroup();
foreach (ZigbeeNode *node, nodes()) {
saveNode(node);
}
}
void ZigbeeNetwork::loadNetwork()
{
qCDebug(dcZigbeeNetwork()) << "Load current network configuration from" << m_settingsFileName;
QSettings settings(m_settingsFileName, QSettings::IniFormat, this);
settings.beginGroup("Network");
quint64 extendedPanId = static_cast<quint64>(settings.value("panId", 0).toULongLong());
setExtendedPanId(extendedPanId);
setChannel(settings.value("channel", 0).toUInt());
settings.endGroup(); // Network
// Load nodes
settings.beginGroup("Nodes");
foreach (const QString ieeeAddressString, settings.childGroups()) {
settings.beginGroup(ieeeAddressString);
ZigbeeNode *node = new ZigbeeNode(this);
node->setExtendedAddress(ZigbeeAddress(ieeeAddressString));
node->setShortAddress(static_cast<quint16>(settings.value("nwkAddress", 0).toUInt()));
node->setMacCapabilitiesFlag(static_cast<quint8>(settings.value("macCapabilitiesFlag", 0).toUInt()));
node->setNodeDescriptorRawData(settings.value("nodeDescriptorRawData", QByteArray()).toByteArray());
node->setPowerDescriptorFlag(static_cast<quint16>(settings.value("powerDescriptorFlag", 0).toUInt()));
// TODO: load endpoints
// settings.beginGroup("inputCluster");
// foreach (const QString &clusterIdString, settings.childGroups()) {
// settings.beginGroup(clusterIdString);
// Zigbee::ClusterId clusterId = static_cast<Zigbee::ClusterId>(clusterIdString.toInt());
// foreach (const QString &attributeIdString, settings.childGroups()) {
// settings.beginGroup(attributeIdString);
// quint16 id = static_cast<quint16>(attributeIdString.toInt());
// Zigbee::DataType dataType = static_cast<Zigbee::DataType>(settings.value("dataType", 0).toInt());
// QByteArray data = settings.value("data").toByteArray();
// node->setClusterAttribute(clusterId, ZigbeeClusterAttribute(id, dataType, data));
// settings.endGroup(); // attributeId
// }
// settings.endGroup(); // clusterId
// }
// settings.endGroup(); // inputCluster
// // Output cluster
// settings.beginGroup("outputCluster");
// foreach (const QString &clusterIdString, settings.childGroups()) {
// settings.beginGroup(clusterIdString);
// Zigbee::ClusterId clusterId = static_cast<Zigbee::ClusterId>(clusterIdString.toInt());
// foreach (const QString &attributeIdString, settings.childGroups()) {
// settings.beginGroup(attributeIdString);
// quint16 id = static_cast<quint16>(attributeIdString.toInt());
// Zigbee::DataType dataType = static_cast<Zigbee::DataType>(settings.value("dataType", 0).toInt());
// QByteArray data = settings.value("data").toByteArray();
// node->setClusterAttribute(clusterId, ZigbeeClusterAttribute(id, dataType, data));
// settings.endGroup(); // attributeId
// }
// settings.endGroup(); // clusterId
// }
// settings.endGroup(); // outputCluster
//FIXME
//node->setState(StateInitialized);
addNodeInternally(node);
settings.endGroup(); // ieeeAddress
}
settings.endGroup(); // Nodes
qCDebug(dcZigbeeNetwork()) << "Extended PAN ID:" << m_extendedPanId << ZigbeeUtils::convertUint64ToHexString(m_extendedPanId);
qCDebug(dcZigbeeNetwork()) << "Channel" << m_channel;
qCDebug(dcZigbeeNetwork()) << QStringLiteral("Nodes: (%1)").arg(m_nodes.count());
foreach (ZigbeeNode *node, nodes()) {
qCDebug(dcZigbeeNetwork()) << " - " << node;
// qCDebug(dcZigbeeNetwork()) << "Output cluster:";
// foreach (ZigbeeCluster *cluster, node->outputClusters()) {
// qCDebug(dcZigbeeNetwork()) << " " << cluster;
// foreach (const ZigbeeClusterAttribute &attribute, cluster->attributes()) {
// qCDebug(dcZigbeeNetwork()) << " " << attribute;
// }
// }
// qCDebug(dcZigbeeNetwork()) << "Input cluster:";
// foreach (ZigbeeCluster *cluster, node->inputClusters()) {
// qCDebug(dcZigbeeNetwork()) << " " << cluster;
// foreach (const ZigbeeClusterAttribute &attribute, cluster->attributes()) {
// qCDebug(dcZigbeeNetwork()) << " " << attribute;
// }
// }
}
}
void ZigbeeNetwork::clearSettings()
{
qCDebug(dcZigbeeNetwork()) << "Remove zigbee nodes from network";
foreach (ZigbeeNode *node, m_nodes) {
removeNode(node);
}
qCDebug(dcZigbeeNetwork()) << "Clear network settings" << m_settingsFileName;
QSettings settings(m_settingsFileName, QSettings::IniFormat, this);
settings.clear();
// Reset network configurations
qCDebug(dcZigbeeNetwork()) << "Clear network properties";
m_extendedPanId = 0;
m_channel = 0;
m_securityConfiguration.clear();
m_nodeType = ZigbeeNode::NodeTypeCoordinator;
}
void ZigbeeNetwork::saveNode(ZigbeeNode *node)
{
QSettings settings(m_settingsFileName, QSettings::IniFormat, this);
settings.beginGroup("Nodes");
// Clear settings for this node before storing it
settings.beginGroup(node->extendedAddress().toString());
settings.remove("");
settings.endGroup();
// Save this node
settings.beginGroup(node->extendedAddress().toString());
settings.setValue("nwkAddress", node->shortAddress());
settings.setValue("macCapabilitiesFlag", node->m_macCapabilitiesFlag);
settings.setValue("nodeDescriptorRawData", node->m_nodeDescriptorRawData);
settings.setValue("powerDescriptorFlag", node->m_powerDescriptorFlag);
// TODO: store endpoints
// TODO: save the rest of the node
// // Input clusters
// settings.beginGroup("inputCluster");
// foreach (ZigbeeCluster *cluster, node->inputClusters()) {
// settings.beginGroup(QString::number(static_cast<int>(cluster->clusterId())));
// foreach (const ZigbeeClusterAttribute &attribute, cluster->attributes()) {
// settings.beginGroup(QString::number(static_cast<int>(attribute.id())));
// settings.setValue("dataType", static_cast<int>(attribute.dataType()));
// settings.setValue("data", attribute.data());
// settings.endGroup(); // attributeId
// }
// settings.endGroup(); // clusterId
// }
// settings.endGroup(); // inputCluster
// // Output clusters
// settings.beginGroup("outputCluster");
// foreach (ZigbeeCluster *cluster, node->outputClusters()) {
// settings.beginGroup(QString::number(static_cast<int>(cluster->clusterId())));
// foreach (const ZigbeeClusterAttribute &attribute, cluster->attributes()) {
// settings.beginGroup(QString::number(static_cast<int>(attribute.id())));
// settings.setValue("dataType", static_cast<int>(attribute.dataType()));
// settings.setValue("data", attribute.data());
// settings.endGroup(); // attributeId
// }
// settings.endGroup(); // clusterId
// }
// settings.endGroup(); // inputCluster
settings.endGroup(); // Node ieee address
settings.endGroup(); // Nodes
}
void ZigbeeNetwork::removeNodeFromSettings(ZigbeeNode *node)
{
qCDebug(dcZigbeeNetwork()) << "Remove node" << node << "from settings" << m_settingsFileName;
QSettings settings(m_settingsFileName, QSettings::IniFormat, this);
settings.beginGroup("Nodes");
// Clear settings for this node before storing it
settings.beginGroup(node->extendedAddress().toString());
settings.remove("");
settings.endGroup();
settings.endGroup(); // Nodes
}
ZigbeeNode *ZigbeeNetwork::createNode(QObject *parent)
{
return new ZigbeeNode(parent);
}
void ZigbeeNetwork::addNode(ZigbeeNode *node)
{
qCDebug(dcZigbeeNetwork()) << "Add node" << node;
addNodeInternally(node);
saveNode(node);
}
void ZigbeeNetwork::addUnitializedNode(ZigbeeNode *node)
{
if (m_uninitializedNodes.contains(node)) {
qCWarning(dcZigbeeNetwork()) << "The uninitialized node" << node << "has already been added.";
return;
}
connect(node, &ZigbeeNode::stateChanged, this, &ZigbeeNetwork::onNodeStateChanged);
m_uninitializedNodes.append(node);
}
void ZigbeeNetwork::removeNode(ZigbeeNode *node)
{
qCDebug(dcZigbeeNetwork()) << "Remove node" << node;
removeNodeInternally(node);
removeNodeFromSettings(node);
}
void ZigbeeNetwork::setState(ZigbeeNetwork::State state)
{
if (m_state == state)
return;
qCDebug(dcZigbeeNetwork()) << "State changed" << state;
m_state = state;
emit stateChanged(m_state);
if (state == StateRunning) saveNetwork();
}
void ZigbeeNetwork::setError(ZigbeeNetwork::Error error)
{
if (m_error == error)
return;
if (m_error != ErrorNoError) qCDebug(dcZigbeeNetwork()) << "Error occured" << error;
m_error = error;
emit errorOccured(m_error);
}
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(node, &ZigbeeNode::stateChanged, this, &ZigbeeNetwork::onNodeStateChanged);
addNode(node);
}
}
void ZigbeeNetwork::onNodeClusterAttributeChanged(ZigbeeCluster *cluster, const ZigbeeClusterAttribute &attribute)
{
Q_UNUSED(cluster)
Q_UNUSED(attribute)
ZigbeeNode *node = qobject_cast<ZigbeeNode *>(sender());
saveNode(node);
}