- // SPDX-License-Identifier: GPL-3.0-or-later
- /*
- * Qt mutizone MQTT thermostat
- *
- * Copyright (C) 2019 Richard Genoud
- *
- */
-
- #include <QtWidgets>
- #include <QPushButton>
- #include <QTime>
- #include <QDate>
- #include <QtMath>
- #include <QLocale>
- #include <QVector>
-
- #include "mqttclient.h"
- #include "zoneitem.h"
- #include "settings.h"
- #include "mainwindow.h"
-
- MainWindow::MainWindow(QWidget *parent) :
- QMainWindow(parent)
- {
- ZoneItem *zone;
-
- Settings *s = Settings::getInstance();
-
- for (int i = 0; i < s->nbZones(); i++) {
- zone = new ZoneItem(s->m_rooms.at(i).name, this);
- zone->m_target_temperature = get_target_temperature(i);
- m_zones << zone;
- }
-
- /*
- * Sensors-related layout
- */
- QGridLayout *mainLayout = new QGridLayout;
-
- m_state_btn.setText(tr("Auto"));
-
- if (s->nbZones() < MAX_NB_ZONES) {
- // TODO
- qDebug() << "bad configuration" ;
- } else {
- mainLayout->addWidget(m_zones.at(0), 0, 0);
- mainLayout->addWidget(m_zones.at(1), 0, 1);
- mainLayout->addWidget(new QPushButton(tr("Menu")), 0, 2);
- mainLayout->addWidget(m_zones.at(2), 1, 0);
- mainLayout->addWidget(m_zones.at(3), 1, 1);
- mainLayout->addWidget(&m_state_btn, 1, 2);
- }
- update_state_btn(s->m_state);
-
- QWidget *mainWidget = new QWidget;
- mainWidget->setLayout(mainLayout);
-
- /*
- * Top widget
- */
- setCentralWidget(mainWidget);
-
- setWindowTitle(tr("Sorico's thermostat"));
-
- /*
- * MQTT client
- */
- m_mqtt = new MQTTClient(QHostAddress(s->m_broker.address), s->m_broker.port, NULL);
-
- m_mqtt->connectToHost();
-
- connect(m_mqtt, SIGNAL(new_temperature(int, double)),
- this, SLOT(temperature_slot(int, double)));
- connect(m_mqtt, SIGNAL(new_hygro(int, double)),
- this, SLOT(hygro_slot(int, double)));
- connect(m_mqtt, SIGNAL(new_availability(int, bool)),
- this, SLOT(availability_slot(int, bool)));
-
- connect(this, SIGNAL(setAllHeatersOn(bool)), m_mqtt, SLOT(allHeatersOn(bool)));
- connect(m_mqtt, SIGNAL(connected(void)), this, SLOT(apply_order_to_heaters(void)));
-
- connect(&m_state_btn, SIGNAL(clicked()), this, SLOT(change_state()));
-
- /*
- * Heater timer
- */
- m_timer = new QTimer(this);
- connect(m_timer, SIGNAL(timeout()), this, SLOT(apply_order_to_heaters()));
- m_timer->start(60000);
- }
-
- MainWindow::~MainWindow()
- {
- }
-
- void MainWindow::update_state_btn(enum power_states st)
- {
- switch (st) {
- case OFF:
- m_state_btn.setText(tr("Force off"));
- break;
- case ON:
- m_state_btn.setText(tr("Force on"));
- break;
- case AUTO:
- m_state_btn.setText(tr("Auto"));
- break;
- default:
- m_state_btn.setText(tr("Error"));
- break;
- }
- }
-
- double MainWindow::get_target_temperature(int room_idx)
- {
- Settings *s = Settings::getInstance();
- const struct Room *r = NULL;
- const struct Program *p = NULL;
- double target = FORCE_OFF;
- QTime now = QTime::currentTime();
- uint8_t dow = QDate::currentDate().dayOfWeek();
-
- if ((room_idx < 0) || (room_idx >= MAX_NB_ZONES)) {
- goto out;
- }
-
- r = &(s->m_rooms.at(room_idx));
- target = r->default_temperature;
- if (!dow) {
- goto out;
- }
-
- dow <<= 1;
- for (int i = 0; i < r->progs.count(); i++) {
- p = &(r->progs.at(i));
- if (dow & p->DoW) {
- if (p->start_time == p->end_time) {
- continue;
- }
-
- /*
- * Ex: from 10h to 18h
- */
- if (p->start_time < p->end_time) {
- if ((now >= p->start_time) && (now < p->end_time)) {
- target = p->temperature;
- }
- } else {
- /*
- * Ex: from 22h to 6h
- */
- if (now >= p->start_time) {
- target = p->temperature;
- } else {
- if (now < p->end_time) {
- target = p->temperature;
- }
- }
- }
- }
- }
-
- out:
- return target;
- }
-
- bool MainWindow::get_heater_order(int room_idx)
- {
- bool should_heat = false;
-
- if ((room_idx < 0) || (room_idx >= MAX_NB_ZONES)) {
- goto out;
- }
-
- if (m_zones.at(room_idx)->m_temperature_value == FORCE_OFF) {
- goto out;
- }
- if (!m_zones.at(room_idx)->m_available) {
- goto out;
- }
-
- /* TODO: make it smarter */
- if (qFabs(get_target_temperature(room_idx)
- - m_zones.at(room_idx)->m_temperature_value) < 0.3) {
- /*
- * Stay like before
- */
- should_heat = m_zones.at(room_idx)->m_heating_on;
- } else {
-
- if (get_target_temperature(room_idx) >
- m_zones.at(room_idx)->m_temperature_value) {
- should_heat = true;
- }
- }
-
- out:
- return should_heat;
- }
-
- void MainWindow::apply_automatic_state(void)
- {
- Settings *s = Settings::getInstance();
- int i;
-
- qDebug() << "apply auto state";
- for (i = 0; i < s->m_rooms.count(); i++) {
- const struct Room *r = &(s->m_rooms.at(i));
- m_zones.at(i)->m_heating_on = get_heater_order(i);
- qDebug() << "room " << r->name;
- for (int j = 0; j < r->heaters.count(); j++) {
- const struct Heater *h = &(r->heaters.at(j));
- qDebug() << "heater " << h->ctrl_topic;
- /* TODO: check if connected */
- m_mqtt->publish_msg(h->ctrl_topic,
- m_zones.at(i)->m_heating_on ? "on" : "off");
- }
- }
- }
-
- void MainWindow::change_state(void)
- {
- Settings *s = Settings::getInstance();
-
- switch (s->m_state) {
- case OFF:
- s->m_state = ON;
- break;
- case ON:
- s->m_state = AUTO;
- break;
- case AUTO:
- /* fall through */
- default:
- s->m_state = OFF;
- break;
- }
-
- update_state_btn(s->m_state);
-
- apply_order_to_heaters();
- }
-
- void MainWindow::apply_order_to_heaters(void)
- {
- Settings *s = Settings::getInstance();
-
- switch (s->m_state) {
- case ON:
- qDebug() << "emit ALL_ON order";
- emit setAllHeatersOn(true);
- break;
- case AUTO:
- qDebug() << "apply AUTO state";
- apply_automatic_state();
- break;
- case OFF:
- /* fall */
- default:
- qDebug() << "emit ALL_OFF order";
- emit setAllHeatersOn(false);
- break;
- }
- }
-
- void MainWindow::temperature_slot(int idx, double val)
- {
- static bool temp_received[MAX_NB_ZONES];
-
- Settings *s = Settings::getInstance();
-
- qDebug() << "temperature idx:" << idx << " val: " << val << endl;
- if (idx < s->nbZones()) {
- m_zones.at(idx)->m_temperature_value = val;
- m_zones.at(idx)->refresh();
- if (!temp_received[idx]) {
- temp_received[idx] = true;
- apply_order_to_heaters();
- }
- }
- }
-
- void MainWindow::hygro_slot(int idx, double val)
- {
- Settings *s = Settings::getInstance();
-
- qDebug() << "hygro idx:" << idx << " val: " << val << endl;
- if (idx < s->nbZones()) {
- m_zones.at(idx)-> m_hygro_value = val;
- m_zones.at(idx)->refresh();
- }
- }
-
- void MainWindow::availability_slot(int idx, bool ok)
- {
- Settings *s = Settings::getInstance();
-
- if (idx < s->nbZones()) {
- m_zones.at(idx)->m_available = ok;
- if (!ok) {
- m_zones.at(idx)->m_hygro_value = 0;
- m_zones.at(idx)->m_temperature_value = 0;
- m_zones.at(idx)->refresh();
- }
- }
- }
-
- /* vim: set tabstop=8 shiftwidth=8 softtabstop=0 noexpandtab: */
|