diff --git a/avahimonitor/devicepluginavahimonitor.cpp b/avahimonitor/devicepluginavahimonitor.cpp index b61ba603..d81dc4c5 100644 --- a/avahimonitor/devicepluginavahimonitor.cpp +++ b/avahimonitor/devicepluginavahimonitor.cpp @@ -44,6 +44,7 @@ #include "plugin/device.h" #include "devicemanager.h" #include "plugininfo.h" +#include "network/avahi/qtavahiservicebrowser.h" #include #include @@ -56,8 +57,8 @@ DevicePluginAvahiMonitor::DevicePluginAvahiMonitor() void DevicePluginAvahiMonitor::init() { - connect(avahiServiceBrowser(), SIGNAL(serviceEntryAdded(AvahiServiceEntry)), this, SLOT(onServiceEntryAdded(AvahiServiceEntry))); - connect(avahiServiceBrowser(), SIGNAL(serviceEntryRemoved(AvahiServiceEntry)), this, SLOT(onServiceEntryRemoved(AvahiServiceEntry))); + connect(hardwareManager()->avahiBrowser(), &QtAvahiServiceBrowser::serviceEntryAdded, this, &DevicePluginAvahiMonitor::onServiceEntryAdded); + connect(hardwareManager()->avahiBrowser(), &QtAvahiServiceBrowser::serviceEntryRemoved, this, &DevicePluginAvahiMonitor::onServiceEntryRemoved); } DeviceManager::DeviceSetupStatus DevicePluginAvahiMonitor::setupDevice(Device *device) @@ -75,7 +76,7 @@ DeviceManager::DeviceError DevicePluginAvahiMonitor::discoverDevices(const Devic return DeviceManager::DeviceErrorDeviceClassNotFound; QList deviceDescriptors; - foreach (const AvahiServiceEntry &service, avahiServiceBrowser()->serviceEntries()) { + foreach (const AvahiServiceEntry &service, hardwareManager()->avahiBrowser()->serviceEntries()) { DeviceDescriptor deviceDescriptor(avahiDeviceClassId, service.name(), service.hostAddress().toString()); ParamList params; params.append(Param(serviceParamTypeId, service.name())); @@ -89,11 +90,6 @@ DeviceManager::DeviceError DevicePluginAvahiMonitor::discoverDevices(const Devic return DeviceManager::DeviceErrorAsync; } -DeviceManager::HardwareResources DevicePluginAvahiMonitor::requiredHardware() const -{ - return DeviceManager::HardwareResourceNone; -} - void DevicePluginAvahiMonitor::onServiceEntryAdded(const AvahiServiceEntry &serviceEntry) { foreach (Device *device, myDevices()) { diff --git a/avahimonitor/devicepluginavahimonitor.h b/avahimonitor/devicepluginavahimonitor.h index a68e34de..b4ce50c0 100644 --- a/avahimonitor/devicepluginavahimonitor.h +++ b/avahimonitor/devicepluginavahimonitor.h @@ -25,6 +25,7 @@ #define DEVICEPLUGINAVAHIMONITOR_H #include "plugin/deviceplugin.h" +#include "network/avahi/avahiserviceentry.h" #include @@ -42,7 +43,6 @@ public: DeviceManager::DeviceSetupStatus setupDevice(Device *device) override; DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override; - DeviceManager::HardwareResources requiredHardware() const override; private slots: void onServiceEntryAdded(const AvahiServiceEntry &serviceEntry); diff --git a/awattar/devicepluginawattar.cpp b/awattar/devicepluginawattar.cpp index 1eea4607..71cb67f8 100644 --- a/awattar/devicepluginawattar.cpp +++ b/awattar/devicepluginawattar.cpp @@ -69,6 +69,8 @@ #include "devicepluginawattar.h" #include "plugin/device.h" #include "plugininfo.h" +#include "hardwaremanager.h" +#include "network/networkaccessmanager.h" #include #include @@ -78,9 +80,15 @@ DevicePluginAwattar::DevicePluginAwattar() { } -DeviceManager::HardwareResources DevicePluginAwattar::requiredHardware() const +DevicePluginAwattar::~DevicePluginAwattar() { - return DeviceManager::HardwareResourceNetworkManager | DeviceManager::HardwareResourceTimer; + hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer); +} + +void DevicePluginAwattar::init() +{ + m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(15); + connect(m_pluginTimer, &PluginTimer::timeout, this, &DevicePluginAwattar::onPluginTimer); } DeviceManager::DeviceSetupStatus DevicePluginAwattar::setupDevice(Device *device) @@ -119,85 +127,6 @@ void DevicePluginAwattar::deviceRemoved(Device *device) m_device = 0; } -void DevicePluginAwattar::networkManagerReplyReady(QNetworkReply *reply) -{ - int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - if (m_updatePrice.contains(reply)) { - m_updatePrice.removeAll(reply); - - // check HTTP status code - if (status != 200) { - qCWarning(dcAwattar) << "Update reply HTTP error:" << status << reply->errorString(); - setOnlineStatus(false); - reply->deleteLater(); - return; - } - - // check JSON file - QJsonParseError error; - QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll(), &error); - if (error.error != QJsonParseError::NoError) { - qCWarning(dcAwattar) << "Update reply JSON error:" << error.errorString(); - setOnlineStatus(false); - reply->deleteLater(); - return; - } - - processPriceData(jsonDoc.toVariant().toMap()); - - // start user data update - m_updateUserData.append(requestUserData(m_token, m_userUuid)); - - } else if (m_updateUserData.contains(reply)) { - m_updateUserData.removeAll(reply); - - // check HTTP status code - if (status != 200) { - qCWarning(dcAwattar) << "Update user data reply HTTP error:" << status << reply->errorString(); - setOnlineStatus(false); - reply->deleteLater(); - return; - } - - // check JSON file - QJsonParseError error; - QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll(), &error); - if (error.error != QJsonParseError::NoError) { - qCWarning(dcAwattar) << "Update user data reply JSON error:" << error.errorString(); - setOnlineStatus(false); - reply->deleteLater(); - return; - } - - setOnlineStatus(true); - processUserData(jsonDoc.toVariant().toMap()); - - } else if (m_searchPumpReplies.contains(reply)) { - m_searchPumpReplies.removeAll(reply); - - // check HTTP status code - if (status != 200) { - qCWarning(dcAwattar) << "Search pump reply HTTP error:" << status << reply->errorString(); - reply->deleteLater(); - return; - } - - processPumpSearchData(reply->readAll()); - } - - reply->deleteLater(); -} - -void DevicePluginAwattar::guhTimer() -{ - if (m_device.isNull()) - return; - - updateData(); - searchHeatPumps(); -} - DeviceManager::DeviceError DevicePluginAwattar::executeAction(Device *device, const Action &action) { if (m_device.isNull() || m_device != device) @@ -244,7 +173,9 @@ QNetworkReply *DevicePluginAwattar::requestPriceData(const QString &token) QNetworkRequest request(QUrl("https://api.awattar.com/v1/marketdata")); request.setRawHeader("Authorization", header.toLocal8Bit()); request.setSslConfiguration(QSslConfiguration::defaultConfiguration()); - return networkManagerGet(request); + QNetworkReply *reply = hardwareManager()->networkManager()->get(request); + connect(reply, &QNetworkReply::finished, this, &DevicePluginAwattar::onNetworkReplyFinished); + return reply; } QNetworkReply *DevicePluginAwattar::requestUserData(const QString &token, const QString &userId) @@ -255,7 +186,9 @@ QNetworkReply *DevicePluginAwattar::requestUserData(const QString &token, const QNetworkRequest request(QUrl(QString("https://api.awattar.com/v1/devices/%1/actuators").arg(userId))); request.setRawHeader("Authorization", header.toLocal8Bit()); request.setSslConfiguration(QSslConfiguration::defaultConfiguration()); - return networkManagerGet(request); + QNetworkReply *reply = hardwareManager()->networkManager()->get(request); + connect(reply, &QNetworkReply::finished, this, &DevicePluginAwattar::onNetworkReplyFinished); + return reply; } void DevicePluginAwattar::updateData() @@ -273,8 +206,8 @@ void DevicePluginAwattar::searchHeatPumps() } QNetworkRequest request(QUrl(QString("http://[%1]").arg(rplAddress.toString()))); - QNetworkReply *reply = networkManagerGet(request); - + QNetworkReply *reply = hardwareManager()->networkManager()->get(request); + connect(reply, &QNetworkReply::finished, this, &DevicePluginAwattar::onNetworkReplyFinished); m_searchPumpReplies.append(reply); } @@ -452,6 +385,86 @@ bool DevicePluginAwattar::heatPumpExists(const QHostAddress &pumpAddress) return false; } +void DevicePluginAwattar::onPluginTimer() +{ + if (m_device.isNull()) + return; + + updateData(); + searchHeatPumps(); +} + +void DevicePluginAwattar::onNetworkReplyFinished() +{ + QNetworkReply *reply = static_cast(sender()); + int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + if (m_updatePrice.contains(reply)) { + m_updatePrice.removeAll(reply); + + // check HTTP status code + if (status != 200) { + qCWarning(dcAwattar) << "Update reply HTTP error:" << status << reply->errorString(); + setOnlineStatus(false); + reply->deleteLater(); + return; + } + + // check JSON file + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll(), &error); + if (error.error != QJsonParseError::NoError) { + qCWarning(dcAwattar) << "Update reply JSON error:" << error.errorString(); + setOnlineStatus(false); + reply->deleteLater(); + return; + } + + processPriceData(jsonDoc.toVariant().toMap()); + + // start user data update + m_updateUserData.append(requestUserData(m_token, m_userUuid)); + + } else if (m_updateUserData.contains(reply)) { + m_updateUserData.removeAll(reply); + + // check HTTP status code + if (status != 200) { + qCWarning(dcAwattar) << "Update user data reply HTTP error:" << status << reply->errorString(); + setOnlineStatus(false); + reply->deleteLater(); + return; + } + + // check JSON file + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll(), &error); + if (error.error != QJsonParseError::NoError) { + qCWarning(dcAwattar) << "Update user data reply JSON error:" << error.errorString(); + setOnlineStatus(false); + reply->deleteLater(); + return; + } + + setOnlineStatus(true); + processUserData(jsonDoc.toVariant().toMap()); + + } else if (m_searchPumpReplies.contains(reply)) { + m_searchPumpReplies.removeAll(reply); + + // check HTTP status code + if (status != 200) { + qCWarning(dcAwattar) << "Search pump reply HTTP error:" << status << reply->errorString(); + reply->deleteLater(); + return; + } + + processPumpSearchData(reply->readAll()); + } + + reply->deleteLater(); +} + void DevicePluginAwattar::onHeatPumpReachableChanged() { HeatPump *pump = static_cast(sender()); diff --git a/awattar/devicepluginawattar.h b/awattar/devicepluginawattar.h index e485f662..a13aa6ce 100644 --- a/awattar/devicepluginawattar.h +++ b/awattar/devicepluginawattar.h @@ -24,6 +24,8 @@ #define DEVICEPLUGINAWATTAR_H #include "plugin/deviceplugin.h" +#include "plugintimer.h" + #include "heatpump.h" #include @@ -39,17 +41,16 @@ class DevicePluginAwattar : public DevicePlugin public: explicit DevicePluginAwattar(); + ~DevicePluginAwattar(); - DeviceManager::HardwareResources requiredHardware() const override; + void init() override; DeviceManager::DeviceSetupStatus setupDevice(Device *device) override; void postSetupDevice(Device *device) override; void deviceRemoved(Device *device) override; - void networkManagerReplyReady(QNetworkReply *reply) override; - - void guhTimer() override; DeviceManager::DeviceError executeAction(Device *device, const Action &action) override; private: + PluginTimer *m_pluginTimer = nullptr; QPointer m_device; QList > m_heatPumps; @@ -63,7 +64,6 @@ private: int m_autoSgMode; int m_manualSgMode; - QNetworkReply *requestPriceData(const QString& token); QNetworkReply *requestUserData(const QString& token, const QString &userId); @@ -79,6 +79,8 @@ private: bool heatPumpExists(const QHostAddress &pumpAddress); private slots: + void onPluginTimer(); + void onNetworkReplyFinished(); void onHeatPumpReachableChanged(); }; diff --git a/commandlauncher/deviceplugincommandlauncher.cpp b/commandlauncher/deviceplugincommandlauncher.cpp index 7656b8ea..9a29d809 100644 --- a/commandlauncher/deviceplugincommandlauncher.cpp +++ b/commandlauncher/deviceplugincommandlauncher.cpp @@ -106,6 +106,7 @@ DevicePluginCommandLauncher::DevicePluginCommandLauncher() { + } DeviceManager::DeviceSetupStatus DevicePluginCommandLauncher::setupDevice(Device *device) @@ -137,11 +138,6 @@ DeviceManager::DeviceSetupStatus DevicePluginCommandLauncher::setupDevice(Device return DeviceManager::DeviceSetupStatusFailure; } -DeviceManager::HardwareResources DevicePluginCommandLauncher::requiredHardware() const -{ - return DeviceManager::HardwareResourceNone; -} - DeviceManager::DeviceError DevicePluginCommandLauncher::executeAction(Device *device, const Action &action) { // Application diff --git a/commandlauncher/deviceplugincommandlauncher.h b/commandlauncher/deviceplugincommandlauncher.h index 99646872..fee4e3e2 100644 --- a/commandlauncher/deviceplugincommandlauncher.h +++ b/commandlauncher/deviceplugincommandlauncher.h @@ -24,6 +24,7 @@ #define DEVICEPLUGINCOMMANDLAUNCHER_H #include "plugin/deviceplugin.h" + #include #include @@ -38,7 +39,6 @@ public: explicit DevicePluginCommandLauncher(); DeviceManager::DeviceSetupStatus setupDevice(Device *device) override; - DeviceManager::HardwareResources requiredHardware() const override; DeviceManager::DeviceError executeAction(Device *device, const Action &action) override; void deviceRemoved(Device *device) override; diff --git a/conrad/devicepluginconrad.cpp b/conrad/devicepluginconrad.cpp index 5e51b200..f691ead1 100644 --- a/conrad/devicepluginconrad.cpp +++ b/conrad/devicepluginconrad.cpp @@ -46,6 +46,7 @@ #include "plugin/device.h" #include "devicemanager.h" #include "plugininfo.h" +#include "hardware/radio433/radio433.h" #include #include @@ -53,11 +54,7 @@ DevicePluginConrad::DevicePluginConrad() { -} -DeviceManager::HardwareResources DevicePluginConrad::requiredHardware() const -{ - return DeviceManager::HardwareResourceRadio433; } DeviceManager::DeviceSetupStatus DevicePluginConrad::setupDevice(Device *device) @@ -71,6 +68,10 @@ DeviceManager::DeviceSetupStatus DevicePluginConrad::setupDevice(Device *device) DeviceManager::DeviceError DevicePluginConrad::executeAction(Device *device, const Action &action) { + if (!hardwareManager()->radio433()->available()) { + return DeviceManager::DeviceErrorHardwareNotAvailable; + } + QList rawData; QByteArray binCode; @@ -119,13 +120,13 @@ DeviceManager::DeviceError DevicePluginConrad::executeAction(Device *device, con // ======================================= // send data to driver - if(transmitData(delay, rawData, repetitions)){ + if(hardwareManager()->radio433()->sendData(delay, rawData, repetitions)){ qCDebug(dcConrad) << "Transmitted successfully" << device->name() << action.actionTypeId(); - return DeviceManager::DeviceErrorNoError; }else{ qCWarning(dcConrad) << "Could not transmitt" << pluginName() << device->name() << action.actionTypeId(); return DeviceManager::DeviceErrorHardwareNotAvailable; } + return DeviceManager::DeviceErrorNoError; } void DevicePluginConrad::radioData(const QList &rawData) diff --git a/conrad/devicepluginconrad.h b/conrad/devicepluginconrad.h index ef31c23c..79412ede 100644 --- a/conrad/devicepluginconrad.h +++ b/conrad/devicepluginconrad.h @@ -36,9 +36,8 @@ class DevicePluginConrad : public DevicePlugin public: explicit DevicePluginConrad(); - DeviceManager::HardwareResources requiredHardware() const override; DeviceManager::DeviceSetupStatus setupDevice(Device *device) override; - void radioData(const QList &rawData) override; + void radioData(const QList &rawData); public slots: diff --git a/datetime/deviceplugindatetime.cpp b/datetime/deviceplugindatetime.cpp index 91b2f507..3e986b9c 100644 --- a/datetime/deviceplugindatetime.cpp +++ b/datetime/deviceplugindatetime.cpp @@ -96,6 +96,8 @@ #include "plugin/device.h" #include "devicemanager.h" #include "plugininfo.h" +#include "hardwaremanager.h" +#include "network/networkaccessmanager.h" #include #include @@ -118,11 +120,6 @@ DevicePluginDateTime::DevicePluginDateTime() : connect(m_timer, &QTimer::timeout, this, &DevicePluginDateTime::onSecondChanged); } -DeviceManager::HardwareResources DevicePluginDateTime::requiredHardware() const -{ - return DeviceManager::HardwareResourceNetworkManager; -} - DeviceManager::DeviceSetupStatus DevicePluginDateTime::setupDevice(Device *device) { // check timezone @@ -243,28 +240,6 @@ DeviceManager::DeviceError DevicePluginDateTime::executeAction(Device *device, c return DeviceManager::DeviceErrorNoError; } -void DevicePluginDateTime::networkManagerReplyReady(QNetworkReply *reply) -{ - int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - if (m_locationReplies.contains(reply)) { - m_locationReplies.removeAll(reply); - if (status != 200) { - qCWarning(dcDateTime) << "Http error status for location request:" << status << reply->error(); - } else { - processGeoLocationData(reply->readAll()); - } - } else if (m_timeReplies.contains(reply)) { - m_timeReplies.removeAll(reply); - if (status != 200) { - qCWarning(dcDateTime) << "Http error status for time request:" << status << reply->error(); - } else { - processTimesData(reply->readAll()); - } - } - reply->deleteLater(); -} - void DevicePluginDateTime::startMonitoringAutoDevices() { // foreach (Device *device, myDevices()) { @@ -285,9 +260,10 @@ void DevicePluginDateTime::searchGeoLocation() QNetworkRequest request; request.setUrl(QUrl("http://ip-api.com/json")); - qCDebug(dcDateTime) << "Requesting geo location."; + qCDebug(dcDateTime()) << "Requesting geo location."; - QNetworkReply *reply = networkManagerGet(request); + QNetworkReply *reply = hardwareManager()->networkManager()->get(request); + connect(reply, &QNetworkReply::finished, this, &DevicePluginDateTime::onNetworkReplayFinished); m_locationReplies.append(reply); } @@ -340,7 +316,9 @@ void DevicePluginDateTime::getTimes(const QString &latitude, const QString &long QNetworkRequest request; request.setUrl(url); - QNetworkReply *reply = networkManagerGet(request); + QNetworkReply *reply = hardwareManager()->networkManager()->get(request); + connect(reply, &QNetworkReply::finished, this, &DevicePluginDateTime::onNetworkReplayFinished); + m_timeReplies.append(reply); } @@ -385,6 +363,30 @@ void DevicePluginDateTime::processTimesData(const QByteArray &data) updateTimes(); } +void DevicePluginDateTime::onNetworkReplayFinished() +{ + QNetworkReply *reply = static_cast(sender()); + + int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + if (m_locationReplies.contains(reply)) { + m_locationReplies.removeAll(reply); + if (status != 200) { + qCWarning(dcDateTime) << "Http error status for location request:" << status << reply->error(); + } else { + processGeoLocationData(reply->readAll()); + } + } else if (m_timeReplies.contains(reply)) { + m_timeReplies.removeAll(reply); + if (status != 200) { + qCWarning(dcDateTime) << "Http error status for time request:" << status << reply->error(); + } else { + processTimesData(reply->readAll()); + } + } + reply->deleteLater(); +} + void DevicePluginDateTime::onAlarm() { Alarm *alarm = static_cast(sender()); diff --git a/datetime/deviceplugindatetime.h b/datetime/deviceplugindatetime.h index b609ada4..bad2f824 100644 --- a/datetime/deviceplugindatetime.h +++ b/datetime/deviceplugindatetime.h @@ -42,13 +42,11 @@ class DevicePluginDateTime : public DevicePlugin public: explicit DevicePluginDateTime(); - DeviceManager::HardwareResources requiredHardware() const override; DeviceManager::DeviceSetupStatus setupDevice(Device *device) override; void postSetupDevice(Device *device) override; void deviceRemoved(Device *device) override; DeviceManager::DeviceError executeAction(Device *device, const Action &action) override; - void networkManagerReplyReady(QNetworkReply *reply) override; void startMonitoringAutoDevices() override; @@ -84,6 +82,7 @@ signals: void dawn(); private slots: + void onNetworkReplayFinished(); void onAlarm(); void onCountdownTimeout(); void onCountdownRunningChanged(const bool &running); diff --git a/debian/control b/debian/control index 3d867fe3..ec04855d 100644 --- a/debian/control +++ b/debian/control @@ -102,21 +102,6 @@ Description: guh.io plugin for denon This package will install the guh.io plugin for denon -Package: guh-plugin-dollhouse -Architecture: any -Depends: ${shlibs:Depends}, - ${misc:Depends}, - guh-plugins-translations, -Description: guh.io plugin for dollhouse - The guh daemon is a plugin based IoT (Internet of Things) server. The - server works like a translator for devices, things and services and - allows them to interact. - With the powerful rule engine you are able to connect any device available - in the system and create individual scenes and behaviors for your environment. - . - This package will install the guh.io plugin for dollhouse - - Package: guh-plugin-elgato Architecture: any Depends: ${shlibs:Depends}, @@ -252,21 +237,6 @@ Description: guh.io plugin for lgsmarttv This package will install the guh.io plugin for lgsmarttv -Package: guh-plugin-lircd -Architecture: any -Depends: ${shlibs:Depends}, - ${misc:Depends}, - guh-plugins-translations, -Description: guh.io plugin for lircd - The guh daemon is a plugin based IoT (Internet of Things) server. The - server works like a translator for devices, things and services and - allows them to interact. - With the powerful rule engine you are able to connect any device available - in the system and create individual scenes and behaviors for your environment. - . - This package will install the guh.io plugin for lircd - - Package: guh-plugin-mailnotification Architecture: any Depends: ${shlibs:Depends}, @@ -536,8 +506,7 @@ Description: Plugins for guh IoT server - the default plugin collection Package: guh-plugins-maker Section: libs Architecture: all -Depends: guh-plugin-lircd, - guh-plugin-commandlauncher, +Depends: guh-plugin-commandlauncher, guh-plugin-udpcommander, guh-plugin-tcpcommander, guh-plugin-genericelements, @@ -557,7 +526,6 @@ Package: guh-plugins-merkurboard Section: libs Architecture: all Depends: guh-plugin-osdomotics, - guh-plugin-dollhouse, guh-plugin-plantcare, guh-plugin-ws2812, guh-plugin-orderbutton, diff --git a/debian/guh-plugin-dollhouse.install.in b/debian/guh-plugin-dollhouse.install.in deleted file mode 100644 index 4ba44bd8..00000000 --- a/debian/guh-plugin-dollhouse.install.in +++ /dev/null @@ -1 +0,0 @@ -usr/lib/@DEB_HOST_MULTIARCH@/guh/plugins/libguh_deviceplugindollhouse.so \ No newline at end of file diff --git a/debian/guh-plugin-lircd.install.in b/debian/guh-plugin-lircd.install.in deleted file mode 100644 index 7b808760..00000000 --- a/debian/guh-plugin-lircd.install.in +++ /dev/null @@ -1 +0,0 @@ -usr/lib/@DEB_HOST_MULTIARCH@/guh/plugins/libguh_devicepluginlircd.so \ No newline at end of file diff --git a/denon/deviceplugindenon.cpp b/denon/deviceplugindenon.cpp index d4e4434d..edb69d22 100644 --- a/denon/deviceplugindenon.cpp +++ b/denon/deviceplugindenon.cpp @@ -51,9 +51,15 @@ DevicePluginDenon::DevicePluginDenon() } -DeviceManager::HardwareResources DevicePluginDenon::requiredHardware() const +DevicePluginDenon::~DevicePluginDenon() { - return DeviceManager::HardwareResourceTimer; + hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer); +} + +void DevicePluginDenon::init() +{ + m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(15); + connect(m_pluginTimer, &PluginTimer::timeout, this, &DevicePluginDenon::onPluginTimer); } DeviceManager::DeviceSetupStatus DevicePluginDenon::setupDevice(Device *device) @@ -96,19 +102,6 @@ void DevicePluginDenon::deviceRemoved(Device *device) m_denonConnection->deleteLater(); } - -void DevicePluginDenon::guhTimer() -{ - if (m_denonConnection.isNull()) - return; - - if (!m_denonConnection->connected()) { - m_denonConnection->connectDenon(); - } else { - m_denonConnection->sendData("PW?\rSI?\rMV?\r"); - } -} - DeviceManager::DeviceError DevicePluginDenon::executeAction(Device *device, const Action &action) { qCDebug(dcDenon) << "Execute action" << device->id() << action.id() << action.params(); @@ -163,6 +156,18 @@ DeviceManager::DeviceError DevicePluginDenon::executeAction(Device *device, cons return DeviceManager::DeviceErrorDeviceClassNotFound; } +void DevicePluginDenon::onPluginTimer() +{ + if (m_denonConnection.isNull()) + return; + + if (!m_denonConnection->connected()) { + m_denonConnection->connectDenon(); + } else { + m_denonConnection->sendData("PW?\rSI?\rMV?\r"); + } +} + void DevicePluginDenon::onConnectionChanged() { if (!m_device) diff --git a/denon/deviceplugindenon.h b/denon/deviceplugindenon.h index 079a226a..bdfc2fc0 100644 --- a/denon/deviceplugindenon.h +++ b/denon/deviceplugindenon.h @@ -34,6 +34,7 @@ #include #include +#include "plugintimer.h" #include "denonconnection.h" class DevicePluginDenon : public DevicePlugin @@ -45,15 +46,15 @@ class DevicePluginDenon : public DevicePlugin public: explicit DevicePluginDenon(); + ~DevicePluginDenon(); - DeviceManager::HardwareResources requiredHardware() const override; + void init() override; DeviceManager::DeviceSetupStatus setupDevice(Device *device) override; void deviceRemoved(Device *device) override; - DeviceManager::DeviceError executeAction(Device *device, const Action &action) override; - void guhTimer() override; private: + PluginTimer *m_pluginTimer = nullptr; QPointer m_device; QPointer m_denonConnection; QList m_asyncSetups; @@ -62,6 +63,7 @@ private: QHash m_asyncActionReplies; private slots: + void onPluginTimer(); void onConnectionChanged(); void onDataReceived(const QByteArray &data); void onSocketError(); diff --git a/dollhouse/deviceplugindollhouse.cpp b/dollhouse/deviceplugindollhouse.cpp deleted file mode 100644 index 05d9e092..00000000 --- a/dollhouse/deviceplugindollhouse.cpp +++ /dev/null @@ -1,396 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * * - * Copyright (C) 2016 Simon Stürz * - * * - * This file is part of guh. * - * * - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Lesser General Public * - * License as published by the Free Software Foundation; either * - * version 2.1 of the License, or (at your option) any later version. * - * * - * This library 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 library; If not, see * - * . * - * * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -/*! - \page dollhouse.html - \title Dollhouse - \brief Plugin for a guh demo booth based on 6LoWPAN networking. - - \ingroup plugins - \ingroup guh-plugins-merkur - - The plugin for the guh-dollhouse demo booth. - - \chapter Plugin properties - Following JSON file contains the definition and the description of all available \l{DeviceClass}{DeviceClasses} - and \l{Vendor}{Vendors} of this \l{DevicePlugin}. - - For more details how to read this JSON file please check out the documentation for \l{The plugin JSON File}. - - \quotefile plugins/deviceplugins/dollhouse/deviceplugindollhouse.json -*/ - -#include "deviceplugindollhouse.h" -#include "plugininfo.h" - -#include - -DevicePluginDollHouse::DevicePluginDollHouse() : - m_houseReachable(false) -{ - m_coap = new Coap(this); - connect(m_coap, SIGNAL(replyFinished(CoapReply*)), this, SLOT(coapReplyFinished(CoapReply*))); -} - -DeviceManager::HardwareResources DevicePluginDollHouse::requiredHardware() const -{ - return DeviceManager::HardwareResourceNetworkManager | DeviceManager::HardwareResourceTimer; -} - -DeviceManager::DeviceSetupStatus DevicePluginDollHouse::setupDevice(Device *device) -{ - qCDebug(dcDollhouse) << "Setup" << device->name() << device->params(); - - if (device->deviceClassId() == connectionDeviceClassId) { - - foreach (Device *device, myDevices()) { - if (device->deviceClassId() == connectionDeviceClassId) { - qCWarning(dcDollhouse) << "Dollhouse connection allready configured."; - return DeviceManager::DeviceSetupStatusFailure; - } - } - - int lookupId = QHostInfo::lookupHost(device->paramValue(rplParamTypeId).toString(), this, SLOT(hostLockupFinished(QHostInfo))); - m_asyncSetup.insert(lookupId, device); - - return DeviceManager::DeviceSetupStatusAsync; - - } else if (device->deviceClassId() == lightDeviceClassId) { - - DollhouseLight *light = new DollhouseLight(this); - light->setName(device->paramValue(nameParamTypeId).toString()); - light->setHostAddress(device->paramValue(addressParamTypeId).toString()); - light->setConnectionUuid(device->paramValue(connectionUuidParamTypeId).toString()); - light->setLightId(device->paramValue(lightIdParamTypeId).toInt()); - - device->setParentId(DeviceId(light->connectionUuid())); - - m_lights.insert(device, light); - - return DeviceManager::DeviceSetupStatusSuccess; - } - - return DeviceManager::DeviceSetupStatusFailure; -} - - -void DevicePluginDollHouse::deviceRemoved(Device *device) -{ - if (device->deviceClassId() == lightDeviceClassId) { - DollhouseLight *light = m_lights.take(device); - light->deleteLater(); - } - - m_houseAddress.clear(); - m_houseReachable = false; -} - -void DevicePluginDollHouse::guhTimer() -{ - foreach (Device *device, myDevices()) { - if (device->deviceClassId() == connectionDeviceClassId && m_houseAddress.isNull()) { - scanNodes(device); - } - } - - if (!m_houseAddress.isNull()) { - QUrl url; - url.setScheme("coap"); - url.setHost(m_houseAddress.toString()); - url.setPort(5683); - url.setPath("/a/ws2812"); - m_asyncPings.append(m_coap->ping(CoapRequest(url))); - } - -} - -DeviceManager::DeviceError DevicePluginDollHouse::executeAction(Device *device, const Action &action) -{ - if (device->deviceClassId() == lightDeviceClassId) { - if (!device->stateValue(reachableStateTypeId).toBool()) - return DeviceManager::DeviceErrorHardwareNotAvailable; - - DollhouseLight *light = m_lights.value(device); - - // Create URL for action - QUrlQuery query; - query.addQueryItem("number", QString::number(light->lightId())); - - QUrl url; - url.setScheme("coap"); - url.setHost(device->paramValue(addressParamTypeId).toString()); - url.setPath("/a/ws2812"); - url.setQuery(query); - - if (action.actionTypeId() == colorActionTypeId) { - QColor color = action.param(colorStateParamTypeId).value().value().toHsv(); - QColor newColor = QColor::fromHsv(color.hue(), color.saturation(), 100 * light->brightness() / 255.0); - QByteArray message = "color=" + newColor.toRgb().name().remove("#").toUtf8(); - - qCDebug(dcDollhouse) << "Sending" << url.toString() << message; - - CoapReply *reply = m_coap->post(CoapRequest(url), message); - m_asyncActions.insert(reply, action); - m_asyncActionLights.insert(action.id(), light); - - return DeviceManager::DeviceErrorAsync; - - } else if (action.actionTypeId() == powerActionTypeId) { - - QByteArray message; - if (action.param(powerStateParamTypeId).value().toBool()) { - QColor color = light->color().toHsv(); - QColor newColor = QColor::fromHsv(color.hue(), color.saturation(), 100 * light->brightness() / 255.0); - message = "color=" + newColor.toRgb().name().remove("#").toUtf8(); - } else { - message.append("color=000000"); - } - - qCDebug(dcDollhouse) << "Sending" << url.toString() << message; - - CoapReply *reply = m_coap->post(CoapRequest(url), message); - m_asyncActions.insert(reply, action); - m_asyncActionLights.insert(action.id(), light); - - return DeviceManager::DeviceErrorAsync; - } else if (action.actionTypeId() == brightnessActionTypeId) { - - int brightness = action.param(brightnessStateParamTypeId).value().toInt(); - - QColor color = light->color().toHsv(); - QColor newColor = QColor::fromHsv(color.hue(), color.saturation(), 100 * brightness / 255.0); - - QByteArray message = "color=" + newColor.toRgb().name().remove("#").toUtf8(); - - qCDebug(dcDollhouse) << "Sending" << url.toString() << message; - - CoapReply *reply = m_coap->post(CoapRequest(url), message); - m_asyncActions.insert(reply, action); - m_asyncActionLights.insert(action.id(), light); - - return DeviceManager::DeviceErrorAsync; - } - } - - return DeviceManager::DeviceErrorNoError; -} - -void DevicePluginDollHouse::networkManagerReplyReady(QNetworkReply *reply) -{ - int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - // create user finished - if (m_asyncNodeScan.contains(reply)) { - Device *device = m_asyncNodeScan.take(reply); - - // check HTTP status code - if (status != 200) { - qCWarning(dcDollhouse) << "Node scan reply HTTP error:" << reply->errorString(); - reply->deleteLater(); - return; - } - - parseNode(device, reply->readAll()); - } - reply->deleteLater(); -} - -void DevicePluginDollHouse::scanNodes(Device *device) -{ - QUrl url; - url.setScheme("http"); - url.setHost(device->paramValue(rplParamTypeId).toString()); - - QNetworkReply *reply = networkManagerGet(QNetworkRequest(url)); - m_asyncNodeScan.insert(reply, device); -} - -void DevicePluginDollHouse::parseNode(Device *device, const QByteArray &data) -{ - - QList lines = data.split('\n'); - QList addresses; - foreach (const QByteArray &line, lines) { - if (line.isEmpty()) - continue; - - // remove the '/128' from the address - QHostAddress address(QString(data.left(line.length() - 4))); - - if (!address.isNull()) - addresses.append(address); - - } - - // int index = data.indexOf("Routes
") + 11;
-    //    int delta = data.indexOf("/128",index);
-    //    QHostAddress houseAddress = QHostAddress(QString(data.mid(index, delta - index)));
-
-    if (addresses.isEmpty())
-        return;
-
-    QHostAddress houseAddress = addresses.first();
-    if (houseAddress != m_houseAddress && !houseAddress.isNull()) {
-        m_houseAddress = houseAddress;
-        qCDebug(dcDollhouse) << "Found house at" << m_houseAddress.toString();
-
-        if (!m_lights.isEmpty())
-            return;
-
-        QList deviceDescriptorList;
-
-        for (int i = 0; i < 5; i++) {
-            DeviceDescriptor descriptor(lightDeviceClassId, "Light", QString::number(i));
-            ParamList params;
-            params.append(Param(addressParamTypeId, m_houseAddress.toString()));
-            params.append(Param(lightIdParamTypeId, i));
-            params.append(Param(connectionUuidParamTypeId, device->id()));
-
-            switch (i) {
-            case 0:
-                params.append(Param(nameParamTypeId, "Living room"));
-                break;
-            case 1:
-                params.append(Param(nameParamTypeId, "Kitchen"));
-                break;
-            case 2:
-                params.append(Param(nameParamTypeId, "Under the bed"));
-                break;
-            case 3:
-                params.append(Param(nameParamTypeId, "Bedroom"));
-                break;
-            case 4:
-                params.append(Param(nameParamTypeId, "Dining room"));
-                break;
-            default:
-                params.append(Param(nameParamTypeId, QString("Light %1").arg(QString::number(i))));
-                break;
-            }
-
-            descriptor.setParams(params);
-            deviceDescriptorList.append(descriptor);
-        }
-
-        if (!deviceDescriptorList.isEmpty())
-            emit autoDevicesAppeared(lightDeviceClassId, deviceDescriptorList);
-
-    }
-}
-
-void DevicePluginDollHouse::hostLockupFinished(const QHostInfo &info)
-{
-    Device *device = m_asyncSetup.value(info.lookupId());
-    if (!device)
-        return;
-
-    if (info.error() != QHostInfo::NoError) {
-        qCWarning(dcDollhouse) << "Could not look up host" << info.hostName() << info.errorString();
-        emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusFailure);
-    }
-
-    qCDebug(dcDollhouse) << "Looked up successfully" << info.hostName();
-    emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusSuccess);
-
-    scanNodes(device);
-}
-
-void DevicePluginDollHouse::coapReplyFinished(CoapReply *reply)
-{
-    if (m_asyncPings.contains(reply)) {
-        m_asyncPings.removeAll(reply);
-
-        if (reply->error() != CoapReply::NoError || reply->statusCode() != CoapPdu::Empty) {
-
-            if (m_houseReachable) {
-                qCWarning(dcDollhouse) << "Could not ping Dollhouse:" << reply->errorString();
-                m_houseReachable = false;
-                foreach (Device *device, myDevices()) {
-                    if (device->deviceClassId() == lightDeviceClassId) {
-                        device->setStateValue(reachableStateTypeId, m_houseReachable);
-                    }
-                }
-            }
-
-        } else {
-
-            if (!m_houseReachable) {
-                qCDebug(dcDollhouse) << "Dollhouse reachable";
-                m_houseReachable = true;
-                foreach (Device *device, myDevices()) {
-                    if (device->deviceClassId() == lightDeviceClassId) {
-                        device->setStateValue(reachableStateTypeId, m_houseReachable);
-                    }
-                }
-            }
-        }
-
-    } else if (m_asyncActions.contains(reply)) {
-        Action action = m_asyncActions.take(reply);
-        DollhouseLight *light = m_asyncActionLights.take(action.id());
-
-        if (reply->error() != CoapReply::NoError) {
-            qCWarning(dcDollhouse) << "Got action response with error" << reply->errorString();
-            emit actionExecutionFinished(action.id(), DeviceManager::DeviceErrorHardwareFailure);
-            reply->deleteLater();
-            return;
-        }
-
-        if (reply->statusCode() != CoapPdu::Content) {
-            qCWarning(dcDollhouse) << "Got action response with status code" << reply;
-            emit actionExecutionFinished(action.id(), DeviceManager::DeviceErrorHardwareFailure);
-            reply->deleteLater();
-            return;
-        }
-
-        emit actionExecutionFinished(action.id(), DeviceManager::DeviceErrorNoError);
-
-        // Set the states
-        if (action.actionTypeId() == powerActionTypeId) {
-            bool power = action.param(powerStateParamTypeId).value().toBool();
-            light->setPower(power);
-            m_lights.key(light)->setStateValue(powerStateTypeId, power);
-
-        } else if (action.actionTypeId() == colorActionTypeId) {
-            if (!light->power()) {
-                light->setPower(true);
-                m_lights.key(light)->setStateValue(powerStateTypeId, true);
-            }
-
-            QColor color = action.param(colorStateParamTypeId).value().value();
-            light->setColor(color);
-            m_lights.key(light)->setStateValue(colorStateTypeId, color);
-
-        } else if (action.actionTypeId() == brightnessActionTypeId) {
-            if (!light->power()) {
-                light->setPower(true);
-                m_lights.key(light)->setStateValue(powerStateTypeId, true);
-            }
-
-            int brightness = action.param(brightnessStateParamTypeId).value().toInt();
-            light->setBrightness(brightness);
-            m_lights.key(light)->setStateValue(brightnessStateTypeId, brightness);
-        }
-    }
-
-    reply->deleteLater();
-}
-
diff --git a/dollhouse/deviceplugindollhouse.h b/dollhouse/deviceplugindollhouse.h
deleted file mode 100644
index 4439d0ec..00000000
--- a/dollhouse/deviceplugindollhouse.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- *                                                                         *
- *  Copyright (C) 2016 Simon Stürz                    *
- *                                                                         *
- *  This file is part of guh.                                              *
- *                                                                         *
- *  This library is free software; you can redistribute it and/or          *
- *  modify it under the terms of the GNU Lesser General Public             *
- *  License as published by the Free Software Foundation; either           *
- *  version 2.1 of the License, or (at your option) any later version.     *
- *                                                                         *
- *  This library 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 library; If not, see                           *
- *  .                                        *
- *                                                                         *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-#ifndef DEVICEPLUGINDOLLHOUSE_H
-#define DEVICEPLUGINDOLLHOUSE_H
-
-#include "plugin/deviceplugin.h"
-#include "devicemanager.h"
-
-#include "coap/coap.h"
-#include "dollhouselight.h"
-
-#include 
-
-class DevicePluginDollHouse : public DevicePlugin
-{
-    Q_OBJECT
-    Q_PLUGIN_METADATA(IID "guru.guh.DevicePlugin" FILE "deviceplugindollhouse.json")
-    Q_INTERFACES(DevicePlugin)
-
-public:
-    DevicePluginDollHouse();
-
-    DeviceManager::HardwareResources requiredHardware() const override;
-    DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
-    void deviceRemoved(Device *device) override;
-
-    void guhTimer() override;
-    DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
-
-    void networkManagerReplyReady(QNetworkReply *reply) override;
-private:
-    Coap *m_coap;
-
-    QHash m_asyncSetup;
-    QHash m_asyncNodeScan;
-
-    QHash m_asyncActions;
-    QHash m_asyncActionLights;
-
-    QList m_asyncPings;
-
-    QHostAddress m_houseAddress;
-    bool m_houseReachable;
-
-    QHash m_lights;
-
-    void scanNodes(Device *device);
-    void parseNode(Device *device, const QByteArray &data);
-
-private slots:
-    void hostLockupFinished(const QHostInfo &info);
-    void coapReplyFinished(CoapReply *reply);
-
-};
-
-#endif // DEVICEPLUGINDOLLHOUSE_H
diff --git a/dollhouse/deviceplugindollhouse.json b/dollhouse/deviceplugindollhouse.json
deleted file mode 100644
index 9afcaa39..00000000
--- a/dollhouse/deviceplugindollhouse.json
+++ /dev/null
@@ -1,126 +0,0 @@
-{
-    "name": "Smart Dollhouse",
-    "idName": "Dollhouse",
-    "id": "ee62716c-64b5-4ec5-a877-9a8d9c1042cb",
-    "vendors": [
-        {
-            "name": "guh",
-            "idName": "guh",
-            "id": "2062d64d-3232-433c-88bc-0d33c0ba2ba6",
-            "deviceClasses": [
-                {
-                    "name": "Dollhouse Connection",
-                    "idName": "connection",
-                    "id": "44e12a1c-9711-4780-9913-53bb38264e1e",
-                    "createMethods": ["user"],
-                    "interfaces": ["gateway"],
-                    "basicTags": [
-                        "Device",
-                        "Gateway"
-                    ],
-                    "paramTypes": [
-                        {
-                            "id": "2015e286-4268-4a12-912f-7d70bf2d0bab",
-                            "idName": "rpl",
-                            "name": "RPL address",
-                            "type": "QString",
-                            "inputType": "TextLine",
-                            "defaultValue": "fdaa:e9b8:d03a::ff:fe00:1"
-                        }
-                    ]
-                },
-                {
-                    "name": "Dollhouse Light",
-                    "idName": "light",
-                    "id": "b4dd5f10-36d4-4232-867a-6d3b04a08bad",
-                    "createMethods": ["auto"],
-                    "criticalStateTypeId": "93c539b4-50d8-431e-8be4-5ebba89452b7",
-                    "primaryActionTypeId": "f6ac30a0-77b8-4f1f-8c44-4c2e6d542663",
-                    "interfaces": ["colorlight"],
-                    "basicTags": [
-                        "Device",
-                        "Lighting"
-                    ],
-                    "paramTypes": [
-                        {
-                            "id": "49d3ae89-d76d-4e4c-b1ad-67f5c8f96e52",
-                            "idName": "name",
-                            "name": "name",
-                            "type": "QString",
-                            "defaultValue": "Dollhouse Light"
-                        },
-                        {
-                            "id": "37d5d2fa-dea2-4007-99b1-a1300025c709",
-                            "idName": "address",
-                            "name": "address",
-                            "type": "QString"
-                        },
-                        {
-                            "id": "c843c179-ab02-4aa8-a228-fdc8d9558f1d",
-                            "idName": "lightId",
-                            "name": "light id",
-                            "type": "int",
-                            "defaultValue": 0,
-                            "readOnly": true
-                        },
-                        {
-                            "id": "2bf1d5d5-1f40-4dca-9acd-2909016e4a2b",
-                            "idName": "connectionUuid",
-                            "name": "connection uuid",
-                            "type": "QString",
-                            "inputType": "TextLine",
-                            "readOnly": true
-                        }
-                    ],
-                    "stateTypes": [
-                        {
-                            "id": "e4885a73-9fe1-48ca-b773-aeb93f34cd54",
-                            "idName": "color",
-                            "name": "color",
-                            "eventTypeName": "color changed",
-                            "actionTypeName": "Set color",
-                            "type": "QColor",
-                            "defaultValue": "#fff30a",
-                            "ruleRelevant": false,
-                            "eventRuleRelevant": false,
-                            "writable": true
-                        },
-                        {
-                            "id": "e78c9136-c10a-45c9-b6bc-2d937f09cdec",
-                            "idName": "brightness",
-                            "name": "brightness",
-                            "eventTypeName": "brightness changed",
-                            "actionTypeName": "Set brightness",
-                            "type": "int",
-                            "unit": "Percentage",
-                            "minValue": 0,
-                            "maxValue": 100,
-                            "defaultValue": 100,
-                            "ruleRelevant": false,
-                            "eventRuleRelevant": false,
-                            "writable": true
-                        },
-                        {
-                            "id": "f6ac30a0-77b8-4f1f-8c44-4c2e6d542663",
-                            "idName": "power",
-                            "name": "power",
-                            "eventTypeName": "power changed",
-                            "actionTypeName": "Set power",
-                            "type": "bool",
-                            "defaultValue": false,
-                            "writable": true
-                        },
-                        {
-                            "id": "93c539b4-50d8-431e-8be4-5ebba89452b7",
-                            "idName": "reachable",
-                            "name": "reachable",
-                            "eventTypeName": "reachable changed",
-                            "type": "bool",
-                            "defaultValue": false
-                        }
-                    ]
-                }
-            ]
-        }
-    ]
-}
diff --git a/dollhouse/dollhouse.pro b/dollhouse/dollhouse.pro
deleted file mode 100644
index 0642e388..00000000
--- a/dollhouse/dollhouse.pro
+++ /dev/null
@@ -1,15 +0,0 @@
-TRANSLATIONS = translations/en_US.ts \
-               translations/de_DE.ts
-
-# Note: include after the TRANSLATIONS definition
-include(../plugins.pri)
-
-TARGET = $$qtLibraryTarget(guh_deviceplugindollhouse)
-
-SOURCES += \
-    deviceplugindollhouse.cpp \
-    dollhouselight.cpp
-
-HEADERS += \
-    deviceplugindollhouse.h \
-    dollhouselight.h
diff --git a/dollhouse/dollhouselight.cpp b/dollhouse/dollhouselight.cpp
deleted file mode 100644
index 7d63b877..00000000
--- a/dollhouse/dollhouselight.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- *                                                                         *
- *  Copyright (C) 2016 Simon Stürz                    *
- *                                                                         *
- *  This file is part of guh.                                              *
- *                                                                         *
- *  This library is free software; you can redistribute it and/or          *
- *  modify it under the terms of the GNU Lesser General Public             *
- *  License as published by the Free Software Foundation; either           *
- *  version 2.1 of the License, or (at your option) any later version.     *
- *                                                                         *
- *  This library 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 library; If not, see                           *
- *  .                                        *
- *                                                                         *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-#include "dollhouselight.h"
-
-DollhouseLight::DollhouseLight(QObject *parent) :
-    QObject(parent),
-    m_color(QColor("#fff30a")),
-    m_brightness(100),
-    m_power(false)
-{
-
-}
-
-QString DollhouseLight::name() const
-{
-    return m_name;
-}
-
-void DollhouseLight::setName(const QString &name)
-{
-    m_name = name;
-}
-
-QString DollhouseLight::connectionUuid() const
-{
-    return m_connectionUuid;
-}
-
-void DollhouseLight::setConnectionUuid(const QString &connectionUuid)
-{
-    m_connectionUuid = connectionUuid;
-}
-
-QString DollhouseLight::hostAddress() const
-{
-    return m_hostAddress;
-}
-
-void DollhouseLight::setHostAddress(const QString &address)
-{
-    m_hostAddress = address;
-}
-
-int DollhouseLight::lightId() const
-{
-    return m_lightId;
-}
-
-void DollhouseLight::setLightId(const int &lightId)
-{
-    m_lightId = lightId;
-}
-
-QColor DollhouseLight::color() const
-{
-    return m_color;
-}
-
-void DollhouseLight::setColor(const QColor &color)
-{
-    m_color = color;
-}
-
-int DollhouseLight::brightness() const
-{
-    return m_brightness;
-}
-
-void DollhouseLight::setBrightness(const int &brightness)
-{
-    m_brightness = brightness;
-}
-
-bool DollhouseLight::power() const
-{
-    return m_power;
-}
-
-void DollhouseLight::setPower(const bool &power)
-{
-    m_power = power;
-}
-
diff --git a/dollhouse/dollhouselight.h b/dollhouse/dollhouselight.h
deleted file mode 100644
index 76608cab..00000000
--- a/dollhouse/dollhouselight.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- *                                                                         *
- *  Copyright (C) 2016 Simon Stürz                    *
- *                                                                         *
- *  This file is part of guh.                                              *
- *                                                                         *
- *  This library is free software; you can redistribute it and/or          *
- *  modify it under the terms of the GNU Lesser General Public             *
- *  License as published by the Free Software Foundation; either           *
- *  version 2.1 of the License, or (at your option) any later version.     *
- *                                                                         *
- *  This library 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 library; If not, see                           *
- *  .                                        *
- *                                                                         *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-#ifndef DOLLHOUSELIGHT_H
-#define DOLLHOUSELIGHT_H
-
-#include 
-#include 
-
-class DollhouseLight : public QObject
-{
-    Q_OBJECT
-public:
-    explicit DollhouseLight(QObject *parent = 0);
-
-    QString name() const;
-    void setName(const QString &name);
-
-    QString connectionUuid() const;
-    void setConnectionUuid(const QString &connectionUuid);
-
-    QString hostAddress() const;
-    void setHostAddress(const QString &address);
-
-    int lightId() const;
-    void setLightId(const int &lightId);
-
-    // properties
-    QColor color() const;
-    void setColor(const QColor &color);
-
-    int brightness() const;
-    void setBrightness(const int &brightness);
-
-    bool power() const;
-    void setPower(const bool &power);
-
-private:
-    QString m_name;
-    QString m_connectionUuid;
-    QString m_hostAddress;
-    int m_lightId;
-
-    QColor m_color;
-    int m_brightness;
-    bool m_power;
-
-};
-
-#endif // DOLLHOUSELIGHT_H
diff --git a/elro/devicepluginelro.cpp b/elro/devicepluginelro.cpp
index a69977ae..1ff828db 100644
--- a/elro/devicepluginelro.cpp
+++ b/elro/devicepluginelro.cpp
@@ -43,6 +43,8 @@
 #include "devicepluginelro.h"
 #include "devicemanager.h"
 #include "plugininfo.h"
