diff --git a/libnymea-core/jsonrpc/zigbeehandler.cpp b/libnymea-core/jsonrpc/zigbeehandler.cpp index eab8bb7c..24e7d5bc 100644 --- a/libnymea-core/jsonrpc/zigbeehandler.cpp +++ b/libnymea-core/jsonrpc/zigbeehandler.cpp @@ -1,530 +1,530 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* 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 . -* -* 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 "zigbeehandler.h" -#include "zigbee/zigbeemanager.h" -#include "zigbee/zigbeeadapters.h" -#include "loggingcategories.h" - -#include - -namespace nymeaserver { - -ZigbeeHandler::ZigbeeHandler(ZigbeeManager *zigbeeManager, QObject *parent) : - JsonHandler(parent), - m_zigbeeManager(zigbeeManager) -{ - qRegisterMetaType(); - registerEnum(); - registerEnum(); - registerEnum(); - registerEnum(); - registerObject(); - - // Network object describing a network instance - QVariantMap zigbeeNetworkDescription; - zigbeeNetworkDescription.insert("networkUuid", enumValueName(Uuid)); - zigbeeNetworkDescription.insert("enabled", enumValueName(Bool)); - zigbeeNetworkDescription.insert("serialPort", enumValueName(String)); - zigbeeNetworkDescription.insert("baudRate", enumValueName(Uint)); - zigbeeNetworkDescription.insert("macAddress", enumValueName(String)); - zigbeeNetworkDescription.insert("firmwareVersion", enumValueName(String)); - zigbeeNetworkDescription.insert("panId", enumValueName(Uint)); - zigbeeNetworkDescription.insert("channel", enumValueName(Uint)); - zigbeeNetworkDescription.insert("channelMask", enumValueName(Uint)); - zigbeeNetworkDescription.insert("permitJoiningEnabled", enumValueName(Bool)); - zigbeeNetworkDescription.insert("permitJoiningDuration", enumValueName(Uint)); - zigbeeNetworkDescription.insert("permitJoiningRemaining", enumValueName(Uint)); - zigbeeNetworkDescription.insert("backend", enumValueName(String)); - zigbeeNetworkDescription.insert("networkState", enumRef()); - registerObject("ZigbeeNetwork", zigbeeNetworkDescription); - - // Zigbee node description - QVariantMap zigbeeNodeDescription; - zigbeeNodeDescription.insert("networkUuid", enumValueName(Uuid)); - zigbeeNodeDescription.insert("ieeeAddress", enumValueName(String)); - zigbeeNodeDescription.insert("networkAddress", enumValueName(Uint)); - zigbeeNodeDescription.insert("type", enumRef()); - zigbeeNodeDescription.insert("state", enumRef()); - zigbeeNodeDescription.insert("manufacturer", enumValueName(String)); - zigbeeNodeDescription.insert("model", enumValueName(String)); - zigbeeNodeDescription.insert("version", enumValueName(String)); - zigbeeNodeDescription.insert("receiverOnWhileIdle", enumValueName(Bool)); - zigbeeNodeDescription.insert("reachable", enumValueName(Bool)); - zigbeeNodeDescription.insert("lqi", enumValueName(Uint)); - zigbeeNodeDescription.insert("lastSeen", enumValueName(Uint)); - registerObject("ZigbeeNode", zigbeeNodeDescription); - - QVariantMap params, returns; - QString description; - - // GetAvailableBackends - params.clear(); returns.clear(); - description = "Get the list of available ZigBee backends."; - returns.insert("backends", QVariantList() << enumValueName(String)); - registerMethod("GetAvailableBackends", description, params, returns); - - // GetAdapters - params.clear(); returns.clear(); - description = "Get the list of available ZigBee adapters and serial ports in order to set up the ZigBee network " - "on the desired interface. The \'serialPort\' property can be used as unique identifier for an adapter. " - "If an adapter hardware has been recognized as a well known ZigBee adapter, " - "the \'hardwareRecognized\' property will be true and the \'baudRate\' and \'backend\' " - "configurations can be used as they where given, otherwise the user might set the backend " - "and baud rate manually. The available backends can be fetched using the GetAvailableBackends method."; - returns.insert("adapters", objectRef()); - registerMethod("GetAdapters", description, params, returns); - - // AdapterAdded notification - params.clear(); - description = "Emitted whenever a new ZigBee adapter or serial port has been detected in the system."; - params.insert("adapter", objectRef()); - registerNotification("AdapterAdded", description, params); - - // AdapterRemoved notification - params.clear(); - description = "Emitted whenever a ZigBee adapter or serial port has been removed from the system (i.e. unplugged)."; - params.insert("adapter", objectRef()); - registerNotification("AdapterRemoved", description, params); - - // GetNetworks - params.clear(); returns.clear(); - description = "Returns the list of configured ZigBee networks in the system."; - returns.insert("zigbeeNetworks", QVariantList() << objectRef("ZigbeeNetwork")); - registerMethod("GetNetworks", description, params, returns); - - // AddNetwork - params.clear(); returns.clear(); - description = "Create a new ZigBee network for the given \'serialPort\', \'baudRate\' and \'backend\'. " - "The serial ports can be fetched from the available adapters. See \'GetAdapters\' for more information. " - "The available backends can be fetched using the \'GetAvailableBackends\' method."; - params.insert("serialPort", enumValueName(String)); - params.insert("baudRate", enumValueName(Uint)); - params.insert("backend", enumValueName(String)); - params.insert("o:channelMask", enumValueName(Uint)); - returns.insert("zigbeeError", enumRef()); - returns.insert("o:networkUuid", enumValueName(Uuid)); - registerMethod("AddNetwork", description, params, returns); - - // RemoveNetwork - params.clear(); returns.clear(); - description = "Remove the ZigBee network with the given network uuid."; - params.insert("networkUuid", enumValueName(Uuid)); - returns.insert("zigbeeError", enumRef()); - registerMethod("RemoveNetwork", description, params, returns); - - // NetworkAdded notification - params.clear(); - description = "Emitted whenever a new ZigBee network has been added."; - params.insert("zigbeeNetwork", objectRef("ZigbeeNetwork")); - registerNotification("NetworkAdded", description, params); - - // NetworkRemoved notification - params.clear(); - description = "Emitted whenever a new ZigBee network has been removed."; - params.insert("networkUuid", enumValueName(Uuid)); - registerNotification("NetworkRemoved", description, params); - - // NetworkChanged notification - params.clear(); - description = "Emitted whenever a new ZigBee network has changed."; - params.insert("zigbeeNetwork", objectRef("ZigbeeNetwork")); - registerNotification("NetworkChanged", description, params); - - // FactoryResetNetwork - params.clear(); returns.clear(); - description = "Factory reset the network with the given \'networkUuid\'. The network does not have " - "to be online for this procedure, and all associated nodes and things will be removed permanently."; - params.insert("networkUuid", enumValueName(Uuid)); - returns.insert("zigbeeError", enumRef()); - registerMethod("FactoryResetNetwork", description, params, returns); - - // SetPermitJoin - params.clear(); returns.clear(); - description = "Allow or deny nodes to join the network with the given \'networkUuid\' for a specific \'duration\' in seconds. " - "The duration value has to be between 0 and 255 seconds. The \'permitJoinDuration\' property of ZigBee network " - "object indicates how long permit has been enabled and the \'permitJoiningRemaining\' indicates the rest of the time. " - "Those values can be used to show a countdown or progressbar. This method can be recalled for resetting the timeout. " - "If the duration is set to 0 seconds, joining will be disabled immediatly for the entire network. " - "The \'shortAddress\' is optional and defaults to the broadcast address 0xfffc for all routers in the network. " - "If the short address matches the address of a router node in the network, only that specific router will " - "be able to allow new nodes to join the network. A new node will join to the router with the best link quality index (LQI)."; - params.insert("networkUuid", enumValueName(Uuid)); - params.insert("duration", enumValueName(Uint)); - params.insert("o:shortAddress", enumValueName(Uint)); - returns.insert("zigbeeError", enumRef()); - registerMethod("SetPermitJoin", description, params, returns); - - // GetNodes - params.clear(); returns.clear(); - description = "Returns the list of ZigBee nodes from the network the given \'networkUuid\' in the system."; - params.insert("networkUuid", enumValueName(Uuid)); - returns.insert("zigbeeError", enumRef()); - returns.insert("o:zigbeeNodes", QVariantList() << objectRef("ZigbeeNode")); - registerMethod("GetNodes", description, params, returns); - - // RemoveNode - params.clear(); returns.clear(); - description = "Remove a ZigBee node with the given \'ieeeAddress\' from the network with the given \'networkUuid\'. " - "If there is a thing configured for this node, also the thing will be removed from the system. " - "The coordinator node cannot be removed."; - params.insert("networkUuid", enumValueName(Uuid)); - params.insert("ieeeAddress", enumValueName(String)); - returns.insert("zigbeeError", enumRef()); - registerMethod("RemoveNode", description, params, returns); - - // NodeAdded - params.clear(); - description = "Emitted whenever a new ZigBee node has joined the network with the given \'networkUuid\'."; - params.insert("networkUuid", enumValueName(Uuid)); - params.insert("zigbeeNode", objectRef("ZigbeeNode")); - registerNotification("NodeAdded", description, params); - - // NodeRemoved - params.clear(); - description = "Emitted whenever a ZigBee node has removed from the network with the given \'networkUuid\'."; - params.insert("networkUuid", enumValueName(Uuid)); - params.insert("zigbeeNode", objectRef("ZigbeeNode")); - registerNotification("NodeRemoved", description, params); - - // NodeChanged - params.clear(); - description = "Emitted whenever a ZigBee node has changed."; - params.insert("networkUuid", enumValueName(Uuid)); - params.insert("zigbeeNode", objectRef("ZigbeeNode")); - registerNotification("NodeChanged", description, params); - - connect(m_zigbeeManager, &ZigbeeManager::availableAdapterAdded, this, [this](const ZigbeeAdapter &adapter){ - QVariantMap params; - params.insert("adapter", pack(adapter)); - emit AdapterAdded(params); - }); - - connect(m_zigbeeManager, &ZigbeeManager::availableAdapterRemoved, this, [this](const ZigbeeAdapter &adapter){ - QVariantMap params; - params.insert("adapter", pack(adapter)); - emit AdapterRemoved(params); - }); - - connect(m_zigbeeManager, &ZigbeeManager::zigbeeNetworkAdded, this, [this](ZigbeeNetwork *network){ - QVariantMap params; - params.insert("zigbeeNetwork", packNetwork(network)); - emit NetworkAdded(params); - }); - - connect(m_zigbeeManager, &ZigbeeManager::zigbeeNetworkChanged, this, [this](ZigbeeNetwork *network){ - QVariantMap params; - params.insert("zigbeeNetwork", packNetwork(network)); - emit NetworkChanged(params); - }); - - connect(m_zigbeeManager, &ZigbeeManager::zigbeeNetworkRemoved, this, [this](const QUuid &networkUuid){ - QVariantMap params; - params.insert("networkUuid", networkUuid); - emit NetworkRemoved(params); - }); - - connect(m_zigbeeManager, &ZigbeeManager::nodeJoined, this, &ZigbeeHandler::onNodeJoined); - connect(m_zigbeeManager, &ZigbeeManager::nodeAdded, this, &ZigbeeHandler::onNodeAdded); - connect(m_zigbeeManager, &ZigbeeManager::nodeChanged, this, &ZigbeeHandler::onNodeChanged); - connect(m_zigbeeManager, &ZigbeeManager::nodeRemoved, this, &ZigbeeHandler::onNodeRemoved); - -} - -QString ZigbeeHandler::name() const -{ - return "Zigbee"; -} - -JsonReply *ZigbeeHandler::GetAvailableBackends(const QVariantMap ¶ms) -{ - Q_UNUSED(params) - - QVariantMap returnMap; - QVariantList backendsList; - foreach (const QString &backendName, ZigbeeAdapter::backendNames().values()) { - backendsList << backendName; - } - returnMap.insert("backends", backendsList); - return createReply(returnMap); -} - -JsonReply *ZigbeeHandler::GetAdapters(const QVariantMap ¶ms) -{ - Q_UNUSED(params) - - QVariantMap returnMap; - QVariantList adapterList; - foreach (const ZigbeeAdapter &adapter, m_zigbeeManager->availableAdapters()) { - adapterList << pack(adapter); - } - returnMap.insert("adapters", adapterList); - return createReply(returnMap); -} - -JsonReply *ZigbeeHandler::AddNetwork(const QVariantMap ¶ms) -{ - QVariantMap returnMap; - - QString serialPort = params.value("serialPort").toString(); - uint baudRate = params.value("baudRate").toUInt(); - QString backendString = params.value("backend").toString(); - if (!ZigbeeAdapter::backendNames().values().contains(backendString)) { - returnMap.insert("zigbeeError", enumValueName(ZigbeeManager::ZigbeeErrorUnknownBackend)); - return createReply(returnMap); - } - - ZigbeeChannelMask channelMask = ZigbeeChannelMask::ChannelConfigurationAllChannels; - if (params.contains("channelMask")) { - channelMask = params.value("channelMask").toUInt() & ZigbeeChannelMask::ChannelConfigurationAllChannels; - if (channelMask == ZigbeeChannelMask::ChannelConfigurationNoChannel) { - returnMap.insert("zigbeeError", enumValueName(ZigbeeManager::ZigbeeErrorInvalidChannel)); - return createReply(returnMap); - } - } - - QPair result = m_zigbeeManager->createZigbeeNetwork(serialPort, baudRate, ZigbeeAdapter::backendNames().key(backendString), channelMask); - if (result.first == ZigbeeManager::ZigbeeErrorNoError) { - returnMap.insert("networkUuid", result.second); - } - returnMap.insert("zigbeeError", enumValueName(result.first)); - return createReply(returnMap); -} - -JsonReply *ZigbeeHandler::RemoveNetwork(const QVariantMap ¶ms) -{ - QUuid networkUuid = params.value("networkUuid").toUuid(); - ZigbeeManager::ZigbeeError error = m_zigbeeManager->removeZigbeeNetwork(networkUuid); - QVariantMap returnMap; - returnMap.insert("zigbeeError", enumValueName(error)); - return createReply(returnMap); -} - -JsonReply *ZigbeeHandler::FactoryResetNetwork(const QVariantMap ¶ms) -{ - QUuid networkUuid = params.value("networkUuid").toUuid(); - ZigbeeManager::ZigbeeError error = m_zigbeeManager->factoryResetNetwork(networkUuid); - QVariantMap returnMap; - returnMap.insert("zigbeeError", enumValueName(error)); - return createReply(returnMap); -} - -JsonReply *ZigbeeHandler::SetPermitJoin(const QVariantMap ¶ms) -{ - QUuid networkUuid = params.value("networkUuid").toUuid(); - uint duration = params.value("duration").toUInt(); - quint16 shortAddress = static_cast(Zigbee::BroadcastAddressAllRouters); - if (params.contains("shortAddress")) { - shortAddress = static_cast(params.value("shortAddress").toUInt()); - } - ZigbeeManager::ZigbeeError error = m_zigbeeManager->setZigbeeNetworkPermitJoin(networkUuid, shortAddress, duration); - QVariantMap returnMap; - returnMap.insert("zigbeeError", enumValueName(error)); - return createReply(returnMap); -} - -JsonReply *ZigbeeHandler::GetNodes(const QVariantMap ¶ms) -{ - QVariantMap returnMap; - QUuid networkUuid = params.value("networkUuid").toUuid(); - ZigbeeNetwork *network = m_zigbeeManager->zigbeeNetworks().value(networkUuid); - if (!network) { - returnMap.insert("zigbeeError", enumValueName(ZigbeeManager::ZigbeeErrorNetworkUuidNotFound)); - return createReply(returnMap); - } - - QVariantList nodeList; - foreach (ZigbeeNode *node, network->nodes()) { - nodeList << packNode(node); - } - - returnMap.insert("zigbeeError", enumValueName(ZigbeeManager::ZigbeeErrorNoError)); - returnMap.insert("zigbeeNodes", nodeList); - return createReply(returnMap); -} - -JsonReply *ZigbeeHandler::RemoveNode(const QVariantMap ¶ms) -{ - QVariantMap returnMap; - QUuid networkUuid = params.value("networkUuid").toUuid(); - ZigbeeAddress nodeAddress(params.value("ieeeAddress").toString()); - ZigbeeNetwork *network = m_zigbeeManager->zigbeeNetworks().value(networkUuid); - if (!network) { - returnMap.insert("zigbeeError", enumValueName(ZigbeeManager::ZigbeeErrorNetworkUuidNotFound)); - return createReply(returnMap); - } - - if (!network->hasNode(nodeAddress)) { - returnMap.insert("zigbeeError", enumValueName(ZigbeeManager::ZigbeeErrorNodeNotFound)); - return createReply(returnMap); - } - - ZigbeeNode *node = network->getZigbeeNode(nodeAddress); - if (node->shortAddress() == 0x0000) { - returnMap.insert("zigbeeError", enumValueName(ZigbeeManager::ZigbeeErrorForbidden)); - return createReply(returnMap); - } - - network->removeZigbeeNode(nodeAddress); - returnMap.insert("zigbeeError", enumValueName(ZigbeeManager::ZigbeeErrorNoError)); - return createReply(returnMap); -} - -JsonReply *ZigbeeHandler::GetNetworks(const QVariantMap ¶ms) -{ - Q_UNUSED(params) - - QVariantMap returnMap; - QVariantList networkList; - foreach (ZigbeeNetwork *network, m_zigbeeManager->zigbeeNetworks().values()) { - networkList.append(packNetwork(network)); - } - returnMap.insert("zigbeeNetworks", networkList); - return createReply(returnMap); -} - -QVariantMap ZigbeeHandler::packNetwork(ZigbeeNetwork *network) -{ - QVariantMap networkMap; - networkMap.insert("networkUuid", network->networkUuid()); - networkMap.insert("enabled", true); // FIXME: set actual value once supported - networkMap.insert("serialPort", network->serialPortName()); - networkMap.insert("baudRate", network->serialBaudrate()); - networkMap.insert("macAddress", network->macAddress().toString()); - networkMap.insert("firmwareVersion", network->firmwareVersion()); - networkMap.insert("panId", network->panId()); - networkMap.insert("channel", network->channel()); - networkMap.insert("channelMask", network->channelMask().toUInt32()); - networkMap.insert("permitJoiningEnabled", network->permitJoiningEnabled()); - networkMap.insert("permitJoiningDuration", network->permitJoiningDuration()); - networkMap.insert("permitJoiningRemaining", network->permitJoiningRemaining()); - - switch (network->backendType()) { - case Zigbee::ZigbeeBackendTypeDeconz: - networkMap.insert("backend", ZigbeeAdapter::backendNames().value(ZigbeeAdapter::ZigbeeBackendTypeDeconz)); - break; - case Zigbee::ZigbeeBackendTypeNxp: - networkMap.insert("backend", ZigbeeAdapter::backendNames().value(ZigbeeAdapter::ZigbeeBackendTypeNxp)); - break; - } - - switch (network->state()) { - case ZigbeeNetwork::StateOffline: - case ZigbeeNetwork::StateStopping: - networkMap.insert("networkState", enumValueName(ZigbeeManager::ZigbeeNetworkStateOffline)); - break; - case ZigbeeNetwork::StateStarting: - networkMap.insert("networkState", enumValueName(ZigbeeManager::ZigbeeNetworkStateStarting)); - break; - case ZigbeeNetwork::StateRunning: - networkMap.insert("networkState", enumValueName(ZigbeeManager::ZigbeeNetworkStateOnline)); - break; - case ZigbeeNetwork::StateUpdating: - networkMap.insert("networkState", enumValueName(ZigbeeManager::ZigbeeNetworkStateUpdating)); - break; - case ZigbeeNetwork::StateUninitialized: - networkMap.insert("networkState", enumValueName(ZigbeeManager::ZigbeeNetworkStateError)); - break; - } - - return networkMap; -} - -QVariantMap ZigbeeHandler::packNode(ZigbeeNode *node) -{ - QVariantMap nodeMap; - nodeMap.insert("networkUuid", node->networkUuid()); - nodeMap.insert("ieeeAddress", node->extendedAddress().toString()); - nodeMap.insert("networkAddress", node->shortAddress()); - switch (node->nodeDescriptor().nodeType) { - case ZigbeeDeviceProfile::NodeTypeCoordinator: - nodeMap.insert("type", enumValueName(ZigbeeManager::ZigbeeNodeTypeCoordinator)); - break; - case ZigbeeDeviceProfile::NodeTypeRouter: - nodeMap.insert("type", enumValueName(ZigbeeManager::ZigbeeNodeTypeRouter)); - break; - default: - nodeMap.insert("type", enumValueName(ZigbeeManager::ZigbeeNodeTypeEndDevice)); - break; - } - - switch (node->state()) { - case ZigbeeNode::StateUninitialized: - nodeMap.insert("state", enumValueName(ZigbeeManager::ZigbeeNodeStateUninitialized)); - break; - case ZigbeeNode::StateInitializing: - nodeMap.insert("state", enumValueName(ZigbeeManager::ZigbeeNodeStateInitializing)); - break; - case ZigbeeNode::StateInitialized: - nodeMap.insert("state", enumValueName(ZigbeeManager::ZigbeeNodeStateInitialized)); - break; - } - - nodeMap.insert("manufacturer", node->manufacturerName()); - nodeMap.insert("model", node->modelName()); - nodeMap.insert("version", node->version()); - nodeMap.insert("receiverOnWhileIdle", node->macCapabilities().receiverOnWhenIdle); - nodeMap.insert("reachable", node->reachable()); - nodeMap.insert("lqi", node->lqi()); - nodeMap.insert("lastSeen", node->lastSeen().toMSecsSinceEpoch() / 1000); - return nodeMap; -} - -void ZigbeeHandler::onNodeJoined(const QUuid &networkUuid, ZigbeeNode *node) -{ - QVariantMap params; - params.insert("networkUuid", networkUuid); - params.insert("zigbeeNode", packNode(node)); - emit NodeAdded(params); -} - -void ZigbeeHandler::onNodeAdded(const QUuid &networkUuid, ZigbeeNode *node) -{ - // Note: we emit the node changed signal here, since the node has been added internally after initialization. - onNodeChanged(networkUuid, node); -} - -void ZigbeeHandler::onNodeChanged(const QUuid &networkUuid, ZigbeeNode *node) -{ - QVariantMap params; - params.insert("networkUuid", networkUuid); - params.insert("zigbeeNode", packNode(node)); - emit NodeChanged(params); -} - -void ZigbeeHandler::onNodeRemoved(const QUuid &networkUuid, ZigbeeNode *node) -{ - QVariantMap params; - params.insert("networkUuid", networkUuid); - params.insert("zigbeeNode", packNode(node)); - emit NodeRemoved(params); -} - -} +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* 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 . +* +* 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 "zigbeehandler.h" +#include "zigbee/zigbeemanager.h" +#include "zigbee/zigbeeadapters.h" +#include "loggingcategories.h" + +#include + +namespace nymeaserver { + +ZigbeeHandler::ZigbeeHandler(ZigbeeManager *zigbeeManager, QObject *parent) : + JsonHandler(parent), + m_zigbeeManager(zigbeeManager) +{ + qRegisterMetaType(); + registerEnum(); + registerEnum(); + registerEnum(); + registerEnum(); + registerObject(); + + // Network object describing a network instance + QVariantMap zigbeeNetworkDescription; + zigbeeNetworkDescription.insert("networkUuid", enumValueName(Uuid)); + zigbeeNetworkDescription.insert("enabled", enumValueName(Bool)); + zigbeeNetworkDescription.insert("serialPort", enumValueName(String)); + zigbeeNetworkDescription.insert("baudRate", enumValueName(Uint)); + zigbeeNetworkDescription.insert("macAddress", enumValueName(String)); + zigbeeNetworkDescription.insert("firmwareVersion", enumValueName(String)); + zigbeeNetworkDescription.insert("panId", enumValueName(Uint)); + zigbeeNetworkDescription.insert("channel", enumValueName(Uint)); + zigbeeNetworkDescription.insert("channelMask", enumValueName(Uint)); + zigbeeNetworkDescription.insert("permitJoiningEnabled", enumValueName(Bool)); + zigbeeNetworkDescription.insert("permitJoiningDuration", enumValueName(Uint)); + zigbeeNetworkDescription.insert("permitJoiningRemaining", enumValueName(Uint)); + zigbeeNetworkDescription.insert("backend", enumValueName(String)); + zigbeeNetworkDescription.insert("networkState", enumRef()); + registerObject("ZigbeeNetwork", zigbeeNetworkDescription); + + // Zigbee node description + QVariantMap zigbeeNodeDescription; + zigbeeNodeDescription.insert("networkUuid", enumValueName(Uuid)); + zigbeeNodeDescription.insert("ieeeAddress", enumValueName(String)); + zigbeeNodeDescription.insert("networkAddress", enumValueName(Uint)); + zigbeeNodeDescription.insert("type", enumRef()); + zigbeeNodeDescription.insert("state", enumRef()); + zigbeeNodeDescription.insert("manufacturer", enumValueName(String)); + zigbeeNodeDescription.insert("model", enumValueName(String)); + zigbeeNodeDescription.insert("version", enumValueName(String)); + zigbeeNodeDescription.insert("receiverOnWhileIdle", enumValueName(Bool)); + zigbeeNodeDescription.insert("reachable", enumValueName(Bool)); + zigbeeNodeDescription.insert("lqi", enumValueName(Uint)); + zigbeeNodeDescription.insert("lastSeen", enumValueName(Uint)); + registerObject("ZigbeeNode", zigbeeNodeDescription); + + QVariantMap params, returns; + QString description; + + // GetAvailableBackends + params.clear(); returns.clear(); + description = "Get the list of available ZigBee backends."; + returns.insert("backends", QVariantList() << enumValueName(String)); + registerMethod("GetAvailableBackends", description, params, returns); + + // GetAdapters + params.clear(); returns.clear(); + description = "Get the list of available ZigBee adapters and serial ports in order to set up the ZigBee network " + "on the desired interface. The \'serialPort\' property can be used as unique identifier for an adapter. " + "If an adapter hardware has been recognized as a well known ZigBee adapter, " + "the \'hardwareRecognized\' property will be true and the \'baudRate\' and \'backend\' " + "configurations can be used as they where given, otherwise the user might set the backend " + "and baud rate manually. The available backends can be fetched using the GetAvailableBackends method."; + returns.insert("adapters", objectRef()); + registerMethod("GetAdapters", description, params, returns); + + // AdapterAdded notification + params.clear(); + description = "Emitted whenever a new ZigBee adapter or serial port has been detected in the system."; + params.insert("adapter", objectRef()); + registerNotification("AdapterAdded", description, params); + + // AdapterRemoved notification + params.clear(); + description = "Emitted whenever a ZigBee adapter or serial port has been removed from the system (i.e. unplugged)."; + params.insert("adapter", objectRef()); + registerNotification("AdapterRemoved", description, params); + + // GetNetworks + params.clear(); returns.clear(); + description = "Returns the list of configured ZigBee networks in the system."; + returns.insert("zigbeeNetworks", QVariantList() << objectRef("ZigbeeNetwork")); + registerMethod("GetNetworks", description, params, returns); + + // AddNetwork + params.clear(); returns.clear(); + description = "Create a new ZigBee network for the given \'serialPort\', \'baudRate\' and \'backend\'. " + "The serial ports can be fetched from the available adapters. See \'GetAdapters\' for more information. " + "The available backends can be fetched using the \'GetAvailableBackends\' method."; + params.insert("serialPort", enumValueName(String)); + params.insert("baudRate", enumValueName(Uint)); + params.insert("backend", enumValueName(String)); + params.insert("o:channelMask", enumValueName(Uint)); + returns.insert("zigbeeError", enumRef()); + returns.insert("o:networkUuid", enumValueName(Uuid)); + registerMethod("AddNetwork", description, params, returns); + + // RemoveNetwork + params.clear(); returns.clear(); + description = "Remove the ZigBee network with the given network uuid."; + params.insert("networkUuid", enumValueName(Uuid)); + returns.insert("zigbeeError", enumRef()); + registerMethod("RemoveNetwork", description, params, returns); + + // NetworkAdded notification + params.clear(); + description = "Emitted whenever a new ZigBee network has been added."; + params.insert("zigbeeNetwork", objectRef("ZigbeeNetwork")); + registerNotification("NetworkAdded", description, params); + + // NetworkRemoved notification + params.clear(); + description = "Emitted whenever a new ZigBee network has been removed."; + params.insert("networkUuid", enumValueName(Uuid)); + registerNotification("NetworkRemoved", description, params); + + // NetworkChanged notification + params.clear(); + description = "Emitted whenever a new ZigBee network has changed."; + params.insert("zigbeeNetwork", objectRef("ZigbeeNetwork")); + registerNotification("NetworkChanged", description, params); + + // FactoryResetNetwork + params.clear(); returns.clear(); + description = "Factory reset the network with the given \'networkUuid\'. The network does not have " + "to be online for this procedure, and all associated nodes and things will be removed permanently."; + params.insert("networkUuid", enumValueName(Uuid)); + returns.insert("zigbeeError", enumRef()); + registerMethod("FactoryResetNetwork", description, params, returns); + + // SetPermitJoin + params.clear(); returns.clear(); + description = "Allow or deny nodes to join the network with the given \'networkUuid\' for a specific \'duration\' in seconds. " + "The duration value has to be between 0 and 255 seconds. The \'permitJoinDuration\' property of ZigBee network " + "object indicates how long permit has been enabled and the \'permitJoiningRemaining\' indicates the rest of the time. " + "Those values can be used to show a countdown or progressbar. This method can be recalled for resetting the timeout. " + "If the duration is set to 0 seconds, joining will be disabled immediatly for the entire network. " + "The \'shortAddress\' is optional and defaults to the broadcast address 0xfffc for all routers in the network. " + "If the short address matches the address of a router node in the network, only that specific router will " + "be able to allow new nodes to join the network. A new node will join to the router with the best link quality index (LQI)."; + params.insert("networkUuid", enumValueName(Uuid)); + params.insert("duration", enumValueName(Uint)); + params.insert("o:shortAddress", enumValueName(Uint)); + returns.insert("zigbeeError", enumRef()); + registerMethod("SetPermitJoin", description, params, returns); + + // GetNodes + params.clear(); returns.clear(); + description = "Returns the list of ZigBee nodes from the network the given \'networkUuid\' in the system."; + params.insert("networkUuid", enumValueName(Uuid)); + returns.insert("zigbeeError", enumRef()); + returns.insert("o:zigbeeNodes", QVariantList() << objectRef("ZigbeeNode")); + registerMethod("GetNodes", description, params, returns); + + // RemoveNode + params.clear(); returns.clear(); + description = "Remove a ZigBee node with the given \'ieeeAddress\' from the network with the given \'networkUuid\'. " + "If there is a thing configured for this node, also the thing will be removed from the system. " + "The coordinator node cannot be removed."; + params.insert("networkUuid", enumValueName(Uuid)); + params.insert("ieeeAddress", enumValueName(String)); + returns.insert("zigbeeError", enumRef()); + registerMethod("RemoveNode", description, params, returns); + + // NodeAdded + params.clear(); + description = "Emitted whenever a new ZigBee node has joined the network with the given \'networkUuid\'."; + params.insert("networkUuid", enumValueName(Uuid)); + params.insert("zigbeeNode", objectRef("ZigbeeNode")); + registerNotification("NodeAdded", description, params); + + // NodeRemoved + params.clear(); + description = "Emitted whenever a ZigBee node has removed from the network with the given \'networkUuid\'."; + params.insert("networkUuid", enumValueName(Uuid)); + params.insert("zigbeeNode", objectRef("ZigbeeNode")); + registerNotification("NodeRemoved", description, params); + + // NodeChanged + params.clear(); + description = "Emitted whenever a ZigBee node has changed."; + params.insert("networkUuid", enumValueName(Uuid)); + params.insert("zigbeeNode", objectRef("ZigbeeNode")); + registerNotification("NodeChanged", description, params); + + connect(m_zigbeeManager, &ZigbeeManager::availableAdapterAdded, this, [this](const ZigbeeAdapter &adapter){ + QVariantMap params; + params.insert("adapter", pack(adapter)); + emit AdapterAdded(params); + }); + + connect(m_zigbeeManager, &ZigbeeManager::availableAdapterRemoved, this, [this](const ZigbeeAdapter &adapter){ + QVariantMap params; + params.insert("adapter", pack(adapter)); + emit AdapterRemoved(params); + }); + + connect(m_zigbeeManager, &ZigbeeManager::zigbeeNetworkAdded, this, [this](ZigbeeNetwork *network){ + QVariantMap params; + params.insert("zigbeeNetwork", packNetwork(network)); + emit NetworkAdded(params); + }); + + connect(m_zigbeeManager, &ZigbeeManager::zigbeeNetworkChanged, this, [this](ZigbeeNetwork *network){ + QVariantMap params; + params.insert("zigbeeNetwork", packNetwork(network)); + emit NetworkChanged(params); + }); + + connect(m_zigbeeManager, &ZigbeeManager::zigbeeNetworkRemoved, this, [this](const QUuid &networkUuid){ + QVariantMap params; + params.insert("networkUuid", networkUuid); + emit NetworkRemoved(params); + }); + + connect(m_zigbeeManager, &ZigbeeManager::nodeJoined, this, &ZigbeeHandler::onNodeJoined); + connect(m_zigbeeManager, &ZigbeeManager::nodeAdded, this, &ZigbeeHandler::onNodeAdded); + connect(m_zigbeeManager, &ZigbeeManager::nodeChanged, this, &ZigbeeHandler::onNodeChanged); + connect(m_zigbeeManager, &ZigbeeManager::nodeRemoved, this, &ZigbeeHandler::onNodeRemoved); + +} + +QString ZigbeeHandler::name() const +{ + return "Zigbee"; +} + +JsonReply *ZigbeeHandler::GetAvailableBackends(const QVariantMap ¶ms) +{ + Q_UNUSED(params) + + QVariantMap returnMap; + QVariantList backendsList; + foreach (const QString &backendName, ZigbeeAdapter::backendNames().values()) { + backendsList << backendName; + } + returnMap.insert("backends", backendsList); + return createReply(returnMap); +} + +JsonReply *ZigbeeHandler::GetAdapters(const QVariantMap ¶ms) +{ + Q_UNUSED(params) + + QVariantMap returnMap; + QVariantList adapterList; + foreach (const ZigbeeAdapter &adapter, m_zigbeeManager->availableAdapters()) { + adapterList << pack(adapter); + } + returnMap.insert("adapters", adapterList); + return createReply(returnMap); +} + +JsonReply *ZigbeeHandler::AddNetwork(const QVariantMap ¶ms) +{ + QVariantMap returnMap; + + QString serialPort = params.value("serialPort").toString(); + uint baudRate = params.value("baudRate").toUInt(); + QString backendString = params.value("backend").toString(); + if (!ZigbeeAdapter::backendNames().values().contains(backendString)) { + returnMap.insert("zigbeeError", enumValueName(ZigbeeManager::ZigbeeErrorUnknownBackend)); + return createReply(returnMap); + } + + ZigbeeChannelMask channelMask = ZigbeeChannelMask::ChannelConfigurationAllChannels; + if (params.contains("channelMask")) { + channelMask = params.value("channelMask").toUInt() & ZigbeeChannelMask::ChannelConfigurationAllChannels; + if (channelMask == ZigbeeChannelMask::ChannelConfigurationNoChannel) { + returnMap.insert("zigbeeError", enumValueName(ZigbeeManager::ZigbeeErrorInvalidChannel)); + return createReply(returnMap); + } + } + + QPair result = m_zigbeeManager->createZigbeeNetwork(serialPort, baudRate, ZigbeeAdapter::backendNames().key(backendString), channelMask); + if (result.first == ZigbeeManager::ZigbeeErrorNoError) { + returnMap.insert("networkUuid", result.second); + } + returnMap.insert("zigbeeError", enumValueName(result.first)); + return createReply(returnMap); +} + +JsonReply *ZigbeeHandler::RemoveNetwork(const QVariantMap ¶ms) +{ + QUuid networkUuid = params.value("networkUuid").toUuid(); + ZigbeeManager::ZigbeeError error = m_zigbeeManager->removeZigbeeNetwork(networkUuid); + QVariantMap returnMap; + returnMap.insert("zigbeeError", enumValueName(error)); + return createReply(returnMap); +} + +JsonReply *ZigbeeHandler::FactoryResetNetwork(const QVariantMap ¶ms) +{ + QUuid networkUuid = params.value("networkUuid").toUuid(); + ZigbeeManager::ZigbeeError error = m_zigbeeManager->factoryResetNetwork(networkUuid); + QVariantMap returnMap; + returnMap.insert("zigbeeError", enumValueName(error)); + return createReply(returnMap); +} + +JsonReply *ZigbeeHandler::SetPermitJoin(const QVariantMap ¶ms) +{ + QUuid networkUuid = params.value("networkUuid").toUuid(); + uint duration = params.value("duration").toUInt(); + quint16 shortAddress = static_cast(Zigbee::BroadcastAddressAllRouters); + if (params.contains("shortAddress")) { + shortAddress = static_cast(params.value("shortAddress").toUInt()); + } + ZigbeeManager::ZigbeeError error = m_zigbeeManager->setZigbeeNetworkPermitJoin(networkUuid, shortAddress, duration); + QVariantMap returnMap; + returnMap.insert("zigbeeError", enumValueName(error)); + return createReply(returnMap); +} + +JsonReply *ZigbeeHandler::GetNodes(const QVariantMap ¶ms) +{ + QVariantMap returnMap; + QUuid networkUuid = params.value("networkUuid").toUuid(); + ZigbeeNetwork *network = m_zigbeeManager->zigbeeNetworks().value(networkUuid); + if (!network) { + returnMap.insert("zigbeeError", enumValueName(ZigbeeManager::ZigbeeErrorNetworkUuidNotFound)); + return createReply(returnMap); + } + + QVariantList nodeList; + foreach (ZigbeeNode *node, network->nodes()) { + nodeList << packNode(node); + } + + returnMap.insert("zigbeeError", enumValueName(ZigbeeManager::ZigbeeErrorNoError)); + returnMap.insert("zigbeeNodes", nodeList); + return createReply(returnMap); +} + +JsonReply *ZigbeeHandler::RemoveNode(const QVariantMap ¶ms) +{ + QVariantMap returnMap; + QUuid networkUuid = params.value("networkUuid").toUuid(); + ZigbeeAddress nodeAddress(params.value("ieeeAddress").toString()); + ZigbeeNetwork *network = m_zigbeeManager->zigbeeNetworks().value(networkUuid); + if (!network) { + returnMap.insert("zigbeeError", enumValueName(ZigbeeManager::ZigbeeErrorNetworkUuidNotFound)); + return createReply(returnMap); + } + + if (!network->hasNode(nodeAddress)) { + returnMap.insert("zigbeeError", enumValueName(ZigbeeManager::ZigbeeErrorNodeNotFound)); + return createReply(returnMap); + } + + ZigbeeNode *node = network->getZigbeeNode(nodeAddress); + if (node->shortAddress() == 0x0000) { + returnMap.insert("zigbeeError", enumValueName(ZigbeeManager::ZigbeeErrorForbidden)); + return createReply(returnMap); + } + + network->removeZigbeeNode(nodeAddress); + returnMap.insert("zigbeeError", enumValueName(ZigbeeManager::ZigbeeErrorNoError)); + return createReply(returnMap); +} + +JsonReply *ZigbeeHandler::GetNetworks(const QVariantMap ¶ms) +{ + Q_UNUSED(params) + + QVariantMap returnMap; + QVariantList networkList; + foreach (ZigbeeNetwork *network, m_zigbeeManager->zigbeeNetworks().values()) { + networkList.append(packNetwork(network)); + } + returnMap.insert("zigbeeNetworks", networkList); + return createReply(returnMap); +} + +QVariantMap ZigbeeHandler::packNetwork(ZigbeeNetwork *network) +{ + QVariantMap networkMap; + networkMap.insert("networkUuid", network->networkUuid()); + networkMap.insert("enabled", true); // FIXME: set actual value once supported + networkMap.insert("serialPort", network->serialPortName()); + networkMap.insert("baudRate", network->serialBaudrate()); + networkMap.insert("macAddress", network->macAddress().toString()); + networkMap.insert("firmwareVersion", network->firmwareVersion()); + networkMap.insert("panId", network->panId()); + networkMap.insert("channel", network->channel()); + networkMap.insert("channelMask", network->channelMask().toUInt32()); + networkMap.insert("permitJoiningEnabled", network->permitJoiningEnabled()); + networkMap.insert("permitJoiningDuration", network->permitJoiningDuration()); + networkMap.insert("permitJoiningRemaining", network->permitJoiningRemaining()); + + switch (network->backendType()) { + case Zigbee::ZigbeeBackendTypeDeconz: + networkMap.insert("backend", ZigbeeAdapter::backendNames().value(ZigbeeAdapter::ZigbeeBackendTypeDeconz)); + break; + case Zigbee::ZigbeeBackendTypeNxp: + networkMap.insert("backend", ZigbeeAdapter::backendNames().value(ZigbeeAdapter::ZigbeeBackendTypeNxp)); + break; + } + + switch (network->state()) { + case ZigbeeNetwork::StateOffline: + case ZigbeeNetwork::StateStopping: + networkMap.insert("networkState", enumValueName(ZigbeeManager::ZigbeeNetworkStateOffline)); + break; + case ZigbeeNetwork::StateStarting: + networkMap.insert("networkState", enumValueName(ZigbeeManager::ZigbeeNetworkStateStarting)); + break; + case ZigbeeNetwork::StateRunning: + networkMap.insert("networkState", enumValueName(ZigbeeManager::ZigbeeNetworkStateOnline)); + break; + case ZigbeeNetwork::StateUpdating: + networkMap.insert("networkState", enumValueName(ZigbeeManager::ZigbeeNetworkStateUpdating)); + break; + case ZigbeeNetwork::StateUninitialized: + networkMap.insert("networkState", enumValueName(ZigbeeManager::ZigbeeNetworkStateError)); + break; + } + + return networkMap; +} + +QVariantMap ZigbeeHandler::packNode(ZigbeeNode *node) +{ + QVariantMap nodeMap; + nodeMap.insert("networkUuid", node->networkUuid()); + nodeMap.insert("ieeeAddress", node->extendedAddress().toString()); + nodeMap.insert("networkAddress", node->shortAddress()); + switch (node->nodeDescriptor().nodeType) { + case ZigbeeDeviceProfile::NodeTypeCoordinator: + nodeMap.insert("type", enumValueName(ZigbeeManager::ZigbeeNodeTypeCoordinator)); + break; + case ZigbeeDeviceProfile::NodeTypeRouter: + nodeMap.insert("type", enumValueName(ZigbeeManager::ZigbeeNodeTypeRouter)); + break; + default: + nodeMap.insert("type", enumValueName(ZigbeeManager::ZigbeeNodeTypeEndDevice)); + break; + } + + switch (node->state()) { + case ZigbeeNode::StateUninitialized: + nodeMap.insert("state", enumValueName(ZigbeeManager::ZigbeeNodeStateUninitialized)); + break; + case ZigbeeNode::StateInitializing: + nodeMap.insert("state", enumValueName(ZigbeeManager::ZigbeeNodeStateInitializing)); + break; + case ZigbeeNode::StateInitialized: + nodeMap.insert("state", enumValueName(ZigbeeManager::ZigbeeNodeStateInitialized)); + break; + } + + nodeMap.insert("manufacturer", node->manufacturerName()); + nodeMap.insert("model", node->modelName()); + nodeMap.insert("version", node->version()); + nodeMap.insert("receiverOnWhileIdle", node->macCapabilities().receiverOnWhenIdle); + nodeMap.insert("reachable", node->reachable()); + nodeMap.insert("lqi", node->lqi()); + nodeMap.insert("lastSeen", node->lastSeen().toMSecsSinceEpoch() / 1000); + return nodeMap; +} + +void ZigbeeHandler::onNodeJoined(const QUuid &networkUuid, ZigbeeNode *node) +{ + QVariantMap params; + params.insert("networkUuid", networkUuid); + params.insert("zigbeeNode", packNode(node)); + emit NodeAdded(params); +} + +void ZigbeeHandler::onNodeAdded(const QUuid &networkUuid, ZigbeeNode *node) +{ + // Note: we emit the node changed signal here, since the node has been added internally after initialization. + onNodeChanged(networkUuid, node); +} + +void ZigbeeHandler::onNodeChanged(const QUuid &networkUuid, ZigbeeNode *node) +{ + QVariantMap params; + params.insert("networkUuid", networkUuid); + params.insert("zigbeeNode", packNode(node)); + emit NodeChanged(params); +} + +void ZigbeeHandler::onNodeRemoved(const QUuid &networkUuid, ZigbeeNode *node) +{ + QVariantMap params; + params.insert("networkUuid", networkUuid); + params.insert("zigbeeNode", packNode(node)); + emit NodeRemoved(params); +} + +}