aqi: Add Qt6 support

This commit is contained in:
Simon Stürz 2025-08-07 14:38:10 +02:00
parent 2575f43518
commit 3257413b74
5 changed files with 63 additions and 39 deletions

View File

@ -1,6 +1,6 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright 2013 - 2020, nymea GmbH * Copyright 2013 - 2025, nymea GmbH
* Contact: contact@nymea.io * Contact: contact@nymea.io
* *
* This file is part of nymea. * This file is part of nymea.
@ -54,8 +54,9 @@ QUuid AirQualityIndex::searchByName(const QString &name)
{ {
if (m_apiKey.isEmpty()) { if (m_apiKey.isEmpty()) {
qCWarning(dcAirQualityIndex()) << "API key is not set, not sending request"; qCWarning(dcAirQualityIndex()) << "API key is not set, not sending request";
return ""; return QUuid();
} }
QUuid requestId = QUuid::createUuid();; QUuid requestId = QUuid::createUuid();;
QUrl url; QUrl url;
url.setUrl(m_baseUrl); url.setUrl(m_baseUrl);
@ -75,13 +76,14 @@ QUuid AirQualityIndex::searchByName(const QString &name)
// Check HTTP status code // Check HTTP status code
if (status != 200 || reply->error() != QNetworkReply::NoError) { if (status != 200 || reply->error() != QNetworkReply::NoError) {
if (status == 400) { if (status == 400)
qCWarning(dcAirQualityIndex()) << "Request error due to exceeded request quota"; qCWarning(dcAirQualityIndex()) << "Request error due to exceeded request quota";
}
requestExecuted(requestId, false); emit requestExecuted(requestId, false);
qCWarning(dcAirQualityIndex()) << "Request error:" << status << reply->errorString(); qCWarning(dcAirQualityIndex()) << "Request error:" << status << reply->errorString();
return; return;
} }
QByteArray rawData = reply->readAll(); QByteArray rawData = reply->readAll();
qCDebug(dcAirQualityIndex()) << "Search response" << rawData; qCDebug(dcAirQualityIndex()) << "Search response" << rawData;
@ -95,7 +97,7 @@ QUuid AirQualityIndex::searchByName(const QString &name)
QList<Station> stations; QList<Station> stations;
QVariantList stationList = doc.toVariant().toMap().value("data").toList(); QVariantList stationList = doc.toVariant().toMap().value("data").toList();
foreach (QVariant stationVariant, stationList) { foreach (const QVariant &stationVariant, stationList) {
Station station; Station station;
station.aqi = stationVariant.toMap().value("aqi").toInt(); station.aqi = stationVariant.toMap().value("aqi").toInt();
station.idx = stationVariant.toMap().value("idx").toInt(); station.idx = stationVariant.toMap().value("idx").toInt();
@ -107,10 +109,11 @@ QUuid AirQualityIndex::searchByName(const QString &name)
station.location.longitude = stationVariant.toMap().value("city").toMap().value("geo").toList().last().toDouble(); station.location.longitude = stationVariant.toMap().value("city").toMap().value("geo").toList().last().toDouble();
stations.append(station); stations.append(station);
} }
if (!stations.isEmpty()) if (!stations.isEmpty())
emit stationsReceived(requestId, stations); emit stationsReceived(requestId, stations);
requestExecuted(requestId, true); emit requestExecuted(requestId, true);
}); });
return requestId; return requestId;
} }
@ -119,18 +122,23 @@ QUuid AirQualityIndex::getDataByIp()
{ {
if (m_apiKey.isEmpty()) { if (m_apiKey.isEmpty()) {
qCWarning(dcAirQualityIndex()) << "API key is not set, not sending request"; qCWarning(dcAirQualityIndex()) << "API key is not set, not sending request";
return ""; return QUuid();
} }
QUuid requestId = QUuid::createUuid(); QUuid requestId = QUuid::createUuid();
QUrl url; QUrl url;
url.setUrl(m_baseUrl); url.setUrl(m_baseUrl);
url.setPath("/feed/here/"); url.setPath("/feed/here/");
QUrlQuery query; QUrlQuery query;
query.addQueryItem("token", m_apiKey); query.addQueryItem("token", m_apiKey);
url.setQuery(query); url.setQuery(query);
QNetworkRequest request; QNetworkRequest request;
request.setUrl(url); request.setUrl(url);
request.setRawHeader("User-Agent", "nymea"); request.setRawHeader("User-Agent", "nymea");
qCDebug(dcAirQualityIndex()) << "Get data by IP request" << url.toString(); qCDebug(dcAirQualityIndex()) << "Get data by IP request" << url.toString();
QNetworkReply *reply = m_networkAccessManager->get(request); QNetworkReply *reply = m_networkAccessManager->get(request);
connect(reply, &QNetworkReply::finished, this, [requestId, reply, this] { connect(reply, &QNetworkReply::finished, this, [requestId, reply, this] {
@ -139,17 +147,20 @@ QUuid AirQualityIndex::getDataByIp()
// Check HTTP status code // Check HTTP status code
if (status != 200 || reply->error() != QNetworkReply::NoError) { if (status != 200 || reply->error() != QNetworkReply::NoError) {
if (status == 400) { if (status == 400)
qCWarning(dcAirQualityIndex()) << "Request error due to exceeded request quota"; qCWarning(dcAirQualityIndex()) << "Request error due to exceeded request quota";
}
requestExecuted(requestId, false); emit requestExecuted(requestId, false);
qCWarning(dcAirQualityIndex()) << "Request error:" << status << reply->errorString(); qCWarning(dcAirQualityIndex()) << "Request error:" << status << reply->errorString();
return; return;
} }
if (!parseData(requestId, reply->readAll())) if (!parseData(requestId, reply->readAll()))
requestExecuted(requestId, false); emit requestExecuted(requestId, false);
requestExecuted(requestId, true);
emit requestExecuted(requestId, true);
}); });
return requestId; return requestId;
} }
@ -157,19 +168,23 @@ QUuid AirQualityIndex::getDataByGeolocation(double lat, double lng)
{ {
if (m_apiKey.isEmpty()) { if (m_apiKey.isEmpty()) {
qCWarning(dcAirQualityIndex()) << "API key is not set, not sending request"; qCWarning(dcAirQualityIndex()) << "API key is not set, not sending request";
return ""; return QUuid();
} }
QUuid requestId = QUuid::createUuid(); QUuid requestId = QUuid::createUuid();
QUrl url; QUrl url;
url.setUrl(m_baseUrl); url.setUrl(m_baseUrl);
url.setPath(QString("/feed/geo:%1;%2/").arg(lat).arg(lng)); url.setPath(QString("/feed/geo:%1;%2/").arg(lat).arg(lng));
QUrlQuery query; QUrlQuery query;
query.addQueryItem("token", m_apiKey); query.addQueryItem("token", m_apiKey);
url.setQuery(query); url.setQuery(query);
QNetworkRequest request; QNetworkRequest request;
request.setUrl(url); request.setUrl(url);
request.setRawHeader("User-Agent", "nymea"); request.setRawHeader("User-Agent", "nymea");
qCDebug(dcAirQualityIndex()) << "Get data by geo location request" << url.toString(); qCDebug(dcAirQualityIndex()) << "Get data by geo location request" << url.toString();
QNetworkReply *reply = m_networkAccessManager->get(request); QNetworkReply *reply = m_networkAccessManager->get(request);
connect(reply, &QNetworkReply::finished, this, [requestId, reply, this] { connect(reply, &QNetworkReply::finished, this, [requestId, reply, this] {
@ -178,21 +193,22 @@ QUuid AirQualityIndex::getDataByGeolocation(double lat, double lng)
// Check HTTP status code // Check HTTP status code
if (status != 200 || reply->error() != QNetworkReply::NoError) { if (status != 200 || reply->error() != QNetworkReply::NoError) {
if (status == 400) { if (status == 400)
qCWarning(dcAirQualityIndex()) << "Request error due to exceeded request quota"; qCWarning(dcAirQualityIndex()) << "Request error due to exceeded request quota";
}
requestExecuted(requestId, false); emit requestExecuted(requestId, false);
qCWarning(dcAirQualityIndex()) << "Request error:" << status << reply->errorString(); qCWarning(dcAirQualityIndex()) << "Request error:" << status << reply->errorString();
return; return;
} }
if (!parseData(requestId, reply->readAll())) if (!parseData(requestId, reply->readAll()))
requestExecuted(requestId, false); emit requestExecuted(requestId, false);
requestExecuted(requestId, true);
emit requestExecuted(requestId, true);
}); });
return requestId; return requestId;
} }
bool AirQualityIndex::parseData(QUuid requestId, const QByteArray &data) bool AirQualityIndex::parseData(QUuid requestId, const QByteArray &data)
{ {
qCDebug(dcAirQualityIndex()) << "Parsing data" << data; qCDebug(dcAirQualityIndex()) << "Parsing data" << data;
@ -241,6 +257,7 @@ bool AirQualityIndex::parseData(QUuid requestId, const QByteArray &data)
aqiData.co = iaqi["co"].toMap().value("v").toDouble(); aqiData.co = iaqi["co"].toMap().value("v").toDouble();
aqiData.temperature = iaqi["t"].toMap().value("v").toDouble(); aqiData.temperature = iaqi["t"].toMap().value("v").toDouble();
aqiData.windSpeed = iaqi["w"].toMap().value("v").toDouble(); aqiData.windSpeed = iaqi["w"].toMap().value("v").toDouble();
emit dataReceived(requestId, aqiData); emit dataReceived(requestId, aqiData);
return true; return true;
} }