+#include "hardware/radio433/radio433.h"
+
 
 #include 
 #include 
@@ -51,13 +53,10 @@ DevicePluginElro::DevicePluginElro()
 {
 }
 
-DeviceManager::HardwareResources DevicePluginElro::requiredHardware() const
-{
-    return DeviceManager::HardwareResourceRadio433;
-}
-
 DeviceManager::DeviceError DevicePluginElro::executeAction(Device *device, const Action &action)
-{   
+{
+    if (!hardwareManager()->isAvailable(HardwareResource::TypeRadio433))
+        return DeviceManager::DeviceErrorHardwareNotAvailable;
 
     if (action.actionTypeId() != powerActionTypeId)
         return DeviceManager::DeviceErrorActionTypeNotFound;
@@ -146,13 +145,14 @@ DeviceManager::DeviceError DevicePluginElro::executeAction(Device *device, const
     }
 
     // send data to hardware resource
-    if (transmitData(delay, rawData)) {
+    if (hardwareManager()->radio433()->sendData(delay, rawData, 10)) {
         qCDebug(dcElro) << "Transmitted" << pluginName() << device->name() << "power: " << action.param(powerParamTypeId).value().toBool();
-        return DeviceManager::DeviceErrorNoError;
     } else {
         qCWarning(dcElro) << "Could not transmitt" << pluginName() << device->name() << "power: " << action.param(powerParamTypeId).value().toBool();
         return DeviceManager::DeviceErrorHardwareNotAvailable;
     }
+
+    return DeviceManager::DeviceErrorNoError;
 }
 
 void DevicePluginElro::radioData(const QList &rawData)
diff --git a/elro/devicepluginelro.h b/elro/devicepluginelro.h
index 948e2335..b9c3360b 100644
--- a/elro/devicepluginelro.h
+++ b/elro/devicepluginelro.h
@@ -35,8 +35,7 @@ class DevicePluginElro : public DevicePlugin
 public:
     explicit DevicePluginElro();
 
-    DeviceManager::HardwareResources requiredHardware() const override;
-    void radioData(const QList &rawData) override;
+    void radioData(const QList &rawData);
 
 public slots:
     DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
diff --git a/eq-3/deviceplugineq-3.cpp b/eq-3/deviceplugineq-3.cpp
index 3485a959..467bb111 100644
--- a/eq-3/deviceplugineq-3.cpp
+++ b/eq-3/deviceplugineq-3.cpp
@@ -73,14 +73,21 @@
 
 DevicePluginEQ3::DevicePluginEQ3()
 {
-    m_cubeDiscovery = new MaxCubeDiscovery(this);
 
-    connect(m_cubeDiscovery,SIGNAL(cubesDetected(QList)),this,SLOT(discoveryDone(QList)));
 }
 
-DeviceManager::HardwareResources DevicePluginEQ3::requiredHardware() const
+DevicePluginEQ3::~DevicePluginEQ3()
 {
-    return DeviceManager::HardwareResourceTimer;
+    hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
+}
+
+void DevicePluginEQ3::init()
+{
+    m_cubeDiscovery = new MaxCubeDiscovery(this);
+    connect(m_cubeDiscovery, &MaxCubeDiscovery::cubesDetected, this, &DevicePluginEQ3::discoveryDone);
+
+    m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(10);
+    connect(m_pluginTimer, &PluginTimer::timeout, this, &DevicePluginEQ3::onPluginTimer);
 }
 
 DeviceManager::DeviceError DevicePluginEQ3::discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms)
@@ -145,15 +152,6 @@ void DevicePluginEQ3::deviceRemoved(Device *device)
     cube->deleteLater();
 }
 
