diff --git a/debian/control b/debian/control
index 5f96866a..815fb606 100644
--- a/debian/control
+++ b/debian/control
@@ -12,6 +12,7 @@ Build-Depends: debhelper (>= 9.0.0),
libnymea-mqtt-dev (>= 0.1.2),
libnymea-networkmanager-dev (>= 0.4.0),
libnymea-remoteproxyclient-dev,
+ libnymea-zigbee-dev (>= 0.1.0),
libpython3-dev,
libqt5websockets5-dev,
libqt5bluetooth5,
diff --git a/libnymea-core/hardware/zigbee/zigbeehardwareresourceimplementation.cpp b/libnymea-core/hardware/zigbee/zigbeehardwareresourceimplementation.cpp
new file mode 100644
index 00000000..80a54cd5
--- /dev/null
+++ b/libnymea-core/hardware/zigbee/zigbeehardwareresourceimplementation.cpp
@@ -0,0 +1,255 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 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 .
+*
+* 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 "zigbeehardwareresourceimplementation.h"
+#include "loggingcategories.h"
+#include "nymeasettings.h"
+#include "hardware/zigbee/zigbeehandler.h"
+
+#include
+#include
+
+#include
+
+NYMEA_LOGGING_CATEGORY(dcZigbeeResource, "ZigbeeResource")
+
+namespace nymeaserver {
+
+ZigbeeHardwareResourceImplementation::ZigbeeHardwareResourceImplementation(ZigbeeManager *zigbeeManager, QObject *parent) :
+ ZigbeeHardwareResource(parent),
+ m_zigbeeManager(zigbeeManager)
+{
+ connect(m_zigbeeManager, &ZigbeeManager::zigbeeNetworkChanged, this, &ZigbeeHardwareResourceImplementation::onZigbeeNetworkChanged);
+ connect(m_zigbeeManager, &ZigbeeManager::nodeAdded, this, &ZigbeeHardwareResourceImplementation::onZigbeeNodeAdded);
+ connect(m_zigbeeManager, &ZigbeeManager::nodeRemoved, this, &ZigbeeHardwareResourceImplementation::onZigbeeNodeRemoved);
+ connect(m_zigbeeManager, &ZigbeeManager::availableChanged, this, &ZigbeeHardwareResourceImplementation::onZigbeeAvailableChanged);
+}
+
+bool ZigbeeHardwareResourceImplementation::available() const
+{
+ return m_zigbeeManager->available();
+}
+
+bool ZigbeeHardwareResourceImplementation::enabled() const
+{
+ return m_enabled;
+}
+
+
+void ZigbeeHardwareResourceImplementation::registerHandler(ZigbeeHandler *handler, HandlerType type)
+{
+ qCDebug(dcZigbeeResource()) << "Registering new zigbee handler" << handler->name() << "with type" << type;
+ m_handlers.insert(type, handler);
+}
+
+ZigbeeNode *ZigbeeHardwareResourceImplementation::claimNode(ZigbeeHandler *handler, const QUuid &networkUuid, const ZigbeeAddress &extendedAddress)
+{
+ if (!m_handlers.values().contains(handler)) {
+ qCWarning(dcZigbeeResource()) << "Handler" << handler->name() << "is not registered. Not allowing node to be claimed.";
+ return nullptr;
+ }
+
+ ZigbeeNetwork *network = m_zigbeeManager->zigbeeNetworks().value(networkUuid);
+ if (!network) {
+ qCWarning(dcZigbeeResource()) << "Network" << networkUuid << "not found.";
+ return nullptr;
+ }
+
+ ZigbeeNode *node = network->getZigbeeNode(extendedAddress);
+ if (!node) {
+ qCWarning(dcZigbeeResource()) << "Node with address" << extendedAddress << "not found in Zigbee network" << networkUuid.toString();
+ return nullptr;
+ }
+
+ if (m_nodeHandlers.contains(node) && m_nodeHandlers.value(node) != handler) {
+ qCWarning(dcZigbeeResource()) << "Node with address" << extendedAddress << "is already claimed by another handler (" << m_nodeHandlers.value(node)->name() << "). Not allowing node to be reclaimed.";
+ return nullptr;
+ }
+
+ m_nodeHandlers[node] = handler;
+ return node;
+}
+
+void ZigbeeHardwareResourceImplementation::removeNodeFromNetwork(const QUuid &networkUuid, ZigbeeNode *node)
+{
+ ZigbeeNetwork *network = m_zigbeeManager->zigbeeNetworks().value(networkUuid);
+ if (!network) {
+ qCWarning(dcZigbeeResource()) << "Can not remove note from network" << networkUuid << "because there is no network with this uuid.";
+ return;
+ }
+
+ network->removeZigbeeNode(node->extendedAddress());
+}
+
+ZigbeeNetwork::State ZigbeeHardwareResourceImplementation::networkState(const QUuid &networkUuid)
+{
+ ZigbeeNetwork *network = m_zigbeeManager->zigbeeNetworks().value(networkUuid);
+ if (!network) {
+ qCWarning(dcZigbeeResource()) << "Network" << networkUuid << "not found.";
+ return ZigbeeNetwork::StateUninitialized;
+ }
+ return network->state();
+}
+
+ZigbeeAddress ZigbeeHardwareResourceImplementation::coordinatorAddress(const QUuid &networkUuid)
+{
+ ZigbeeNetwork *network = m_zigbeeManager->zigbeeNetworks().value(networkUuid);
+ if (!network) {
+ qCWarning(dcZigbeeResource()) << "Network" << networkUuid << "not found.";
+ return ZigbeeAddress();
+ }
+ return network->coordinatorNode()->extendedAddress();
+}
+
+void ZigbeeHardwareResourceImplementation::setEnabled(bool enabled)
+{
+ qCDebug(dcZigbeeResource()) << "Set" << (enabled ? "enabled" : "disabled");
+ if (m_enabled && enabled) {
+ qCDebug(dcZigbeeResource()) << "Already enabled.";
+ return;
+ } else if (!m_enabled && !enabled) {
+ qCDebug(dcZigbeeResource()) << "Already disabled.";
+ return;
+ }
+
+ bool success = false;
+ if (enabled) {
+ success = enable();
+ } else {
+ success = disable();
+ }
+
+ if (success) {
+ m_enabled = enabled;
+ emit enabledChanged(m_enabled);
+ }
+}
+
+bool ZigbeeHardwareResourceImplementation::enable()
+{
+ qCDebug(dcZigbeeResource()) << "Enable hardware resource. Not implemented yet.";
+
+ // TODO: enable all networks in the zigbee manager
+
+ return true;
+}
+
+bool ZigbeeHardwareResourceImplementation::disable()
+{
+ qCDebug(dcZigbeeResource()) << "Disable hardware resource. Not implemented yet.";
+
+ // TODO: disable all networks in the zigbee manager
+
+ return true;
+}
+
+void ZigbeeHardwareResourceImplementation::thingsLoaded()
+{
+ m_thingsLoaded = true;
+
+ // We can assume here that all handled nodes have been claimed by plugins
+ // In case we started up and loaded new zigbee plugins, let's try to get all previously joined nodes handled now...
+ foreach (ZigbeeNetwork *network, m_zigbeeManager->zigbeeNetworks()) {
+ if (network->state() == ZigbeeNetwork::StateRunning) {
+ foreach (ZigbeeNode *node, network->nodes()) {
+ // Ignore the coordinator node
+ if (node->shortAddress() == 0x0000)
+ continue;
+
+ if (!m_nodeHandlers.contains(node)) {
+ qCDebug(dcZigbeeResource()) << "Node" << node << "is not yet handled by any plugin. Trying to find a suitable plugin.";
+ onZigbeeNodeAdded(network->networkUuid(), node);
+ }
+ }
+ }
+ }
+}
+
+void ZigbeeHardwareResourceImplementation::onZigbeeAvailableChanged(bool available)
+{
+ if (available) {
+ qCDebug(dcZigbeeResource()) << "Zigbee is now available";
+ } else {
+ qCWarning(dcZigbeeResource()) << "Zigbee is not available any more";
+ }
+
+ emit availableChanged(available);
+}
+
+void ZigbeeHardwareResourceImplementation::onZigbeeNetworkChanged(ZigbeeNetwork *network)
+{
+ emit networkStateChanged(network->networkUuid(), network->state());
+
+ // If the network is now ready and things have been loaded already, check if there are
+ // unclaimed nodes that might be handled now. This might happen if a node joins the network
+ // but no appropriate plugin had been installed at the time. If additional plugins have
+ // been installed now, such nodes might be handled by them now.
+ if (network->state() == ZigbeeNetwork::StateRunning && m_thingsLoaded) {
+ foreach (ZigbeeNode *node, network->nodes()) {
+ // Ignore the coordinator node
+ if (node->shortAddress() == 0x0000)
+ continue;
+
+ if (!m_nodeHandlers.contains(node)) {
+ onZigbeeNodeAdded(network->networkUuid(), node);
+ }
+ }
+ }
+}
+
+void ZigbeeHardwareResourceImplementation::onZigbeeNodeAdded(const QUuid &networkUuid, ZigbeeNode *node)
+{
+ qCDebug(dcZigbeeResource()) << node << "joined the network" << m_zigbeeManager->zigbeeNetworks().value(networkUuid);
+ ZigbeeHandler *handler = nullptr;
+ foreach (ZigbeeHandler *tmp, m_handlers) {
+ if (tmp->handleNode(node, networkUuid)) {
+ handler = tmp;
+ m_nodeHandlers.insert(node, handler);
+ qCDebug(dcZigbeeResource()) << "Node" << node << "taken by handler" << handler->name();
+ break;
+ }
+ }
+ if (!handler) {
+ qCWarning(dcZigbeeResource()) << "No zigbee handler available to handle node" << node;
+ return;
+ }
+}
+
+void ZigbeeHardwareResourceImplementation::onZigbeeNodeRemoved(const QUuid &networkUuid, ZigbeeNode *node)
+{
+ qCDebug(dcZigbeeResource()) << node << "left the network" << m_zigbeeManager->zigbeeNetworks().value(networkUuid);
+
+ ZigbeeHandler *handler = m_nodeHandlers.value(node);
+ if (handler) {
+ handler->handleRemoveNode(node, networkUuid);
+ }
+}
+
+}
diff --git a/libnymea-core/hardware/zigbee/zigbeehardwareresourceimplementation.h b/libnymea-core/hardware/zigbee/zigbeehardwareresourceimplementation.h
new file mode 100644
index 00000000..d14a093c
--- /dev/null
+++ b/libnymea-core/hardware/zigbee/zigbeehardwareresourceimplementation.h
@@ -0,0 +1,88 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 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 .
+*
+* 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
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#ifndef ZIGBEEHARDWARERESOURCEIMPLEMENTATION_H
+#define ZIGBEEHARDWARERESOURCEIMPLEMENTATION_H
+
+#include
+
+#include "zigbee/zigbeemanager.h"
+#include "hardware/zigbee/zigbeehardwareresource.h"
+
+namespace nymeaserver {
+
+class ZigbeeHardwareResourceImplementation : public ZigbeeHardwareResource
+{
+ Q_OBJECT
+
+public:
+ explicit ZigbeeHardwareResourceImplementation(ZigbeeManager *zigbeeManager, QObject *parent = nullptr);
+
+ bool available() const override;
+ bool enabled() const override;
+
+ void registerHandler(ZigbeeHandler *handler, HandlerType type = HandlerTypeVendor) override;
+
+ ZigbeeNode* claimNode(ZigbeeHandler *handler, const QUuid &networkUuid, const ZigbeeAddress &extendedAddress) override;
+ void removeNodeFromNetwork(const QUuid &networkUuid, ZigbeeNode *node) override;
+
+ ZigbeeNetwork::State networkState(const QUuid &networkUuid) override;
+ ZigbeeAddress coordinatorAddress(const QUuid &networkUuid) override;
+
+public slots:
+ bool enable();
+ bool disable();
+
+ void thingsLoaded();
+
+protected:
+ void setEnabled(bool enabled) override;
+
+private slots:
+ void onZigbeeAvailableChanged(bool available);
+ void onZigbeeNetworkChanged(ZigbeeNetwork *network);
+ void onZigbeeNodeAdded(const QUuid &networkUuid, ZigbeeNode *node);
+ void onZigbeeNodeRemoved(const QUuid &networkUuid, ZigbeeNode *node);
+
+private:
+ bool m_available = false;
+ bool m_enabled = false;
+ ZigbeeManager *m_zigbeeManager = nullptr;
+
+ QMultiMap m_handlers;
+
+ bool m_thingsLoaded = false;
+ QHash m_nodeHandlers;
+
+};
+
+}
+
+#endif // ZIGBEEHARDWARERESOURCEIMPLEMENTATION_H
diff --git a/libnymea-core/hardwaremanagerimplementation.cpp b/libnymea-core/hardwaremanagerimplementation.cpp
index 7e057cf0..8d19558c 100644
--- a/libnymea-core/hardwaremanagerimplementation.cpp
+++ b/libnymea-core/hardwaremanagerimplementation.cpp
@@ -42,10 +42,11 @@
#include "hardware/bluetoothlowenergy/bluetoothlowenergymanagerimplementation.h"
#include "hardware/network/mqtt/mqttproviderimplementation.h"
#include "hardware/i2c/i2cmanagerimplementation.h"
+#include "hardware/zigbee/zigbeehardwareresourceimplementation.h"
namespace nymeaserver {
-HardwareManagerImplementation::HardwareManagerImplementation(Platform *platform, MqttBroker *mqttBroker, QObject *parent) :
+HardwareManagerImplementation::HardwareManagerImplementation(Platform *platform, MqttBroker *mqttBroker, ZigbeeManager *zigbeeManager, QObject *parent) :
HardwareManager(parent),
m_platform(platform)
{
@@ -70,8 +71,7 @@ HardwareManagerImplementation::HardwareManagerImplementation(Platform *platform,
m_i2cManager = new I2CManagerImplementation(this);
- qCDebug(dcHardware()) << "Hardware manager initialized successfully";
-
+ m_zigbeeResource = new ZigbeeHardwareResourceImplementation(zigbeeManager, this);
// Enable all the resources
setResourceEnabled(m_pluginTimerManager, true);
@@ -90,6 +90,7 @@ HardwareManagerImplementation::HardwareManagerImplementation(Platform *platform,
setResourceEnabled(m_bluetoothLowEnergyManager, true);
m_mqttProvider = new MqttProviderImplementation(mqttBroker, this);
+ qCDebug(dcHardware()) << "Hardware manager initialized successfully";
}
HardwareManagerImplementation::~HardwareManagerImplementation()
@@ -136,4 +137,14 @@ I2CManager *HardwareManagerImplementation::i2cManager()
return m_i2cManager;
}
+ZigbeeHardwareResource *HardwareManagerImplementation::zigbeeResource()
+{
+ return m_zigbeeResource;
+}
+
+void HardwareManagerImplementation::thingsLoaded()
+{
+ m_zigbeeResource->thingsLoaded();
+}
+
}
diff --git a/libnymea-core/hardwaremanagerimplementation.h b/libnymea-core/hardwaremanagerimplementation.h
index 0c5899eb..8a67e54e 100644
--- a/libnymea-core/hardwaremanagerimplementation.h
+++ b/libnymea-core/hardwaremanagerimplementation.h
@@ -41,13 +41,15 @@ namespace nymeaserver {
class Platform;
class MqttBroker;
+class ZigbeeManager;
+class ZigbeeHardwareResourceImplementation;
class HardwareManagerImplementation : public HardwareManager
{
Q_OBJECT
public:
- explicit HardwareManagerImplementation(Platform *platform, MqttBroker *mqttBroker, QObject *parent = nullptr);
+ explicit HardwareManagerImplementation(Platform *platform, MqttBroker *mqttBroker, ZigbeeManager *zigbeeManager, QObject *parent = nullptr);
~HardwareManagerImplementation() override;
Radio433 *radio433() override;
@@ -57,7 +59,11 @@ public:
PlatformZeroConfController *zeroConfController() override;
BluetoothLowEnergyManager *bluetoothLowEnergyManager() override;
MqttProvider *mqttProvider() override;
- I2CManager * i2cManager() override;
+ I2CManager *i2cManager() override;
+ ZigbeeHardwareResource *zigbeeResource() override;
+
+public slots:
+ void thingsLoaded();
private:
QNetworkAccessManager *m_networkAccessManager = nullptr;
@@ -72,6 +78,7 @@ private:
BluetoothLowEnergyManager *m_bluetoothLowEnergyManager = nullptr;
MqttProvider *m_mqttProvider = nullptr;
I2CManager *m_i2cManager = nullptr;
+ ZigbeeHardwareResourceImplementation *m_zigbeeResource = nullptr;
};
}
diff --git a/libnymea-core/jsonrpc/jsonrpcserverimplementation.cpp b/libnymea-core/jsonrpc/jsonrpcserverimplementation.cpp
index 33671c20..b613f9ab 100644
--- a/libnymea-core/jsonrpc/jsonrpcserverimplementation.cpp
+++ b/libnymea-core/jsonrpc/jsonrpcserverimplementation.cpp
@@ -73,6 +73,7 @@
#include "tagshandler.h"
#include "systemhandler.h"
#include "usershandler.h"
+#include "zigbeehandler.h"
#include
#include
@@ -595,6 +596,7 @@ void JsonRPCServerImplementation::setup()
registerHandler(new TagsHandler(this));
registerHandler(new SystemHandler(NymeaCore::instance()->platform(), this));
registerHandler(new UsersHandler(NymeaCore::instance()->userManager(), this));
+ registerHandler(new ZigbeeHandler(NymeaCore::instance()->zigbeeManager(), this));
connect(NymeaCore::instance()->cloudManager(), &CloudManager::pairingReply, this, &JsonRPCServerImplementation::pairingFinished);
connect(NymeaCore::instance()->cloudManager(), &CloudManager::connectionStateChanged, this, &JsonRPCServerImplementation::onCloudConnectionStateChanged);
diff --git a/libnymea-core/jsonrpc/zigbeehandler.cpp b/libnymea-core/jsonrpc/zigbeehandler.cpp
new file mode 100644
index 00000000..66774f7e
--- /dev/null
+++ b/libnymea-core/jsonrpc/zigbeehandler.cpp
@@ -0,0 +1,341 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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();
+ 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);
+
+ 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));
+ 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);
+
+
+ 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);
+ });
+}
+
+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);
+ }
+
+ QPair result = m_zigbeeManager->createZigbeeNetwork(serialPort, baudRate, ZigbeeAdapter::backendNames().key(backendString));
+ 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::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;
+}
+
+}
diff --git a/libnymea-core/jsonrpc/zigbeehandler.h b/libnymea-core/jsonrpc/zigbeehandler.h
new file mode 100644
index 00000000..0494c8af
--- /dev/null
+++ b/libnymea-core/jsonrpc/zigbeehandler.h
@@ -0,0 +1,76 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#ifndef ZIGBEEHANDLER_H
+#define ZIGBEEHANDLER_H
+
+#include
+
+#include "jsonrpc/jsonhandler.h"
+
+#include
+
+namespace nymeaserver {
+
+class ZigbeeManager;
+
+class ZigbeeHandler : public JsonHandler
+{
+ Q_OBJECT
+public:
+ explicit ZigbeeHandler(ZigbeeManager *zigbeeManager, QObject *parent = nullptr);
+
+ QString name() const override;
+
+ Q_INVOKABLE JsonReply *GetAvailableBackends(const QVariantMap ¶ms);
+ Q_INVOKABLE JsonReply *GetAdapters(const QVariantMap ¶ms);
+ Q_INVOKABLE JsonReply *GetNetworks(const QVariantMap ¶ms);
+ Q_INVOKABLE JsonReply *AddNetwork(const QVariantMap ¶ms);
+ Q_INVOKABLE JsonReply *RemoveNetwork(const QVariantMap ¶ms);
+ Q_INVOKABLE JsonReply *FactoryResetNetwork(const QVariantMap ¶ms);
+ Q_INVOKABLE JsonReply *SetPermitJoin(const QVariantMap ¶ms);
+
+ QVariantMap packNetwork(ZigbeeNetwork *network);
+
+private:
+ ZigbeeManager *m_zigbeeManager = nullptr;
+
+signals:
+ void AdapterAdded(const QVariantMap ¶ms);
+ void AdapterRemoved(const QVariantMap ¶ms);
+ void NetworkAdded(const QVariantMap ¶ms);
+ void NetworkRemoved(const QVariantMap ¶ms);
+ void NetworkChanged(const QVariantMap ¶ms);
+
+};
+
+}
+
+#endif // ZIGBEEHANDLER_H
diff --git a/libnymea-core/libnymea-core.pro b/libnymea-core/libnymea-core.pro
index 0b00372f..ad87afc8 100644
--- a/libnymea-core/libnymea-core.pro
+++ b/libnymea-core/libnymea-core.pro
@@ -8,7 +8,7 @@ INCLUDEPATH += $$top_srcdir/libnymea $$top_builddir
LIBS += -L$$top_builddir/libnymea/ -lnymea -lssl -lcrypto
CONFIG += link_pkgconfig
-PKGCONFIG += nymea-mqtt nymea-networkmanager
+PKGCONFIG += nymea-mqtt nymea-networkmanager nymea-zigbee
# As of Ubuntu focal, there's a commonly named python3-embed pointing to the distro version of python
# For everything below python 3.8 we need to manually select one
@@ -53,6 +53,7 @@ HEADERS += nymeacore.h \
integrations/translator.h \
integrations/pythonintegrationplugin.h \
experiences/experiencemanager.h \
+ jsonrpc/zigbeehandler.h \
ruleengine/ruleengine.h \
ruleengine/rule.h \
ruleengine/stateevaluator.h \
@@ -121,12 +122,16 @@ HEADERS += nymeacore.h \
hardware/network/mqtt/mqttproviderimplementation.h \
hardware/network/mqtt/mqttchannelimplementation.h \
hardware/i2c/i2cmanagerimplementation.h \
+ hardware/zigbee/zigbeehardwareresourceimplementation.h \
debugserverhandler.h \
tagging/tagsstorage.h \
tagging/tag.h \
cloud/cloudtransport.h \
debugreportgenerator.h \
platform/platform.h \
+ zigbee/zigbeeadapter.h \
+ zigbee/zigbeeadapters.h \
+ zigbee/zigbeemanager.h
SOURCES += nymeacore.cpp \
@@ -136,6 +141,7 @@ SOURCES += nymeacore.cpp \
integrations/translator.cpp \
integrations/pythonintegrationplugin.cpp \
experiences/experiencemanager.cpp \
+ jsonrpc/zigbeehandler.cpp \
ruleengine/ruleengine.cpp \
ruleengine/rule.cpp \
ruleengine/stateevaluator.cpp \
@@ -203,12 +209,16 @@ SOURCES += nymeacore.cpp \
hardware/network/mqtt/mqttproviderimplementation.cpp \
hardware/network/mqtt/mqttchannelimplementation.cpp \
hardware/i2c/i2cmanagerimplementation.cpp \
+ hardware/zigbee/zigbeehardwareresourceimplementation.cpp \
debugserverhandler.cpp \
tagging/tagsstorage.cpp \
tagging/tag.cpp \
cloud/cloudtransport.cpp \
debugreportgenerator.cpp \
platform/platform.cpp \
+ zigbee/zigbeeadapter.cpp \
+ zigbee/zigbeeadapters.cpp \
+ zigbee/zigbeemanager.cpp
versionAtLeast(QT_VERSION, 5.12.0) {
diff --git a/libnymea-core/nymeacore.cpp b/libnymea-core/nymeacore.cpp
index 7d76312f..6ce8a8d5 100644
--- a/libnymea-core/nymeacore.cpp
+++ b/libnymea-core/nymeacore.cpp
@@ -51,6 +51,8 @@
#include "cloud/cloudnotifications.h"
#include "cloud/cloudtransport.h"
+#include "zigbee/zigbeemanager.h"
+
#include
#include
@@ -106,8 +108,11 @@ void NymeaCore::init(const QStringList &additionalInterfaces) {
qCDebug(dcCore) << "Creating Server Manager";
m_serverManager = new ServerManager(m_platform, m_configuration, additionalInterfaces, this);
+ qCDebug(dcCore()) << "Create Zigbee Manager";
+ m_zigbeeManager = new ZigbeeManager(this);
+
qCDebug(dcCore) << "Creating Hardware Manager";
- m_hardwareManager = new HardwareManagerImplementation(m_platform, m_serverManager->mqttBroker(), this);
+ m_hardwareManager = new HardwareManagerImplementation(m_platform, m_serverManager->mqttBroker(), m_zigbeeManager, this);
qCDebug(dcCore) << "Creating Thing Manager (locale:" << m_configuration->locale() << ")";
m_thingManager = new ThingManagerImplementation(m_hardwareManager, m_configuration->locale(), this);
@@ -649,6 +654,11 @@ Platform *NymeaCore::platform() const
return m_platform;
}
+ZigbeeManager *NymeaCore::zigbeeManager() const
+{
+ return m_zigbeeManager;
+}
+
void NymeaCore::gotEvent(const Event &event)
{
m_logger->logEvent(event);
@@ -814,6 +824,9 @@ void NymeaCore::thingManagerLoaded()
// Evaluate rules on current time
onDateTimeChanged(m_timeManager->currentDateTime());
+ // Tell hardare resources we're done with loading stuff...
+ m_hardwareManager->thingsLoaded();
+
emit initialized();
// Do some houskeeping...
diff --git a/libnymea-core/nymeacore.h b/libnymea-core/nymeacore.h
index c37c5644..3391c258 100644
--- a/libnymea-core/nymeacore.h
+++ b/libnymea-core/nymeacore.h
@@ -66,6 +66,7 @@ class System;
class ExperienceManager;
class ScriptEngine;
class CloudManager;
+class ZigbeeManager;
class NymeaCore : public QObject
{
@@ -106,6 +107,7 @@ public:
DebugServerHandler *debugServerHandler() const;
TagsStorage *tagsStorage() const;
Platform *platform() const;
+ ZigbeeManager *zigbeeManager() const;
static QStringList getAvailableLanguages();
static QStringList loggingFilters();
@@ -149,6 +151,7 @@ private:
UserManager *m_userManager;
System *m_system;
ExperienceManager *m_experienceManager;
+ ZigbeeManager *m_zigbeeManager;
QList m_executingRules;
diff --git a/libnymea-core/zigbee/zigbeeadapter.cpp b/libnymea-core/zigbee/zigbeeadapter.cpp
new file mode 100644
index 00000000..43b411e6
--- /dev/null
+++ b/libnymea-core/zigbee/zigbeeadapter.cpp
@@ -0,0 +1,146 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 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 .
+*
+* 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 "zigbeeadapter.h"
+
+namespace nymeaserver {
+
+ZigbeeAdapter::ZigbeeAdapter()
+{
+
+}
+
+QString ZigbeeAdapter::name() const
+{
+ return m_name;
+}
+
+void ZigbeeAdapter::setName(const QString &name)
+{
+ m_name = name;
+}
+
+QString ZigbeeAdapter::description() const
+{
+ return m_description;
+}
+
+void ZigbeeAdapter::setDescription(const QString &description)
+{
+ m_description = description;
+}
+
+QString ZigbeeAdapter::serialPort() const
+{
+ return m_serialPort;
+}
+
+void ZigbeeAdapter::setSerialPort(const QString &serialPort)
+{
+ m_serialPort = serialPort;
+}
+
+QString ZigbeeAdapter::serialNumber() const
+{
+ return m_serialNumber;
+}
+
+void ZigbeeAdapter::setSerialNumber(const QString &serialNumber)
+{
+ m_serialNumber = serialNumber;
+}
+
+bool ZigbeeAdapter::hardwareRecognized() const
+{
+ return m_hardwareRecognized;
+}
+
+void ZigbeeAdapter::setHardwareRecognized(bool hardwareRecognized)
+{
+ m_hardwareRecognized = hardwareRecognized;
+}
+
+ZigbeeAdapter::ZigbeeBackendType ZigbeeAdapter::backendType() const
+{
+ return m_backendType;
+}
+
+void ZigbeeAdapter::setBackendType(ZigbeeAdapter::ZigbeeBackendType backendType)
+{
+ m_backendType = backendType;
+}
+
+QString ZigbeeAdapter::backend() const
+{
+ return backendNames().value(m_backendType);
+}
+
+qint32 ZigbeeAdapter::baudRate() const
+{
+ return m_baudRate;
+}
+
+void ZigbeeAdapter::setBaudRate(qint32 baudRate)
+{
+ m_baudRate = baudRate;
+}
+
+bool ZigbeeAdapter::operator==(const ZigbeeAdapter &other) const
+{
+ return m_serialPort == other.serialPort()
+ && m_name == other.name()
+ && m_description == other.description()
+ && m_hardwareRecognized == other.hardwareRecognized()
+ && m_backendType == other.backendType()
+ && m_baudRate == other.baudRate();
+}
+
+QHash ZigbeeAdapter::backendNames()
+{
+ QHash backendNameHash;
+ backendNameHash.insert(ZigbeeBackendTypeDeconz, "deCONZ");
+ backendNameHash.insert(ZigbeeBackendTypeNxp, "NXP");
+ return backendNameHash;
+}
+
+QDebug operator<<(QDebug debug, const ZigbeeAdapter &adapter)
+{
+ debug.nospace() << "ZigbeeAdapter(" << adapter.name() << " - " << adapter.description();
+ debug.nospace() << ", " << adapter.serialPort();
+ if (adapter.hardwareRecognized()) {
+ debug.nospace() << " Hardware recognized: " << adapter.backendType();
+ debug.nospace() << ", " << adapter.baudRate();
+ }
+
+ debug.nospace() << ")";
+ return debug.space();
+}
+
+}
diff --git a/libnymea-core/zigbee/zigbeeadapter.h b/libnymea-core/zigbee/zigbeeadapter.h
new file mode 100644
index 00000000..a2419292
--- /dev/null
+++ b/libnymea-core/zigbee/zigbeeadapter.h
@@ -0,0 +1,105 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 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 .
+*
+* 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
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#ifndef ZIGBEEADAPTER_H
+#define ZIGBEEADAPTER_H
+
+#include
+#include
+
+#include "zigbee.h"
+
+namespace nymeaserver {
+
+class ZigbeeAdapter
+{
+ Q_GADGET
+ Q_PROPERTY(QString name READ name)
+ Q_PROPERTY(QString description READ description)
+ Q_PROPERTY(QString serialPort READ serialPort)
+ Q_PROPERTY(QString serialNumber READ serialNumber)
+ Q_PROPERTY(bool hardwareRecognized READ hardwareRecognized)
+ Q_PROPERTY(QString backend READ backend)
+ Q_PROPERTY(qint32 baudRate READ baudRate)
+
+public:
+ enum ZigbeeBackendType {
+ ZigbeeBackendTypeDeconz,
+ ZigbeeBackendTypeNxp
+ };
+ Q_ENUM(ZigbeeBackendType)
+
+ explicit ZigbeeAdapter();
+
+ QString name() const;
+ void setName(const QString &name);
+
+ QString description() const;
+ void setDescription(const QString &description);
+
+ QString serialPort() const;
+ void setSerialPort(const QString &serialPort);
+
+ QString serialNumber() const;
+ void setSerialNumber(const QString &serialNumber);
+
+ bool hardwareRecognized() const;
+ void setHardwareRecognized(bool hardwareRecognized);
+
+ ZigbeeAdapter::ZigbeeBackendType backendType() const;
+ void setBackendType(ZigbeeAdapter::ZigbeeBackendType backendType);
+ QString backend() const;
+
+ qint32 baudRate() const;
+ void setBaudRate(qint32 baudRate);
+
+ bool operator==(const ZigbeeAdapter &other) const;
+
+ static QHash backendNames();
+
+private:
+ QString m_name;
+ QString m_description;
+ QString m_serialPort;
+ QString m_serialNumber;
+ bool m_hardwareRecognized = false;
+ ZigbeeAdapter::ZigbeeBackendType m_backendType = ZigbeeAdapter::ZigbeeBackendTypeDeconz;
+ qint32 m_baudRate = 38400;
+};
+
+QDebug operator<<(QDebug debug, const ZigbeeAdapter &adapter);
+
+}
+
+Q_DECLARE_METATYPE(nymeaserver::ZigbeeAdapter)
+Q_DECLARE_METATYPE(nymeaserver::ZigbeeAdapter::ZigbeeBackendType)
+
+#endif // ZIGBEEADAPTER_H
+
diff --git a/libnymea-core/zigbee/zigbeeadapters.cpp b/libnymea-core/zigbee/zigbeeadapters.cpp
new file mode 100644
index 00000000..b7408069
--- /dev/null
+++ b/libnymea-core/zigbee/zigbeeadapters.cpp
@@ -0,0 +1,67 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 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 .
+*
+* 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 "zigbeeadapters.h"
+
+namespace nymeaserver {
+
+ZigbeeAdapters::ZigbeeAdapters()
+{
+
+}
+
+ZigbeeAdapters::ZigbeeAdapters(const QList &other) :
+ QList(other)
+{
+
+}
+
+bool ZigbeeAdapters::hasSerialPort(const QString &serialPort)
+{
+ for (int i = 0; i < count(); i++) {
+ if (at(i).serialPort() == serialPort) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+QVariant ZigbeeAdapters::get(int index) const
+{
+ return QVariant::fromValue(at(index));
+}
+
+void ZigbeeAdapters::put(const QVariant &variant)
+{
+ append(variant.value());
+}
+
+}
diff --git a/libnymea-core/zigbee/zigbeeadapters.h b/libnymea-core/zigbee/zigbeeadapters.h
new file mode 100644
index 00000000..303d7a4e
--- /dev/null
+++ b/libnymea-core/zigbee/zigbeeadapters.h
@@ -0,0 +1,57 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 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 .
+*
+* 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
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#ifndef ZIGBEEADAPTERS_H
+#define ZIGBEEADAPTERS_H
+
+#include
+
+#include "zigbeeadapter.h"
+
+namespace nymeaserver {
+
+class ZigbeeAdapters : public QList
+{
+ Q_GADGET
+ Q_PROPERTY(int count READ count)
+
+public:
+ ZigbeeAdapters();
+ ZigbeeAdapters(const QList &other);
+ bool hasSerialPort(const QString &serialPort);
+ Q_INVOKABLE QVariant get(int index) const;
+ Q_INVOKABLE void put(const QVariant &variant);
+};
+
+}
+
+Q_DECLARE_METATYPE(nymeaserver::ZigbeeAdapters)
+
+#endif // ZIGBEEADAPTERS_H
diff --git a/libnymea-core/zigbee/zigbeemanager.cpp b/libnymea-core/zigbee/zigbeemanager.cpp
new file mode 100644
index 00000000..eead3535
--- /dev/null
+++ b/libnymea-core/zigbee/zigbeemanager.cpp
@@ -0,0 +1,632 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 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 .
+*
+* 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 "zigbeemanager.h"
+#include "nymeasettings.h"
+#include "loggingcategories.h"
+
+#include
+#include
+
+#include
+
+NYMEA_LOGGING_CATEGORY(dcZigbee, "Zigbee")
+
+// Register debug category from the libnymea-zigbee
+NYMEA_LOGGING_CATEGORY(dcZigbeeNetworkLibNymeaZigbee, "ZigbeeNetwork")
+
+namespace nymeaserver {
+
+ZigbeeManager::ZigbeeManager(QObject *parent) :
+ QObject(parent)
+{
+ // Adapter monitor
+ qCDebug(dcZigbee()) << "Initialize the Zigbee manager";
+ m_adapterMonitor = new ZigbeeUartAdapterMonitor(this);
+ if (!m_adapterMonitor->isValid()) {
+ qCWarning(dcZigbee()) << "Could not initialize the Zigbee adapter monitor.";
+ // Lets continue anyways, maybe we can set up existing networks right the way.
+ }
+
+ qCDebug(dcZigbee()) << "Loading initial adapter list";
+ foreach(const ZigbeeUartAdapter &uartAdapter, m_adapterMonitor->availableAdapters()) {
+ ZigbeeAdapter adapter = convertUartAdapterToAdapter(uartAdapter);
+ qCDebug(dcZigbee()) << "Adapter added" << adapter;
+ m_adapters.append(adapter);
+ }
+
+ connect(m_adapterMonitor, &ZigbeeUartAdapterMonitor::adapterAdded, this, [this](const ZigbeeUartAdapter &uartAdapter){
+ ZigbeeAdapter adapter = convertUartAdapterToAdapter(uartAdapter);
+ qCDebug(dcZigbee()) << "Adapter added" << adapter;
+ m_adapters.append(adapter);
+
+
+
+ if (m_autoSetupAdapters) {
+ if (networkExistsForAdapter(uartAdapter)) {
+ qCDebug(dcZigbee()) << "Auto setup enabled. There is already a network configured for" << uartAdapter;
+ } else {
+ qCDebug(dcZigbee()) << "Auto setup enabled. No network for" << uartAdapter << "created yet. Creating network...";
+ ZigbeeNetwork *network = createPlatformNetwork(uartAdapter.serialPort(), uartAdapter.baudRate(), uartAdapter.zigbeeBackend());
+ if (!uartAdapter.serialNumber().isEmpty())
+ network->setSerialNumber(uartAdapter.serialNumber());
+
+ addNetwork(network);
+ network->startNetwork();
+ }
+ }
+
+ // FIXME: check if serial number available and gets used by a network (adjust serial port if changed)
+
+ emit availableAdapterAdded(adapter);
+ });
+
+ connect(m_adapterMonitor, &ZigbeeUartAdapterMonitor::adapterRemoved, this, [this](const ZigbeeUartAdapter &uartAdapter){
+ foreach (const ZigbeeAdapter &adapter, m_adapters) {
+ if (adapter.serialPort() == uartAdapter.serialPort()) {
+ qCDebug(dcZigbee()) << "Adapter removed" << adapter;
+ m_adapters.removeAll(adapter);
+ emit availableAdapterRemoved(adapter);
+ }
+ }
+ });
+
+ // Load zigbee networks from settings
+ loadZigbeeNetworks();
+
+ // Check if we have a zigbee platform configuration and if we have to create the platform network automatically
+ checkPlatformConfiguration();
+
+ // Check if auto setup recognized adapters is enabled
+ if (m_autoSetupAdapters) {
+ qCDebug(dcZigbee()) << "Auto setup enabled. Verify existing networks and adapters...";
+ foreach(const ZigbeeUartAdapter &uartAdapter, m_adapterMonitor->availableAdapters()) {
+ if (networkExistsForAdapter(uartAdapter)) {
+ qCDebug(dcZigbee()) << "Network for adapter" << uartAdapter << "already created.";
+ } else {
+ qCDebug(dcZigbee()) << "Auto setup enabled and there is no network for" << uartAdapter << "created yet. Creating network...";
+ ZigbeeNetwork *network = createPlatformNetwork(uartAdapter.serialPort(), uartAdapter.baudRate(), uartAdapter.zigbeeBackend());
+ if (!uartAdapter.serialNumber().isEmpty())
+ network->setSerialNumber(uartAdapter.serialNumber());
+
+ addNetwork(network);
+ }
+ }
+ }
+
+ // Start all loaded networks
+ foreach (ZigbeeNetwork *network, m_zigbeeNetworks.values()) {
+ network->startNetwork();
+ }
+}
+
+bool ZigbeeManager::available() const
+{
+ return m_available;
+}
+
+bool ZigbeeManager::enabled() const
+{
+ return true;//m_zigbeeNetwork && m_zigbeeNetwork->state() != ZigbeeNetwork::StateUninitialized;
+}
+
+ZigbeeAdapters ZigbeeManager::availableAdapters() const
+{
+ return m_adapters;
+}
+
+QHash ZigbeeManager::zigbeeNetworks() const
+{
+ return m_zigbeeNetworks;
+}
+
+QPair ZigbeeManager::createZigbeeNetwork(const QString &serialPort, uint baudRate, ZigbeeAdapter::ZigbeeBackendType backendType, ZigbeeChannelMask channelMask)
+{
+ qCDebug(dcZigbee()) << "Start creating network for" << serialPort << baudRate << backendType << channelMask;
+
+ // Make sure we don't have aleardy a network for this adapter
+ foreach (ZigbeeNetwork *existingNetwork, m_zigbeeNetworks.values()) {
+ if (existingNetwork->serialPortName() == serialPort) {
+ qCWarning(dcZigbee()) << "Failed to create a network for" << serialPort << "because this adapter is already in use for network" << existingNetwork;
+ return QPair(ZigbeeManager::ZigbeeErrorAdapterAlreadyInUse, QUuid());
+ }
+ }
+
+ if (!m_adapters.hasSerialPort(serialPort)) {
+ qCWarning(dcZigbee()) << "Failed to create a network for" << serialPort << "because the adapter is not available any more";
+ return QPair(ZigbeeManager::ZigbeeErrorAdapterNotAvailable, QUuid());
+ }
+
+ QString serialNumber;
+ foreach (const ZigbeeAdapter &adapter, m_adapters) {
+ if (adapter.serialPort() == serialPort && !adapter.serialNumber().isEmpty()) {
+ serialNumber = adapter.serialNumber();
+ }
+ }
+
+ ZigbeeNetwork *network = buildNetworkObject(QUuid::createUuid(), backendType);
+ network->setChannelMask(channelMask);
+ network->setSerialPortName(serialPort);
+ network->setSerialBaudrate(baudRate);
+ network->setSerialNumber(serialNumber);
+ addNetwork(network);
+
+ qCDebug(dcZigbee()) << "Starting" << network;
+ network->startNetwork();
+ return QPair(ZigbeeManager::ZigbeeErrorNoError, network->networkUuid());
+}
+
+ZigbeeManager::ZigbeeError ZigbeeManager::removeZigbeeNetwork(const QUuid &networkUuid)
+{
+ if (!m_zigbeeNetworks.keys().contains(networkUuid)) {
+ qCWarning(dcZigbee()) << "Could not remove network with uuid" << networkUuid.toString() << "because there is no network with this uuid.";
+ return ZigbeeManager::ZigbeeErrorNetworkUuidNotFound;
+ }
+
+ ZigbeeNetwork *network = m_zigbeeNetworks.value(networkUuid);
+ qCDebug(dcZigbee()) << "Removing" << network;
+ // Note: destroy will remove all nodes from the network and wipe/delete the database
+ network->destroyNetwork();
+ emit zigbeeNetworkRemoved(network->networkUuid());
+
+ // Make sure to delete later, so all node removed signals can be processed
+ m_zigbeeNetworks.remove(networkUuid);
+ network->deleteLater();
+
+ // Delete network settings
+ NymeaSettings settings(NymeaSettings::SettingsRoleZigbee);
+ settings.beginGroup("ZigbeeNetworks");
+ settings.beginGroup(network->networkUuid().toString());
+ settings.remove("");
+ settings.endGroup();
+ settings.endGroup();
+
+ qCDebug(dcZigbee()) << "Network removed successfully";
+ return ZigbeeManager::ZigbeeErrorNoError;
+}
+
+ZigbeeManager::ZigbeeError ZigbeeManager::setZigbeeNetworkPermitJoin(const QUuid &networkUuid, quint16 shortAddress, uint duration)
+{
+ if (!m_zigbeeNetworks.keys().contains(networkUuid)) {
+ qCWarning(dcZigbee()) << "Could not set permit join network" << networkUuid.toString() << "because there is no network with this uuid.";
+ return ZigbeeManager::ZigbeeErrorNetworkUuidNotFound;
+ }
+
+ if (duration > 255) {
+ qCWarning(dcZigbee()) << "The given duration for permit join is out of range. Only values between 0 and 255 are allowed.";
+ return ZigbeeManager::ZigbeeErrorDurationOutOfRange;
+ }
+
+ ZigbeeNetwork *network = m_zigbeeNetworks.value(networkUuid);
+ if (network->state() != ZigbeeNetwork::StateRunning) {
+ qCWarning(dcZigbee()) << "Could not set permit join in" << network << "because the network is not running.";
+ return ZigbeeManager::ZigbeeErrorNetworkOffline;
+ }
+
+ qCDebug(dcZigbee()) << "Set permit joining in network" << network << "to" << duration << "seconds" << ZigbeeUtils::convertUint16ToHexString(shortAddress);
+ network->setPermitJoining(duration, shortAddress);
+
+ // Notify all clients about the new configuration
+ emit zigbeeNetworkChanged(network);
+
+ return ZigbeeManager::ZigbeeErrorNoError;
+}
+
+ZigbeeManager::ZigbeeError ZigbeeManager::factoryResetNetwork(const QUuid &networkUuid)
+{
+ if (!m_zigbeeNetworks.keys().contains(networkUuid)) {
+ qCWarning(dcZigbee()) << "Could not factory reset network with uuid" << networkUuid.toString() << "because there is no network with this uuid.";
+ return ZigbeeManager::ZigbeeErrorNetworkUuidNotFound;
+ }
+
+ ZigbeeNetwork *network = m_zigbeeNetworks.value(networkUuid);
+ qCDebug(dcZigbee()) << "Start factory resetting" << network;
+ network->factoryResetNetwork();
+ return ZigbeeManager::ZigbeeErrorNoError;
+}
+
+void ZigbeeManager::saveNetwork(ZigbeeNetwork *network)
+{
+ NymeaSettings settings(NymeaSettings::SettingsRoleZigbee);
+ settings.beginGroup("ZigbeeNetworks");
+ settings.beginGroup(network->networkUuid().toString());
+ settings.setValue("serialPort", network->serialPortName());
+ settings.setValue("baudRate", network->serialBaudrate());
+ switch (network->backendType()) {
+ case Zigbee::ZigbeeBackendTypeDeconz:
+ settings.setValue("backendType", static_cast(ZigbeeAdapter::ZigbeeBackendTypeDeconz));
+ break;
+ case Zigbee::ZigbeeBackendTypeNxp:
+ settings.setValue("backendType", static_cast(ZigbeeAdapter::ZigbeeBackendTypeNxp));
+ break;
+ default:
+ qCWarning(dcZigbee()) << "Unhandled backend type" << network->backendType() << "which is not implemented in nymea yet.";
+ break;
+ }
+ settings.setValue("panId", network->panId());
+ settings.setValue("channel", network->channel());
+ settings.setValue("macAddress", network->macAddress().toString());
+ settings.setValue("channelMask", network->channelMask().toUInt32());
+ settings.setValue("networkKey", network->securityConfiguration().networkKey().toString());
+ settings.setValue("trustCenterLinkKey", network->securityConfiguration().globalTrustCenterLinkKey().toString());
+ if (!network->serialNumber().isEmpty()) {
+ settings.setValue("serialNumber", network->serialNumber());
+ }
+
+ settings.endGroup(); // networkUuid
+ settings.endGroup(); // ZigbeeNetworks
+}
+
+void ZigbeeManager::loadZigbeeNetworks()
+{
+ NymeaSettings settings(NymeaSettings::SettingsRoleZigbee);
+ qCDebug(dcZigbee()) << "Loading zigbee networks from" << settings.fileName();
+ settings.beginGroup("ZigbeeNetworks");
+ foreach (const QString networkUuidGroupString, settings.childGroups()) {
+ settings.beginGroup(networkUuidGroupString);
+
+ // FIXME: load also the serial number of the port if available and search that serial port
+
+ QUuid networkUuid = QUuid(networkUuidGroupString);
+ QString serialPortName = settings.value("serialPort").toString();
+ QString serialNumber = settings.value("serialNumber").toString();
+ qint32 serialBaudRate = settings.value("baudRate").toInt();
+ ZigbeeAdapter::ZigbeeBackendType backendType = static_cast(settings.value("backendType").toInt());
+ quint16 panId = static_cast(settings.value("panId", 0).toUInt());
+ quint8 channel = settings.value("channel", 0).toUInt();
+ ZigbeeChannelMask channelMask = ZigbeeChannelMask(static_cast(settings.value("channelMask").toUInt()));
+ ZigbeeAddress macAddress = ZigbeeAddress(settings.value("macAddress").toString());
+ ZigbeeSecurityConfiguration securityConfiguration;
+ ZigbeeNetworkKey netKey(settings.value("networkKey", QString()).toString());
+ if (netKey.isValid()) {
+ securityConfiguration.setNetworkKey(netKey);
+ }
+
+ ZigbeeNetworkKey tcKey(settings.value("trustCenterLinkKey", QString("5A6967426565416C6C69616E63653039")).toString());
+ if (!tcKey.isValid()) {
+ securityConfiguration.setGlobalTrustCenterlinkKey(tcKey);
+ }
+
+ ZigbeeNetwork *network = buildNetworkObject(networkUuid, backendType);
+ network->setSerialPortName(serialPortName);
+ network->setSerialBaudrate(serialBaudRate);
+ network->setSerialNumber(serialNumber);
+ network->setMacAddress(macAddress);
+ network->setPanId(panId);
+ network->setChannel(channel);
+ network->setChannelMask(channelMask);
+ network->setSecurityConfiguration(securityConfiguration);
+ settings.endGroup(); // networkUuid
+
+ // Load the nodes before adding the network internally, so the loaded nodes will not generate added signals
+ network->loadNetwork();
+
+ // Add the network internally
+ addNetwork(network);
+ }
+ settings.endGroup(); // ZigbeeNetworks
+
+ if (m_zigbeeNetworks.isEmpty()) {
+ qCDebug(dcZigbee()) << "There are no zigbee networks configured yet.";
+ return;
+ }
+
+}
+
+void ZigbeeManager::checkPlatformConfiguration()
+{
+ /* Example platform configurations
+ *
+ * serialPort=/dev/ttymxc2
+ * baudRate=115200
+ * backend=nxp
+ * autoSetup=false
+ *
+ * serialPort=/dev/ttyS0
+ * baudRate=38400
+ * backend=deconz
+ * autoSetup=false
+ *
+ * autoSetup=true
+ */
+
+ QFileInfo platformConfigurationFileInfo(NymeaSettings::settingsPath() + QDir::separator() + "zigbee-platform.conf");
+ if (platformConfigurationFileInfo.exists()) {
+ qCDebug(dcZigbee()) << "Found zigbee platform configuration" << platformConfigurationFileInfo.absoluteFilePath();
+ QSettings platformSettings(platformConfigurationFileInfo.absoluteFilePath(), QSettings::IniFormat);
+ QString serialPort = platformSettings.value("serialPort").toString();
+ qint32 baudRate = platformSettings.value("baudRate").toUInt();
+ QString backendString = platformSettings.value("backend").toString();
+ m_autoSetupAdapters = platformSettings.value("autoSetup").toBool();
+ Zigbee::ZigbeeBackendType backenType = Zigbee::ZigbeeBackendTypeNxp;
+ if (backendString.toLower().contains("deconz")) {
+ backenType = Zigbee::ZigbeeBackendTypeDeconz;
+ }
+
+ if (serialPort.isEmpty()) {
+ qCWarning(dcZigbee()) << "The serial port is not specified correctly in the platform configuration file" << platformConfigurationFileInfo.absoluteFilePath() << "The platform based network will not be created.";
+ return;
+ }
+
+ if (!m_adapterMonitor->hasAdapter(serialPort)) {
+ qCWarning(dcZigbee()) << "Could not find platform specific serial port" << serialPort << "on this system. Please check the wiring or the port configuration. The platform based network will not be created.";
+ return;
+ }
+
+ bool alreadyCreated = false;
+ foreach (ZigbeeNetwork *network, m_zigbeeNetworks.values()) {
+ if (network->serialPortName() == serialPort && network->serialBaudrate() == baudRate && network->backendType() == backenType) {
+ qCDebug(dcZigbee()) << "Network based on platform configuration already created" << network;
+ alreadyCreated = true;
+ break;
+ }
+ }
+
+ if (!alreadyCreated) {
+ qCDebug(dcZigbee()) << "Network based on platform configuration has not been created yet.";
+ ZigbeeNetwork *network = createPlatformNetwork(serialPort, baudRate, backenType);
+ addNetwork(network);
+ // Note: it will be saved once the network has started successfully
+ }
+ } else {
+ qCDebug(dcZigbee()) << "No platform configuration specified.";
+ }
+}
+
+bool ZigbeeManager::networkExistsForAdapter(const ZigbeeUartAdapter &uartAdapter)
+{
+ bool networkCreated = false;
+ foreach (ZigbeeNetwork *network, m_zigbeeNetworks) {
+ // Use the serial number if possible as refference
+ if (!uartAdapter.serialNumber().isEmpty()) {
+ if (network->serialNumber() == uartAdapter.serialNumber()) {
+ networkCreated = true;
+ break;
+ }
+ }
+
+ // Otherwise check the serial port
+ if (network->serialPortName() == uartAdapter.serialPort()) {
+ networkCreated = true;
+ break;
+ }
+ }
+ return networkCreated;
+}
+
+ZigbeeNetwork *ZigbeeManager::createPlatformNetwork(const QString &serialPort, uint baudRate, Zigbee::ZigbeeBackendType backendType, ZigbeeChannelMask channelMask)
+{
+ qCDebug(dcZigbee()) << "Creating platform network on" << serialPort << baudRate << backendType << channelMask;
+ ZigbeeNetwork *network = ZigbeeNetworkManager::createZigbeeNetwork(QUuid::createUuid(), backendType);
+ network->setSettingsDirectory(QDir(NymeaSettings::settingsPath()));
+ network->setChannelMask(channelMask);
+ network->setSerialPortName(serialPort);
+ network->setSerialBaudrate(baudRate);
+ return network;
+}
+
+ZigbeeNetwork *ZigbeeManager::buildNetworkObject(const QUuid &networkId, ZigbeeAdapter::ZigbeeBackendType backendType)
+{
+ ZigbeeNetwork *network = nullptr;
+ switch (backendType) {
+ case ZigbeeAdapter::ZigbeeBackendTypeDeconz:
+ network = ZigbeeNetworkManager::createZigbeeNetwork(networkId, Zigbee::ZigbeeBackendTypeDeconz, this);
+ break;
+ case ZigbeeAdapter::ZigbeeBackendTypeNxp:
+ network = ZigbeeNetworkManager::createZigbeeNetwork(networkId, Zigbee::ZigbeeBackendTypeNxp, this);
+ break;
+ }
+ network->setSettingsDirectory(QDir(NymeaSettings::settingsPath()));
+ return network;
+}
+
+void ZigbeeManager::addNetwork(ZigbeeNetwork *network)
+{
+ connect(network, &ZigbeeNetwork::stateChanged, this, [this, network](ZigbeeNetwork::State state){
+ Q_UNUSED(state)
+ qCDebug(dcZigbee()) << "Network state changed" << network;
+ evaluateZigbeeAvailable();
+ emit zigbeeNetworkChanged(network);
+ });
+
+ connect(network, &ZigbeeNetwork::errorOccured, this, [this, network](ZigbeeNetwork::Error error){
+ qCWarning(dcZigbee()) << "Network error occured for" << network << error;
+ evaluateZigbeeAvailable();
+ // TODO: handle error
+ });
+
+ connect(network, &ZigbeeNetwork::panIdChanged, this, [this, network](quint16 panId){
+ qCDebug(dcZigbee()) << "Network PAN ID changed for" << network << panId;
+ saveNetwork(network);
+ emit zigbeeNetworkChanged(network);
+ });
+
+ connect(network, &ZigbeeNetwork::channelChanged, this, [this, network](quint8 channel){
+ qCDebug(dcZigbee()) << "Network channel changed for" << network << channel;
+ saveNetwork(network);
+ emit zigbeeNetworkChanged(network);
+ });
+
+ connect(network, &ZigbeeNetwork::macAddressChanged, this, [this, network](const ZigbeeAddress &macAddress){
+ qCDebug(dcZigbee()) << "Network MAC address changed for" << network << macAddress.toString();
+ saveNetwork(network);
+ emit zigbeeNetworkChanged(network);
+ });
+
+ connect(network, &ZigbeeNetwork::securityConfigurationChanged, this, [this, network](const ZigbeeSecurityConfiguration &securityConfiguration){
+ qCDebug(dcZigbee()) << "Network security configuration changed for" << network << securityConfiguration.networkKey().toString() << securityConfiguration.globalTrustCenterLinkKey().toString();
+ saveNetwork(network);
+ });
+
+ connect(network, &ZigbeeNetwork::channelMaskChanged, this, [this, network](const ZigbeeChannelMask &channelMask){
+ qCDebug(dcZigbee()) << "Network channel mask changed for" << network << channelMask;
+ saveNetwork(network);
+ emit zigbeeNetworkChanged(network);
+ });
+
+ connect(network, &ZigbeeNetwork::permitJoiningEnabledChanged, this, [this, network](bool permitJoiningEnabled){
+ qCDebug(dcZigbee()) << "Network permit joining changed" << network->networkUuid().toString() << permitJoiningEnabled;
+ emit zigbeeNetworkChanged(network);
+ });
+
+ connect(network, &ZigbeeNetwork::nodeAdded, this, [this, network](ZigbeeNode *node){
+ qCDebug(dcZigbee()) << "Node added to" << network << node;
+ // The plugin don't need to see the coordinator node
+ if (node->shortAddress() == 0) {
+ return;
+ }
+
+ qCDebug(dcZigbee()) << "-->" << node;
+ foreach (ZigbeeNodeEndpoint *endpoint, node->endpoints()) {
+ qCDebug(dcZigbee()) << " " << endpoint;
+ if (!endpoint->manufacturerName().isEmpty())
+ qCDebug(dcZigbee()) << " Manufacturer:" << endpoint->manufacturerName();
+
+ if (!endpoint->modelIdentifier().isEmpty())
+ qCDebug(dcZigbee()) << " Model" << endpoint->modelIdentifier();
+
+ if (!endpoint->softwareBuildId().isEmpty())
+ qCDebug(dcZigbee()) << " Version" << endpoint->softwareBuildId();
+
+ qCDebug(dcZigbee()) << " Input clusters (" << endpoint->inputClusters().count() << ")";
+ foreach (ZigbeeCluster *cluster, endpoint->inputClusters()) {
+ qCDebug(dcZigbee()) << " -" << cluster;
+ foreach(const ZigbeeClusterAttribute &attribute, cluster->attributes()) {
+ qCDebug(dcZigbee()) << " - " << attribute;
+ }
+ }
+
+ qCDebug(dcZigbee()) << " Output clusters (" << endpoint->outputClusters().count() << ")";
+ foreach (ZigbeeCluster *cluster, endpoint->outputClusters()) {
+ qCDebug(dcZigbee()) << " -" << cluster;
+ foreach(const ZigbeeClusterAttribute &attribute, cluster->attributes()) {
+ qCDebug(dcZigbee()) << " - " << attribute;
+ }
+ }
+ }
+
+ emit nodeAdded(network->networkUuid(), node);
+ });
+
+ connect(network, &ZigbeeNetwork::nodeRemoved, this, [this, network](ZigbeeNode *node){
+ qCDebug(dcZigbee()) << "Node removed from" << network->networkUuid().toString() << node;
+ // The plugin don't need to see the coordinator node
+ if (node->shortAddress() == 0) {
+ return;
+ }
+ emit nodeRemoved(network->networkUuid(), node);
+ });
+
+ connect(network, &ZigbeeNetwork::firmwareVersionChanged, this, [this, network](const QString &firmwareVersion){
+ qCDebug(dcZigbee()) << "Network adapter firmware version changed for" << network << firmwareVersion;
+ emit zigbeeNetworkChanged(network);
+ });
+
+ m_zigbeeNetworks.insert(network->networkUuid(), network);
+ emit zigbeeNetworkAdded(network);
+
+ qCDebug(dcZigbee()) << "Network added" << network;
+ foreach (ZigbeeNode *node, network->nodes()) {
+ qCDebug(dcZigbee()) << "-->" << node;
+ foreach (ZigbeeNodeEndpoint *endpoint, node->endpoints()) {
+ qCDebug(dcZigbee()) << " " << endpoint;
+ if (!endpoint->manufacturerName().isEmpty())
+ qCDebug(dcZigbee()) << " Manufacturer:" << endpoint->manufacturerName();
+
+ if (!endpoint->modelIdentifier().isEmpty())
+ qCDebug(dcZigbee()) << " Model" << endpoint->modelIdentifier();
+
+ if (!endpoint->softwareBuildId().isEmpty())
+ qCDebug(dcZigbee()) << " Version" << endpoint->softwareBuildId();
+
+ qCDebug(dcZigbee()) << " Input clusters (" << endpoint->inputClusters().count() << ")";
+ foreach (ZigbeeCluster *cluster, endpoint->inputClusters()) {
+ qCDebug(dcZigbee()) << " -" << cluster;
+ foreach(const ZigbeeClusterAttribute &attribute, cluster->attributes()) {
+ qCDebug(dcZigbee()) << " - " << attribute;
+ }
+ }
+
+ qCDebug(dcZigbee()) << " Output clusters (" << endpoint->outputClusters().count() << ")";
+ foreach (ZigbeeCluster *cluster, endpoint->outputClusters()) {
+ qCDebug(dcZigbee()) << " -" << cluster;
+ foreach(const ZigbeeClusterAttribute &attribute, cluster->attributes()) {
+ qCDebug(dcZigbee()) << " - " << attribute;
+ }
+ }
+ }
+ }
+}
+
+ZigbeeAdapter ZigbeeManager::convertUartAdapterToAdapter(const ZigbeeUartAdapter &uartAdapter)
+{
+ ZigbeeAdapter adapter;
+ adapter.setName(uartAdapter.name());
+ adapter.setSerialPort(uartAdapter.serialPort());
+ adapter.setDescription(uartAdapter.description());
+ adapter.setSerialNumber(uartAdapter.serialNumber());
+ adapter.setHardwareRecognized(uartAdapter.hardwareRecognized());
+ adapter.setBaudRate(uartAdapter.baudRate());
+ switch (uartAdapter.zigbeeBackend()) {
+ case Zigbee::ZigbeeBackendTypeDeconz:
+ adapter.setBackendType(ZigbeeAdapter::ZigbeeBackendTypeDeconz);
+ break;
+ case Zigbee::ZigbeeBackendTypeNxp:
+ adapter.setBackendType(ZigbeeAdapter::ZigbeeBackendTypeNxp);
+ break;
+ default:
+ qCWarning(dcZigbee()) << "Unhandled backend type" << uartAdapter.zigbeeBackend() << "which is not implemented in nymea yet.";
+ break;
+ }
+ return adapter;
+}
+
+void ZigbeeManager::evaluateZigbeeAvailable()
+{
+ bool zigbeeAvailable = false;
+ if (!m_zigbeeNetworks.isEmpty()) {
+ foreach (ZigbeeNetwork *network, m_zigbeeNetworks.values()) {
+ if (network->state() == ZigbeeNetwork::StateRunning) {
+ zigbeeAvailable = true;
+ break;
+ }
+ }
+ }
+
+ if (m_available == zigbeeAvailable)
+ return;
+
+ qCDebug(dcZigbee()) << "Zigbee is" << (zigbeeAvailable ? "now available" : "not available any more.");
+ m_available = zigbeeAvailable;
+ emit availableChanged(m_available);
+}
+
+
+}
diff --git a/libnymea-core/zigbee/zigbeemanager.h b/libnymea-core/zigbee/zigbeemanager.h
new file mode 100644
index 00000000..3e829043
--- /dev/null
+++ b/libnymea-core/zigbee/zigbeemanager.h
@@ -0,0 +1,116 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 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 .
+*
+* 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
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#ifndef ZIGBEEMANAGER_H
+#define ZIGBEEMANAGER_H
+
+#include
+
+#include
+#include
+
+#include "zigbeeadapters.h"
+
+namespace nymeaserver {
+
+class ZigbeeManager : public QObject
+{
+ Q_OBJECT
+public:
+ enum ZigbeeNetworkState {
+ ZigbeeNetworkStateOffline,
+ ZigbeeNetworkStateStarting,
+ ZigbeeNetworkStateUpdating,
+ ZigbeeNetworkStateOnline,
+ ZigbeeNetworkStateError
+ };
+ Q_ENUM(ZigbeeNetworkState)
+
+ enum ZigbeeError {
+ ZigbeeErrorNoError,
+ ZigbeeErrorAdapterNotAvailable,
+ ZigbeeErrorAdapterAlreadyInUse,
+ ZigbeeErrorNetworkUuidNotFound,
+ ZigbeeErrorDurationOutOfRange,
+ ZigbeeErrorNetworkOffline,
+ ZigbeeErrorUnknownBackend
+ };
+ Q_ENUM(ZigbeeError)
+
+ explicit ZigbeeManager(QObject *parent = nullptr);
+
+ bool available() const;
+ bool enabled() const;
+
+ ZigbeeAdapters availableAdapters() const;
+ QHash zigbeeNetworks() const;
+
+ QPair createZigbeeNetwork(const QString &serialPort, uint baudRate, ZigbeeAdapter::ZigbeeBackendType backendType, ZigbeeChannelMask channelMask = ZigbeeChannelMask(ZigbeeChannelMask::ChannelConfigurationAllChannels));
+ ZigbeeError removeZigbeeNetwork(const QUuid &networkUuid);
+ ZigbeeError setZigbeeNetworkPermitJoin(const QUuid &networkUuid, quint16 shortAddress = Zigbee::BroadcastAddressAllRouters, uint duration = 120);
+ ZigbeeError factoryResetNetwork(const QUuid &networkUuid);
+
+private:
+ ZigbeeAdapters m_adapters;
+ ZigbeeUartAdapterMonitor *m_adapterMonitor = nullptr;
+ QHash m_zigbeeNetworks;
+
+ bool m_available = false;
+ bool m_autoSetupAdapters = false;
+
+ void saveNetwork(ZigbeeNetwork *network);
+ void loadZigbeeNetworks();
+ void checkPlatformConfiguration();
+ bool networkExistsForAdapter(const ZigbeeUartAdapter &uartAdapter);
+ ZigbeeNetwork *createPlatformNetwork(const QString &serialPort, uint baudRate, Zigbee::ZigbeeBackendType backendType, ZigbeeChannelMask channelMask = ZigbeeChannelMask(ZigbeeChannelMask::ChannelConfigurationAllChannels));
+
+ ZigbeeNetwork *buildNetworkObject(const QUuid &networkId, ZigbeeAdapter::ZigbeeBackendType backendType);
+ void addNetwork(ZigbeeNetwork *network);
+
+ ZigbeeAdapter convertUartAdapterToAdapter(const ZigbeeUartAdapter &uartAdapter);
+ void evaluateZigbeeAvailable();
+
+signals:
+ void availableChanged(bool available);
+
+ void availableAdapterAdded(const ZigbeeAdapter &adapter);
+ void availableAdapterRemoved(const ZigbeeAdapter &adapter);
+
+ void zigbeeNetworkAdded(ZigbeeNetwork *zigbeeNetwork);
+ void zigbeeNetworkRemoved(const QUuid networkUuid);
+ void zigbeeNetworkChanged(ZigbeeNetwork *zigbeeNetwork);
+
+ void nodeAdded(const QUuid &networkUuid, ZigbeeNode *node);
+ void nodeRemoved(const QUuid &networkUuid, ZigbeeNode *node);
+};
+
+}
+
+#endif // ZIGBEEMANAGER_H
diff --git a/libnymea/hardware/zigbee/zigbeehandler.cpp b/libnymea/hardware/zigbee/zigbeehandler.cpp
new file mode 100644
index 00000000..9d5a209a
--- /dev/null
+++ b/libnymea/hardware/zigbee/zigbeehandler.cpp
@@ -0,0 +1,36 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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"
+
+ZigbeeHandler::ZigbeeHandler()
+{
+
+}
diff --git a/libnymea/hardware/zigbee/zigbeehandler.h b/libnymea/hardware/zigbee/zigbeehandler.h
new file mode 100644
index 00000000..52f7b10f
--- /dev/null
+++ b/libnymea/hardware/zigbee/zigbeehandler.h
@@ -0,0 +1,48 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#ifndef ZIGBEEHANDLER_H
+#define ZIGBEEHANDLER_H
+
+#include "zigbeenode.h"
+#include "libnymea.h"
+
+class LIBNYMEA_EXPORT ZigbeeHandler
+{
+public:
+ ZigbeeHandler();
+ virtual ~ZigbeeHandler() = default;
+
+ virtual QString name() const = 0;
+ virtual bool handleNode(ZigbeeNode *node, const QUuid &networkUuid) = 0;
+ virtual void handleRemoveNode(ZigbeeNode *node, const QUuid &networkUuid) = 0;
+};
+
+#endif // ZIGBEEHANDLER_H
diff --git a/libnymea/hardware/zigbee/zigbeehardwareresource.cpp b/libnymea/hardware/zigbee/zigbeehardwareresource.cpp
new file mode 100644
index 00000000..4ca1fea0
--- /dev/null
+++ b/libnymea/hardware/zigbee/zigbeehardwareresource.cpp
@@ -0,0 +1,37 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 "zigbeehardwareresource.h"
+
+ZigbeeHardwareResource::ZigbeeHardwareResource(QObject *parent) :
+ HardwareResource("Zigbee hardware resource", parent)
+{
+
+}
diff --git a/libnymea/hardware/zigbee/zigbeehardwareresource.h b/libnymea/hardware/zigbee/zigbeehardwareresource.h
new file mode 100644
index 00000000..c4a7b860
--- /dev/null
+++ b/libnymea/hardware/zigbee/zigbeehardwareresource.h
@@ -0,0 +1,69 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#ifndef ZIGBEEHARDWARERESOURCE_H
+#define ZIGBEEHARDWARERESOURCE_H
+
+#include
+
+#include "hardwareresource.h"
+
+#include
+#include
+
+class ZigbeeHandler;
+class ZigbeeNode;
+
+class ZigbeeHardwareResource : public HardwareResource
+{
+ Q_OBJECT
+public:
+ enum HandlerType {
+ HandlerTypeBranding,
+ HandlerTypeVendor,
+ HandlerTypeCatchAll
+ };
+ Q_ENUM(HandlerType)
+ explicit ZigbeeHardwareResource(QObject *parent = nullptr);
+ virtual ~ZigbeeHardwareResource() = default;
+
+ virtual void registerHandler(ZigbeeHandler *handler, HandlerType type = HandlerTypeVendor) = 0;
+ virtual ZigbeeNode* claimNode(ZigbeeHandler *hanlder, const QUuid &networkUuid, const ZigbeeAddress &extendedAddress) = 0;
+ virtual void removeNodeFromNetwork(const QUuid &networkUuid, ZigbeeNode *node) = 0;
+
+ virtual ZigbeeNetwork::State networkState(const QUuid &networkUuid) = 0;
+ virtual ZigbeeAddress coordinatorAddress(const QUuid &networkUuid) = 0;
+
+signals:
+ void networkStateChanged(const QUuid &networkUuid, ZigbeeNetwork::State state);
+
+};
+
+#endif // ZIGBEEHARDWARERESOURCE_H
diff --git a/libnymea/hardwaremanager.cpp b/libnymea/hardwaremanager.cpp
index 12aa022b..5141e605 100644
--- a/libnymea/hardwaremanager.cpp
+++ b/libnymea/hardwaremanager.cpp
@@ -69,6 +69,10 @@
Returns the MqttProvider \l{HardwareResource}.
*/
+/*! \fn ZigbeeHardwareResource *HardwareManager::zigbeeResource();
+ Returns the Zigbee \l{HardwareResource}.
+*/
+
#include "hardwaremanager.h"
#include "hardwareresource.h"
diff --git a/libnymea/hardwaremanager.h b/libnymea/hardwaremanager.h
index d5ed2c45..745a4ca9 100644
--- a/libnymea/hardwaremanager.h
+++ b/libnymea/hardwaremanager.h
@@ -42,6 +42,7 @@ class PlatformZeroConfController;
class BluetoothLowEnergyManager;
class MqttProvider;
class I2CManager;
+class ZigbeeHardwareResource;
class HardwareResource;
class HardwareManager : public QObject
@@ -61,6 +62,7 @@ public:
virtual BluetoothLowEnergyManager *bluetoothLowEnergyManager() = 0;
virtual MqttProvider *mqttProvider() = 0;
virtual I2CManager *i2cManager() = 0;
+ virtual ZigbeeHardwareResource *zigbeeResource() = 0;
protected:
void setResourceEnabled(HardwareResource* resource, bool enabled);
diff --git a/libnymea/interfaces/alert.json b/libnymea/interfaces/alert.json
index bdebb170..8e4bc8c9 100644
--- a/libnymea/interfaces/alert.json
+++ b/libnymea/interfaces/alert.json
@@ -1,5 +1,5 @@
{
- "description": "Used for things that have some sort of alert. For instance, light bulbs may be able to blink on alert actions, or speaker might be able to play an alert sound. ZigBee devices for example oftion provide alert actions to identify themselves.",
+ "description": "Used for things that have some sort of alert. For instance, light bulbs may be able to blink on alert actions, or speaker might be able to play an alert sound. Zigbee devices for example often provide alert actions to identify themselves.",
"actions": [
{
"name": "alert"
diff --git a/libnymea/interfaces/gateway.json b/libnymea/interfaces/gateway.json
index 9ae8633a..032442c4 100644
--- a/libnymea/interfaces/gateway.json
+++ b/libnymea/interfaces/gateway.json
@@ -1,4 +1,4 @@
{
- "description": "The gateway interface is used for gateway devices like bridges to other networks. For instance Ethernet to ZigBee bridges, Ethernet to RF bridges or similar. Typically such device classes implement the actual functionality in child devices that will auto-appear after successful connection to the gateway/bridge.",
+ "description": "The gateway interface is used for gateway devices like bridges to other networks. For instance Ethernet to Zigbee bridges, Ethernet to RF bridges or similar. Typically such device classes implement the actual functionality in child devices that will auto-appear after successful connection to the gateway/bridge.",
"extends": "connectable"
}
diff --git a/libnymea/libnymea.pro b/libnymea/libnymea.pro
index b86c15e8..6be24a33 100644
--- a/libnymea/libnymea.pro
+++ b/libnymea/libnymea.pro
@@ -7,9 +7,14 @@ QT += network bluetooth dbus
QT -= gui
DEFINES += LIBNYMEA_LIBRARY
+CONFIG += link_pkgconfig
+PKGCONFIG += nymea-zigbee
+
QMAKE_LFLAGS += -fPIC
HEADERS += \
+ hardware/zigbee/zigbeehandler.h \
+ hardware/zigbee/zigbeehardwareresource.h \
integrations/browseractioninfo.h \
integrations/browseritemactioninfo.h \
integrations/browseritemresult.h \
@@ -102,6 +107,8 @@ HEADERS += \
experiences/experienceplugin.h \
SOURCES += \
+ hardware/zigbee/zigbeehandler.cpp \
+ hardware/zigbee/zigbeehardwareresource.cpp \
integrations/browseractioninfo.cpp \
integrations/browseritemactioninfo.cpp \
integrations/browseritemresult.cpp \
diff --git a/libnymea/nymeasettings.cpp b/libnymea/nymeasettings.cpp
index f7f2324e..417c7472 100644
--- a/libnymea/nymeasettings.cpp
+++ b/libnymea/nymeasettings.cpp
@@ -122,6 +122,9 @@ NymeaSettings::NymeaSettings(const SettingsRole &role, QObject *parent):
case SettingsRoleIOConnections:
fileName = "ioconnections.conf";
break;
+ case SettingsRoleZigbee:
+ fileName = "zigbee.conf";
+ break;
}
m_settings = new QSettings(basePath + settingsPrefix + fileName, QSettings::IniFormat, this);
}
diff --git a/libnymea/nymeasettings.h b/libnymea/nymeasettings.h
index 8c810e1d..36301eb3 100644
--- a/libnymea/nymeasettings.h
+++ b/libnymea/nymeasettings.h
@@ -52,6 +52,7 @@ public:
SettingsRoleTags,
SettingsRoleMqttPolicies,
SettingsRoleIOConnections,
+ SettingsRoleZigbee,
};
explicit NymeaSettings(const SettingsRole &role = SettingsRoleNone, QObject *parent = nullptr);
diff --git a/nymea.pro b/nymea.pro
index 4a8c3cda..9f9e0cd3 100644
--- a/nymea.pro
+++ b/nymea.pro
@@ -5,7 +5,7 @@ NYMEA_VERSION_STRING=$$system('dpkg-parsechangelog | sed -n -e "s/^Version: //p"
# define protocol versions
JSON_PROTOCOL_VERSION_MAJOR=5
-JSON_PROTOCOL_VERSION_MINOR=2
+JSON_PROTOCOL_VERSION_MINOR=3
JSON_PROTOCOL_VERSION="$${JSON_PROTOCOL_VERSION_MAJOR}.$${JSON_PROTOCOL_VERSION_MINOR}"
LIBNYMEA_API_VERSION_MAJOR=7
LIBNYMEA_API_VERSION_MINOR=0
diff --git a/tests/auto/api.json b/tests/auto/api.json
index 1a25139c..41551894 100644
--- a/tests/auto/api.json
+++ b/tests/auto/api.json
@@ -1,4 +1,4 @@
-5.2
+5.3
{
"enums": {
"BasicType": [
@@ -364,6 +364,22 @@
"WirelessModeAdhoc",
"WirelessModeInfrastructure",
"WirelessModeAccessPoint"
+ ],
+ "ZigbeeError": [
+ "ZigbeeErrorNoError",
+ "ZigbeeErrorAdapterNotAvailable",
+ "ZigbeeErrorAdapterAlreadyInUse",
+ "ZigbeeErrorNetworkUuidNotFound",
+ "ZigbeeErrorDurationOutOfRange",
+ "ZigbeeErrorNetworkOffline",
+ "ZigbeeErrorUnknownBackend"
+ ],
+ "ZigbeeNetworkState": [
+ "ZigbeeNetworkStateOffline",
+ "ZigbeeNetworkStateStarting",
+ "ZigbeeNetworkStateUpdating",
+ "ZigbeeNetworkStateOnline",
+ "ZigbeeNetworkStateError"
]
},
"flags": {
@@ -1932,6 +1948,75 @@
"success": "Bool",
"transactionId": "Int"
}
+ },
+ "Zigbee.AddNetwork": {
+ "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": {
+ "backend": "String",
+ "baudRate": "Uint",
+ "serialPort": "String"
+ },
+ "returns": {
+ "o:networkUuid": "Uuid",
+ "zigbeeError": "$ref:ZigbeeError"
+ }
+ },
+ "Zigbee.FactoryResetNetwork": {
+ "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": {
+ "networkUuid": "Uuid"
+ },
+ "returns": {
+ "zigbeeError": "$ref:ZigbeeError"
+ }
+ },
+ "Zigbee.GetAdapters": {
+ "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.",
+ "params": {
+ },
+ "returns": {
+ "adapters": "$ref:ZigbeeAdapters"
+ }
+ },
+ "Zigbee.GetAvailableBackends": {
+ "description": "Get the list of available ZigBee backends.",
+ "params": {
+ },
+ "returns": {
+ "backends": [
+ "String"
+ ]
+ }
+ },
+ "Zigbee.GetNetworks": {
+ "description": "Returns the list of configured ZigBee networks in the system.",
+ "params": {
+ },
+ "returns": {
+ "zigbeeNetworks": [
+ "$ref:ZigbeeNetwork"
+ ]
+ }
+ },
+ "Zigbee.RemoveNetwork": {
+ "description": "Remove the ZigBee network with the given network uuid.",
+ "params": {
+ "networkUuid": "Uuid"
+ },
+ "returns": {
+ "zigbeeError": "$ref:ZigbeeError"
+ }
+ },
+ "Zigbee.SetPermitJoin": {
+ "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": {
+ "duration": "Uint",
+ "networkUuid": "Uuid",
+ "o:shortAddress": "Uint"
+ },
+ "returns": {
+ "zigbeeError": "$ref:ZigbeeError"
+ }
}
},
"notifications": {
@@ -2350,6 +2435,36 @@
"success": "Bool",
"transactionId": "Int"
}
+ },
+ "Zigbee.AdapterAdded": {
+ "description": "Emitted whenever a new ZigBee adapter or serial port has been detected in the system.",
+ "params": {
+ "adapter": "$ref:ZigbeeAdapter"
+ }
+ },
+ "Zigbee.AdapterRemoved": {
+ "description": "Emitted whenever a ZigBee adapter or serial port has been removed from the system (i.e. unplugged).",
+ "params": {
+ "adapter": "$ref:ZigbeeAdapter"
+ }
+ },
+ "Zigbee.NetworkAdded": {
+ "description": "Emitted whenever a new ZigBee network has been added.",
+ "params": {
+ "zigbeeNetwork": "$ref:ZigbeeNetwork"
+ }
+ },
+ "Zigbee.NetworkChanged": {
+ "description": "Emitted whenever a new ZigBee network has changed.",
+ "params": {
+ "zigbeeNetwork": "$ref:ZigbeeNetwork"
+ }
+ },
+ "Zigbee.NetworkRemoved": {
+ "description": "Emitted whenever a new ZigBee network has been removed.",
+ "params": {
+ "networkUuid": "Uuid"
+ }
}
},
"types": {
@@ -2819,6 +2934,34 @@
"r:mode": "$ref:WirelessMode",
"r:o:currentAccessPoint": "$ref:WirelessAccessPoint",
"r:state": "$ref:NetworkDeviceState"
+ },
+ "ZigbeeAdapter": {
+ "r:backend": "String",
+ "r:baudRate": "Int",
+ "r:description": "String",
+ "r:hardwareRecognized": "Bool",
+ "r:name": "String",
+ "r:serialNumber": "String",
+ "r:serialPort": "String"
+ },
+ "ZigbeeAdapters": [
+ "$ref:ZigbeeAdapter"
+ ],
+ "ZigbeeNetwork": {
+ "backend": "String",
+ "baudRate": "Uint",
+ "channel": "Uint",
+ "channelMask": "Uint",
+ "enabled": "Bool",
+ "firmwareVersion": "String",
+ "macAddress": "String",
+ "networkState": "$ref:ZigbeeNetworkState",
+ "networkUuid": "Uuid",
+ "panId": "Uint",
+ "permitJoiningDuration": "Uint",
+ "permitJoiningEnabled": "Bool",
+ "permitJoiningRemaining": "Uint",
+ "serialPort": "String"
}
}
}
diff --git a/tests/auto/jsonrpc/testjsonrpc.cpp b/tests/auto/jsonrpc/testjsonrpc.cpp
index 7fddb182..b5c33cec 100644
--- a/tests/auto/jsonrpc/testjsonrpc.cpp
+++ b/tests/auto/jsonrpc/testjsonrpc.cpp
@@ -674,7 +674,7 @@ void TestJSONRPC::enableDisableNotifications_legacy()
QStringList expectedNamespaces;
if (enabled == "true") {
- expectedNamespaces << "Actions" << "NetworkManager" << "Devices" << "Integrations" << "System" << "Rules" << "States" << "Logging" << "Tags" << "JSONRPC" << "Configuration" << "Events" << "Scripts" << "Users";
+ expectedNamespaces << "Actions" << "NetworkManager" << "Devices" << "Integrations" << "System" << "Rules" << "States" << "Logging" << "Tags" << "JSONRPC" << "Configuration" << "Events" << "Scripts" << "Users" << "Zigbee";
}
std::sort(expectedNamespaces.begin(), expectedNamespaces.end());