diff --git a/libguh/devicemanager.cpp b/libguh/devicemanager.cpp index 95e7154e..70872ce9 100644 --- a/libguh/devicemanager.cpp +++ b/libguh/devicemanager.cpp @@ -169,7 +169,7 @@ DeviceManager::DeviceManager(QObject *parent) : qRegisterMetaType(); qRegisterMetaType(); - m_pluginTimer.setInterval(15000); + m_pluginTimer.setInterval(10000); connect(&m_pluginTimer, &QTimer::timeout, this, &DeviceManager::timerEvent); m_settingsFile = QCoreApplication::instance()->organizationName() + "/devices"; diff --git a/libguh/network/networkmanager.cpp b/libguh/network/networkmanager.cpp index 237aa2ee..2fe81f2c 100644 --- a/libguh/network/networkmanager.cpp +++ b/libguh/network/networkmanager.cpp @@ -93,11 +93,8 @@ QNetworkReply *NetworkManager::put(const PluginId &pluginId, const QNetworkReque void NetworkManager::replyFinished(QNetworkReply *reply) { - if (reply->error()) { - qWarning() << "ERROR: network manager reply error: " << reply->errorString(); - } - // NOTE: Each plugin has to delete his own replys with deleteLater()!! + // NOTE: also the reply->error() has to be handled in each plugin!! PluginId pluginId = m_replies.take(reply); emit replyReady(pluginId, reply); } diff --git a/plugins/deviceplugins/openweathermap/devicepluginopenweathermap.cpp b/plugins/deviceplugins/openweathermap/devicepluginopenweathermap.cpp index 19d1d33f..24370db5 100644 --- a/plugins/deviceplugins/openweathermap/devicepluginopenweathermap.cpp +++ b/plugins/deviceplugins/openweathermap/devicepluginopenweathermap.cpp @@ -98,7 +98,7 @@ DeviceManager::DeviceSetupStatus DevicePluginOpenweathermap::setupDevice(Device } device->setName("Weather from OpenWeatherMap (" + device->paramValue("location").toString() + ")"); - update(); + update(device); return DeviceManager::DeviceSetupStatusSuccess; } diff --git a/plugins/deviceplugins/wemo/devicepluginwemo.cpp b/plugins/deviceplugins/wemo/devicepluginwemo.cpp index 011d54c3..7025ffae 100644 --- a/plugins/deviceplugins/wemo/devicepluginwemo.cpp +++ b/plugins/deviceplugins/wemo/devicepluginwemo.cpp @@ -27,7 +27,7 @@ \l{http://www.belkin.com/de/PRODUKTE/home-automation/c/wemo-home-automation/}{Belkin} home automation system. - \underline{NOTE}: The devices can only be discovered if they are already in the local network. In order + \note: The devices can only be discovered if they are already in the local network. In order to configure the WeMo devices please use the original software. \chapter Plugin properties @@ -50,6 +50,11 @@ #include "plugininfo.h" #include +#include +#include +#include +#include +#include DeviceClassId wemoSwitchDeviceClassId = DeviceClassId("69d97d3b-a8e6-42f3-afc0-ca8a53eb7cce"); @@ -75,28 +80,16 @@ DeviceManager::DeviceError DevicePluginWemo::discoverDevices(const DeviceClassId DeviceManager::DeviceSetupStatus DevicePluginWemo::setupDevice(Device *device) { if (device->deviceClassId() == wemoSwitchDeviceClassId) { - foreach (WemoSwitch *wemoSwitch, m_wemoSwitches.keys()) { - if (wemoSwitch->serialNumber() == device->paramValue("serial number").toString()) { - qWarning() << "WeMo Switch " << wemoSwitch->serialNumber() << " allready added..."; + foreach (Device *d, myDevices()) { + if (d->paramValue("serial number").toString() == device->paramValue("serial number").toString()) { + qWarning() << "WeMo Switch " << device->paramValue("serial number").toString() << " allready added..."; return DeviceManager::DeviceSetupStatusFailure; } } - UpnpDeviceDescriptor upnpDeviceDescriptor; - upnpDeviceDescriptor.setFriendlyName(device->paramValue("name").toString()); - upnpDeviceDescriptor.setHostAddress(QHostAddress(device->paramValue("host address").toString())); - upnpDeviceDescriptor.setPort(device->paramValue("port").toInt()); - upnpDeviceDescriptor.setSerialNumber(device->paramValue("serial number").toString()); - device->setName("WeMo Switch (" + device->paramValue("serial number").toString() + ")"); - WemoSwitch *wemoSwitch = new WemoSwitch(this, upnpDeviceDescriptor); - - connect(wemoSwitch,SIGNAL(stateChanged()),this,SLOT(wemoSwitchStateChanged())); - connect(wemoSwitch,SIGNAL(setPowerFinished(bool,ActionId)),this,SLOT(setPowerFinished(bool,ActionId))); - - m_wemoSwitches.insert(wemoSwitch,device); - wemoSwitch->refresh(); + refresh(device); return DeviceManager::DeviceSetupStatusSuccess; } return DeviceManager::DeviceSetupStatusFailure; @@ -104,49 +97,93 @@ DeviceManager::DeviceSetupStatus DevicePluginWemo::setupDevice(Device *device) DeviceManager::HardwareResources DevicePluginWemo::requiredHardware() const { - return DeviceManager::HardwareResourceTimer | DeviceManager::HardwareResourceUpnpDisovery; + return DeviceManager::HardwareResourceTimer | DeviceManager::HardwareResourceUpnpDisovery | DeviceManager::HardwareResourceNetworkManager; } DeviceManager::DeviceError DevicePluginWemo::executeAction(Device *device, const Action &action) { - if (device->deviceClassId() == wemoSwitchDeviceClassId) { - if (action.actionTypeId() == powerActionTypeId) { - WemoSwitch *wemoSwitch = m_wemoSwitches.key(device); - if (wemoSwitch->reachable()) { - // setPower returns false, if the curent powerState == new powerState - if (wemoSwitch->setPower(action.param("power").value().toBool(), action.id())) { - return DeviceManager::DeviceErrorAsync; - } else { - return DeviceManager::DeviceErrorNoError; - } - } else { - return DeviceManager::DeviceErrorHardwareNotAvailable; - } - } else if (action.actionTypeId() == rediscoverActionTypeId) { - upnpDiscover("upnp:rootdevice"); - return DeviceManager::DeviceErrorNoError; - } - return DeviceManager::DeviceErrorActionTypeNotFound; + if (device->deviceClassId() != wemoSwitchDeviceClassId) { + return DeviceManager::DeviceErrorDeviceClassNotFound; } - return DeviceManager::DeviceErrorDeviceClassNotFound; + + // Set power + if (action.actionTypeId() == powerActionTypeId) { + // Check if wemo device is reachable + if (device->stateValue(reachableStateTypeId).toBool()) { + // setPower returns false, if the curent powerState is allready the new powerState + if (setPower(device, action.param("power").value().toBool(), action.id())) { + return DeviceManager::DeviceErrorAsync; + } else { + return DeviceManager::DeviceErrorNoError; + } + } else { + return DeviceManager::DeviceErrorHardwareNotAvailable; + } + } + + // Rediscover + if (action.actionTypeId() == rediscoverActionTypeId) { + upnpDiscover("upnp:rootdevice"); + return DeviceManager::DeviceErrorNoError; + } + return DeviceManager::DeviceErrorActionTypeNotFound; } void DevicePluginWemo::deviceRemoved(Device *device) { - if (!m_wemoSwitches.values().contains(device)) { - return; + // Check if there is a missing reply for this device + foreach (Device *d, m_refreshReplies.values()) { + if (d->id() == device->id()) { + QNetworkReply * reply = m_refreshReplies.key(device); + m_refreshReplies.remove(reply); + // Note: delete will be done in networkManagerReplyReady() + } + } + foreach (Device *d, m_setPowerReplies.values()) { + if (d->id() == device->id()) { + QNetworkReply * reply = m_setPowerReplies.key(device); + m_setPowerReplies.remove(reply); + // Note: delete will be done in networkManagerReplyReady() + } + } +} + +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()) { + qWarning() << "ERROR: 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()) { + qWarning() << "ERROR: WeMo reply error: " << reply->errorString(); + } + device->setStateValue(reachableStateTypeId, false); + } else { + processSetPowerData(data, device, actionId); + } } - WemoSwitch *wemoSwitch= m_wemoSwitches.key(device); - qDebug() << "remove wemo swich " << wemoSwitch->serialNumber(); - m_wemoSwitches.remove(wemoSwitch); - wemoSwitch->deleteLater(); + reply->deleteLater(); } void DevicePluginWemo::guhTimer() { - foreach (WemoSwitch* wemoSwitch, m_wemoSwitches.keys()) { - wemoSwitch->refresh(); + foreach (Device* device, myDevices()) { + refresh(device); } } @@ -177,25 +214,22 @@ void DevicePluginWemo::upnpNotifyReceived(const QByteArray ¬ifyData) bool DevicePluginWemo::verifyExistingDevices(UpnpDeviceDescriptor deviceDescriptor) { - foreach (WemoSwitch *wemoSwitch, m_wemoSwitches.keys()) { + foreach (Device* device, myDevices()) { // check if we allready have added this Wemo device and verify the params - if (wemoSwitch->serialNumber() == deviceDescriptor.serialNumber()) { - qDebug() << "verify wemo paramters... of" << wemoSwitch->serialNumber(); - Device *device = m_wemoSwitches.value(wemoSwitch); + if (device->paramValue("serial number").toString() == deviceDescriptor.serialNumber()) { + qDebug() << "verify wemo paramters... of" << deviceDescriptor.serialNumber(); // now check if ip or port changed bool somethingChanged = false; - if (wemoSwitch->hostAddress() != deviceDescriptor.hostAddress()) { + if (device->paramValue("host address").toString() != deviceDescriptor.hostAddress().toString()) { device->setParamValue("host address", deviceDescriptor.hostAddress().toString()); - wemoSwitch->setHostAddress(deviceDescriptor.hostAddress()); somethingChanged = true; } - if(wemoSwitch->port() != deviceDescriptor.port()){ + if(device->paramValue("port").toInt() != deviceDescriptor.port()){ device->setParamValue("port", deviceDescriptor.port()); - wemoSwitch->setPort(deviceDescriptor.port()); somethingChanged = true; } if (somethingChanged) { - wemoSwitch->refresh(); + refresh(device); } return true; } @@ -203,23 +237,62 @@ bool DevicePluginWemo::verifyExistingDevices(UpnpDeviceDescriptor deviceDescript return false; } -void DevicePluginWemo::wemoSwitchStateChanged() +void DevicePluginWemo::refresh(Device *device) { - WemoSwitch *wemoSwitch = static_cast(sender()); + QByteArray getBinarayStateMessage("1"); - if (m_wemoSwitches.contains(wemoSwitch)) { - Device * device = m_wemoSwitches.value(wemoSwitch); - device->setStateValue(powerStateTypeId, wemoSwitch->powerState()); - device->setStateValue(reachableStateTypeId, wemoSwitch->reachable()); + QNetworkRequest request; + request.setUrl(QUrl("http://" + device->paramValue("host address").toString() + ":" + device->paramValue("port").toString() + "/upnp/control/basicevent1")); + request.setHeader(QNetworkRequest::ContentTypeHeader,QVariant("text/xml; charset=\"utf-8\"")); + request.setHeader(QNetworkRequest::UserAgentHeader,QVariant("guh")); + request.setRawHeader("SOAPACTION", "\"urn:Belkin:service:basicevent:1#GetBinaryState\""); + + QNetworkReply *reply = networkManagerPost(request, getBinarayStateMessage); + m_refreshReplies.insert(reply, device); +} + +bool DevicePluginWemo::setPower(Device *device, const bool &power, const ActionId &actionId) +{ + // check if the power would change... + if (device->stateValue(powerStateTypeId).toBool() == power) { + return false; + } + + QByteArray setPowerMessage("" + QByteArray::number((int)power) + ""); + + QNetworkRequest request; + request.setUrl(QUrl("http://" + device->paramValue("host address").toString() + ":" + device->paramValue("port").toString() + "/upnp/control/basicevent1")); + request.setHeader(QNetworkRequest::ContentTypeHeader,QVariant("text/xml; charset=\"utf-8\"")); + request.setHeader(QNetworkRequest::UserAgentHeader,QVariant("guh")); + request.setRawHeader("SOAPACTION", "\"urn:Belkin:service:basicevent:1#SetBinaryState\""); + + QNetworkReply *reply = networkManagerPost(request, setPowerMessage); + m_setPowerReplies.insert(reply, device); + m_runningActionExecutions.insert(reply, actionId); + return true; +} + +void DevicePluginWemo::processRefreshData(const QByteArray &data, Device *device) +{ + if (data.contains("0")) { + device->setStateValue(powerStateTypeId, false); + device->setStateValue(reachableStateTypeId, true); + } else if (data.contains("1")) { + device->setStateValue(powerStateTypeId, true); + device->setStateValue(reachableStateTypeId, true); + } else { + device->setStateValue(reachableStateTypeId, false); } } -void DevicePluginWemo::setPowerFinished(const bool &succeeded, const ActionId &actionId) +void DevicePluginWemo::processSetPowerData(const QByteArray &data, Device *device, const ActionId &actionId) { - if (succeeded) { + if (data.contains("1") || data.contains("0")) { emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorNoError); + device->setStateValue(reachableStateTypeId, true); + refresh(device); } else { + device->setStateValue(reachableStateTypeId, false); emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorHardwareNotAvailable); } } - diff --git a/plugins/deviceplugins/wemo/devicepluginwemo.h b/plugins/deviceplugins/wemo/devicepluginwemo.h index 9ff3431b..f659f44b 100644 --- a/plugins/deviceplugins/wemo/devicepluginwemo.h +++ b/plugins/deviceplugins/wemo/devicepluginwemo.h @@ -20,7 +20,6 @@ #define DEVICEPLUGINWEMO_H #include "plugin/deviceplugin.h" -#include "wemoswitch.h" class DevicePluginWemo : public DevicePlugin { @@ -38,22 +37,23 @@ public: 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: - QHash m_wemoSwitches; + QHash m_refreshReplies; + QHash m_setPowerReplies; + QHash m_runningActionExecutions; bool verifyExistingDevices(UpnpDeviceDescriptor deviceDescriptor); -private slots: - void wemoSwitchStateChanged(); - void setPowerFinished(const bool &succeeded, const ActionId &actionId); - - -public slots: + void refresh(Device* device); + bool setPower(Device *device, const bool &power, const ActionId &actionId); + void processRefreshData(const QByteArray &data, Device *device); + void processSetPowerData(const QByteArray &data, Device *device, const ActionId &actionId); }; diff --git a/plugins/deviceplugins/wemo/wemo.pro b/plugins/deviceplugins/wemo/wemo.pro index 4ca60b0c..240234f5 100644 --- a/plugins/deviceplugins/wemo/wemo.pro +++ b/plugins/deviceplugins/wemo/wemo.pro @@ -5,13 +5,10 @@ TARGET = $$qtLibraryTarget(guh_devicepluginwemo) QT+= network SOURCES += \ - devicepluginwemo.cpp \ - wemoswitch.cpp - + devicepluginwemo.cpp HEADERS += \ - devicepluginwemo.h \ - wemoswitch.h + devicepluginwemo.h diff --git a/plugins/deviceplugins/wemo/wemoswitch.cpp b/plugins/deviceplugins/wemo/wemoswitch.cpp index e4c2dbbb..0a0cc217 100644 --- a/plugins/deviceplugins/wemo/wemoswitch.cpp +++ b/plugins/deviceplugins/wemo/wemoswitch.cpp @@ -19,101 +19,28 @@ #include "wemoswitch.h" WemoSwitch::WemoSwitch(QObject *parent, UpnpDeviceDescriptor upnpDeviceDescriptor): - UpnpDevice(parent, upnpDeviceDescriptor) -{ - m_manager = new QNetworkAccessManager(this); - - connect(m_manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(replyFinished(QNetworkReply*))); -} - -WemoSwitch::~WemoSwitch() + UpnpDevice(parent, upnpDeviceDescriptor), + m_powerState(false), + m_reachable(false) { } -bool WemoSwitch::powerState() +void WemoSwitch::setPowerState(const bool &powerState) +{ + m_powerState = powerState; +} + +bool WemoSwitch::powerState() const { return m_powerState; } -bool WemoSwitch::reachable() +void WemoSwitch::setReachable(const bool &reachable) +{ + m_reachable = reachable; +} + +bool WemoSwitch::reachable() const { return m_reachable; } - -void WemoSwitch::replyFinished(QNetworkReply *reply) -{ - if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) { - // clean up - if (reply == m_setPowerReplay) { - emit setPowerFinished(false,m_actionId); - m_setPowerReplay->deleteLater(); - } - if (reply == m_refrashReplay) { - m_refrashReplay->deleteLater(); - } - m_reachable = false; - emit stateChanged(); - return; - } else { - m_reachable = true; - } - - // if this is the answerer to a refresh request - if (reply == m_refrashReplay) { - QByteArray data = reply->readAll(); - if (data.contains("0")) { - m_powerState = false; - } - if (data.contains("1")) { - m_powerState = true; - } - m_refrashReplay->deleteLater(); - } - // if this is the answerer to a "set power" request - if (reply == m_setPowerReplay) { - QByteArray data = reply->readAll(); - if (data.contains("1") || data.contains("0")) { - emit setPowerFinished(true,m_actionId); - } else { - emit setPowerFinished(false,m_actionId); - } - refresh(); - m_setPowerReplay->deleteLater(); - } - - emit stateChanged(); -} - -void WemoSwitch::refresh() -{ - QByteArray getBinarayStateMessage("1"); - - QNetworkRequest request; - request.setUrl(QUrl("http://" + hostAddress().toString() + ":" + QString::number(port()) + "/upnp/control/basicevent1")); - request.setHeader(QNetworkRequest::ContentTypeHeader,QVariant("text/xml; charset=\"utf-8\"")); - request.setHeader(QNetworkRequest::UserAgentHeader,QVariant("guh")); - request.setRawHeader("SOAPACTION", "\"urn:Belkin:service:basicevent:1#GetBinaryState\""); - - m_refrashReplay = m_manager->post(request,getBinarayStateMessage); -} - -bool WemoSwitch::setPower(const bool &power, const ActionId &actionId) -{ - m_actionId = actionId; - - // check if the power state changed... - if (m_powerState == power) { - return false; - } - - QByteArray setPowerMessage("" + QByteArray::number((int)power) + ""); - - QNetworkRequest request; - request.setUrl(QUrl("http://" + hostAddress().toString() + ":" + QString::number(port()) + "/upnp/control/basicevent1")); - request.setHeader(QNetworkRequest::ContentTypeHeader,QVariant("text/xml; charset=\"utf-8\"")); - request.setHeader(QNetworkRequest::UserAgentHeader,QVariant("guh")); - request.setRawHeader("SOAPACTION", "\"urn:Belkin:service:basicevent:1#SetBinaryState\""); - - m_setPowerReplay = m_manager->post(request,setPowerMessage); - return true; -} diff --git a/plugins/deviceplugins/wemo/wemoswitch.h b/plugins/deviceplugins/wemo/wemoswitch.h index ca08c1f3..15fce2ab 100644 --- a/plugins/deviceplugins/wemo/wemoswitch.h +++ b/plugins/deviceplugins/wemo/wemoswitch.h @@ -20,14 +20,6 @@ #define WEMOSWITCH_H #include -#include -#include -#include -#include -#include -#include -#include -#include #include "plugin/deviceplugin.h" #include "network/upnpdiscovery/upnpdevice.h" @@ -37,30 +29,17 @@ class WemoSwitch : public UpnpDevice Q_OBJECT public: explicit WemoSwitch(QObject *parent = 0, UpnpDeviceDescriptor upnpDeviceDescriptor = UpnpDeviceDescriptor()); - ~WemoSwitch(); - bool powerState(); - bool reachable(); + void setPowerState(const bool &powerState); + bool powerState() const; + + void setReachable(const bool &reachable); + bool reachable() const; private: - QNetworkAccessManager *m_manager; - QNetworkReply *m_refrashReplay; - QNetworkReply *m_setPowerReplay; - bool m_powerState; bool m_reachable; - ActionId m_actionId; -signals: - void stateChanged(); - void setPowerFinished(const bool &succeeded, const ActionId &actionId); - -private slots: - void replyFinished(QNetworkReply *reply); - -public slots: - void refresh(); - bool setPower(const bool &power, const ActionId &actionId); }; #endif // WEMOSWITCH_H