Browse Source

Thermostat: 1st thermostat implementation

master
rico rico 4 years ago
parent
commit
4488da5de3
8 changed files with 201 additions and 69 deletions
  1. +3
    -1
      soft/thermostat/Thermostat.conf
  2. +3
    -8
      soft/thermostat/inc/mainwindow.h
  3. +10
    -0
      soft/thermostat/inc/settings.h
  4. +5
    -8
      soft/thermostat/inc/zoneitem.h
  5. +133
    -15
      soft/thermostat/src/mainwindow.cpp
  6. +11
    -11
      soft/thermostat/src/mqttclient.cpp
  7. +16
    -2
      soft/thermostat/src/settings.cpp
  8. +20
    -24
      soft/thermostat/src/zoneitem.cpp

+ 3
- 1
soft/thermostat/Thermostat.conf View File

@ -1,6 +1,8 @@
# This file should be in:
# $HOME/.config/Sorico/Thermostat.conf
state=auto
[broker]
address=192.168.1.2
port=1883
@ -12,7 +14,7 @@ port=1883
1\name=Bedroom
1\sensor_topic=sensors/chambre_so/xiaomi
1\default_temperature=21
1\temperature_schedule\1\temperature=21
1\temperature_schedule\1\temperature=18
1\temperature_schedule\1\days_of_week=127
1\temperature_schedule\1\start_time=10:00
1\temperature_schedule\1\end_time=18:30


+ 3
- 8
soft/thermostat/inc/mainwindow.h View File

@ -18,13 +18,6 @@
#include "zoneitem.h"
#include "settings.h"
enum power_states {
OFF,
ON,
AUTO,
MAX_POWER_STATES
};
class MainWindow : public QMainWindow
{
Q_OBJECT
@ -38,8 +31,10 @@ private:
QPushButton m_state_btn;
QVector<ZoneItem *> m_zones;
QTimer *m_timer;
enum power_states m_pwr_state;
void update_state_btn(enum power_states st);
double get_target_temperature(int room_idx);
bool get_heater_order(int room_idx);
void apply_automatic_state(void);
private slots:
void temperature_slot(int idx, double val);


+ 10
- 0
soft/thermostat/inc/settings.h View File

@ -14,6 +14,15 @@
#include <QTime>
#define MAX_NB_ZONES 4
#define FORCE_OFF (-256)
#define FORCE_ON (99)
enum power_states {
OFF,
ON,
AUTO,
MAX_POWER_STATES
};
struct Heater {
QString ctrl_topic;
@ -49,6 +58,7 @@ public:
QVector<struct Room> m_rooms;
struct Brocker m_broker;
enum power_states m_state;
private:
explicit Settings(QObject *parent = Q_NULLPTR);


+ 5
- 8
soft/thermostat/inc/zoneitem.h View File

@ -21,21 +21,18 @@ class ZoneItem : public QWidget
public:
explicit ZoneItem(const QString &zoneName, QWidget *parent = Q_NULLPTR);
~ZoneItem();
void refresh(void);
void set_temperature_value(double val);
void set_target_temperature(double val);
void set_hygro_value(double val);
double m_temperature_value;
double m_hygro_value;
double m_target_temperature;
bool m_heating_on;
private:
QPushButton m_zoneNameBtn;
QPushButton m_temperatureBtn;
QPushButton m_hygroBtn;
void refresh(void);
double m_temperature_value;
double m_hygro_value;
double m_target_temperature;
};
#endif // ZONEITEM_H


+ 133
- 15
soft/thermostat/src/mainwindow.cpp View File

@ -9,6 +9,8 @@
#include <QtWidgets>
#include <QPushButton>
#include <QTime>
#include <QDate>
#include <QtMath>
#include <QLocale>
#include <QVector>
@ -26,6 +28,7 @@ MainWindow::MainWindow(QWidget *parent) :
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;
}
@ -36,7 +39,7 @@ MainWindow::MainWindow(QWidget *parent) :
m_state_btn.setText(tr("Auto"));
if (s->nbZones() < 4) {
if (s->nbZones() < MAX_NB_ZONES) {
// TODO
qDebug() << "bad configuration" ;
} else {
@ -47,8 +50,7 @@ MainWindow::MainWindow(QWidget *parent) :
mainLayout->addWidget(m_zones.at(3), 1, 1);
mainLayout->addWidget(&m_state_btn, 1, 2);
}
m_pwr_state = OFF;
update_state_btn(m_pwr_state);
update_state_btn(s->m_state);
QWidget *mainWidget = new QWidget;
mainWidget->setLayout(mainLayout);
@ -75,6 +77,7 @@ MainWindow::MainWindow(QWidget *parent) :
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()));
@ -83,7 +86,7 @@ MainWindow::MainWindow(QWidget *parent) :
*/
m_timer = new QTimer(this);
connect(m_timer, SIGNAL(timeout()), this, SLOT(apply_order_to_heaters()));
m_timer->start(600000);
m_timer->start(60000);
}
MainWindow::~MainWindow()
@ -108,39 +111,150 @@ void MainWindow::update_state_btn(enum power_states st)
}
}
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;
}
/* 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));
bool should_heat = 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,
should_heat ? "on" : "off");
}
}
}
void MainWindow::change_state(void)
{
switch (m_pwr_state) {
Settings *s = Settings::getInstance();
switch (s->m_state) {
case OFF:
m_pwr_state = ON;
s->m_state = ON;
break;
case ON:
m_pwr_state = AUTO;
s->m_state = AUTO;
break;
case AUTO:
/* fall through */
default:
m_pwr_state = OFF;
s->m_state = OFF;
break;
}
update_state_btn(m_pwr_state);
update_state_btn(s->m_state);
apply_order_to_heaters();
}
void MainWindow::apply_order_to_heaters(void)
{
switch (m_pwr_state) {
Settings *s = Settings::getInstance();
switch (s->m_state) {
case ON:
qDebug() << "emit ALL_ON order";
emit setAllHeatersOn(true);
break;
case AUTO:
/* TODO */
qDebug() << "apply AUTO state";
apply_automatic_state();
break;
case OFF:
/* fall */
default:
qDebug() << "emit ALL_OFF order";
emit setAllHeatersOn(false);
break;
}
@ -152,7 +266,8 @@ void MainWindow::temperature_slot(int idx, double val)
qDebug() << "temperature idx:" << idx << " val: " << val << endl;
if (idx < s->nbZones()) {
m_zones.at(idx)->set_temperature_value(val);
m_zones.at(idx)->m_temperature_value = val;
m_zones.at(idx)->refresh();
}
}
@ -162,16 +277,19 @@ void MainWindow::hygro_slot(int idx, double val)
qDebug() << "hygro idx:" << idx << " val: " << val << endl;
if (idx < s->nbZones()) {
m_zones.at(idx)->set_hygro_value(val);
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()) && !ok) {
m_zones.at(idx)->set_hygro_value(0);
m_zones.at(idx)->set_temperature_value(0);
m_zones.at(idx)->m_hygro_value = 0;
m_zones.at(idx)->m_temperature_value = 0;
m_zones.at(idx)->refresh();
}
}