-void DevicePluginEQ3::guhTimer()
-{
-    foreach (MaxCube *cube, m_cubes.keys()){
-        if(cube->isConnected() && cube->isInitialized()){
-            cube->refresh();
-        }
-    }
-}
-
 DeviceManager::DeviceError DevicePluginEQ3::executeAction(Device *device, const Action &action)
 {    
     if(device->deviceClassId() == wallThermostateDeviceClassId || device->deviceClassId() == radiatorThermostateDeviceClassId){
@@ -182,6 +180,15 @@ DeviceManager::DeviceError DevicePluginEQ3::executeAction(Device *device, const
     return DeviceManager::DeviceErrorActionTypeNotFound;
 }
 
+void DevicePluginEQ3::onPluginTimer()
+{
+    foreach (MaxCube *cube, m_cubes.keys()){
+        if(cube->isConnected() && cube->isInitialized()){
+            cube->refresh();
+        }
+    }
+}
+
 void DevicePluginEQ3::cubeConnectionStatusChanged(const bool &connected)
 {
     if(connected){
diff --git a/eq-3/deviceplugineq-3.h b/eq-3/deviceplugineq-3.h
index d9e03eeb..0bef0abc 100644
--- a/eq-3/deviceplugineq-3.h
+++ b/eq-3/deviceplugineq-3.h
@@ -25,6 +25,7 @@
 
 #include "plugin/deviceplugin.h"
 #include "maxcubediscovery.h"
+#include "plugintimer.h"
 
 #include 
 
@@ -39,25 +40,27 @@ class DevicePluginEQ3: public DevicePlugin
 
 public:
     explicit DevicePluginEQ3();
+    ~DevicePluginEQ3();
 
-    DeviceManager::HardwareResources requiredHardware() const override;
+    void init() override;
     DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override;
 
     void startMonitoringAutoDevices() override;
 
     DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
     void deviceRemoved(Device *device) override;
-    void guhTimer() override;
 
 private:
+    PluginTimer *m_pluginTimer = nullptr;
     QList m_config;
-    MaxCubeDiscovery *m_cubeDiscovery;
-    QHash m_cubes;
+    MaxCubeDiscovery *m_cubeDiscovery = nullptr;
+    QHash m_cubes;
 
 public slots:
     DeviceManager::DeviceError executeAction(Device *device, const Action &action);
 
 private slots:
+    void onPluginTimer();
     void cubeConnectionStatusChanged(const bool &connected);
     void discoveryDone(const QList &cubeList);
     void commandActionFinished(const bool &succeeded, const ActionId &actionId);
diff --git a/genericelements/deviceplugingenericelements.cpp b/genericelements/deviceplugingenericelements.cpp
index 988e33ef..7357a37e 100644
--- a/genericelements/deviceplugingenericelements.cpp
+++ b/genericelements/deviceplugingenericelements.cpp
@@ -62,11 +62,6 @@ DevicePluginGenericElements::DevicePluginGenericElements()
 {
 }
 
-DeviceManager::HardwareResources DevicePluginGenericElements::requiredHardware() const
-{
-    return DeviceManager::HardwareResourceNone;
-}
-
 DeviceManager::DeviceSetupStatus DevicePluginGenericElements::setupDevice(Device *device)
 {
     // Toggle Button
diff --git a/genericelements/deviceplugingenericelements.h b/genericelements/deviceplugingenericelements.h
index 1e9555d2..520e0edc 100644
--- a/genericelements/deviceplugingenericelements.h
+++ b/genericelements/deviceplugingenericelements.h
@@ -34,11 +34,8 @@ class DevicePluginGenericElements : public DevicePlugin
 
 public:
     explicit DevicePluginGenericElements();
-
-    DeviceManager::HardwareResources requiredHardware() const override;
     DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
 
-
 public slots:
     DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
 
diff --git a/gpio/deviceplugingpio.cpp b/gpio/deviceplugingpio.cpp
index 4da6471a..beb471cd 100644
--- a/gpio/deviceplugingpio.cpp
+++ b/gpio/deviceplugingpio.cpp
@@ -211,11 +211,6 @@ DeviceManager::DeviceError DevicePluginGpio::discoverDevices(const DeviceClassId
     return DeviceManager::DeviceErrorVendorNotFound;
 }
 
-DeviceManager::HardwareResources DevicePluginGpio::requiredHardware() const
-{
-    return DeviceManager::HardwareResourceNone;
-}
-
 void DevicePluginGpio::deviceRemoved(Device *device)
 {
     if (m_gpioDevices.values().contains(device)) {
diff --git a/gpio/deviceplugingpio.h b/gpio/deviceplugingpio.h
index 40dff470..f95d2e83 100644
--- a/gpio/deviceplugingpio.h
+++ b/gpio/deviceplugingpio.h
@@ -41,7 +41,6 @@ public:
 
     DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
     DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override;
-    DeviceManager::HardwareResources requiredHardware() const override;
     void deviceRemoved(Device *device) override;
     DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
 
diff --git a/guh-plugins.pro b/guh-plugins.pro
index 6f147a8c..24267b83 100644
--- a/guh-plugins.pro
+++ b/guh-plugins.pro
@@ -6,7 +6,6 @@ PLUGIN_DIRS = \
     networkdetector     \
     conrad              \
     openweathermap      \
-    lircd               \
     wakeonlan           \
     mailnotification    \
     philipshue          \
@@ -21,17 +20,16 @@ PLUGIN_DIRS = \
     udpcommander        \
     tcpcommander        \
     kodi                \
-    elgato              \
+    #elgato              \
     awattar             \
     netatmo             \
-    dollhouse           \
     plantcare           \
     osdomotics          \
     ws2812              \
     orderbutton         \
     denon               \
     avahimonitor        \
-    senic               \
+    #senic               \
     gpio                \
 
 
diff --git a/intertechno/devicepluginintertechno.cpp b/intertechno/devicepluginintertechno.cpp
index 1dc4768a..463eb777 100644
--- a/intertechno/devicepluginintertechno.cpp
+++ b/intertechno/devicepluginintertechno.cpp
@@ -44,6 +44,7 @@
 
 #include "plugin/device.h"
 #include "devicemanager.h"
+#include "hardware/radio433/radio433.h"
 #include "plugininfo.h"
 
 #include 
@@ -51,15 +52,15 @@
 
 DevicePluginIntertechno::DevicePluginIntertechno()
 {
+
 }
 
-DeviceManager::HardwareResources DevicePluginIntertechno::requiredHardware() const
-{
-    return DeviceManager::HardwareResourceRadio433;
-}
 
 DeviceManager::DeviceError DevicePluginIntertechno::executeAction(Device *device, const Action &action)
 {
+    if (!hardwareManager()->isAvailable(HardwareResource::TypeRadio433))
+        return DeviceManager::DeviceErrorHardwareNotAvailable;
+
     QList rawData;
     QByteArray binCode;
 
@@ -175,13 +176,13 @@ DeviceManager::DeviceError DevicePluginIntertechno::executeAction(Device *device
 
     // =======================================
     // send data to hardware resource
-    if (transmitData(delay, rawData)) {
+    if (hardwareManager()->radio433()->sendData(delay, rawData, 10)) {
         qCDebug(dcIntertechno) << "transmitted" << pluginName() << device->name() << "power: " << action.param(powerParamTypeId).value().toBool();
-        return DeviceManager::DeviceErrorNoError;
     } else {
         qCWarning(dcIntertechno) << "could not transmitt" << pluginName() << device->name() << "power: " << action.param(powerParamTypeId).value().toBool();
         return DeviceManager::DeviceErrorHardwareNotAvailable;
     }
+    return DeviceManager::DeviceErrorNoError;
 }
 
 void DevicePluginIntertechno::radioData(const QList &rawData)
diff --git a/intertechno/devicepluginintertechno.h b/intertechno/devicepluginintertechno.h
index 7a086d01..d13a31cb 100644
--- a/intertechno/devicepluginintertechno.h
+++ b/intertechno/devicepluginintertechno.h
@@ -35,8 +35,7 @@ class DevicePluginIntertechno : public DevicePlugin
 public:
     explicit DevicePluginIntertechno();
 
-    DeviceManager::HardwareResources requiredHardware() const override;
-    void radioData(const QList &rawData) override;
+    void radioData(const QList &rawData);
 
 public slots:
     DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
diff --git a/kodi/devicepluginkodi.cpp b/kodi/devicepluginkodi.cpp
index 9d36aaf9..5583359d 100644
--- a/kodi/devicepluginkodi.cpp
+++ b/kodi/devicepluginkodi.cpp
@@ -64,6 +64,7 @@
 #include "devicepluginkodi.h"
 #include "plugin/device.h"
 #include "plugininfo.h"
+#include "network/upnp/upnpdiscovery.h"
 
 DevicePluginKodi::DevicePluginKodi()
 {
@@ -79,12 +80,18 @@ DevicePluginKodi::DevicePluginKodi()
 //        qCWarning(dcKodi) << "could not read" << file.fileName();
 //        return;
 //    }
-//    m_logo = guhLogoByteArray;
+    //    m_logo = guhLogoByteArray;
 }
 
-DeviceManager::HardwareResources DevicePluginKodi::requiredHardware() const
+DevicePluginKodi::~DevicePluginKodi()
 {
-    return DeviceManager::HardwareResourceTimer | DeviceManager::HardwareResourceUpnpDisovery;
+    hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
+}
+
+void DevicePluginKodi::init()
+{
+    m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(10);
+    connect(m_pluginTimer, &PluginTimer::timeout, this, &DevicePluginKodi::onPluginTimer);
 }
 
 DeviceManager::DeviceSetupStatus DevicePluginKodi::setupDevice(Device *device)
@@ -117,59 +124,18 @@ void DevicePluginKodi::deviceRemoved(Device *device)
     kodi->deleteLater();
 }
 
-void DevicePluginKodi::guhTimer()
-{
-    foreach (Kodi *kodi, m_kodis.keys()) {
-        if (!kodi->connected()) {
-            kodi->connectKodi();
-            continue;
-        } else {
-            // no need for polling information, notifications do the job
-            //kodi->update();
-        }
-    }
-}
-
-
 DeviceManager::DeviceError DevicePluginKodi::discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms)
 {
     Q_UNUSED(params)
     Q_UNUSED(deviceClassId)
+
     qCDebug(dcKodi) << "Start UPnP search";
-    upnpDiscover();
+    UpnpDiscoveryReply *reply = hardwareManager()->upnpDiscovery()->discoverDevices();
+    connect(reply, &UpnpDiscoveryReply::finished, this, &DevicePluginKodi::onUpnpDiscoveryFinished);
+
     return DeviceManager::DeviceErrorAsync;
 }
 
-void DevicePluginKodi::upnpDiscoveryFinished(const QList &upnpDeviceDescriptorList)
-{
-    QList deviceDescriptors;
-    foreach (const UpnpDeviceDescriptor &upnpDescriptor, upnpDeviceDescriptorList) {
-        if (upnpDescriptor.modelName().contains("Kodi")) {
-
-            // check if we allready found the kodi on this ip
-            bool alreadyAdded = false;
-            foreach (const DeviceDescriptor dDescriptor, deviceDescriptors) {
-                if (dDescriptor.params().paramValue(ipParamTypeId).toString() == upnpDescriptor.hostAddress().toString()) {
-                    alreadyAdded = true;
-                    break;
-                }
-            }
-            if (alreadyAdded)
-                continue;
-
-            qCDebug(dcKodi) << upnpDescriptor;
-            DeviceDescriptor deviceDescriptor(kodiDeviceClassId, "Kodi - Media Center", upnpDescriptor.hostAddress().toString());
-            ParamList params;
-            params.append(Param(nameParamTypeId, upnpDescriptor.friendlyName()));
-            params.append(Param(ipParamTypeId, upnpDescriptor.hostAddress().toString()));
-            params.append(Param(portParamTypeId, 9090));
-            deviceDescriptor.setParams(params);
-            deviceDescriptors.append(deviceDescriptor);
-        }
-    }
-    emit devicesDiscovered(kodiDeviceClassId, deviceDescriptors);
-}
-
 DeviceManager::DeviceError DevicePluginKodi::executeAction(Device *device, const Action &action)
 {
     if (device->deviceClassId() == kodiDeviceClassId) {
@@ -207,6 +173,53 @@ DeviceManager::DeviceError DevicePluginKodi::executeAction(Device *device, const
     return DeviceManager::DeviceErrorDeviceClassNotFound;
 }
 
+void DevicePluginKodi::onPluginTimer()
+{
+    foreach (Kodi *kodi, m_kodis.keys()) {
+        if (!kodi->connected()) {
+            kodi->connectKodi();
+            continue;
+        }
+    }
+}
+
+void DevicePluginKodi::onUpnpDiscoveryFinished()
+{
+    qCDebug(dcKodi()) << "Upnp discovery finished";
+    UpnpDiscoveryReply *reply = static_cast(sender());
+    if (reply->error() != UpnpDiscoveryReply::UpnpDiscoveryReplyErrorNoError) {
+        qCWarning(dcKodi()) << "Upnp discovery error" << reply->error();
+    }
+    reply->deleteLater();
+
+    QList deviceDescriptors;
+    foreach (const UpnpDeviceDescriptor &upnpDescriptor, reply->deviceDescriptors()) {
+        if (upnpDescriptor.modelName().contains("Kodi")) {
+
+            // check if we allready found the kodi on this ip
+            bool alreadyAdded = false;
+            foreach (const DeviceDescriptor dDescriptor, deviceDescriptors) {
+                if (dDescriptor.params().paramValue(ipParamTypeId).toString() == upnpDescriptor.hostAddress().toString()) {
+                    alreadyAdded = true;
+                    break;
+                }
+            }
+            if (alreadyAdded)
+                continue;
+
+            qCDebug(dcKodi) << upnpDescriptor;
+            DeviceDescriptor deviceDescriptor(kodiDeviceClassId, "Kodi - Media Center", upnpDescriptor.hostAddress().toString());
+            ParamList params;
+            params.append(Param(nameParamTypeId, upnpDescriptor.friendlyName()));
+            params.append(Param(ipParamTypeId, upnpDescriptor.hostAddress().toString()));
+            params.append(Param(portParamTypeId, 9090));
+            deviceDescriptor.setParams(params);
+            deviceDescriptors.append(deviceDescriptor);
+        }
+    }
+    emit devicesDiscovered(kodiDeviceClassId, deviceDescriptors);
+}
+
 void DevicePluginKodi::onConnectionChanged()
 {
     Kodi *kodi = static_cast(sender());
diff --git a/kodi/devicepluginkodi.h b/kodi/devicepluginkodi.h
index 680f40b9..5e592a48 100644
--- a/kodi/devicepluginkodi.h
+++ b/kodi/devicepluginkodi.h
@@ -24,6 +24,7 @@
 #define DEVICEPLUGINKODI_H
 
 #include "plugin/deviceplugin.h"
+#include "plugintimer.h"
 #include "kodi.h"
 
 #include 
@@ -38,23 +39,22 @@ class DevicePluginKodi : public DevicePlugin
 
 public:
     explicit DevicePluginKodi();
+    ~DevicePluginKodi();
 
-    DeviceManager::HardwareResources requiredHardware() const override;
+    void init() override;
     DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
     void deviceRemoved(Device *device) override;
-    void guhTimer() override;
-
     DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override;
-    void upnpDiscoveryFinished(const QList &upnpDeviceDescriptorList) override;
-
     DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
 
 private:
+    PluginTimer *m_pluginTimer;
     QHash m_kodis;
     QList m_asyncSetups;
 
-
 private slots:
+    void onPluginTimer();
+    void onUpnpDiscoveryFinished();
     void onConnectionChanged();
     void onStateChanged();
     void onActionExecuted(const ActionId &actionId, const bool &success);
diff --git a/leynew/devicepluginleynew.cpp b/leynew/devicepluginleynew.cpp
index bd1f05a5..b443d585 100644
--- a/leynew/devicepluginleynew.cpp
+++ b/leynew/devicepluginleynew.cpp
@@ -49,6 +49,7 @@
 #include "devicepluginleynew.h"
 #include "devicemanager.h"
 #include "plugininfo.h"
+#include "hardware/radio433/radio433.h"
 
 #include 
 #include 
@@ -64,13 +65,11 @@ DeviceManager::DeviceSetupStatus DevicePluginLeynew::setupDevice(Device *device)
     return DeviceManager::DeviceSetupStatusSuccess;
 }
 
-DeviceManager::HardwareResources DevicePluginLeynew::requiredHardware() const
-{
-    return DeviceManager::HardwareResourceRadio433;
-}
-
 DeviceManager::DeviceError DevicePluginLeynew::executeAction(Device *device, const Action &action)
 {   
+    if (!hardwareManager()->radio433()->available()) {
+        return DeviceManager::DeviceErrorHardwareNotAvailable;
+    }
 
     if (device->deviceClassId() != rfControllerDeviceClassId) {
         return DeviceManager::DeviceErrorDeviceClassNotFound;
@@ -170,11 +169,11 @@ DeviceManager::DeviceError DevicePluginLeynew::executeAction(Device *device, con
 
     // =======================================
     // send data to hardware resource
-    if(transmitData(delay, rawData, repetitions)){
+    if(hardwareManager()->radio433()->sendData(delay, rawData, repetitions)){
         qCDebug(dcLeynew) << "Transmitted" << pluginName() << device->name() << action.id();
-        return DeviceManager::DeviceErrorNoError;
     }else{
         qCWarning(dcLeynew) << "Could not transmitt" << pluginName() << device->name() << action.id();
         return DeviceManager::DeviceErrorHardwareNotAvailable;
     }
+    return DeviceManager::DeviceErrorNoError;
 }
diff --git a/leynew/devicepluginleynew.h b/leynew/devicepluginleynew.h
index da9eb4c0..b492bd5c 100644
--- a/leynew/devicepluginleynew.h
+++ b/leynew/devicepluginleynew.h
@@ -36,7 +36,6 @@ public:
     explicit DevicePluginLeynew();
 
     DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
-    DeviceManager::HardwareResources requiredHardware() const override;
 
 public slots:
     DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
diff --git a/lgsmarttv/devicepluginlgsmarttv.cpp b/lgsmarttv/devicepluginlgsmarttv.cpp
index 5c11e388..ee497d05 100644
--- a/lgsmarttv/devicepluginlgsmarttv.cpp
+++ b/lgsmarttv/devicepluginlgsmarttv.cpp
@@ -45,6 +45,9 @@
 #include "plugin/device.h"
 #include "devicemanager.h"
 #include "plugininfo.h"
+#include "network/networkaccessmanager.h"
+#include "network/upnp/upnpdiscovery.h"
+#include "hardwaremanager.h"
 
 #include 
 
@@ -52,22 +55,25 @@ DevicePluginLgSmartTv::DevicePluginLgSmartTv()
 {
 }
 
-DeviceManager::HardwareResources DevicePluginLgSmartTv::requiredHardware() const
+DevicePluginLgSmartTv::~DevicePluginLgSmartTv()
 {
-    return DeviceManager::HardwareResourceTimer |
-            DeviceManager::HardwareResourceUpnpDisovery |
-            DeviceManager::HardwareResourceNetworkManager;
+    hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
+}
+
+void DevicePluginLgSmartTv::init()
+{
+    m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(15);
+    connect(m_pluginTimer, &PluginTimer::timeout, this, &DevicePluginLgSmartTv::onPluginTimer);
 }
 
 DeviceManager::DeviceError DevicePluginLgSmartTv::discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms)
 {
     Q_UNUSED(params)
+    Q_UNUSED(deviceClassId)
 
-    if(deviceClassId != lgSmartTvDeviceClassId){
-        return DeviceManager::DeviceErrorDeviceClassNotFound;
-    }
-    qCDebug(dcLgSmartTv) << "Start discovering";
-    upnpDiscover("udap:rootservice","UDAP/2.0");
+    qCDebug(dcLgSmartTv()) << "Start discovering";
+    UpnpDiscoveryReply *reply = hardwareManager()->upnpDiscovery()->discoverDevices("udap:rootservice","UDAP/2.0");
+    connect(reply, &UpnpDiscoveryReply::finished, this, &DevicePluginLgSmartTv::onUpnpDiscoveryFinished);
     return DeviceManager::DeviceErrorAsync;
 }
 
@@ -121,10 +127,206 @@ void DevicePluginLgSmartTv::deviceRemoved(Device *device)
     delete tvDevice;
 }
 
-void DevicePluginLgSmartTv::upnpDiscoveryFinished(const QList &upnpDeviceDescriptorList)
+DeviceManager::DeviceError DevicePluginLgSmartTv::executeAction(Device *device, const Action &action)
 {
+    TvDevice * tvDevice = m_tvList.key(device);
+
+    if (!tvDevice->reachable()) {
+        qCWarning(dcLgSmartTv) << "Device not reachable";
+        return DeviceManager::DeviceErrorHardwareNotAvailable;
+    }
+
+    if (action.actionTypeId() == commandVolumeUpActionTypeId) {
+        QPair request = tvDevice->createPressButtonRequest(TvDevice::VolUp);
+        QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
+        connect(reply, &QNetworkReply::finished, this, &DevicePluginLgSmartTv::onNetworkManagerReplyFinished);
+        m_asyncActions.insert(reply, action.id());
+    } else if(action.actionTypeId() == commandVolumeDownActionTypeId) {
+        QPair request = tvDevice->createPressButtonRequest(TvDevice::VolDown);
+        QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
+        connect(reply, &QNetworkReply::finished, this, &DevicePluginLgSmartTv::onNetworkManagerReplyFinished);
+        m_asyncActions.insert(reply, action.id());
+    } else if(action.actionTypeId() == commandMuteActionTypeId) {
+        QPair request = tvDevice->createPressButtonRequest(TvDevice::Mute);
+        QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
+        connect(reply, &QNetworkReply::finished, this, &DevicePluginLgSmartTv::onNetworkManagerReplyFinished);
+        m_asyncActions.insert(reply, action.id());
+    } else if(action.actionTypeId() == commandChannelUpActionTypeId) {
+        QPair request = tvDevice->createPressButtonRequest(TvDevice::ChannelUp);
+        QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
+        connect(reply, &QNetworkReply::finished, this, &DevicePluginLgSmartTv::onNetworkManagerReplyFinished);
+        m_asyncActions.insert(reply, action.id());
+    } else if(action.actionTypeId() == commandChannelDownActionTypeId) {
+        QPair request = tvDevice->createPressButtonRequest(TvDevice::ChannelDown);
+        QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
+        connect(reply, &QNetworkReply::finished, this, &DevicePluginLgSmartTv::onNetworkManagerReplyFinished);
+        m_asyncActions.insert(reply, action.id());
+    } else if(action.actionTypeId() == commandPowerOffActionTypeId) {
+        QPair request = tvDevice->createPressButtonRequest(TvDevice::Power);
+        QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
+        connect(reply, &QNetworkReply::finished, this, &DevicePluginLgSmartTv::onNetworkManagerReplyFinished);
+        m_asyncActions.insert(reply, action.id());
+    } else if(action.actionTypeId() == commandArrowUpActionTypeId) {
+        QPair request = tvDevice->createPressButtonRequest(TvDevice::Up);
+        QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
+        connect(reply, &QNetworkReply::finished, this, &DevicePluginLgSmartTv::onNetworkManagerReplyFinished);
+        m_asyncActions.insert(reply, action.id());
+    } else if(action.actionTypeId() == commandArrowDownActionTypeId) {
+        QPair request = tvDevice->createPressButtonRequest(TvDevice::Down);
+        QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
+        connect(reply, &QNetworkReply::finished, this, &DevicePluginLgSmartTv::onNetworkManagerReplyFinished);
+        m_asyncActions.insert(reply, action.id());
+    } else if(action.actionTypeId() == commandArrowLeftActionTypeId) {
+        QPair request = tvDevice->createPressButtonRequest(TvDevice::Left);
+        QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
+        connect(reply, &QNetworkReply::finished, this, &DevicePluginLgSmartTv::onNetworkManagerReplyFinished);
+        m_asyncActions.insert(reply, action.id());
+    } else if(action.actionTypeId() == commandArrowRightActionTypeId) {
+        QPair request = tvDevice->createPressButtonRequest(TvDevice::Right);
+        QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
+        connect(reply, &QNetworkReply::finished, this, &DevicePluginLgSmartTv::onNetworkManagerReplyFinished);
+        m_asyncActions.insert(reply, action.id());
+    } else if(action.actionTypeId() == commandOkActionTypeId) {
+        QPair request = tvDevice->createPressButtonRequest(TvDevice::Ok);
+        QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
+        connect(reply, &QNetworkReply::finished, this, &DevicePluginLgSmartTv::onNetworkManagerReplyFinished);
+        m_asyncActions.insert(reply, action.id());
+    } else if(action.actionTypeId() == commandBackActionTypeId) {
+        QPair request = tvDevice->createPressButtonRequest(TvDevice::Back);
+        QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
+        connect(reply, &QNetworkReply::finished, this, &DevicePluginLgSmartTv::onNetworkManagerReplyFinished);
+        m_asyncActions.insert(reply, action.id());
+    } else if(action.actionTypeId() == commandHomeActionTypeId) {
+        QPair request = tvDevice->createPressButtonRequest(TvDevice::Home);
+        QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
+        connect(reply, &QNetworkReply::finished, this, &DevicePluginLgSmartTv::onNetworkManagerReplyFinished);
+        m_asyncActions.insert(reply, action.id());
+    } else if(action.actionTypeId() == commandInputSourceActionTypeId) {
+        QPair request = tvDevice->createPressButtonRequest(TvDevice::ExternalInput);
+        QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
+        connect(reply, &QNetworkReply::finished, this, &DevicePluginLgSmartTv::onNetworkManagerReplyFinished);
+        m_asyncActions.insert(reply, action.id());
+    } else if(action.actionTypeId() == commandExitActionTypeId) {
+        QPair request = tvDevice->createPressButtonRequest(TvDevice::Exit);
+        QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
+        connect(reply, &QNetworkReply::finished, this, &DevicePluginLgSmartTv::onNetworkManagerReplyFinished);
+        m_asyncActions.insert(reply, action.id());
+    } else if(action.actionTypeId() == commandInfoActionTypeId) {
+        QPair request = tvDevice->createPressButtonRequest(TvDevice::Info);
+        QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
+        connect(reply, &QNetworkReply::finished, this, &DevicePluginLgSmartTv::onNetworkManagerReplyFinished);
+        m_asyncActions.insert(reply, action.id());
+    } else if(action.actionTypeId() == commandMyAppsActionTypeId) {
+        QPair request = tvDevice->createPressButtonRequest(TvDevice::MyApps);
+        QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
+        connect(reply, &QNetworkReply::finished, this, &DevicePluginLgSmartTv::onNetworkManagerReplyFinished);
+        m_asyncActions.insert(reply, action.id());
+    } else if(action.actionTypeId() == commandProgramListActionTypeId) {
+        QPair request = tvDevice->createPressButtonRequest(TvDevice::ProgramList);
+        QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
+        connect(reply, &QNetworkReply::finished, this, &DevicePluginLgSmartTv::onNetworkManagerReplyFinished);
+        m_asyncActions.insert(reply, action.id());
+    } else {
+        return DeviceManager::DeviceErrorActionTypeNotFound;
+    }
+    return DeviceManager::DeviceErrorAsync;
+}
+
+DeviceManager::DeviceError DevicePluginLgSmartTv::displayPin(const PairingTransactionId &pairingTransactionId, const DeviceDescriptor &deviceDescriptor)
+{
+    Q_UNUSED(pairingTransactionId)
+
+    QHostAddress host = QHostAddress(deviceDescriptor.params().paramValue(hostAddressParamTypeId).toString());
+    int port = deviceDescriptor.params().paramValue(portParamTypeId).toInt();
+    QPair request = TvDevice::createDisplayKeyRequest(host, port);
+    QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
+    connect(reply, &QNetworkReply::finished, this, &DevicePluginLgSmartTv::onNetworkManagerReplyFinished);
+
+    m_showPinReply.append(reply);
+    return DeviceManager::DeviceErrorNoError;
+}
+
+DeviceManager::DeviceSetupStatus DevicePluginLgSmartTv::confirmPairing(const PairingTransactionId &pairingTransactionId, const DeviceClassId &deviceClassId, const ParamList ¶ms, const QString &secret)
+{
+    Q_UNUSED(deviceClassId)
+
+    QHostAddress host = QHostAddress(params.paramValue(hostAddressParamTypeId).toString());
+    int port = params.paramValue(portParamTypeId).toInt();
+    QPair request = TvDevice::createPairingRequest(host, port, secret);
+    QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
+    connect(reply, &QNetworkReply::finished, this, &DevicePluginLgSmartTv::onNetworkManagerReplyFinished);
+
+    m_setupPairingTv.insert(reply, pairingTransactionId);
+    m_tvKeys.insert(params.paramValue(uuidParamTypeId).toString(), secret);
+
+    return DeviceManager::DeviceSetupStatusAsync;
+}
+
+void DevicePluginLgSmartTv::pairTvDevice(Device *device, const bool &setup)
+{
+    QHostAddress host = QHostAddress(device->paramValue(hostAddressParamTypeId).toString());
+    int port = device->paramValue(portParamTypeId).toInt();
+    QString key = device->paramValue(keyParamTypeId).toString();
+    QPair request = TvDevice::createPairingRequest(host, port, key);
+    QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
+    connect(reply, &QNetworkReply::finished, this, &DevicePluginLgSmartTv::onNetworkManagerReplyFinished);
+
+    if (setup) {
+        m_asyncSetup.insert(reply, device);
+    } else {
+        m_pairRequests.insert(reply, device);
+    }
+}
+
+void DevicePluginLgSmartTv::unpairTvDevice(Device *device)
+{
+    QHostAddress host = QHostAddress(device->paramValue(hostAddressParamTypeId).toString());
+    int port = device->paramValue(portParamTypeId).toInt();
+    QPair request = TvDevice::createEndPairingRequest(host, port);
+    QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
+    connect(reply, &QNetworkReply::finished, this, &DevicePluginLgSmartTv::onNetworkManagerReplyFinished);
+
+    m_deleteTv.append(reply);
+}
+
+void DevicePluginLgSmartTv::refreshTv(Device *device)
+{
+    TvDevice *tv = m_tvList.key(device);
+    // check volume information
+    QNetworkReply *volumeReply = hardwareManager()->networkManager()->get(tv->createVolumeInformationRequest());
+    connect(volumeReply, &QNetworkReply::finished, this, &DevicePluginLgSmartTv::onNetworkManagerReplyFinished);
+    m_volumeInfoRequests.insert(volumeReply, device);
+
+    // check channel information
+    QNetworkReply *channelReply = hardwareManager()->networkManager()->get(tv->createChannelInformationRequest());
+    connect(channelReply, &QNetworkReply::finished, this, &DevicePluginLgSmartTv::onNetworkManagerReplyFinished);
+    m_channelInfoRequests.insert(channelReply, device);
+}
+
+void DevicePluginLgSmartTv::onPluginTimer()
+{
+    foreach (Device *device, m_tvList.values()) {
+        TvDevice *tv = m_tvList.key(device);
+        if (tv->paired()) {
+            refreshTv(device);
+        } else {
+            pairTvDevice(device);
+        }
+    }
+}
+
+void DevicePluginLgSmartTv::onUpnpDiscoveryFinished()
+{
+    qCDebug(dcLgSmartTv()) << "Upnp discovery finished";
+
+    UpnpDiscoveryReply *reply = static_cast(sender());
+    if (reply->error() != UpnpDiscoveryReply::UpnpDiscoveryReplyErrorNoError) {
+        qCWarning(dcLgSmartTv()) << "Upnp discovery error" << reply->error();
+    }
+    reply->deleteLater();
+
     QList deviceDescriptors;
-    foreach (UpnpDeviceDescriptor upnpDeviceDescriptor, upnpDeviceDescriptorList) {
+    foreach (UpnpDeviceDescriptor upnpDeviceDescriptor, reply->deviceDescriptors()) {
         if (!upnpDeviceDescriptor.friendlyName().contains("LG") || !upnpDeviceDescriptor.deviceType().contains("TV"))
             continue;
         qCDebug(dcLgSmartTv) << upnpDeviceDescriptor;
@@ -140,125 +342,13 @@ void DevicePluginLgSmartTv::upnpDiscoveryFinished(const QList(sender());
 
-    if (!tvDevice->reachable()) {
-        qCWarning(dcLgSmartTv) << "Device not reachable";
-        return DeviceManager::DeviceErrorHardwareNotAvailable;
-    }
-
-    if (action.actionTypeId() == commandVolumeUpActionTypeId) {
-        QPair request = tvDevice->createPressButtonRequest(TvDevice::VolUp);
-        QNetworkReply *reply = networkManagerPost(request.first, request.second);
-        m_asyncActions.insert(reply, action.id());
-    } else if(action.actionTypeId() == commandVolumeDownActionTypeId) {
-        QPair request = tvDevice->createPressButtonRequest(TvDevice::VolDown);
-        QNetworkReply *reply = networkManagerPost(request.first, request.second);
-        m_asyncActions.insert(reply, action.id());
-    } else if(action.actionTypeId() == commandMuteActionTypeId) {
-        QPair request = tvDevice->createPressButtonRequest(TvDevice::Mute);
-        QNetworkReply *reply = networkManagerPost(request.first, request.second);
-        m_asyncActions.insert(reply, action.id());
-    } else if(action.actionTypeId() == commandChannelUpActionTypeId) {
-        QPair request = tvDevice->createPressButtonRequest(TvDevice::ChannelUp);
-        QNetworkReply *reply = networkManagerPost(request.first, request.second);
-        m_asyncActions.insert(reply, action.id());
-    } else if(action.actionTypeId() == commandChannelDownActionTypeId) {
-        QPair request = tvDevice->createPressButtonRequest(TvDevice::ChannelDown);
-        QNetworkReply *reply = networkManagerPost(request.first, request.second);
-        m_asyncActions.insert(reply, action.id());
-    } else if(action.actionTypeId() == commandPowerOffActionTypeId) {
-        QPair request = tvDevice->createPressButtonRequest(TvDevice::Power);
-        QNetworkReply *reply = networkManagerPost(request.first, request.second);
-        m_asyncActions.insert(reply, action.id());
-    } else if(action.actionTypeId() == commandArrowUpActionTypeId) {
-        QPair request = tvDevice->createPressButtonRequest(TvDevice::Up);
-        QNetworkReply *reply = networkManagerPost(request.first, request.second);
-        m_asyncActions.insert(reply, action.id());
-    } else if(action.actionTypeId() == commandArrowDownActionTypeId) {
-        QPair request = tvDevice->createPressButtonRequest(TvDevice::Down);
-        QNetworkReply *reply = networkManagerPost(request.first, request.second);
-        m_asyncActions.insert(reply, action.id());
-    } else if(action.actionTypeId() == commandArrowLeftActionTypeId) {
-        QPair request = tvDevice->createPressButtonRequest(TvDevice::Left);
-        QNetworkReply *reply = networkManagerPost(request.first, request.second);
-        m_asyncActions.insert(reply, action.id());
-    } else if(action.actionTypeId() == commandArrowRightActionTypeId) {
-        QPair request = tvDevice->createPressButtonRequest(TvDevice::Right);
-        QNetworkReply *reply = networkManagerPost(request.first, request.second);
-        m_asyncActions.insert(reply, action.id());
-    } else if(action.actionTypeId() == commandOkActionTypeId) {
-        QPair request = tvDevice->createPressButtonRequest(TvDevice::Ok);
-        QNetworkReply *reply = networkManagerPost(request.first, request.second);
-        m_asyncActions.insert(reply, action.id());
-    } else if(action.actionTypeId() == commandBackActionTypeId) {
-        QPair request = tvDevice->createPressButtonRequest(TvDevice::Back);
-        QNetworkReply *reply = networkManagerPost(request.first, request.second);
-        m_asyncActions.insert(reply, action.id());
-    } else if(action.actionTypeId() == commandHomeActionTypeId) {
-        QPair request = tvDevice->createPressButtonRequest(TvDevice::Home);
-        QNetworkReply *reply = networkManagerPost(request.first, request.second);
-        m_asyncActions.insert(reply, action.id());
-    } else if(action.actionTypeId() == commandInputSourceActionTypeId) {
-        QPair request = tvDevice->createPressButtonRequest(TvDevice::ExternalInput);
-        QNetworkReply *reply = networkManagerPost(request.first, request.second);
-        m_asyncActions.insert(reply, action.id());
-    } else if(action.actionTypeId() == commandExitActionTypeId) {
-        QPair request = tvDevice->createPressButtonRequest(TvDevice::Exit);
-        QNetworkReply *reply = networkManagerPost(request.first, request.second);
-        m_asyncActions.insert(reply, action.id());
-    } else if(action.actionTypeId() == commandInfoActionTypeId) {
-        QPair request = tvDevice->createPressButtonRequest(TvDevice::Info);
-        QNetworkReply *reply = networkManagerPost(request.first, request.second);
-        m_asyncActions.insert(reply, action.id());
-    } else if(action.actionTypeId() == commandMyAppsActionTypeId) {
-        QPair request = tvDevice->createPressButtonRequest(TvDevice::MyApps);
-        QNetworkReply *reply = networkManagerPost(request.first, request.second);
-        m_asyncActions.insert(reply, action.id());
-    } else if(action.actionTypeId() == commandProgramListActionTypeId) {
-        QPair request = tvDevice->createPressButtonRequest(TvDevice::ProgramList);
-        QNetworkReply *reply = networkManagerPost(request.first, request.second);
-        m_asyncActions.insert(reply, action.id());
-    } else {
-        return DeviceManager::DeviceErrorActionTypeNotFound;
-    }
-    return DeviceManager::DeviceErrorAsync;
-}
-
-DeviceManager::DeviceError DevicePluginLgSmartTv::displayPin(const PairingTransactionId &pairingTransactionId, const DeviceDescriptor &deviceDescriptor)
-{
-    Q_UNUSED(pairingTransactionId)
-
-    QHostAddress host = QHostAddress(deviceDescriptor.params().paramValue(hostAddressParamTypeId).toString());
-    int port = deviceDescriptor.params().paramValue(portParamTypeId).toInt();
-    QPair request = TvDevice::createDisplayKeyRequest(host, port);
-    QNetworkReply *reply = networkManagerPost(request.first, request.second);
-
-    m_showPinReply.append(reply);
-    return DeviceManager::DeviceErrorNoError;
-}
-
-DeviceManager::DeviceSetupStatus DevicePluginLgSmartTv::confirmPairing(const PairingTransactionId &pairingTransactionId, const DeviceClassId &deviceClassId, const ParamList ¶ms, const QString &secret)
-{
-    Q_UNUSED(deviceClassId)
-
-    QHostAddress host = QHostAddress(params.paramValue(hostAddressParamTypeId).toString());
-    int port = params.paramValue(portParamTypeId).toInt();
-    QPair request = TvDevice::createPairingRequest(host, port, secret);
-    QNetworkReply *reply = networkManagerPost(request.first, request.second);
-
-    m_setupPairingTv.insert(reply, pairingTransactionId);
-    m_tvKeys.insert(params.paramValue(uuidParamTypeId).toString(), secret);
-
-    return DeviceManager::DeviceSetupStatusAsync;
-}
-
-void DevicePluginLgSmartTv::networkManagerReplyReady(QNetworkReply *reply)
-{
     int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
 
     if (m_showPinReply.contains(reply)) {
@@ -274,7 +364,8 @@ void DevicePluginLgSmartTv::networkManagerReplyReady(QNetworkReply *reply)
         } else {
             // End pairing before calling setupDevice, which will always try to pair
             QPair request = TvDevice::createEndPairingRequest(reply->request().url());
-            QNetworkReply *reply = networkManagerPost(request.first, request.second);
+            QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
+            connect(reply, &QNetworkReply::finished, this, &DevicePluginLgSmartTv::onNetworkManagerReplyFinished);
             m_setupEndPairingTv.insert(reply, pairingTransactionId);
         }
     } else if (m_setupEndPairingTv.keys().contains(reply)) {
@@ -337,54 +428,6 @@ void DevicePluginLgSmartTv::networkManagerReplyReady(QNetworkReply *reply)
     reply->deleteLater();
 }
 
-void DevicePluginLgSmartTv::guhTimer()
-{
-    foreach (Device *device, m_tvList.values()) {
-        TvDevice *tv = m_tvList.key(device);
-        if (tv->paired()) {
-            refreshTv(device);
-        } else {
-            pairTvDevice(device);
-        }
-    }
-}
-
-void DevicePluginLgSmartTv::pairTvDevice(Device *device, const bool &setup)
-{
-    QHostAddress host = QHostAddress(device->paramValue(hostAddressParamTypeId).toString());
-    int port = device->paramValue(portParamTypeId).toInt();
-    QString key = device->paramValue(keyParamTypeId).toString();
-    QPair request = TvDevice::createPairingRequest(host, port, key);
-    QNetworkReply *reply = networkManagerPost(request.first, request.second);
-    if (setup) {
-        m_asyncSetup.insert(reply, device);
-    } else {
-        m_pairRequests.insert(reply, device);
-    }
-}
-
-void DevicePluginLgSmartTv::unpairTvDevice(Device *device)
-{
-    QHostAddress host = QHostAddress(device->paramValue(hostAddressParamTypeId).toString());
-    int port = device->paramValue(portParamTypeId).toInt();
-    QPair request = TvDevice::createEndPairingRequest(host, port);
-    QNetworkReply *reply = networkManagerPost(request.first, request.second);
-
-    m_deleteTv.append(reply);
-}
-
-void DevicePluginLgSmartTv::refreshTv(Device *device)
-{
-    TvDevice *tv = m_tvList.key(device);
-    // check volume information
-    QNetworkReply *volumeReply = networkManagerGet(tv->createVolumeInformationRequest());
-    m_volumeInfoRequests.insert(volumeReply, device);
-
-    // check channel information
-    QNetworkReply *channelReply = networkManagerGet(tv->createChannelInformationRequest());
-    m_channelInfoRequests.insert(channelReply, device);
-}
-
 void DevicePluginLgSmartTv::stateChanged()
 {
     TvDevice *tvDevice = static_cast(sender());
diff --git a/lgsmarttv/devicepluginlgsmarttv.h b/lgsmarttv/devicepluginlgsmarttv.h
index b063fe9f..44800c5c 100644
--- a/lgsmarttv/devicepluginlgsmarttv.h
+++ b/lgsmarttv/devicepluginlgsmarttv.h
@@ -23,8 +23,9 @@
 #ifndef DEVICEPLUGINLGSMARTTV_H
 #define DEVICEPLUGINLGSMARTTV_H
 
-#include "plugin/deviceplugin.h"
 #include "tvdevice.h"
+#include "plugintimer.h"
+#include "plugin/deviceplugin.h"
 #include "network/upnp/upnpdevicedescriptor.h"
 
 class DevicePluginLgSmartTv : public DevicePlugin
@@ -36,23 +37,19 @@ class DevicePluginLgSmartTv : public DevicePlugin
 
 public:
     explicit DevicePluginLgSmartTv();
+    ~DevicePluginLgSmartTv();
 
-    DeviceManager::HardwareResources requiredHardware() const override;
+    void init() override;
     DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override;
     DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
     void deviceRemoved(Device *device) override;
 
-    void upnpDiscoveryFinished(const QList &upnpDeviceDescriptorList) override;
-
     DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
     DeviceManager::DeviceError displayPin(const PairingTransactionId &pairingTransactionId, const DeviceDescriptor &deviceDescriptor) override;
     DeviceManager::DeviceSetupStatus confirmPairing(const PairingTransactionId &pairingTransactionId, const DeviceClassId &deviceClassId, const ParamList ¶ms, const QString &secret) override;
 
-    void networkManagerReplyReady(QNetworkReply *reply) override;
-
-    void guhTimer() override;
-
 private:
+    PluginTimer *m_pluginTimer = nullptr;
     QHash m_tvList;
     QHash m_tvKeys;
 
@@ -78,6 +75,9 @@ private:
     void refreshTv(Device *device);
 
 private slots:
+    void onPluginTimer();
+    void onUpnpDiscoveryFinished();
+    void onNetworkManagerReplyFinished();
     void stateChanged();
 };
 
diff --git a/lircd/devicepluginlircd.cpp b/lircd/devicepluginlircd.cpp
deleted file mode 100644
index 140ef4a9..00000000
--- a/lircd/devicepluginlircd.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- *                                                                         *
- *  Copyright (C) 2015 Simon Stürz                    *
- *  Copyright (C) 2014 Michael Zanetti            *
- *                                                                         *
- *  This file is part of guh.                                              *
- *                                                                         *
- *  This library is free software; you can redistribute it and/or          *
- *  modify it under the terms of the GNU Lesser General Public             *
- *  License as published by the Free Software Foundation; either           *
- *  version 2.1 of the License, or (at your option) any later version.     *
- *                                                                         *
- *  This library 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 library; If not, see                           *
- *  .                                        *
- *                                                                         *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-/*!
-    \page lirc.html
-    \title LIRC
-    \brief Plugin for the LIRC infrared daemon.
-
-    \ingroup plugins
-    \ingroup guh-plugins-maker
-
-    This plugin allows to interact with \l{http://www.lirc.org/}{LIRC} daemon and controll commonly used remote controls.
-    If lircd (LIRC daemon) is configured on your system, guh will connect to the lirc daemon and all configured remote
-    controls of lircd will appear in guh.
-
-    \chapter Plugin properties
-    Following JSON file contains the definition and the description of all available \l{DeviceClass}{DeviceClasses}
-    and \l{Vendor}{Vendors} of this \l{DevicePlugin}.
-
-    For more details how to read this JSON file please check out the documentation for \l{The plugin JSON File}.
-
-    \quotefile plugins/deviceplugins/lircd/devicepluginlircd.json
-*/
-
-#include "devicepluginlircd.h"
-
-#include "plugin/device.h"
-#include "devicemanager.h"
-#include "plugininfo.h"
-#include "lircdclient.h"
-
-#include 
-#include 
-
-DeviceClassId lircdDeviceClassId = DeviceClassId("5c2bc4cd-ba6c-4052-b6cd-1db83323ea22");
-EventTypeId LircKeypressEventTypeId = EventTypeId("8711471a-fa0e-410b-b174-dfc3d2aeffb1");
-
-DevicePluginLircd::DevicePluginLircd()
-{
-    m_lircClient = new LircClient(this);
-
-    //m_lircClient->connect();
-    connect(m_lircClient, &LircClient::buttonPressed, this, &DevicePluginLircd::buttonPressed);
-}
-
-DeviceManager::HardwareResources DevicePluginLircd::requiredHardware() const
-{
-    return DeviceManager::HardwareResourceNone;
-}
-
-void DevicePluginLircd::buttonPressed(const QString &remoteName, const QString &buttonName, int repeat)
-{
-    Device *remote = nullptr;
-    QList configuredRemotes = deviceManager()->findConfiguredDevices(lircdDeviceClassId);
-    foreach (Device *device, configuredRemotes) {
-        if (device->paramValue(nameParamTypeId).toString() == remoteName) {
-            remote = device;
-            break;
-        }
-    }
-    if (!remote) {
-        qCWarning(dcLircd) << "Unhandled remote" << remoteName << buttonName;
-        return;
-    }
-
-    qCDebug(dcLircd) << "found remote" << remoteName << supportedDevices().first().eventTypes().count();
-    ParamList params;
-    Param buttonParam(buttonParamTypeId, buttonName);
-    params.append(buttonParam);
-    Param repeatParam(repeatParamTypeId, repeat);
-    params.append(repeatParam);
-    Event event(LircKeypressEventTypeId, remote->id(), params);
-    emitEvent(event);
-}
-
-//QVariantMap DevicePluginLircd::configuration() const
-//{
-//    return m_config;
-//}
-
-//void DevicePluginLircd::setConfiguration(const QVariantMap &configuration)
-//{
-//    m_config = configuration;
-//}
diff --git a/lircd/devicepluginlircd.h b/lircd/devicepluginlircd.h
deleted file mode 100644
index ac8fcb7f..00000000
--- a/lircd/devicepluginlircd.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- *                                                                         *
- *  Copyright (C) 2015 Simon Stürz                    *
- *  Copyright (C) 2014 Michael Zanetti            *
- *                                                                         *
- *  This file is part of guh.                                              *
- *                                                                         *
- *  This library is free software; you can redistribute it and/or          *
- *  modify it under the terms of the GNU Lesser General Public             *
- *  License as published by the Free Software Foundation; either           *
- *  version 2.1 of the License, or (at your option) any later version.     *
- *                                                                         *
- *  This library 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 library; If not, see                           *
- *  .                                        *
- *                                                                         *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-#ifndef DEVICEPLUGINLIRCD_H
-#define DEVICEPLUGINLIRCD_H
-
-#include "plugin/deviceplugin.h"
-
-#include 
-
-class LircClient;
-
-class DevicePluginLircd: public DevicePlugin
-{
-    Q_OBJECT
-
-    Q_PLUGIN_METADATA(IID "guru.guh.DevicePlugin" FILE "devicepluginlircd.json")
-    Q_INTERFACES(DevicePlugin)
-
-public:
-    explicit DevicePluginLircd();
-
-    DeviceManager::HardwareResources requiredHardware() const override;
-
-//    QVariantMap configuration() const override;
-//    void setConfiguration(const QVariantMap &configuration) override;
-
-private slots:
-    void buttonPressed(const QString &remoteName, const QString &buttonName, int repeat);
-
-private:
-    LircClient *m_lircClient;
-//    QVariantMap m_config;
-};
-
-#endif // DEVICEPLUGINBOLIRCD_H
diff --git a/lircd/devicepluginlircd.json b/lircd/devicepluginlircd.json
deleted file mode 100644
index e28bd865..00000000
--- a/lircd/devicepluginlircd.json
+++ /dev/null
@@ -1,55 +0,0 @@
-{
-    "name": "Lirc receiver",
-    "idName": "Lircd",
-    "id": "075f734f-4d76-4ce3-9ef8-34c212285676",
-    "vendors": [
-        {
-            "name": "Lirc",
-            "idName": "lirc",
-            "id": "9a53049c-8828-4b87-b3f6-7bc7708196cd",
-            "deviceClasses": [
-                {
-                    "id": "5c2bc4cd-ba6c-4052-b6cd-1db83323ea22",
-                    "idName": "irReceiver",
-                    "name": "IR receiver",
-                    "basicTags": [
-                        "Service",
-                        "Actuator",
-                        "Multimedia"
-                    ],
-                    "createMethods": ["user"],
-                    "paramTypes": [
-                        {
-                            "id": "06704d73-66d1-486b-bc0b-0346534983a1",
-                            "idName": "name",
-                            "name": "remoteName",
-                            "type": "QString",
-                            "inputType": "TextLine"
-                        }
-                    ],
-                    "eventTypes": [
-                        {
-                            "id": "8711471a-fa0e-410b-b174-dfc3d2aeffb1",
-                            "idName": "buttonPressed",
-                            "name": "button pressed",
-                            "paramTypes": [
-                                {
-                                    "id": "a6344673-17f5-4a37-b59e-f342a2877909",
-                                    "idName": "button",
-                                    "name": "button",
-                                    "type": "QString"
-                                },
-                                {
-                                    "id": "a98d9348-1154-45c6-a163-c9f0477be626",
-                                    "idName": "repeat",
-                                    "name": "repeat",
-                                    "type": "int"
-                                }
-                            ]
-                        }
-                    ]
-                }
-            ]
-        }
-    ]
-}
diff --git a/lircd/lircd.pro b/lircd/lircd.pro
deleted file mode 100644
index 18dec536..00000000
--- a/lircd/lircd.pro
+++ /dev/null
@@ -1,20 +0,0 @@
-TRANSLATIONS = translations/en_US.ts \
-               translations/de_DE.ts
-
-# Note: include after the TRANSLATIONS definition
-include(../plugins.pri)
-
-TARGET = $$qtLibraryTarget(guh_devicepluginlircd)
-
-QT += network
-
-SOURCES += \
-    devicepluginlircd.cpp \
-    lircdclient.cpp \
-
-HEADERS += \
-    devicepluginlircd.h \
-    lircdclient.h \
-
-
-
diff --git a/lircd/lircdclient.cpp b/lircd/lircdclient.cpp
deleted file mode 100644
index da24af36..00000000
--- a/lircd/lircdclient.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- *                                                                         *
- *  Copyright (C) 2015 Simon Stürz                    *
- *                                                                         *
- *  This file is part of guh.                                              *
- *                                                                         *
- *  This library is free software; you can redistribute it and/or          *
- *  modify it under the terms of the GNU Lesser General Public             *
- *  License as published by the Free Software Foundation; either           *
- *  version 2.1 of the License, or (at your option) any later version.     *
- *                                                                         *
- *  This library 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 library; If not, see                           *
- *  .                                        *
- *                                                                         *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-#include "lircdclient.h"
-#include "extern-plugininfo.h"
-
-#include 
-#include 
-
-LircClient::LircClient(QObject *parent) :
-    QObject(parent)
-{
-    m_socket = new QLocalSocket(this);
-    QObject::connect(m_socket, &QLocalSocket::readyRead, this, &LircClient::readyRead);
-}
-
-bool LircClient::connect()
-{
-    m_socket->connectToServer("/var/run/lirc/lircd", QIODevice::ReadWrite);
-    if (!m_socket->isOpen()) {
-        qCWarning(dcLircd) << "--> Lirc daemon NOT available.";
-        return false;
-    }
-    m_socket->write("LIST\n");
-    qCDebug(dcLircd) << "--> Lirc daemon available.";
-    return true;
-}
-
-void LircClient::readyRead()
-{
-    qCDebug(dcLircd) << "got data" << m_socket->readAll();
-
-    bool inBlock = false;
-    while (m_socket->canReadLine()) {
-        QByteArray line = m_socket->readLine().trimmed();
-        qCDebug(dcLircd) << "got line:" << line;
-        if (line == "BEGIN") {
-            inBlock = true;
-            continue;
-        }
-        if (line == "LIST") {
-            if (m_socket->readLine().trimmed() == "SUCCESS") {
-                readRemotes();
-            } else {
-                qCWarning(dcLircd) << "Error reading remotes from Lircd";
-            }
-            continue;
-        }
-        if ( line == "END") {
-            inBlock = false;
-            continue;
-        }
-
-        if (!inBlock) {
-            QList parts = line.split(' ');
-            if (parts.count() != 4) {
-                qCWarning(dcLircd) << "Don't understand IR command. ignoring...";
-                continue;
-            }
-            qCDebug(dcLircd) << "emitting buttonpress";
-            emit buttonPressed(QString(parts.at(3)), QString(parts.at(2)), parts.at(1).toInt());
-        }
-    }
-}
-
-void LircClient::readRemotes()
-{
-    m_socket->readLine(); // IGNORE DATA
-    int remoteCount = m_socket->readLine().trimmed().toInt();
-    qCDebug(dcLircd) << "found" << remoteCount << "lirc remotes";
-    for (int i = 0; i < remoteCount; i++) {
-        QByteArray line = m_socket->readLine().trimmed();
-        m_remotes.append(line);
-    }
-}
-
diff --git a/lircd/lircdclient.h b/lircd/lircdclient.h
deleted file mode 100644
index 7c07535e..00000000
--- a/lircd/lircdclient.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- *                                                                         *
- *  Copyright (C) 2015 Simon Stürz                    *
- *                                                                         *
- *  This file is part of guh.                                              *
- *                                                                         *
- *  This library is free software; you can redistribute it and/or          *
- *  modify it under the terms of the GNU Lesser General Public             *
- *  License as published by the Free Software Foundation; either           *
- *  version 2.1 of the License, or (at your option) any later version.     *
- *                                                                         *
- *  This library 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 library; If not, see                           *
- *  .                                        *
- *                                                                         *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-#ifndef LIRCCLIENT_H
-#define LIRCCLIENT_H
-
-#include 
-#include 
-#include 
-
-class LircClient : public QObject
-{
-    Q_OBJECT
-public:
-    explicit LircClient(QObject *parent = 0);
-
-    bool connect();
-
-signals:
-    void buttonPressed(const QString &remoteName, const QString &buttonName, int repeat);
-
-private slots:
-    void readyRead();
-
-private:
-    void readRemotes();
-
-private:
-    QLocalSocket *m_socket;
-
-    QStringList m_remotes;
-};
-
-#endif // LIRCCLIENT_H
diff --git a/mailnotification/devicepluginmailnotification.cpp b/mailnotification/devicepluginmailnotification.cpp
index 173a9947..968ae01a 100644
--- a/mailnotification/devicepluginmailnotification.cpp
+++ b/mailnotification/devicepluginmailnotification.cpp
@@ -164,11 +164,6 @@ DeviceManager::DeviceSetupStatus DevicePluginMailNotification::setupDevice(Devic
     return DeviceManager::DeviceSetupStatusFailure;
 }
 
-DeviceManager::HardwareResources DevicePluginMailNotification::requiredHardware() const
-{
-    return DeviceManager::HardwareResourceNone;
-}
-
 DeviceManager::DeviceError DevicePluginMailNotification::executeAction(Device *device, const Action &action)
 {
     if(action.actionTypeId() == sendMailActionTypeId) {
diff --git a/mailnotification/devicepluginmailnotification.h b/mailnotification/devicepluginmailnotification.h
index 257f4aba..ea173d41 100644
--- a/mailnotification/devicepluginmailnotification.h
+++ b/mailnotification/devicepluginmailnotification.h
@@ -38,7 +38,6 @@ public:
     ~DevicePluginMailNotification();
 
     DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
-    DeviceManager::HardwareResources requiredHardware() const override;
     DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
     void deviceRemoved(Device *device) override;
 
diff --git a/netatmo/devicepluginnetatmo.cpp b/netatmo/devicepluginnetatmo.cpp
index 06c6f534..c8a8128c 100644
--- a/netatmo/devicepluginnetatmo.cpp
+++ b/netatmo/devicepluginnetatmo.cpp
@@ -42,6 +42,7 @@
 #include "devicepluginnetatmo.h"
 #include "plugin/device.h"
 #include "plugininfo.h"
+#include "network/networkaccessmanager.h"
 
 #include 
 #include 
@@ -52,9 +53,15 @@ DevicePluginNetatmo::DevicePluginNetatmo()
 
 }
 
-DeviceManager::HardwareResources DevicePluginNetatmo::requiredHardware() const
+DevicePluginNetatmo::~DevicePluginNetatmo()
 {
-    return DeviceManager::HardwareResourceNetworkManager | DeviceManager::HardwareResourceTimer;
+    hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
+}
+
+void DevicePluginNetatmo::init()
+{
+    m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(30);
+    connect(m_pluginTimer, &PluginTimer::timeout, this, &DevicePluginNetatmo::onPluginTimer);
 }
 
 DeviceManager::DeviceSetupStatus DevicePluginNetatmo::setupDevice(Device *device)
@@ -119,49 +126,6 @@ void DevicePluginNetatmo::deviceRemoved(Device *device)
     }
 }
 
-void DevicePluginNetatmo::networkManagerReplyReady(QNetworkReply *reply)
-{
-    int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
-
-    // update values request
-    if (m_refreshRequest.keys().contains(reply)) {
-        Device *device = m_refreshRequest.take(reply);
-
-        // check HTTP status code
-        if (status != 200) {
-            qCWarning(dcNetatmo) << "Device list reply HTTP error:" << status << reply->errorString();
-            device->setStateValue(availableStateTypeId, false);
-            reply->deleteLater();
-            return;
-        }
-
-        // check JSON file
-        QJsonParseError error;
-        QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll(), &error);
-        if (error.error != QJsonParseError::NoError) {
-            qCWarning(dcNetatmo) << "Device list reply JSON error:" << error.errorString();
-            reply->deleteLater();
-            return;
-        }
-
-        //qCDebug(dcNetatmo) << jsonDoc.toJson();
-        processRefreshData(jsonDoc.toVariant().toMap(), device->id().toString());
-    }
-
-    reply->deleteLater();
-}
-
-void DevicePluginNetatmo::guhTimer()
-{
-    foreach (OAuth2 *authentication, m_authentications.keys()) {
-        if (authentication->authenticated()) {
-            refreshData(m_authentications.value(authentication), authentication->token());
-        } else {
-            authentication->startAuthentication();
-        }
-    }
-}
-
 DeviceManager::DeviceError DevicePluginNetatmo::executeAction(Device *device, const Action &action)
 {
     Q_UNUSED(device)
@@ -178,7 +142,9 @@ void DevicePluginNetatmo::refreshData(Device *device, const QString &token)
     QUrl url("https://api.netatmo.com/api/devicelist");
     url.setQuery(query);
 
-    QNetworkReply *reply = networkManagerGet(QNetworkRequest(url));
+    QNetworkReply *reply = hardwareManager()->networkManager()->get(QNetworkRequest(url));
+    connect(reply, &QNetworkReply::finished, this, &DevicePluginNetatmo::onNetworkReplyFinished);
+
     m_refreshRequest.insert(reply, device);
 }
 
@@ -267,6 +233,50 @@ Device *DevicePluginNetatmo::findOutdoorDevice(const QString &macAddress)
     return 0;
 }
 
+void DevicePluginNetatmo::onPluginTimer()
+{
+    foreach (OAuth2 *authentication, m_authentications.keys()) {
+        if (authentication->authenticated()) {
+            refreshData(m_authentications.value(authentication), authentication->token());
+        } else {
+            authentication->startAuthentication();
+        }
+    }
+}
+
+void DevicePluginNetatmo::onNetworkReplyFinished()
+{
+    QNetworkReply *reply = static_cast(sender());
+    int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+
+    // update values request
+    if (m_refreshRequest.keys().contains(reply)) {
+        Device *device = m_refreshRequest.take(reply);
+
+        // check HTTP status code
+        if (status != 200) {
+            qCWarning(dcNetatmo) << "Device list reply HTTP error:" << status << reply->errorString();
+            device->setStateValue(availableStateTypeId, false);
+            reply->deleteLater();
+            return;
+        }
+
+        // check JSON file
+        QJsonParseError error;
+        QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll(), &error);
+        if (error.error != QJsonParseError::NoError) {
+            qCWarning(dcNetatmo) << "Device list reply JSON error:" << error.errorString();
+            reply->deleteLater();
+            return;
+        }
+
+        //qCDebug(dcNetatmo) << jsonDoc.toJson();
+        processRefreshData(jsonDoc.toVariant().toMap(), device->id().toString());
+    }
+
+    reply->deleteLater();
+}
+
 void DevicePluginNetatmo::onAuthenticationChanged()
 {
     OAuth2 *authentication = static_cast(sender());
diff --git a/netatmo/devicepluginnetatmo.h b/netatmo/devicepluginnetatmo.h
index 05b8e6e3..26cb42dd 100644
--- a/netatmo/devicepluginnetatmo.h
+++ b/netatmo/devicepluginnetatmo.h
@@ -23,6 +23,7 @@
 #ifndef DEVICEPLUGINNETATMO_H
 #define DEVICEPLUGINNETATMO_H
 
+#include "plugintimer.h"
 #include "plugin/deviceplugin.h"
 #include "network/oauth2.h"
 #include "netatmobasestation.h"
@@ -39,18 +40,17 @@ class DevicePluginNetatmo : public DevicePlugin
 
 public:
     explicit DevicePluginNetatmo();
+    ~DevicePluginNetatmo();
 
-    DeviceManager::HardwareResources requiredHardware() const override;
+    void init() override;
     DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
     void deviceRemoved(Device *device) override;
-    void networkManagerReplyReady(QNetworkReply *reply) override;
-
-    void guhTimer() override;
 
 public slots:
     DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
 
 private:
+    PluginTimer *m_pluginTimer;
     QList m_asyncSetups;
 
     QHash m_authentications;
@@ -66,6 +66,8 @@ private:
     Device *findOutdoorDevice(const QString &macAddress);
 
 private slots:
+    void onPluginTimer();
+    void onNetworkReplyFinished();
     void onAuthenticationChanged();
     void onIndoorStatesChanged();
     void onOutdoorStatesChanged();
diff --git a/networkdetector/devicepluginnetworkdetector.cpp b/networkdetector/devicepluginnetworkdetector.cpp
index 284d7c10..bf4624b3 100644
--- a/networkdetector/devicepluginnetworkdetector.cpp
+++ b/networkdetector/devicepluginnetworkdetector.cpp
@@ -61,11 +61,19 @@ DevicePluginNetworkDetector::DevicePluginNetworkDetector()
 
 DevicePluginNetworkDetector::~DevicePluginNetworkDetector()
 {
+    hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
+
     if (m_discovery->isRunning()) {
         m_discovery->abort();
     }
 }
 
+void DevicePluginNetworkDetector::init()
+{
+    m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(10);
+    connect(m_pluginTimer, &PluginTimer::timeout, this, &DevicePluginNetworkDetector::onPluginTimer);
+}
+
 DeviceManager::DeviceSetupStatus DevicePluginNetworkDetector::setupDevice(Device *device)
 {
     qCDebug(dcNetworkDetector()) << "Setup" << device->name() << device->params();
@@ -81,7 +89,6 @@ DeviceManager::DeviceError DevicePluginNetworkDetector::discoverDevices(const De
 {
     Q_UNUSED(params)
 
-
     if (deviceClassId != networkDeviceClassId)
         return DeviceManager::DeviceErrorDeviceClassNotFound;
 
@@ -95,11 +102,6 @@ DeviceManager::DeviceError DevicePluginNetworkDetector::discoverDevices(const De
     return DeviceManager::DeviceErrorAsync;
 }
 
-DeviceManager::HardwareResources DevicePluginNetworkDetector::requiredHardware() const
-{
-    return DeviceManager::HardwareResourceTimer;
-}
-
 void DevicePluginNetworkDetector::deviceRemoved(Device *device)
 {
     DeviceMonitor *monitor = m_monitors.key(device);
@@ -107,7 +109,7 @@ void DevicePluginNetworkDetector::deviceRemoved(Device *device)
     delete monitor;
 }
 
-void DevicePluginNetworkDetector::guhTimer()
+void DevicePluginNetworkDetector::onPluginTimer()
 {
     foreach (DeviceMonitor *monitor, m_monitors.keys()) {
         monitor->update();
diff --git a/networkdetector/devicepluginnetworkdetector.h b/networkdetector/devicepluginnetworkdetector.h
index c9946827..705f935f 100644
--- a/networkdetector/devicepluginnetworkdetector.h
+++ b/networkdetector/devicepluginnetworkdetector.h
@@ -27,6 +27,7 @@
 #include "plugin/deviceplugin.h"
 #include "host.h"
 #include "discovery.h"
+#include "plugintimer.h"
 #include "devicemonitor.h"
 
 #include 
@@ -44,19 +45,23 @@ public:
     explicit DevicePluginNetworkDetector();
     ~DevicePluginNetworkDetector();
 
+    void init() override;
+
     DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
     DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override;
-    DeviceManager::HardwareResources requiredHardware() const override;
     void deviceRemoved(Device *device) override;
 
-    void guhTimer() override;
 
 private slots:
     void discoveryFinished(const QList &hosts);
 
     void deviceReachableChanged(bool reachable);
     void deviceAddressChanged(const QString &address);
+
+    void onPluginTimer();
+
 private:
+    PluginTimer *m_pluginTimer = nullptr;
     Discovery *m_discovery = nullptr;
     QHash m_monitors;
 };
diff --git a/openweathermap/devicepluginopenweathermap.cpp b/openweathermap/devicepluginopenweathermap.cpp
index 03347692..b6a2ce07 100644
--- a/openweathermap/devicepluginopenweathermap.cpp
+++ b/openweathermap/devicepluginopenweathermap.cpp
@@ -48,7 +48,6 @@
 */
 
 #include "devicepluginopenweathermap.h"
-
 #include "plugin/device.h"
 #include "devicemanager.h"
 #include "plugininfo.h"
@@ -66,12 +65,17 @@ DevicePluginOpenweathermap::DevicePluginOpenweathermap()
     // max 50000 calls/day
     m_apiKey = "c1b9d5584bb740804871583f6c62744f";
 
-    // update every 15 minutes
-    m_timer = new QTimer(this);
-    m_timer->setSingleShot(false);
-    m_timer->setInterval(900000);
+}
 
-    connect(m_timer, SIGNAL(timeout()), this, SLOT(onTimeout()));
+DevicePluginOpenweathermap::~DevicePluginOpenweathermap()
+{
+    hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
+}
+
+void DevicePluginOpenweathermap::init()
+{
+    m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(900);
+    connect(m_pluginTimer, &PluginTimer::timeout, this, &DevicePluginOpenweathermap::onPluginTimer);
 }
 
 DeviceManager::DeviceError DevicePluginOpenweathermap::discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms)
@@ -96,19 +100,11 @@ DeviceManager::DeviceSetupStatus DevicePluginOpenweathermap::setupDevice(Device
     if (device->deviceClassId() != openweathermapDeviceClassId)
         return DeviceManager::DeviceSetupStatusFailure;
 
-    if (!m_timer->isActive())
-        m_timer->start();
-
     update(device);
 
     return DeviceManager::DeviceSetupStatusSuccess;
 }
 
-DeviceManager::HardwareResources DevicePluginOpenweathermap::requiredHardware() const
-{
-    return DeviceManager::HardwareResourceNetworkManager;
-}
-
 DeviceManager::DeviceError DevicePluginOpenweathermap::executeAction(Device *device, const Action &action)
 {
     if(action.actionTypeId() == refreshWeatherActionTypeId){
@@ -128,13 +124,12 @@ void DevicePluginOpenweathermap::deviceRemoved(Device *device)
             reply->deleteLater();
         }
     }
-
-    if (myDevices().isEmpty())
-        m_timer->stop();
 }
 
-void DevicePluginOpenweathermap::networkManagerReplyReady(QNetworkReply *reply)
+void DevicePluginOpenweathermap::networkManagerReplyReady()
 {
+    QNetworkReply *reply = static_cast(sender());
+
     if (reply->error()) {
         qCWarning(dcOpenWeatherMap) << "OpenWeatherMap reply error: " << reply->errorString();
         reply->deleteLater();
@@ -163,6 +158,7 @@ void DevicePluginOpenweathermap::networkManagerReplyReady(QNetworkReply *reply)
 
 void DevicePluginOpenweathermap::update(Device *device)
 {
+    qCDebug(dcOpenWeatherMap()) << "Refresh data for" << device->name();
     QUrl url("http://api.openweathermap.org/data/2.5/weather");
     QUrlQuery query;
     query.addQueryItem("id", device->paramValue(idParamTypeId).toString());
@@ -171,13 +167,15 @@ void DevicePluginOpenweathermap::update(Device *device)
     query.addQueryItem("appid", m_apiKey);
     url.setQuery(query);
 
-    QNetworkReply *reply = networkManagerGet(QNetworkRequest(url));
+    QNetworkReply *reply = hardwareManager()->networkManager()->get(QNetworkRequest(url));
+    connect(reply, &QNetworkReply::finished, this, &DevicePluginOpenweathermap::networkManagerReplyReady);
     m_weatherReplies.insert(reply, device);
 }
 
 void DevicePluginOpenweathermap::searchAutodetect()
 {
-    QNetworkReply *reply = networkManagerGet(QNetworkRequest(QUrl("http://ip-api.com/json")));
+    QNetworkReply *reply = hardwareManager()->networkManager()->get(QNetworkRequest(QUrl("http://ip-api.com/json")));
+    connect(reply, &QNetworkReply::finished, this, &DevicePluginOpenweathermap::networkManagerReplyReady);
     m_autodetectionReplies.append(reply);
 }
 
@@ -192,7 +190,8 @@ void DevicePluginOpenweathermap::search(QString searchString)
     query.addQueryItem("appid", m_apiKey);
     url.setQuery(query);
 
-    QNetworkReply *reply = networkManagerGet(QNetworkRequest(url));
+    QNetworkReply *reply = hardwareManager()->networkManager()->get(QNetworkRequest(url));
+    connect(reply, &QNetworkReply::finished, this, &DevicePluginOpenweathermap::networkManagerReplyReady);
     m_searchReplies.append(reply);
 }
 
@@ -209,7 +208,8 @@ void DevicePluginOpenweathermap::searchGeoLocation(double lat, double lon)
     query.addQueryItem("appid", m_apiKey);
     url.setQuery(query);
 
-    QNetworkReply *reply = networkManagerGet(QNetworkRequest(url));
+    QNetworkReply *reply = hardwareManager()->networkManager()->get(QNetworkRequest(url));
+    connect(reply, &QNetworkReply::finished, this, &DevicePluginOpenweathermap::networkManagerReplyReady);
     m_searchGeoReplies.append(reply);
 }
 
@@ -392,7 +392,7 @@ void DevicePluginOpenweathermap::processWeatherData(const QByteArray &data, Devi
     }
 }
 
-void DevicePluginOpenweathermap::onTimeout()
+void DevicePluginOpenweathermap::onPluginTimer()
 {
     foreach (Device *device, myDevices()) {
         update(device);
diff --git a/openweathermap/devicepluginopenweathermap.h b/openweathermap/devicepluginopenweathermap.h
index 6ec750b7..ac96b0d9 100644
--- a/openweathermap/devicepluginopenweathermap.h
+++ b/openweathermap/devicepluginopenweathermap.h
@@ -23,9 +23,12 @@
 #ifndef DEVICEPLUGINOPENWEATHERMAP_H
 #define DEVICEPLUGINOPENWEATHERMAP_H
 
+#include "plugintimer.h"
 #include "plugin/deviceplugin.h"
+#include "network/networkaccessmanager.h"
 
 #include 
+#include 
 
 class DevicePluginOpenweathermap : public DevicePlugin
 {
@@ -36,17 +39,17 @@ class DevicePluginOpenweathermap : public DevicePlugin
 
 public:
     explicit DevicePluginOpenweathermap();
+    ~DevicePluginOpenweathermap();
 
+    void init() override;
     DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override;
     DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
-    DeviceManager::HardwareResources requiredHardware() const override;
     DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
 
     void deviceRemoved(Device *device) override;
-    void networkManagerReplyReady(QNetworkReply *reply) override;
-    //void guhTimer() override;
 
 private:
+    PluginTimer *m_pluginTimer = nullptr;
     QList m_autodetectionReplies;
     QList m_searchReplies;
     QList m_searchGeoReplies;
@@ -59,7 +62,6 @@ private:
     double m_longitude;
     double m_latitude;
 
-    QTimer *m_timer;
     QString m_apiKey;
 
     void update(Device *device);
@@ -75,7 +77,8 @@ private:
     void processWeatherData(const QByteArray &data, Device *device);
 
 private slots:
-    void onTimeout();
+    void networkManagerReplyReady();
+    void onPluginTimer();
 
 };
 
diff --git a/orderbutton/devicepluginorderbutton.cpp b/orderbutton/devicepluginorderbutton.cpp
index 7101db24..23ebb1ef 100644
--- a/orderbutton/devicepluginorderbutton.cpp
+++ b/orderbutton/devicepluginorderbutton.cpp
@@ -42,16 +42,22 @@
 #include "devicepluginorderbutton.h"
 #include "plugin/device.h"
 #include "plugininfo.h"
+#include "network/networkaccessmanager.h"
 
 DevicePluginOrderButton::DevicePluginOrderButton()
 {
 
 }
 
-DeviceManager::HardwareResources DevicePluginOrderButton::requiredHardware() const
+DevicePluginOrderButton::~DevicePluginOrderButton()
 {
-    // We need the NetworkAccessManager for node discovery and the timer for ping requests
-    return DeviceManager::HardwareResourceNetworkManager | DeviceManager::HardwareResourceTimer;
+    hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
+}
+
+void DevicePluginOrderButton::init()
+{
+    m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(10);
+    connect(m_pluginTimer, &PluginTimer::timeout, this, &DevicePluginOrderButton::onPluginTimer);
 }
 
 DeviceManager::DeviceSetupStatus DevicePluginOrderButton::setupDevice(Device *device)
@@ -96,68 +102,18 @@ DeviceManager::DeviceError DevicePluginOrderButton::discoverDevices(const Device
     url.setScheme("http");
     url.setHost(address.toString());
 
-    m_asyncNodeScans.insert(networkManagerGet(QNetworkRequest(url)), deviceClassId);
+    QNetworkReply *reply = hardwareManager()->networkManager()->get(QNetworkRequest(url));
+    connect(reply, &QNetworkReply::finished, this, &DevicePluginOrderButton::onNetworkReplyFinished);
+    m_asyncNodeScans.insert(reply, deviceClassId);
     return DeviceManager::DeviceErrorAsync;
 }
 
-void DevicePluginOrderButton::networkManagerReplyReady(QNetworkReply *reply)
-{
-    if (m_asyncNodeScans.keys().contains(reply)) {
-        DeviceClassId deviceClassId = m_asyncNodeScans.take(reply);
-        // Check HTTP status code
-        if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) {
-            qCWarning(dcOrderButton) << "Node scan reply HTTP error:" << reply->errorString();
-            emit devicesDiscovered(deviceClassId, QList());
-            reply->deleteLater();
-            return;
-        }
-
-        QByteArray data = reply->readAll();
-        qCDebug(dcOrderButton) << "Node discovery finished:" << endl << data;
-
-        QList deviceDescriptors;
-        QList lines = data.split('\n');
-        qCDebug(dcOrderButton) << lines;
-        foreach (const QByteArray &line, lines) {
-            if (line.isEmpty())
-                continue;
-
-            QHostAddress address(QString(line.left(line.length() - 4)));
-            if (address.isNull())
-                continue;
-
-            qCDebug(dcOrderButton) << "Found node" << address.toString();
-            // Create a deviceDescriptor for each found address
-            DeviceDescriptor descriptor(deviceClassId, "Order Button", address.toString());
-            ParamList params;
-            params.append(Param(hostParamTypeId, address.toString()));
-            descriptor.setParams(params);
-            deviceDescriptors.append(descriptor);
-        }
-        // Inform the user which devices were found
-        emit devicesDiscovered(deviceClassId, deviceDescriptors);
-    }
-
-    // Delete the HTTP reply
-    reply->deleteLater();
-}
-
 void DevicePluginOrderButton::postSetupDevice(Device *device)
 {
     // Try to ping the device after a successful setup
     pingDevice(device);
 }
 
-void DevicePluginOrderButton::guhTimer()
-{
-    // Try to ping each device every 10 seconds to make sure it is still reachable
-    foreach (Device *device, myDevices()) {
-        if (device->deviceClassId() == orderbuttonDeviceClassId) {
-            pingDevice(device);
-        }
-    }
-}
-
 DeviceManager::DeviceError DevicePluginOrderButton::executeAction(Device *device, const Action &action)
 {
     if (device->deviceClassId() != orderbuttonDeviceClassId)
@@ -360,6 +316,60 @@ Device *DevicePluginOrderButton::findDevice(const QHostAddress &address)
     return NULL;
 }
 
+void DevicePluginOrderButton::onPluginTimer()
+{
+    // Try to ping each device every 10 seconds to make sure it is still reachable
+    foreach (Device *device, myDevices()) {
+        if (device->deviceClassId() == orderbuttonDeviceClassId) {
+            pingDevice(device);
+        }
+    }
+}
+
+void DevicePluginOrderButton::onNetworkReplyFinished()
+{
+    QNetworkReply *reply = static_cast(sender());
+
+    if (m_asyncNodeScans.keys().contains(reply)) {
+        DeviceClassId deviceClassId = m_asyncNodeScans.take(reply);
+        // Check HTTP status code
+        if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) {
+            qCWarning(dcOrderButton) << "Node scan reply HTTP error:" << reply->errorString();
+            emit devicesDiscovered(deviceClassId, QList());
+            reply->deleteLater();
+            return;
+        }
+
+        QByteArray data = reply->readAll();
+        qCDebug(dcOrderButton) << "Node discovery finished:" << endl << data;
+
+        QList deviceDescriptors;
+        QList lines = data.split('\n');
+        qCDebug(dcOrderButton) << lines;
+        foreach (const QByteArray &line, lines) {
+            if (line.isEmpty())
+                continue;
+
+            QHostAddress address(QString(line.left(line.length() - 4)));
+            if (address.isNull())
+                continue;
+
+            qCDebug(dcOrderButton) << "Found node" << address.toString();
+            // Create a deviceDescriptor for each found address
+            DeviceDescriptor descriptor(deviceClassId, "Order Button", address.toString());
+            ParamList params;
+            params.append(Param(hostParamTypeId, address.toString()));
+            descriptor.setParams(params);
+            deviceDescriptors.append(descriptor);
+        }
+        // Inform the user which devices were found
+        emit devicesDiscovered(deviceClassId, deviceDescriptors);
+    }
+
+    // Delete the HTTP reply
+    reply->deleteLater();
+}
+
 void DevicePluginOrderButton::coapReplyFinished(CoapReply *reply)
 {
     if (m_pingReplies.contains(reply)) {
diff --git a/orderbutton/devicepluginorderbutton.h b/orderbutton/devicepluginorderbutton.h
index 9e3b2532..f81c2a87 100644
--- a/orderbutton/devicepluginorderbutton.h
+++ b/orderbutton/devicepluginorderbutton.h
@@ -27,6 +27,7 @@
 #include "plugin/deviceplugin.h"
 #include "types/action.h"
 #include "coap/coap.h"
+#include "plugintimer.h"
 
 #include 
 
@@ -38,19 +39,17 @@ class DevicePluginOrderButton : public DevicePlugin
 
 public:
     explicit DevicePluginOrderButton();
+    ~DevicePluginOrderButton();
 
-    DeviceManager::HardwareResources requiredHardware() const override;
+    void init() override;
     DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override;
     DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
     void deviceRemoved(Device *device) override;
-    void networkManagerReplyReady(QNetworkReply *reply) override;
-
     void postSetupDevice(Device *device) override;
-
-    void guhTimer() override;
     DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
 
 private:
+    PluginTimer *m_pluginTimer = nullptr;
     QPointer m_coap;
     QHash m_asyncNodeScans;
     QHash m_enableNotification;
@@ -81,6 +80,8 @@ private:
     Device *findDevice(const QHostAddress &address);
 
 private slots:
+    void onPluginTimer();
+    void onNetworkReplyFinished();
     void coapReplyFinished(CoapReply *reply);
     void onNotificationReceived(const CoapObserveResource &resource, const int ¬ificationNumber, const QByteArray &payload);
 };
diff --git a/osdomotics/devicepluginosdomotics.cpp b/osdomotics/devicepluginosdomotics.cpp
index 72ce79cd..e361b139 100644
--- a/osdomotics/devicepluginosdomotics.cpp
+++ b/osdomotics/devicepluginosdomotics.cpp
@@ -46,21 +46,29 @@
 #include "devicepluginosdomotics.h"
 #include "plugin/device.h"
 #include "plugininfo.h"
+#include "network/networkaccessmanager.h"
 
 DevicePluginOsdomotics::DevicePluginOsdomotics()
 {
-    m_coap = new Coap(this);
-    connect(m_coap, SIGNAL(replyFinished(CoapReply*)), this, SLOT(coapReplyFinished(CoapReply*)));
+
 }
 
-DeviceManager::HardwareResources DevicePluginOsdomotics::requiredHardware() const
+DevicePluginOsdomotics::~DevicePluginOsdomotics()
 {
-    return DeviceManager::HardwareResourceNetworkManager | DeviceManager::HardwareResourceTimer;
+    hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
+}
+
+void DevicePluginOsdomotics::init()
+{
+    m_coap = new Coap(this);
+    connect(m_coap, &Coap::replyFinished, this, &DevicePluginOsdomotics::coapReplyFinished);
+
+    m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(10);
+    connect(m_pluginTimer, &PluginTimer::timeout, this, &DevicePluginOsdomotics::onPluginTimer);
 }
 
 DeviceManager::DeviceSetupStatus DevicePluginOsdomotics::setupDevice(Device *device)
 {
-
     if (device->deviceClassId() == rplRouterDeviceClassId) {
         qCDebug(dcOsdomotics) << "Setup RPL router" << device->paramValue(hostParamTypeId).toString();
         QHostAddress address(device->paramValue(hostParamTypeId).toString());
@@ -74,7 +82,8 @@ DeviceManager::DeviceSetupStatus DevicePluginOsdomotics::setupDevice(Device *dev
         url.setScheme("http");
         url.setHost(address.toString());
 
-        QNetworkReply *reply = networkManagerGet(QNetworkRequest(url));
+        QNetworkReply *reply = hardwareManager()->networkManager()->get(QNetworkRequest(url));
+        connect(reply, &QNetworkReply::finished, this, &DevicePluginOsdomotics::onNetworkReplyFinished);
         m_asyncSetup.insert(reply, device);
 
         return DeviceManager::DeviceSetupStatusAsync;
@@ -91,59 +100,11 @@ void DevicePluginOsdomotics::deviceRemoved(Device *device)
     Q_UNUSED(device)
 }
 
-void DevicePluginOsdomotics::networkManagerReplyReady(QNetworkReply *reply)
-{
-    int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
-
-    // create user finished
-    if (m_asyncSetup.contains(reply)) {
-        Device *device = m_asyncSetup.take(reply);
-
-        // check HTTP status code
-        if (status != 200) {
-            qCWarning(dcOsdomotics) << "Setup reply HTTP error:" << reply->errorString();
-            emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusFailure);
-            reply->deleteLater();
-            return;
-        }
-
-        QByteArray data = reply->readAll();
-        parseNodes(device, data);
-
-        emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusSuccess);
-    } else if (m_asyncNodeRescans.contains(reply)) {
-        Device *device = m_asyncSetup.take(reply);
-
-        // check HTTP status code
-        if (status != 200) {
-            qCWarning(dcOsdomotics) << "Setup reply HTTP error:" << reply->errorString();
-            emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusFailure);
-            reply->deleteLater();
-            return;
-        }
-
-        QByteArray data = reply->readAll();
-        parseNodes(device, data);
-    }
-    reply->deleteLater();
-}
-
 void DevicePluginOsdomotics::postSetupDevice(Device *device)
 {
     updateNode(device);
 }
 
-void DevicePluginOsdomotics::guhTimer()
-{
-    foreach (Device *device, myDevices()) {
-        if (device->deviceClassId() == merkurNodeDeviceClassId) {
-            updateNode(device);
-        } else if(device->deviceClassId() == rplRouterDeviceClassId) {
-            scanNodes(device);
-        }
-    }
-}
-
 DeviceManager::DeviceError DevicePluginOsdomotics::executeAction(Device *device, const Action &action)
 {
     if (device->deviceClassId() == merkurNodeDeviceClassId) {
@@ -183,7 +144,8 @@ void DevicePluginOsdomotics::scanNodes(Device *device)
     url.setScheme("http");
     url.setHost(address.toString());
 
-    QNetworkReply *reply = networkManagerGet(QNetworkRequest(url));
+    QNetworkReply *reply = hardwareManager()->networkManager()->get(QNetworkRequest(url));
+    connect(reply, &QNetworkReply::finished, this, &DevicePluginOsdomotics::onNetworkReplyFinished);
     m_asyncNodeRescans.insert(reply, device);
 }
 
@@ -258,6 +220,55 @@ Device *DevicePluginOsdomotics::findDevice(const QHostAddress &address)
     return 0;
 }
 
+void DevicePluginOsdomotics::onPluginTimer()
+{
+    foreach (Device *device, myDevices()) {
+        if (device->deviceClassId() == merkurNodeDeviceClassId) {
+            updateNode(device);
+        } else if(device->deviceClassId() == rplRouterDeviceClassId) {
+            scanNodes(device);
+        }
+    }
+}
+
+void DevicePluginOsdomotics::onNetworkReplyFinished()
+{
+    QNetworkReply *reply = static_cast(sender());
+    int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+
+    // create user finished
+    if (m_asyncSetup.contains(reply)) {
+        Device *device = m_asyncSetup.take(reply);
+
+        // check HTTP status code
+        if (status != 200) {
+            qCWarning(dcOsdomotics) << "Setup reply HTTP error:" << reply->errorString();
+            emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusFailure);
+            reply->deleteLater();
+            return;
+        }
+
+        QByteArray data = reply->readAll();
+        parseNodes(device, data);
+
+        emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusSuccess);
+    } else if (m_asyncNodeRescans.contains(reply)) {
+        Device *device = m_asyncSetup.take(reply);
+
+        // check HTTP status code
+        if (status != 200) {
+            qCWarning(dcOsdomotics) << "Setup reply HTTP error:" << reply->errorString();
+            emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusFailure);
+            reply->deleteLater();
+            return;
+        }
+
+        QByteArray data = reply->readAll();
+        parseNodes(device, data);
+    }
+    reply->deleteLater();
+}
+
 void DevicePluginOsdomotics::coapReplyFinished(CoapReply *reply)
 {
     qCDebug(dcOsdomotics) << "coap reply finished" << reply;
diff --git a/osdomotics/devicepluginosdomotics.h b/osdomotics/devicepluginosdomotics.h
index 44e9061f..b0854d29 100644
--- a/osdomotics/devicepluginosdomotics.h
+++ b/osdomotics/devicepluginosdomotics.h
@@ -29,6 +29,7 @@
 #include 
 
 #include "coap/coap.h"
+#include "plugintimer.h"
 
 class DevicePluginOsdomotics : public DevicePlugin
 {
@@ -38,19 +39,17 @@ class DevicePluginOsdomotics : public DevicePlugin
 
 public:
     explicit DevicePluginOsdomotics();
+    ~DevicePluginOsdomotics();
 
-    DeviceManager::HardwareResources requiredHardware() const override;
+    void init() override;
     DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
     void deviceRemoved(Device *device) override;
-    void networkManagerReplyReady(QNetworkReply *reply) override;
-
     void postSetupDevice(Device *device) override;
-
-    void guhTimer() override;
     DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
 
 private:
-    Coap *m_coap;
+    PluginTimer *m_pluginTimer = nullptr;
+    Coap *m_coap = nullptr;
     QHash m_asyncSetup;
     QHash m_asyncNodeRescans;
 
@@ -65,6 +64,8 @@ private:
     Device *findDevice(const QHostAddress &address);
 
 private slots:
+    void onPluginTimer();
+    void onNetworkReplyFinished();
     void coapReplyFinished(CoapReply *reply);
 
 };
diff --git a/philipshue/devicepluginphilipshue.cpp b/philipshue/devicepluginphilipshue.cpp
index ed0367a9..49449619 100644
--- a/philipshue/devicepluginphilipshue.cpp
+++ b/philipshue/devicepluginphilipshue.cpp
@@ -47,6 +47,8 @@
 #include "plugin/device.h"
 #include "types/param.h"
 #include "plugininfo.h"
+#include "network/upnp/upnpdiscovery.h"
+#include "network/upnp/upnpdiscoveryreply.h"
 
 #include 
 #include 
@@ -55,24 +57,28 @@
 
 DevicePluginPhilipsHue::DevicePluginPhilipsHue()
 {
-    m_timer = new QTimer(this);
-    m_timer->setSingleShot(false);
-    m_timer->setInterval(5000);
 
-    connect(m_timer, SIGNAL(timeout()), this, SLOT(onTimeout()));
 }
 
-DeviceManager::HardwareResources DevicePluginPhilipsHue::requiredHardware() const
+DevicePluginPhilipsHue::~DevicePluginPhilipsHue()
 {
-    return DeviceManager::HardwareResourceUpnpDisovery | DeviceManager::HardwareResourceNetworkManager;
+    hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
+}
+
+void DevicePluginPhilipsHue::init()
+{
+    m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(5);
+    connect(m_pluginTimer, &PluginTimer::timeout, this, &DevicePluginPhilipsHue::onPluginTimer);
 }
 
 DeviceManager::DeviceError DevicePluginPhilipsHue::discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms)
 {
-    Q_UNUSED(deviceClassId)
     Q_UNUSED(params)
+    Q_UNUSED(deviceClassId)
+
+    UpnpDiscoveryReply *reply = hardwareManager()->upnpDiscovery()->discoverDevices("libhue:idl");
+    connect(reply, &UpnpDiscoveryReply::finished, this, &DevicePluginPhilipsHue::onUpnpDiscoveryFinished);
 
-    upnpDiscover("libhue:idl");
     return DeviceManager::DeviceErrorAsync;
 }
 
@@ -94,7 +100,6 @@ DeviceManager::DeviceSetupStatus DevicePluginPhilipsHue::setupDevice(Device *dev
                 m_bridges.insert(b, device);
                 device->setStateValue(bridgeReachableStateTypeId, true);
                 discoverBridgeDevices(b);
-                m_timer->start();
                 return DeviceManager::DeviceSetupStatusSuccess;
             }
         }
@@ -111,7 +116,6 @@ DeviceManager::DeviceSetupStatus DevicePluginPhilipsHue::setupDevice(Device *dev
         bridge->setZigbeeChannel(device->paramValue(bridgeZigbeeChannelParamTypeId).toInt());
 
         m_bridges.insert(bridge, device);
-        m_timer->start();
         return DeviceManager::DeviceSetupStatusSuccess;
     }
 
@@ -213,38 +217,6 @@ void DevicePluginPhilipsHue::deviceRemoved(Device *device)
         m_remotes.remove(remote);
         remote->deleteLater();
     }
-
-    if (myDevices().isEmpty())
-        m_timer->stop();
-}
-
-void DevicePluginPhilipsHue::upnpDiscoveryFinished(const QList &upnpDeviceDescriptorList)
-{
-    if (upnpDeviceDescriptorList.isEmpty()) {
-        qCDebug(dcPhilipsHue) << "No UPnP device found. Try N-UPNP discovery.";
-        QNetworkRequest request(QUrl("https://www.meethue.com/api/nupnp"));
-        QNetworkReply *reply = networkManagerGet(request);
-        m_discoveryRequests.append(reply);
-        return;
-    }
-
-    QList deviceDescriptors;
-    foreach (const UpnpDeviceDescriptor &upnpDevice, upnpDeviceDescriptorList) {
-        if (upnpDevice.modelDescription().contains("Philips")) {
-            DeviceDescriptor descriptor(hueBridgeDeviceClassId, "Philips Hue Bridge", upnpDevice.hostAddress().toString());
-            ParamList params;
-            params.append(Param(bridgeNameParamTypeId, upnpDevice.friendlyName()));
-            params.append(Param(bridgeHostParamTypeId, upnpDevice.hostAddress().toString()));
-            params.append(Param(bridgeApiParamTypeId, QString()));
-            params.append(Param(bridgeMacParamTypeId, QString()));
-            params.append(Param(bridgeIdParamTypeId, upnpDevice.serialNumber().toLower()));
-            params.append(Param(bridgeZigbeeChannelParamTypeId, -1));
-            descriptor.setParams(params);
-            deviceDescriptors.append(descriptor);
-        }
-    }
-
-    emit devicesDiscovered(hueBridgeDeviceClassId, deviceDescriptors);
 }
 
 DeviceManager::DeviceSetupStatus DevicePluginPhilipsHue::confirmPairing(const PairingTransactionId &pairingTransactionId, const DeviceClassId &deviceClassId, const ParamList ¶ms, const QString &secret)
@@ -265,15 +237,17 @@ DeviceManager::DeviceSetupStatus DevicePluginPhilipsHue::confirmPairing(const Pa
 
     QNetworkRequest request(QUrl("http://" + pairingInfo->host().toString() + "/api"));
     request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
-    QNetworkReply *reply = networkManagerPost(request, jsonDoc.toJson());
+    QNetworkReply *reply = hardwareManager()->networkManager()->post(request, jsonDoc.toJson());
+    connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
 
     m_pairingRequests.insert(reply, pairingInfo);
 
     return DeviceManager::DeviceSetupStatusAsync;
 }
 
-void DevicePluginPhilipsHue::networkManagerReplyReady(QNetworkReply *reply)
+void DevicePluginPhilipsHue::networkManagerReplyReady()
 {
+    QNetworkReply *reply = static_cast(sender());
     int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
 
     // create user finished
@@ -444,27 +418,39 @@ DeviceManager::DeviceError DevicePluginPhilipsHue::executeAction(Device *device,
 
         if (action.actionTypeId() == huePowerActionTypeId) {
             QPair request = light->createSetPowerRequest(action.param(huePowerStateParamTypeId).value().toBool());
-            m_asyncActions.insert(networkManagerPut(request.first, request.second),QPair(device, action.id()));
+            QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second);
+            connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
+            m_asyncActions.insert(reply, QPair(device, action.id()));
             return DeviceManager::DeviceErrorAsync;
         } else if (action.actionTypeId() == hueColorActionTypeId) {
             QPair request = light->createSetColorRequest(action.param(hueColorStateParamTypeId).value().value());
-            m_asyncActions.insert(networkManagerPut(request.first, request.second),QPair(device, action.id()));
+            QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second);
+            connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
+            m_asyncActions.insert(reply,QPair(device, action.id()));
             return DeviceManager::DeviceErrorAsync;
         } else if (action.actionTypeId() == hueBrightnessActionTypeId) {
             QPair request = light->createSetBrightnessRequest(percentageToBrightness(action.param(hueBrightnessStateParamTypeId).value().toInt()));
-            m_asyncActions.insert(networkManagerPut(request.first, request.second),QPair(device, action.id()));
+            QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second);
+            connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
+            m_asyncActions.insert(reply, QPair(device, action.id()));
             return DeviceManager::DeviceErrorAsync;
         } else if (action.actionTypeId() == hueEffectActionTypeId) {
             QPair request = light->createSetEffectRequest(action.param(hueEffectStateParamTypeId).value().toString());
-            m_asyncActions.insert(networkManagerPut(request.first, request.second),QPair(device, action.id()));
+            QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second);
+            connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
+            m_asyncActions.insert(reply, QPair(device, action.id()));
             return DeviceManager::DeviceErrorAsync;
         } else if (action.actionTypeId() == hueAlertActionTypeId) {
             QPair request = light->createFlashRequest(action.param(alertParamTypeId).value().toString());
-            m_asyncActions.insert(networkManagerPut(request.first, request.second),QPair(device, action.id()));
+            QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second);
+            connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
+            m_asyncActions.insert(reply, QPair(device, action.id()));
             return DeviceManager::DeviceErrorAsync;
         } else if (action.actionTypeId() == hueTemperatureActionTypeId) {
             QPair request = light->createSetTemperatureRequest(action.param(hueTemperatureStateParamTypeId).value().toInt());
-            m_asyncActions.insert(networkManagerPut(request.first, request.second),QPair(device, action.id()));
+            QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second);
+            connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
+            m_asyncActions.insert(reply, QPair(device, action.id()));
             return DeviceManager::DeviceErrorAsync;
         }
         return DeviceManager::DeviceErrorActionTypeNotFound;
