add coap to awattar plugin

pull/135/head
Simon Stürz 2016-01-14 14:46:41 +01:00 committed by Michael Zanetti
parent 8ff4af2c16
commit dfd8186c19
6 changed files with 352 additions and 4 deletions

View File

@ -3,9 +3,11 @@ include(../../plugins.pri)
TARGET = $$qtLibraryTarget(guh_devicepluginawattar)
SOURCES += \
devicepluginawattar.cpp
devicepluginawattar.cpp \
heatpump.cpp
HEADERS += \
devicepluginawattar.h
devicepluginawattar.h \
heatpump.h

View File

@ -93,6 +93,23 @@ DeviceManager::DeviceSetupStatus DevicePluginAwattar::setupDevice(Device *device
return DeviceManager::DeviceSetupStatusAsync;
}
void DevicePluginAwattar::startMonitoringAutoDevices()
{
QHostAddress rplAddress = QHostAddress(configuration().paramValue("RPL address").toString());
if (rplAddress.isNull()) {
qCWarning(dcAwattar) << "Invalid RPL address" << configuration().paramValue("RPL address").toString();
return;
}
qCDebug(dcAwattar) << "Search heat pump" << rplAddress.toString();
QNetworkRequest request(QUrl(QString("http://[%1]").arg(rplAddress.toString())));
QNetworkReply *reply = networkManagerGet(request);
m_searchPumpReplies.append(reply);
}
void DevicePluginAwattar::deviceRemoved(Device *device)
{
Q_UNUSED(device)
@ -173,7 +190,20 @@ void DevicePluginAwattar::networkManagerReplyReady(QNetworkReply *reply)
}
processUserData(device, 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();
}
@ -185,6 +215,21 @@ void DevicePluginAwattar::guhTimer()
}
}
DeviceManager::DeviceError DevicePluginAwattar::executeAction(Device *device, const Action &action)
{
Q_UNUSED(device)
if (action.actionTypeId() == ledPowerActionTypeId) {
foreach (HeatPump *pump, m_heatPumps) {
if (!pump->reachable())
return DeviceManager::DeviceErrorHardwareNotAvailable;
pump->setLed(action.param("led power").value().toBool());
}
}
return DeviceManager::DeviceErrorNoError;
}
void DevicePluginAwattar::processPriceData(Device *device, const QVariantMap &data, const bool &fromSetup)
{
if (!data.contains("data")) {
@ -304,7 +349,38 @@ void DevicePluginAwattar::processUserData(Device *device, const QVariantMap &dat
break;
}
// todo: send sg mode to 6LoWPAN node
foreach (HeatPump *pump, m_heatPumps) {
pump->setSgMode(sgMode);
}
}
}
}
void DevicePluginAwattar::processPumpSearchData(const QByteArray &data)
{
//qCDebug(dcAwattar) << "Search result:" << endl << data;
QList<QByteArray> lines = data.split('\n');
foreach (const QByteArray &line, lines) {
if (line.isEmpty())
continue;
// remove the '/128' from the address
QHostAddress pumpAddress(QString(data.left(line.length() - 4)));
if (!pumpAddress.isNull()) {
qCDebug(dcAwattar) << "Found heat pump at" << pumpAddress.toString();
// check if we already created this heat pump
if (heatPumpExists(pumpAddress))
continue;
HeatPump *pump = new HeatPump(pumpAddress, this);
connect(pump, SIGNAL(reachableChanged()), this, SLOT(onHeatPumpReachableChanged()));
m_heatPumps.append(pump);
} else {
qCWarning(dcAwattar) << "Could not read pump address" << line;
}
}
}
@ -335,3 +411,22 @@ void DevicePluginAwattar::updateDevice(Device *device)
QNetworkReply *priceReply = requestPriceData(device->paramValue("token").toString());
m_updatePrice.insert(priceReply, device);
}
bool DevicePluginAwattar::heatPumpExists(const QHostAddress &pumpAddress)
{
foreach (HeatPump *pump, m_heatPumps) {
if (pump->address() == pumpAddress) {
return true;
}
}
return false;
}
void DevicePluginAwattar::onHeatPumpReachableChanged()
{
HeatPump *pump = static_cast<HeatPump *>(sender());
foreach (Device *device, myDevices()) {
device->setStateValue(reachableStateTypeId, pump->reachable());
}
}

View File

@ -22,6 +22,7 @@
#define DEVICEPLUGINAWATTAR_H
#include "plugin/deviceplugin.h"
#include "heatpump.h"
#include <QHash>
#include <QDebug>
@ -39,26 +40,35 @@ public:
DeviceManager::HardwareResources requiredHardware() const override;
DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
void startMonitoringAutoDevices() override;
void deviceRemoved(Device *device) override;
void networkManagerReplyReady(QNetworkReply *reply) override;
void guhTimer() override;
DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
private:
QHash<QNetworkReply *, Device *> m_asyncSetup;
QHash<QNetworkReply *, Device *> m_updatePrice;
QHash<QNetworkReply *, Device *> m_updateUserData;
QList<QNetworkReply *> m_searchPumpReplies;
QList<HeatPump *> m_heatPumps;
void processPriceData(Device *device, const QVariantMap &data, const bool &fromSetup = false);
void processUserData(Device *device, const QVariantMap &data);
void processPumpSearchData(const QByteArray &data);
QNetworkReply *requestPriceData(const QString& token);
QNetworkReply *requestUserData(const QString& token, const QString &userId);
void updateDevice(Device *device);
bool heatPumpExists(const QHostAddress &pumpAddress);
private slots:
void onTimeout();
void onHeatPumpReachableChanged();
};

View File

