Files
pws2mqtt-qt/pws2mqtt.cpp

450 lines
13 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "pws2mqtt.h"
#include "mqtt.h"
//#include "httpserver.h"
#include <unistd.h>
#include <QtGlobal>
#include <iostream>
#include <QList>
#include <sys/socket.h>
#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)." \
};
// 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"}},
{"winddir", {"Direction du vent", "°"}},
{"windspeedmph", {"vitesse du vent", "km/h"}},
{"windgustmph", {"Rafales", "km/h"}},
{"rainin", {"Pluie", "mm/h"}},
{"dailyrainin", {"Pluie de la journée", "mm"}},
{"monthlyrainin", {"Pluie du mois", "mm"}},
{"yearlyrainin", {"Pluie de l'année", "mm"}},
{"solarradiation", {"Énergie solaire", "W/m²"}},
{"indoortempf", {"Température intérieure", "°C"}},
{"indoorhumidity", {"Humidité intérieure", "%"}},
{"baromin", {"pression atmosphérique", "hPa"}},
{"lowbatt", {"Alerte batterie faible", ""}},
{"UV", {"UV", ""}}
};
Pws2mqtt::Pws2mqtt()
{
this->init();
}
Pws2mqtt::~Pws2mqtt()
{
}
void Pws2mqtt::init()
{
debug(DEBUGMACRO, "init http server", DEBUG);
httpServer->setMissingHandler([](const QHttpServerRequest &request, QHttpServerResponder &&responder)
{
(void) responder;
debug(DEBUGMACRO, "body " + request.url().toString(), WARNING);
return QHttpServerResponse("404 - Page non trouvée. Route par défaut.", QHttpServerResponse::StatusCode::NotFound);
//responder.write(QHttpServerResponse("404 - Page non trouvée. Route par défaut.", QHttpServerResponse::StatusCode::NotFound));
});
httpServer->route("/", [](const QHttpServerRequest &request)
{
QList<std::pair<QByteArray, QByteArray>> headersList = request.headers().toList();
debug(DEBUGMACRO, "Remote address" + request.remoteAddress().toString(), WARNING);
return QHttpServerResponse("text/plain", "Failed\n");
});
httpServer->route("/query", [this](const QHttpServerRequest &request)
{
QByteArray data;
QList<std::pair<QString, QString>> queryList = request.query().queryItems();
//QTextStream result(&data);
if (queryList.isEmpty())
{
debug(DEBUGMACRO, "Request query is empty", WARNING);
}else
{
debug(DEBUGMACRO, "Request query :" + request.query().toString() , DEBUG);
this->parseData(queryList);
debug(DEBUGMACRO, "Returning 'success'", DEBUG);
}
//mqttClient.send_message(jsonString);
return QHttpServerResponse("text/plain", "Success\n");
});
}
void Pws2mqtt::listeningHttp()
{
//QByteArray data;
debug (DEBUGMACRO, "listening http requests", DEBUG);
const auto port = httpServer->listen(QHostAddress::Any, 5000);
if (!port)
{
debug(DEBUGMACRO, "Http Server failed to listen on a port.", ERROR);
//return 0;
}
debug(DEBUGMACRO, "Listening on port " + QString::number(port));
}
void Pws2mqtt::parseData(QList<std::pair<QString, QString>> queryList)
{
debug(DEBUGMACRO, "Parsing Datas", DEBUG);
QString jsonString = "{";
bool propertyFlag = false;
bool deviceFlag = false;
QString spaceFlag = "";
// QString topic;
QString deviceString = "\"device\": {\"ieeeAddress\": \"" + mqttClient->macAddress + "\", \"type\": \"" + mqttClient->type + "\", \"powerSource\": \"Battery\"";
QString notif = "";
double propertyValue;
QStringList priorityList {"", "min", "low", "default", "High", "urgent"};
quint8 priority = 2;
debug(DEBUGMACRO, "looping list of query", DEBUG);
for (QPair<QString, QString> pair : queryList)
{
debug(DEBUGMACRO, pair.first + " = " + pair.second, DEBUG);
if (this->deviceProperties.contains(pair.first))
{
if(deviceFlag == false)
{
deviceFlag = true;
}else
{
deviceString.append(", ");
}
deviceString += "\"" + pair.first + "\": ";
deviceString += pair.second;
}else
{
if(propertyFlag == false)
{
propertyFlag = true;
}else
{
jsonString.append(", ");
}
jsonString.append("\"" + pair.first + "\": ");
jsonString.append(pair.second);
//mqttClient->end_message(topic, jsonString);
if (pair.first == "tempf")
{
propertyValue = fahrenheitToCelsius(pair.second.toFloat());
if (compare (propertyList[pair.first.toLatin1()], propertyValue, 0.5))
{
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")
{
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);
}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);
}
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)
{
return value * 33.8639;
}
qfloat16 mphTokmh (qfloat16 value)
{
return value * 1.60934;
}
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;
}
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();
}