@@ -481,15 +467,21 @@ DeviceManager::DeviceError DevicePluginPhilipsHue::executeAction(Device *device,
 
         if (action.actionTypeId() == huePowerActionTypeId) {
             QPair request = light->createSetPowerRequest(action.param(huePowerStateParamTypeId).value().toBool());
-            m_asyncActions.insert(networkManagerPut(request.first, request.second),QPair(device, action.id()));
+            QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second);
+            connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
+            m_asyncActions.insert(reply, QPair(device, action.id()));
             return DeviceManager::DeviceErrorAsync;
         } else if (action.actionTypeId() == hueBrightnessActionTypeId) {
             QPair request = light->createSetBrightnessRequest(percentageToBrightness(action.param(hueBrightnessStateParamTypeId).value().toInt()));
-            m_asyncActions.insert(networkManagerPut(request.first, request.second),QPair(device, action.id()));
+            QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second);
+            connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
+            m_asyncActions.insert(reply, QPair(device, action.id()));
             return DeviceManager::DeviceErrorAsync;
         } else if (action.actionTypeId() == hueAlertActionTypeId) {
             QPair request = light->createFlashRequest(action.param(alertParamTypeId).value().toString());
-            m_asyncActions.insert(networkManagerPut(request.first, request.second),QPair(device, action.id()));
+            QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second);
+            connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
+            m_asyncActions.insert(reply, QPair(device, action.id()));
             return DeviceManager::DeviceErrorAsync;
         }
         return DeviceManager::DeviceErrorActionTypeNotFound;
