update awattar plugin

pull/135/head
Simon Stürz 2016-01-06 16:43:19 +01:00 committed by Michael Zanetti
parent e151c0f72b
commit c377d4137f
5 changed files with 226 additions and 46 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

View File

@ -29,7 +29,24 @@
In order to use this plugin you need to enter the access token from your energy provider. You can find more
information about you accesstoken \l{https://www.awattar.com/api-unser-datenfeed}{here}.
The data will be updated every hour. The API allows a maximum of 100 calls per day.
The pricing data will be updated every hour.
\chapter Available data
In following chart you can see an example of the market prices from -12 hours to + 12 hours from the current
time (0).The green line describes the current market price, the red point line describes the average
price of this interval and the red line describes the deviation. If the deviation is positiv, the current
price is above the average, if the deviation is negative, the current price is below the average.
\list
\li -100 % \unicode{0x2192} current price equals lowest price in the interval [-12h < now < + 12h]
\li 0 % \unicode{0x2192} current price equals average price in the interval [-12h < now < + 12h]
\li +100 % \unicode{0x2192} current price equals highest price in the interval [-12h < now < + 12h]
\endlist
\image awattar-graph.png
Information about the smart grid modes can be found \l{https://www.waermepumpe.de/sg-ready/}{here}.
\chapter Plugin properties
Following JSON file contains the definition and the description of all available \l{DeviceClass}{DeviceClasses}
@ -57,16 +74,11 @@
DevicePluginAwattar::DevicePluginAwattar()
{
m_timer = new QTimer(this);
m_timer->setSingleShot(false);
m_timer->setInterval(60000);
connect(m_timer, &QTimer::timeout, this, &DevicePluginAwattar::onTimeout);
}
DeviceManager::HardwareResources DevicePluginAwattar::requiredHardware() const
{
return DeviceManager::HardwareResourceNetworkManager;
return DeviceManager::HardwareResourceNetworkManager | DeviceManager::HardwareResourceTimer;
}
DeviceManager::DeviceSetupStatus DevicePluginAwattar::setupDevice(Device *device)
@ -74,7 +86,7 @@ DeviceManager::DeviceSetupStatus DevicePluginAwattar::setupDevice(Device *device
QString token = device->paramValue("token").toString();
qCDebug(dcAwattar) << "Setup device with token" << token;
QNetworkReply *reply = requestData(token);
QNetworkReply *reply = requestPriceData(token);
m_asyncSetup.insert(reply, device);
return DeviceManager::DeviceSetupStatusAsync;
@ -83,10 +95,6 @@ DeviceManager::DeviceSetupStatus DevicePluginAwattar::setupDevice(Device *device
void DevicePluginAwattar::deviceRemoved(Device *device)
{
Q_UNUSED(device)
if (myDevices().isEmpty()) {
m_timer->stop();
}
}
void DevicePluginAwattar::networkManagerReplyReady(QNetworkReply *reply)
@ -115,9 +123,13 @@ void DevicePluginAwattar::networkManagerReplyReady(QNetworkReply *reply)
return;
}
processData(device, jsonDoc.toVariant().toMap(), true);
} else if (m_update.keys().contains(reply)) {
Device *device = m_update.take(reply);
processPriceData(device, jsonDoc.toVariant().toMap(), true);
QNetworkReply *userReply = requestUserData(device->paramValue("token").toString(), device->paramValue("user id").toString());
m_updateUserData.insert(userReply, device);
} else if (m_updatePrice.keys().contains(reply)) {
Device *device = m_updatePrice.take(reply);
// check HTTP status code
if (status != 200) {
@ -135,12 +147,44 @@ void DevicePluginAwattar::networkManagerReplyReady(QNetworkReply *reply)
return;
}
processData(device, jsonDoc.toVariant().toMap());
processPriceData(device, jsonDoc.toVariant().toMap());
QNetworkReply *userReply = requestUserData(device->paramValue("token").toString(), device->paramValue("user id").toString());
m_updateUserData.insert(userReply, device);
} else if (m_updateUserData.keys().contains(reply)) {
Device *device = m_updateUserData.take(reply);
// check HTTP status code
if (status != 200) {
qCWarning(dcAwattar) << "Update user data reply HTTP error:" << status << reply->errorString();
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();
reply->deleteLater();
return;
}
processUserData(device, jsonDoc.toVariant().toMap());
}
reply->deleteLater();
}
void DevicePluginAwattar::processData(Device *device, const QVariantMap &data, const bool &fromSetup)
void DevicePluginAwattar::guhTimer()
{
foreach (Device *device, myDevices()) {
qCDebug(dcAwattar) << "Update device" << device->id().toString();
updateDevice(device);
}
}
void DevicePluginAwattar::processPriceData(Device *device, const QVariantMap &data, const bool &fromSetup)
{
if (!data.contains("data")) {
if (fromSetup) {
@ -155,28 +199,111 @@ void DevicePluginAwattar::processData(Device *device, const QVariantMap &data, c
QVariantList dataElements = data.value("data").toList();
QDateTime currentTime = QDateTime::currentDateTime();
double sum = 0;
double count = 0;
double averagePrice = 0;
double currentPrice = 0;
int deviation = 0;
double maxPrice = -1000;
double minPrice = 1000;
foreach (QVariant element, dataElements) {
QVariantMap elementMap = element.toMap();
QDateTime startTime = QDateTime::fromMSecsSinceEpoch((qint64)elementMap.value("start_timestamp").toLongLong());
QDateTime endTime = QDateTime::fromMSecsSinceEpoch((qint64)elementMap.value("end_timestamp").toLongLong());
double marketPrice = elementMap.value("marketprice").toDouble();
currentPrice = elementMap.value("marketprice").toDouble();
// check interval [-12h < x < + 12h]
if ((startTime >= currentTime.addSecs(-(3600 * 12)) && endTime <= currentTime ) ||
(endTime <= currentTime.addSecs(3600 * 12) && startTime >= currentTime )) {
//qCDebug(dcAwattar) << " - " << startTime.toString() << " - " << endTime.toString() << " : " << currentPrice;
sum += currentPrice;
count++;
if (currentPrice > maxPrice)
maxPrice = currentPrice;
if (currentPrice < minPrice)
minPrice = currentPrice;
}
if (currentTime >= startTime && currentTime <= endTime) {
qCDebug(dcAwattar) << "---------------------------------------";
qCDebug(dcAwattar) << "start :" << startTime.toString();
qCDebug(dcAwattar) << "end :" << endTime.toString();
qCDebug(dcAwattar) << "price :" << marketPrice << elementMap.value("unit").toString();
device->setStateValue(currentMarketPriceStateTypeId, marketPrice);
qCDebug(dcAwattar) << startTime.toString() << " -> " << endTime.toString();
device->setStateValue(currentMarketPriceStateTypeId, currentPrice);
device->setStateValue(validUntilStateTypeId, endTime.toLocalTime().toTime_t());
}
}
if (fromSetup) {
m_timer->start();
// calculate averagePrice and mean deviation
averagePrice = sum / count;
if (currentPrice <= averagePrice) {
deviation = -1 * qRound(100 + (-100 * (currentPrice - minPrice) / (averagePrice - minPrice)));
} else {
deviation = qRound(-100 * (averagePrice - currentPrice) / (maxPrice - averagePrice));
}
qCDebug(dcAwattar) << " price :" << currentPrice << "Eur/MWh";
qCDebug(dcAwattar) << " average :" << averagePrice << "Eur/MWh";
qCDebug(dcAwattar) << " deviation:" << deviation << "%";
qCDebug(dcAwattar) << " min :" << minPrice << "Eur/MWh";
qCDebug(dcAwattar) << " max :" << maxPrice << "Eur/MWh";
device->setStateValue(averagePriceStateTypeId, averagePrice);
device->setStateValue(lowestPriceStateTypeId, minPrice);
device->setStateValue(highestPriceStateTypeId, maxPrice);
device->setStateValue(meanDeviationStateTypeId, deviation);
if (fromSetup)
emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusSuccess);
}
void DevicePluginAwattar::processUserData(Device *device, const QVariantMap &data)
{
QVariantList dataElements = data.value("data").toList();
QDateTime currentTime = QDateTime::currentDateTime();
foreach (QVariant element, dataElements) {
QVariantMap elementMap = element.toMap();
QDateTime startTime = QDateTime::fromMSecsSinceEpoch((qint64)elementMap.value("start_timestamp").toLongLong());
QDateTime endTime = QDateTime::fromMSecsSinceEpoch((qint64)elementMap.value("end_timestamp").toLongLong());
// check if we are in the current interval
if (currentTime >= startTime && currentTime <= endTime) {
//qCDebug(dcAwattar) << QJsonDocument::fromVariant(elementMap).toJson();
int sgMode = 0;
if (elementMap.contains("data")) {
if (elementMap.value("data").toMap().contains("sg-mode")) {
sgMode = elementMap.value("data").toMap().value("sg-mode").toInt();
}
}
QNetworkReply *DevicePluginAwattar::requestData(const QString &token)
//qCDebug(dcAwattar) << startTime.toString() << " -> " << endTime.toString();
qCDebug(dcAwattar) << "sg-mode:" << sgMode;
switch (sgMode) {
case 1:
device->setStateValue(sgModeStateTypeId, "1 - Off");
break;
case 2:
device->setStateValue(sgModeStateTypeId, "2 - Normal");
break;
case 3:
device->setStateValue(sgModeStateTypeId, "3 - High Temperature");
break;
case 4:
device->setStateValue(sgModeStateTypeId, "4 - On");
break;
default:
break;
}
// todo: send sg mode to 6LoWPAN node
}
}
}
QNetworkReply *DevicePluginAwattar::requestPriceData(const QString &token)
{
QByteArray data = QString(token + ":").toUtf8().toBase64();
QString header = "Basic " + data;
@ -186,20 +313,19 @@ QNetworkReply *DevicePluginAwattar::requestData(const QString &token)
return networkManagerGet(request);
}
QNetworkReply *DevicePluginAwattar::requestUserData(const QString &token, const QString &userId)
{
QByteArray data = QString(token + ":").toUtf8().toBase64();
QString header = "Basic " + data;
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);
}
void DevicePluginAwattar::updateDevice(Device *device)
{
QNetworkReply *reply = requestData(device->paramValue("token").toString());
m_update.insert(reply, device);
QNetworkReply *priceReply = requestPriceData(device->paramValue("token").toString());
m_updatePrice.insert(priceReply, device);
}
void DevicePluginAwattar::onTimeout()
{
// check every hour
if(QDateTime::currentDateTime().time().minute() == 0) {
foreach (Device *device, myDevices()) {
qCDebug(dcAwattar) << "Update device" << device->id().toString();
updateDevice(device);
}
}
}

View File

@ -41,14 +41,19 @@ public:
void deviceRemoved(Device *device) override;
void networkManagerReplyReady(QNetworkReply *reply) override;
void guhTimer() override;
private:
QTimer *m_timer;
QHash<QNetworkReply *, Device *> m_asyncSetup;
QHash<QNetworkReply *, Device *> m_update;
QHash<QNetworkReply *, Device *> m_updatePrice;
QHash<QNetworkReply *, Device *> m_updateUserData;
void processData(Device *device, const QVariantMap &data, const bool &fromSetup = false);
void processPriceData(Device *device, const QVariantMap &data, const bool &fromSetup = false);
void processUserData(Device *device, const QVariantMap &data);
QNetworkReply *requestPriceData(const QString& token);
QNetworkReply *requestUserData(const QString& token, const QString &userId);
QNetworkReply *requestData(const QString& token);
void updateDevice(Device *device);
private slots:

View File

@ -23,7 +23,12 @@
"name": "name",
"type": "QString",
"inputType": "TextLine",
"defaultValue": "Energy price"
"defaultValue": "Heating system"
},
{
"name": "user id",
"type": "QString",
"inputType": "TextLine"
},
{
"name": "token",
@ -40,6 +45,14 @@
"unit": "EuroPerMegaWattHour",
"defaultValue": 0
},
{
"id": "38b86cee-9588-4269-a585-128907929dc2",
"idName": "meanDeviation",
"name": "deviation",
"type": "double",
"unit": "Percentage",
"defaultValue": 0
},
{
"id": "d5a8a176-aca0-45b1-b043-95c43750f383",
"idName": "validUntil",
@ -47,6 +60,43 @@
"unit": "UnixTime",
"type": "int",
"defaultValue": 0
},
{
"id": "55d6d7a8-446f-48ae-8014-1225810d03ee",
"idName": "averagePrice",
"name": "average market price [± 12 h]",
"type": "double",
"unit": "EuroPerMegaWattHour",
"defaultValue": 0
},
{
"id": "e7af5bdc-48d7-4e96-b877-331da4dcfae5",
"idName": "lowestPrice",
"name": "lowest market price [± 12 h]",
"type": "double",
"unit": "EuroPerMegaWattHour",
"defaultValue": 0
},
{
"id": "0c171c42-b070-453e-8a63-df9aebfa8533",
"idName": "highestPrice",
"name": " highest market price [± 12 h]",
"type": "double",
"unit": "EuroPerMegaWattHour",
"defaultValue": 0
},
{
"id": "b83d3533-aeae-4a9b-95d8-28466bf6c0cf",
"idName": "sgMode",
"name": "sg mode",
"type": "QString",
"possibleValues": [
"1 - Off",
"2 - Normal",
"3 - High Temperature",
"4 - On"
],
"defaultValue": "1 - Off"
}
]
}

View File

@ -79,7 +79,6 @@
\chapter Countdown
The countdown plugin allowes you to define a countown which triggers an \l{Event} on timeout.
\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}.