+ 11
- 11
soft/thermostat/src/mqttclient.cpp View File

@ -38,18 +38,18 @@ MQTTClient::~MQTTClient() {
void MQTTClient::onError(const QMQTT::ClientError error)
{
qDebug() << "error" << error << endl;
qDebug() << "error" << error;
}
void MQTTClient::onConnected()
{
qDebug() << "connected" << endl;
qDebug() << "connected";
this->subscribe("sensors/#", 1);
}
void MQTTClient::onSubscribed(const QString& topic)
{
qDebug() << "subscribed " << topic << endl;
qDebug() << "subscribed " << topic;
}
void MQTTClient::onReceived(const QMQTT::Message& message)
@ -59,33 +59,33 @@ void MQTTClient::onReceived(const QMQTT::Message& message)
QJsonValue val;
qDebug() << "publish received: \"" << QString::fromUtf8(message.payload())
<< "\"" << " from: " << message.topic() << endl;
<< "\"" << " from: " << message.topic();
for (int i = 0; i < s->nbZones(); ++i) {
if (s->m_rooms.at(i).sensor_topic == message.topic()) {
qDebug() << "this is for us !" << endl;
qDebug() << "this is for us !";
sensorData = QJsonDocument::fromJson(message.payload());
if (!sensorData.isObject()) {
qWarning() << "malformed JSON data" << endl;
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() << endl;
qDebug() << val.toDouble();
}
val = obj["humidity"];
if (val.isDouble()) {
emit new_hygro(i, val.toDouble());
qDebug() << val.toDouble() << endl;
qDebug() << val.toDouble();
}
val = obj["battery"];
if (val.isDouble()) {
emit new_battery(i, val.toDouble());
qDebug() << val.toDouble() << endl;
qDebug() << val.toDouble();
}
break;
@ -102,12 +102,12 @@ out:
void MQTTClient::onPublished(const QMQTT::Message& message, quint16 msgid)
{
qDebug() << "published" << msgid << " payload: " << message.topic() << endl;
qDebug() << "published" << msgid << " topic: " << message.topic() << "payload" << QString::fromUtf8(message.payload());
}
void MQTTClient::onDisconnected(void)
{
qDebug() << "disconnected" << endl;
qDebug() << "disconnected";
}
void MQTTClient::publish_msg(const QString& topic, const QString& payload)


+ 16
- 2
soft/thermostat/src/settings.cpp View File

@ -50,6 +50,16 @@ Settings::Settings(QObject *parent) : QSettings(parent)
struct Program program;
int nb_rooms;
m_state = OFF;
QString st = this->value("state", "off").toString();
if (st.compare(QString("on"), Qt::CaseInsensitive) == 0) {
m_state = ON;
}
if (st.compare(QString("auto"), Qt::CaseInsensitive) == 0) {
m_state = AUTO;
}
nb_rooms = this->beginReadArray("rooms");
if (nb_rooms == 0) {
this->endArray();
@ -79,6 +89,7 @@ Settings::Settings(QObject *parent) : QSettings(parent)
room.default_temperature = this->value("default_temperature", 20).toDouble();
size = this->beginReadArray("temperature_schedule");
for (int j = 0; j < size; ++j) {
/* TODO: check for overlapping schedules ? */
this->setArrayIndex(j);
program.temperature = this->value("temperature", 20).toDouble();
program.DoW = this->value("days_of_week", 127).toInt();
@ -91,6 +102,8 @@ Settings::Settings(QObject *parent) : QSettings(parent)
}
this->endArray();
m_rooms << room;
room.progs.clear();
room.heaters.clear();
}
this->endArray();
m_broker.address = this->value("broker/address", "localhost").toString();
@ -99,6 +112,7 @@ Settings::Settings(QObject *parent) : QSettings(parent)
void Settings::set_rooms_default_config(void)
{
this->setValue("state", "auto");
this->beginWriteArray("rooms");
for (int i = 0; i < MAX_NB_ZONES; i++) {
@ -126,12 +140,12 @@ void Settings::set_rooms_default_config(void)
this->setArrayIndex(3);
this->beginWriteArray("temperature_schedule");
this->setArrayIndex(0);
this->setValue("temperature", 99); // Force On
this->setValue("temperature", FORCE_ON);
this->setValue("days_of_week", 31);
this->setValue("start_time", QTime(6,30).toString("H:m"));
this->setValue("end_time", QTime(7,0).toString("H:m"));
this->setArrayIndex(1);
this->setValue("temperature", 99); // Force On
this->setValue("temperature", FORCE_ON);
this->setValue("days_of_week", 96);
this->setValue("start_time", QTime(7,30).toString("H:m"));
this->setValue("end_time", QTime(8,0).toString("H:m"));


+ 20
- 24
soft/thermostat/src/zoneitem.cpp View File

@ -12,6 +12,7 @@
#include <QLocale>
#include <QLabel>
#include "settings.h"
#include "zoneitem.h"
ZoneItem::ZoneItem(const QString &zoneName, QWidget *parent) :
@ -22,9 +23,9 @@ ZoneItem::ZoneItem(const QString &zoneName, QWidget *parent) :
m_temperatureBtn.setFlat(true);
m_hygroBtn.setFlat(true);
m_temperature_value = 0;
m_hygro_value = 0;
m_target_temperature = 0;
m_temperature_value = FORCE_OFF;
m_hygro_value = FORCE_OFF;
m_target_temperature = FORCE_OFF;
/*
* Layout for the left part of the window
@ -45,34 +46,29 @@ void ZoneItem::refresh(void)
{
QString text;
text += QString::number(m_temperature_value);
if (m_temperature_value == FORCE_OFF)
text += QString("-");
else
text += QString::number(m_temperature_value);
text += QString("°C / ");
text += QString::number(m_target_temperature);
if (m_target_temperature == FORCE_OFF) {
text += QString("-");
} else if (m_target_temperature == FORCE_ON) {
text += QString("+");
} else {
text += QString::number(m_target_temperature);
}
text += QString("°C");
m_temperatureBtn.setText(text);
text = QString::number(m_hygro_value);
if (m_hygro_value == FORCE_OFF)
text = QString("-");
else
text = QString::number(m_hygro_value);
text += QString("%h");
m_hygroBtn.setText(text);
}
void ZoneItem::set_temperature_value(double val)
{
m_temperature_value = val;
refresh();
}
void ZoneItem::set_target_temperature(double val)
{
m_target_temperature = val;
refresh();
}
void ZoneItem::set_hygro_value(double val)
{
m_hygro_value = val;
refresh();
}
/* vim: set tabstop=8 shiftwidth=8 softtabstop=0 noexpandtab: */

Loading…
Cancel
Save