@@ -507,11 +499,15 @@ DeviceManager::DeviceError DevicePluginPhilipsHue::executeAction(Device *device,
             return DeviceManager::DeviceErrorNoError;
         } else if (action.actionTypeId() == checkForUpdatesActionTypeId) {
             QPair request = bridge->createCheckUpdatesRequest();
-            m_asyncActions.insert(networkManagerPut(request.first, request.second),QPair(device, action.id()));
+            QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second);
+            connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
+            m_asyncActions.insert(reply, QPair(device, action.id()));
             return DeviceManager::DeviceErrorAsync;
         } else if (action.actionTypeId() == upgradeActionTypeId) {
             QPair request = bridge->createUpgradeRequest();
-            m_asyncActions.insert(networkManagerPut(request.first, request.second),QPair(device, action.id()));
+            QNetworkReply *reply = hardwareManager()->networkManager()->put(request.first, request.second);
+            connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
+            m_asyncActions.insert(reply, QPair(device, action.id()));
             return DeviceManager::DeviceErrorAsync;
         }
         return DeviceManager::DeviceErrorActionTypeNotFound;
@@ -591,21 +587,58 @@ void DevicePluginPhilipsHue::onRemoteButtonEvent(const int &buttonCode)
     }
 }
 
-void DevicePluginPhilipsHue::onTimeout()
+void DevicePluginPhilipsHue::onPluginTimer()
 {
     foreach (Device *device, m_bridges.values()) {
         refreshBridge(device);
     }
 }
 
