diff --git a/aqi/airqualityindex.cpp b/aqi/airqualityindex.cpp
new file mode 100644
index 00000000..3bb20fc0
--- /dev/null
+++ b/aqi/airqualityindex.cpp
@@ -0,0 +1,231 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2020, nymea GmbH
+* Contact: contact@nymea.io
+*
+* This file is part of nymea.
+* This project including source code and documentation is protected by
+* copyright law, and remains the property of nymea GmbH. All rights, including
+* reproduction, publication, editing and translation, are reserved. The use of
+* this project is subject to the terms of a license agreement to be concluded
+* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
+* under https://nymea.io/license
+*
+* GNU Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the
+* terms of the GNU Lesser General Public License as published by the Free
+* Software Foundation; version 3. This project 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 project. If not, see .
+*
+* For any further details and any questions please contact us under
+* contact@nymea.io or see our FAQ/Licensing Information on
+* https://nymea.io/license/faq
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#include "airqualityindex.h"
+#include "extern-plugininfo.h"
+
+#include
+#include
+#include
+#include
+#include
+
+AirQualityIndex::AirQualityIndex(NetworkAccessManager *networkAccessManager, const QString &apiKey, QObject *parent) :
+ QObject(parent),
+ m_networkAccessManager(networkAccessManager),
+ m_apiKey(apiKey)
+{
+
+}
+
+void AirQualityIndex::setApiKey(const QString &apiKey)
+{
+ m_apiKey = apiKey;
+}
+
+QUuid AirQualityIndex::searchByName(const QString &name)
+{
+ if (m_apiKey.isEmpty())
+ qCWarning(dcAirQualityIndex()) << "API key is not set";
+
+ QUuid requestId;
+ QUrl url;
+ url.setUrl(m_baseUrl);
+ url.setPath("/search/");
+ QUrlQuery query;
+ query.addQueryItem("token", m_apiKey);
+ query.addQueryItem("keyword", name);
+ url.setQuery(query);
+ QNetworkRequest request;
+ request.setUrl(url);
+ request.setRawHeader("User-Agent", "nymea");
+
+ QNetworkReply *reply = m_networkAccessManager->get(request);
+ connect(reply, &QNetworkReply::finished, this, [requestId, reply, this] {
+ reply->deleteLater();
+ int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+
+ // Check HTTP status code
+ if (status != 200 || reply->error() != QNetworkReply::NoError) {
+ if (status == 400) {
+ qCWarning(dcAirQualityIndex()) << "Request error due to exceeded request quota";
+ }
+ requestExecuted(requestId, false);
+ qCWarning(dcAirQualityIndex()) << "Request error:" << status << reply->errorString();
+ return;
+ }
+ QByteArray rawData = reply->readAll();
+ qCDebug(dcAirQualityIndex()) << "Search response" << rawData;
+
+ QJsonParseError error;
+ QJsonDocument doc = QJsonDocument::fromJson(rawData, &error);
+ if (error.error != QJsonParseError::NoError) {
+ emit requestExecuted(requestId, false);
+ qCWarning(dcAirQualityIndex()) << "Received invalide JSON object";
+ return;
+ }
+ emit requestExecuted(requestId, true);
+
+ QList stations;
+ QVariantList stationList = doc.toVariant().toMap().value("data").toList();
+ foreach (QVariant stationVariant, stationList) {
+ Station station;
+ station.aqi = stationVariant.toMap().value("aqi").toInt();
+ station.idx = stationVariant.toMap().value("idx").toInt();
+ station.measurementTime = QTime::fromString(stationVariant.toMap().value("time").toMap().value("s").toString());
+ station.timezone = stationVariant.toMap().value("time").toMap().value("tz").toString();
+ station.name = stationVariant.toMap().value("city").toMap().value("name").toString();
+ station.url = QUrl(stationVariant.toMap().value("city").toMap().value("url").toString());
+ station.location.latitude = stationVariant.toMap().value("city").toMap().value("geo").toList().first().toDouble();
+ station.location.longitude = stationVariant.toMap().value("city").toMap().value("geo").toList().last().toDouble();
+ stations.append(station);
+ }
+ if (!stations.isEmpty())
+ emit stationsReceived(requestId, stations);
+ });
+ return requestId;
+}
+
+QUuid AirQualityIndex::getDataByIp()
+{
+ if (m_apiKey.isEmpty())
+ qCWarning(dcAirQualityIndex()) << "API key is not set";
+
+ QUuid requestId;
+ QUrl url;
+ url.setUrl(m_baseUrl);
+ url.setPath("/feed/here/");
+ QUrlQuery query;
+ query.addQueryItem("token", m_apiKey);
+ url.setQuery(query);
+ QNetworkRequest request;
+ request.setUrl(url);
+ request.setRawHeader("User-Agent", "nymea");
+ qCDebug(dcAirQualityIndex()) << "Get data by IP request" << url.toString();
+ QNetworkReply *reply = m_networkAccessManager->get(request);
+ connect(reply, &QNetworkReply::finished, this, [requestId, reply, this] {
+ reply->deleteLater();
+ int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+
+ // Check HTTP status code
+ if (status != 200 || reply->error() != QNetworkReply::NoError) {
+ if (status == 400) {
+ qCWarning(dcAirQualityIndex()) << "Request error due to exceeded request quota";
+ }
+ requestExecuted(requestId, false);
+ qCWarning(dcAirQualityIndex()) << "Request error:" << status << reply->errorString();
+ return;
+ }
+ parseData(requestId, reply->readAll());
+ });
+ return requestId;
+}
+
+QUuid AirQualityIndex::getDataByGeolocation(const QString &lat, const QString &lng)
+{
+ if (m_apiKey.isEmpty())
+ qCWarning(dcAirQualityIndex()) << "API key is not set";
+
+ QUuid requestId;
+ QUrl url;
+ url.setUrl(m_baseUrl);
+ url.setPath("/feed/geo:"+lat+";"+lng+"/");
+ QUrlQuery query;
+ query.addQueryItem("token", m_apiKey);
+ url.setQuery(query);
+ QNetworkRequest request;
+ request.setUrl(url);
+ request.setRawHeader("User-Agent", "nymea");
+ qCDebug(dcAirQualityIndex()) << "Get data by geo location request" << url.toString();
+ QNetworkReply *reply = m_networkAccessManager->get(request);
+ connect(reply, &QNetworkReply::finished, this, [requestId, reply, this] {
+ reply->deleteLater();
+ int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+
+ // Check HTTP status code
+ if (status != 200 || reply->error() != QNetworkReply::NoError) {
+ if (status == 400) {
+ qCWarning(dcAirQualityIndex()) << "Request error due to exceeded request quota";
+ }
+ requestExecuted(requestId, false);
+ qCWarning(dcAirQualityIndex()) << "Request error:" << status << reply->errorString();
+ return;
+ }
+ requestExecuted(requestId, true);
+ parseData(requestId, reply->readAll());
+ });
+ return requestId;
+}
+
+
+void AirQualityIndex::parseData(QUuid requestId, const QByteArray &data)
+{
+ qCDebug(dcAirQualityIndex()) << "Parsing data" << data;
+ QJsonParseError error;
+ QJsonDocument doc = QJsonDocument::fromJson(data, &error);
+ if (error.error != QJsonParseError::NoError) {
+ emit requestExecuted(requestId, false);
+ qCWarning(dcAirQualityIndex()) << "Received invalide JSON object";
+ return;
+ }
+ emit requestExecuted(requestId, true);
+ Station station;
+ station.aqi = doc.toVariant().toMap().value("data").toMap().value("aqi").toInt();
+ station.idx = doc.toVariant().toMap().value("data").toMap().value("idx").toInt();
+
+ QVariantMap city = doc.toVariant().toMap().value("data").toMap().value("city").toMap();
+ if (city["geo"].toList().length() == 2) {
+ station.location.latitude = city["geo"].toList().first().toDouble();
+ station.location.longitude = city["geo"].toList().last().toDouble();
+ } else {
+ qCWarning(dcAirQualityIndex()) << "Parse data: geo location data list error" << city["geo"];
+ }
+ station.name = city["name"].toString();
+ station.url = city["url"].toString();
+
+ QVariantMap time = doc.toVariant().toMap().value("data").toMap().value("time").toMap();
+ station.timezone = time["tz"].toString();
+ station.measurementTime = QTime::fromString(time["s"].toString());
+ emit stationsReceived(requestId, QList() << station);
+
+ QVariantMap iaqi = doc.toVariant().toMap().value("data").toMap().value("iaqi").toMap();
+ AirQualityData aqiData;
+ aqiData.humidity = iaqi["h"].toMap().value("v").toDouble();
+ aqiData.pressure = iaqi["p"].toMap().value("v").toDouble();
+ aqiData.pm25 = iaqi["pm25"].toMap().value("v").toInt();
+ aqiData.pm10 = iaqi["pm10"].toMap().value("v").toInt();
+ aqiData.so2 = iaqi["so2"].toMap().value("v").toDouble();
+ aqiData.no2 = iaqi["no2"].toMap().value("v").toDouble();
+ aqiData.o3 = iaqi["o3"].toMap().value("v").toDouble();
+ aqiData.co = iaqi["co"].toMap().value("v").toDouble();
+ aqiData.temperature = iaqi["t"].toMap().value("v").toDouble();
+ aqiData.windSpeed = iaqi["w"].toMap().value("v").toDouble();
+ emit dataReceived(requestId, aqiData);
+}
diff --git a/aqi/airqualityindex.h b/aqi/airqualityindex.h
new file mode 100644
index 00000000..5f15b8d1
--- /dev/null
+++ b/aqi/airqualityindex.h
@@ -0,0 +1,91 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2020, nymea GmbH
+* Contact: contact@nymea.io
+*
+* This file is part of nymea.
+* This project including source code and documentation is protected by
+* copyright law, and remains the property of nymea GmbH. All rights, including
+* reproduction, publication, editing and translation, are reserved. The use of
+* this project is subject to the terms of a license agreement to be concluded
+* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
+* under https://nymea.io/license
+*
+* GNU Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the
+* terms of the GNU Lesser General Public License as published by the Free
+* Software Foundation; version 3. This project 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 project. If not, see .
+*
+* For any further details and any questions please contact us under
+* contact@nymea.io or see our FAQ/Licensing Information on
+* https://nymea.io/license/faq
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#ifndef AIRQUALITYINDEX_H
+#define AIRQUALITYINDEX_H
+
+#include "network/networkaccessmanager.h"
+
+#include
+#include
+#include
+
+class AirQualityIndex : public QObject
+{
+ Q_OBJECT
+public:
+ struct AirQualityData {
+ double humidity;
+ double pressure;
+ int pm25;
+ int pm10;
+ double so2;
+ double no2;
+ double o3;
+ double co;
+ double temperature;
+ double windSpeed;
+ };
+
+ struct GeoData {
+ double latitude;
+ double longitude;
+ };
+
+ struct Station {
+ int idx;
+ int aqi;
+ QTime measurementTime;
+ QString timezone;
+ QString name;
+ GeoData location;
+ QUrl url;
+ };
+
+ explicit AirQualityIndex(NetworkAccessManager *networkAccessManager, const QString &apiKey, QObject *parent = nullptr);
+ void setApiKey(const QString &apiKey);
+ QUuid searchByName(const QString &name);
+ QUuid getDataByIp();
+ QUuid getDataByGeolocation(const QString &lat, const QString &lng);
+
+private:
+ NetworkAccessManager *m_networkAccessManager;
+ QString m_baseUrl = "https://api.waqi.info";
+ QString m_apiKey;
+
+ void parseData(QUuid requestId, const QByteArray &data);
+
+signals:
+ void stationsReceived(QUuid requestId, QList stations);
+ void requestExecuted(QUuid requestId, bool success);
+ void dataReceived(QUuid requestId, const AirQualityData &data);
+};
+
+#endif // AIRQUALITYINDEX_H
diff --git a/aqi/aqi.pro b/aqi/aqi.pro
index 679f1419..0c7b9893 100644
--- a/aqi/aqi.pro
+++ b/aqi/aqi.pro
@@ -3,8 +3,10 @@ include(../plugins.pri)
QT+= network
SOURCES += \
+ airqualityindex.cpp \
integrationpluginaqi.cpp \
HEADERS += \
+ airqualityindex.h \
integrationpluginaqi.h \
diff --git a/aqi/integrationpluginaqi.cpp b/aqi/integrationpluginaqi.cpp
index 6b37c3c6..4e340505 100644
--- a/aqi/integrationpluginaqi.cpp
+++ b/aqi/integrationpluginaqi.cpp
@@ -32,59 +32,106 @@
#include "plugininfo.h"
#include
-#include
-#include
-#include
-#include
-#include
IntegrationPluginAqi::IntegrationPluginAqi()
{
}
+void IntegrationPluginAqi::startPairing(ThingPairingInfo *info)
+{
+ info->finish(Thing::ThingErrorNoError, QT_TR_NOOP("Please enter your API token for Air Quality Index"));
+}
+
+void IntegrationPluginAqi::confirmPairing(ThingPairingInfo *info, const QString &username, const QString &secret)
+{
+ Q_UNUSED(username)
+
+ QNetworkRequest request(QUrl("https://api.waqi.info/feed/here/?token="+secret));
+ QNetworkReply *reply = hardwareManager()->networkManager()->get(request);
+ connect(reply, &QNetworkReply::finished, info, [this, reply, info, secret](){
+ reply->deleteLater();
+
+ int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+
+ // check HTTP status code
+ if (status != 200) {
+ //: Error setting up device with invalid token
+ info->finish(Thing::ThingErrorAuthenticationFailure, QT_TR_NOOP("This token is not valid."));
+ return;
+ }
+
+ pluginStorage()->beginGroup(info->thingId().toString());
+ pluginStorage()->setValue("apiKey", secret);
+ pluginStorage()->endGroup();
+ info->finish(Thing::ThingErrorNoError);
+ });
+}
+
+void IntegrationPluginAqi::discoverThings(ThingDiscoveryInfo *info)
+{
+ if (!m_aqiConnection) {
+ QString apiKey = "74d31bb5ad9bcdeaed48097418b55188cb56d450"; //temporary key for discovery
+ m_aqiConnection = new AirQualityIndex(hardwareManager()->networkManager(), apiKey, this);
+ connect(m_aqiConnection, &AirQualityIndex::requestExecuted, this, &IntegrationPluginAqi::onRequestExecuted);
+ connect(m_aqiConnection, &AirQualityIndex::dataReceived, this, &IntegrationPluginAqi::onAirQualityDataReceived);
+ connect(m_aqiConnection, &AirQualityIndex::stationsReceived, this, &IntegrationPluginAqi::onAirQualityStationsReceived);
+
+ connect(info, &ThingDiscoveryInfo::aborted, [this] {
+ m_aqiConnection->deleteLater();
+ m_aqiConnection = nullptr;
+ });
+ } else {
+ qCDebug(dcAirQualityIndex()) << "AQI connection alread created";
+ }
+ QUuid requestId = m_aqiConnection->getDataByIp();
+ m_asyncDiscovery.insert(requestId, info);
+}
+
void IntegrationPluginAqi::setupThing(ThingSetupInfo *info)
{
if (info->thing()->thingClassId() == airQualityIndexThingClassId) {
+ if (!m_aqiConnection) {
+ pluginStorage()->beginGroup(info->thing()->id().toString());
+ QString apiKey = pluginStorage()->value("apiKey").toString();
+ pluginStorage()->endGroup();
+ m_aqiConnection = new AirQualityIndex(hardwareManager()->networkManager(), apiKey, this);
+ connect(m_aqiConnection, &AirQualityIndex::requestExecuted, this, &IntegrationPluginAqi::onRequestExecuted);
+ connect(m_aqiConnection, &AirQualityIndex::dataReceived, this, &IntegrationPluginAqi::onAirQualityDataReceived);
+ connect(m_aqiConnection, &AirQualityIndex::stationsReceived, this, &IntegrationPluginAqi::onAirQualityStationsReceived);
- if (!myThings().filterByThingClassId(info->thing()->thingClassId()).isEmpty()) {
- if (!myThings().findById(info->thing()->id())) {
- info->finish(Thing::ThingErrorSetupFailed, tr("Service is already in use."));
- return;
- }
+ QString longitude = info->thing()->paramValue(airQualityIndexThingLongitudeParamTypeId).toString();
+ QString latitude = info->thing()->paramValue(airQualityIndexThingLatitudeParamTypeId).toString();
+ QUuid requestId = m_aqiConnection->getDataByGeolocation(latitude, longitude);
+ m_asyncSetups.insert(requestId, info);
+
+ connect(info, &ThingSetupInfo::aborted, [requestId, this] {
+ m_asyncSetups.remove(requestId);
+ //m_aqiConnection->deleteLater();
+ //m_aqiConnection = nullptr;
+ });
+ } else {
+ info->finish(Thing::ThingErrorNoError);
}
-
- QUrl url;
- url.setUrl(m_baseUrl);
- url.setPath("/feed/here/");
- url.setQuery("token="+configValue(airQualityIndexPluginApiKeyParamTypeId).toString());
- QNetworkRequest request;
- request.setUrl(url);
- request.setRawHeader("User-Agent", "nymea 1.0");
-
- QNetworkReply *reply = hardwareManager()->networkManager()->get(request);
- connect(reply, &QNetworkReply::finished, this, [reply, info, this] {
- reply->deleteLater();
- int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
-
- // Check HTTP status code
- if (status != 200 || reply->error() != QNetworkReply::NoError) {
- if (status == 400 || status == 401) {
-
- }
- qCWarning(dcAirQualityIndex()) << "Request error:" << status << reply->errorString();
- return info->finish(Thing::ThingErrorSetupFailed, reply->errorString());
- }
- return info->finish(Thing::ThingErrorNoError);
- });
+ } else {
+ qCWarning(dcAirQualityIndex()) << "setupThing - thing class id not found" << info->thing()->thingClassId();
+ info->finish(Thing::ThingErrorSetupFailed);
}
}
void IntegrationPluginAqi::postSetupThing(Thing *thing)
{
- Q_UNUSED(thing);
- getDataByIp();
+ if (thing->thingClassId() == airQualityIndexThingClassId) {
+
+ if (!m_aqiConnection)
+ return;
+
+ QString longitude = thing->paramValue(airQualityIndexThingLongitudeParamTypeId).toString();
+ QString latitude = thing->paramValue(airQualityIndexThingLatitudeParamTypeId).toString();
+ QUuid requestId = m_aqiConnection->getDataByGeolocation(latitude, longitude);
+ m_asyncRequests.insert(requestId, thing->id());
+ }
if(!m_pluginTimer) {
m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(60);
@@ -95,103 +142,114 @@ void IntegrationPluginAqi::postSetupThing(Thing *thing)
void IntegrationPluginAqi::thingRemoved(Thing *thing)
{
- Q_UNUSED(thing);
+ if (thing->thingClassId() == airQualityIndexThingClassId) {
+
+ }
if (myThings().empty()) {
- hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
- m_pluginTimer = nullptr;
+ if (!m_pluginTimer) {
+ hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
+ m_pluginTimer = nullptr;
+ }
+ if (!m_aqiConnection) {
+ m_aqiConnection->deleteLater();
+ m_aqiConnection = nullptr;
+ }
}
}
-void IntegrationPluginAqi::getDataByIp()
+void IntegrationPluginAqi::onAirQualityDataReceived(QUuid requestId, AirQualityIndex::AirQualityData data)
{
- QUrl url;
- url.setUrl(m_baseUrl);
- url.setPath("/feed/here/");
- url.setQuery("token="+configValue(airQualityIndexPluginApiKeyParamTypeId).toString());
- QNetworkRequest request;
- request.setUrl(url);
- request.setRawHeader("User-Agent", "nymea");
+ if (m_asyncSetups.contains(requestId)) {
+ ThingSetupInfo *info = m_asyncSetups.value(requestId);
+ return info->finish(Thing::ThingErrorNoError);
+ }
- QNetworkReply *reply = hardwareManager()->networkManager()->get(request);
- connect(reply, &QNetworkReply::finished, this, [reply, this] {
- reply->deleteLater();
- int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
-
- // Check HTTP status code
- if (status != 200 || reply->error() != QNetworkReply::NoError) {
- foreach (Thing *thing, myThings().filterByThingClassId(airQualityIndexThingClassId)) {
- thing->setStateValue(airQualityIndexConnectedStateTypeId, true);
- }
- qCWarning(dcAirQualityIndex()) << "Request error:" << status << reply->errorString();
+ if (m_asyncRequests.contains(requestId)) {
+ Thing * thing = myThings().findById(m_asyncRequests.take(requestId));
+ if (!thing)
return;
+
+ //thing->setStateValue(airQualityIndexStationNameStateTypeId, data);
+ thing->setStateValue(airQualityIndexConnectedStateTypeId, true);
+ thing->setStateValue(airQualityIndexCoStateTypeId, data.co);
+ thing->setStateValue(airQualityIndexHumidityStateTypeId, data.humidity);
+ thing->setStateValue(airQualityIndexTemperatureStateTypeId, data.temperature);
+ thing->setStateValue(airQualityIndexPressureStateTypeId, data.pressure);
+ thing->setStateValue(airQualityIndexO3StateTypeId, data.o3);
+ thing->setStateValue(airQualityIndexNo2StateTypeId, data.no2);
+ thing->setStateValue(airQualityIndexSo2StateTypeId, data.so2);
+ thing->setStateValue(airQualityIndexPm10StateTypeId, data.pm10);
+ thing->setStateValue(airQualityIndexPm25StateTypeId, data.pm25);
+ thing->setStateValue(airQualityIndexWindSpeedStateTypeId, data.windSpeed);
+
+ if (data.pm25 <= 50.00) {
+ thing->setStateValue(airQualityIndexAirQualityStateTypeId, "Good");
+ thing->setStateValue(airQualityIndexCautionaryStatementStateTypeId, tr("None"));
+ } else if ((data.pm25 > 50.00) && (data.pm25 <= 100.00)) {
+ thing->setStateValue(airQualityIndexAirQualityStateTypeId, "Moderate");
+ thing->setStateValue(airQualityIndexCautionaryStatementStateTypeId, tr("Active children and adults, and people with respiratory disease, such as asthma, should limit prolonged outdoor exertion."));
+ } else if ((data.pm25 > 100.00) && (data.pm25 <= 150.00)) {
+ thing->setStateValue(airQualityIndexAirQualityStateTypeId, "Unhealthy for Sensitive Groups");
+ thing->setStateValue(airQualityIndexCautionaryStatementStateTypeId, tr("Active children and adults, and people with respiratory disease, such as asthma, should limit prolonged outdoor exertion."));
+ } else if ((data.pm25 > 150.00) && (data.pm25 <= 200.00)) {
+ thing->setStateValue(airQualityIndexAirQualityStateTypeId, "Unhealthy");
+ thing->setStateValue(airQualityIndexCautionaryStatementStateTypeId, tr("Active children and adults, and people with respiratory disease, such as asthma, should avoid prolonged outdoor exertion; everyone else, especially children, should limit prolonged outdoor exertion"));
+ } else if ((data.pm25 > 200.00) && (data.pm25 <= 300.00)) {
+ thing->setStateValue(airQualityIndexAirQualityStateTypeId, "Very Unhealthy");
+ thing->setStateValue(airQualityIndexCautionaryStatementStateTypeId, tr("Active children and adults, and people with respiratory disease, such as asthma, should avoid all outdoor exertion; everyone else, especially children, should limit outdoor exertion."));
+ } else {
+ thing->setStateValue(airQualityIndexAirQualityStateTypeId, "Hazardous");
+ thing->setStateValue(airQualityIndexCautionaryStatementStateTypeId, tr("Everyone should avoid all outdoor exertion"));
}
- QJsonParseError error;
- QJsonDocument data = QJsonDocument::fromJson(reply->readAll(), &error);
- if (error.error != QJsonParseError::NoError) {
- qDebug(dcAirQualityIndex()) << "Received invalide JSON object";
+ }
+}
+
+void IntegrationPluginAqi::onAirQualityStationsReceived(QUuid requestId, QList stations)
+{
+ if (m_asyncDiscovery.contains(requestId)) {
+ ThingDiscoveryInfo *info = m_asyncDiscovery.take(requestId);
+ foreach(AirQualityIndex::Station station, stations) {
+ ThingDescriptor descriptor(airQualityIndexThingClassId, station.name, "Air Quality Index Station");
+ ParamList params;
+ params << Param(airQualityIndexThingLatitudeParamTypeId, station.location.latitude);
+ params << Param(airQualityIndexThingLongitudeParamTypeId, station.location.longitude);
+ descriptor.setParams(params);
+ info->addThingDescriptor(descriptor);
+ }
+ info->finish(Thing::ThingErrorNoError);
+ }
+
+ if (m_asyncRequests.contains(requestId)) {
+ Thing *thing = myThings().findById(m_asyncRequests.take(requestId));
+ if (!thing)
return;
- }
- QVariantMap city = data.toVariant().toMap().value("data").toMap().value("city").toMap();
- //double lat = city["geo"].toList().takeAt(0).toDouble();
- //double lng = city["geo"].toList().takeAt(1).toDouble();
- QString name = city["name"].toString();
- QString url = city["url"].toString();
-
- QVariantMap iaqi = data.toVariant().toMap().value("data").toMap().value("iaqi").toMap();
- double humidity = iaqi["h"].toMap().value("v").toDouble();
- double pressure = iaqi["p"].toMap().value("v").toDouble();
- int pm25 = iaqi["pm25"].toMap().value("v").toInt();
- int pm10 = iaqi["pm10"].toMap().value("v").toInt();
- double so2 = iaqi["so2"].toMap().value("v").toDouble();
- double no2 = iaqi["no2"].toMap().value("v").toDouble();
- double o3 = iaqi["o3"].toMap().value("v").toDouble();
- double co = iaqi["co"].toMap().value("v").toDouble();
- double temperature = iaqi["t"].toMap().value("v").toDouble();
- double windSpeed = iaqi["w"].toMap().value("v").toDouble();
-
- foreach (Thing *thing, myThings().filterByThingClassId(airQualityIndexThingClassId)) {
- thing->setStateValue(airQualityIndexStationNameStateTypeId, name);
- thing->setStateValue(airQualityIndexConnectedStateTypeId, true);
- thing->setStateValue(airQualityIndexCoStateTypeId, co);
- thing->setStateValue(airQualityIndexHumidityStateTypeId, humidity);
- thing->setStateValue(airQualityIndexTemperatureStateTypeId, temperature);
- thing->setStateValue(airQualityIndexPressureStateTypeId, pressure);
- thing->setStateValue(airQualityIndexO3StateTypeId, o3);
- thing->setStateValue(airQualityIndexNo2StateTypeId, no2);
- thing->setStateValue(airQualityIndexSo2StateTypeId, so2);
- thing->setStateValue(airQualityIndexPm10StateTypeId, pm10);
- thing->setStateValue(airQualityIndexPm25StateTypeId, pm25);
- thing->setStateValue(airQualityIndexWindSpeedStateTypeId, windSpeed);
-
- if (pm25 <= 50.00) {
- thing->setStateValue(airQualityIndexAirQualityStateTypeId, "Good");
- thing->setStateValue(airQualityIndexCautionaryStatementStateTypeId, tr("None"));
- } else if ((pm25 > 50.00) && (pm25 <= 100.00)) {
- thing->setStateValue(airQualityIndexAirQualityStateTypeId, "Moderate");
- thing->setStateValue(airQualityIndexCautionaryStatementStateTypeId, tr("Active children and adults, and people with respiratory disease, such as asthma, should limit prolonged outdoor exertion."));
- } else if ((pm25 > 100.00) && (pm25 <= 150.00)) {
- thing->setStateValue(airQualityIndexAirQualityStateTypeId, "Unhealthy for Sensitive Groups");
- thing->setStateValue(airQualityIndexCautionaryStatementStateTypeId, tr("Active children and adults, and people with respiratory disease, such as asthma, should limit prolonged outdoor exertion."));
- } else if ((pm25 > 150.00) && (pm25 <= 200.00)) {
- thing->setStateValue(airQualityIndexAirQualityStateTypeId, "Unhealthy");
- thing->setStateValue(airQualityIndexCautionaryStatementStateTypeId, tr("Active children and adults, and people with respiratory disease, such as asthma, should avoid prolonged outdoor exertion; everyone else, especially children, should limit prolonged outdoor exertion"));
- } else if ((pm25 > 200.00) && (pm25 <= 300.00)) {
- thing->setStateValue(airQualityIndexAirQualityStateTypeId, "Very Unhealthy");
- thing->setStateValue(airQualityIndexCautionaryStatementStateTypeId, tr("Active children and adults, and people with respiratory disease, such as asthma, should avoid all outdoor exertion; everyone else, especially children, should limit outdoor exertion."));
- } else {
- thing->setStateValue(airQualityIndexAirQualityStateTypeId, "Hazardous");
- thing->setStateValue(airQualityIndexCautionaryStatementStateTypeId, tr("Everyone should avoid all outdoor exertion"));
- }
- }
- });
+ thing->setStateValue(airQualityIndexConnectedStateTypeId, true);
+ }
}
void IntegrationPluginAqi::onPluginTimer()
{
- getDataByIp();
+ if (!m_aqiConnection)
+ return;
+
+ foreach (Thing *thing, myThings().filterByThingClassId(airQualityIndexThingClassId)) {
+
+ QString longitude = thing->paramValue(airQualityIndexThingLongitudeParamTypeId).toString();
+ QString latitude = thing->paramValue(airQualityIndexThingLatitudeParamTypeId).toString();
+ QUuid requestId = m_aqiConnection->getDataByGeolocation(latitude, longitude);
+ m_asyncRequests.insert(requestId, thing->id());
+ }
}
+void IntegrationPluginAqi::onRequestExecuted(QUuid requestId, bool success)
+{
+ if (m_asyncRequests.contains(requestId)) {
-
+ Thing *thing = myThings().findById(m_asyncRequests.value(requestId));
+ thing->setStateValue(airQualityIndexConnectedStateTypeId, success);
+ if (!success)
+ m_asyncRequests.remove(requestId);
+ }
+}
diff --git a/aqi/integrationpluginaqi.h b/aqi/integrationpluginaqi.h
index 3b55eace..14808bfd 100644
--- a/aqi/integrationpluginaqi.h
+++ b/aqi/integrationpluginaqi.h
@@ -34,6 +34,7 @@
#include "plugintimer.h"
#include "integrations/integrationplugin.h"
#include "network/networkaccessmanager.h"
+#include "airqualityindex.h"
#include
#include
@@ -49,19 +50,26 @@ class IntegrationPluginAqi : public IntegrationPlugin
public:
explicit IntegrationPluginAqi();
+ void startPairing(ThingPairingInfo *info) override;
+ void confirmPairing(ThingPairingInfo *info, const QString &username, const QString &secret) override;
+ void discoverThings(ThingDiscoveryInfo *info) override;
void setupThing(ThingSetupInfo *info) override;
void thingRemoved(Thing *thing) override;
void postSetupThing(Thing *thing) override;
private:
PluginTimer *m_pluginTimer = nullptr;
- QString m_baseUrl = "https://api.waqi.info";
- QString m_apiKey;
+ AirQualityIndex *m_aqiConnection = nullptr;
- void getDataByIp();
+ QHash m_asyncDiscovery;
+ QHash m_asyncSetups;
+ QHash m_asyncRequests;
private slots:
void onPluginTimer();
+ void onRequestExecuted(QUuid requestId, bool success);
+ void onAirQualityDataReceived(QUuid requestId, AirQualityIndex::AirQualityData data);
+ void onAirQualityStationsReceived(QUuid requestId, QList stations);
};
#endif // INTEGRATIONPLUGINAQI_H
diff --git a/aqi/integrationpluginaqi.json b/aqi/integrationpluginaqi.json
index 3f85210f..85d2c9e9 100644
--- a/aqi/integrationpluginaqi.json
+++ b/aqi/integrationpluginaqi.json
@@ -2,15 +2,6 @@
"name": "AirQualityIndex",
"displayName": "AirQualityIndex",
"id": "57d69b76-4d2d-41ec-bef6-949a79ffbe6b",
- "paramTypes": [
- {
- "id": "a3525f8a-18be-4739-b0ea-ef3af0c7f280",
- "name": "apiKey",
- "displayName": "API key",
- "type": "QString",
- "defaultValue": "74d31bb5ad9bcdeaed48097418b55188cb56d450"
- }
- ],
"vendors": [
{
"name": "airQualityIndex",
@@ -22,8 +13,23 @@
"name": "airQualityIndex",
"displayName": "Air quality index",
"interfaces": ["windspeedsensor", "humiditysensor", "pressuresensor", "temperaturesensor", "connectable"],
- "createMethods": ["user"],
+ "createMethods": ["discovery", "user"],
+ "setupMethod": "displaypin",
"paramTypes": [
+ {
+ "id": "afd5803b-6c98-44d7-9f4a-45e91cfb062e",
+ "name": "latitude",
+ "displayName": "Latitude",
+ "type": "QString",
+ "inputType": "TextLine"
+ },
+ {
+ "id": "4800d78e-a367-41f7-9bf6-7c81d40ce19a",
+ "name": "longitude",
+ "displayName": "Longitude",
+ "type": "QString",
+ "inputType": "TextLine"
+ }
],
"stateTypes": [
{
diff --git a/debian/nymea-plugin-aqi.install.in b/debian/nymea-plugin-aqi.install.in
index 69ba2fda..e2bd73fd 100644
--- a/debian/nymea-plugin-aqi.install.in
+++ b/debian/nymea-plugin-aqi.install.in
@@ -1 +1 @@
-usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_devicepluginaqi.so
+usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginaqi.so