added AQI plug-in

This commit is contained in:
Boernsman 2019-12-23 15:08:40 +01:00 committed by bernhard.trinnes
parent ac6dcf0765
commit c9117867a5
8 changed files with 446 additions and 0 deletions

15
aqi/README.md Normal file
View File

@ -0,0 +1,15 @@
# Air Quality Index
This plug-in gets the air quality information from http://aqicn.org.
Through your IP address the next nearby sensor station will be discovered.
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.
More about the different Air Quality Levels: https://www.airnow.gov/index.cfm?action=aqibasics.aqi

12
aqi/aqi.pro Normal file
View File

@ -0,0 +1,12 @@
include(../plugins.pri)
TARGET = $$qtLibraryTarget(nymea_devicepluginaqi)
QT+= network
SOURCES += \
devicepluginaqi.cpp \
HEADERS += \
devicepluginaqi.h \

186
aqi/devicepluginaqi.cpp Normal file
View File

@ -0,0 +1,186 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2020 Bernhard Trinnes <bernhard.trinnes@nymea.io> *
* *
* This file is part of nymea. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or (at your option) any later version. *
* *
* This library 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 library; If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "devicepluginaqi.h"
#include "plugininfo.h"
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QUrlQuery>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
DevicePluginAqi::DevicePluginAqi()
{
}
void DevicePluginAqi::setupDevice(DeviceSetupInfo *info)
{
if (info->device()->deviceClassId() == airQualityIndexDeviceClassId) {
if (myDevices().filterByDeviceClassId(info->device()->deviceClassId()).count() > 1) {
info->finish(Device::DeviceErrorSetupFailed, tr("Service is already in use"));
return;
}
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(Device::DeviceErrorSetupFailed, reply->errorString());
}
return info->finish(Device::DeviceErrorNoError);
});
}
}
void DevicePluginAqi::postSetupDevice(Device *device)
{
Q_UNUSED(device);
getDataByIp();
if(!m_pluginTimer) {
m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(60);
connect(m_pluginTimer, &PluginTimer::timeout, this, &DevicePluginAqi::onPluginTimer);
}
}
void DevicePluginAqi::deviceRemoved(Device *device)
{
Q_UNUSED(device);
if (myDevices().empty()) {
hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
m_pluginTimer = nullptr;
}
}
void DevicePluginAqi::getDataByIp()
{
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");
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 (Device *device, myDevices().filterByDeviceClassId(airQualityIndexDeviceClassId)) {
device->setStateValue(airQualityIndexConnectedStateTypeId, true);
}
qCWarning(dcAirQualityIndex()) << "Request error:" << status << reply->errorString();
return;
}
QJsonParseError error;
QJsonDocument data = QJsonDocument::fromJson(reply->readAll(), &error);
if (error.error != QJsonParseError::NoError) {
qDebug(dcAirQualityIndex()) << "Received invalide JSON object";
return;
}
qCDebug(dcAirQualityIndex()) << data.toJson();
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();
foreach (Device *device, myDevices().filterByDeviceClassId(airQualityIndexDeviceClassId)) {
device->setStateValue(airQualityIndexStationNameStateTypeId, name);
device->setStateValue(airQualityIndexConnectedStateTypeId, true);
device->setStateValue(airQualityIndexCoStateTypeId, co);
device->setStateValue(airQualityIndexHumidityStateTypeId, humidity);
device->setStateValue(airQualityIndexTemperatureStateTypeId, temperature);
device->setStateValue(airQualityIndexPressureStateTypeId, pressure);
device->setStateValue(airQualityIndexO3StateTypeId, o3);
device->setStateValue(airQualityIndexNo2StateTypeId, no2);
device->setStateValue(airQualityIndexSo2StateTypeId, so2);
device->setStateValue(airQualityIndexPm10StateTypeId, pm10);
device->setStateValue(airQualityIndexPm25StateTypeId, pm25);
if (pm25 <= 50.00) {
device->setStateValue(airQualityIndexAirQualityStateTypeId, "Good");
device->setStateValue(airQualityIndexCautionaryStatementStateTypeId, "None");
} else if ((pm25 > 50.00) && (pm25 <= 100.00)) {
device->setStateValue(airQualityIndexAirQualityStateTypeId, "Moderate");
device->setStateValue(airQualityIndexCautionaryStatementStateTypeId, "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)) {
device->setStateValue(airQualityIndexAirQualityStateTypeId, "Unhealthy for Sensitive Groups");
device->setStateValue(airQualityIndexCautionaryStatementStateTypeId, "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)) {
device->setStateValue(airQualityIndexAirQualityStateTypeId, "Unhealthy");
device->setStateValue(airQualityIndexCautionaryStatementStateTypeId, "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)) {
device->setStateValue(airQualityIndexAirQualityStateTypeId, "Very Unhealthy");
device->setStateValue(airQualityIndexCautionaryStatementStateTypeId, "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 {
device->setStateValue(airQualityIndexAirQualityStateTypeId, "Hazardous");
device->setStateValue(airQualityIndexCautionaryStatementStateTypeId, "Everyone should avoid all outdoor exertion");
}
}
});
}
void DevicePluginAqi::onPluginTimer()
{
getDataByIp();
}

59
aqi/devicepluginaqi.h Normal file
View File

@ -0,0 +1,59 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2020 Bernhard Trinnes <bernhard.trinnes@nymea.io> *
* *
* This file is part of nymea. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or (at your option) any later version. *
* *
* This library 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 library; If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef DEVICEPLUGINAQI_H
#define DEVICEPLUGINAQI_H
#include "plugintimer.h"
#include "devices/deviceplugin.h"
#include "network/networkaccessmanager.h"
#include <QTimer>
#include <QUrl>
#include <QHostAddress>
class DevicePluginAqi : public DevicePlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "devicepluginaqi.json")
Q_INTERFACES(DevicePlugin)
public:
explicit DevicePluginAqi();
void setupDevice(DeviceSetupInfo *info) override;
void deviceRemoved(Device *device) override;
void postSetupDevice(Device *device) override;
private:
PluginTimer *m_pluginTimer = nullptr;
QString m_baseUrl = "https://api.waqi.info";
QString m_apiKey;
void getDataByIp();
private slots:
void onPluginTimer();
};
#endif // DEVICEPLUGINAQI_H

156
aqi/devicepluginaqi.json Normal file
View File

@ -0,0 +1,156 @@
{
"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",
"displayName": "Air Quality Index",
"id": "6c8e2ded-0a33-4e77-b76c-ea02168741ec",
"deviceClasses": [
{
"id": "23ea32c9-38b0-4155-bacc-3afa8c09f6ee",
"name": "airQualityIndex",
"displayName": "Air quality index",
"interfaces": ["humiditysensor", "pressuresensor", "temperaturesensor", "connectable"],
"createMethods": ["user"],
"paramTypes": [
],
"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": "HectoPascal",
"type": "double",
"defaultValue": 0
}
]
}
]
}
]
}

16
debian/control vendored
View File

@ -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

1
debian/nymea-plugin-aqi.install.in vendored Normal file
View File

@ -0,0 +1 @@
usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_devicepluginaqi.so

View File

@ -2,6 +2,7 @@ TEMPLATE = subdirs
PLUGIN_DIRS = \
anel \
aqi \
avahimonitor \
awattar \
boblight \