View File

@ -1,6 +1,6 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright 2013 - 2020, nymea GmbH * Copyright 2013 - 2025, nymea GmbH
* Contact: contact@nymea.io * Contact: contact@nymea.io
* *
* This file is part of nymea. * This file is part of nymea.
@ -31,12 +31,13 @@
#ifndef AIRQUALITYINDEX_H #ifndef AIRQUALITYINDEX_H
#define AIRQUALITYINDEX_H #define AIRQUALITYINDEX_H
#include "network/networkaccessmanager.h"
#include <QObject> #include <QObject>
#include <QUuid> #include <QUuid>
#include <QTime> #include <QTime>
#include <network/networkaccessmanager.h>
class AirQualityIndex : public QObject class AirQualityIndex : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -70,6 +71,7 @@ public:
}; };
explicit AirQualityIndex(NetworkAccessManager *networkAccessManager, const QString &apiKey, QObject *parent = nullptr); explicit AirQualityIndex(NetworkAccessManager *networkAccessManager, const QString &apiKey, QObject *parent = nullptr);
void setApiKey(const QString &apiKey); void setApiKey(const QString &apiKey);
QUuid searchByName(const QString &name); QUuid searchByName(const QString &name);
QUuid getDataByIp(); QUuid getDataByIp();

View File

