diff --git a/pws2mqtt-qt.pro b/pws2mqtt-qt.pro index 5f83235..fb7e584 100644 --- a/pws2mqtt-qt.pro +++ b/pws2mqtt-qt.pro @@ -14,10 +14,10 @@ CONFIG -= app_bundle #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ - src/main.cpp \ - src/mqtt.cpp \ - src/pws2mqtt.cpp \ - src/utcicalculator.cpp +src/main.cpp \ +# src/mqtt.cpp \ +src/pws2mqtt.cpp \ +src/utcicalculator.cpp # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin @@ -25,10 +25,10 @@ else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target HEADERS += \ - src/barometertrend.h \ - src/httpserver.h \ - src/mqtt.h \ - src/pws2mqtt.h \ - src/utcicalculator.h \ - src/version.h +src/barometertrend.h \ +#src/httpserver.h \ +# src/mqtt.h \ +src/pws2mqtt.h \ +src/utcicalculator.h \ + src/version.h diff --git a/src/barometertrend.h b/src/barometertrend.h index bf61599..6e6467f 100644 --- a/src/barometertrend.h +++ b/src/barometertrend.h @@ -8,426 +8,445 @@ struct ForecastResult { - QString message; - int urgency = 0; // 0-5 - QString icons = ""; // "vert", "jaune", ... - bool snowRisk = false; - bool rainRisk = false; - bool stormRisk = false; - bool blackIce = false; - QString prevision1h =""; - quint8 confiance = 50; + QString message; + int urgency = 0; // 0-5 + QString icons = ""; // "vert", "jaune", ... + bool snowRisk = false; + bool rainRisk = false; + bool stormRisk = false; + bool blackIce = false; + QString prevision1h =""; + quint8 confiance = 50; }; class BarometerTrend : public QObject { - Q_OBJECT + Q_OBJECT public: - explicit BarometerTrend(QObject *parent = nullptr) - : QObject(parent) - { - m_values.reserve(360 + 16); // évite reallocs fréquentes - m_timestamps.reserve(360 + 16); - restoreHistory(); - // Sauvegarde automatique toutes les 10 minutes - m_saveTimer = new QTimer(this); - connect(m_saveTimer, &QTimer::timeout, this, &BarometerTrend::saveHistory); - m_saveTimer->start(10 * 60 * 1000); - if (!m_saveTimer->isActive()) - { - debug(DEBUGMACRO, "Le timer n'est pas démarré", ERROR); - } - } + explicit BarometerTrend(QObject *parent = nullptr) + : QObject(parent) + { + m_values.reserve(360 + 16); // évite reallocs fréquentes + m_timestamps.reserve(360 + 16); + restoreHistory(); + // Sauvegarde automatique toutes les 10 minutes + m_saveTimer = new QTimer(this); + connect(m_saveTimer, &QTimer::timeout, this, &BarometerTrend::saveHistory); + m_saveTimer->start(10 * 60 * 1000); + if (!m_saveTimer->isActive()) + { + debug(DEBUGMACRO, "Le timer n'est pas démarré", ERROR); + } + } - ~BarometerTrend() - { - saveHistory(); - } - void addPressure(double pressure_hPa) - { - debug(DEBUGMACRO, "adding pressure " + QByteArray::number(pressure_hPa), DEBUG); - // Enregistrement de la nouvelle mesure - m_values.append(pressure_hPa); - m_timestamps.append(QDateTime::currentDateTime()); + ~BarometerTrend() + { + saveHistory(); + } + void addPressure(double pressure_hPa) + { + debug(DEBUGMACRO, "adding pressure " + QByteArray::number(pressure_hPa), DEBUG); + // Enregistrement de la nouvelle mesure + m_values.append(pressure_hPa); + m_timestamps.append(QDateTime::currentDateTime()); - // Limiter la taille de l’historique à 3 heures (360 mesures à 30s d’intervalle) - const int maxSamples = 360; - while (m_values.size() > maxSamples) - { - m_values.removeFirst(); - m_timestamps.removeFirst(); - } - } + // Limiter la taille de l’historique à 3 heures (360 mesures à 30s d’intervalle) + const int maxSamples = 360; + while (m_values.size() > maxSamples) + { + m_values.removeFirst(); + m_timestamps.removeFirst(); + } + } - double currentPressure() const - { - return m_values.isEmpty() ? 0.0 : m_values.last(); - } + double currentPressure() const + { + return m_values.isEmpty() ? 0.0 : m_values.last(); + } - // Moyenne glissante sur les dernières N mesures (par ex. 5 minutes) - double smoothedPressure(int samples = 10) const - { - if (m_values.isEmpty()) - { - return 0.0; - } - int n = qMin(samples, m_values.size()); - double sum = 0.0; - for (int i = m_values.size() - n; i < m_values.size(); ++i) - sum += m_values[i]; - debug(DEBUGMACRO, "smoothed pressure : " + QByteArray::number(sum/n), DEBUG); - return sum / n; + // Moyenne glissante sur les dernières N mesures (par ex. 5 minutes) + double smoothedPressure(int samples = 10) const + { + if (m_values.isEmpty()) + { + return 0.0; + } + int n = qMin(samples, m_values.size()); + double sum = 0.0; + for (int i = m_values.size() - n; i < m_values.size(); ++i) + sum += m_values[i]; + debug(DEBUGMACRO, "smoothed pressure : " + QByteArray::number(sum/n), DEBUG); + return sum / n; - } + } - // Retourne la pression moyenne autour d'un instant cible (fenêtre +/- windowSecs) - double pressureAround(const QDateTime &target, int windowSecs = 60) const - { - if (m_values.isEmpty()) return 0.0; - const QDateTime from = target.addSecs(-windowSecs); - const QDateTime to = target.addSecs(+windowSecs); - double sum = 0.0; - int count = 0; - for (int i = 0; i < m_timestamps.size(); ++i) { - if (m_timestamps[i] >= from && m_timestamps[i] <= to) { - sum += m_values[i]; - ++count; - } - } - if (count == 0) { - // pas d'échantillon proche -> fallback sur la valeur la plus proche dans le passé - for (int i = m_timestamps.size() - 1; i >= 0; --i) { - if (m_timestamps[i] <= target) { - return m_values[i]; - } - } - return m_values.first(); - } - return sum / count; - } - - // Variation de pression sur N heures (ex: 1 ou 3) - double deltaPressureHours(double hours) const - { - if (m_values.size() < 2) return 0.0; - QDateTime now = QDateTime::currentDateTime(); - QDateTime past = now.addSecs(-qRound(hours * 3600.0)); - double p_now = smoothedPressure(); // moyenne récente (~5 min) - double p_past = pressureAround(past, 60); // moyenne autour de l'instant passé - double delta = p_now - p_past; - // debug(DEBUGMACRO, "delta " + QByteArray::number(hours) + "h : " + QByteArray::number(delta), DEBUG); - return delta; - } - - // helpers spécifiques - double deltaPressure3h() const { return deltaPressureHours(3.0); } - double deltaPressure1h() const { return deltaPressureHours(1.0); } - - ForecastResult forecast(double temperatureC, double humidityPercent) const - { - ForecastResult r; - - r.confiance = 50; - double p = currentPressure(); - double d = deltaPressure3h(); - double d3 = deltaPressure3h(); - double d1 = deltaPressure1h(); + // Retourne la pression moyenne autour d'un instant cible (fenêtre +/- windowSecs) + double pressureAround(const QDateTime &target, int windowSecs = 60) const + { + if (m_values.isEmpty()) return 0.0; + const QDateTime from = target.addSecs(-windowSecs); + const QDateTime to = target.addSecs(+windowSecs); + double sum = 0.0; + int count = 0; + for (int i = 0; i < m_timestamps.size(); ++i) { + if (m_timestamps[i] >= from && m_timestamps[i] <= to) { + sum += m_values[i]; + ++count; + } + } + if (count == 0) { + // pas d'échantillon proche -> fallback sur la valeur la plus proche dans le passé + for (int i = m_timestamps.size() - 1; i >= 0; --i) { + if (m_timestamps[i] <= target) { + return m_values[i]; + } + } + return m_values.first(); + } + return sum / count; + } - debug(DEBUGMACRO, "current pressure : " + QByteArray::number(p) + " - delta pressure 3h" + QByteArray::number(d), DEBUG); + // Variation de pression sur N heures (ex: 1 ou 3) + double deltaPressureHours(double hours) const + { + if (m_values.size() < 2) return 0.0; + QDateTime now = QDateTime::currentDateTime(); + QDateTime past = now.addSecs(-qRound(hours * 3600.0)); + double p_now = smoothedPressure(); // moyenne récente (~5 min) + double p_past = pressureAround(past, 60); // moyenne autour de l'instant passé + double delta = p_now - p_past; + // debug(DEBUGMACRO, "delta " + QByteArray::number(hours) + "h : " + QByteArray::number(delta), DEBUG); + return delta; + } - // build icon list to produce colorCode - QStringList icons; + // helpers spécifiques + double deltaPressure3h() const { return deltaPressureHours(3.0); } + double deltaPressure1h() const { return deltaPressureHours(1.0); } - // Logique combinée pression / tendance - if (p < 990 && d3 <= -3.0) - { - r.message = "- Tempête ou orage"; - r.urgency = 5; - icons << "cloud_with_lightning"; - debug(DEBUGMACRO, "message : " + r.message, DEBUG); - }else if (p >= 1020 && d3 >= +3.0) - { - r.message = "- Très beau temps durable"; - r.urgency = 0; - icons << "sunny"; - debug(DEBUGMACRO, "message : " + r.message, DEBUG); - }else if (p >= 1020 && d3 >= +1.0) - { - r.message = "- Beau temps durable"; - r.urgency = 0; - icons << "sunny"; - debug(DEBUGMACRO, "message : " + r.message, DEBUG); - }else if (p >= 1020 && d3 < -3.0) - { - r.message = "- Beau, mais rapide dégradation"; - r.urgency = 4; - icons << "sun_behind_large_cloud"; - debug(DEBUGMACRO, "message : " + r.message, DEBUG); - }else if (p >= 1020 && d3 < -1.0) - { - r.message = "- Beau, mais possible dégradation"; - r.urgency = 1; - icons << "sun_behind_small_cloud"; - debug(DEBUGMACRO, "message : " + r.message, DEBUG); - /*}else if (p >= 1020 && d3 < 0.5.0) - { - r.message = "- Beau temps"; - r.urgency = 0; - icons << "sunny"; - debug(DEBUGMACRO, "message : " + r.message, DEBUG);*/ - }else if (p >= 1010 && d3 >= +3.0) - { - r.message = "- Nuageux, rapide amélioration"; - r.urgency = 4; - icons << "sun_behind_small_cloud"; - debug(DEBUGMACRO, "message : " + r.message, DEBUG); - }else if (p >= 1010 && d3 >= +1.0) - { - r.message = "- Nuageux, en amélioration"; - r.urgency = 0; - icons << "sun_behind_large_cloud"; - debug(DEBUGMACRO, "message : " + r.message, DEBUG); - }else if (p >= 1010 && d3 <= -3.0) - { - r.message = "- Nuageux, fort risque de pluie ou vent"; - r.urgency = 4; - icons << "sun_behind_rain_cloud"; - debug(DEBUGMACRO, "message : " + r.message, DEBUG); - }else if (p >= 1010 && d3 <= -1.0) - { - r.message = "- nuageux, risque de pluie ou vent"; - r.urgency = 2; - icons << "sun_behind_rain_cloud"; - debug(DEBUGMACRO, "message : " + r.message, DEBUG); - }else if (p >= 1010 && d3 <= 0.5) - { - r.message = "- Nuageux avec des éclaircies"; - r.urgency = 2; - icons << "sun_behind_large_cloud"; - debug(DEBUGMACRO, "message : " + r.message, DEBUG); - }else if (p >= 1000 && d3 >= +3.0) - { - r.message = "- Variable, amélioration rapide"; - r.urgency = 4; - icons << "sun_behind_large_cloud"; - debug(DEBUGMACRO, "message : " + r.message, DEBUG); - }else if (p >= 1000 && d3 >= +1.0) - { - r.message = "- Variable, tendance au beau"; - r.urgency = 0; - icons << "sun_behind_large_cloud"; - debug(DEBUGMACRO, "message : " + r.message, DEBUG); - }else if (p >= 1000 && d3 <= -3.0) - { - r.message = "- Pluie ou perturbation, dégradation rapide"; - r.urgency = 4; - icons << "cloud_with_rain"; - debug(DEBUGMACRO, "message : " + r.message, DEBUG); - }else if (p >= 1000 && d3 <= -1.0) - { - r.message = "- Pluie ou perturbation"; - r.urgency = 2; - icons << "cloud_with_rain"; - debug(DEBUGMACRO, "message : " + r.message, DEBUG); - }else if (p < 1000 && d3 >= +3.0) - { - r.message = "- Pluie avec accalmie temporaire, amélioration rapide"; - r.urgency = 1; - icons << "sun_behind_rain_cloud"; - debug(DEBUGMACRO, "message : " + r.message, DEBUG); - }else if (p < 1000 && d3 >= +1.0) - { - r.message = "- Pluie avec accalmie temporaire"; - r.urgency = 1; - icons << "sun_behind_rain_cloud"; - debug(DEBUGMACRO, "message : " + r.message, DEBUG); - }else if (p < 1000 && d3 <= -1.0) - { - r.message = "- Mauvais temps durable"; - r.urgency = 4; - icons << "cloud_with_rain"; - debug(DEBUGMACRO, "message : " + r.message, DEBUG); - } - if (qAbs(d3) < 1) - { - r.message += "- Stable, pas de changement significatif"; - r.urgency = 0; - debug(DEBUGMACRO, "message : " + r.message, DEBUG); - } + ForecastResult forecast(double temperatureC, double humidityPercent) const + { + ForecastResult r; - // Risque de neige - if (p < 1000 && d3 < -1.0 && temperatureC <= 1.0 && humidityPercent > 80.0) - { - r.snowRisk = true; - r.message += "- Risque de neige"; - r.urgency = qMax(r.urgency, 3); - icons << "snowflake"; - debug(DEBUGMACRO, "message : " + r.message, DEBUG); - } + r.confiance = 50; + double p = currentPressure(); + double d = deltaPressure3h(); + double d3 = deltaPressure3h(); + double d1 = deltaPressure1h(); - // Risque de pluie - else if (p < 1010 && d3 < -1.0 && temperatureC > 1.0 && humidityPercent > 70.0) - { - r.rainRisk = true; - r.message += "- Risque de pluie"; - r.urgency = qMax(r.urgency, 2); - icons << "cloud_with_rain"; - debug(DEBUGMACRO, "message : " + r.message, DEBUG); - } + debug(DEBUGMACRO, "current pressure : " + QByteArray::number(p) + " - delta pressure 3h" + QByteArray::number(d), DEBUG); - // Risque d’orage - if (p < 995 && d3 < -3.0 && temperatureC > 20.0 && humidityPercent > 70.0) - { - r.stormRisk = true; - r.message += "- Risque d’orage"; - r.urgency = qMax(r.urgency, 4); - icons << "cloud_with_lightning"; - debug(DEBUGMACRO, "message : " + r.message, DEBUG); - } + // build icon list to produce colorCode + QStringList icons; - if (temperatureC <= 0.5 && humidityPercent >= 85.0) { - // conditions classiques de gel - if (d3 >= -1.0 && d3 <= +1.0) - { - r.blackIce = true; // temps stable = ciel clair = refroidissement - }else if (d < -1 && p > 995) - { - r.blackIce = true; // arrivée d'air humide froid - } - } + // Logique combinée pression / tendance + if (p < 990 && d3 <= -3.0) + { + r.message = "- Tempête ou orage"; + r.urgency = 5; + icons << "cloud_with_lightning"; + debug(DEBUGMACRO, "message : " + r.message, DEBUG); + }else if (p >= 1020 && d3 >= +3.0) + { + r.message = "- Très beau temps durable"; + r.urgency = 0; + icons << "sunny"; + debug(DEBUGMACRO, "message : " + r.message, DEBUG); + }else if (p >= 1020 && d3 >= +1.0) + { + r.message = "- Beau temps durable"; + r.urgency = 0; + icons << "sunny"; + debug(DEBUGMACRO, "message : " + r.message, DEBUG); + }else if (p >= 1020 && (d3 <= +1.0 && d3 >= -1.0)) + { + r.message = "- Beau temps stable"; + r.urgency = 0; + icons << "sunny"; + debug(DEBUGMACRO, "message : " + r.message, DEBUG); + }else if (p >= 1015 && d3 < -3.0) + { + r.message = "- Beau, mais rapide dégradation"; + r.urgency = 4; + icons << "sun_behind_large_cloud"; + debug(DEBUGMACRO, "message : " + r.message, DEBUG); + }else if (p >= 1015 && d3 < -1.0) + { + r.message = "- Beau, mais possible dégradation"; + r.urgency = 1; + icons << "sun_behind_small_cloud"; + debug(DEBUGMACRO, "message : " + r.message, DEBUG); + }else if (p >= 1015 && (d3 <= 1.0 && d3 >= -1.0)) + { + r.message = "- Beau temps stable"; + r.urgency = 0; + icons << "sunny"; + debug(DEBUGMACRO, "message : " + r.message, DEBUG); + }else if (p >= 1005 && d3 >= +3.0) + { + r.message = "- Nuageux, rapide amélioration"; + r.urgency = 4; + icons << "sun_behind_small_cloud"; + debug(DEBUGMACRO, "message : " + r.message, DEBUG); + }else if (p >= 1005 && d3 >= +1.0) + { + r.message = "- Nuageux, en amélioration"; + r.urgency = 0; + icons << "sun_behind_large_cloud"; + debug(DEBUGMACRO, "message : " + r.message, DEBUG); + }else if (p >= 1005 && d3 <= -3.0) + { + r.message = "- Nuageux, fort risque de pluie ou vent"; + r.urgency = 4; + icons << "sun_behind_rain_cloud"; + debug(DEBUGMACRO, "message : " + r.message, DEBUG); + }else if (p >= 1005 && d3 <= -1.0) + { + r.message = "- nuageux, risque de pluie ou vent"; + r.urgency = 2; + icons << "sun_behind_rain_cloud"; + debug(DEBUGMACRO, "message : " + r.message, DEBUG); + }else if (p >= 1005 && (d3 <= 1.0 && d3 >= 1.0)) + { + r.message = "- Nuageux avec des éclaircies"; + r.urgency = 2; + icons << "sun_behind_large_cloud"; + debug(DEBUGMACRO, "message : " + r.message, DEBUG); + }else if (p >= 1000 && d3 >= +3.0) + { + r.message = "- Variable, amélioration rapide"; + r.urgency = 4; + icons << "sun_behind_large_cloud"; + debug(DEBUGMACRO, "message : " + r.message, DEBUG); + }else if (p >= 1000 && d3 >= +1.0) + { + r.message = "- Variable, tendance au beau"; + r.urgency = 0; + icons << "sun_behind_large_cloud"; + debug(DEBUGMACRO, "message : " + r.message, DEBUG); + }else if (p >= 1000 && d3 <= -3.0) + { + r.message = "- Pluie ou perturbation, dégradation rapide"; + r.urgency = 4; + icons << "cloud_with_rain"; + debug(DEBUGMACRO, "message : " + r.message, DEBUG); + }else if (p >= 1000 && d3 <= -1.0) + { + r.message = "- Pluie ou perturbation"; + r.urgency = 2; + icons << "cloud_with_rain"; + debug(DEBUGMACRO, "message : " + r.message, DEBUG); + }else if (p >= 1000 && (d3 <= 1.0 && d3 >= -1.0)) + { + r.message = "- Pluie ou perturbation stable"; + r.urgency = 2; + icons << "cloud_with_rain"; + debug(DEBUGMACRO, "message : " + r.message, DEBUG); + }else if (p < 1000 && d3 >= +3.0) + { + r.message = "- Pluie avec accalmie temporaire, amélioration rapide"; + r.urgency = 1; + icons << "sun_behind_rain_cloud"; + debug(DEBUGMACRO, "message : " + r.message, DEBUG); + }else if (p < 1000 && d3 >= +1.0) + { + r.message = "- Pluie avec accalmie temporaire"; + r.urgency = 1; + icons << "sun_behind_rain_cloud"; + debug(DEBUGMACRO, "message : " + r.message, DEBUG); + }else if (p < 1000 && d3 <= -1.0) + { + r.message = "- Mauvais temps durable"; + r.urgency = 4; + icons << "cloud_with_rain"; + debug(DEBUGMACRO, "message : " + r.message, DEBUG); + }else if (p < 1000 && (d3 <= 1.0 && d3 >= -1.0)) + { + r.message = "- Mauvais temps stable"; + r.urgency = 4; + icons << "cloud_with_rain"; + debug(DEBUGMACRO, "message : " + r.message, DEBUG); + } - if (r.blackIce) - { - r.message += "- Risque de verglas"; - icons << "ice_cube"; - r.urgency = qMax(r.urgency, 4); - debug(DEBUGMACRO, "message : " + r.message, DEBUG); - } + /*if (qAbs(d3) < 1) + { + r.message += "- Stable, pas de changement significatif"; + r.urgency = 0; + debug(DEBUGMACRO, "message : " + r.message, DEBUG); + }*/ - // --- Prévision à 1 heure --- - if (d <= -1.5 && p < 1010 && humidityPercent > 80.0) - { - r.prevision1h = "- Pluie probable dans l'heure"; - r.urgency = qMax(r.urgency, 4); - r.confiance = 80; - }else if (d >= +1.5 && p > 1015) - { - r.prevision1h = "- mélioration probable dans l'heure"; - r.confiance = 85; - }else if (qAbs(d1) < 0.3 && qAbs(temperatureC) < 0.5) - { - r.prevision1h = "- Conditions stables pour la prochaine heure"; - r.confiance = 70; - }else if (temperatureC < 0 && humidityPercent > 85) - { - r.prevision1h = "- Possible formation de givre ou verglas dans l'heure"; - r.urgency = qMax(r.urgency, 4); - r.confiance = 75; - }else - { - r.prevision1h = "- pas de changements dans l'heure"; - r.urgency = qMax(r.urgency, 2); - r.confiance = 85; - } + // Risque de neige + if (p < 1000 && d3 < -1.0 && temperatureC <= 1.0 && humidityPercent > 80.0) + { + r.snowRisk = true; + r.message += "- Risque de neige"; + r.urgency = qMax(r.urgency, 3); + icons << "snowflake"; + debug(DEBUGMACRO, "message : " + r.message, DEBUG); + } + + // Risque de pluie + else if (p < 1010 && d3 < -1.0 && temperatureC > 1.0 && humidityPercent > 70.0) + { + r.rainRisk = true; + r.message += "- Risque de pluie"; + r.urgency = qMax(r.urgency, 3); + icons << "cloud_with_rain"; + debug(DEBUGMACRO, "message : " + r.message, DEBUG); + } + + // Risque d’orage + if (p < 995 && d3 < -3.0 && temperatureC > 20.0 && humidityPercent > 70.0) + { + r.stormRisk = true; + r.message += "- Risque d’orage"; + r.urgency = qMax(r.urgency, 4); + icons << "cloud_with_lightning"; + debug(DEBUGMACRO, "message : " + r.message, DEBUG); + } + + if (temperatureC <= 0.5 && humidityPercent >= 85.0) { + // conditions classiques de gel + if (d3 >= -1.0 && d3 <= +1.0) + { + r.blackIce = true; // temps stable = ciel clair = refroidissement + }else if (d < -1 && p > 995) + { + r.blackIce = true; // arrivée d'air humide froid + } + } + + if (r.blackIce) + { + r.message += "- Risque de verglas"; + icons << "ice_cube"; + r.urgency = qMax(r.urgency, 4); + debug(DEBUGMACRO, "message : " + r.message, DEBUG); + } + + // --- Prévision à 1 heure --- + if (d <= -1.5 && p < 1010 && humidityPercent > 80.0) + { + r.prevision1h = "- Pluie probable dans l'heure"; + r.urgency = qMax(r.urgency, 3); + r.confiance = 80; + }else if (d >= +1.5 && p > 1015) + { + r.prevision1h = "- mélioration probable dans l'heure"; + r.confiance = 85; + }else if (qAbs(d1) < 0.3 && qAbs(temperatureC) < 0.5) + { + r.prevision1h = "- Conditions stables pour la prochaine heure"; + r.confiance = 70; + }else if (temperatureC < 0 && humidityPercent > 85) + { + r.prevision1h = "- Possible formation de givre ou verglas dans l'heure"; + r.urgency = qMax(r.urgency, 4); + r.confiance = 75; + }else + { + r.prevision1h = "- pas de changements dans l'heure"; + r.urgency = qMax(r.urgency, 2); + r.confiance = 85; + } - // --- Ajustement de confiance selon stabilité barométrique --- - if (qAbs(d) > 3.0) - { - r.confiance -= 15; // variations trop fortes → prévision moins fiable - } - if (p < 990 || p > 1030) - { - r.confiance -= 10; // situations extrêmes, moins prédictibles - } - if (r.blackIce) - { - r.confiance += 5; // phénomène local très probable - } - r.confiance = qBound((quint8)0, r.confiance, (quint8)100); // borne entre 0 et 100 + // --- Ajustement de confiance selon stabilité barométrique --- + if (qAbs(d) > 3.0) + { + r.confiance -= 15; // variations trop fortes → prévision moins fiable + } + if (p < 990 || p > 1030) + { + r.confiance -= 10; // situations extrêmes, moins prédictibles + } + if (r.blackIce) + { + r.confiance += 5; // phénomène local très probable + } + r.confiance = qBound((quint8)0, r.confiance, (quint8)100); // borne entre 0 et 100 - // --- Résultat complet --- - if (!r.prevision1h.isEmpty()) - { - r.message += "\n**Prévision dans l'heure**"; - r.message += "\n" + r.prevision1h; - r.message += "\n- Probabilité " + QByteArray::number(r.confiance) + "%"; - } - debug(DEBUGMACRO, "message : " + r.message, DEBUG); + // --- Résultat complet --- + if (!r.prevision1h.isEmpty()) + { + r.message += "\n**Prévision dans l'heure**"; + r.message += "\n" + r.prevision1h; + r.message += "\n- Probabilité " + QByteArray::number(r.confiance) + "%"; + } + debug(DEBUGMACRO, "message : " + r.message, DEBUG); - if (!icons.isEmpty()) r.icons = icons.join(","); + if (!icons.isEmpty()) r.icons = icons.join(","); - return r; - } + return r; + } private: - QVector m_values; - QVector m_timestamps; - QTimer *m_saveTimer; + QVector m_values; + QVector m_timestamps; + QTimer *m_saveTimer; private slots: - void saveHistory() - { - QJsonArray array; - for (int i = 0; i < m_values.size(); ++i) - { - QJsonObject obj; - obj["timestamp"] = m_timestamps[i].toString(Qt::ISODate); - obj["pressure"] = m_values[i]; - array.append(obj); - } + void saveHistory() + { + QJsonArray array; + for (int i = 0; i < m_values.size(); ++i) + { + QJsonObject obj; + obj["timestamp"] = m_timestamps[i].toString(Qt::ISODate); + obj["pressure"] = m_values[i]; + array.append(obj); + } - QJsonDocument doc(array); - QFile file("/usr/local/share/pws2mqtt/barometer_history.json"); - if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) - { - file.write(doc.toJson()); - file.close(); - debug(DEBUGMACRO, "Barometer history saved (" + QByteArray::number(m_values.size()) + " samples)", DEBUG); - }else - { - debug(DEBUGMACRO, "File not open : " + file.errorString() , WARNING); - } - } + QJsonDocument doc(array); + QFile file("/usr/local/share/pws2mqtt/barometer_history.json"); + if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) + { + file.write(doc.toJson()); + file.close(); + debug(DEBUGMACRO, "Barometer history saved (" + QByteArray::number(m_values.size()) + " samples)", DEBUG); + }else + { + debug(DEBUGMACRO, "File not open : " + file.errorString() , WARNING); + } + } - void restoreHistory() - { - QFile file("/usr/local/share/pws2mqtt/barometer_history.json"); - if (!file.exists()) - { - debug(DEBUGMACRO, "File not open : " + file.errorString() , WARNING); - return; - } - if (file.open(QIODevice::ReadOnly)) - { - QByteArray data = file.readAll(); - file.close(); + void restoreHistory() + { + QFile file("/usr/local/share/pws2mqtt/barometer_history.json"); + if (!file.exists()) + { + debug(DEBUGMACRO, "File not open : " + file.errorString() , WARNING); + return; + } + if (file.open(QIODevice::ReadOnly)) + { + QByteArray data = file.readAll(); + file.close(); - QJsonDocument doc = QJsonDocument::fromJson(data); - if (!doc.isArray()) return; + QJsonDocument doc = QJsonDocument::fromJson(data); + if (!doc.isArray()) return; - QJsonArray array = doc.array(); - for (const QJsonValue &val : array) - { - QJsonObject obj = val.toObject(); - double pressure = obj["pressure"].toDouble(); - QDateTime ts = QDateTime::fromString(obj["timestamp"].toString(), Qt::ISODate); - if (ts.isValid()) - { - m_values.append(pressure); - m_timestamps.append(ts); - } - } + QJsonArray array = doc.array(); + for (const QJsonValue &val : array) + { + QJsonObject obj = val.toObject(); + double pressure = obj["pressure"].toDouble(); + QDateTime ts = QDateTime::fromString(obj["timestamp"].toString(), Qt::ISODate); + if (ts.isValid()) + { + m_values.append(pressure); + m_timestamps.append(ts); + } + } - // On ne garde que les 3 dernières heures - QDateTime limit = QDateTime::currentDateTime().addSecs(-3 * 3600); - while (!m_timestamps.isEmpty() && m_timestamps.first() < limit) - { - m_timestamps.removeFirst(); - m_values.removeFirst(); - } + // On ne garde que les 3 dernières heures + QDateTime limit = QDateTime::currentDateTime().addSecs(-3 * 3600); + while (!m_timestamps.isEmpty() && m_timestamps.first() < limit) + { + m_timestamps.removeFirst(); + m_values.removeFirst(); + } - debug(DEBUGMACRO, "Barometer history restored (" + QByteArray::number(m_values.size()) + " samples)", DEBUG); - } - } + debug(DEBUGMACRO, "Barometer history restored (" + QByteArray::number(m_values.size()) + " samples)", DEBUG); + } + } }; diff --git a/src/main.cpp b/src/main.cpp index 4a04fac..99907a2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,41 +1,39 @@ #include #include -//#include -#include +//#include #include -#include -#include -#include -#include -#include -#include -#include -#include +//#include +//#include #include #include #include "pws2mqtt.h" -#include "mqtt.h" +#ifdef WITH_MQTT + #include "mqtt.h" +#endif #include "src/barometertrend.h" #include "version.h" -#include -#include -#include -#include - +//#include +//#include +#ifdef WITH_MQTT + #include + #include + #include + #include +#endif #define CLIENT_ID "Client_ID" -#define BROKER_ADDRESS "localhost" -#define MQTT_PORT 1883; -uint debugLevel = DEBUG | INFO | NOTICE | INFO | WARNING | ERROR | ALERT; +uint debugLevel = INFO | ERROR | ALERT; QByteArray toFollow; -//class MqttClient; +#ifdef WITH_MQTT + class MqttClient; + MqttClient *mqttClient; +#endif using namespace std; //Configuration config; Pws2mqtt *pws2mqtt; -MqttClient *mqttClient; QHttpServer *httpServer; BarometerTrend *baro; @@ -54,45 +52,28 @@ int main(int argc, char *argv[]) // Enable logging to journald qputenv("QT_FORCE_STDERR_LOGGING", QByteArray("0")); - debug(DEBUGMACRO, "declaration of mqttClient", DEBUG); +#ifdef WITH_MQTT + debug(DEBUGMACRO, "declaration of mqttClient", INFO); mqttClient = new MqttClient; - +#endif httpServer = new QHttpServer; //declaration of debug level pws2mqtt = new Pws2mqtt; - baro = new BarometerTrend; + baro = new BarometerTrend; pws2mqtt->listeningHttp(); - - - //notify (QString("Program started"), "default"); a.exec(); +#ifdef WITH_MQTT mqttClient->qmqttClient->unsubscribe(mqttClient->topic); mqttClient->qmqttClient->disconnectFromHost(); - debug(DEBUGMACRO, "exiting", DEBUG); +#endif + debug(DEBUGMACRO, "exiting", INFO); notify ("Program exiting \n", "high"); - //Closing http server } -/*QString addValue(QByteArray value) -{ - bool ok; - QString str; - - value.toFloat(&ok); - if (ok) - { - str = (value); - }else - { - str = ("\"" + value + "\"" ); - } - return str; -}*/ - void debug(QString debugHeader, QString msg, uint8_t level, QByteArray property) { @@ -108,7 +89,7 @@ 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); + notify (debugHeader + " - " + msg); } if ((debugLevel & level) == 8) { diff --git a/src/mqtt.h b/src/mqtt.h index 1b494b9..d84fd96 100644 --- a/src/mqtt.h +++ b/src/mqtt.h @@ -11,63 +11,6 @@ #define MQTT_TOPIC "pws2mqtt" - -/*{128, "Unspecified error: The Server does not wish to reveal the reason for the failure, or none of the other Reason Codes apply."}, -{129, "Malformed Packet; Data within the CONNECT packet could not be correctly parsed."}, -{130, "Protocol Error: Data in the CONNECT packet does not conform to this specification."}, -{131, "Implementation specific error: The CONNECT is valid but is not accepted by this Server."}, -{132, "Unsupported Protocol Version: The Server does not support the version of the MQTT protocol requested by the Client."}, -{133, "Client Identifier not valid: The Client Identifier is a valid string but is not allowed by the Server."}, -{134, "Bad User Name or Password: The Server does not accept the User Name or Password specified by the Client."}, -{135, "Not authorized: The Client is not authorized to connect."}, -{136, "Server unavailable: The MQTT Server is not available."}, -{137, "Server busy: The Server is busy. Try again later."}, -{138, "Banned: This Client has been banned by administrative action. Contact the server administrator."}, -{140, "Bad authentication method: The authentication method is not supported or does not match the authentication method currently in use."}, -{144, "Topic Name invalid: The Will Topic Name is not malformed, but is not accepted by this Server."}, -{149, "Packet too large: The CONNECT packet exceeded the maximum permissible size."}, -{151, "Quota exceeded: An implementation or administrative imposed limit has been exceeded."}, -{153, "Payload format invalid: The Will Payload does not match the specified Payload Format Indicator."}, -{154, "Retain not supported: The Server does not support retained messages, and Will Retain was set to 1."}, -{155, "QoS not supported: The Server does not support the QoS set in Will QoS."}, -{156, "Use another server: The Client should temporarily use another server."}, -{157, "Server moved: The Client should permanently use another server."}, -{159, "Connection rate exceeded: The connection rate limit has been exceeded."}, - - -QHash mosq_error -{ - {MOSQ_ERR_ERRNO, "System return error"}, - {MOSQ_ERR_INVAL, "Input parameters were invalid."}, - {MOSQ_ERR_NOMEM, "Out of memory condition occurred."}, - {MOSQ_ERR_PAYLOAD_SIZE, "Payloadlen is too large."}, - {MOSQ_ERR_MALFORMED_UTF8, "The topic is not valid UTF-8."}, - {MOSQ_ERR_NOT_SUPPORTED, "Properties is not NULL and the client is not using MQTT v5"}, - {MOSQ_ERR_NO_CONN, "if the client isn’t connected to a broker."}, - {MOSQ_ERR_DUPLICATE_PROPERTY, "A property is duplicated where it is forbidden."}, - {MOSQ_ERR_PROTOCOL, "Any property is invalid for use with DISCONNECT."}, - {MOSQ_ERR_QOS_NOT_SUPPORTED, "The QoS is greater than that supported by the broker."}, - {MOSQ_ERR_OVERSIZE_PACKET, "The resulting packet would be larger than supported by the broker."}, - {MOSQ_ERR_CONN_LOST, "The connection to the broker was lost."}, - {MOSQ_ERR_PROTOCOL, "There is a protocol error communicating with the broker."}, -}; -*/ - -/*class MqttSub : public QMqttSubscription -{ - Q_OBJECT; - - - - public: - explicit MqttSub(); - ~MqttSub(); - - public slots: - void updateStatus({ , "SubscriptionState state); - -};*/ - class MqttClient : public QObject { Q_OBJECT diff --git a/src/pws2mqtt.cpp b/src/pws2mqtt.cpp index f11e767..8d0d3e8 100644 --- a/src/pws2mqtt.cpp +++ b/src/pws2mqtt.cpp @@ -1,25 +1,23 @@ #include "pws2mqtt.h" #include "barometertrend.h" -#include "mqtt.h" -//#include "httpserver.h" -#include -#include +//#include #include #include -#include -#include #include #include -#include -#include #include #include #include -#include -#include +//#include +#include "qstringview.h" #include "utcicalculator.h" +#include +#include +#ifdef WITH_MQTT + #include "mqtt.h" + /extern MqttClient *mqttClient; +#endif -extern MqttClient *mqttClient; extern Pws2mqtt *pws2mqtt; extern QHttpServer *httpServer; extern BarometerTrend *baro; @@ -46,7 +44,7 @@ QMap > propertyName { {"tempf", {"Température extérieure", "°C"}}, {"humidity", {"Humidité extérieure", "%"}}, - {"dewptf", {"Point de rosée", "°C"}}, +// {"dewptf", {"Point de rosée", "°C"}}, {"windchill", {"Température ressentie", "°C"}}, {"winddir", {"Direction du vent", "°"}}, {"windspeedmph", {"Vitesse du vent", " km/h"}}, @@ -55,12 +53,12 @@ QMap > propertyName {"dailyrainin", {"Pluie de la journée", " mm"}}, // {"monthlyrainin", {"Pluie du mois", " mm"}}, // {"yearlyrainin", {"Pluie de l'année", " mm"}}, -// {"solarradiation", {"Énergie solaire", " W/m²"}}, + {"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", {"Alerte UV", ""}} + {"UV", {"UV", ""}} }; QList > forceVent @@ -91,13 +89,13 @@ Pws2mqtt::~Pws2mqtt() void Pws2mqtt::init() { - debug(DEBUGMACRO, "init http server", DEBUG); - httpServer->setMissingHandler(httpServer,[](const QHttpServerRequest &request, QHttpServerResponder &responder) + debug(DEBUGMACRO, "init http server", INFO); + /*httpServer->setMissingHandler([](const QHttpServerRequest &request, QHttpServerResponder &&responder) { debug(DEBUGMACRO, "body " + request.url().toString(), WARNING); //responder.write(QHttpServerResponse(QString("404 - Page non trouvée. Route par défaut."), QHttpServerResponse::StatusCode::NotFound)); responder.write(QByteArray("404 - Page non trouvée. Route par défaut."), "text/plain", QHttpServerResponse::StatusCode::NotFound); - }); + });*/ httpServer->route("/", [](const QHttpServerRequest &request) { @@ -108,7 +106,7 @@ void Pws2mqtt::init() { QByteArray data; QList> queryList = request.query().queryItems(); - + debug(DEBUGMACRO, "Réception des données", INFO); //QTextStream result(&data); if (queryList.isEmpty()) @@ -116,21 +114,59 @@ void Pws2mqtt::init() debug(DEBUGMACRO, "Request query is empty", WARNING); }else { - debug(DEBUGMACRO, "Request query :" + request.query().toString() , DEBUG); - this->parseData(queryList); + debug(DEBUGMACRO, "Request query :" + request.query().toString() , INFO + ); +/* QMetaObject::invokeMethod(this, [this, query = request.query()]() { + this->parseData(query.queryItems()); + }, Qt::QueuedConnection);*/ + parseData(request.query().queryItems()); debug(DEBUGMACRO, "Returning 'success'", DEBUG); } //mqttClient.send_message(jsonString); + return QHttpServerResponse("text/plain", "Requête traitée en arrière-plan\n"); - return QHttpServerResponse("text/plain", "Success\n"); + //return QHttpServerResponse("text/plain", "Success\n"); }); + for (auto [name, pair]: propertyName.asKeyValueRange()) { - propertyList[name].append(formatNotifString(pair.first, pair.second, "0")); + propertyList[name].append(formatNotifString(pair, "0")); debug (DEBUGMACRO, "Init " + name + " => " + propertyList[name], DEBUG); } } +void notify(QString notif, QString priority, QString inputPath, QString tag) +{ + (void) inputPath; + QNetworkAccessManager *manager = new QNetworkAccessManager(); + QNetworkRequest request(QUrl("http://localhost:81/Meteo")); + request.setRawHeader("Content-Type", "charset=UTF-8; text/markdown"); + if (!tag.isEmpty()) + { + request.setRawHeader("Tags", tag.toUtf8()); + } + request.setRawHeader("Title", "Météo"); + request.setRawHeader("Priority", priority.toUtf8()); + request.setRawHeader("Markdown", "yes"); + request.setRawHeader("Config", "/etc/ntfy.client.yml"); + request.setRawHeader("Firebase", "no"); + + QNetworkReply *reply = manager->post(request, notif.toUtf8()); + QObject::connect(reply, &QNetworkReply::finished, [reply]() + { + if (reply->error() != QNetworkReply::NoError) + { + debug(DEBUGMACRO, "Erreur réseau : " + reply->errorString(), ERROR); + } else + { + debug(DEBUGMACRO, "Notification envoyée", DEBUG); + } + reply->deleteLater(); + }); + delete(manager); +} + + void Pws2mqtt::listeningHttp() { //QByteArray data; @@ -141,20 +177,23 @@ void Pws2mqtt::listeningHttp() { debug(DEBUGMACRO, "Http Server failed to listen on a port.", ERROR); } - debug(DEBUGMACRO, "Listening on port " + QString::number(port)); + debug(DEBUGMACRO, "Listening on port " + QString::number(port), INFO); } void Pws2mqtt::parseData(QList> queryList) { debug(DEBUGMACRO, "Parsing Datas", DEBUG); +#ifdef WITH_MQTT QString jsonString = "{"; QString deviceString = "\"device\": {\"ieeeAddress\": \"" + mqttClient->macAddress + "\", \"type\": \"" + mqttClient->type + "\", \"powerSource\": \"Battery\""; +#endif QString notif = ""; QStringList priorityList {"", "min", "low", "default", "High", "urgent"}; QString attachment = ""; QString tag = ""; - quint8 priority = 2; + QHash priority; + bool start = false; double propertyValue = 0; bool propertyFlag = false; bool deviceFlag = false; @@ -174,24 +213,29 @@ void Pws2mqtt::parseData(QList> queryList) if(deviceFlag == false) { deviceFlag = true; - }else + } +#ifdef WITH_MQTT + else { deviceString.append(", "); } deviceString += "\"" + name + "\": "; deviceString += pair.second; +#endif }else { if(propertyFlag == false) { propertyFlag = true; - }else + } +#ifdef WITH_MQTT + else { jsonString.append(", "); } jsonString.append("\"" + name + "\": "); jsonString.append(pair.second); - +#endif if (name == "tempf") { static QDateTime timeTemp = QDateTime::currentDateTime().addSecs(-600); @@ -203,8 +247,15 @@ void Pws2mqtt::parseData(QList> queryList) //notif += formatNotifString (propertyName[name].first, propertyName[name].second, QByteArray::number(qPow(propertyValue, 1.0))); //debug (DEBUGMACRO, "", DEBUG); timeTemp = timeTemp.currentDateTime().addSecs(300); - propertyList[name] = formatNotifString(propertyName[name].first, propertyName[name].second, QByteArray::number(propertyValue)); + propertyList[name] = formatNotifString(propertyName[name], QByteArray::number(propertyValue)); propertiesValue[name] = propertyValue; + if (propertyValue > 35) + { + priority[name] = 4; + }else if (propertyValue > 27) + { + priority[name] = 3; + } } }else if (name == "humidity") { @@ -214,7 +265,7 @@ void Pws2mqtt::parseData(QList> queryList) if (compare (propertiesValue[name], propertyValue, 3)) { //notif += formatNotifString (propertyName[name].first, propertyName[name].second , value); - propertyList[name] = formatNotifString(propertyName[name].first, propertyName[name].second, QByteArray::number(propertyValue)); + propertyList[name] = formatNotifString(propertyName[name], QByteArray::number(propertyValue)); propertiesValue[name] = propertyValue; } }else if (name == "winddir") @@ -222,7 +273,8 @@ void Pws2mqtt::parseData(QList> queryList) propertyValue = value.toFloat(); rotateAndSaveImage(this->inputPath, this->outputPath, (qreal)propertyValue); debug (DEBUGMACRO, name + " : " + QByteArray::number(propertyValue), DEBUG); - propertyList[name] = formatNotifString(propertyName[name].first, propertyName[name].second, QByteArray::number(propertyValue)); + + propertyList[name] = formatNotifString(propertyName[name], QByteArray::number(propertyValue)); propertiesValue[name] = propertyValue; }else if (name == "windspeedmph") { @@ -231,13 +283,13 @@ void Pws2mqtt::parseData(QList> queryList) static QDateTime timeWind = QDateTime::currentDateTime().addSecs(-600); quint8 windPriority = 1; - propertiesValue["vent"] = round(mphTokmh(value.toFloat())); + propertiesValue[name] = round(mphTokmh(value.toFloat())); - debug (DEBUGMACRO, name + " : " + QByteArray::number(propertiesValue["vent"]), DEBUG); + debug (DEBUGMACRO, name + " : " + QByteArray::number(propertiesValue[name]), DEBUG); for (i=0; i> queryList) } } - propertyList[name] = formatNotifString ("Vent - " + msg, propertyName[name].second , QByteArray::number(propertiesValue["vent"])); + propertyList[name] = "- Vent - " + msg + " : " + QByteArray::number(propertiesValue[name]) + "Km/h" + "\n"; propertiesValue["forcevent"] = i; if (i >= 5) { @@ -254,9 +306,9 @@ void Pws2mqtt::parseData(QList> queryList) if (propertiesValue["forcevent"] != i) { - priority = qMax(priority, windPriority); + priority[name] = qMax(priority[name], windPriority); } - debug (DEBUGMACRO, "priority = " + QString::number(priority), DEBUG); + debug (DEBUGMACRO, "priority = " + QString::number(priority[name]), DEBUG); }else if (name == "windgustmph") { QString msg = ""; @@ -264,15 +316,15 @@ void Pws2mqtt::parseData(QList> queryList) propertiesValue[name] = round(mphTokmh(value.toFloat())); - debug (DEBUGMACRO, name + " : " + QByteArray::number(propertiesValue["rafale"]), DEBUG); + debug (DEBUGMACRO, name + " : " + QByteArray::number(propertiesValue[name]), DEBUG); - propertyList[name] = formatNotifString (propertyName[name].first, propertyName[name].second , QByteArray::number(propertiesValue[name])); + propertyList[name] = formatNotifString (propertyName[name], QByteArray::number(propertiesValue[name])); - if (propertiesValue["rafales"] > 50) + if (propertiesValue[name] > 50) { - priority = qMax(priority, (quint8)4); + priority[name] = qMax(priority[name], (quint8)4); } - debug (DEBUGMACRO, "priority = " + QString::number(priority), DEBUG); + debug (DEBUGMACRO, "priority = " + QString::number(priority[name]), DEBUG); }else if (name == "rainin") { static double ecart; @@ -289,12 +341,12 @@ void Pws2mqtt::parseData(QList> queryList) if (compare (propertiesValue[name], propertyValue, ecart)) { QString pluviosite = getPluviosite(propertyValue, raininPriority); - priority = setPriority (priority, raininPriority); + priority[name] = setPriority (priority[name], raininPriority); //debug (DEBUGMACRO, "Notif = #" + notif + "#", DEBUG); - propertyList[name] = formatNotifString(pluviosite, propertyName[name].second, QByteArray::number(propertyValue)); + propertyList[name] = formatNotifString(propertyName[name], QByteArray::number(propertyValue)); propertiesValue[name] = propertyValue; } - debug (DEBUGMACRO, "priority = " + QString::number(priority), DEBUG); + debug (DEBUGMACRO, "priority = " + QString::number(priority[name]), DEBUG); }else if (name == "dailyrainin") { static double ecart; @@ -309,20 +361,19 @@ void Pws2mqtt::parseData(QList> queryList) debug (DEBUGMACRO, name + " : " + QByteArray::number(propertyValue), DEBUG); if (compare (propertiesValue[name], propertyValue, ecart)) { - //priority = setPriority (priority, 3); + //priority[name] = setPriority (priority[name], 3); //debug (DEBUGMACRO, "Notif = #" + notif + "#", DEBUG); - propertyList[name] = formatNotifString(propertyName[name].first, propertyName[name].second, QByteArray::number(propertyValue)); + propertyList[name] = formatNotifString(propertyName[name], QByteArray::number(propertyValue)); propertiesValue[name] = propertyValue; } - debug (DEBUGMACRO, "priority = " + QString::number(priority), DEBUG); + debug (DEBUGMACRO, "priority = " + QString::number(priority[name]), DEBUG); }else if (name == "baromin") { //static QDateTime timePrevision = QDateTime::currentDateTime().addSecs(-1000); - propertyValue = round(tohPa(pair.second.toFloat()) * 100) / 100; debug (DEBUGMACRO, "Barometre en hPa : " + QByteArray::number(propertyValue), DEBUG); baro->addPressure(propertyValue); - propertyList[name] = formatNotifString(propertyName[name].first, "", QByteArray::number(propertyValue)); + propertyList[name] = formatNotifString(propertyName[name], QByteArray::number(propertyValue)); propertiesValue[name] = propertyValue; static QString prevision; @@ -331,13 +382,14 @@ void Pws2mqtt::parseData(QList> queryList) prevision = ret.message; //if (prevision != newPrevision) //{ - priority = setPriority(priority, ret.urgency); + priority[name] = setPriority(priority[name], ret.urgency); tag += ret.icons; propertyList["prevision"] = prevision; - debug (DEBUGMACRO, "priority = " + QString::number(priority), DEBUG); + debug (DEBUGMACRO, "priority = " + QString::number(priority[name]), DEBUG); }else if (name == "UV") { + QString msg; static QDateTime timeUV = QDateTime::currentDateTime().addSecs(-600); propertyValue = pair.second.toUInt(); @@ -346,27 +398,30 @@ void Pws2mqtt::parseData(QList> queryList) { //notif += formatNotifString (propertyName[name].first, propertyName[name].second , value); if (propertyValue == 5 ) - priority = setPriority (priority, 4); + priority[name] = setPriority (priority[name], 4); if (propertyValue >= 6 ) - priority = setPriority (priority, 5); + priority[name] = setPriority (priority[name], 5); timeUV = timeUV.currentDateTime().addSecs(300); propertiesValue[name] = propertyValue; debug (DEBUGMACRO, "Notif = #" + notif + "#", DEBUG); - propertyList[name] = formatNotifString(propertyName[name].first, "", QByteArray::number(propertyValue)); + propertyList[name] = "- UV - " + QByteArray::number(propertyValue) + "\n"; } - debug (DEBUGMACRO, "priority = " + QString::number(priority), DEBUG); - + debug (DEBUGMACRO, "priority = " + QString::number(priority[name]), DEBUG); }else if (name == "solarradiation") { + propertyValue = pair.second.toFloat()*100/100; + propertyList[name] = formatNotifString(propertyName[name], QByteArray::number(propertyValue)); propertiesValue[name] = round(pair.second.toFloat()*100/100); + debug (DEBUGMACRO, "SolarRadiations = " + QByteArray::number(propertyValue), DEBUG); } } } propertiesValue["windchill"] = calc.computeUTCI(propertiesValue["tempf"], propertiesValue["vent"], propertiesValue["solarradiation"], propertiesValue["humidity"]); - propertyList["windchill"] = formatNotifString(propertyName["windchill"].first, propertyName["windchill"].second, QByteArray::number(propertiesValue["windchill"])); + propertyList["windchill"] = formatNotifString(propertyName["windchill"], QByteArray::number(propertiesValue["windchill"])); //windChill(propertiesValue["tempf"], propertiesValue["vent"]); //calculerHumidex(propertiesValue["tempf"], propertiesValue["humidity"]); +#ifdef WITH_MQTT if (!jsonString.isEmpty()) { debug(DEBUGMACRO, "json string : " + jsonString, DEBUG); @@ -376,50 +431,76 @@ void Pws2mqtt::parseData(QList> queryList) { debug(DEBUGMACRO, "No values to send", DEBUG); } +#endif debug(DEBUGMACRO, "current datetime : " + QDateTime::currentDateTime().toString() + ", timer = " + timer.toString(), DEBUG); - if (priority > 3 or QDateTime::currentDateTime() > timer.addSecs(1800)) + //bool changed = false; + quint8 aPriority= 0; + + debug(DEBUGMACRO, "looping to fill notif", DEBUG); + + QTime currentTime = QTime::currentTime(); + if (currentTime.minute() == 0 or start == false) { - bool changed = false; timer = QDateTime::currentDateTime(); - debug(DEBUGMACRO, "looping to fill notif, priority = " + QString::number(priority), DEBUG); - - if (precPropertyList.size() == 0) - { - changed = true; - } - for (auto [name, value]: propertyList.asKeyValueRange()) - { - if (precPropertyList.contains(name)) - { - if (precPropertyList[name] != propertyList[name]) - { - debug(DEBUGMACRO, "changed = true", DEBUG); - changed = true; - } - } - - debug(DEBUGMACRO, "Name = " + name + ", value = " + value, DEBUG); - if (! (name == "prevision")) - { - notif += propertyList[name]; - } - } - if (changed) - { - notify (notif, priorityList[priority], attachment, tag); - precPropertyList = propertyList; - } + start = true; + notif = listProperties2notify (propertyList, priority, 0); + notify (notif, priorityList[aPriority], attachment, tag); + precPropertyList = propertyList; + debug(DEBUGMACRO, "calling notify with notif = #" + notif + "#", INFO); notif = "**Prévisions à 12/24h**\n" + propertyList["prevision"]; - notify (notif, priorityList[priority], attachment, tag); - debug(DEBUGMACRO, "calling notify with notif = #" + notif + "#", DEBUG); - + notify (notif, priorityList[aPriority], "", tag); + debug(DEBUGMACRO, "calling notify with notif = #" + notif + "#", INFO); + } + if (aPriority >= 3) + { + notif = "**ALERTES**\n" + listProperties2notify (propertyList,priority, 3); + precPropertyList = propertyList; + notify (notif, priorityList[aPriority], attachment, tag); } debug(DEBUGMACRO, "parseData: Returning", DEBUG); } +QString listProperties2notify (QMap propertyList, QHash priority, quint8 minAlert) +{ + static QHash announced = {}; + QString notif = ""; + for (auto [name, value]: propertyList.asKeyValueRange()) + { + if (name != "prevision") + { + //if (precPropertyList.contains(name)) + //{ + //if (precPropertyList[name] != propertyList[name]) + //{ + if (minAlert == 0) + { + notif += propertyList[name]; + }else if (priority[name] >= minAlert) + { + if (announced.contains(name) and priority.contains(name)) + { + if (announced[name] != priority[name]) + { + notif += propertyList[name]; + announced[name] = priority[name]; + }else + { + debug (DEBUGMACRO, name + " : déjà annoncé = " + announced[name] + " nouvelle valeur = " + priority[name], INFO); + } + } + } + //} + //} + + debug(DEBUGMACRO, "Name = " + name + ", value = " + value, DEBUG); + } + } + return notif; +} + double calculerUTCI(double temperature, double humiditeRelative, double vitesseVent, double rayonnement) { double e = (humiditeRelative / 100.0) * 6.105 * exp((17.27 * temperature) / (237.7 + temperature)); @@ -506,154 +587,17 @@ bool compare (double value, double currentValue, double ecart) } } -QString formatNotifString (QString name, QString unit ,QByteArray value) +QString formatNotifString (QPair property ,QByteArray value) { - QString text = "- " + name + " : " + value; - if (!unit.isEmpty()) + QString text = "- " + property.first + " : " + value; + if (!property.second.isEmpty()) { - text += unit; + text += property.second; //"- " + propertyName[name].first + " : " + value + " " + propertyName[name].second + " "; } return text += "\n"; } -static size_t ReadFile(void *ptr, size_t size, size_t nmemb, void *stream) { - FILE *f = (FILE *)stream; - size_t n = fread(ptr, size, nmemb, f); - return n; -} - -// Callback for curl library -static size_t WriteCallback(void *contents, size_t size, size_t nmemb, std::string *output) -{ - size_t total_size = size * nmemb; - output->append((char*)contents, total_size); - return total_size; -} - -void notify(QString notif, QString priority, QString inputPath, QString tag) -{ - CURL *curl; - CURLcode res; - std::string readBuffer; - - priority = "Priority: " + priority; - debug (DEBUGMACRO, "notifying at priority " + priority + "with tag : " + tag + " - message : " + notif, DEBUG); - - // Initialise libcurl - curl_global_init(CURL_GLOBAL_DEFAULT); - curl = curl_easy_init(); - if (curl) - { - // Définis les en-têtes pour le titre et les priorités - struct curl_slist *headers = NULL; - - notif.replace("\\", "\\\\\\'"); // Échappe les apostrophes - - if (!tag.isEmpty()) - { - headers = curl_slist_append(headers, ("Tags: " + tag).toStdString().c_str()); - } - 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"); -// headers = curl_slist_append(headers, "Content-Type: charset=UTF-8; text/markdown"); // Ajout de l'encodage - - struct curl_slist *h = headers; - while (h) { - debug(DEBUGMACRO, "Header: " + QString(h->data), DEBUG); - h = h->next; - } - debug(DEBUGMACRO, "Notif: " + notif, DEBUG); - - // Configure la requête POST - curl_easy_setopt(curl, CURLOPT_URL, "http://localhost:81/Meteo"); - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, notif.toUtf8().constData()); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - - // Exécute la requête - - res = curl_easy_perform(curl); - - // Vérifie les erreurs - if (res != CURLE_OK) - { - debug(DEBUGMACRO, "Erreur libcurl :" + QString(curl_easy_strerror(res)), DEBUG); - } else - { - debug(DEBUGMACRO, "Réponse du serveur: " + QString::fromStdString(readBuffer), DEBUG); - } - - // Nettoie les ressources - //curl_mime_free(mime); - curl_slist_free_all(headers); - curl_easy_cleanup(curl); - } - // Nettoie libcurl - curl_global_cleanup(); - - if (!inputPath.isEmpty()) - { - FILE *fd = fopen(inputPath.toStdString().c_str(), "rb"); - - if (!fd) { - perror("Erreur lors de l'ouverture du fichier"); - return; - } - // Initialise libcurl - curl_global_init(CURL_GLOBAL_DEFAULT); - curl = curl_easy_init(); - if (curl) - { - // 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, "Config: /etc/ntfy.client.yml"); - headers = curl_slist_append(headers, "Firebase: no"); - headers = curl_slist_append(headers, "Content-Type: image/png"); - headers = curl_slist_append(headers, "X-Original-Size: true"); - headers = curl_slist_append(headers, "X-Filename: fleche.png"); - - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); // Configurer l'URL - curl_easy_setopt(curl, CURLOPT_URL, "localhost:81/Meteo"); - - // Utiliser la méthode PUT (comme `curl -T`) - curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); - - // Lire le fichier et l'envoyer en tant que body - curl_easy_setopt(curl, CURLOPT_READFUNCTION, ReadFile); - curl_easy_setopt(curl, CURLOPT_READDATA, fd); - - // Définir la taille du fichier (optionnel mais recommandé) - struct stat file_info; - fstat(fileno(fd), &file_info); - curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size); - - // Exécuter la requête - res = curl_easy_perform(curl); - // Vérifie les erreurs - if (res != CURLE_OK) - { - debug(DEBUGMACRO, "Erreur libcurl :" + QString(curl_easy_strerror(res)), DEBUG); - } else - { - debug(DEBUGMACRO, "Notification envoyée avec succès", DEBUG); - } - // Nettoyer - fclose(fd); - curl_easy_cleanup(curl); - } - // Nettoie libcurl - curl_global_cleanup(); - } -} - void rotateAndSaveImage(const QString &inputPath, const QString &outputPath, qreal angle) { QImage image(inputPath); diff --git a/src/pws2mqtt.h b/src/pws2mqtt.h index f9f497d..9976c14 100644 --- a/src/pws2mqtt.h +++ b/src/pws2mqtt.h @@ -5,6 +5,7 @@ #include #include #include +//#include #define RED "\e[31m" #define GREEN "\e[32m" @@ -53,9 +54,8 @@ class Pws2mqtt : public QObject double fahrenheitToCelsius(double fahrenheit); double tohPa(double value); bool compare (double value=0, double testValue=0, double ecart = 0.5); -QString formatNotifString (QString name, QString unit, QByteArray value=""); +QString formatNotifString (QPair property ,QByteArray value); double mphTokmh (double value); -void notify (QString notif, QString priority = "low", QString inputPath = "", QString tag = ""); quint8 setPriority (quint8 currentPriority, quint8 newPriority); QString previsionMeteo(double currentPressure, double variation3h, quint8 &priority); QString pressureVariation(double currentPressure, quint8 &priority); @@ -63,4 +63,6 @@ QString getPluviosite(double value, quint8 &priority); void rotateAndSaveImage(const QString &inputPath, const QString &outputPath, qreal angle=0); double windChill(double airT, double vent); double calculerHumidex(double temperature, double humiditeRelative); +void notify (QString notif, QString priority = "low", QString inputPath = "", QString tag = ""); +QString listProperties2notify (QMap propertyList, QHash priority, quint8 minAlert = 0); diff --git a/src/utcicalculator.cpp b/src/utcicalculator.cpp index 6449985..aad1ff7 100644 --- a/src/utcicalculator.cpp +++ b/src/utcicalculator.cpp @@ -5,174 +5,147 @@ #include UtciCalculator::UtciCalculator(QObject *parent) - : QObject(parent) + : QObject(parent) { } /*// Loads coefficients from a simple text file containing one coefficient per line or tab-separated. // The expected file is the ESM_3 coefficients table exported to a plain text file where the coefficients // appear in the same order as in the official table (210 coefficients). -bool UtciCalculator::loadCoefficientsFromFile(const QString &path) -{ - QFile f(path); - if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) - return false; - - QTextStream in(&f); - QVector coeffs; - coeffs.reserve(300); - - while (!in.atEnd()) { - QString line = in.readLine().trimmed(); - if (line.isEmpty()) continue; - // Allow lines with comments or 'term coefficient' headings: - // We try to extract numbers from the line. - QStringList parts = line.split(QRegularExpression("[\\s,\\t]+"), Qt::SkipEmptyParts); - for (const QString &p : parts) { - bool ok = false; - double val = p.toDouble(&ok); - if (ok) coeffs.append(val); - } - } - if (coeffs.isEmpty()) return false; - - m_coeffs = coeffs; - return true; -} */ // Magnus formula to estimate saturation vapour pressure (hPa) then return pa in kPa double UtciCalculator::vapourPressureFromRH(double Ta, double rh) { - // Magnus-Tetens approximation for saturation vapour pressure (hPa) - // valid roughly for -45C..+60C - // constants for water over liquid - double a = 17.27; - double b = 237.7; // °C - double es = 6.112 * qExp((a * Ta) / (Ta + b)); - double ea = es * (rh / 100.0); - return ea / 10.0; // hPa -> kPa + // Magnus-Tetens approximation for saturation vapour pressure (hPa) + // valid roughly for -45C..+60C + // constants for water over liquid + double a = 17.27; + double b = 237.7; // °C + double es = 6.112 * qExp((a * Ta) / (Ta + b)); + double ea = es * (rh / 100.0); + return ea / 10.0; // hPa -> kPa } double UtciCalculator::computeUTCI(double Ta, double va, double Tr, double rh) const { - double pa = vapourPressureFromRH(Ta, rh); - double tm = Tr - Ta; - double offset = evalOffset(Ta, va, tm, pa); - return Ta + offset; + double pa = vapourPressureFromRH(Ta, rh); + double tm = Tr - Ta; + double offset = evalOffset(Ta, va, tm, pa); + return Ta + offset; } double UtciCalculator::evalOffset(double Ta, double va, double tm, double pa) const { - if (m_coeffs.size() < 210) - return 0.0; + if (m_coeffs.size() < 210) + return 0.0; - // Pré-calcul des puissances - double Ta2 = Ta*Ta, Ta3 = Ta2*Ta, Ta4 = Ta3*Ta, Ta5 = Ta4*Ta, Ta6 = Ta5*Ta; - double va2 = va*va, va3 = va2*va, va4 = va3*va, va5 = va4*va, va6 = va5*va; - double tm2 = tm*tm, tm3 = tm2*tm, tm4 = tm3*tm, tm5 = tm4*tm, tm6 = tm5*tm; - double pa2 = pa*pa, pa3 = pa2*pa, pa4 = pa3*pa, pa5 = pa4*pa, pa6 = pa5*pa; + // Pré-calcul des puissances + double Ta2 = Ta*Ta, Ta3 = Ta2*Ta, Ta4 = Ta3*Ta, Ta5 = Ta4*Ta, Ta6 = Ta5*Ta; + double va2 = va*va, va3 = va2*va, va4 = va3*va, va5 = va4*va, va6 = va5*va; + double tm2 = tm*tm, tm3 = tm2*tm, tm4 = tm3*tm, tm5 = tm4*tm, tm6 = tm5*tm; + double pa2 = pa*pa, pa3 = pa2*pa, pa4 = pa3*pa, pa5 = pa4*pa, pa6 = pa5*pa; - QVector terms(210, 0.0); + QVector terms(210, 0.0); - // Construire les termes dans le même ordre que les coefficients ESM_3 - terms[0] = 1.0; // constante - terms[1] = Ta; - terms[2] = Ta2; - terms[3] = Ta3; - terms[4] = Ta4; - terms[5] = Ta5; - terms[6] = Ta6; + // Construire les termes dans le même ordre que les coefficients ESM_3 + terms[0] = 1.0; // constante + terms[1] = Ta; + terms[2] = Ta2; + terms[3] = Ta3; + terms[4] = Ta4; + terms[5] = Ta5; + terms[6] = Ta6; - terms[7] = va; - terms[8] = Ta*va; - terms[9] = Ta2*va; - terms[10] = Ta3*va; - terms[11] = Ta4*va; - terms[12] = Ta5*va; + terms[7] = va; + terms[8] = Ta*va; + terms[9] = Ta2*va; + terms[10] = Ta3*va; + terms[11] = Ta4*va; + terms[12] = Ta5*va; - terms[13] = va2; - terms[14] = Ta*va2; - terms[15] = Ta2*va2; - terms[16] = Ta3*va2; - terms[17] = Ta4*va2; + terms[13] = va2; + terms[14] = Ta*va2; + terms[15] = Ta2*va2; + terms[16] = Ta3*va2; + terms[17] = Ta4*va2; - terms[18] = va3; - terms[19] = Ta*va3; - terms[20] = Ta2*va3; - terms[21] = Ta3*va3; + terms[18] = va3; + terms[19] = Ta*va3; + terms[20] = Ta2*va3; + terms[21] = Ta3*va3; - terms[22] = va4; - terms[23] = Ta*va4; - terms[24] = Ta2*va4; + terms[22] = va4; + terms[23] = Ta*va4; + terms[24] = Ta2*va4; - terms[25] = va5; - terms[26] = Ta*va5; + terms[25] = va5; + terms[26] = Ta*va5; - terms[27] = va6; + terms[27] = va6; - terms[28] = tm; - terms[29] = Ta*tm; - terms[30] = Ta2*tm; - terms[31] = Ta3*tm; - terms[32] = Ta4*tm; - terms[33] = Ta5*tm; - terms[34] = Ta6*tm; + terms[28] = tm; + terms[29] = Ta*tm; + terms[30] = Ta2*tm; + terms[31] = Ta3*tm; + terms[32] = Ta4*tm; + terms[33] = Ta5*tm; + terms[34] = Ta6*tm; - terms[35] = va*tm; - terms[36] = Ta*va*tm; - terms[37] = Ta2*va*tm; - terms[38] = Ta3*va*tm; - terms[39] = Ta4*va*tm; + terms[35] = va*tm; + terms[36] = Ta*va*tm; + terms[37] = Ta2*va*tm; + terms[38] = Ta3*va*tm; + terms[39] = Ta4*va*tm; - terms[40] = va2*tm; - terms[41] = Ta*va2*tm; - terms[42] = Ta2*va2*tm; - terms[43] = Ta3*va2*tm; + terms[40] = va2*tm; + terms[41] = Ta*va2*tm; + terms[42] = Ta2*va2*tm; + terms[43] = Ta3*va2*tm; - terms[44] = va3*tm; - terms[45] = Ta*va3*tm; - terms[46] = Ta2*va3*tm; + terms[44] = va3*tm; + terms[45] = Ta*va3*tm; + terms[46] = Ta2*va3*tm; - terms[47] = va4*tm; - terms[48] = Ta*va4*tm; + terms[47] = va4*tm; + terms[48] = Ta*va4*tm; - terms[49] = va5*tm; + terms[49] = va5*tm; - // tm^2 à tm^6 - terms[50] = tm2; terms[51] = Ta*tm2; terms[52] = Ta2*tm2; terms[53] = Ta3*tm2; terms[54] = Ta4*tm2; - terms[55] = va*tm2; terms[56] = Ta*va*tm2; terms[57] = Ta2*va*tm2; terms[58] = Ta3*va*tm2; - terms[59] = va2*tm2; terms[60] = Ta*va2*tm2; terms[61] = Ta2*va2*tm2; - terms[62] = va3*tm2; - terms[63] = tm3; terms[64] = Ta*tm3; terms[65] = Ta2*tm3; - terms[66] = va*tm3; terms[67] = Ta*va*tm3; terms[68] = va2*tm3; - terms[69] = tm4; terms[70] = Ta*tm4; terms[71] = va*tm4; - terms[72] = tm5; - terms[73] = tm6; + // tm^2 à tm^6 + terms[50] = tm2; terms[51] = Ta*tm2; terms[52] = Ta2*tm2; terms[53] = Ta3*tm2; terms[54] = Ta4*tm2; + terms[55] = va*tm2; terms[56] = Ta*va*tm2; terms[57] = Ta2*va*tm2; terms[58] = Ta3*va*tm2; + terms[59] = va2*tm2; terms[60] = Ta*va2*tm2; terms[61] = Ta2*va2*tm2; + terms[62] = va3*tm2; + terms[63] = tm3; terms[64] = Ta*tm3; terms[65] = Ta2*tm3; + terms[66] = va*tm3; terms[67] = Ta*va*tm3; terms[68] = va2*tm3; + terms[69] = tm4; terms[70] = Ta*tm4; terms[71] = va*tm4; + terms[72] = tm5; + terms[73] = tm6; - // pa et combinaisons - terms[74] = pa; terms[75] = Ta*pa; terms[76] = Ta2*pa; terms[77] = Ta3*pa; terms[78] = Ta4*pa; terms[79] = Ta5*pa; - terms[80] = va*pa; terms[81] = Ta*va*pa; terms[82] = Ta2*va*pa; terms[83] = Ta3*va*pa; - terms[84] = va2*pa; terms[85] = Ta*va2*pa; terms[86] = Ta2*va2*pa; - terms[87] = va3*pa; terms[88] = Ta*va3*pa; - terms[89] = va4*pa; + // pa et combinaisons + terms[74] = pa; terms[75] = Ta*pa; terms[76] = Ta2*pa; terms[77] = Ta3*pa; terms[78] = Ta4*pa; terms[79] = Ta5*pa; + terms[80] = va*pa; terms[81] = Ta*va*pa; terms[82] = Ta2*va*pa; terms[83] = Ta3*va*pa; + terms[84] = va2*pa; terms[85] = Ta*va2*pa; terms[86] = Ta2*va2*pa; + terms[87] = va3*pa; terms[88] = Ta*va3*pa; + terms[89] = va4*pa; - terms[90] = pa2; terms[91] = Ta*pa2; terms[92] = Ta2*pa2; + terms[90] = pa2; terms[91] = Ta*pa2; terms[92] = Ta2*pa2; - // Remplir le reste avec 0.0 pour simplifier (les 210 coefficients réels peuvent être affectés directement) - for (int i = 93; i < 210; ++i) terms[i] = 0.0; + // Remplir le reste avec 0.0 pour simplifier (les 210 coefficients réels peuvent être affectés directement) + for (int i = 93; i < 210; ++i) terms[i] = 0.0; - // Calcul de l'offset - double offset = 0.0; - for (int i = 0; i < 210; ++i) - offset += m_coeffs[i] * terms[i]; + // Calcul de l'offset + double offset = 0.0; + for (int i = 0; i < 210; ++i) + offset += m_coeffs[i] * terms[i]; - return offset; + return offset; } void UtciCalculator::initCoefficients() { - // Coefficients ESM_3 (exemple simplifié, à remplacer par la liste complète 210 valeurs) - m_coeffs = { - 6.07562052E-01, -2.27712343E-02, 8.06470249E-04, -1.54271372E-04, -3.24651735E-06, 7.32602852E-08, 1.35959073E-09, -2.25836520E+00, 8.80326035E-02, 2.16844454E-03, -1.53347087E-05, -5.72983704E-07, -2.55090145E-09, -7.51269505E-01, -4.08350271E-03, -5.21670675E-05, 1.94544667E-06, 1.14099531E-08, 1.58137256E-01, -6.57263143E-05, 2.22697524E-07, -4.16117031E-08, -1.27762753E-02, 9.66891875E-06, 2.52785852E-09, 4.56306672E-04, -1.74202546E-07, -5.91491269E-06, 3.98374029E-01, 1.83945314E-04, -1.73754510E-04, -7.60781159E-07, 3.77830287E-08, 5.43079673E-10, -2.00518269E-02, 8.92859837E-04, 3.45433048E-06, -3.77925774E-07, -1.69699377E-09, 1.69992415E-04, -4.99204314E-05, 2.47417178E-07, 1.07596466E-08, 8.49242932E-05, 1.35191328E-06, -6.21531254E-09, -4.99410301E-06, -1.89489258E-08, 8.15300114E-08, 7.55043090E-04, -3.69476348E-02, 1.62325322E-03, -3.14279680E-05, 2.59835559E-06, -4.77136523E-08, 8.64203390E-03, -6.87405181E-04, -9.13863872E-06, 5.15916806E-07, -3.59217476E-05, 3.28696511E-05, -7.10542454E-07, -1.24382300E-05, -7.38584400E-09, 2.20609296E-07, -7.32469180E-04, -1.87381964E-05, 4.80925239E-06, -8.75492040E-08, 2.77862930E-05, -5.06004592E-06, 1.14325367E-07, 2.53016723E-06, -1.72857035E-08, -3.95079398E-08, -3.59413173E-07, 7.04388046E-07, -1.89309167E-08, -4.79768731E-07, 7.96079978E-09, 1.62897058E-09, 3.94367674E-08, -1.18566247E-09, 3.34678041E-10, -1.15606447E-10, -2.80626406E+00, 5.48712484E-01, -3.99428410E-03, -9.54009191E-04, 1.93090978E-05, -3.08806365E-01, 1.16952364E-02, 4.95271903E-04, -1.90710882E-05, 2.10787756E-03, -6.98445738E-04, 2.30109073E-05, 4.17856590E-04, -1.27043871E-05, -3.04620472E-06, -5.65095215E-05, -4.52166564E-07, 2.46688878E-08, 2.42674348E-10, 1.54547250E-04, 5.24110970E-06, -8.75874982E-08, -1.50743064E-09, -1.56236307E-05, -1.33895614E-07, 2.49709824E-09, 6.51711721E-07, 1.94960053E-09, -1.00361113E-08, -1.21206673E-05, -2.18203660E-07, 7.51269482E-09, 9.79063848E-11, 1.25006734E-06, -1.81584736E-09, -3.52197671E-10, -3.36514630E-08, 1.35908359E-10, 4.17032620E-10, -1.30369025E-09, 4.13908461E-10, 9.22652254E-12, -5.08220384E-09, -2.24730961E-11, 1.17139133E-10, 6.62154879E-10, 4.03863260E-13, 1.95087203E-12, -4.73602469E-12, 5.12733497E+00, -3.12788561E-01, -1.96701861E-02, 9.99690870E-04, 9.51738512E-06, -4.66426341E-07, 5.48050612E-01, -3.30552823E-03, -1.64119440E-03, -5.16670694E-06, 9.52692432E-07, -4.29223622E-02, 5.00845667E-03, 1.00601257E-06, -1.81748644E-06, -1.25813502E-03, -1.79330391E-04, 2.34994441E-06, 1.29735808E-04, 1.29064870E-06, -2.28558686E-06, 5.14507424E-02, -4.32510997E-03, 8.99281156E-05, -7.14663943E-07, -2.66016305E-04, 2.63789586E-04, -7.01199003E-06, -1.06823306E-04, 3.61341136E-06, 2.29748967E-07, 3.04788893E-04, -6.42070836E-05, 1.16257971E-06, 7.68023384E-06, -5.47446896E-07, -3.59937910E-08, -4.36497725E-06, 1.68737969E-07, 2.67489271E-08, 3.23926897E-09, -3.53874123E-02, -2.21201190E-01, 1.55126038E-02, -2.63917279E-04, 4.53433455E-02, -4.32943862E-03, 1.45389826E-04, 2.17508610E-04, -6.66724702E-05, 3.33217140E-05, -2.26921615E-03, 3.80261982E-04, -5.45314314E-09, -7.96355448E-04, 2.53458034E-05, -6.31223658E-06, 3.02122035E-04, -4.77403547E-06, 1.73825715E-06, -4.09087898E-07, 6.14155345E-01, -6.16755931E-02, 1.33374846E-03, 3.55375387E-03, -5.13027851E-04, 1.02449757E-04, -1.48526421E-03, -4.11469183E-05, -6.80434415E-06, -9.77675906E-06, 8.82773108E-02, -3.01859306E-03, 1.04452989E-03, 2.47090539E-04, 1.48348065E-03 - }; + // Coefficients ESM_3 (exemple simplifié, à remplacer par la liste complète 210 valeurs) + m_coeffs = { + 6.07562052E-01, -2.27712343E-02, 8.06470249E-04, -1.54271372E-04, -3.24651735E-06, 7.32602852E-08, 1.35959073E-09, -2.25836520E+00, 8.80326035E-02, 2.16844454E-03, -1.53347087E-05, -5.72983704E-07, -2.55090145E-09, -7.51269505E-01, -4.08350271E-03, -5.21670675E-05, 1.94544667E-06, 1.14099531E-08, 1.58137256E-01, -6.57263143E-05, 2.22697524E-07, -4.16117031E-08, -1.27762753E-02, 9.66891875E-06, 2.52785852E-09, 4.56306672E-04, -1.74202546E-07, -5.91491269E-06, 3.98374029E-01, 1.83945314E-04, -1.73754510E-04, -7.60781159E-07, 3.77830287E-08, 5.43079673E-10, -2.00518269E-02, 8.92859837E-04, 3.45433048E-06, -3.77925774E-07, -1.69699377E-09, 1.69992415E-04, -4.99204314E-05, 2.47417178E-07, 1.07596466E-08, 8.49242932E-05, 1.35191328E-06, -6.21531254E-09, -4.99410301E-06, -1.89489258E-08, 8.15300114E-08, 7.55043090E-04, -3.69476348E-02, 1.62325322E-03, -3.14279680E-05, 2.59835559E-06, -4.77136523E-08, 8.64203390E-03, -6.87405181E-04, -9.13863872E-06, 5.15916806E-07, -3.59217476E-05, 3.28696511E-05, -7.10542454E-07, -1.24382300E-05, -7.38584400E-09, 2.20609296E-07, -7.32469180E-04, -1.87381964E-05, 4.80925239E-06, -8.75492040E-08, 2.77862930E-05, -5.06004592E-06, 1.14325367E-07, 2.53016723E-06, -1.72857035E-08, -3.95079398E-08, -3.59413173E-07, 7.04388046E-07, -1.89309167E-08, -4.79768731E-07, 7.96079978E-09, 1.62897058E-09, 3.94367674E-08, -1.18566247E-09, 3.34678041E-10, -1.15606447E-10, -2.80626406E+00, 5.48712484E-01, -3.99428410E-03, -9.54009191E-04, 1.93090978E-05, -3.08806365E-01, 1.16952364E-02, 4.95271903E-04, -1.90710882E-05, 2.10787756E-03, -6.98445738E-04, 2.30109073E-05, 4.17856590E-04, -1.27043871E-05, -3.04620472E-06, -5.65095215E-05, -4.52166564E-07, 2.46688878E-08, 2.42674348E-10, 1.54547250E-04, 5.24110970E-06, -8.75874982E-08, -1.50743064E-09, -1.56236307E-05, -1.33895614E-07, 2.49709824E-09, 6.51711721E-07, 1.94960053E-09, -1.00361113E-08, -1.21206673E-05, -2.18203660E-07, 7.51269482E-09, 9.79063848E-11, 1.25006734E-06, -1.81584736E-09, -3.52197671E-10, -3.36514630E-08, 1.35908359E-10, 4.17032620E-10, -1.30369025E-09, 4.13908461E-10, 9.22652254E-12, -5.08220384E-09, -2.24730961E-11, 1.17139133E-10, 6.62154879E-10, 4.03863260E-13, 1.95087203E-12, -4.73602469E-12, 5.12733497E+00, -3.12788561E-01, -1.96701861E-02, 9.99690870E-04, 9.51738512E-06, -4.66426341E-07, 5.48050612E-01, -3.30552823E-03, -1.64119440E-03, -5.16670694E-06, 9.52692432E-07, -4.29223622E-02, 5.00845667E-03, 1.00601257E-06, -1.81748644E-06, -1.25813502E-03, -1.79330391E-04, 2.34994441E-06, 1.29735808E-04, 1.29064870E-06, -2.28558686E-06, 5.14507424E-02, -4.32510997E-03, 8.99281156E-05, -7.14663943E-07, -2.66016305E-04, 2.63789586E-04, -7.01199003E-06, -1.06823306E-04, 3.61341136E-06, 2.29748967E-07, 3.04788893E-04, -6.42070836E-05, 1.16257971E-06, 7.68023384E-06, -5.47446896E-07, -3.59937910E-08, -4.36497725E-06, 1.68737969E-07, 2.67489271E-08, 3.23926897E-09, -3.53874123E-02, -2.21201190E-01, 1.55126038E-02, -2.63917279E-04, 4.53433455E-02, -4.32943862E-03, 1.45389826E-04, 2.17508610E-04, -6.66724702E-05, 3.33217140E-05, -2.26921615E-03, 3.80261982E-04, -5.45314314E-09, -7.96355448E-04, 2.53458034E-05, -6.31223658E-06, 3.02122035E-04, -4.77403547E-06, 1.73825715E-06, -4.09087898E-07, 6.14155345E-01, -6.16755931E-02, 1.33374846E-03, 3.55375387E-03, -5.13027851E-04, 1.02449757E-04, -1.48526421E-03, -4.11469183E-05, -6.80434415E-06, -9.77675906E-06, 8.82773108E-02, -3.01859306E-03, 1.04452989E-03, 2.47090539E-04, 1.48348065E-03 + }; }