mirror of https://github.com/nymea/nymea.git
723 lines
28 KiB
C++
723 lines
28 KiB
C++
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
*
|
|
* Copyright 2013 - 2022, 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 General Public License Usage
|
|
* Alternatively, this project may be redistributed and/or modified under the
|
|
* terms of the GNU General Public License as published by the Free Software
|
|
* Foundation, GNU 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 General
|
|
* Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU 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 "zwavemanager.h"
|
|
#include "hardware/zwave/zwavebackend.h"
|
|
#include "zwavemanagerreply.h"
|
|
#include "zwave/zwavenodeimplementation.h"
|
|
|
|
#include "nymeasettings.h"
|
|
|
|
#include <QSerialPortInfo>
|
|
#include <QCoreApplication>
|
|
#include <QDir>
|
|
#include <QPluginLoader>
|
|
#include <QStringList>
|
|
|
|
namespace nymeaserver
|
|
{
|
|
|
|
ZWaveManager::ZWaveManager(SerialPortMonitor *serialPortMonitor, QObject *parent):
|
|
QObject(parent),
|
|
m_serialPortMonitor(serialPortMonitor)
|
|
{
|
|
qRegisterMetaType<ZWave::ZWaveError>();
|
|
qRegisterMetaType<ZWaveValue::Genre>();
|
|
qRegisterMetaType<ZWaveValue::CommandClass>();
|
|
|
|
#ifdef WITH_OPENZWAVE
|
|
m_backend = new OpenZWaveBackend(this);
|
|
#endif
|
|
|
|
if (!loadBackend()) {
|
|
qCInfo(dcZWave()) << "No Z-Wave backend found. Z-Wave support will be disabled.";
|
|
return;
|
|
}
|
|
|
|
loadZWaveNetworks();
|
|
|
|
connect(m_backend, &ZWaveBackend::networkStarted, this, &ZWaveManager::onNetworkStarted);
|
|
connect(m_backend, &ZWaveBackend::networkFailed, this, &ZWaveManager::onNetworkFailed);
|
|
connect(m_backend, &ZWaveBackend::waitingForNodeAdditionChanged, this, &ZWaveManager::onWaitingForNodeAdditionChanged);
|
|
connect(m_backend, &ZWaveBackend::waitingForNodeRemovalChanged, this, &ZWaveManager::onWaitingForNodeRemovalChanged);
|
|
|
|
connect(m_backend, &ZWaveBackend::nodeAdded, this, &ZWaveManager::onNodeAdded);
|
|
connect(m_backend, &ZWaveBackend::nodeInitialized, this, &ZWaveManager::onNodeInitialized);
|
|
connect(m_backend, &ZWaveBackend::nodeRemoved, this, &ZWaveManager::onNodeRemoved);
|
|
connect(m_backend, &ZWaveBackend::nodeDataChanged, this, &ZWaveManager::onNodeDataChanged);
|
|
connect(m_backend, &ZWaveBackend::nodeReachableStatus, this, &ZWaveManager::onNodeReachableStatus);
|
|
connect(m_backend, &ZWaveBackend::nodeFailedStatus, this, &ZWaveManager::onNodeFailedStatus);
|
|
connect(m_backend, &ZWaveBackend::nodeSleepStatus, this, &ZWaveManager::onNodeSleepStatus);
|
|
connect(m_backend, &ZWaveBackend::nodeLinkQualityStatus, this, &ZWaveManager::onNodeLinkQualityStatus);
|
|
|
|
connect(m_backend, &ZWaveBackend::valueAdded, this, &ZWaveManager::onValueAdded);
|
|
connect(m_backend, &ZWaveBackend::valueChanged, this, &ZWaveManager::onValueChanged);
|
|
connect(m_backend, &ZWaveBackend::valueRemoved, this, &ZWaveManager::onValueRemoved);
|
|
}
|
|
|
|
ZWaveManager::~ZWaveManager()
|
|
{
|
|
}
|
|
|
|
bool ZWaveManager::available() const
|
|
{
|
|
return m_backend != nullptr;
|
|
}
|
|
|
|
bool ZWaveManager::enabled() const
|
|
{
|
|
return true; // TODO
|
|
}
|
|
|
|
void ZWaveManager::setEnabled(bool enabled)
|
|
{
|
|
Q_UNUSED(enabled)
|
|
// TODO
|
|
}
|
|
|
|
SerialPorts ZWaveManager::serialPorts() const
|
|
{
|
|
SerialPorts serialPorts;
|
|
|
|
// FIXME: There should be a mechanism in SerialPortMonitor so that resources can claim a port and it won't show
|
|
// up any more in other resources.
|
|
foreach (const SerialPort &serialPort, m_serialPortMonitor->serialPorts()) {
|
|
bool used = false;
|
|
foreach (ZWaveNetwork *network, m_networks) {
|
|
if (network->serialPort() == serialPort.portName()) {
|
|
used = true;
|
|
}
|
|
}
|
|
if (!used) {
|
|
serialPorts.append(serialPort);
|
|
}
|
|
}
|
|
return serialPorts;
|
|
}
|
|
|
|
void ZWaveManager::loadZWaveNetworks()
|
|
{
|
|
NymeaSettings settings(NymeaSettings::SettingsRoleZWave);
|
|
qCDebug(dcZWave()) << "Loading ZWave networks from" << settings.fileName();
|
|
settings.beginGroup("Networks");
|
|
foreach (const QString &uuidString, settings.childGroups()) {
|
|
settings.beginGroup(uuidString);
|
|
QString serialPort = settings.value("serialPort").toString();
|
|
quint32 homeId = settings.value("homeId").toULongLong();
|
|
QString networkKey = settings.value("networkKey").toString();
|
|
quint8 controllerNodeId = settings.value("controllerNodeId").toUInt();
|
|
bool isZWavePlus = settings.value("isZWavePlus").toBool();
|
|
bool isPrimaryController = settings.value("isPrimaryController").toBool();
|
|
bool isStaticUpdateController = settings.value("isStaticUpdateController").toBool();
|
|
settings.endGroup(); // uuid
|
|
|
|
ZWaveNetwork *network = new ZWaveNetwork(QUuid(uuidString), serialPort, networkKey, this);
|
|
network->setHomeId(homeId);
|
|
network->setControllerNodeId(controllerNodeId);
|
|
network->setIsZWavePlus(isZWavePlus);
|
|
network->setIsPrimaryController(isPrimaryController);
|
|
network->setIsStaticUpdateController(isStaticUpdateController);
|
|
|
|
loadNetwork(network);
|
|
qCInfo(dcZWave) << "Loaded network" << uuidString << "with" << network->nodes().count() << "nodes";
|
|
foreach (ZWaveNode *node, network->nodes()) {
|
|
qCDebug(dcZWave) << node;
|
|
}
|
|
}
|
|
|
|
settings.endGroup(); // Networks
|
|
}
|
|
|
|
bool ZWaveManager::loadNetwork(ZWaveNetwork *network)
|
|
{
|
|
bool success = m_backend->startNetwork(network->networkUuid(), network->serialPort(), network->networkKey());
|
|
if (!success) {
|
|
return false;
|
|
}
|
|
|
|
ZWaveDeviceDatabase *db = new ZWaveDeviceDatabase(NymeaSettings::settingsPath(), network->networkUuid());
|
|
if (!db->initDB()) {
|
|
qCCritical(dcZWave()) << "Unable to initialize ZWave device database";
|
|
delete db;
|
|
return false;
|
|
}
|
|
|
|
m_networks.insert(network->networkUuid(), network);
|
|
m_dbs.insert(network->networkUuid(), db);
|
|
|
|
foreach (ZWaveNode *n, db->createNodes(this)) {
|
|
ZWaveNodeImplementation *node = qobject_cast<ZWaveNodeImplementation*>(n);
|
|
node->setInitialized(true);
|
|
connect(node, &ZWaveNodeImplementation::nodeChanged, this, [this, node](){emit nodeChanged(node);});
|
|
network->addNode(node);
|
|
}
|
|
|
|
network->setNetworkState(ZWaveNetwork::ZWaveNetworkStateStarting);
|
|
emit networkStateChanged(network);
|
|
return true;
|
|
}
|
|
|
|
void ZWaveManager::storeNetwork(ZWaveNetwork *network)
|
|
{
|
|
NymeaSettings settings(NymeaSettings::SettingsRoleZWave);
|
|
settings.beginGroup("Networks");
|
|
settings.beginGroup(network->networkUuid().toString());
|
|
settings.setValue("serialPort", network->serialPort());
|
|
settings.setValue("homeId", network->homeId());
|
|
settings.setValue("networkKey", network->networkKey());
|
|
settings.setValue("controllerNodeId", network->controllerNodeId());
|
|
settings.setValue("isZWavePlus", network->isZWavePlus());
|
|
settings.setValue("isPrimaryController", network->isPrimaryController());
|
|
settings.setValue("isStaticUpdateController", network->isStaticUpdateController());
|
|
settings.endGroup();
|
|
settings.endGroup();
|
|
}
|
|
|
|
void ZWaveManager::onNetworkStarted(const QUuid &networkUuid)
|
|
{
|
|
ZWaveNetwork *network = m_networks.value(networkUuid);
|
|
if (!network) {
|
|
qCWarning(dcZWave()) << "Received a network started signal for a network we don't know:" << networkUuid.toString();
|
|
return;
|
|
}
|
|
|
|
network->setHomeId(m_backend->homeId(networkUuid));
|
|
network->setControllerNodeId(m_backend->controllerNodeId(networkUuid));
|
|
network->setIsPrimaryController(m_backend->isPrimaryController(networkUuid));
|
|
network->setIsStaticUpdateController(m_backend->isStaticUpdateController(networkUuid));
|
|
network->setIsBridgeController(m_backend->isBridgeController(networkUuid));
|
|
qCDebug(dcZWave()) << "Network started" << network->networkUuid().toString();
|
|
storeNetwork(network);
|
|
emit networkChanged(network);
|
|
|
|
network->setNetworkState(ZWaveNetwork::ZWaveNetworkStateOnline);
|
|
emit networkStateChanged(network);
|
|
|
|
foreach (ZWaveNode *n, network->nodes()) {
|
|
ZWaveNodeImplementation *node = qobject_cast<ZWaveNodeImplementation*>(n);
|
|
if (node->failed() != m_backend->isNodeFailed(networkUuid, node->nodeId())) {
|
|
node->setFailed(m_backend->isNodeFailed(networkUuid, node->nodeId()));
|
|
emit nodeChanged(node);
|
|
}
|
|
qCDebug(dcZWave) << "Node" << node->productName() << "is failed:" << node->failed() << "awake:" << m_backend->isNodeAwake(networkUuid, node->nodeId());
|
|
}
|
|
}
|
|
|
|
void ZWaveManager::onNetworkFailed(const QUuid &networkUuid)
|
|
{
|
|
ZWaveNetwork *network = m_networks.value(networkUuid);
|
|
if (!network) {
|
|
qCWarning(dcZWave()) << "Received a network failed signal for a network we don't know:" << networkUuid.toString();
|
|
return;
|
|
}
|
|
qCWarning(dcZWave()).nospace() << "Failed to initialize adapter for network " << network->networkUuid().toString() << " at " << network->serialPort() << ". Retrying in 5 seconds...";
|
|
network->setNetworkState(ZWaveNetwork::ZWaveNetworkStateError);
|
|
emit networkStateChanged(network);
|
|
|
|
// As long as the network exists, keep retrying...
|
|
QTimer::singleShot(5000, network, [this, network]() {
|
|
qCInfo(dcZWave()) << "Retrying to initialize adapter for network" << network->networkUuid().toString() << "at" << network->serialPort();
|
|
network->setNetworkState(ZWaveNetwork::ZWaveNetworkStateStarting);
|
|
emit networkStateChanged(network);
|
|
if (!m_backend->startNetwork(network->networkUuid(), network->serialPort())) {
|
|
onNetworkFailed(network->networkUuid());
|
|
}
|
|
});
|
|
}
|
|
|
|
void ZWaveManager::onWaitingForNodeAdditionChanged(const QUuid &networkUuid, bool waitingForNodeAddition)
|
|
{
|
|
ZWaveNetwork *network = m_networks.value(networkUuid);
|
|
if (!network) {
|
|
qCWarning(dcZWave()) << "Received a network waiting for node addition changed signal for a network we don't know:" << networkUuid.toString();
|
|
return;
|
|
}
|
|
network->setWaitingForNodeAddition(waitingForNodeAddition);
|
|
emit networkChanged(network);
|
|
}
|
|
|
|
void ZWaveManager::onWaitingForNodeRemovalChanged(const QUuid &networkUuid, bool waitingForNodeRemoval)
|
|
{
|
|
ZWaveNetwork *network = m_networks.value(networkUuid);
|
|
if (!network) {
|
|
qCWarning(dcZWave()) << "Received a network waiting for node addition changed signal for a network we don't know:" << networkUuid.toString();
|
|
return;
|
|
}
|
|
network->setWaitingForNodeRemoval(waitingForNodeRemoval);
|
|
emit networkChanged(network);
|
|
}
|
|
|
|
void ZWaveManager::onNodeAdded(const QUuid &networkUuid, quint8 nodeId)
|
|
{
|
|
ZWaveNetwork *network = m_networks.value(networkUuid);
|
|
if (!network) {
|
|
qCWarning(dcZWave()) << "Received a node added signal for a network we don't know:" << networkUuid.toString();
|
|
return;
|
|
}
|
|
ZWaveNodeImplementation *node = qobject_cast<ZWaveNodeImplementation*>(network->node(nodeId));
|
|
if (node) {
|
|
qCDebug(dcZWave()) << "Received a new node signal for a node we already know:" << nodeId;
|
|
return;
|
|
}
|
|
|
|
qCInfo(dcZWave()) << "New node with ID" << nodeId << "joined the network" << network->networkUuid();
|
|
|
|
node = new ZWaveNodeImplementation(this, network->networkUuid(), nodeId, this);
|
|
connect(node, &ZWaveNodeImplementation::nodeChanged, this, [this, node](){emit nodeChanged(node);});
|
|
network->addNode(node);
|
|
|
|
emit nodeAdded(node);
|
|
|
|
}
|
|
|
|
void ZWaveManager::onNodeInitialized(const QUuid &networkUuid, quint8 nodeId)
|
|
{
|
|
ZWaveNetwork *network = m_networks.value(networkUuid);
|
|
if (!network) {
|
|
qCWarning(dcZWave()) << "Received a node initialized signal for a network we don't know:" << networkUuid.toString();
|
|
return;
|
|
}
|
|
|
|
ZWaveNodeImplementation *node = qobject_cast<ZWaveNodeImplementation*>(network->node(nodeId));
|
|
if (!node) {
|
|
qCWarning(dcZWave()) << "Received a node initialized signal for a node we don't know:" << nodeId;
|
|
return;
|
|
}
|
|
node->setReachable(true);
|
|
|
|
m_dbs.value(network->networkUuid())->storeNode(node);
|
|
|
|
|
|
if (!node->initialized()) {
|
|
node->setInitialized(true);
|
|
emit nodeInitialized(node);
|
|
}
|
|
|
|
qCInfo(dcZWave()) << "Node initialized:" << node->nodeId();
|
|
emit nodeChanged(node);
|
|
}
|
|
|
|
void ZWaveManager::onNodeDataChanged(const QUuid &networkUuid, quint8 nodeId)
|
|
{
|
|
ZWaveNetwork *network = m_networks.value(networkUuid);
|
|
if (!network) {
|
|
qCWarning(dcZWave()) << "Received a node names changed signal for a network we don't know:" << networkUuid.toString();
|
|
return;
|
|
}
|
|
|
|
ZWaveNodeImplementation *node = qobject_cast<ZWaveNodeImplementation*>(network->node(nodeId));
|
|
if (!node) {
|
|
qCWarning(dcZWave()) << "Received a node names changed signal for a node we don't know:" << nodeId;
|
|
return;
|
|
}
|
|
|
|
qCDebug(dcZWave()) << "Node names changed for node" << nodeId << "in network" << network->networkUuid();
|
|
node->blockSignals(true);
|
|
node->setName(m_backend->nodeName(networkUuid, nodeId));
|
|
node->setNodeType(m_backend->nodeType(networkUuid, nodeId));
|
|
node->setRole(m_backend->nodeRole(networkUuid, nodeId));
|
|
node->setDeviceType(m_backend->nodeDeviceType(networkUuid, nodeId));
|
|
node->setManufacturerId(m_backend->nodeManufacturerId(networkUuid, nodeId));
|
|
node->setManufacturerName(m_backend->nodeManufacturerName(networkUuid, nodeId));
|
|
node->setProductId(m_backend->nodeProductId(networkUuid, nodeId));
|
|
node->setProductName(m_backend->nodeProductName(networkUuid, nodeId));
|
|
node->setProductType(m_backend->nodeProductType(networkUuid, nodeId));
|
|
node->setVersion(m_backend->nodeVersion(networkUuid, nodeId));
|
|
node->setIsZWavePlusDevice(m_backend->nodeIsZWavePlus(networkUuid, nodeId));
|
|
node->setIsSecurityDevice(m_backend->nodeIsSecureDevice(networkUuid, nodeId));
|
|
node->setSecurityMode(m_backend->nodeSecurityMode(networkUuid, nodeId));
|
|
node->setIsBeamingDevice(m_backend->nodeIsBeamingDevice(networkUuid, nodeId));
|
|
node->setPlusDeviceType(m_backend->nodePlusDeviceType(networkUuid, nodeId));
|
|
node->blockSignals(false);
|
|
emit nodeChanged(node);
|
|
|
|
m_dbs.value(network->networkUuid())->storeNode(node);
|
|
|
|
if (node->nodeId() == network->controllerNodeId()) {
|
|
network->setIsZWavePlus(m_backend->nodeIsZWavePlus(networkUuid, network->controllerNodeId()));
|
|
emit networkChanged(network);
|
|
}
|
|
}
|
|
|
|
void ZWaveManager::onNodeReachableStatus(const QUuid &networkUuid, quint8 nodeId, bool reachable)
|
|
{
|
|
ZWaveNetwork *network = m_networks.value(networkUuid);
|
|
if (!network) {
|
|
qCWarning(dcZWave()) << "Received a node reachable changed signal for a network we don't know:" << networkUuid.toString();
|
|
return;
|
|
}
|
|
|
|
ZWaveNodeImplementation *node = qobject_cast<ZWaveNodeImplementation*>(network->node(nodeId));
|
|
if (!node) {
|
|
qCWarning(dcZWave()) << "Received a node reachable status signal for a node we don't know:" << nodeId;
|
|
return;
|
|
}
|
|
qCInfo(dcZWave()) << "Node" << nodeId << "in network" << network->networkUuid().toString() << "is" << (reachable ? "reachable" : "not reachable");
|
|
node->setReachable(reachable);
|
|
}
|
|
|
|
void ZWaveManager::onNodeFailedStatus(const QUuid &networkUuid, quint8 nodeId, bool failed)
|
|
{
|
|
ZWaveNetwork *network = m_networks.value(networkUuid);
|
|
if (!network) {
|
|
qCWarning(dcZWave()) << "Received a node failed status signal for a network we don't know:" << networkUuid.toString();
|
|
return;
|
|
}
|
|
|
|
ZWaveNodeImplementation *node = qobject_cast<ZWaveNodeImplementation*>(network->node(nodeId));
|
|
if (!node) {
|
|
qCWarning(dcZWave()) << "Received a node reachable changed signal for a node we don't know:" << nodeId;
|
|
return;
|
|
}
|
|
qCInfo(dcZWave()) << "Node" << nodeId << "in network" << network->networkUuid().toString() << "is" << (failed ? "failed" : "ok");
|
|
node->setFailed(failed);
|
|
}
|
|
|
|
void ZWaveManager::onNodeSleepStatus(const QUuid &networkUuid, quint8 nodeId, bool sleeping)
|
|
{
|
|
ZWaveNetwork *network = m_networks.value(networkUuid);
|
|
if (!network) {
|
|
qCWarning(dcZWave()) << "Received a node sleep status signal for a network we don't know:" << networkUuid.toString();
|
|
return;
|
|
}
|
|
|
|
ZWaveNodeImplementation *node = qobject_cast<ZWaveNodeImplementation*>(network->node(nodeId));
|
|
if (!node) {
|
|
qCWarning(dcZWave()) << "Received a node sleep signal for a node we don't know:" << nodeId;
|
|
return;
|
|
}
|
|
qCInfo(dcZWave()) << "Node" << nodeId << "in network" << network->networkUuid().toString() << "is" << (sleeping ? "sleeping" : "awake");
|
|
node->setSleeping(sleeping);
|
|
}
|
|
|
|
void ZWaveManager::onNodeLinkQualityStatus(const QUuid &networkUuid, quint8 nodeId, quint8 linkQuality)
|
|
{
|
|
ZWaveNetwork *network = m_networks.value(networkUuid);
|
|
if (!network) {
|
|
qCWarning(dcZWave()) << "Received a node link qlility status signal for a network we don't know:" << networkUuid.toString();
|
|
return;
|
|
}
|
|
|
|
ZWaveNodeImplementation *node = qobject_cast<ZWaveNodeImplementation*>(network->node(nodeId));
|
|
if (!node) {
|
|
qCWarning(dcZWave()) << "Received a node link quality signal for a node we don't know:" << nodeId;
|
|
return;
|
|
}
|
|
qCInfo(dcZWave()) << "Link quality for node" << nodeId << "in network" << network->networkUuid().toString() << "is" << linkQuality;
|
|
node->setLinkQuality(linkQuality);
|
|
}
|
|
|
|
void ZWaveManager::onNodeRemoved(const QUuid &networkUuid, quint8 nodeId)
|
|
{
|
|
ZWaveNetwork *network = m_networks.value(networkUuid);
|
|
if (!network) {
|
|
qCWarning(dcZWave()) << "Received a node removed signal for a network we don't know:" << networkUuid.toString();
|
|
return;
|
|
}
|
|
|
|
ZWaveNode *node = network->node(nodeId);
|
|
if (!node) {
|
|
qCWarning(dcZWave()) << "Received a node removed signal for a node we don't know:" << nodeId;
|
|
return;
|
|
}
|
|
qCInfo(dcZWave()) << "Node" << nodeId << "removed from network" << network->networkUuid();
|
|
network->removeNode(nodeId);
|
|
m_dbs.value(network->networkUuid())->removeNode(nodeId);
|
|
emit nodeRemoved(node);
|
|
}
|
|
|
|
void ZWaveManager::onValueAdded(const QUuid &networkUuid, quint8 nodeId, const ZWaveValue &value)
|
|
{
|
|
ZWaveNetwork *network = m_networks.value(networkUuid);
|
|
if (!network) {
|
|
qCWarning(dcZWave) << "Received a value added signal for a network we don't know:" << networkUuid.toString();
|
|
return;
|
|
}
|
|
|
|
ZWaveNodeImplementation *node = qobject_cast<ZWaveNodeImplementation*>(network->node(nodeId));
|
|
if (!node) {
|
|
qCWarning(dcZWave) << "Received a value added signal for a node we don't know:" << nodeId;
|
|
return;
|
|
}
|
|
|
|
qCDebug(dcZWave()) << "Value added to node" << nodeId << value;
|
|
node->updateValue(value);
|
|
m_dbs.value(network->networkUuid())->storeValue(node, value.id());
|
|
}
|
|
|
|
void ZWaveManager::onValueChanged(const QUuid &networkUuid, quint8 nodeId, const ZWaveValue &value)
|
|
{
|
|
ZWaveNetwork *network = m_networks.value(networkUuid);
|
|
if (!network) {
|
|
qCWarning(dcZWave) << "Received a value changed signal for a network we don't know:" << networkUuid.toString();
|
|
return;
|
|
}
|
|
|
|
ZWaveNodeImplementation *node = qobject_cast<ZWaveNodeImplementation*>(network->node(nodeId));
|
|
if (!node) {
|
|
qCWarning(dcZWave) << "Received a value changed signal for a node we don't know:" << nodeId;
|
|
return;
|
|
}
|
|
|
|
qCDebug(dcZWave) << "Value changed for node" << node->nodeId() << value;
|
|
node->updateValue(value);
|
|
// node->setReachable(true);
|
|
m_dbs.value(network->networkUuid())->storeValue(node, value.id());
|
|
}
|
|
|
|
void ZWaveManager::onValueRemoved(const QUuid &networkUuid, quint8 nodeId, quint64 valueId)
|
|
{
|
|
ZWaveNetwork *network = m_networks.value(networkUuid);
|
|
if (!network) {
|
|
qCWarning(dcZWave()) << "Received a value removed signal for a network we don't know:" << networkUuid.toString();
|
|
return;
|
|
}
|
|
ZWaveNodeImplementation *node = qobject_cast<ZWaveNodeImplementation*>(network->node(nodeId));
|
|
if (!node) {
|
|
qCWarning(dcZWave()) << "Received a value removed signal for a node we don't know:" << nodeId;
|
|
return;
|
|
}
|
|
node->removeValue(valueId);
|
|
m_dbs.value(network->networkUuid())->removeValue(node, valueId);
|
|
}
|
|
|
|
|
|
ZWaveNetworks ZWaveManager::networks() const
|
|
{
|
|
return m_networks.values();
|
|
}
|
|
|
|
ZWaveNetwork *ZWaveManager::network(const QUuid &networkUuid) const
|
|
{
|
|
return m_networks.value(networkUuid);
|
|
}
|
|
|
|
QPair<ZWave::ZWaveError, QUuid> ZWaveManager::createNetwork(const QString &serialPort)
|
|
{
|
|
if (!available()) {
|
|
qCWarning(dcZWave()) << "Z-Wave is not available.";
|
|
return qMakePair<ZWave::ZWaveError, QUuid>(ZWave::ZWaveErrorBackendError, QUuid());
|
|
}
|
|
|
|
QString networkKey = QUuid::createUuid().toString().remove(QRegExp("[{\\-}]*"));
|
|
ZWaveNetwork *network = new ZWaveNetwork(QUuid::createUuid(), serialPort, networkKey, this);
|
|
bool success = loadNetwork(network);
|
|
if (!success) {
|
|
delete network;
|
|
return qMakePair<ZWave::ZWaveError, QUuid>(ZWave::ZWaveErrorInUse, QUuid());
|
|
}
|
|
emit networkAdded(network);
|
|
|
|
storeNetwork(network);
|
|
|
|
return qMakePair<ZWave::ZWaveError, QUuid>(ZWave::ZWaveErrorNoError, network->networkUuid());
|
|
}
|
|
|
|
ZWave::ZWaveError ZWaveManager::removeNetwork(const QUuid &networkUuid)
|
|
{
|
|
ZWaveNetwork *network = m_networks.value(networkUuid);
|
|
if (!network) {
|
|
return ZWave::ZWaveErrorNetworkUuidNotFound;
|
|
}
|
|
bool status = m_backend->stopNetwork(networkUuid);
|
|
if (!status) {
|
|
return ZWave::ZWaveErrorBackendError;
|
|
}
|
|
|
|
foreach (ZWaveNode *node, network->nodes()) {
|
|
network->removeNode(node->nodeId());
|
|
emit nodeRemoved(node);
|
|
}
|
|
|
|
m_networks.remove(network->networkUuid());
|
|
ZWaveDeviceDatabase *db = m_dbs.take(network->networkUuid());
|
|
db->removeDB();
|
|
delete db;
|
|
network->deleteLater();
|
|
NymeaSettings settings(NymeaSettings::SettingsRoleZWave);
|
|
settings.beginGroup("Networks");
|
|
settings.remove(network->networkUuid().toString());
|
|
settings.endGroup();
|
|
emit networkRemoved(network->networkUuid());
|
|
|
|
return ZWave::ZWaveErrorNoError;
|
|
}
|
|
|
|
ZWave::ZWaveError ZWaveManager::factoryResetNetwork(const QUuid &networkUuid)
|
|
{
|
|
if (!m_networks.contains(networkUuid)) {
|
|
return ZWave::ZWaveErrorNetworkUuidNotFound;
|
|
}
|
|
qCInfo(dcZWave()) << "Resetting controller for network:" << networkUuid.toString();
|
|
|
|
ZWaveNetwork *network = m_networks.value(networkUuid);
|
|
|
|
foreach (ZWaveNode *node, network->nodes()) {
|
|
network->removeNode(node->nodeId());
|
|
emit nodeRemoved(node);
|
|
}
|
|
|
|
m_backend->factoryResetNetwork(networkUuid);
|
|
ZWaveDeviceDatabase *db = m_dbs.value(network->networkUuid());
|
|
db->clearDB();
|
|
network->setHomeId(0);
|
|
emit networkChanged(network);
|
|
network->setNetworkState(ZWaveNetwork::ZWaveNetworkStateStarting);
|
|
emit networkStateChanged(network);
|
|
storeNetwork(network);
|
|
|
|
qCInfo(dcZWave()) << "Controller reset succeeded";
|
|
return ZWave::ZWaveErrorNoError;
|
|
}
|
|
|
|
|
|
ZWaveReply *ZWaveManager::addNode(const QUuid &networkUuid)
|
|
{
|
|
ZWaveNetwork *network = m_networks.value(networkUuid);
|
|
ZWaveManagerReply *reply = new ZWaveManagerReply(this);
|
|
if (!network) {
|
|
reply->finish(ZWave::ZWaveErrorNetworkUuidNotFound);
|
|
return reply;
|
|
}
|
|
ZWaveReply *backendReply = m_backend->addNode(network->networkUuid(), true);
|
|
connect(backendReply, &ZWaveReply::finished, reply, &ZWaveReply::finished);
|
|
qCDebug(dcZWave) << "Adding node to network" << networkUuid.toString();
|
|
return reply;
|
|
}
|
|
|
|
ZWaveReply* ZWaveManager::removeNode(const QUuid &networkUuid)
|
|
{
|
|
ZWaveManagerReply *reply = new ZWaveManagerReply(this);
|
|
ZWaveNetwork *network = m_networks.value(networkUuid);
|
|
if (!network) {
|
|
reply->finish(ZWave::ZWaveErrorNetworkUuidNotFound);
|
|
return reply;
|
|
}
|
|
ZWaveReply *backendReply = m_backend->removeNode(networkUuid);
|
|
connect(backendReply, &ZWaveReply::finished, reply, &ZWaveManagerReply::finish);
|
|
return reply;
|
|
}
|
|
|
|
ZWaveReply* ZWaveManager::removeFailedNode(const QUuid &networkUuid, quint8 nodeId)
|
|
{
|
|
ZWaveManagerReply *reply = new ZWaveManagerReply(this);
|
|
ZWaveNetwork *network = m_networks.value(networkUuid);
|
|
if (!network) {
|
|
reply->finish(ZWave::ZWaveErrorNetworkUuidNotFound);
|
|
return reply;
|
|
}
|
|
ZWaveNode *node = network->node(nodeId);
|
|
if (!node) {
|
|
reply->finish(ZWave::ZWaveErrorNodeIdNotFound);
|
|
return reply;
|
|
}
|
|
|
|
ZWaveReply *backendReply = m_backend->removeFailedNode(networkUuid, nodeId);
|
|
connect(backendReply, &ZWaveReply::finished, reply, &ZWaveManagerReply::finish);
|
|
return reply;
|
|
}
|
|
|
|
ZWaveReply* ZWaveManager::cancelPendingOperation(const QUuid &networkUuid)
|
|
{
|
|
ZWaveManagerReply *reply = new ZWaveManagerReply(this);
|
|
ZWaveNetwork *network = m_networks.value(networkUuid);
|
|
if (!network) {
|
|
reply->finish(ZWave::ZWaveErrorNetworkUuidNotFound);
|
|
return reply;
|
|
}
|
|
|
|
ZWaveReply *backendReply = m_backend->cancelPendingOperation(networkUuid);
|
|
connect(backendReply, &ZWaveReply::finished, reply, &ZWaveManagerReply::finish);
|
|
return reply;
|
|
}
|
|
|
|
void ZWaveManager::setValue(const QUuid &networkUuid, quint8 nodeId, const ZWaveValue &value)
|
|
{
|
|
ZWaveNetwork *network = m_networks.value(networkUuid);
|
|
if (!network) {
|
|
qCWarning(dcZWave()) << "No network with UUID" << networkUuid;
|
|
return;
|
|
}
|
|
|
|
qCDebug(dcZWave) << "Setting value" << value.id() << "on node" << nodeId;
|
|
m_backend->setValue(network->networkUuid(), nodeId, value);
|
|
|
|
}
|
|
|
|
bool ZWaveManager::loadBackend()
|
|
{
|
|
QStringList searchDirs;
|
|
QByteArray envPath = qgetenv("NYMEA_ZWAVE_PLUGIN_PATH");
|
|
if (!envPath.isEmpty()) {
|
|
searchDirs << QString(envPath).split(':');
|
|
}
|
|
|
|
foreach (QString libraryPath, QCoreApplication::libraryPaths()) {
|
|
searchDirs << libraryPath.replace("qt5", "nymea").replace("plugins", "zwave");
|
|
}
|
|
foreach (QString libraryPath, QCoreApplication::libraryPaths()) {
|
|
searchDirs << libraryPath.replace("plugins", "nymea/zwave");
|
|
}
|
|
searchDirs << QCoreApplication::applicationDirPath() + "/../lib/nymea/zwave/";
|
|
searchDirs << QCoreApplication::applicationDirPath() + "/../zwave/";
|
|
searchDirs << QCoreApplication::applicationDirPath() + "/../../../zwave/";
|
|
|
|
foreach (const QString &path, searchDirs) {
|
|
QDir dir(path);
|
|
qCDebug(dcZWave) << "Loading Z-Wave backend from:" << dir.absolutePath();
|
|
foreach (const QString &entry, dir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot)) {
|
|
QFileInfo fi(path + "/" + entry);
|
|
if (fi.isFile()) {
|
|
if (entry.startsWith("libnymea_zwaveplugin") && entry.endsWith(".so")) {
|
|
QPluginLoader loader;
|
|
loader.setFileName(path + "/" + entry);
|
|
loader.setLoadHints(QLibrary::ResolveAllSymbolsHint);
|
|
if (!loader.load()) {
|
|
qCWarning(dcZWave) << loader.errorString();
|
|
continue;
|
|
}
|
|
m_backend = qobject_cast<ZWaveBackend*>(loader.instance());
|
|
if (!m_backend) {
|
|
qCWarning(dcZWave) << "Could not get plugin instance of" << loader.fileName();
|
|
loader.unload();
|
|
continue;
|
|
}
|
|
qCDebug(dcZWave()) << "Loaded Z-Wave backend:" << loader.fileName();
|
|
m_backend->setParent(this);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
}
|