diff --git a/doc/typeutils.qdoc b/doc/typeutils.qdoc index 97ef7256..9e395d90 100644 --- a/doc/typeutils.qdoc +++ b/doc/typeutils.qdoc @@ -249,16 +249,20 @@ \li 34 \li The value of the \l{Param} has unit \tt {[kWh]} \unicode{0x2192} kilo watt hour. \row - \li Types::UnitPercentage + \li Types::UnitEuroPerMegaWattHour \li 35 + \li The value of the \l{Param} has unit \tt {[€/MWh]} \unicode{0x2192} euro per mega watt hour. + \row + \li Types::UnitPercentage + \li 36 \li The value of the \l{Param} has unit \tt {[\%]} \unicode{0x2192} percentage. \row \li Types::UnitEuro - \li 36 + \li 37 \li The value of the \l{Param} has unit \tt {[€]} \unicode{0x2192} euro. \row \li Types::UnitDollar - \li 37 + \li 38 \li The value of the \l{Param} has unit \tt {[\$]} \unicode{0x2192} dollar. \endtable diff --git a/guh.pri b/guh.pri index 71b7a2c0..44ac0d59 100644 --- a/guh.pri +++ b/guh.pri @@ -2,7 +2,7 @@ GUH_VERSION_STRING=$$system('dpkg-parsechangelog | sed -n -e "s/^Version: //p"') # define protocol versions -JSON_PROTOCOL_VERSION=29 +JSON_PROTOCOL_VERSION=30 REST_API_VERSION=1 DEFINES += GUH_VERSION_STRING=\\\"$${GUH_VERSION_STRING}\\\" \ diff --git a/libguh/plugin/deviceplugin.cpp b/libguh/plugin/deviceplugin.cpp index dd030733..90e04df0 100644 --- a/libguh/plugin/deviceplugin.cpp +++ b/libguh/plugin/deviceplugin.cpp @@ -756,6 +756,8 @@ Types::Unit DevicePlugin::unitStringToUnit(const QString &unitString) const return Types::UnitKiloWatt; } else if (unitString == "KiloWattHour") { return Types::UnitKiloWattHour; + } else if (unitString == "EuroPerMegaWattHour") { + return Types::UnitEuroPerMegaWattHour; } else if (unitString == "Percentage") { return Types::UnitPercentage; } else if (unitString == "Euro") { diff --git a/libguh/typeutils.h b/libguh/typeutils.h index fdfc55f8..81a2b989 100644 --- a/libguh/typeutils.h +++ b/libguh/typeutils.h @@ -113,6 +113,7 @@ public: UnitWatt, UnitKiloWatt, UnitKiloWattHour, + UnitEuroPerMegaWattHour, UnitPercentage, UnitEuro, UnitDollar diff --git a/plugins/deviceplugins/awattar/awattar.pro b/plugins/deviceplugins/awattar/awattar.pro new file mode 100644 index 00000000..11914d6b --- /dev/null +++ b/plugins/deviceplugins/awattar/awattar.pro @@ -0,0 +1,11 @@ +include(../../plugins.pri) + +TARGET = $$qtLibraryTarget(guh_devicepluginawattar) + +SOURCES += \ + devicepluginawattar.cpp + +HEADERS += \ + devicepluginawattar.h + + diff --git a/plugins/deviceplugins/awattar/devicepluginawattar.cpp b/plugins/deviceplugins/awattar/devicepluginawattar.cpp new file mode 100644 index 00000000..98ac248f --- /dev/null +++ b/plugins/deviceplugins/awattar/devicepluginawattar.cpp @@ -0,0 +1,185 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2015 Simon Stuerz * + * * + * 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 . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/*! + \page awattar.html + \title aWATTar + + \ingroup plugins + \ingroup network + + This plugin allows to receive the current energy market price from the \l{https://www.awattar.com/}{aWATTar GmbH}. + 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 fetched every hour. The API allows you a maximum of 100 calls per day. + + \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}. + + Each \l{DeviceClass} has a list of \l{ParamType}{paramTypes}, \l{ActionType}{actionTypes}, \l{StateType}{stateTypes} + and \l{EventType}{eventTypes}. The \l{DeviceClass::CreateMethod}{createMethods} parameter describes how the \l{Device} + will be created in the system. A device can have more than one \l{DeviceClass::CreateMethod}{CreateMethod}. + The \l{DeviceClass::SetupMethod}{setupMethod} describes the setup method of the \l{Device}. + The detailed implementation of each \l{DeviceClass} can be found in the source code. + + \note If a \l{StateType} has the parameter \tt{"writable": {...}}, an \l{ActionType} with the same uuid and \l{ParamType}{ParamTypes} + will be created automatically. + + \quotefile plugins/deviceplugins/udpcommander/devicepluginawattar.json +*/ + +#include "devicepluginawattar.h" +#include "plugin/device.h" +#include "plugininfo.h" + +#include +#include +#include + +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; +} + +DeviceManager::DeviceSetupStatus DevicePluginAwattar::setupDevice(Device *device) +{ + QString token = device->paramValue("token").toString(); + qCDebug(dcAwattar) << "Setup device with token" << token; + + QNetworkReply *reply = requestData(token); + m_asyncSetup.insert(reply, device); + + return DeviceManager::DeviceSetupStatusAsync; +} + +void DevicePluginAwattar::deviceRemoved(Device *device) +{ + Q_UNUSED(device) + + if (myDevices().isEmpty()) { + m_timer->stop(); + } +} + +void DevicePluginAwattar::networkManagerReplyReady(QNetworkReply *reply) +{ + int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + // create user finished + if (m_asyncSetup.keys().contains(reply)) { + Device *device = m_asyncSetup.take(reply); + + // check HTTP status code + if (status != 200) { + qCWarning(dcAwattar) << "Setup reply HTTP error:" << status << reply->errorString(); + emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusFailure); + reply->deleteLater(); + return; + } + + // check JSON file + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll(), &error); + if (error.error != QJsonParseError::NoError) { + qCWarning(dcAwattar) << "Setup reply JSON error:" << error.errorString(); + emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusFailure); + reply->deleteLater(); + return; + } + + processData(device, jsonDoc.toVariant().toMap(), true); + } + reply->deleteLater(); +} + +void DevicePluginAwattar::processData(Device *device, const QVariantMap &data, const bool &fromSetup) +{ + if (!data.contains("data")) { + if (fromSetup) { + qCWarning(dcAwattar) << "Device setup failed." << device->id().toString() << "No data element received"; + emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusFailure); + return; + } + qCWarning(dcAwattar) << "Update failed for device" << device->id().toString() << "No data element received"; + return; + } + + 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()); + double marketPrice = elementMap.value("marketprice").toDouble(); + 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); + } + } + + if (fromSetup) { + m_timer->start(); + emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusSuccess); + } +} + +QNetworkReply *DevicePluginAwattar::requestData(const QString &token) +{ + QByteArray data = QString(token + ":").toUtf8().toBase64(); + QString header = "Basic " + data; + QNetworkRequest request(QUrl("https://api.awattar.com/v1/marketdata")); + 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); +} + +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); + } + } +} + diff --git a/plugins/deviceplugins/awattar/devicepluginawattar.h b/plugins/deviceplugins/awattar/devicepluginawattar.h new file mode 100644 index 00000000..0cc708bb --- /dev/null +++ b/plugins/deviceplugins/awattar/devicepluginawattar.h @@ -0,0 +1,59 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2015 Simon Stuerz * + * * + * 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 . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef DEVICEPLUGINAWATTAR_H +#define DEVICEPLUGINAWATTAR_H + +#include "plugin/deviceplugin.h" + +#include +#include +#include + +class DevicePluginAwattar : public DevicePlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "guru.guh.DevicePlugin" FILE "devicepluginawattar.json") + Q_INTERFACES(DevicePlugin) + +public: + explicit DevicePluginAwattar(); + + DeviceManager::HardwareResources requiredHardware() const override; + DeviceManager::DeviceSetupStatus setupDevice(Device *device) override; + void deviceRemoved(Device *device) override; + void networkManagerReplyReady(QNetworkReply *reply) override; + +private: + QTimer *m_timer; + QHash m_asyncSetup; + QHash m_update; + + void processData(Device *device, const QVariantMap &data, const bool &fromSetup = false); + + QNetworkReply *requestData(const QString& token); + void updateDevice(Device *device); + +private slots: + void onTimeout(); + +}; + +#endif // DEVICEPLUGINAWATTAR_H diff --git a/plugins/deviceplugins/awattar/devicepluginawattar.json b/plugins/deviceplugins/awattar/devicepluginawattar.json new file mode 100644 index 00000000..50d59f4f --- /dev/null +++ b/plugins/deviceplugins/awattar/devicepluginawattar.json @@ -0,0 +1,42 @@ +{ + "name": "aWATTar", + "idName": "Awattar", + "id": "9c261c33-d44e-461e-8ec1-68803cb73f12", + "vendors": [ + { + "name": "aWATTar", + "idName": "awattar", + "id": "acd47238-bbbc-4eaf-b484-38c52cfa4866", + "deviceClasses": [ + { + "deviceClassId": "29cd8265-d8bb-4cf9-9080-bfc2cf9787bc", + "name": "aWATTar", + "createMethods": ["user"], + "paramTypes": [ + { + "name": "name", + "type": "QString", + "inputType": "TextLine", + "defaultValue": "Energy price" + }, + { + "name": "token", + "type": "QString", + "inputType": "TextLine" + } + ], + "stateTypes": [ + { + "id": "eab37309-3dd8-46a0-94d4-bd05b5bb0430", + "idName": "currentMarketPrice", + "name": "current market price", + "type": "double", + "unit": "EuroPerMegaWattHour", + "defaultValue": 0 + } + ] + } + ] + } + ] +} diff --git a/plugins/deviceplugins/deviceplugins.pro b/plugins/deviceplugins/deviceplugins.pro index 3be90732..54956118 100644 --- a/plugins/deviceplugins/deviceplugins.pro +++ b/plugins/deviceplugins/deviceplugins.pro @@ -18,6 +18,8 @@ SUBDIRS += elro \ tune \ udpcommander \ kodi \ + elgato \ + awattar \ diff --git a/tests/auto/api.json b/tests/auto/api.json index ae63c8cf..cfd595d6 100644 --- a/tests/auto/api.json +++ b/tests/auto/api.json @@ -1,4 +1,4 @@ -29 +30 { "methods": { "Actions.ExecuteAction": { @@ -803,6 +803,7 @@ "UnitWatt", "UnitKiloWatt", "UnitKiloWattHour", + "UnitEuroPerMegaWattHour", "UnitPercentage", "UnitEuro", "UnitDollar"