// SPDX-License-Identifier: GPL-3.0-or-later /* * Qt mutizone MQTT thermostat * * Copyright (C) 2019 Richard Genoud * */ #include #include #include #include #include #include "mqttclient.h" #include "settings.h" #define TIMER_PERIOD_MS 30000 MQTTClient::MQTTClient(const QString& host, int port, QObject* parent) : QMqttClient(parent) { this->setHostname(host); this->setPort(port); connect(this, SIGNAL(connected(void)), this, SLOT(onConnected(void))); connect(this, SIGNAL(disconnected(void)), this, SLOT(onDisconnected(void))); connect(this, SIGNAL(messageReceived(const QByteArray &, const QMqttTopicName &)), this, SLOT(onReceived(const QByteArray &, const QMqttTopicName &))); connect(this, SIGNAL(messageSent(qint32)), this, SLOT(onPublished(qint32))); connect(this, SIGNAL(errorChanged(ClientError)), this, SLOT(onError(ClientError))); connect(this, SIGNAL(stateChanged(ClientState)), this, SLOT(onStateChanged(ClientState))); connect(&m_timer, SIGNAL(timeout()), this, SLOT(onConnectTimeout())); m_timer.start(TIMER_PERIOD_MS); } MQTTClient::~MQTTClient() { } /* * the QMqttClient doesn't handle auto-reconnection. * It can stay in the connecting state for ever, even * if the server is back on. */ void MQTTClient::onConnectTimeout(void) { switch (state()) { case Connecting: qDebug() << "force to disconnect"; disconnectFromHost(); /* fall through */ case Disconnected: qDebug() << "re-try do connect"; connectToHost(); break; case Connected: break; } } void MQTTClient::onStateChanged(ClientState state) { qDebug() << "mqtt state: "; switch (state) { case Disconnected: qDebug() << "disconnected"; break; case Connecting: qDebug() << "Connecting"; break; case Connected: qDebug() << "Connected"; break; } } void MQTTClient::onError(ClientError error) { qDebug() << "mqtt error " << error; /* TODO */ switch (error) { case NoError: break; case InvalidProtocolVersion: break; case IdRejected: break; case ServerUnavailable: break; case BadUsernameOrPassword: break; case NotAuthorized: break; case TransportInvalid: break; case ProtocolViolation: break; case UnknownError: break; default: break; } } void MQTTClient::onConnected() { qDebug() << "subscribe"; this->subscribe(QString("sensors/#"), 1); } void MQTTClient::onReceived(const QByteArray &message, const QMqttTopicName &topic) { Settings *s = Settings::getInstance(); QJsonDocument sensorData; QJsonValue val; qDebug() << "publish received: \"" << QString::fromUtf8(message) << "\"" << " from: " << topic.name(); for (int i = 0; i < s->nbZones(); ++i) { if (s->m_rooms.at(i).sensor_topic == topic.name()) { qDebug() << "this is for us !"; sensorData = QJsonDocument::fromJson(message); if (!sensorData.isObject()) { qWarning() << "malformed JSON data"; goto out; } QJsonObject obj(sensorData.object()); val = obj["temperature"]; if (val.isDouble()) { emit new_temperature(i, val.toDouble()); qDebug() << val.toDouble(); } val = obj["humidity"]; if (val.isDouble()) { emit new_hygro(i, val.toDouble()); qDebug() << val.toDouble(); } val = obj["battery"]; if (val.isDouble()) { emit new_battery(i, val.toDouble()); qDebug() << val.toDouble(); } break; } if (s->m_rooms.at(i).availability_topic == topic.name()) { emit new_availability(i, message == QString("online").toUtf8()); } } out: return; } void MQTTClient::onPublished(qint32 msgid) { qDebug() << "published id " << msgid; } void MQTTClient::onDisconnected(void) { /* TODO */ qDebug() << "disconnected"; } void MQTTClient::publish_msg(const QString& topic, const QString& payload) { qint32 msgid; msgid = this->publish(QMqttTopicName(topic), payload.toUtf8(), 1, true); if (msgid == -1) { qDebug() << "fail to publish message " << payload << " on " << topic; } } void MQTTClient::allHeatersOn(bool on) { Settings *s = Settings::getInstance(); QString payload = on ? QString("1") : QString("0"); for (int i = 0; i < s->nbZones(); i++) { const struct Room *r = &(s->m_rooms.at(i)); for (int j = 0; j < r->heaters.count(); j++) { const struct Heater *h = &(r->heaters.at(j)); publish_msg(h->ctrl_topic, payload); } } } /* vim: set tabstop=8 shiftwidth=8 softtabstop=0 noexpandtab: */