+void DevicePluginPhilipsHue::onUpnpDiscoveryFinished()
+{
+    qCDebug(dcPhilipsHue()) << "Upnp discovery finished";
+    UpnpDiscoveryReply *reply = static_cast(sender());
+    if (reply->error() != UpnpDiscoveryReply::UpnpDiscoveryReplyErrorNoError) {
+        qCWarning(dcPhilipsHue()) << "Upnp discovery error" << reply->error();
+    }
+    reply->deleteLater();
+
+    if (reply->deviceDescriptors().isEmpty()) {
+        qCDebug(dcPhilipsHue) << "No UPnP device found. Try N-UPNP discovery.";
+        QNetworkRequest request(QUrl("https://www.meethue.com/api/nupnp"));
+        QNetworkReply *reply = hardwareManager()->networkManager()->get(request);
+        connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
+        m_discoveryRequests.append(reply);
+        return;
+    }
+
+    QList deviceDescriptors;
+    foreach (const UpnpDeviceDescriptor &upnpDevice, reply->deviceDescriptors()) {
+        if (upnpDevice.modelDescription().contains("Philips")) {
+            DeviceDescriptor descriptor(hueBridgeDeviceClassId, "Philips Hue Bridge", upnpDevice.hostAddress().toString());
+            ParamList params;
+            params.append(Param(bridgeNameParamTypeId, upnpDevice.friendlyName()));
+            params.append(Param(bridgeHostParamTypeId, upnpDevice.hostAddress().toString()));
+            params.append(Param(bridgeApiParamTypeId, QString()));
+            params.append(Param(bridgeMacParamTypeId, QString()));
+            params.append(Param(bridgeIdParamTypeId, upnpDevice.serialNumber().toLower()));
+            params.append(Param(bridgeZigbeeChannelParamTypeId, -1));
+            descriptor.setParams(params);
+            deviceDescriptors.append(descriptor);
+        }
+    }
+
+    emit devicesDiscovered(hueBridgeDeviceClassId, deviceDescriptors);
+}
+
 void DevicePluginPhilipsHue::refreshLight(Device *device)
 {
     HueLight *light = m_lights.key(device);
 
     QNetworkRequest request(QUrl("http://" + light->hostAddress().toString() + "/api/" + light->apiKey() + "/lights/" + QString::number(light->id())));
     request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
-    QNetworkReply *reply = networkManagerGet(request);
-
+    QNetworkReply *reply = hardwareManager()->networkManager()->get(request);
+    connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
     m_lightRefreshRequests.insert(reply, device);
 }
 
