- correction of bugs\n- added notifications with ntfy

This commit is contained in:
2025-09-26 21:03:07 +02:00
parent 2816e0a70b
commit af3bf2ce08
5 changed files with 294 additions and 29 deletions

View File

@@ -16,7 +16,7 @@
#include "pws2mqtt.h"
#include "mqtt.h"
//#include "version.h"
#include <curl/curl.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/socket.h>
@@ -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)

View File

@@ -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<int>(s)], DEBUG);
debug(DEBUGMACRO, "message id = " + QString::number(id) + this->messageStatus[static_cast<int>(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 );
}

View File

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

View File

@@ -9,19 +9,36 @@
#include <netinet/in.h>
#include <QtHttpServer/QHttpServer>
#include <QtHttpServer/QHttpServerRequest>
#include <curl/curl.h>
#include <QtMath>
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 <QByteArray, QByteArray> 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 <QByteArray, qfloat16> propertyList;
QMap <QByteArray, QPair<QString, QByteArray>> 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 <QByteArray, QPair<QString, QByteArray>> 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<std::pair<QString, QString>> 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<std::pair<QString, QString>> 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<double> 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 <QString, QString> 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();
}

View File

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