diff --git a/main.cpp b/main.cpp index 5e607db..a183523 100644 --- a/main.cpp +++ b/main.cpp @@ -16,7 +16,7 @@ #include "pws2mqtt.h" #include "mqtt.h" //#include "version.h" - +#include #include #include #include @@ -25,7 +25,7 @@ #define BROKER_ADDRESS "localhost" #define MQTT_PORT 1883; -uint debugLevel = ALL; +uint debugLevel = INFO | NOTICE | INFO | WARNING | ERROR | ALERT; QByteArray toFollow; //class MqttClient; @@ -43,6 +43,7 @@ int main(int argc, char *argv[]) debug(DEBUGMACRO, "QcoreApplication", DEBUG); QCoreApplication a(argc, argv); + notify ("Météo" "Program starting"); // Enable logging to journald qputenv("QT_FORCE_STDERR_LOGGING", QByteArray("0")); @@ -61,6 +62,7 @@ int main(int argc, char *argv[]) mqttClient->qmqttClient->unsubscribe(mqttClient->topic); mqttClient->qmqttClient->disconnectFromHost(); debug(DEBUGMACRO, "exiting", DEBUG); + notify ("Météo" "Program exiting"); //Closing http server } @@ -96,10 +98,12 @@ void debug(QString debugHeader, QString msg, uint8_t level, QByteArray property) if ((debugLevel & level) == 4) { qInfo("%s%s WARNING: %s%s", debugHeader.toStdString().c_str(), ORANGE, msg.toStdString().c_str(), NORMAL); + notify (debugHeader + " - " + msg); } if ((debugLevel & level) == 8) { qInfo("%s%s ERROR: %s%s", debugHeader.toStdString().c_str(), RED, msg.toStdString().c_str(), NORMAL); + notify ("Proram exiting " + debugHeader + " - " + msg ); exit(1); } if ((debugLevel & level) == 16) diff --git a/mqtt.cpp b/mqtt.cpp index f1bedc9..3dbcb7a 100644 --- a/mqtt.cpp +++ b/mqtt.cpp @@ -82,7 +82,7 @@ void MqttClient::on_messageSentStatusChanged(qint32 id, QMqtt::MessageStatus s, if ( id == 0) { - debug(DEBUGMACRO, "message id = " + QString::number(id) + this->messageStatus[static_cast(s)], DEBUG); + debug(DEBUGMACRO, "message id = " + QString::number(id) + this->messageStatus[static_cast(s)], WARNING); } } @@ -116,10 +116,10 @@ void MqttClient::updateStatus(QMqttSubscription::SubscriptionState state) switch (state) { case QMqttSubscription::Unsubscribed: - debug(DEBUGMACRO, "Unsubscribed", INFO); + debug(DEBUGMACRO, "Unsubscribed", WARNING); break; case QMqttSubscription::SubscriptionPending: - debug(DEBUGMACRO, "Pending subscription", DEBUG); + debug(DEBUGMACRO, "Pending subscription", WARNING); break; case QMqttSubscription::Subscribed: debug(DEBUGMACRO, "Subscribed", INFO); @@ -128,7 +128,7 @@ void MqttClient::updateStatus(QMqttSubscription::SubscriptionState state) debug(DEBUGMACRO, "Subscription error" + this->subscription->reason(), WARNING); break; case QMqttSubscription::UnsubscriptionPending: - debug(DEBUGMACRO, "Pending Unsubscription", DEBUG); + debug(DEBUGMACRO, "Pending Unsubscription", WARNING); break; default: debug(DEBUGMACRO, "--Unknown--", DEBUG); @@ -138,5 +138,5 @@ void MqttClient::updateStatus(QMqttSubscription::SubscriptionState state) void MqttClient::on_stateChanged(QMqttClient::ClientState state) { - debug(DEBUGMACRO, QLatin1String("State Change : ") + this->clientStateString[state], DEBUG ); + debug(DEBUGMACRO, QLatin1String("State Change : ") + this->clientStateString[state], WARNING ); } diff --git a/pws2mqtt-qt.pro b/pws2mqtt-qt.pro index e05df73..ca3ac40 100644 --- a/pws2mqtt-qt.pro +++ b/pws2mqtt-qt.pro @@ -4,6 +4,8 @@ QT += core QT += mqtt QT += httpserver +LIBS += -lcurl + CONFIG += c++17 console CONFIG -= app_bundle @@ -24,6 +26,6 @@ else: unix:!android: target.path = /opt/$${TARGET}/bin HEADERS += \ httpserver.h \ mqtt.h \ - pws2mqtt.h \ +pws2mqtt.h \ version.h diff --git a/pws2mqtt.cpp b/pws2mqtt.cpp index 9f5e6c1..385a2d8 100644 --- a/pws2mqtt.cpp +++ b/pws2mqtt.cpp @@ -9,19 +9,36 @@ #include #include #include +#include +#include extern MqttClient *mqttClient; extern Pws2mqtt *pws2mqtt; extern QHttpServer *httpServer; +QStringList previsionList {"Temps variable (incertain).", \ + "Beau temps stable (anticyclone).", \ + "Amélioration progressive (éclaircies).", \ + "Risque de pluie légère ou nuages.", \ + "Dégradation marquée (pluie/vent/orage).", \ + "Tempête ou dépression forte (vigilance)." \ + }; -QMap propertyList; +// Callback for curl library +static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) +{ + (void)contents; // Évite les warnings de compilation + (void)userp; + return size * nmemb; // On ne traite pas les données reçues +} + +QMap propertyList; QMap > propertyName { {"tempf", {"Température extérieure", "°C"}}, {"humidity", {"Humidité extérieure", "%"}}, {"dewptf", {"Point de rosée", "°C"}}, {"windchillf", {"Température ressentie", "°C"}}, - {"windir", {"Direction du vent", "°"}}, + {"winddir", {"Direction du vent", "°"}}, {"windspeedmph", {"vitesse du vent", "km/h"}}, {"windgustmph", {"Rafales", "km/h"}}, {"rainin", {"Pluie", "mm/h"}}, @@ -32,7 +49,7 @@ QMap > propertyName {"indoortempf", {"Température intérieure", "°C"}}, {"indoorhumidity", {"Humidité intérieure", "%"}}, {"baromin", {"pression atmosphérique", "hPa"}}, - {"lowbatt", {"Batterie faible", ""}}, + {"lowbatt", {"Alerte batterie faible", ""}}, {"UV", {"UV", ""}} }; @@ -105,9 +122,13 @@ void Pws2mqtt::parseData(QList> queryList) QString jsonString = "{"; bool propertyFlag = false; bool deviceFlag = false; - QString topic; + QString spaceFlag = ""; +// QString topic; QString deviceString = "\"device\": {\"ieeeAddress\": \"" + mqttClient->macAddress + "\", \"type\": \"" + mqttClient->type + "\", \"powerSource\": \"Battery\""; - QString notif; + QString notif = ""; + double propertyValue; + QStringList priorityList {"", "min", "low", "default", "High", "urgent"}; + quint8 priority = 2; debug(DEBUGMACRO, "looping list of query", DEBUG); @@ -140,56 +161,289 @@ void Pws2mqtt::parseData(QList> queryList) //mqttClient->end_message(topic, jsonString); if (pair.first == "tempf") { - QByteArray tmp = pair.second.toLatin1(); - if (compare (propertyList[pair.first.toLatin1()], tmp, 0.2)) + propertyValue = fahrenheitToCelsius(pair.second.toFloat()); + if (compare (propertyList[pair.first.toLatin1()], propertyValue, 0.5)) { - notif += formatNotifString (pair, QByteArray::number(fahrenheitToCelsius(pair.second.toFloat()))); + notif += spaceFlag + formatNotifString (pair, QByteArray::number(qPow(propertyValue, 1.0))); + debug (DEBUGMACRO, "Notif = #" + notif + "#", DEBUG); + propertyList[pair.first.toLatin1()] = propertyValue; } }else if (pair.first == "humidity") - //TODO - propertyList[pair.first.toLatin1()] = pair.second.toLatin1(); + { + propertyValue = pair.second.toFloat(); + if (compare (propertyList[pair.first.toLatin1()], propertyValue, 3)) + { + notif += spaceFlag + formatNotifString (pair, pair.second.toLatin1()); + debug (DEBUGMACRO, "Notif = #" + notif + "#", DEBUG); + } + }else if (pair.first == "windchillf") + { + propertyValue = fahrenheitToCelsius(pair.second.toFloat()); + if (compare (propertyList[pair.first.toLatin1()], propertyValue, 0.2)) + { + notif += spaceFlag + formatNotifString (pair, QByteArray::number(qPow(propertyValue, 1.0))); + debug (DEBUGMACRO, "Notif = #" + notif + "#", DEBUG); + } + }else if (pair.first == "winddir") + { + propertyValue = pair.second.toFloat(); + if (compare (propertyList[pair.first.toLatin1()], propertyValue, 90)) + { + notif += spaceFlag + formatNotifString (pair, pair.second.toLatin1()); + debug (DEBUGMACRO, "Notif = #" + notif + "#", DEBUG); + } + }else if (pair.first == "windspeedmph") + { + propertyValue = mphTokmh(pair.second.toFloat()); + if (compare (propertyList[pair.first.toLatin1()], propertyValue, 10)) + { + notif += spaceFlag + formatNotifString (pair, QByteArray::number(round(propertyValue))); + if (propertyValue > 30) + priority = setPriority (priority, 4); + if (propertyValue > 50) + priority = setPriority (priority, 5); + debug (DEBUGMACRO, "Notif = #" + notif + "#", DEBUG); + } + }else if (pair.first == "windgustmph") + { + propertyValue = mphTokmh(pair.second.toFloat()); + if (compare (propertyList[pair.first.toLatin1()], propertyValue, 5) and propertyValue > 20) + { + notif += spaceFlag + formatNotifString (pair, QByteArray::number(round(propertyValue))); + if (propertyValue > 40) + priority = setPriority (priority, 4); + if (propertyValue > 60) + priority = setPriority (priority, 5); + debug (DEBUGMACRO, "Notif = #" + notif + "#", DEBUG); + } + }else if (pair.first == "rainin") + { + propertyValue = pair.second.toFloat(); + if (compare (propertyList[pair.first.toLatin1()], propertyValue, 0.01)) + { + notif += spaceFlag + formatNotifString (pair, pair.second.toLatin1()); + priority = setPriority (priority, 4); + debug (DEBUGMACRO, "Notif = #" + notif + "#", DEBUG); + } + }else if (pair.first == "baromin") + { + propertyValue = tohPa(pair.second.toFloat()); + pressureVariation(propertyValue); + if (compare (propertyList[pair.first.toLatin1()], propertyValue, 100)) + { + notif += spaceFlag + formatNotifString (pair, QByteArray::number(propertyValue)); + debug (DEBUGMACRO, "Notif = #" + notif + "#", DEBUG); + } + }else if (pair.first == "UV") + { + propertyValue = pair.second.toFloat(); + if (compare (propertyList[pair.first.toLatin1()], propertyValue, 1)) + { + notif += spaceFlag + formatNotifString (pair, pair.second.toLatin1()); + if (propertyValue > 3) + priority = setPriority (priority, 5); + debug (DEBUGMACRO, "Notif = #" + notif + "#", DEBUG); + } + }else if (pair.first == "lowbat") + { + static QTime time; + if (pair.second.toInt() == 1 and time > QTime::currentTime().addSecs(3600)) + { + notif += spaceFlag + formatNotifString (pair, ""); + debug (DEBUGMACRO, "Notif = #" + notif + "#", DEBUG); + priority = setPriority (priority, 4); + time = time.currentTime(); + } + } + } + propertyList[pair.first.toLatin1()] = propertyValue; + if (!notif.isEmpty()) + { + spaceFlag = " "; } } if (!jsonString.isEmpty()) { jsonString = jsonString +", " + deviceString + "}}"; mqttClient->send_message(jsonString); - debug(DEBUGMACRO, "sent => " + jsonString, DEBUG); + //debug(DEBUGMACRO, "sent => " + jsonString, DEBUG); }else { debug(DEBUGMACRO, "No values to send", DEBUG); } + + if (!notif.isEmpty()) + { + debug(DEBUGMACRO, "calling notify with notif = #" + notif + "#", DEBUG); + notify (notif, priorityList[priority]); + } debug(DEBUGMACRO, "parseData: Returning", DEBUG); - //return jsonString; } -double fahrenheitToCelsius(double fahrenheit) +quint8 previsionMeteo(double currentPressure, double variation3h) +{ + if (currentPressure > 1020.0 && variation3h >= 0.0) + { + return 0; + } else if (currentPressure > 1010.0 && currentPressure <= 1020.0 && variation3h > 0.0) + { + return 1; + } else if (currentPressure > 1000.0 && currentPressure <= 1010.0 && variation3h < 0.0) + { + return 2; + } else if (currentPressure <= 1000.0 && variation3h < -2.0) + { + return 3; + } else if (currentPressure < 990.0) + { + return 4; + } else + { + return 5; + } +} + +void pressureVariation(qfloat16 currentPressure) +{ + const int NB_MESURES_3H = 360; // 3h × 120 mesures/heure (1 mesure toutes les 30s) + QVector historiquePressions; + + debug(DEBUGMACRO, "PressureVariation", DEBUG); + + historiquePressions.reserve(NB_MESURES_3H); + + // Ajout de la mesure à l'historique + historiquePressions.push_back(currentPressure); + + // Si on a assez de mesures pour couvrir 3h + if (historiquePressions.size() > NB_MESURES_3H) + { + historiquePressions.erase(historiquePressions.begin()); + } + + // Calcul de la variation sur 3h si on a assez de mesures + if (historiquePressions.size() == NB_MESURES_3H) + { + double pressionInitiale = historiquePressions.front(); + double pressionFinale = historiquePressions.back(); + double variation = pressionFinale - pressionInitiale; + + debug(DEBUGMACRO, "Pression actuelle : " + QString::number(pressionFinale) + " hPa", DEBUG); + debug(DEBUGMACRO, "Variation sur 3h : " + QString::number(variation) + " hPa", DEBUG); + + // Prévision météo + quint8 prevision = previsionMeteo(pressionFinale, variation); + debug(DEBUGMACRO, "Prévision : " + previsionList[prevision], DEBUG); + + // Exemple : Envoi d'une alerte si nécessaire + if (prevision == 4) + { + notify ("ALERTE : " + previsionList[prevision], "high"); + }else if (prevision == 5) + { + notify ("ALERTE : " + previsionList[prevision], "urgent"); + } + } +} + +quint8 setPriority (quint8 currentPriority, quint8 newPriority) +{ + if (newPriority > currentPriority) + { + return newPriority; + }else + { + return currentPriority; + } +} + +double fahrenheitToCelsius (double fahrenheit) { return (fahrenheit - 32.0) * 5.0 / 9.0; } -qfloat16 tohPa(qfloat16 value) +qfloat16 tohPa (qfloat16 value) { return value * 33.8639; } -bool compare (QByteArray valueBA, QByteArray testValueBA, qfloat16 ecart) +qfloat16 mphTokmh (qfloat16 value) { - qfloat16 value = valueBA.toFloat(); - qfloat16 testValue = testValueBA.toFloat(); + return value * 1.60934; +} - if (value <= (testValue - ecart) or value >= (testValue + ecart)) +bool compare (qfloat16 value, qfloat16 currentValue, qfloat16 ecart) +{ + + debug(DEBUGMACRO, "value: " + QByteArray::number(value) + "testValue: " + QByteArray::number(currentValue), DEBUG); + + if (currentValue <= (value - ecart) or currentValue >= (value + ecart)) { + debug(DEBUGMACRO, "compare return true", DEBUG); return true; }else { + debug(DEBUGMACRO, "compare return false", DEBUG); return false; } } QString formatNotifString (QPair pair, QByteArray value) { - return propertyName[pair.first.toLatin1()].first + " : " + value + propertyName[pair.first.toLatin1()].second; - + return "- " + propertyName[pair.first.toLatin1()].first + " : " + value + propertyName[pair.first.toLatin1()].second; +} + +void notify(QString notif, QString priority) +{ + CURL *curl; + CURLcode res; + + priority = "Priority: " + priority; + + // Initialise libcurl + curl_global_init(CURL_GLOBAL_DEFAULT); + curl = curl_easy_init(); + + if (curl) + { + // URL de ton serveur ntfy + QString url = "http://localhost:81/Meteo"; // Remplace par ton URL et topic + + // Message à envoyer + QString message = notif; + + // Définis les en-têtes pour le titre et les priorités + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Title: Météo"); + headers = curl_slist_append(headers, priority.toStdString().c_str()); + headers = curl_slist_append(headers, "Markdown: yes"); + headers = curl_slist_append(headers, "Config: /etc/ntfy.client.yml"); + headers = curl_slist_append(headers, "Firebase: no"); + + // Configure la requête POST + curl_easy_setopt(curl, CURLOPT_URL, url.toUtf8().constData()); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, message.toUtf8().constData()); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); + + // Exécute la requête + res = curl_easy_perform(curl); + + // Vérifie les erreurs + if (res != CURLE_OK) + { + qCritical() << "Erreur libcurl :" << curl_easy_strerror(res); + } else + { + qDebug() << "Notification envoyée avec succès !"; + } + + // Nettoie les ressources + curl_slist_free_all(headers); + curl_easy_cleanup(curl); + } + + // Nettoie libcurl + curl_global_cleanup(); } diff --git a/pws2mqtt.h b/pws2mqtt.h index de26147..83eec45 100644 --- a/pws2mqtt.h +++ b/pws2mqtt.h @@ -50,7 +50,12 @@ class Pws2mqtt : public QObject }; double fahrenheitToCelsius(double fahrenheit); qfloat16 tohPa(qfloat16 value); -bool compare (QByteArray value, QByteArray testValue, qfloat16 ecart = 0.5); +bool compare (qfloat16 value, qfloat16 testValue, qfloat16 ecart = 0.5); QString formatNotifString (QPair pair, QByteArray value); +qfloat16 mphTokmh (qfloat16 value); +void notify (QString notif, QString priority = "low"); +quint8 setPriority (quint8 currentPriority, quint8 newPriority); +quint8 previsionMeteo(double currentPressure, double variation3h); +void pressureVariation(qfloat16 currentPressure); #endif // PWS2MQTT_H