suite
This commit is contained in:
14
httpserver.h
14
httpserver.h
@@ -1,14 +0,0 @@
|
|||||||
#ifndef HTTPSERVER_H
|
|
||||||
#define HTTPSERVER_H
|
|
||||||
|
|
||||||
#include <QtHttpServer/QHttpServer>
|
|
||||||
|
|
||||||
class HttpServer : QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
int port = 5000;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif // HTTPSERVER_H
|
|
||||||
128
main.cpp
128
main.cpp
@@ -1,128 +0,0 @@
|
|||||||
#include <QCoreApplication>
|
|
||||||
#include <QString>
|
|
||||||
//#include <QObject>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <QtGlobal>
|
|
||||||
#include <iostream>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <QtMqtt>
|
|
||||||
#include <QtMqtt/QMqttClient>
|
|
||||||
#include <QtMqtt/QMqttMessage>
|
|
||||||
#include <QtMqtt/QMqttSubscription>
|
|
||||||
#include <qhttpserver.h>
|
|
||||||
#include <QHttpServerResponse>
|
|
||||||
#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>
|
|
||||||
#include "utcicalculator.h"
|
|
||||||
|
|
||||||
#define CLIENT_ID "Client_ID"
|
|
||||||
#define BROKER_ADDRESS "localhost"
|
|
||||||
#define MQTT_PORT 1883;
|
|
||||||
|
|
||||||
uint debugLevel = DEBUG | INFO | NOTICE | INFO | WARNING | ERROR | ALERT;
|
|
||||||
QByteArray toFollow;
|
|
||||||
|
|
||||||
//class MqttClient;
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
//Configuration config;
|
|
||||||
Pws2mqtt *pws2mqtt;
|
|
||||||
MqttClient *mqttClient;
|
|
||||||
QHttpServer *httpServer;
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
QCoreApplication a(argc, argv);
|
|
||||||
|
|
||||||
if (argc > 1)
|
|
||||||
{
|
|
||||||
if (strcmp(argv[1], "v"))
|
|
||||||
{
|
|
||||||
printf ("Version : %s\n", version.c_str());
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Enable logging to journald
|
|
||||||
qputenv("QT_FORCE_STDERR_LOGGING", QByteArray("0"));
|
|
||||||
|
|
||||||
debug(DEBUGMACRO, "declaration of mqttClient", DEBUG);
|
|
||||||
mqttClient = new MqttClient;
|
|
||||||
|
|
||||||
httpServer = new QHttpServer;
|
|
||||||
|
|
||||||
//declaration of debug level
|
|
||||||
pws2mqtt = new Pws2mqtt;
|
|
||||||
|
|
||||||
pws2mqtt->listeningHttp();
|
|
||||||
//notify (QString("Program started"), "default");
|
|
||||||
a.exec();
|
|
||||||
|
|
||||||
mqttClient->qmqttClient->unsubscribe(mqttClient->topic);
|
|
||||||
mqttClient->qmqttClient->disconnectFromHost();
|
|
||||||
debug(DEBUGMACRO, "exiting", DEBUG);
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
|
|
||||||
msg.replace("\n", "\\n");
|
|
||||||
if ((debugLevel & level) == 1)
|
|
||||||
{
|
|
||||||
qInfo("%s%sINFO: %s%s", debugHeader.toStdString().c_str(), GREEN, msg.toStdString().c_str(), NORMAL);
|
|
||||||
}
|
|
||||||
if ((debugLevel & level) == 2)
|
|
||||||
{
|
|
||||||
qInfo("%s%s NOTICE: %s%s", debugHeader.toStdString().c_str(), GREEN, msg.toStdString().c_str(), NORMAL);
|
|
||||||
}
|
|
||||||
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 ("Program error : " + debugHeader + " - " + msg, "high");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if ((debugLevel & level) == 16)
|
|
||||||
{
|
|
||||||
qInfo("%s%s DEBUG: %s%s", debugHeader.toStdString().c_str(), GREEN, ("'" + msg.toStdString() + "'").c_str(), NORMAL);
|
|
||||||
}
|
|
||||||
if ((debugLevel & level) == 32)
|
|
||||||
{
|
|
||||||
qInfo("%s%s ALERT: %s%s", debugHeader.toStdString().c_str(), RED, msg.toStdString().c_str(), NORMAL);
|
|
||||||
notify ("Program exiting " + debugHeader + " - " + msg, "high" );
|
|
||||||
exit(2);
|
|
||||||
}
|
|
||||||
if (toFollow == property and !toFollow.isEmpty())
|
|
||||||
{
|
|
||||||
qInfo("%s%s FOLLOWING %s => %s%s", debugHeader.toStdString().c_str(), GREEN, property.toStdString().c_str(), msg.toStdString().c_str(), NORMAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
142
mqtt.cpp
142
mqtt.cpp
@@ -1,142 +0,0 @@
|
|||||||
#include "mqtt.h"
|
|
||||||
#include "pws2mqtt.h"
|
|
||||||
#include <iostream>
|
|
||||||
#include <cstring>
|
|
||||||
#include <QString>
|
|
||||||
#include <QObject>
|
|
||||||
#include <QtMqtt>
|
|
||||||
#include <QMqttMessageStatusProperties>
|
|
||||||
#include <QtMqtt/QMqttClient>
|
|
||||||
#include <QtMqtt/QMqttSubscription>
|
|
||||||
#include <QtMqtt/QMqttMessage>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
extern MqttClient *mqttClient;
|
|
||||||
|
|
||||||
MqttClient::MqttClient()
|
|
||||||
{
|
|
||||||
// init mqtt connexion to server
|
|
||||||
debug(DEBUGMACRO, "declaration of qmqttClient", DEBUG);
|
|
||||||
this->qmqttClient = new QMqttClient(this);
|
|
||||||
|
|
||||||
debug(DEBUGMACRO, "setting hostname of mqtt server", DEBUG);
|
|
||||||
this->qmqttClient->setHostname(this->server);
|
|
||||||
debug(DEBUGMACRO, "setting port of mqtt server", DEBUG);
|
|
||||||
this->qmqttClient->setPort(this->serverPort); //this->serverPort);
|
|
||||||
|
|
||||||
debug(DEBUGMACRO, "Connecting 'messageReceived'", DEBUG);
|
|
||||||
QObject::connect(this->qmqttClient, &QMqttClient::messageReceived, this, &MqttClient::on_message);
|
|
||||||
debug (DEBUGMACRO, "Connecting 'disconnect'", DEBUG);
|
|
||||||
QObject::connect(this->qmqttClient, &QMqttClient::disconnected, this, &MqttClient::on_disconnect);
|
|
||||||
debug (DEBUGMACRO, "Connecting 'connected'", DEBUG);
|
|
||||||
QObject::connect(this->qmqttClient, &QMqttClient::connected, this, &MqttClient::on_connect);
|
|
||||||
debug (DEBUGMACRO, "Connecting 'errorChanged", DEBUG);
|
|
||||||
QObject::connect(this->qmqttClient, &QMqttClient::errorChanged, this, &MqttClient::on_error);
|
|
||||||
debug (DEBUGMACRO, "Connecting 'messageSent'", DEBUG);
|
|
||||||
QObject::connect(this->qmqttClient, &QMqttClient::messageSent, this, &MqttClient::on_messageSent);
|
|
||||||
debug (DEBUGMACRO, "Connecting 'messageStatusChanged'", DEBUG);
|
|
||||||
QObject::connect(this->qmqttClient, &QMqttClient::messageStatusChanged, this, &MqttClient::on_messageSentStatusChanged);
|
|
||||||
debug (DEBUGMACRO, "Connecting 'stateChanged'", DEBUG);
|
|
||||||
QObject::connect(this->qmqttClient, &QMqttClient::stateChanged, this, &MqttClient::on_stateChanged);
|
|
||||||
|
|
||||||
// qmqttClient->setClientId("pws2mqtt");
|
|
||||||
// qmqttClient->setProtocolVersion(QMqttClient::MQTT_3_1_1);
|
|
||||||
|
|
||||||
debug(DEBUGMACRO, "connecting to mqtt server", DEBUG);
|
|
||||||
this->qmqttClient->connectToHost();
|
|
||||||
|
|
||||||
//debug(DEBUGMACRO, "error status => " + this->qmqttErrors[this->qmqttClient->error()], DEBUG);
|
|
||||||
}
|
|
||||||
|
|
||||||
MqttClient::~MqttClient()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
qint32 MqttClient::send_message(QString message)
|
|
||||||
{
|
|
||||||
qint32 mid;
|
|
||||||
|
|
||||||
debug(DEBUGMACRO, "send_message() for topic: " + this->topic + " => " + message , DEBUG);
|
|
||||||
mid = this->qmqttClient->publish(this->topic, message.toUtf8(), this->qos, true);
|
|
||||||
if (mid == -1)
|
|
||||||
{
|
|
||||||
debug("MQTT", "Erreur, impossiblité d'envoyer le message : " + this->qmqttErrors[this->qmqttClient->error()], WARNING);
|
|
||||||
}
|
|
||||||
messagesList.insert(mid, message);
|
|
||||||
return (mid);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MqttClient::on_connect()
|
|
||||||
{
|
|
||||||
//this->connected = true;
|
|
||||||
debug(DEBUGMACRO, "Connected to broker", DEBUG);
|
|
||||||
this->subscription = this->qmqttClient->subscribe(this->topic, this->qos);
|
|
||||||
debug (DEBUGMACRO, "Connecting sign 'subscription state Changed'", DEBUG);
|
|
||||||
QObject::connect(this->subscription, &QMqttSubscription::stateChanged, mqttClient, &MqttClient::updateStatus);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MqttClient::on_messageSentStatusChanged(qint32 id, QMqtt::MessageStatus s, const QMqttMessageStatusProperties &properties)
|
|
||||||
{
|
|
||||||
(void) properties;
|
|
||||||
|
|
||||||
if ( id == 0)
|
|
||||||
{
|
|
||||||
debug(DEBUGMACRO, "message id = " + QString::number(id) + this->messageStatus[static_cast<int>(s)], INFO);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MqttClient::on_error(QMqttClient::ClientError error)
|
|
||||||
{
|
|
||||||
debug("MQTT", "Error " + qmqttErrors[error], WARNING);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MqttClient::on_message(const QByteArray &message, const QMqttTopicName &topic)
|
|
||||||
{
|
|
||||||
(void) message;
|
|
||||||
(void) topic;
|
|
||||||
debug(DEBUGMACRO, "Message : " + message, DEBUG);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MqttClient::on_disconnect()
|
|
||||||
{
|
|
||||||
debug(DEBUGMACRO, "Unexpected disconnect", DEBUG);
|
|
||||||
this->qmqttClient->connectToHost();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void MqttClient::on_messageSent(int mid)
|
|
||||||
{
|
|
||||||
debug(DEBUGMACRO, "published(" + QString::number(mid) + ")", DEBUG);
|
|
||||||
messagesList.remove(mid);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MqttClient::updateStatus(QMqttSubscription::SubscriptionState state)
|
|
||||||
{
|
|
||||||
switch (state)
|
|
||||||
{
|
|
||||||
case QMqttSubscription::Unsubscribed:
|
|
||||||
debug("MQTT", "Unsubscribed", WARNING);
|
|
||||||
break;
|
|
||||||
case QMqttSubscription::SubscriptionPending:
|
|
||||||
debug("MQTT", "Pending subscription", WARNING);
|
|
||||||
break;
|
|
||||||
case QMqttSubscription::Subscribed:
|
|
||||||
debug(DEBUGMACRO, "Subscribed", INFO);
|
|
||||||
break;
|
|
||||||
case QMqttSubscription::Error:
|
|
||||||
debug("MQTT", "Subscription error" + this->subscription->reason(), WARNING);
|
|
||||||
break;
|
|
||||||
case QMqttSubscription::UnsubscriptionPending:
|
|
||||||
debug("MQTT", "Pending Unsubscription", WARNING);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
debug(DEBUGMACRO, "--Unknown--", DEBUG);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MqttClient::on_stateChanged(QMqttClient::ClientState state)
|
|
||||||
{
|
|
||||||
debug("MQTT", QLatin1String("State Change : ") + this->clientStateString[state], WARNING );
|
|
||||||
}
|
|
||||||
191
mqtt.h
191
mqtt.h
@@ -1,191 +0,0 @@
|
|||||||
#ifndef MQTT_H
|
|
||||||
#define MQTT_H
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <QString>
|
|
||||||
#include <QHash>
|
|
||||||
#include <QtMqtt>
|
|
||||||
#include <QtMqtt/QMqttClient>
|
|
||||||
#include <QtMqtt/QMqttMessage>
|
|
||||||
#include <QtMqtt/QMqttSubscription>
|
|
||||||
|
|
||||||
#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 <int, QString> 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
|
|
||||||
|
|
||||||
public:
|
|
||||||
MqttClient ();
|
|
||||||
~MqttClient();
|
|
||||||
|
|
||||||
QString topic = "pws2mqtt/wh2650a";
|
|
||||||
QString host = "127.0.0.1";
|
|
||||||
QString friendlyName = "WH2650A"; // friendly name of the device in mqtt
|
|
||||||
QString macAddress = "0x483fda53cbcb"; // lan mac address as lan identity TODO get from device
|
|
||||||
QString type = "Meteo";
|
|
||||||
int qos = 0;
|
|
||||||
QString server = "127.0.0.1"; //"127.0.0.1";
|
|
||||||
int serverPort = 1883;
|
|
||||||
int keepAlive = 60;
|
|
||||||
int protocolVersion = 4;
|
|
||||||
int retryConnectionDelay = 10; // in seconds
|
|
||||||
int retrySubscribeDelay = 10; // in seconds
|
|
||||||
QMqttClient *qmqttClient;
|
|
||||||
|
|
||||||
QHash <qint32, QString> messagesList;
|
|
||||||
QMqttSubscription *subscription = nullptr;
|
|
||||||
QMqttSubscription::SubscriptionState subscriptionState;
|
|
||||||
QMqttClient::ClientState clientState;
|
|
||||||
|
|
||||||
qint32 send_message(QString message);
|
|
||||||
|
|
||||||
QHash <int , QString> messageStatus =
|
|
||||||
{
|
|
||||||
{0, "Unknown : The message status is unknown."},
|
|
||||||
{1, "Published : The client received a message for one of its subscriptions. This applies to QoS levels 1 and 2."},
|
|
||||||
{2, "Acknowledged A message has been acknowledged. This applies to QoS 1 and states that the message handling has been finished from the client side."},
|
|
||||||
{3, "Received : A message has been received. This applies to QoS 2."},
|
|
||||||
{4, "Released : A message has been released. This applies to QoS 2. For a publisher the message handling has been finished."},
|
|
||||||
{5, "Completed : A message has been completed. This applies to QoS 2 and states that the message handling has been finished from the client side."}
|
|
||||||
};
|
|
||||||
|
|
||||||
QHash <int, QString> connectReasonCode =
|
|
||||||
{
|
|
||||||
{0, "SubscriptionQoSLevel0 : subscription with QoS level 0 has been created."},
|
|
||||||
{1 , "SubscriptionQoSLevel1 0x01 A subscription with QoS level 1 has been created."},
|
|
||||||
{2 , "SubscriptionQoSLevel2 0x02 A subscription with QoS level 2 has been created."},
|
|
||||||
{16 , "NoMatchingSubscriber 0x10 The message has been accepted by the server, but there are no subscribers to receive this message. A broker may send this reason code instead of Success."},
|
|
||||||
{17 , "NoSubscriptionExisted (since Qt 5.15) 0x11 No matching Topic Filter is being used by the Client."},
|
|
||||||
{24 , "ContinueAuthentication (since Qt 5.15) 0x18 Continue the authentication with another step."},
|
|
||||||
{25 , "ReAuthenticate (since Qt 5.15) 0x19 Initiate a re-authentication."},
|
|
||||||
{128 , "UnspecifiedError 0x80 An unspecified error occurred."},
|
|
||||||
{129 , "MalformedPacket 0x81 The packet sent to the server is invalid."},
|
|
||||||
{130 , "ProtocolError 0x82 A protocol error has occurred. In most cases, this will cause the server to disconnect the client."},
|
|
||||||
{131 , "ImplementationSpecificError 0x83 The packet is valid, but the recipient rejects it."},
|
|
||||||
{132 , "UnsupportedProtocolVersion 0x84 The requested protocol version is not supported by the server."},
|
|
||||||
{133 , "InvalidClientId 0x85 The client ID is invalid."},
|
|
||||||
{134 , "InvalidUserNameOrPassword 0x86 The username or password specified is invalid."},
|
|
||||||
{135 , "NotAuthorized 0x87 The client is not authorized for the specified action."},
|
|
||||||
{136 , "ServerNotAvailable 0x88 The server to connect to is not available."},
|
|
||||||
{137 , "ServerBusy 0x89 The server to connect to is not available. The client is asked to try at a later time."},
|
|
||||||
{138 , "ClientBanned 0x8A The client has been banned from the server."},
|
|
||||||
{139 , "InvalidAuthenticationMethod 0x8C The authentication method specified is invalid."},
|
|
||||||
{143 , "InvalidTopicFilter 0x8F The topic filter specified is invalid."},
|
|
||||||
{144 , "InvalidTopicName 0x90 The topic name specified is invalid."},
|
|
||||||
{145 , "MessageIdInUse 0x91 The message ID used in the previous packet is already in use."},
|
|
||||||
{146 , "MessageIdNotFound 0x92 The message ID used in the previous packet has not been found."},
|
|
||||||
{149 , "PacketTooLarge 0x95 The packet received is too large. See also QMqttServerConnectionProperties::maximumPacketSize()."},
|
|
||||||
{151 , "QuotaExceeded 0x97 An administratively imposed limit has been exceeded."},
|
|
||||||
{153 , "InvalidPayloadFormat 0x99 The payload format is invalid. See also QMqttPublishProperties::payloadFormatIndicator()."},
|
|
||||||
{154 , "RetainNotSupported 0x9A The server does not support retained messages. See also QMqttServerConnectionProperties::retainAvailable()."},
|
|
||||||
{155 , "QoSNotSupported 0x9B The QoS level requested is not supported. See also QMqttServerConnectionProperties::maximumQoS()."},
|
|
||||||
{156 , "UseAnotherServer 0x9C The server the client tries to connect to is not available. See also QMqttServerConnectionProperties::serverReference()."},
|
|
||||||
{157 , "ServerMoved 0x9D The server the client tries to connect to has moved to a new address. See also QMqttServerConnectionProperties::serverReference()."},
|
|
||||||
{158 , "SharedSubscriptionsNotSupported 0x9E Shared subscriptions are not supported. See also QMqttServerConnectionProperties::sharedSubscriptionSupported()."},
|
|
||||||
{159 , "ExceededConnectionRate 0x9F The connection rate limit has been exceeded."},
|
|
||||||
{161 , "SubscriptionIdsNotSupported 0xA1 Subscription IDs are not supported. See also QMqttServerConnectionProperties::subscriptionIdentifierSupported()."},
|
|
||||||
{162 , "WildCardSubscriptionsNotSupported 0xA2 Subscriptions using wildcards are not supported by the server. See also QMqttServerConnectionProperties::wildcardSupported()."},
|
|
||||||
};
|
|
||||||
|
|
||||||
QHash <int, QString> qmqttErrors =
|
|
||||||
{
|
|
||||||
{1, "InvalidProtocolVersion : The broker does not accept a connection using the specified protocol version."},
|
|
||||||
{2, "IdRejected ; The client ID is malformed. This might be related to its length."},
|
|
||||||
{3, "ServerUnavailable :The network connection has been established, but the service is unavailable on the broker side."},
|
|
||||||
{4, "QMqttClient::Bad Username Or Password : The data in the username or password is malformed."},
|
|
||||||
{5, "NotAuthorized : The client is not authorized to connect."},
|
|
||||||
{256, "TransportInvalid : The underlying transport caused an error. For example, the connection might have been interrupted unexpectedly."},
|
|
||||||
{257, "ProtocolViolation : The client encountered a protocol violation, and therefore closed the connection."},
|
|
||||||
{258, "UnknownError : An unknown error occurred."},
|
|
||||||
{259, "Mqtt5SpecificError : The error is related to MQTT protocol level 5. A reason code might provide more details."}
|
|
||||||
};
|
|
||||||
|
|
||||||
QHash <int, QString> clientStateString
|
|
||||||
{
|
|
||||||
{0, "Disconnected : The client is disconnected from the broker."},
|
|
||||||
{1, "Connecting : A connection request has been made, but the broker has not approved the connection yet."},
|
|
||||||
{2, "Connected : The client is connected to the broker."}
|
|
||||||
};
|
|
||||||
|
|
||||||
QHash <int, QString> subscriptionStateString
|
|
||||||
{
|
|
||||||
{0, "Unsubscribed : The topic has been unsubscribed from."},
|
|
||||||
{1, "SubscriptionPending : A request for a subscription has been sent, but is has not been confirmed by the broker yet."},
|
|
||||||
{2, "Subscribed : The subscription was successful and messages will be received."},
|
|
||||||
{3, "UnsubscriptionPending : A request to unsubscribe from a topic has been sent, but it has not been confirmed by the broker yet."},
|
|
||||||
{4, "Error : An error occured."}
|
|
||||||
};
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void on_message(const QByteArray &message, const QMqttTopicName &topic);
|
|
||||||
void on_connect();
|
|
||||||
void on_disconnect();
|
|
||||||
void on_error(QMqttClient::ClientError error);
|
|
||||||
void on_messageSentStatusChanged(qint32 id, QMqtt::MessageStatus s, const QMqttMessageStatusProperties &properties);
|
|
||||||
void on_messageSent(int mid);
|
|
||||||
void updateStatus(QMqttSubscription::SubscriptionState state);
|
|
||||||
void on_stateChanged(QMqttClient::ClientState state);
|
|
||||||
// void updateMessage();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif //MQTT_H
|
|
||||||
778
pws2mqtt.cpp
778
pws2mqtt.cpp
@@ -1,778 +0,0 @@
|
|||||||
#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>
|
|
||||||
#include <QImage>
|
|
||||||
#include <QTransform>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include "utcicalculator.h"
|
|
||||||
|
|
||||||
extern MqttClient *mqttClient;
|
|
||||||
extern Pws2mqtt *pws2mqtt;
|
|
||||||
extern QHttpServer *httpServer;
|
|
||||||
UtciCalculator calc;
|
|
||||||
|
|
||||||
QStringList previsionList
|
|
||||||
{
|
|
||||||
"Temps variable (incertain).", \
|
|
||||||
"Beau temps stable (anticyclone).", \
|
|
||||||
"Beau temps en baisse (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).", \
|
|
||||||
"Temps stable"
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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, QString> propertyList;
|
|
||||||
QMap <QByteArray, QString> precPropertyList;
|
|
||||||
QMap <QByteArray, double> propertiesValue;
|
|
||||||
QMap <QByteArray, QPair<QString, QByteArray>> propertyName
|
|
||||||
{
|
|
||||||
{"tempf", {"Température extérieure", "°C"}},
|
|
||||||
{"humidity", {"Humidité extérieure", "%"}},
|
|
||||||
{"dewptf", {"Point de rosée", "°C"}},
|
|
||||||
{"windchill", {"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", {"Alerte UV", ""}}
|
|
||||||
};
|
|
||||||
|
|
||||||
QList <QList <QVariant>> forceVent
|
|
||||||
{
|
|
||||||
// { max wind speed, name, priority}
|
|
||||||
{1, "Temps calme", 1},
|
|
||||||
{5, "Très légère brise", 1},
|
|
||||||
{11, "Légère brise", 1},
|
|
||||||
{19, "Petite brise", 1},
|
|
||||||
{28, "Jolie brise", 1},
|
|
||||||
{38, "Bonne brise", 1},
|
|
||||||
{49, "Vent frais", 3},
|
|
||||||
{61, "Grand vent", 4},
|
|
||||||
{74, "Vent fort", 5},
|
|
||||||
{88, "Vent violent", 5},
|
|
||||||
{117, "Tempête", 5},
|
|
||||||
{1000, "Ouragan", 5}
|
|
||||||
};
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
debug(DEBUGMACRO, "Listening on port " + QString::number(port));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Pws2mqtt::parseData(QList<std::pair<QString, QString>> queryList)
|
|
||||||
{
|
|
||||||
debug(DEBUGMACRO, "Parsing Datas", DEBUG);
|
|
||||||
|
|
||||||
QString jsonString = "{";
|
|
||||||
QString deviceString = "\"device\": {\"ieeeAddress\": \"" + mqttClient->macAddress + "\", \"type\": \"" + mqttClient->type + "\", \"powerSource\": \"Battery\"";
|
|
||||||
QString notif = "";
|
|
||||||
QStringList priorityList {"", "min", "low", "default", "High", "urgent"};
|
|
||||||
QString attachment = "";
|
|
||||||
quint8 priority = 2;
|
|
||||||
double propertyValue = 0;
|
|
||||||
bool propertyFlag = false;
|
|
||||||
bool deviceFlag = false;
|
|
||||||
static QDateTime timer = QDateTime::currentDateTime().addSecs(-2000);
|
|
||||||
|
|
||||||
debug(DEBUGMACRO, "looping list of query", DEBUG);
|
|
||||||
|
|
||||||
for (QPair<QString, QString> pair : queryList)
|
|
||||||
{
|
|
||||||
debug(DEBUGMACRO, pair.first + " = " + pair.second, DEBUG);
|
|
||||||
QByteArray name = pair.first.toLatin1();
|
|
||||||
QByteArray value = pair.second.toLatin1();
|
|
||||||
float fValue = value.toDouble();
|
|
||||||
|
|
||||||
if (this->deviceProperties.contains(name))
|
|
||||||
{
|
|
||||||
if(deviceFlag == false)
|
|
||||||
{
|
|
||||||
deviceFlag = true;
|
|
||||||
}else
|
|
||||||
{
|
|
||||||
deviceString.append(", ");
|
|
||||||
}
|
|
||||||
deviceString += "\"" + name + "\": ";
|
|
||||||
deviceString += pair.second;
|
|
||||||
}else
|
|
||||||
{
|
|
||||||
if(propertyFlag == false)
|
|
||||||
{
|
|
||||||
propertyFlag = true;
|
|
||||||
}else
|
|
||||||
{
|
|
||||||
jsonString.append(", ");
|
|
||||||
}
|
|
||||||
jsonString.append("\"" + name + "\": ");
|
|
||||||
jsonString.append(pair.second);
|
|
||||||
|
|
||||||
if (name == "tempf")
|
|
||||||
{
|
|
||||||
static QDateTime timeTemp = QDateTime::currentDateTime().addSecs(-600);
|
|
||||||
propertyValue = round(fahrenheitToCelsius(fValue));
|
|
||||||
debug (DEBUGMACRO, "timeTemp = " + timeTemp.time().toString() + " - current :" + QDateTime::currentDateTime().toString() , DEBUG);
|
|
||||||
|
|
||||||
if (compare (propertiesValue[name], propertyValue, 1) and timeTemp < QDateTime::currentDateTime())
|
|
||||||
{
|
|
||||||
//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));
|
|
||||||
propertiesValue[name] = propertyValue;
|
|
||||||
}
|
|
||||||
}else if (name == "humidity")
|
|
||||||
{
|
|
||||||
propertyValue = value.toFloat();
|
|
||||||
debug (DEBUGMACRO, name + " : " + QByteArray::number(propertyValue), DEBUG);
|
|
||||||
|
|
||||||
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));
|
|
||||||
propertiesValue[name] = propertyValue;
|
|
||||||
}
|
|
||||||
/*}else if (name == "windchillf")
|
|
||||||
{
|
|
||||||
static QDateTime timeWindchill = QDateTime::currentDateTime().addSecs(-600);
|
|
||||||
propertyValue = round(fahrenheitToCelsius(value.toFloat()));
|
|
||||||
debug (DEBUGMACRO, name + " : " + QByteArray::number(propertyValue), DEBUG);
|
|
||||||
if (compare (propertiesValue[name], propertyValue, 1) and timeWindchill < QDateTime::currentDateTime())
|
|
||||||
{
|
|
||||||
//notif += formatNotifString (propertyName[name].first, propertyName[name].second , QByteArray::number(qPow(propertyValue, 1.0)));
|
|
||||||
//debug (DEBUGMACRO, "Notif = #" + notif + "#", DEBUG);
|
|
||||||
timeWindchill = timeWindchill.currentDateTime().addSecs(300);
|
|
||||||
propertyList[name] = formatNotifString(propertyName[name].first, propertyName[name].second, QByteArray::number(propertyValue));
|
|
||||||
propertiesValue[name] = propertyValue;
|
|
||||||
}*/
|
|
||||||
}else if (name == "winddir")
|
|
||||||
{
|
|
||||||
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));
|
|
||||||
propertiesValue[name] = propertyValue;
|
|
||||||
}else if (name == "windspeedmph" or name == "windgustmph")
|
|
||||||
{
|
|
||||||
QString msg = "";
|
|
||||||
static QDateTime timeWind = QDateTime::currentDateTime().addSecs(-600);
|
|
||||||
quint8 windPriority = 1;
|
|
||||||
|
|
||||||
propertiesValue["vent"] = round(mphTokmh(value.toFloat()));
|
|
||||||
|
|
||||||
debug (DEBUGMACRO, name + " : " + QByteArray::number(propertiesValue["vent"]), DEBUG);
|
|
||||||
|
|
||||||
for (quint8 i=0; i<forceVent.size();i++)
|
|
||||||
{
|
|
||||||
if (propertiesValue["vent"] <= forceVent[i][0].toDouble())
|
|
||||||
{
|
|
||||||
msg = forceVent[i][1].toString();
|
|
||||||
windPriority = forceVent[i][2].toUInt();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (propertyList["forcevent"] != msg or propertyList["rafales"] != msg)
|
|
||||||
{
|
|
||||||
if (name == "windspeedmph")
|
|
||||||
{
|
|
||||||
//propertyList["forcevent"] = msg;
|
|
||||||
propertyList[name] = formatNotifString ("Vent - " + msg, propertyName[name].second , QByteArray::number(propertiesValue["vent"]));
|
|
||||||
}else
|
|
||||||
{
|
|
||||||
propertyList[name] = formatNotifString (propertyName[name].first, propertyName[name].second , QByteArray::number(propertiesValue["vent"]));
|
|
||||||
}
|
|
||||||
setPriority(priority, windPriority);
|
|
||||||
|
|
||||||
attachment = this->outputPath;
|
|
||||||
}
|
|
||||||
}else if (name == "rainin")
|
|
||||||
{
|
|
||||||
static double ecart;
|
|
||||||
quint8 raininPriority = 1;
|
|
||||||
propertyValue = round(pair.second.toFloat()*100)/100;
|
|
||||||
if (propertyValue == 0)
|
|
||||||
{
|
|
||||||
ecart = 0.0001;
|
|
||||||
}else
|
|
||||||
{
|
|
||||||
ecart = 0.01;
|
|
||||||
}
|
|
||||||
debug (DEBUGMACRO, name + " : " + QByteArray::number(propertyValue), DEBUG);
|
|
||||||
if (compare (propertiesValue[name], propertyValue, ecart))
|
|
||||||
{
|
|
||||||
QString pluviosite = getPluviosite(propertyValue, raininPriority);
|
|
||||||
priority = setPriority (priority, raininPriority);
|
|
||||||
//debug (DEBUGMACRO, "Notif = #" + notif + "#", DEBUG);
|
|
||||||
propertyList[name] = formatNotifString(pluviosite + " : ", propertyName[name].second, QByteArray::number(propertyValue));
|
|
||||||
propertiesValue[name] = propertyValue;
|
|
||||||
}
|
|
||||||
}else if (name == "dailyrainin")
|
|
||||||
{
|
|
||||||
static double ecart;
|
|
||||||
propertyValue = round(pair.second.toFloat()*100)/100;
|
|
||||||
if (propertyValue == 0)
|
|
||||||
{
|
|
||||||
ecart = 0.0001;
|
|
||||||
}else
|
|
||||||
{
|
|
||||||
ecart = 0.01;
|
|
||||||
}
|
|
||||||
debug (DEBUGMACRO, name + " : " + QByteArray::number(propertyValue), DEBUG);
|
|
||||||
if (compare (propertiesValue[name], propertyValue, ecart))
|
|
||||||
{
|
|
||||||
priority = setPriority (priority, 3);
|
|
||||||
//debug (DEBUGMACRO, "Notif = #" + notif + "#", DEBUG);
|
|
||||||
propertyList[name] = formatNotifString(propertyName[name].first, propertyName[name].second, QByteArray::number(propertyValue));
|
|
||||||
propertiesValue[name] = propertyValue;
|
|
||||||
}
|
|
||||||
}else if (name == "baromin")
|
|
||||||
{
|
|
||||||
//static QDateTime timePrevision = QDateTime::currentDateTime().addSecs(-1000);
|
|
||||||
|
|
||||||
propertyValue = tohPa(pair.second.toFloat());
|
|
||||||
debug (DEBUGMACRO, "Barometre en hPa : " + QByteArray::number(propertyValue), DEBUG);
|
|
||||||
if (compare (propertiesValue[name], propertyValue, 0.5))
|
|
||||||
{
|
|
||||||
//notif += formatNotifString (propertyName[name].first, propertyName[name].second , QByteArray::number(propertyValue));
|
|
||||||
//debug (DEBUGMACRO, "Notif = #" + notif + "#", DEBUG);
|
|
||||||
propertyList[name] = formatNotifString(propertyName[name].first, "", QByteArray::number(propertyValue));
|
|
||||||
propertiesValue[name] = propertyValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
static QString prevision;
|
|
||||||
quint8 prevPriority = 0;
|
|
||||||
QString ret = pressureVariation(propertyValue, prevPriority);
|
|
||||||
if (!ret.isEmpty())
|
|
||||||
{
|
|
||||||
debug (DEBUGMACRO, "baromin ret not empty : " + ret, DEBUG);
|
|
||||||
QString newPrevision = ret;
|
|
||||||
priority = setPriority(priority, prevPriority);
|
|
||||||
if (prevision != newPrevision)
|
|
||||||
{
|
|
||||||
prevision = newPrevision;
|
|
||||||
propertyList["prevision"] = "- " + prevision + " \n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}else if (name == "UV")
|
|
||||||
{
|
|
||||||
static QDateTime timeUV = QDateTime::currentDateTime().addSecs(-600);
|
|
||||||
propertyValue = pair.second.toFloat();
|
|
||||||
|
|
||||||
debug (DEBUGMACRO, name + " : " + QByteArray::number(propertyValue), DEBUG);
|
|
||||||
if (compare (propertiesValue[name], propertyValue, 1) and timeUV < QDateTime::currentDateTime())
|
|
||||||
{
|
|
||||||
//notif += formatNotifString (propertyName[name].first, propertyName[name].second , value);
|
|
||||||
if (propertyValue == 5 )
|
|
||||||
priority = setPriority (priority, 4);
|
|
||||||
if (propertyValue >= 6 )
|
|
||||||
priority = setPriority (priority, 5);
|
|
||||||
timeUV = timeUV.currentDateTime().addSecs(300);
|
|
||||||
propertyList[name] = formatNotifString(propertyName[name].first, "", QByteArray::number(propertyValue));
|
|
||||||
propertiesValue[name] = propertyValue;
|
|
||||||
debug (DEBUGMACRO, "Notif = #" + notif + "#", DEBUG);
|
|
||||||
}
|
|
||||||
}else if (name == "solarradiation")
|
|
||||||
{
|
|
||||||
propertiesValue[name] = round(pair.second.toFloat()*100/100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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"]));
|
|
||||||
windChill(propertiesValue["tempf"], propertiesValue["vent"]);
|
|
||||||
calculerHumidex(propertiesValue["tempf"], propertiesValue["humidity"]);
|
|
||||||
|
|
||||||
if (!jsonString.isEmpty())
|
|
||||||
{
|
|
||||||
debug(DEBUGMACRO, "json string : " + jsonString, DEBUG);
|
|
||||||
jsonString = jsonString +", " + deviceString + "}}";
|
|
||||||
mqttClient->send_message(jsonString);
|
|
||||||
}else
|
|
||||||
{
|
|
||||||
debug(DEBUGMACRO, "No values to send", DEBUG);
|
|
||||||
}
|
|
||||||
|
|
||||||
debug(DEBUGMACRO, "current datetime : " + QDateTime::currentDateTime().toString() + ", timer = " + timer.toString(), DEBUG);
|
|
||||||
|
|
||||||
if (priority > 3 or QDateTime::currentDateTime() > timer.addSecs(1800))
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
notif += propertyList[name];
|
|
||||||
}
|
|
||||||
debug(DEBUGMACRO, "calling notify with notif = #" + notif + "#", DEBUG);
|
|
||||||
if (changed)
|
|
||||||
{
|
|
||||||
notify (notif, priorityList[priority], attachment);
|
|
||||||
precPropertyList = propertyList;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
debug(DEBUGMACRO, "parseData: Returning", DEBUG);
|
|
||||||
}
|
|
||||||
|
|
||||||
double calculerUTCI(double temperature, double humiditeRelative, double vitesseVent, double rayonnement)
|
|
||||||
{
|
|
||||||
double e = (humiditeRelative / 100.0) * 6.105 * exp((17.27 * temperature) / (237.7 + temperature));
|
|
||||||
debug(DEBUGMACRO, "UTCI => e = " + QString::number(e), DEBUG);
|
|
||||||
double V = vitesseVent * 5.0 / 18.0; // Vitesse du vent en m/s
|
|
||||||
debug(DEBUGMACRO, "UTCI => V = " + QString::number(V), DEBUG);
|
|
||||||
|
|
||||||
double utci = temperature +
|
|
||||||
(0.607562 + 0.022437 * e + 7.3869e-4 * temperature * e - 3.5582e-6 * temperature * temperature * e) +
|
|
||||||
(-11.4 + 0.11 * temperature + 0.55 * V) +
|
|
||||||
(0.0023 * temperature * temperature - 0.118 * V - 0.0014 * temperature * V);
|
|
||||||
debug(DEBUGMACRO, "UTCI = " + QString::number(utci), DEBUG);
|
|
||||||
|
|
||||||
// Ajustement simplifié pour le rayonnement
|
|
||||||
double ref = 200.0; // Rayonnement de référence (W/m²)
|
|
||||||
double k = 0.02; // Coefficient empirique
|
|
||||||
utci = utci + k * (rayonnement - ref);
|
|
||||||
debug(DEBUGMACRO, "UTCI = " + QString::number(utci), DEBUG);
|
|
||||||
return round(utci * 100) / 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
double windChill(double airT, double vent)
|
|
||||||
{
|
|
||||||
double w = 0;
|
|
||||||
|
|
||||||
if (airT > 10 or vent < 4.8)
|
|
||||||
{
|
|
||||||
return airT;
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
w = 13.12 + 0.6215 * airT - 11.37 * pow(vent, 0.16) + 0.3965 * airT * pow(vent, 0.16);
|
|
||||||
debug(DEBUGMACRO, "windChill = " + QString::number(w), DEBUG);
|
|
||||||
return w;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
double calculerHumidex(double temperature, double humiditeRelative)
|
|
||||||
{
|
|
||||||
// Calcul de la température du point de rosée (T_rosée)
|
|
||||||
double T_rosee = (243.04 * (log(humiditeRelative / 100.0) + (17.625 * temperature) / (243.04 + temperature))) / (17.625 - (log(humiditeRelative / 100.0) + (17.625 * temperature) / (243.04 + temperature)));
|
|
||||||
|
|
||||||
// Calcul de l'Humidex (H)
|
|
||||||
double H = temperature + 0.5555 * (6.11 * exp(5417.7530 * (1.0 / 273.16 - 1.0 / (273.15 + T_rosee))) - 10.0);
|
|
||||||
debug(DEBUGMACRO, "humidex = " + QString::number(H), DEBUG);
|
|
||||||
|
|
||||||
return H;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString getPluviosite(double value, quint8 &priority)
|
|
||||||
{
|
|
||||||
if (value < 1)
|
|
||||||
{
|
|
||||||
return "Pluie fine, bruine";
|
|
||||||
}else if (value < 5)
|
|
||||||
{
|
|
||||||
return "Pluie Modérée";
|
|
||||||
}else if (value < 10)
|
|
||||||
{
|
|
||||||
priority = 4;
|
|
||||||
return "Pluie forte";
|
|
||||||
}else if (value < 30)
|
|
||||||
{
|
|
||||||
priority = 5;
|
|
||||||
return "Pluie Très forte, risque d'inondation";
|
|
||||||
}else
|
|
||||||
{
|
|
||||||
priority = 5;
|
|
||||||
return "Pluie diluvienne, risque élevé d'inondations, crues";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString previsionMeteo(double currentPressure, double variation3h, quint8 &priority)
|
|
||||||
{
|
|
||||||
QString variation = "";
|
|
||||||
|
|
||||||
priority = 3;
|
|
||||||
debug (DEBUGMACRO, "begin", DEBUG);
|
|
||||||
|
|
||||||
if (variation3h == 0.0)
|
|
||||||
{
|
|
||||||
variation = " - temps stable";
|
|
||||||
}else if (variation3h > 0.2)
|
|
||||||
{
|
|
||||||
variation = " en amélioration rapide";
|
|
||||||
}else if (variation3h < -2.0)
|
|
||||||
{
|
|
||||||
variation = "en dégradation rapide";
|
|
||||||
}else if (variation3h > 0)
|
|
||||||
{
|
|
||||||
variation = " en amélioration";
|
|
||||||
}else if (variation3h <0)
|
|
||||||
{
|
|
||||||
variation = " en dégradation";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentPressure > 1020.0)
|
|
||||||
{
|
|
||||||
return "Beau temps (anticyclone)" + variation;
|
|
||||||
} else if (currentPressure > 1010.0 && currentPressure <= 1020.0)
|
|
||||||
{
|
|
||||||
return "Éclaircies" + variation;
|
|
||||||
} else if (currentPressure > 1000.0 && currentPressure <= 1010.0)
|
|
||||||
{
|
|
||||||
priority = 4;
|
|
||||||
return "Temps variable, risque de pluie légère" + variation;
|
|
||||||
} else if (currentPressure >=990 && currentPressure <= 1000.0)
|
|
||||||
{
|
|
||||||
priority = 5;
|
|
||||||
return "Pluie/vent/orage" + variation;
|
|
||||||
} else if (currentPressure < 990.0)
|
|
||||||
{
|
|
||||||
priority = 5;
|
|
||||||
return "Tempête ou dépression forte (vigilance)" + variation;
|
|
||||||
}
|
|
||||||
return variation;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString pressureVariation(double currentPressure, quint8 &priority)
|
|
||||||
{
|
|
||||||
const int NB_MESURES_3H = 360; // 3h × 120 mesures/heure (1 mesure toutes les 30s)
|
|
||||||
static QVector<double> historiquePressions;
|
|
||||||
debug(DEBUGMACRO, "PressureVariation : current pressure = " + QByteArray::number(currentPressure), DEBUG);
|
|
||||||
|
|
||||||
historiquePressions.append(currentPressure);
|
|
||||||
|
|
||||||
// Si on a assez de mesures pour couvrir 3h
|
|
||||||
if (historiquePressions.size() > NB_MESURES_3H)
|
|
||||||
{
|
|
||||||
debug(DEBUGMACRO, "historiquePressions > " + QByteArray::number(NB_MESURES_3H), DEBUG);
|
|
||||||
historiquePressions.erase(historiquePressions.begin());
|
|
||||||
}
|
|
||||||
debug(DEBUGMACRO, "taille de historiquePression : " + QByteArray::number(historiquePressions.size()), DEBUG);
|
|
||||||
// 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
|
|
||||||
QString prevision = previsionMeteo(pressionFinale, variation, priority);
|
|
||||||
debug(DEBUGMACRO, "Prévision : " + prevision, DEBUG);
|
|
||||||
|
|
||||||
// Exemple : Envoi d'une alerte si nécessaire
|
|
||||||
|
|
||||||
debug(DEBUGMACRO, "ending pressureVariation with result", DEBUG);
|
|
||||||
return prevision;
|
|
||||||
}else
|
|
||||||
{
|
|
||||||
static QTime time = QTime::currentTime().addSecs(NB_MESURES_3H * 30);
|
|
||||||
return "Première prévision à " + time.toString();
|
|
||||||
}
|
|
||||||
debug(DEBUGMACRO, "ending pressureVariation no result", DEBUG);
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
double tohPa (double value)
|
|
||||||
{
|
|
||||||
return round(value * 33.8639 * 100) / 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
double mphTokmh (double value)
|
|
||||||
{
|
|
||||||
return value * 1.60934;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool compare (double value, double currentValue, double 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 (QString name, QString unit ,QByteArray value)
|
|
||||||
{
|
|
||||||
QString text = "- " + name + " : " + value;
|
|
||||||
if (!unit.isEmpty())
|
|
||||||
{
|
|
||||||
text += unit;
|
|
||||||
//"- " + propertyName[name].first + " : " + value + " " + propertyName[name].second + " \n";
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void notify(QString notif, QString priority, QString inputPath)
|
|
||||||
{
|
|
||||||
CURL *curl;
|
|
||||||
CURLcode res;
|
|
||||||
std::string readBuffer;
|
|
||||||
|
|
||||||
priority = "Priority: " + priority;
|
|
||||||
debug (DEBUGMACRO, "notifying at priority " + priority + " - 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;
|
|
||||||
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");
|
|
||||||
|
|
||||||
/*curl_mime *mime;
|
|
||||||
curl_mimepart *part;
|
|
||||||
|
|
||||||
// Initialise la structure mime
|
|
||||||
mime = curl_mime_init(curl);
|
|
||||||
|
|
||||||
// Ajoute le champ "message" (ton texte de notification)
|
|
||||||
part = curl_mime_addpart(mime);
|
|
||||||
curl_mime_name(part, "message");
|
|
||||||
curl_mime_data(part, notif.toUtf8().constData(), CURL_ZERO_TERMINATED);
|
|
||||||
*/
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
// 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, "Notification envoyée avec succès", 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);
|
|
||||||
if (image.isNull())
|
|
||||||
{
|
|
||||||
debug (DEBUGMACRO, "Impossible de charger l'image : " + inputPath, WARNING);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Crée une transformation pour pivoter l'image
|
|
||||||
QTransform transform;
|
|
||||||
// Décale le centre de l'image à l'origine
|
|
||||||
transform.translate(-image.width() / 2.0, -image.height() / 2.0);
|
|
||||||
// Applique la rotation
|
|
||||||
transform.rotate(angle);
|
|
||||||
// Remet le centre à sa position d'origine
|
|
||||||
transform.translate(image.width() / 2.0, image.height() / 2.0);
|
|
||||||
|
|
||||||
// Applique la transformation
|
|
||||||
QImage rotatedImage = image.transformed(transform, Qt::SmoothTransformation);
|
|
||||||
|
|
||||||
// Enregistre l'image pivotée
|
|
||||||
if (!rotatedImage.save(outputPath))
|
|
||||||
{
|
|
||||||
debug (DEBUGMACRO, "Impossible d'enregistrer l'image : ", WARNING);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
68
pws2mqtt.h
68
pws2mqtt.h
@@ -1,68 +0,0 @@
|
|||||||
#ifndef PWS2MQTT_H
|
|
||||||
#define PWS2MQTT_H
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
#include <QByteArray>
|
|
||||||
#include <QObject>
|
|
||||||
#include <QtMqtt>
|
|
||||||
#include <QtMqtt/QMqttClient>
|
|
||||||
|
|
||||||
#define RED "\e[31m"
|
|
||||||
#define GREEN "\e[32m"
|
|
||||||
#define BLUE "\e[94m"
|
|
||||||
#define ORANGE "\e[33m"
|
|
||||||
#define NORMAL "\e[0m"
|
|
||||||
|
|
||||||
#define DEBUG 16 // => 16
|
|
||||||
#define INFO 1 // => 1
|
|
||||||
#define NOTICE 2 // => 2
|
|
||||||
#define WARNING 4 // => 4
|
|
||||||
#define ERROR 8 // => 8
|
|
||||||
#define ALERT 32
|
|
||||||
#define ALL DEBUG | INFO | NOTICE | WARNING | ERROR | ALERT
|
|
||||||
#define DEBUGMACRO QString(__FILE__) + ": " + QString::number(__LINE__) + " -> "
|
|
||||||
|
|
||||||
void debug(QString debugHeader, QString msg, uint8_t level=ALL, QByteArray value="");
|
|
||||||
//QString addValue(QByteArray value);
|
|
||||||
|
|
||||||
class Pws2mqtt : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
Pws2mqtt();
|
|
||||||
~Pws2mqtt();
|
|
||||||
|
|
||||||
QString ProcName = "pws2mqtt"; // name of the proceesus in ps, top, pstree, ...;
|
|
||||||
FILE * logFh = nullptr;
|
|
||||||
QString listenHost = "0.0.0.0";
|
|
||||||
uint listenPort = 5000;
|
|
||||||
int sockfd = 0;
|
|
||||||
int newsockfd = 0;
|
|
||||||
QString inputPath = "/usr/local/share/pws2mqtt/fleche.png";
|
|
||||||
QString outputPath = "/tmp/pws2mqttFleche.png";
|
|
||||||
QList <QString> deviceProperties =
|
|
||||||
{
|
|
||||||
"ieeeAddress", "type", "dateutc", "softwaretype", "action", "realtime", "freq", "wh65batt", "wh25batt", "runtime"
|
|
||||||
};
|
|
||||||
|
|
||||||
void init();
|
|
||||||
void listeningHttp();
|
|
||||||
void parseData(QList<std::pair<QString, QString>> queryList);
|
|
||||||
};
|
|
||||||
|
|
||||||
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="");
|
|
||||||
double mphTokmh (double value);
|
|
||||||
void notify (QString notif, QString priority = "low", QString inputPath = "");
|
|
||||||
quint8 setPriority (quint8 currentPriority, quint8 newPriority);
|
|
||||||
QString previsionMeteo(double currentPressure, double variation3h, quint8 &priority);
|
|
||||||
QString pressureVariation(double currentPressure, quint8 &priority);
|
|
||||||
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);
|
|
||||||
|
|
||||||
#endif // PWS2MQTT_H
|
|
||||||
@@ -1,178 +0,0 @@
|
|||||||
#include "utcicalculator.h"
|
|
||||||
#include <QFile>
|
|
||||||
#include <QTextStream>
|
|
||||||
#include <QtMath>
|
|
||||||
#include <QRegularExpression>
|
|
||||||
|
|
||||||
UtciCalculator::UtciCalculator(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<double> 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
|
|
||||||
}
|
|
||||||
|
|
||||||
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 UtciCalculator::evalOffset(double Ta, double va, double tm, double pa) const
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
|
|
||||||
QVector<double> 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;
|
|
||||||
|
|
||||||
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[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[25] = va5;
|
|
||||||
terms[26] = Ta*va5;
|
|
||||||
|
|
||||||
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[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[44] = va3*tm;
|
|
||||||
terms[45] = Ta*va3*tm;
|
|
||||||
terms[46] = Ta2*va3*tm;
|
|
||||||
|
|
||||||
terms[47] = va4*tm;
|
|
||||||
terms[48] = Ta*va4*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;
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
// 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];
|
|
||||||
|
|
||||||
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
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QVector>
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
/*
|
|
||||||
UtciCalculator
|
|
||||||
- lit un fichier de coefficients (format ESM_3/tab-delimited)
|
|
||||||
- calcule UTCI via l'approximation polynomiale : UTCI = Ta + Offset(Ta, va, tm, pa)
|
|
||||||
where tm = Tr - Ta and pa is water vapour pressure in kPa.
|
|
||||||
- fournit une fonction utilitaire pour calculer pa depuis Ta et relative humidity (Magnus-Tetens).
|
|
||||||
*/
|
|
||||||
|
|
||||||
class UtciCalculator : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit UtciCalculator(QObject *parent = nullptr);
|
|
||||||
|
|
||||||
// Load coefficients from a file (ESM_3 table or exported txt)
|
|
||||||
// Return true on success.
|
|
||||||
//bool loadCoefficientsFromFile(const QString &path);
|
|
||||||
|
|
||||||
// Calculate UTCI.
|
|
||||||
// Ta: air temperature (°C)
|
|
||||||
// va: wind speed at 10 m (m/s)
|
|
||||||
// Tr: mean radiant temperature (°C)
|
|
||||||
// rh: relative humidity in % (0..100). If rh < 0, supply pa directly (next param) and set pa_provided=true.
|
|
||||||
// pa_kpa: vapour pressure in kPa (used if pa_provided==true)
|
|
||||||
double computeUTCI(double Ta, double va, double Tr, double rh) const;
|
|
||||||
|
|
||||||
// Utility: compute water vapour pressure (kPa) from Ta and relative humidity (Magnus formula)
|
|
||||||
// Ta in °C, rh in %
|
|
||||||
static double vapourPressureFromRH(double Ta, double rh_percent);
|
|
||||||
|
|
||||||
private:
|
|
||||||
// coefficients in the same order as the ESM_3 polynomial table terms.
|
|
||||||
QVector<double> m_coeffs;
|
|
||||||
|
|
||||||
// Evaluate the polynomial offset = f(Ta, va, tm, pa)
|
|
||||||
double evalOffset(double Ta, double va, double tm, double pa) const;
|
|
||||||
void initCoefficients(); // initialise m_coeffs avec les 210 valeurs
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user