// SPDX-License-Identifier: LGPL-3.0-or-later /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2013 - 2024, nymea GmbH * Copyright (C) 2024 - 2025, chargebyte austria GmbH * * This file is part of libnymea-app. * * libnymea-app is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * libnymea-app 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 libnymea-app. If not, see . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "zwavemanager.h" #include #include #include "types/serialport.h" #include "logging.h" NYMEA_LOGGING_CATEGORY(dcZWave, "ZWave") ZWaveManager::ZWaveManager(QObject *parent): QObject{parent}, m_serialPorts(new SerialPorts(this)), m_networks(new ZWaveNetworks(this)) { } ZWaveManager::~ZWaveManager() { if (m_engine) { m_engine->jsonRpcClient()->unregisterNotificationHandler(this); } } void ZWaveManager::setEngine(Engine *engine) { if (m_engine != engine) { if (m_engine) { m_engine->jsonRpcClient()->unregisterNotificationHandler(this); } m_engine = engine; emit engineChanged(); init(); } } Engine *ZWaveManager::engine() const { return m_engine; } bool ZWaveManager::fetchingData() const { return m_fetchingData; } bool ZWaveManager::zwaveAvailable() const { return m_zwaveAvailable; } SerialPorts *ZWaveManager::serialPorts() const { return m_serialPorts; } ZWaveNetworks *ZWaveManager::networks() const { return m_networks; } int ZWaveManager::addNetwork(const QString &serialPort) { QVariantMap params; params.insert("serialPort", serialPort); return m_engine->jsonRpcClient()->sendCommand("ZWave.AddNetwork", params, this, "addNetworkResponse"); } int ZWaveManager::removeNetwork(const QUuid &networkUuid) { QVariantMap params = {{"networkUuid", networkUuid}}; return m_engine->jsonRpcClient()->sendCommand("ZWave.RemoveNetwork", params, this, "removeNetworkResponse"); } int ZWaveManager::addNode(const QUuid &networkUuid) { QVariantMap params = {{"networkUuid", networkUuid}}; return m_engine->jsonRpcClient()->sendCommand("ZWave.AddNode", params, this, "addNodeResponse"); } void ZWaveManager::cancelPendingOperation(const QUuid &networkUuid) { m_engine->jsonRpcClient()->sendCommand("ZWave.CancelPendingOperation", {{"networkUuid", networkUuid}}, this, "cancelPendingOperationResponse"); } int ZWaveManager::factoryResetNetwork(const QUuid &networkUuid) { QVariantMap params = {{"networkUuid", networkUuid}}; return m_engine->jsonRpcClient()->sendCommand("ZWave.FactoryResetNetwork", params, this, "factoryResetNetworkResponse"); } int ZWaveManager::removeNode(const QUuid &networkUuid) { return m_engine->jsonRpcClient()->sendCommand("ZWave.RemoveNode", {{"networkUuid", networkUuid}}, this, "removeNodeResponse"); } int ZWaveManager::removeFailedNode(const QUuid &networkUuid, int nodeId) { return m_engine->jsonRpcClient()->sendCommand("ZWave.RemoveFailedNode", {{"networkUuid", networkUuid}, {"nodeId", nodeId}}, this, "removeFailedNodeResponse"); } void ZWaveManager::init() { m_zwaveAvailable = false; emit zwaveAvailableChanged(); m_fetchingData = true; emit fetchingDataChanged(); m_networks->clear(); m_serialPorts->clear(); m_engine->jsonRpcClient()->registerNotificationHandler(this, "ZWave", "notificationReceived"); m_engine->jsonRpcClient()->sendCommand("ZWave.IsZWaveAvailable", this, "isZWaveAvailableResponse"); m_engine->jsonRpcClient()->sendCommand("ZWave.GetSerialPorts", this, "getSerialPortsResponse"); m_engine->jsonRpcClient()->sendCommand("ZWave.GetNetworks", this, "getNetworksResponse"); } void ZWaveManager::isZWaveAvailableResponse(int commandId, const QVariantMap ¶ms) { Q_UNUSED(commandId) m_zwaveAvailable = params.value("available").toBool(); emit zwaveAvailableChanged(); } void ZWaveManager::getSerialPortsResponse(int commandId, const QVariantMap ¶ms) { qCDebug(dcZWave()) << "Serial ports response:" << commandId << qUtf8Printable(QJsonDocument::fromVariant(params).toJson()); foreach (const QVariant &entryVariant, params.value("serialPorts").toList()) { SerialPort *serialPort = SerialPort::unpackSerialPort(entryVariant.toMap(), this); m_serialPorts->addSerialPort(serialPort); } qCDebug(dcZWave) << "Added" << m_serialPorts->rowCount() << "ports"; } void ZWaveManager::getNetworksResponse(int commandId, const QVariantMap ¶ms) { qCDebug(dcZWave()) << "get networks response:" << commandId << params; foreach (const QVariant &entryVariant, params.value("networks").toList()) { QVariantMap entry = entryVariant.toMap(); ZWaveNetwork *network = unpackNetwork(entry); m_networks->addNetwork(network); int id = m_engine->jsonRpcClient()->sendCommand("ZWave.GetNodes", {{"networkUuid", network->networkUuid()}}, this, "getNodesResponse"); m_pendingGetNodeCalls.insert(id, network->networkUuid()); } m_fetchingData = false; emit fetchingDataChanged(); } void ZWaveManager::addNetworkResponse(int commandId, const QVariantMap ¶ms) { qCDebug(dcZWave()) << "Add network response" << commandId << params; QMetaEnum errorEnum = QMetaEnum::fromType(); ZWaveError error = static_cast(errorEnum.keyToValue(params.value("zwaveError").toByteArray())); emit addNetworkReply(commandId, error, params.value("networkUuid").toUuid()); } void ZWaveManager::removeNetworkResponse(int commandId, const QVariantMap ¶ms) { qCDebug(dcZWave()) << "Remove network response" << commandId << params; QMetaEnum errorEnum = QMetaEnum::fromType(); ZWaveError error = static_cast(errorEnum.keyToValue(params.value("zwaveError").toByteArray())); emit removeNetworkReply(commandId, error); } void ZWaveManager::cancelPendingOperationResponse(int commandId, const QVariantMap ¶ms) { qCDebug(dcZWave()) << "Cancel pending operation response" << commandId << params; QMetaEnum errorEnum = QMetaEnum::fromType(); ZWaveError error = static_cast(errorEnum.keyToValue(params.value("zwaveError").toByteArray())); emit cancelPendingOperationReply(commandId, error); } void ZWaveManager::addNodeResponse(int commandId, const QVariantMap ¶ms) { QMetaEnum errorEnum = QMetaEnum::fromType(); ZWaveError error = static_cast(errorEnum.keyToValue(params.value("zwaveError").toByteArray())); emit addNodeReply(commandId, error); } void ZWaveManager::softResetControllerResponse(int commandId, const QVariantMap ¶ms) { QMetaEnum errorEnum = QMetaEnum::fromType(); ZWaveError error = static_cast(errorEnum.keyToValue(params.value("zwaveError").toByteArray())); emit softResetControllerReply(commandId, error); } void ZWaveManager::factoryResetNetworkResponse(int commandId, const QVariantMap ¶ms) { QMetaEnum errorEnum = QMetaEnum::fromType(); ZWaveError error = static_cast(errorEnum.keyToValue(params.value("zwaveError").toByteArray())); emit factoryResetNetworkReply(commandId, error); } void ZWaveManager::getNodesResponse(int commandId, const QVariantMap ¶ms) { QUuid networkUuid = m_pendingGetNodeCalls.value(commandId); ZWaveNetwork *network = m_networks->getNetwork(networkUuid); if (!network) { qCWarning(dcZWave()) << "Received a getNodes response for a network we don't know!?"; return; } qCDebug(dcZWave()) << "GetNodes response:" << qUtf8Printable(QJsonDocument::fromVariant(params).toJson()); foreach (const QVariant &entry, params.value("nodes").toList()) { QVariantMap nodeMap = entry.toMap(); network->addNode(unpackNode(nodeMap)); } } void ZWaveManager::removeNodeResponse(int commandId, const QVariantMap ¶ms) { qCDebug(dcZWave()) << "Remove noderesponse" << commandId << params; QMetaEnum errorEnum = QMetaEnum::fromType(); ZWaveError error = static_cast(errorEnum.keyToValue(params.value("zwaveError").toByteArray())); emit removeNodeReply(commandId, error); } void ZWaveManager::removeFailedNodeResponse(int commandId, const QVariantMap ¶ms) { qCDebug(dcZWave()) << "RemoveFailedNode response:" << commandId << params; QMetaEnum errorEnum = QMetaEnum::fromType(); ZWaveError error = static_cast(errorEnum.keyToValue(params.value("zwaveError").toByteArray())); emit removeFailedNodeReply(commandId, error); } void ZWaveManager::notificationReceived(const QVariantMap &data) { qCDebug(dcZWave) << "Notification received:" << data; QString notification = data.value("notification").toString(); if (notification == "ZWave.NetworkAdded") { ZWaveNetwork *network = unpackNetwork(data.value("params").toMap().value("network").toMap()); m_networks->addNetwork(network); } else if (notification == "ZWave.NetworkRemoved") { m_networks->removeNetwork(data.value("params").toMap().value("networkUuid").toUuid()); } else if (notification == "ZWave.NetworkChanged") { QVariantMap networkMap = data.value("params").toMap().value("network").toMap(); QUuid networkUuid = networkMap.value("networkUuid").toUuid(); ZWaveNetwork *network = m_networks->getNetwork(networkUuid); if (!network) { qCWarning(dcZWave()) << "Received a NetworkChanged notification for a network we don't know."; return; } unpackNetwork(networkMap, network); } else if (notification == "ZWave.NodeAdded") { QVariantMap nodeMap = data.value("params").toMap().value("node").toMap(); QUuid networkUuid = data.value("params").toMap().value("networkUuid").toUuid(); ZWaveNetwork *network = m_networks->getNetwork(networkUuid); if (!network) { qCWarning(dcZWave()) << "Received a NodeAdded notification for a network we don't know."; return; } network->addNode(unpackNode(nodeMap)); } else if (notification == "ZWave.NodeRemoved") { quint8 nodeId = data.value("params").toMap().value("nodeId").toUInt(); QUuid networkUuid = data.value("params").toMap().value("networkUuid").toUuid(); ZWaveNetwork *network = m_networks->getNetwork(networkUuid); if (!network) { qCWarning(dcZWave()) << "Received a NodeRemoved notification for a network we don't know."; return; } network->removeNode(nodeId); } else if (notification == "ZWave.NodeChanged") { QVariantMap nodeMap = data.value("params").toMap().value("node").toMap(); QUuid networkUuid = data.value("params").toMap().value("networkUuid").toUuid(); ZWaveNetwork *network = m_networks->getNetwork(networkUuid); if (!network) { qCWarning(dcZWave()) << "Received a NodeChanged notification for a network we don't know."; return; } ZWaveNode *node = network->nodes()->getNode(nodeMap.value("nodeId").toUInt()); if (!node) { qCWarning(dcZWave()) << "Received a NodeChanged notification for a node we don't know"; return; } unpackNode(nodeMap, node); } } ZWaveNetwork *ZWaveManager::unpackNetwork(const QVariantMap &networkMap, ZWaveNetwork *network) { if (!network) { network = new ZWaveNetwork(networkMap.value("networkUuid").toUuid(), networkMap.value("serialPort").toString()); } network->setHomeId(networkMap.value("homeId").toUInt()); QMetaEnum stateEnum = QMetaEnum::fromType(); network->setIsZWavePlus(networkMap.value("isZWavePlus").toBool()); network->setIsPrimaryController(networkMap.value("isPrimaryController").toBool()); network->setIsStaticUpdateController(networkMap.value("isStaticUpdateController").toBool()); network->setWaitingForNodeAddition(networkMap.value("waitingForNodeAddition").toBool()); network->setWaitingForNodeRemoval(networkMap.value("waitingForNodeRemoval").toBool()); network->setNetworkState(static_cast(stateEnum.keyToValue(networkMap.value("networkState").toByteArray()))); return network; } ZWaveNode *ZWaveManager::unpackNode(const QVariantMap &nodeMap, ZWaveNode *node) { if (!node) { node = new ZWaveNode(nodeMap.value("networkUuid").toUuid(), nodeMap.value("nodeId").toUInt()); } node->setInitialized(nodeMap.value("initialized").toBool()); node->setReachable(nodeMap.value("reachable").toBool()); node->setFailed(nodeMap.value("failed").toBool()); node->setSleeping(nodeMap.value("sleeping").toBool()); node->setLinkQuality(nodeMap.value("linkQuality").toUInt()); node->setSecurityMode(nodeMap.value("securityMode").toUInt()); QMetaEnum nodeTypeEnum = QMetaEnum::fromType(); node->setNodeType(static_cast(nodeTypeEnum.keyToValue(nodeMap.value("nodeType").toByteArray()))); QMetaEnum roleEnum = QMetaEnum::fromType(); node->setRole(static_cast(roleEnum.keyToValue(nodeMap.value("role").toByteArray()))); QMetaEnum deviceTypeEnum = QMetaEnum::fromType(); node->setDeviceType(static_cast(deviceTypeEnum.keyToValue(nodeMap.value("deviceType").toByteArray()))); node->setName(nodeMap.value("name").toString()); node->setManufacturerId(nodeMap.value("manufacturerId").toUInt()); node->setManufacturerName(nodeMap.value("manufacturerName").toString()); node->setProductId(nodeMap.value("productId").toUInt()); node->setProductName(nodeMap.value("productName").toString()); node->setProductType(nodeMap.value("productType").toUInt()); node->setVersion(nodeMap.value("version").toUInt()); node->setIsZWavePlusDevice(nodeMap.value("isZWavePlusDevice").toBool()); node->setIsSecurityDevice(nodeMap.value("isSecurityDevice").toBool()); node->setIsBeamingDevice(nodeMap.value("isBeamingDevice").toBool()); return node; }