/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 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, not sending request"; return ""; } 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, not sending request"; return ""; } 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(double lat, double lng) { if (m_apiKey.isEmpty()) { qCWarning(dcAirQualityIndex()) << "API key is not set, not sending request"; return ""; } QUuid requestId = QUuid::createUuid(); QUrl url; url.setUrl(m_baseUrl); url.setPath(QString("/feed/geo:%1;%2/").arg(lat).arg(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; } if (doc.toVariant().toMap().contains("status")) { if (doc.toVariant().toMap().value("status") == "error") { qCWarning(dcAirQualityIndex()) << "Server responded with error:" << doc.toVariant().toMap().value("data").toString(); 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; }