@ -2,6 +2,14 @@
"name": "aWATTar",
"idName": "Awattar",
"id": "9c261c33-d44e-461e-8ec1-68803cb73f12",
"paramTypes": [
{
"name": "RPL address",
"type": "QString",
"inputType": "TextLine",
"defaultValue": "fdaa:e9b8:d03a::ff:fe00:1"
}
],
"vendors": [
{
"name": "aWATTar",
@ -85,6 +93,13 @@
"unit": "EuroPerMegaWattHour",
"defaultValue": 0
},
{
"id": "1c9d139a-6618-4a39-bc83-37f80942017d",
"idName": "reachable",
"name": "pump reachable",
"type": "bool",
"defaultValue": false
},
{
"id": "b83d3533-aeae-4a9b-95d8-28466bf6c0cf",
"idName": "sgMode",
@ -98,6 +113,19 @@
],
"defaultValue": "1 - Off"
}
],
"actionTypes": [
{
"id": "5be2f57f-a22d-4766-856a-a31481bcf6d6",
"idName": "ledPower",
"name": "led power",
"paramTypes": [
{
"name": "power",
"type": "bool"
}
]
}
]
}
]

View File

@ -0,0 +1,147 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2016 Simon Stuerz <simon.stuerz@guh.guru> *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "heatpump.h"
#include "coap/corelinkparser.h"
#include "extern-plugininfo.h"
HeatPump::HeatPump(QHostAddress address, QObject *parent) :
QObject(parent),
m_address(address),
m_reachable(false),
m_sgMode(1)
{
m_coap = new Coap(this);
connect(m_coap, SIGNAL(replyFinished(CoapReply*)), this, SLOT(onReplyFinished(CoapReply*)));
QUrl url;
url.setScheme("coap");
url.setHost(m_address.toString());
url.setPath("/.well-known/core");
qCDebug(dcAwattar) << "Discover pump resources on" << url.toString();
m_discoverReplies.append(m_coap->get(CoapRequest(url)));
}
QHostAddress HeatPump::address() const
{
return m_address;
}
bool HeatPump::reachable() const
{
return m_reachable;
}
void HeatPump::setSgMode(const int &sgMode)
{
QUrl url;
url.setScheme("coap");
url.setHost(m_address.toString());
url.setPath("/a/sg_mode");
m_sgModeReplies.append(m_coap->post(CoapRequest(url), QByteArray::number(sgMode)));
}
void HeatPump::setLed(const bool &power)
{
QUrl url;
url.setScheme("coap");
url.setHost(m_address.toString());
url.setPath("/a/led");
if (power) {
m_ledReplies.append(m_coap->post(CoapRequest(url), "mode=on"));
} else {
m_ledReplies.append(m_coap->post(CoapRequest(url), "mode=off"));
}
}
void HeatPump::setReachable(const bool &reachable)
{
m_reachable = reachable;
emit reachableChanged();
}
void HeatPump::onReplyFinished(CoapReply *reply)
{
if (m_discoverReplies.contains(reply)) {
m_discoverReplies.removeAll(reply);
if (reply->error() != CoapReply::NoError) {
qCWarning(dcAwattar()) << "CoAP resource discovery reply error" << reply->errorString();
setReachable(false);
reply->deleteLater();
return;
}
qCDebug(dcAwattar) << "Discovered successfully the resources";
CoreLinkParser parser(reply->payload());
foreach (const CoreLink &link, parser.links()) {
qCDebug(dcAwattar) << link << endl;
}
} else if (m_sgModeReplies.contains(reply)) {
m_sgModeReplies.removeAll(reply);
if (reply->error() != CoapReply::NoError) {
if (reachable())
qCWarning(dcAwattar()) << "CoAP sg-mode reply error" << reply->errorString();
setReachable(false);
reply->deleteLater();
return;
}
if (!reachable())
qCDebug(dcAwattar) << "Set sg-mode successfully.";
} else if (m_ledReplies.contains(reply)) {
m_ledReplies.removeAll(reply);
if (reply->error() != CoapReply::NoError) {
if (reachable())
qCWarning(dcAwattar()) << "CoAP set led power reply error" << reply->errorString();
setReachable(false);
reply->deleteLater();
return;
}
qCDebug(dcAwattar) << "Set led power successfully.";
} else {
// unhandled reply
if (reply->error() != CoapReply::NoError) {
qCWarning(dcAwattar()) << "CoAP reply error" << reply->errorString();
setReachable(false);
reply->deleteLater();
return;
}
qCDebug(dcAwattar) << reply;
}
// the reply had no error until now, so make sure the resource is reachable
setReachable(true);
reply->deleteLater();
}

View File

@ -0,0 +1,66 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2016 Simon Stuerz <simon.stuerz@guh.guru> *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef HEATPUMP_H
#define HEATPUMP_H
#include <QObject>
#include <QHostAddress>
#include "coap/coap.h"
#include "coap/coapreply.h"
#include "coap/coaprequest.h"
class HeatPump : public QObject
{
Q_OBJECT
public:
explicit HeatPump(QHostAddress address, QObject *parent = 0);
QHostAddress address() const;
bool reachable() const;
void setSgMode(const int &sgMode);
void setLed(const bool &power);
private:
QHostAddress m_address;
bool m_reachable;
int m_sgMode;
Coap *m_coap;
QList<CoapReply *> m_discoverReplies;
QList<CoapReply *> m_sgModeReplies;
QList<CoapReply *> m_ledReplies;
void setReachable(const bool &reachable);
private slots:
void onReplyFinished(CoapReply *reply);
signals:
void reachableChanged();
};
#endif // HEATPUMP_H