@@ -624,8 +657,8 @@ void DevicePluginPhilipsHue::refreshBridge(Device *device)
 
     QNetworkRequest request(QUrl("http://" + bridge->hostAddress().toString() + "/api/" + bridge->apiKey() + "/config"));
     request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
-    QNetworkReply *reply = networkManagerGet(request);
-
+    QNetworkReply *reply = hardwareManager()->networkManager()->get(request);
+    connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
     m_bridgeRefreshRequests.insert(reply, device);
 }
 
@@ -635,8 +668,8 @@ void DevicePluginPhilipsHue::refreshLights(HueBridge *bridge)
 
     QNetworkRequest request(QUrl("http://" + bridge->hostAddress().toString() + "/api/" + bridge->apiKey() + "/lights"));
     request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
-    QNetworkReply *reply = networkManagerGet(request);
-
+    QNetworkReply *reply = hardwareManager()->networkManager()->get(request);
+    connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
     m_lightsRefreshRequests.insert(reply, device);
 }
 
@@ -646,8 +679,8 @@ void DevicePluginPhilipsHue::refreshSensors(HueBridge *bridge)
 
     QNetworkRequest request(QUrl("http://" + bridge->hostAddress().toString() + "/api/" + bridge->apiKey() + "/sensors"));
     request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
-    QNetworkReply *reply = networkManagerGet(request);
-
+    QNetworkReply *reply = hardwareManager()->networkManager()->get(request);
+    connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
     m_sensorsRefreshRequests.insert(reply, device);
 }
 
@@ -657,10 +690,14 @@ void DevicePluginPhilipsHue::discoverBridgeDevices(HueBridge *bridge)
     qCDebug(dcPhilipsHue) << "Discover bridge devices" << bridge->hostAddress();
 
     QPair lightsRequest = bridge->createDiscoverLightsRequest();
-    m_bridgeLightsDiscoveryRequests.insert(networkManagerGet(lightsRequest.first), device);
+    QNetworkReply *lightsReply = hardwareManager()->networkManager()->get(lightsRequest.first);
+    connect(lightsReply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
+    m_bridgeLightsDiscoveryRequests.insert(lightsReply, device);
 
     QPair sensorsRequest = bridge->createSearchSensorsRequest();
-    m_bridgeSensorsDiscoveryRequests.insert(networkManagerGet(sensorsRequest.first), device);
+    QNetworkReply *reply = hardwareManager()->networkManager()->get(sensorsRequest.first);
+    connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
+    m_bridgeSensorsDiscoveryRequests.insert(reply, device);
 }
 
 void DevicePluginPhilipsHue::searchNewDevices(HueBridge *bridge)
@@ -669,7 +706,9 @@ void DevicePluginPhilipsHue::searchNewDevices(HueBridge *bridge)
     qCDebug(dcPhilipsHue) << "Discover bridge devices" << bridge->hostAddress();
 
     QPair request = bridge->createSearchLightsRequest();
-    m_bridgeSearchDevicesRequests.insert(networkManagerPost(request.first, request.second), device);
+    QNetworkReply *reply = hardwareManager()->networkManager()->post(request.first, request.second);
+    connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
+    m_bridgeSearchDevicesRequests.insert(reply, device);
 }
 
 void DevicePluginPhilipsHue::setLightName(Device *device, const QString &name)
@@ -684,7 +723,8 @@ void DevicePluginPhilipsHue::setLightName(Device *device, const QString &name)
                                  "/lights/" + QString::number(light->id())));
     request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
 
-    QNetworkReply *reply = networkManagerPut(request,jsonDoc.toJson());
+    QNetworkReply *reply = hardwareManager()->networkManager()->put(request,jsonDoc.toJson());
+    connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
     m_lightSetNameRequests.insert(reply, device);
 }
 
@@ -1059,8 +1099,8 @@ void DevicePluginPhilipsHue::processPairingResponse(PairingInfo *pairingInfo, co
     // Paired successfully, check bridge information
     QNetworkRequest request(QUrl("http://" + pairingInfo->host().toString() + "/api/" + pairingInfo->apiKey() + "/config"));
     request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
-    QNetworkReply *reply = networkManagerGet(request);
-
+    QNetworkReply *reply = hardwareManager()->networkManager()->get(request);
+    connect(reply, &QNetworkReply::finished, this, &DevicePluginPhilipsHue::networkManagerReplyReady);
     m_informationRequests.insert(reply, pairingInfo);
 }
 
diff --git a/philipshue/devicepluginphilipshue.h b/philipshue/devicepluginphilipshue.h
index 60b78dc1..f754141f 100644
--- a/philipshue/devicepluginphilipshue.h
+++ b/philipshue/devicepluginphilipshue.h
@@ -29,6 +29,9 @@
 #include "huelight.h"
 #include "hueremote.h"
 #include "pairinginfo.h"
+#include "plugintimer.h"
+#include "network/networkaccessmanager.h"
+#include "network/upnp/upnpdiscovery.h"
 
 class QNetworkReply;
 
@@ -41,19 +44,14 @@ class DevicePluginPhilipsHue: public DevicePlugin
 
 public:
     explicit DevicePluginPhilipsHue();
+    ~DevicePluginPhilipsHue();
 
-    DeviceManager::HardwareResources requiredHardware() const override;
-
+    void init() override;
     DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
-
     DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override;
     void deviceRemoved(Device *device) override;
-
-    void upnpDiscoveryFinished(const QList &upnpDeviceDescriptorList) override;
     DeviceManager::DeviceSetupStatus confirmPairing(const PairingTransactionId &pairingTransactionId, const DeviceClassId &deviceClassId, const ParamList ¶ms, const QString &secret) override;
 
-    void networkManagerReplyReady(QNetworkReply *reply) override;
-
 public slots:
     DeviceManager::DeviceError executeAction(Device *device, const Action &action);
 
@@ -61,10 +59,14 @@ private slots:
     void lightStateChanged();
     void remoteStateChanged();
     void onRemoteButtonEvent(const int &buttonCode);
-    void onTimeout();
+    void onPluginTimer();
+
+private slots:
+    void onUpnpDiscoveryFinished();
+    void networkManagerReplyReady();
 
 private:
-    QTimer *m_timer;
+    PluginTimer *m_pluginTimer = nullptr;
 
     QHash m_pairingRequests;
     QHash m_informationRequests;
@@ -119,6 +121,7 @@ private:
 
     int brightnessToPercentage(int brightness);
     int percentageToBrightness(int percentage);
+
 };
 
 #endif // DEVICEPLUGINBOBLIGHT_H
diff --git a/plantcare/devicepluginplantcare.cpp b/plantcare/devicepluginplantcare.cpp
index 3380bc4b..f37f75f8 100644
--- a/plantcare/devicepluginplantcare.cpp
+++ b/plantcare/devicepluginplantcare.cpp
@@ -43,16 +43,22 @@
 #include "devicepluginplantcare.h"
 #include "plugin/device.h"
 #include "plugininfo.h"
+#include "network/networkaccessmanager.h"
 
 DevicePluginPlantCare::DevicePluginPlantCare()
 {
 
 }
 
-DeviceManager::HardwareResources DevicePluginPlantCare::requiredHardware() const
+DevicePluginPlantCare::~DevicePluginPlantCare()
 {
-    // We need the NetworkAccessManager for node discovery and the timer for ping requests
-    return DeviceManager::HardwareResourceNetworkManager | DeviceManager::HardwareResourceTimer;
+    hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
+}
+
+void DevicePluginPlantCare::init()
+{
+    m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(10);
+    connect(m_pluginTimer, &PluginTimer::timeout, this, &DevicePluginPlantCare::onPluginTimer);
 }
 
 DeviceManager::DeviceSetupStatus DevicePluginPlantCare::setupDevice(Device *device)
@@ -85,6 +91,12 @@ void DevicePluginPlantCare::deviceRemoved(Device *device)
     }
 }
 
+void DevicePluginPlantCare::postSetupDevice(Device *device)
+{
+    // Try to ping the device after a successful setup
+    pingDevice(device);
+}
+
 DeviceManager::DeviceError DevicePluginPlantCare::discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms)
 {
     Q_UNUSED(params)
@@ -97,68 +109,12 @@ DeviceManager::DeviceError DevicePluginPlantCare::discoverDevices(const DeviceCl
     url.setScheme("http");
     url.setHost(address.toString());
 
-    m_asyncNodeScans.insert(networkManagerGet(QNetworkRequest(url)), deviceClassId);
+    QNetworkReply *reply = hardwareManager()->networkManager()->get(QNetworkRequest(url));
+    connect(reply, &QNetworkReply::finished, this, &DevicePluginPlantCare::onNetworkReplyFinished);
+    m_asyncNodeScans.insert(reply, deviceClassId);
     return DeviceManager::DeviceErrorAsync;
 }
 
-void DevicePluginPlantCare::networkManagerReplyReady(QNetworkReply *reply)
-{
-    if (m_asyncNodeScans.keys().contains(reply)) {
-        DeviceClassId deviceClassId = m_asyncNodeScans.take(reply);
-        // Check HTTP status code
-        if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) {
-            qCWarning(dcPlantCare) << "Node scan reply HTTP error:" << reply->errorString();
-            emit devicesDiscovered(deviceClassId, QList());
-            reply->deleteLater();
-            return;
-        }
-
-        QByteArray data = reply->readAll();
-        qCDebug(dcPlantCare) << "Node discovery finished:" << endl << data;
-
-        QList deviceDescriptors;
-        QList lines = data.split('\n');
-        qCDebug(dcPlantCare) << lines;
-        foreach (const QByteArray &line, lines) {
-            if (line.isEmpty())
-                continue;
-
-            QHostAddress address(QString(line.left(line.length() - 4)));
-            if (address.isNull())
-                continue;
-
-            qCDebug(dcPlantCare) << "Found node" << address.toString();
-            // Create a deviceDescriptor for each found address
-            DeviceDescriptor descriptor(deviceClassId, "Plant Care", address.toString());
-            ParamList params;
-            params.append(Param(hostParamTypeId, address.toString()));
-            descriptor.setParams(params);
-            deviceDescriptors.append(descriptor);
-        }
-        // Inform the user which devices were found
-        emit devicesDiscovered(deviceClassId, deviceDescriptors);
-    }
-
-    // Delete the HTTP reply
-    reply->deleteLater();
-}
-
-void DevicePluginPlantCare::postSetupDevice(Device *device)
-{
-    // Try to ping the device after a successful setup
-    pingDevice(device);
-}
-
-void DevicePluginPlantCare::guhTimer()
-{
-    // Try to ping each device every 10 seconds to make sure it is still reachable
-    foreach (Device *device, myDevices()) {
-        if (device->deviceClassId() == plantCareDeviceClassId) {
-            pingDevice(device);
-        }
-    }
-}
-
 DeviceManager::DeviceError DevicePluginPlantCare::executeAction(Device *device, const Action &action)
 {
     if (device->deviceClassId() != plantCareDeviceClassId)
@@ -404,6 +360,60 @@ Device *DevicePluginPlantCare::findDevice(const QHostAddress &address)
     return NULL;
 }
 
