Thermostat pour piloter jusqu'à 4 radiateurs avec fil pilote
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

190 lines
4.3 KiB

  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. /*
  3. * Qt mutizone MQTT thermostat
  4. *
  5. * Copyright (C) 2019 Richard Genoud
  6. *
  7. */
  8. #include <QLoggingCategory>
  9. #include <QtMqtt/QtMqtt>
  10. #include <QJsonDocument>
  11. #include <QJsonObject>
  12. #include <QJsonValue>
  13. #include "mqttclient.h"
  14. #include "settings.h"
  15. #define TIMER_PERIOD_MS 30000
  16. MQTTClient::MQTTClient(const QString& host, int port, QObject* parent)
  17. : QMqttClient(parent)
  18. {
  19. this->setHostname(host);
  20. this->setPort(port);
  21. connect(this, SIGNAL(connected(void)), this, SLOT(onConnected(void)));
  22. connect(this, SIGNAL(disconnected(void)), this, SLOT(onDisconnected(void)));
  23. connect(this, SIGNAL(messageReceived(const QByteArray &, const QMqttTopicName &)),
  24. this, SLOT(onReceived(const QByteArray &, const QMqttTopicName &)));
  25. connect(this, SIGNAL(messageSent(qint32)), this, SLOT(onPublished(qint32)));
  26. connect(this, SIGNAL(errorChanged(ClientError)),
  27. this, SLOT(onError(ClientError)));
  28. connect(this, SIGNAL(stateChanged(ClientState)),
  29. this, SLOT(onStateChanged(ClientState)));
  30. connect(&m_timer, SIGNAL(timeout()), this, SLOT(onConnectTimeout()));
  31. m_timer.start(TIMER_PERIOD_MS);
  32. }
  33. MQTTClient::~MQTTClient() {
  34. }
  35. /*
  36. * the QMqttClient doesn't handle auto-reconnection.
  37. * It can stay in the connecting state for ever, even
  38. * if the server is back on.
  39. */
  40. void MQTTClient::onConnectTimeout(void)
  41. {
  42. switch (state()) {
  43. case Connecting:
  44. qDebug() << "force to disconnect";
  45. disconnectFromHost();
  46. /* fall through */
  47. case Disconnected:
  48. qDebug() << "re-try do connect";
  49. connectToHost();
  50. break;
  51. case Connected:
  52. break;
  53. }
  54. }
  55. void MQTTClient::onStateChanged(ClientState state)
  56. {
  57. qDebug() << "mqtt state: ";
  58. switch (state) {
  59. case Disconnected:
  60. qDebug() << "disconnected";
  61. break;
  62. case Connecting:
  63. qDebug() << "Connecting";
  64. break;
  65. case Connected:
  66. qDebug() << "Connected";
  67. break;
  68. }
  69. }
  70. void MQTTClient::onError(ClientError error)
  71. {
  72. qDebug() << "mqtt error " << error;
  73. /* TODO */
  74. switch (error) {
  75. case NoError: break;
  76. case InvalidProtocolVersion: break;
  77. case IdRejected: break;
  78. case ServerUnavailable: break;
  79. case BadUsernameOrPassword: break;
  80. case NotAuthorized: break;
  81. case TransportInvalid: break;
  82. case ProtocolViolation: break;
  83. case UnknownError: break;
  84. default: break;
  85. }
  86. }
  87. void MQTTClient::onConnected()
  88. {
  89. qDebug() << "subscribe";
  90. this->subscribe(QString("sensors/#"), 1);
  91. }
  92. void MQTTClient::onReceived(const QByteArray &message, const QMqttTopicName &topic)
  93. {
  94. Settings *s = Settings::getInstance();
  95. QJsonDocument sensorData;
  96. QJsonValue val;
  97. qDebug() << "publish received: \"" << QString::fromUtf8(message)
  98. << "\"" << " from: " << topic.name();
  99. for (int i = 0; i < s->nbZones(); ++i) {
  100. if (s->m_rooms.at(i).sensor_topic == topic.name()) {
  101. qDebug() << "this is for us !";
  102. sensorData = QJsonDocument::fromJson(message);
  103. if (!sensorData.isObject()) {
  104. qWarning() << "malformed JSON data";
  105. goto out;
  106. }
  107. QJsonObject obj(sensorData.object());
  108. val = obj["temperature"];
  109. if (val.isDouble()) {
  110. emit new_temperature(i, val.toDouble());
  111. qDebug() << val.toDouble();
  112. }
  113. val = obj["humidity"];
  114. if (val.isDouble()) {
  115. emit new_hygro(i, val.toDouble());
  116. qDebug() << val.toDouble();
  117. }
  118. val = obj["battery"];
  119. if (val.isDouble()) {
  120. emit new_battery(i, val.toDouble());
  121. qDebug() << val.toDouble();
  122. }
  123. break;
  124. }
  125. if (s->m_rooms.at(i).availability_topic == topic.name()) {
  126. emit new_availability(i, message == QString("online").toUtf8());
  127. }
  128. }
  129. out:
  130. return;
  131. }
  132. void MQTTClient::onPublished(qint32 msgid)
  133. {
  134. qDebug() << "published id " << msgid;
  135. }
  136. void MQTTClient::onDisconnected(void)
  137. {
  138. /* TODO */
  139. qDebug() << "disconnected";
  140. }
  141. void MQTTClient::publish_msg(const QString& topic, const QString& payload)
  142. {
  143. qint32 msgid;
  144. msgid = this->publish(QMqttTopicName(topic), payload.toUtf8(), 1, true);
  145. if (msgid == -1) {
  146. qDebug() << "fail to publish message " << payload << " on " << topic;
  147. }
  148. }
  149. void MQTTClient::allHeatersOn(bool on) {
  150. Settings *s = Settings::getInstance();
  151. QString payload = on ? QString("1") : QString("0");
  152. for (int i = 0; i < s->nbZones(); i++) {
  153. const struct Room *r = &(s->m_rooms.at(i));
  154. for (int j = 0; j < r->heaters.count(); j++) {
  155. const struct Heater *h = &(r->heaters.at(j));
  156. publish_msg(h->ctrl_topic, payload);
  157. }
  158. }
  159. }
  160. /* vim: set tabstop=8 shiftwidth=8 softtabstop=0 noexpandtab: */