diff --git a/aqi/README.md b/aqi/README.md
new file mode 100644
index 00000000..6e18d23f
--- /dev/null
+++ b/aqi/README.md
@@ -0,0 +1,42 @@
+
+# Air Quality Index
+
+This plug-in gets air quality information from http://aqicn.org.
+Through the WAN IP address the next nearby sensor station will be discovered.
+The geo location can also be set manually.
+
+## Supported Things
+
+* Air Quality Index
+ * Location discovery
+ * Manually location set
+ * Air Quality
+ * Cautionary statement
+ * PM2.5 pollution level
+ * PM10 pollution lebel
+ * Ozone level
+ * Nitrogen dioxide level
+ * Carbon monoxide level
+ * Sulfur dioxide level
+ * Temperature
+ * Humidity
+ * Pressure
+ * Wind speed
+
+NOTE: If you encounter that a value stays to zero, it means the sensor station
+doesn't support that value.
+
+Besides the air pollution level the plug-in also states a cautionary statement.
+Both states can be used to let nymea notify you about the pollution level and
+inform you what precautions should be taken.
+
+## Requirments
+
+* Valid "Air Quality Index" API Key
+* The package "nymea-plugin-airqualityindex" must be installed
+* Internet connection
+
+## More
+
+More about the different Air Quality Levels: https://www.airnow.gov/index.cfm?action=aqibasics.aqi
+
diff --git a/aqi/airqualityindex.cpp b/aqi/airqualityindex.cpp
new file mode 100644
index 00000000..9373a2c8
--- /dev/null
+++ b/aqi/airqualityindex.cpp
@@ -0,0 +1,234 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 = QUuid::createUuid();;
+ 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;
+ }
+
+ 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);
+
+ requestExecuted(requestId, true);
+ });
+ return requestId;
+}
+
+QUuid AirQualityIndex::getDataByIp()
+{
+ if (m_apiKey.isEmpty())
+ qCWarning(dcAirQualityIndex()) << "API key is not set";
+
+ QUuid requestId = QUuid::createUuid();;
+ 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;
+ }
+ if (!parseData(requestId, reply->readAll()))
+ requestExecuted(requestId, false);
+ requestExecuted(requestId, true);
+ });
+ return requestId;
+}
+
+QUuid AirQualityIndex::getDataByGeolocation(const QString &lat, const QString &lng)
+{
+ if (m_apiKey.isEmpty())
+ qCWarning(dcAirQualityIndex()) << "API key is not set";
+
+ QUuid requestId = QUuid::createUuid();
+ 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;
+ }
+ if (!parseData(requestId, reply->readAll()))
+ requestExecuted(requestId, false);
+ requestExecuted(requestId, true);
+ });
+ return requestId;
+}
+
+
+bool 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) {
+ qCWarning(dcAirQualityIndex()) << "Received invalide JSON object";
+ return false;
+ }
+ 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);
+ return true;
+}
diff --git a/aqi/airqualityindex.h b/aqi/airqualityindex.h
new file mode 100644
index 00000000..fd332c42
--- /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;
+
+ bool 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
new file mode 100644
index 00000000..0c7b9893
--- /dev/null
+++ b/aqi/aqi.pro
@@ -0,0 +1,12 @@
+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
new file mode 100644
index 00000000..f6a21a77
--- /dev/null
+++ b/aqi/integrationpluginaqi.cpp
@@ -0,0 +1,282 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 "integrationpluginaqi.h"
+#include "plugininfo.h"
+
+#include
+
+IntegrationPluginAqi::IntegrationPluginAqi()
+{
+
+}
+
+void IntegrationPluginAqi::startPairing(ThingPairingInfo *info)
+{
+ NetworkAccessManager *network = hardwareManager()->networkManager();
+ QNetworkReply *reply = network->get(QNetworkRequest(QUrl("https://api.waqi.info")));
+ connect(reply, &QNetworkReply::finished, this, [reply, info] {
+ reply->deleteLater();
+
+ if (reply->error() == QNetworkReply::NetworkError::HostNotFoundError) {
+ info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Air quality index server is not reachable."));
+ } else {
+ 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] {
+ if (myThings().filterByThingClassId(airQualityIndexThingClassId).isEmpty()) {
+ m_aqiConnection->deleteLater();
+ m_aqiConnection = nullptr;
+ }
+ });
+ } else {
+ qCDebug(dcAirQualityIndex()) << "AQI connection alread created";
+ }
+ QUuid requestId = m_aqiConnection->getDataByIp();
+ m_asyncDiscovery.insert(requestId, info);
+ connect(info, &ThingDiscoveryInfo::aborted, [=] {m_asyncDiscovery.remove(requestId);});
+}
+
+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);
+
+ 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);
+ if (myThings().filterByThingClassId(airQualityIndexThingClassId).isEmpty()) {
+ m_aqiConnection->deleteLater();
+ m_aqiConnection = nullptr;
+ }
+ });
+ } else {
+ // An AQI connection might be setup because of an discovery request
+ // or because there is already another thing using the connection
+ // In any case the API key is being updated to avoid using the discovery key.
+ pluginStorage()->beginGroup(info->thing()->id().toString());
+ QString apiKey = pluginStorage()->value("apiKey").toString();
+ pluginStorage()->endGroup();
+ m_aqiConnection->setApiKey(apiKey);
+ 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)
+{
+ if (thing->thingClassId() == airQualityIndexThingClassId) {
+
+ if (!m_aqiConnection) {
+ qCWarning(dcAirQualityIndex()) << "Air quality connection not initialized";
+ 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);
+ connect(m_pluginTimer, &PluginTimer::timeout, this, &IntegrationPluginAqi::onPluginTimer);
+ }
+}
+
+void IntegrationPluginAqi::thingRemoved(Thing *thing)
+{
+ Q_UNUSED(thing)
+ if (myThings().empty()) {
+ if (m_pluginTimer) {
+ hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
+ m_pluginTimer = nullptr; }
+ if (m_aqiConnection) {
+ m_aqiConnection->deleteLater();
+ m_aqiConnection = nullptr;
+ }
+ }
+}
+
+void IntegrationPluginAqi::onAirQualityDataReceived(QUuid requestId, AirQualityIndex::AirQualityData data)
+{
+ qCDebug(dcAirQualityIndex()) << "Air Quality data received, request id:" << requestId << "is an async request:" << m_asyncRequests.contains(requestId);
+
+ if (m_asyncSetups.contains(requestId)) {
+ ThingSetupInfo *info = m_asyncSetups.value(requestId);
+ return info->finish(Thing::ThingErrorNoError);
+ }
+
+ if (m_asyncRequests.contains(requestId)) {
+ Thing * thing = myThings().findById(m_asyncRequests.take(requestId));
+ if (!thing)
+ return;
+
+ 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"));
+ }
+ }
+}
+
+void IntegrationPluginAqi::onAirQualityStationsReceived(QUuid requestId, QList stations)
+{
+ qCDebug(dcAirQualityIndex()) << "Air Quality Stations received, request id:" << requestId << "is an async request:" << m_asyncRequests.contains(requestId);
+ 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.value(requestId));
+ if (!thing) {
+ qCWarning(dcAirQualityIndex()) << "Can't find thing, associated to this async request";
+ return;
+ }
+ if (stations.length() != 0) {
+ thing->setStateValue(airQualityIndexStationNameStateTypeId, stations.first().name);
+ }
+ }
+}
+
+void IntegrationPluginAqi::onPluginTimer()
+{
+ 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)
+{
+ qCDebug(dcAirQualityIndex()) << "Request executd, requestId:" << requestId << "Success:" << success << "is an async request:" << m_asyncRequests.contains(requestId);
+ if (m_asyncRequests.contains(requestId)) {
+
+ Thing *thing = myThings().findById(m_asyncRequests.value(requestId));
+ thing->setStateValue(airQualityIndexConnectedStateTypeId, success);
+ if (!success) {
+ qCWarning(dcAirQualityIndex()) << "Request failed, removing request from async request list";
+ }
+ m_asyncRequests.remove(requestId);
+ }
+}
diff --git a/aqi/integrationpluginaqi.h b/aqi/integrationpluginaqi.h
new file mode 100644
index 00000000..14808bfd
--- /dev/null
+++ b/aqi/integrationpluginaqi.h
@@ -0,0 +1,75 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 INTEGRATIONPLUGINAQI_H
+#define INTEGRATIONPLUGINAQI_H
+
+#include "plugintimer.h"
+#include "integrations/integrationplugin.h"
+#include "network/networkaccessmanager.h"
+#include "airqualityindex.h"
+
+#include
+#include
+#include
+
+class IntegrationPluginAqi : public IntegrationPlugin
+{
+ Q_OBJECT
+
+ Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginaqi.json")
+ Q_INTERFACES(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;
+ AirQualityIndex *m_aqiConnection = nullptr;
+
+ 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
new file mode 100644
index 00000000..85d2c9e9
--- /dev/null
+++ b/aqi/integrationpluginaqi.json
@@ -0,0 +1,171 @@
+{
+ "name": "AirQualityIndex",
+ "displayName": "AirQualityIndex",
+ "id": "57d69b76-4d2d-41ec-bef6-949a79ffbe6b",
+ "vendors": [
+ {
+ "name": "airQualityIndex",
+ "displayName": "Air Quality Index",
+ "id": "6c8e2ded-0a33-4e77-b76c-ea02168741ec",
+ "thingClasses": [
+ {
+ "id": "23ea32c9-38b0-4155-bacc-3afa8c09f6ee",
+ "name": "airQualityIndex",
+ "displayName": "Air quality index",
+ "interfaces": ["windspeedsensor", "humiditysensor", "pressuresensor", "temperaturesensor", "connectable"],
+ "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": [
+ {
+ "id": "7b9135cd-2461-4d33-b2b3-3dc600983895",
+ "name": "connected",
+ "displayName": "Connected",
+ "displayNameEvent": "Connected changed",
+ "type": "bool",
+ "defaultValue": false,
+ "cached": false
+ },
+ {
+ "id": "33a3329a-4117-4488-aa18-91c76056ed6e",
+ "name": "airQuality",
+ "displayName": "Air quality",
+ "displayNameEvent": "Air quality changed",
+ "type": "QString",
+ "possibleValues": [
+ "Good",
+ "Moderate",
+ "Unhealthy for Sensitive Groups",
+ "Unhealthy",
+ "Very unhealthy",
+ "Hazardous"
+ ],
+ "defaultValue": "Good"
+ },
+ {
+ "id": "cfece671-4e88-4c49-9456-e3f8f7c79ab3",
+ "name": "cautionaryStatement",
+ "displayName": "Cautionary statement",
+ "displayNameEvent": "Cautionary statement changed",
+ "type": "QString",
+ "defaultValue": "-"
+ },
+ {
+ "id": "8385f3d5-62f7-482e-927c-b5d61a70d607",
+ "name": "stationName",
+ "displayName": "Station name",
+ "displayNameEvent": "Station name changed",
+ "type": "QString",
+ "defaultValue": "Undefined"
+ },
+ {
+ "id": "bc8c4c83-d229-4be4-8732-bc4f2390f399",
+ "name": "pm25",
+ "displayName": "Fine particles pollution level (PM2.5)",
+ "displayNameEvent": "Fine particles pollution level (PM2.5) changed",
+ "type": "int",
+ "defaultValue": 0
+ },
+ {
+ "id": "24b41ec4-e26b-4dfb-b52c-8e2b1bbdafc6",
+ "name": "pm10",
+ "displayName": "Coarse dust particles pollution level (PM10)",
+ "displayNameEvent": "Coarse dust particles pollution level (PM10) changed",
+ "type": "int",
+ "defaultValue": 0
+ },
+ {
+ "id": "4e88526d-009f-4820-9a84-09b3646d23c9",
+ "name": "o3",
+ "displayName": "Ozone level (O3)",
+ "displayNameEvent": "Ozone level (O3) changed",
+ "unit": "",
+ "type": "double",
+ "defaultValue": 0
+ },
+ {
+ "id": "6ed6c505-f36e-44c4-a982-f395b04e539b",
+ "name": "no2",
+ "displayName": "Nitrogen Dioxide level (NO2)",
+ "displayNameEvent": "Nitrogen Dioxide level (NO2) changed",
+ "unit": "",
+ "type": "double",
+ "defaultValue": 0
+ },
+ {
+ "id": "54ac72f3-6444-46a8-a43d-210c2a6fbfb5",
+ "name": "co",
+ "displayName": "Carbon monoxide level (CO)",
+ "displayNameEvent": "Carbon monoxide level (CO) changed",
+ "unit": "",
+ "type": "double",
+ "defaultValue": 0
+ },
+ {
+ "id": "f3a05e65-a9b3-48fd-be43-688d4c293cc9",
+ "name": "so2",
+ "displayName": "Sulfur dioxide level (SO2)",
+ "displayNameEvent": "Sulfur dioxide level (SO2) changed",
+ "unit": "",
+ "type": "double",
+ "defaultValue": 0
+ },
+ {
+ "id": "94219802-0a82-4761-99b3-c6b6dfc096db",
+ "name": "temperature",
+ "displayName": "Temperature",
+ "displayNameEvent": "Temperature changed",
+ "unit": "DegreeCelsius",
+ "type": "double",
+ "defaultValue": 0
+ },
+ {
+ "id": "4fc45fca-25ab-45a0-b862-817eea1f51e3",
+ "name": "humidity",
+ "displayName": "Humidity",
+ "displayNameEvent": "Humidity changed",
+ "unit": "Percentage",
+ "type": "double",
+ "maxValue": 100,
+ "minValue": 0,
+ "defaultValue": 0
+ },
+ {
+ "id": "5f799040-08f8-44d1-aa0a-4cab7caad839",
+ "name": "pressure",
+ "displayName": "Pressure",
+ "displayNameEvent": "Pressure changed",
+ "unit": "MilliBar",
+ "type": "double",
+ "defaultValue": 0
+ },
+ {
+ "id": "c4366608-2511-428b-964e-2ad9e37f8f3c",
+ "name": "windSpeed",
+ "displayName": "Wind speed",
+ "displayNameEvent": "Wind speed changed",
+ "unit": "MeterPerSecond",
+ "type": "double",
+ "defaultValue": 0
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/aqi/translations/57d69b76-4d2d-41ec-bef6-949a79ffbe6b-en_US.ts b/aqi/translations/57d69b76-4d2d-41ec-bef6-949a79ffbe6b-en_US.ts
new file mode 100644
index 00000000..a7e01783
--- /dev/null
+++ b/aqi/translations/57d69b76-4d2d-41ec-bef6-949a79ffbe6b-en_US.ts
@@ -0,0 +1,287 @@
+
+
+
+
+ AirQualityIndex
+
+
+ Air Quality Index
+ The name of the vendor ({6c8e2ded-0a33-4e77-b76c-ea02168741ec})
+
+
+
+
+
+ Air quality
+ The name of the ParamType (ThingClass: airQualityIndex, EventType: airQuality, ID: {33a3329a-4117-4488-aa18-91c76056ed6e})
+----------
+The name of the StateType ({33a3329a-4117-4488-aa18-91c76056ed6e}) of ThingClass airQualityIndex
+
+
+
+
+ Air quality changed
+ The name of the EventType ({33a3329a-4117-4488-aa18-91c76056ed6e}) of ThingClass airQualityIndex
+
+
+
+
+ Air quality index
+ The name of the ThingClass ({23ea32c9-38b0-4155-bacc-3afa8c09f6ee})
+
+
+
+
+ AirQualityIndex
+ The name of the plugin AirQualityIndex ({57d69b76-4d2d-41ec-bef6-949a79ffbe6b})
+
+
+
+
+
+ Carbon monoxide level (CO)
+ The name of the ParamType (ThingClass: airQualityIndex, EventType: co, ID: {54ac72f3-6444-46a8-a43d-210c2a6fbfb5})
+----------
+The name of the StateType ({54ac72f3-6444-46a8-a43d-210c2a6fbfb5}) of ThingClass airQualityIndex
+
+
+
+
+ Carbon monoxide level (CO) changed
+ The name of the EventType ({54ac72f3-6444-46a8-a43d-210c2a6fbfb5}) of ThingClass airQualityIndex
+
+
+
+
+
+ Cautionary statement
+ The name of the ParamType (ThingClass: airQualityIndex, EventType: cautionaryStatement, ID: {cfece671-4e88-4c49-9456-e3f8f7c79ab3})
+----------
+The name of the StateType ({cfece671-4e88-4c49-9456-e3f8f7c79ab3}) of ThingClass airQualityIndex
+
+
+
+
+ Cautionary statement changed
+ The name of the EventType ({cfece671-4e88-4c49-9456-e3f8f7c79ab3}) of ThingClass airQualityIndex
+
+
+
+
+
+ Coarse dust particles pollution level (PM10)
+ The name of the ParamType (ThingClass: airQualityIndex, EventType: pm10, ID: {24b41ec4-e26b-4dfb-b52c-8e2b1bbdafc6})
+----------
+The name of the StateType ({24b41ec4-e26b-4dfb-b52c-8e2b1bbdafc6}) of ThingClass airQualityIndex
+
+
+
+
+ Coarse dust particles pollution level (PM10) changed
+ The name of the EventType ({24b41ec4-e26b-4dfb-b52c-8e2b1bbdafc6}) of ThingClass airQualityIndex
+
+
+
+
+
+ Connected
+ The name of the ParamType (ThingClass: airQualityIndex, EventType: connected, ID: {7b9135cd-2461-4d33-b2b3-3dc600983895})
+----------
+The name of the StateType ({7b9135cd-2461-4d33-b2b3-3dc600983895}) of ThingClass airQualityIndex
+
+
+
+
+ Connected changed
+ The name of the EventType ({7b9135cd-2461-4d33-b2b3-3dc600983895}) of ThingClass airQualityIndex
+
+
+
+
+
+ Fine particles pollution level (PM2.5)
+ The name of the ParamType (ThingClass: airQualityIndex, EventType: pm25, ID: {bc8c4c83-d229-4be4-8732-bc4f2390f399})
+----------
+The name of the StateType ({bc8c4c83-d229-4be4-8732-bc4f2390f399}) of ThingClass airQualityIndex
+
+
+
+
+ Fine particles pollution level (PM2.5) changed
+ The name of the EventType ({bc8c4c83-d229-4be4-8732-bc4f2390f399}) of ThingClass airQualityIndex
+
+
+
+
+
+ Humidity
+ The name of the ParamType (ThingClass: airQualityIndex, EventType: humidity, ID: {4fc45fca-25ab-45a0-b862-817eea1f51e3})
+----------
+The name of the StateType ({4fc45fca-25ab-45a0-b862-817eea1f51e3}) of ThingClass airQualityIndex
+
+
+
+
+ Humidity changed
+ The name of the EventType ({4fc45fca-25ab-45a0-b862-817eea1f51e3}) of ThingClass airQualityIndex
+
+
+
+
+ Latitude
+ The name of the ParamType (ThingClass: airQualityIndex, Type: thing, ID: {afd5803b-6c98-44d7-9f4a-45e91cfb062e})
+
+
+
+
+ Longitude
+ The name of the ParamType (ThingClass: airQualityIndex, Type: thing, ID: {4800d78e-a367-41f7-9bf6-7c81d40ce19a})
+
+
+
+
+
+ Nitrogen Dioxide level (NO2)
+ The name of the ParamType (ThingClass: airQualityIndex, EventType: no2, ID: {6ed6c505-f36e-44c4-a982-f395b04e539b})
+----------
+The name of the StateType ({6ed6c505-f36e-44c4-a982-f395b04e539b}) of ThingClass airQualityIndex
+
+
+
+
+ Nitrogen Dioxide level (NO2) changed
+ The name of the EventType ({6ed6c505-f36e-44c4-a982-f395b04e539b}) of ThingClass airQualityIndex
+
+
+
+
+
+ Ozone level (O3)
+ The name of the ParamType (ThingClass: airQualityIndex, EventType: o3, ID: {4e88526d-009f-4820-9a84-09b3646d23c9})
+----------
+The name of the StateType ({4e88526d-009f-4820-9a84-09b3646d23c9}) of ThingClass airQualityIndex
+
+
+
+
+ Ozone level (O3) changed
+ The name of the EventType ({4e88526d-009f-4820-9a84-09b3646d23c9}) of ThingClass airQualityIndex
+
+
+
+
+
+ Pressure
+ The name of the ParamType (ThingClass: airQualityIndex, EventType: pressure, ID: {5f799040-08f8-44d1-aa0a-4cab7caad839})
+----------
+The name of the StateType ({5f799040-08f8-44d1-aa0a-4cab7caad839}) of ThingClass airQualityIndex
+
+
+
+
+ Pressure changed
+ The name of the EventType ({5f799040-08f8-44d1-aa0a-4cab7caad839}) of ThingClass airQualityIndex
+
+
+
+
+
+ Station name
+ The name of the ParamType (ThingClass: airQualityIndex, EventType: stationName, ID: {8385f3d5-62f7-482e-927c-b5d61a70d607})
+----------
+The name of the StateType ({8385f3d5-62f7-482e-927c-b5d61a70d607}) of ThingClass airQualityIndex
+
+
+
+
+ Station name changed
+ The name of the EventType ({8385f3d5-62f7-482e-927c-b5d61a70d607}) of ThingClass airQualityIndex
+
+
+
+
+
+ Sulfur dioxide level (SO2)
+ The name of the ParamType (ThingClass: airQualityIndex, EventType: so2, ID: {f3a05e65-a9b3-48fd-be43-688d4c293cc9})
+----------
+The name of the StateType ({f3a05e65-a9b3-48fd-be43-688d4c293cc9}) of ThingClass airQualityIndex
+
+
+
+
+ Sulfur dioxide level (SO2) changed
+ The name of the EventType ({f3a05e65-a9b3-48fd-be43-688d4c293cc9}) of ThingClass airQualityIndex
+
+
+
+
+
+ Temperature
+ The name of the ParamType (ThingClass: airQualityIndex, EventType: temperature, ID: {94219802-0a82-4761-99b3-c6b6dfc096db})
+----------
+The name of the StateType ({94219802-0a82-4761-99b3-c6b6dfc096db}) of ThingClass airQualityIndex
+
+
+
+
+ Temperature changed
+ The name of the EventType ({94219802-0a82-4761-99b3-c6b6dfc096db}) of ThingClass airQualityIndex
+
+
+
+
+
+ Wind speed
+ The name of the ParamType (ThingClass: airQualityIndex, EventType: windSpeed, ID: {c4366608-2511-428b-964e-2ad9e37f8f3c})
+----------
+The name of the StateType ({c4366608-2511-428b-964e-2ad9e37f8f3c}) of ThingClass airQualityIndex
+
+
+
+
+ Wind speed changed
+ The name of the EventType ({c4366608-2511-428b-964e-2ad9e37f8f3c}) of ThingClass airQualityIndex
+
+
+
+
+ IntegrationPluginAqi
+
+
+ Please enter your API token for Air Quality Index
+
+
+
+
+ This token is not valid.
+ Error setting up device with invalid token
+
+
+
+
+ None
+
+
+
+
+
+ Active children and adults, and people with respiratory disease, such as asthma, should limit prolonged outdoor exertion.
+
+
+
+
+ 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
+
+
+
+
+ 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.
+
+
+
+
+ Everyone should avoid all outdoor exertion
+
+
+
+
diff --git a/debian/control b/debian/control
index 6efef76e..20f9b769 100644
--- a/debian/control
+++ b/debian/control
@@ -39,6 +39,22 @@ Description: nymea.io plugin for ANEL Elektronik NET-PwrCtrl power sockets
network controlled power sockets.
+Package: nymea-plugin-aqi
+Architecture: any
+Section: libs
+Depends: ${shlibs:Depends},
+ ${misc:Depends},
+ nymea-plugins-translations,
+Description: nymea.io plugin to fetch the air quaility index from http://aqicn.org
+ The nymea daemon is a plugin based IoT (Internet of Things) server. The
+ server works like a translator for devices, things and services and
+ allows them to interact.
+ With the powerful rule engine you are able to connect any device available
+ in the system and create individual scenes and behaviors for your environment.
+ .
+ This package will install the nymea.io plugin for the air quality index
+
+
Package: nymea-plugin-avahimonitor
Architecture: any
Section: libs
@@ -960,6 +976,7 @@ Package: nymea-plugins
Section: libs
Architecture: all
Depends: nymea-plugin-anel,
+ nymea-plugin-aqi,
nymea-plugin-awattar,
nymea-plugin-bose,
nymea-plugin-datetime,
diff --git a/debian/nymea-plugin-aqi.install.in b/debian/nymea-plugin-aqi.install.in
new file mode 100644
index 00000000..e2bd73fd
--- /dev/null
+++ b/debian/nymea-plugin-aqi.install.in
@@ -0,0 +1 @@
+usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginaqi.so
diff --git a/nymea-plugins.pro b/nymea-plugins.pro
index 23d78cdf..6ea6aacf 100644
--- a/nymea-plugins.pro
+++ b/nymea-plugins.pro
@@ -2,6 +2,7 @@ TEMPLATE = subdirs
PLUGIN_DIRS = \
anel \
+ aqi \
avahimonitor \
awattar \
boblight \