+void DevicePluginPlantCare::onPluginTimer()
+{
+    // Try to ping each device every 10 seconds to make sure it is still reachable
+    foreach (Device *device, myDevices()) {
+        if (device->deviceClassId() == plantCareDeviceClassId) {
+            pingDevice(device);
+        }
+    }
+}
+
+void DevicePluginPlantCare::onNetworkReplyFinished()
+{
+    QNetworkReply *reply = static_cast(sender());
+
+    if (m_asyncNodeScans.keys().contains(reply)) {
+        DeviceClassId deviceClassId = m_asyncNodeScans.take(reply);
+        // Check HTTP status code
+        if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) {
+            qCWarning(dcPlantCare) << "Node scan reply HTTP error:" << reply->errorString();
+            emit devicesDiscovered(deviceClassId, QList());
+            reply->deleteLater();
+            return;
+        }
+
+        QByteArray data = reply->readAll();
+        qCDebug(dcPlantCare) << "Node discovery finished:" << endl << data;
+
+        QList deviceDescriptors;
+        QList lines = data.split('\n');
+        qCDebug(dcPlantCare) << lines;
+        foreach (const QByteArray &line, lines) {
+            if (line.isEmpty())
+                continue;
+
+            QHostAddress address(QString(line.left(line.length() - 4)));
+            if (address.isNull())
+                continue;
+
+            qCDebug(dcPlantCare) << "Found node" << address.toString();
+            // Create a deviceDescriptor for each found address
+            DeviceDescriptor descriptor(deviceClassId, "Plant Care", address.toString());
+            ParamList params;
+            params.append(Param(hostParamTypeId, address.toString()));
+            descriptor.setParams(params);
+            deviceDescriptors.append(descriptor);
+        }
+        // Inform the user which devices were found
+        emit devicesDiscovered(deviceClassId, deviceDescriptors);
+    }
+
+    // Delete the HTTP reply
+    reply->deleteLater();
+}
+
 void DevicePluginPlantCare::coapReplyFinished(CoapReply *reply)
 {
     if (m_pingReplies.contains(reply)) {
diff --git a/plantcare/devicepluginplantcare.h b/plantcare/devicepluginplantcare.h
index e115762e..48aeb887 100644
--- a/plantcare/devicepluginplantcare.h
+++ b/plantcare/devicepluginplantcare.h
@@ -27,6 +27,7 @@
 #include "plugin/deviceplugin.h"
 #include "types/action.h"
 #include "coap/coap.h"
+#include "plugintimer.h"
 
 #include 
 
@@ -38,19 +39,17 @@ class DevicePluginPlantCare : public DevicePlugin
 
 public:
     explicit DevicePluginPlantCare();
+    ~DevicePluginPlantCare();
 
-    DeviceManager::HardwareResources requiredHardware() const override;
+    void init() override;
     DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override;
     DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
     void deviceRemoved(Device *device) override;
-    void networkManagerReplyReady(QNetworkReply *reply) override;
-
     void postSetupDevice(Device *device) override;
-
-    void guhTimer() override;
     DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
 
 private:
+    PluginTimer *m_pluginTimer = nullptr;
     QPointer m_coap;
     QHash m_asyncNodeScans;
     QHash m_enableNotification;
@@ -82,6 +81,8 @@ private:
     Device *findDevice(const QHostAddress &address);
 
 private slots:
+    void onPluginTimer();
+    void onNetworkReplyFinished();
     void coapReplyFinished(CoapReply *reply);
     void onNotificationReceived(const CoapObserveResource &resource, const int ¬ificationNumber, const QByteArray &payload);
 };
diff --git a/tcpcommander/deviceplugintcpcommander.cpp b/tcpcommander/deviceplugintcpcommander.cpp
index 36491c6f..c22f54e9 100644
--- a/tcpcommander/deviceplugintcpcommander.cpp
+++ b/tcpcommander/deviceplugintcpcommander.cpp
@@ -26,12 +26,6 @@ DevicePluginTcpCommander::DevicePluginTcpCommander()
 {
 }
 
-DeviceManager::HardwareResources DevicePluginTcpCommander::requiredHardware() const
-{
-    return DeviceManager::HardwareResourceNone;
-}
-
-
 DeviceManager::DeviceSetupStatus DevicePluginTcpCommander::setupDevice(Device *device)
 {
     if (device->deviceClassId() == tcpOutputDeviceClassId) {
diff --git a/tcpcommander/deviceplugintcpcommander.h b/tcpcommander/deviceplugintcpcommander.h
index d000f3a4..d2f00ab4 100644
--- a/tcpcommander/deviceplugintcpcommander.h
+++ b/tcpcommander/deviceplugintcpcommander.h
@@ -35,7 +35,6 @@ class DevicePluginTcpCommander : public DevicePlugin
 public:
     explicit DevicePluginTcpCommander();
 
-    DeviceManager::HardwareResources requiredHardware() const override;
     DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
 
     void deviceRemoved(Device *device) override;
diff --git a/udpcommander/devicepluginudpcommander.cpp b/udpcommander/devicepluginudpcommander.cpp
index ce076e1e..3f8f538d 100644
--- a/udpcommander/devicepluginudpcommander.cpp
+++ b/udpcommander/devicepluginudpcommander.cpp
@@ -67,11 +67,6 @@ DevicePluginUdpCommander::DevicePluginUdpCommander()
 
 }
 
-DeviceManager::HardwareResources DevicePluginUdpCommander::requiredHardware() const
-{
-    return DeviceManager::HardwareResourceNone;
-}
-
 DeviceManager::DeviceSetupStatus DevicePluginUdpCommander::setupDevice(Device *device)
 {
     // check port
diff --git a/udpcommander/devicepluginudpcommander.h b/udpcommander/devicepluginudpcommander.h
index 615213f7..86e6721b 100644
--- a/udpcommander/devicepluginudpcommander.h
+++ b/udpcommander/devicepluginudpcommander.h
@@ -38,7 +38,6 @@ class DevicePluginUdpCommander : public DevicePlugin
 public:
     explicit DevicePluginUdpCommander();
 
-    DeviceManager::HardwareResources requiredHardware() const override;
     DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
     void deviceRemoved(Device *device) override;
 
diff --git a/unitec/devicepluginunitec.cpp b/unitec/devicepluginunitec.cpp
index 30300f3d..9c6333b7 100644
--- a/unitec/devicepluginunitec.cpp
+++ b/unitec/devicepluginunitec.cpp
@@ -49,6 +49,8 @@
 #include "devicepluginunitec.h"
 #include "devicemanager.h"
 #include "plugininfo.h"
+#include "hardwaremanager.h"
+#include "hardware/radio433/radio433.h"
 
 #include 
 #include 
@@ -57,11 +59,6 @@ DevicePluginUnitec::DevicePluginUnitec()
 {
 }
 
-DeviceManager::HardwareResources DevicePluginUnitec::requiredHardware() const
-{
-    return DeviceManager::HardwareResourceRadio433;
-}
-
 DeviceManager::DeviceSetupStatus DevicePluginUnitec::setupDevice(Device *device)
 {
     if (device->deviceClassId() != switchDeviceClassId) {
@@ -80,6 +77,9 @@ DeviceManager::DeviceSetupStatus DevicePluginUnitec::setupDevice(Device *device)
 
 DeviceManager::DeviceError DevicePluginUnitec::executeAction(Device *device, const Action &action)
 {   
+    if (!hardwareManager()->radio433()->available())
+        return DeviceManager::DeviceErrorHardwareNotAvailable;
+
     QList rawData;
     QByteArray binCode;
 
@@ -127,11 +127,12 @@ DeviceManager::DeviceError DevicePluginUnitec::executeAction(Device *device, con
 
     // =======================================
     // send data to hardware resource
-    if(transmitData(delay, rawData)){
+    if(hardwareManager()->radio433()->sendData(delay, rawData, 10)){
         qCDebug(dcUnitec) << "transmitted" << pluginName() << device->name() << "power: " << action.param(powerParamTypeId).value().toBool();
-        return DeviceManager::DeviceErrorNoError;
     }else{
         qCWarning(dcUnitec) << "could not transmitt" << pluginName() << device->name() << "power: " << action.param(powerParamTypeId).value().toBool();
         return DeviceManager::DeviceErrorHardwareNotAvailable;
     }
+
+    return DeviceManager::DeviceErrorNoError;
 }
diff --git a/unitec/devicepluginunitec.h b/unitec/devicepluginunitec.h
index 3b85785d..aaa0b638 100644
--- a/unitec/devicepluginunitec.h
+++ b/unitec/devicepluginunitec.h
@@ -35,7 +35,6 @@ class DevicePluginUnitec : public DevicePlugin
 public:
     explicit DevicePluginUnitec();
 
-    DeviceManager::HardwareResources requiredHardware() const override;
     DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
 
 public slots:
diff --git a/wakeonlan/devicepluginwakeonlan.cpp b/wakeonlan/devicepluginwakeonlan.cpp
index 6fb4e3cc..3eaf9a99 100644
--- a/wakeonlan/devicepluginwakeonlan.cpp
+++ b/wakeonlan/devicepluginwakeonlan.cpp
@@ -58,11 +58,6 @@ DevicePluginWakeOnLan::DevicePluginWakeOnLan()
 {
 }
 
-DeviceManager::HardwareResources DevicePluginWakeOnLan::requiredHardware() const
-{
-    return DeviceManager::HardwareResourceNone;
-}
-
 DeviceManager::DeviceError DevicePluginWakeOnLan::executeAction(Device *device, const Action &action)
 {
     if(action.actionTypeId() == wolActionTypeId){
diff --git a/wakeonlan/devicepluginwakeonlan.h b/wakeonlan/devicepluginwakeonlan.h
index c86b02f2..9eb27ee6 100644
--- a/wakeonlan/devicepluginwakeonlan.h
+++ b/wakeonlan/devicepluginwakeonlan.h
@@ -37,11 +37,8 @@ class DevicePluginWakeOnLan : public DevicePlugin
 public:
     explicit DevicePluginWakeOnLan();
 
-    DeviceManager::HardwareResources requiredHardware() const override;
-
     DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
 
-
 private slots:
     void wakeup(QString mac);
 
diff --git a/wemo/devicepluginwemo.cpp b/wemo/devicepluginwemo.cpp
index 01a68d3d..762ab0a3 100644
--- a/wemo/devicepluginwemo.cpp
+++ b/wemo/devicepluginwemo.cpp
@@ -49,6 +49,8 @@
 #include "plugin/device.h"
 #include "devicemanager.h"
 #include "plugininfo.h"
+#include "network/networkaccessmanager.h"
+#include "network/upnp/upnpdiscovery.h"
 
 #include 
 #include 
@@ -61,13 +63,26 @@ DevicePluginWemo::DevicePluginWemo()
 {
 }
 
+DevicePluginWemo::~DevicePluginWemo()
+{
+    hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
+}
+
+void DevicePluginWemo::init()
+{
+    m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(10);
+    connect(m_pluginTimer, &PluginTimer::timeout, this, &DevicePluginWemo::onPluginTimer);
+
+    connect(hardwareManager()->upnpDiscovery(), &UpnpDiscovery::upnpNotify, this, &DevicePluginWemo::onUpnpNotifyReceived);
+}
+
 DeviceManager::DeviceError DevicePluginWemo::discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms)
 {
     Q_UNUSED(params);
-    if (deviceClassId != wemoSwitchDeviceClassId) {
-        return DeviceManager::DeviceErrorDeviceClassNotFound;
-    }
-    upnpDiscover("upnp:rootdevice");
+    Q_UNUSED(deviceClassId)
+
+    UpnpDiscoveryReply *reply = hardwareManager()->upnpDiscovery()->discoverDevices("upnp:rootdevice");
+    connect(reply, &UpnpDiscoveryReply::finished, this, &DevicePluginWemo::onUpnpDiscoveryFinished);
     return DeviceManager::DeviceErrorAsync;
 }
 
@@ -81,11 +96,6 @@ DeviceManager::DeviceSetupStatus DevicePluginWemo::setupDevice(Device *device)
     return DeviceManager::DeviceSetupStatusSuccess;
 }
 
-DeviceManager::HardwareResources DevicePluginWemo::requiredHardware() const
-{
-    return DeviceManager::HardwareResourceTimer | DeviceManager::HardwareResourceUpnpDisovery | DeviceManager::HardwareResourceNetworkManager;
-}
-
 DeviceManager::DeviceError DevicePluginWemo::executeAction(Device *device, const Action &action)
 {
     if (device->deviceClassId() != wemoSwitchDeviceClassId)
@@ -128,69 +138,6 @@ void DevicePluginWemo::deviceRemoved(Device *device)
     }
 }
 
-void DevicePluginWemo::networkManagerReplyReady(QNetworkReply *reply)
-{
-    if (m_refreshReplies.contains(reply)) {
-        QByteArray data = reply->readAll();
-        Device *device = m_refreshReplies.take(reply);
-        if (reply->error()) {
-            // give only error if we don't already know that is unreachable
-            if (device->stateValue(reachableStateTypeId).toBool()) {
-                qCWarning(dcWemo) << "WeMo reply error: " << reply->errorString();
-            }
-            device->setStateValue(reachableStateTypeId, false);
-        } else {
-            processRefreshData(data, device);
-        }
-    } else if (m_setPowerReplies.contains(reply)) {
-        QByteArray data = reply->readAll();
-        Device *device = m_setPowerReplies.take(reply);
-        ActionId actionId = m_runningActionExecutions.take(reply);
-        if (reply->error()) {
-            // give only error if we don't already know that is unreachable
-            if (device->stateValue(reachableStateTypeId).toBool()) {
-                qCWarning(dcWemo) << "WeMo reply error: " << reply->errorString();
-            }
-            device->setStateValue(reachableStateTypeId, false);
-        } else {
-            processSetPowerData(data, device, actionId);
-        }
-    }
-
-    reply->deleteLater();
-}
-
-void DevicePluginWemo::guhTimer()
-{
-    foreach (Device* device, myDevices()) {
-        refresh(device);
-    }
-}
-
-void DevicePluginWemo::upnpDiscoveryFinished(const QList &upnpDeviceDescriptorList)
-{
-    QList deviceDescriptors;
-    foreach (UpnpDeviceDescriptor upnpDeviceDescriptor, upnpDeviceDescriptorList) {
-        if (upnpDeviceDescriptor.friendlyName() == "WeMo Switch") {
-            DeviceDescriptor descriptor(wemoSwitchDeviceClassId, "WemoSwitch", upnpDeviceDescriptor.serialNumber());
-            ParamList params;
-            params.append(Param(nameParamTypeId, upnpDeviceDescriptor.friendlyName()));
-            params.append(Param(hostParamTypeId, upnpDeviceDescriptor.hostAddress().toString()));
-            params.append(Param(portParamTypeId, upnpDeviceDescriptor.port()));
-            params.append(Param(serialParamTypeId, upnpDeviceDescriptor.serialNumber()));
-            descriptor.setParams(params);
-            deviceDescriptors.append(descriptor);
-        }
-    }
-    emit devicesDiscovered(wemoSwitchDeviceClassId, deviceDescriptors);
-}
-
-void DevicePluginWemo::upnpNotifyReceived(const QByteArray ¬ifyData)
-{
-    Q_UNUSED(notifyData);
-}
-
-
 void DevicePluginWemo::refresh(Device *device)
 {
     QByteArray getBinarayStateMessage("1");
@@ -201,7 +148,8 @@ void DevicePluginWemo::refresh(Device *device)
     request.setHeader(QNetworkRequest::UserAgentHeader,QVariant("guh"));
     request.setRawHeader("SOAPACTION", "\"urn:Belkin:service:basicevent:1#GetBinaryState\"");
 
-    QNetworkReply *reply = networkManagerPost(request, getBinarayStateMessage);
+    QNetworkReply *reply = hardwareManager()->networkManager()->post(request, getBinarayStateMessage);
+    connect(reply, &QNetworkReply::finished, this, &DevicePluginWemo::onNetworkReplyFinished);
     m_refreshReplies.insert(reply, device);
 }
 
@@ -220,7 +168,8 @@ bool DevicePluginWemo::setPower(Device *device, const bool &power, const ActionI
     request.setHeader(QNetworkRequest::UserAgentHeader,QVariant("guh"));
     request.setRawHeader("SOAPACTION", "\"urn:Belkin:service:basicevent:1#SetBinaryState\"");
 
-    QNetworkReply *reply = networkManagerPost(request, setPowerMessage);
+    QNetworkReply *reply = hardwareManager()->networkManager()->post(request, setPowerMessage);
+    connect(reply, &QNetworkReply::finished, this, &DevicePluginWemo::onNetworkReplyFinished);
     m_setPowerReplies.insert(reply, device);
     m_runningActionExecutions.insert(reply, actionId);
     return true;
@@ -251,3 +200,67 @@ void DevicePluginWemo::processSetPowerData(const QByteArray &data, Device *devic
         emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorHardwareNotAvailable);
     }
 }
+
+void DevicePluginWemo::onNetworkReplyFinished()
+{
+    QNetworkReply *reply = static_cast(sender());
+    int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+
+    // check HTTP status code
+    if (status != 200 || reply->error() != QNetworkReply::NoError) {
+        qCWarning(dcWemo()) << "Request error:" << status << reply->errorString();
+        reply->deleteLater();
+        return;
+    }
+
+    if (m_refreshReplies.contains(reply)) {
+        QByteArray data = reply->readAll();
+        Device *device = m_refreshReplies.take(reply);
+        processRefreshData(data, device);
+    } else if (m_setPowerReplies.contains(reply)) {
+        QByteArray data = reply->readAll();
+        Device *device = m_setPowerReplies.take(reply);
+        ActionId actionId = m_runningActionExecutions.take(reply);
+        processSetPowerData(data, device, actionId);
+    }
+
+    reply->deleteLater();
+}
+
+void DevicePluginWemo::onPluginTimer()
+{
+    foreach (Device* device, myDevices()) {
+        refresh(device);
+    }
+}
+
+void DevicePluginWemo::onUpnpDiscoveryFinished()
+{
+    qCDebug(dcWemo()) << "Upnp discovery finished";
+
+    UpnpDiscoveryReply *reply = static_cast(sender());
+    if (reply->error() != UpnpDiscoveryReply::UpnpDiscoveryReplyErrorNoError) {
+        qCWarning(dcWemo()) << "Upnp discovery error" << reply->error();
+    }
+    reply->deleteLater();
+
+    QList deviceDescriptors;
+    foreach (const UpnpDeviceDescriptor &upnpDeviceDescriptor, reply->deviceDescriptors()) {
+        if (upnpDeviceDescriptor.friendlyName() == "WeMo Switch") {
+            DeviceDescriptor descriptor(wemoSwitchDeviceClassId, "WeMo Switch", upnpDeviceDescriptor.serialNumber());
+            ParamList params;
+            params.append(Param(nameParamTypeId, upnpDeviceDescriptor.friendlyName()));
+            params.append(Param(hostParamTypeId, upnpDeviceDescriptor.hostAddress().toString()));
+            params.append(Param(portParamTypeId, upnpDeviceDescriptor.port()));
+            params.append(Param(serialParamTypeId, upnpDeviceDescriptor.serialNumber()));
+            descriptor.setParams(params);
+            deviceDescriptors.append(descriptor);
+        }
+    }
+    emit devicesDiscovered(wemoSwitchDeviceClassId, deviceDescriptors);
+}
+
+void DevicePluginWemo::onUpnpNotifyReceived(const QByteArray ¬ification)
+{
+    Q_UNUSED(notification)
+}
diff --git a/wemo/devicepluginwemo.h b/wemo/devicepluginwemo.h
index df0971b3..0bc8b751 100644
--- a/wemo/devicepluginwemo.h
+++ b/wemo/devicepluginwemo.h
@@ -23,6 +23,7 @@
 #ifndef DEVICEPLUGINWEMO_H
 #define DEVICEPLUGINWEMO_H
 
+#include "plugintimer.h"
 #include "plugin/deviceplugin.h"
 
 class DevicePluginWemo : public DevicePlugin
@@ -34,20 +35,16 @@ class DevicePluginWemo : public DevicePlugin
 
 public:
     explicit DevicePluginWemo();
+    ~DevicePluginWemo();
 
+    void init() override;
     DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override;
     DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
-    DeviceManager::HardwareResources requiredHardware() const override;
     DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
-
     void deviceRemoved(Device *device) override;
-    void networkManagerReplyReady(QNetworkReply *reply) override;
-
-    void guhTimer() override;
-    void upnpDiscoveryFinished(const QList &upnpDeviceDescriptorList) override;
-    void upnpNotifyReceived(const QByteArray ¬ifyData);
 
 private:
+    PluginTimer *m_pluginTimer = nullptr;
     QHash m_refreshReplies;
     QHash m_setPowerReplies;
     QHash m_runningActionExecutions;
@@ -58,6 +55,12 @@ private:
     void processRefreshData(const QByteArray &data, Device *device);
     void processSetPowerData(const QByteArray &data, Device *device, const ActionId &actionId);
 
+private slots:
+    void onNetworkReplyFinished();
+    void onPluginTimer();
+    void onUpnpDiscoveryFinished();
+    void onUpnpNotifyReceived(const QByteArray ¬ification);
+
 };
 
 #endif // DEVICEPLUGINWEMO_H
diff --git a/ws2812/devicepluginws2812.cpp b/ws2812/devicepluginws2812.cpp
index 72fb30a1..ccab78b5 100644
--- a/ws2812/devicepluginws2812.cpp
+++ b/ws2812/devicepluginws2812.cpp
@@ -44,7 +44,7 @@
 #include "devicepluginws2812.h"
 #include "plugin/device.h"
 #include "plugininfo.h"
-
+#include "network/networkaccessmanager.h"
 
 #include 
 #include 
@@ -55,10 +55,15 @@ DevicePluginWs2812::DevicePluginWs2812()
 
 }
 
-DeviceManager::HardwareResources DevicePluginWs2812::requiredHardware() const
+DevicePluginWs2812::~DevicePluginWs2812()
 {
-    // We need the NetworkAccessManager for node discovery and the timer for ping requests
-    return DeviceManager::HardwareResourceNetworkManager | DeviceManager::HardwareResourceTimer;
+    hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
+}
+
+void DevicePluginWs2812::init()
+{
+    m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(10);
+    connect(m_pluginTimer, &PluginTimer::timeout, this, &DevicePluginWs2812::onPluginTimer);
 }
 
 DeviceManager::DeviceSetupStatus DevicePluginWs2812::setupDevice(Device *device)
@@ -103,68 +108,18 @@ DeviceManager::DeviceError DevicePluginWs2812::discoverDevices(const DeviceClass
     url.setScheme("http");
     url.setHost(address.toString());
 
-    m_asyncNodeScans.insert(networkManagerGet(QNetworkRequest(url)), deviceClassId);
+    QNetworkReply *reply = hardwareManager()->networkManager()->get(QNetworkRequest(url));
+    connect(reply, &QNetworkReply::finished, this, &DevicePluginWs2812::onNetworkReplyFinished);
+    m_asyncNodeScans.insert(reply, deviceClassId);
     return DeviceManager::DeviceErrorAsync;
 }
 
-void DevicePluginWs2812::networkManagerReplyReady(QNetworkReply *reply)
-{
-    if (m_asyncNodeScans.keys().contains(reply)) {
-        DeviceClassId deviceClassId = m_asyncNodeScans.take(reply);
-        // Check HTTP status code
-        if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) {
-            qCWarning(dcWs2812) << "Node scan reply HTTP error:" << reply->errorString();
-            emit devicesDiscovered(deviceClassId, QList());
-            reply->deleteLater();
-            return;
-        }
-
-        QByteArray data = reply->readAll();
-        qCDebug(dcWs2812) << "Node discovery finished:" << endl << data;
-
-        QList deviceDescriptors;
-        QList lines = data.split('\n');
-        qCDebug(dcWs2812) << lines;
-        foreach (const QByteArray &line, lines) {
-            if (line.isEmpty())
-                continue;
-
-            QHostAddress address(QString(line.left(line.length() - 4)));
-            if (address.isNull())
-                continue;
-
-            qCDebug(dcWs2812) << "Found node" << address.toString();
-            // Create a deviceDescriptor for each found address
-            DeviceDescriptor descriptor(deviceClassId, "ws2812", address.toString());
-            ParamList params;
-            params.append(Param(hostParamTypeId, address.toString()));
-            descriptor.setParams(params);
-            deviceDescriptors.append(descriptor);
-        }
-        // Inform the user which devices were found
-        emit devicesDiscovered(deviceClassId, deviceDescriptors);
-    }
-
-    // Delete the HTTP reply
-    reply->deleteLater();
-}
-
 void DevicePluginWs2812::postSetupDevice(Device *device)
 {
     // Try to ping the device after a successful setup
     pingDevice(device);
 }
 
-void DevicePluginWs2812::guhTimer()
-{
-    // Try to ping each device every 10 seconds to make sure it is still reachable
-    foreach (Device *device, myDevices()) {
-        if (device->deviceClassId() == ws2812DeviceClassId) {
-            pingDevice(device);
-        }
-    }
-}
-
 DeviceManager::DeviceError DevicePluginWs2812::executeAction(Device *device, const Action &action)
 {
     if (device->deviceClassId() != ws2812DeviceClassId)
@@ -228,7 +183,7 @@ DeviceManager::DeviceError DevicePluginWs2812::executeAction(Device *device, con
         return DeviceManager::DeviceErrorAsync;
 
 
-    }else if(action.actionTypeId() == brightnessActionTypeId) {
+    } else if(action.actionTypeId() == brightnessActionTypeId) {
 
         QUrl url;
         url.setScheme("coap");
@@ -255,7 +210,7 @@ DeviceManager::DeviceError DevicePluginWs2812::executeAction(Device *device, con
         return DeviceManager::DeviceErrorAsync;
 
 
-    }else if(action.actionTypeId() == maxPixActionTypeId) {
+    } else if(action.actionTypeId() == maxPixActionTypeId) {
 
         QUrl url;
         url.setScheme("coap");
@@ -288,7 +243,7 @@ DeviceManager::DeviceError DevicePluginWs2812::executeAction(Device *device, con
         return DeviceManager::DeviceErrorAsync;
 
 
-    }else if(action.actionTypeId() == effectModeActionTypeId) {
+    } else if(action.actionTypeId() == effectModeActionTypeId) {
 
         int effectmode = 0;
 
@@ -304,7 +259,7 @@ DeviceManager::DeviceError DevicePluginWs2812::executeAction(Device *device, con
 
 
         //TODO switch to enum
-        if(effectModeString == "Off") {
+        if (effectModeString == "Off") {
             effectmode = 0;
         } else if (effectModeString == "Color On") {
             effectmode = 1;
@@ -312,17 +267,17 @@ DeviceManager::DeviceError DevicePluginWs2812::executeAction(Device *device, con
             effectmode = 2;
         } else if (effectModeString == "Color Fade") {
             effectmode = 3;
-        }else if (effectModeString == "Color Flash") {
+        } else if (effectModeString == "Color Flash") {
             effectmode = 4;
-        }else if (effectModeString == "Rainbow Wave") {
+        } else if (effectModeString == "Rainbow Wave") {
             effectmode = 5;
-        }else if (effectModeString == "Rainbow Flash") {
+        } else if (effectModeString == "Rainbow Flash") {
             effectmode = 6;
-        }else if (effectModeString == "Knight Rider") {
+        } else if (effectModeString == "Knight Rider") {
             effectmode = 7;
-        }else if (effectModeString == "Fire") {
+        } else if (effectModeString == "Fire") {
             effectmode = 8;
-        }else if (effectModeString == "Tricolore") {
+        } else if (effectModeString == "Tricolore") {
             effectmode = 9;
         }
 
@@ -330,7 +285,7 @@ DeviceManager::DeviceError DevicePluginWs2812::executeAction(Device *device, con
         qCDebug(dcWs2812()) << "Sending" << payload << url.path();
 
         CoapReply *reply = m_coap->post(CoapRequest(url), payload);
-        if (reply->isFinished() && reply->error() != CoapReply::NoError) {
+        if(reply->isFinished() && reply->error() != CoapReply::NoError) {
             qCWarning(dcWs2812) << "CoAP reply finished with error" << reply->errorString();
             setReachable(device, false);
             reply->deleteLater();
@@ -341,7 +296,7 @@ DeviceManager::DeviceError DevicePluginWs2812::executeAction(Device *device, con
         m_asyncActions.insert(action.id(), device);
         return DeviceManager::DeviceErrorAsync;
 
-    }else if(action.actionTypeId() == tcolor1ActionTypeId || action.actionTypeId() == tcolor2ActionTypeId || action.actionTypeId() == tcolor3ActionTypeId) {
+    } else if(action.actionTypeId() == tcolor1ActionTypeId || action.actionTypeId() == tcolor2ActionTypeId || action.actionTypeId() == tcolor3ActionTypeId) {
 
         QUrl url;
         url.setScheme("coap");
@@ -356,7 +311,7 @@ DeviceManager::DeviceError DevicePluginWs2812::executeAction(Device *device, con
          *
          */
 
-        if(action.actionTypeId() == tcolor1ActionTypeId){
+        if(action.actionTypeId() == tcolor1ActionTypeId) {
             tColor1 = action.param(tcolor1StateParamTypeId).value().value().toRgb();
         } else if(action.actionTypeId() == tcolor2ActionTypeId){
             tColor2 = action.param(tcolor2StateParamTypeId).value().value().toRgb();
@@ -558,6 +513,60 @@ Device *DevicePluginWs2812::findDevice(const QHostAddress &address)
     return NULL;
 }
 
+void DevicePluginWs2812::onPluginTimer()
+{
+    // Try to ping each device every 10 seconds to make sure it is still reachable
+    foreach (Device *device, myDevices()) {
+        if (device->deviceClassId() == ws2812DeviceClassId) {
+            pingDevice(device);
+        }
+    }
+}
+
+void DevicePluginWs2812::onNetworkReplyFinished()
+{
+    QNetworkReply *reply = static_cast(sender());
+
+    if (m_asyncNodeScans.keys().contains(reply)) {
+        DeviceClassId deviceClassId = m_asyncNodeScans.take(reply);
+        // Check HTTP status code
+        if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) {
+            qCWarning(dcWs2812) << "Node scan reply HTTP error:" << reply->errorString();
+            emit devicesDiscovered(deviceClassId, QList());
+            reply->deleteLater();
+            return;
+        }
+
+        QByteArray data = reply->readAll();
+        qCDebug(dcWs2812) << "Node discovery finished:" << endl << data;
+
+        QList deviceDescriptors;
+        QList lines = data.split('\n');
+        qCDebug(dcWs2812) << lines;
+        foreach (const QByteArray &line, lines) {
+            if (line.isEmpty())
+                continue;
+
+            QHostAddress address(QString(line.left(line.length() - 4)));
+            if (address.isNull())
+                continue;
+
+            qCDebug(dcWs2812) << "Found node" << address.toString();
+            // Create a deviceDescriptor for each found address
+            DeviceDescriptor descriptor(deviceClassId, "ws2812", address.toString());
+            ParamList params;
+            params.append(Param(hostParamTypeId, address.toString()));
+            descriptor.setParams(params);
+            deviceDescriptors.append(descriptor);
+        }
+        // Inform the user which devices were found
+        emit devicesDiscovered(deviceClassId, deviceDescriptors);
+    }
+
+    // Delete the HTTP reply
+    reply->deleteLater();
+}
+
 void DevicePluginWs2812::coapReplyFinished(CoapReply *reply)
 {
     if (m_pingReplies.contains(reply)) {
diff --git a/ws2812/devicepluginws2812.h b/ws2812/devicepluginws2812.h
index 5e8c1345..83a22272 100644
--- a/ws2812/devicepluginws2812.h
+++ b/ws2812/devicepluginws2812.h
@@ -27,6 +27,7 @@
 #include "plugin/deviceplugin.h"
 #include "types/action.h"
 #include "coap/coap.h"
+#include "plugintimer.h"
 
 #include 
 #include 
@@ -39,20 +40,17 @@ class DevicePluginWs2812 : public DevicePlugin
 
 public:
     explicit DevicePluginWs2812();
+    ~DevicePluginWs2812();
 
-    DeviceManager::HardwareResources requiredHardware() const override;
+    void init() override;
     DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override;
     DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
     void deviceRemoved(Device *device) override;
-    void networkManagerReplyReady(QNetworkReply *reply) override;
-
     void postSetupDevice(Device *device) override;
-
-    void guhTimer() override;
     DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
 
 private:
-
+    PluginTimer *m_pluginTimer = nullptr;
     QPointer m_coap;
     QHash m_asyncNodeScans;
     QHash m_enableNotification;
@@ -91,6 +89,8 @@ private:
     QColor tColor3;
 
 private slots:
+    void onPluginTimer();
+    void onNetworkReplyFinished();
     void coapReplyFinished(CoapReply *reply);
     void onNotificationReceived(const CoapObserveResource &resource, const int ¬ificationNumber, const QByteArray &payload);
 };