@ -1,12 +1,12 @@
include(../plugins.pri) include(../plugins.pri)
QT+= network QT *= network
SOURCES += \ SOURCES += \
airqualityindex.cpp \ airqualityindex.cpp \
integrationpluginaqi.cpp \ integrationpluginaqi.cpp
HEADERS += \ HEADERS += \
airqualityindex.h \ airqualityindex.h \
integrationpluginaqi.h \ integrationpluginaqi.h

View File

@ -1,6 +1,6 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright 2013 - 2020, nymea GmbH * Copyright 2013 - 2025, nymea GmbH
* Contact: contact@nymea.io * Contact: contact@nymea.io
* *
* This file is part of nymea. * This file is part of nymea.
@ -30,7 +30,8 @@
#include "integrationpluginaqi.h" #include "integrationpluginaqi.h"
#include "plugininfo.h" #include "plugininfo.h"
#include "nymeasettings.h"
#include <nymeasettings.h>
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
@ -122,7 +123,8 @@ void IntegrationPluginAqi::discoverThings(ThingDiscoveryInfo *info)
if(!createAqiConnection()) { if(!createAqiConnection()) {
return info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("API key is not available.")); return info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("API key is not available."));
} }
connect(info, &ThingDiscoveryInfo::aborted, [this] {
connect(info, &ThingDiscoveryInfo::aborted, this, [this] {
if (myThings().filterByThingClassId(airQualityIndexThingClassId).isEmpty()) { if (myThings().filterByThingClassId(airQualityIndexThingClassId).isEmpty()) {
m_aqiConnection->deleteLater(); m_aqiConnection->deleteLater();
m_aqiConnection = nullptr; m_aqiConnection = nullptr;
@ -133,7 +135,9 @@ void IntegrationPluginAqi::discoverThings(ThingDiscoveryInfo *info)
} }
QUuid requestId = m_aqiConnection->getDataByIp(); QUuid requestId = m_aqiConnection->getDataByIp();
m_asyncDiscovery.insert(requestId, info); m_asyncDiscovery.insert(requestId, info);
connect(info, &ThingDiscoveryInfo::aborted, [=] {m_asyncDiscovery.remove(requestId);}); connect(info, &ThingDiscoveryInfo::aborted, this, [this, requestId] {
m_asyncDiscovery.remove(requestId);
});
} }
void IntegrationPluginAqi::setupThing(ThingSetupInfo *info) void IntegrationPluginAqi::setupThing(ThingSetupInfo *info)
@ -148,7 +152,7 @@ void IntegrationPluginAqi::setupThing(ThingSetupInfo *info)
QUuid requestId = m_aqiConnection->getDataByGeolocation(latitude, longitude); QUuid requestId = m_aqiConnection->getDataByGeolocation(latitude, longitude);
m_asyncSetups.insert(requestId, info); m_asyncSetups.insert(requestId, info);
connect(info, &ThingSetupInfo::aborted, [requestId, this] { connect(info, &ThingSetupInfo::aborted, this, [requestId, this] {
m_asyncSetups.remove(requestId); m_asyncSetups.remove(requestId);
if (myThings().filterByThingClassId(airQualityIndexThingClassId).isEmpty()) { if (myThings().filterByThingClassId(airQualityIndexThingClassId).isEmpty()) {
m_aqiConnection->deleteLater(); m_aqiConnection->deleteLater();
@ -219,6 +223,7 @@ double IntegrationPluginAqi::convertFromAQI(int aqi, const QList<QPair<int, doub
il = map.at(index - 1).first; il = map.at(index - 1).first;
vl = map.at(index - 1).second; vl = map.at(index - 1).second;
} }
ih = map.at(index).first; ih = map.at(index).first;
vh = map.at(index).second; vh = map.at(index).second;
double value = (aqi - il) * (vh - vl) / (ih - il) + vl; double value = (aqi - il) * (vh - vl) / (ih - il) + vl;
@ -253,7 +258,6 @@ void IntegrationPluginAqi::onAirQualityDataReceived(QUuid requestId, AirQualityI
if (!thing) if (!thing)
return; return;
thing->setStateValue(airQualityIndexConnectedStateTypeId, true); thing->setStateValue(airQualityIndexConnectedStateTypeId, true);
thing->setStateValue(airQualityIndexHumidityStateTypeId, data.humidity); thing->setStateValue(airQualityIndexHumidityStateTypeId, data.humidity);
thing->setStateValue(airQualityIndexTemperatureStateTypeId, data.temperature); thing->setStateValue(airQualityIndexTemperatureStateTypeId, data.temperature);
@ -284,7 +288,6 @@ void IntegrationPluginAqi::onAirQualityStationsReceived(QUuid requestId, QList<A
info->finish(Thing::ThingErrorNoError); info->finish(Thing::ThingErrorNoError);
} }
if (m_asyncRequests.contains(requestId)) { if (m_asyncRequests.contains(requestId)) {
Thing * thing = myThings().findById(m_asyncRequests.value(requestId)); Thing * thing = myThings().findById(m_asyncRequests.value(requestId));
if (!thing) { if (!thing) {

View File

@ -1,6 +1,6 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright 2013 - 2020, nymea GmbH * Copyright 2013 - 2025, nymea GmbH
* Contact: contact@nymea.io * Contact: contact@nymea.io
* *
* This file is part of nymea. * This file is part of nymea.
@ -31,11 +31,11 @@
#ifndef INTEGRATIONPLUGINAQI_H #ifndef INTEGRATIONPLUGINAQI_H
#define INTEGRATIONPLUGINAQI_H #define INTEGRATIONPLUGINAQI_H
#include "plugintimer.h" #include <plugintimer.h>
#include "integrations/integrationplugin.h" #include <integrations/integrationplugin.h>
#include "network/networkaccessmanager.h" #include <network/networkaccessmanager.h>
#include "airqualityindex.h"
#include "airqualityindex.h"
#include "extern-plugininfo.h" #include "extern-plugininfo.h"
#include <QTimer> #include <QTimer>
@ -64,6 +64,7 @@ private:
QHash<QUuid, ThingDiscoveryInfo *> m_asyncDiscovery; QHash<QUuid, ThingDiscoveryInfo *> m_asyncDiscovery;
QHash<QUuid, ThingSetupInfo *> m_asyncSetups; QHash<QUuid, ThingSetupInfo *> m_asyncSetups;
QHash<QUuid, ThingId> m_asyncRequests; QHash<QUuid, ThingId> m_asyncRequests;
QString getApiKey(); QString getApiKey();
bool createAqiConnection(); bool createAqiConnection();
@ -74,6 +75,7 @@ private slots:
void onRequestExecuted(QUuid requestId, bool success); void onRequestExecuted(QUuid requestId, bool success);
void onAirQualityDataReceived(QUuid requestId, AirQualityIndex::AirQualityData data); void onAirQualityDataReceived(QUuid requestId, AirQualityIndex::AirQualityData data);
void onAirQualityStationsReceived(QUuid requestId, QList<AirQualityIndex::Station> stations); void onAirQualityStationsReceived(QUuid requestId, QList<AirQualityIndex::Station> stations);
}; };
#endif // INTEGRATIONPLUGINAQI_H #endif // INTEGRATIONPLUGINAQI_H