Rev 4 | Rev 8 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*
* Copyright (C) 2015 by Andreas Theofilu. All rights reserved!
*
* All rights reserved. No warranty, explicit or implicit, provided.
*
* NOTICE: All information contained herein is, and remains
* the property of Andreas Theofilu and his suppliers, if any.
* The intellectual and technical concepts contained
* herein are proprietary to Andreas Theofilu and its suppliers and
* may be covered by European and Foreign Patents, patents in process,
* and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Andreas Theofilu.
*
* Author: Andreas Theofilu <andreas@theosys.at>
*/
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
#include <cstring>
#include <cctype>
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <fstream>
#include <cerrno>
#include <unistd.h>
#include <syslog.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sqlite3.h>
#include "heating.h"
#include "config.h"
#include "helper.h"
using namespace std;
heating::heating()
{
enableDebug(Configure.debug);
glb_minimal = 16.0;
glb_night = 18.0;
rm = 0;
rstop = false;
rrun = false;
HeatConf = nullptr;
for (int i = 0; i < 100; i++)
s1[i] = -1;
initialized = false;
if (readConf())
{
if (buildDB())
initialized = true;
}
}
heating::~heating()
{
destroyHConf();
}
void heating::run()
{
int i;
double tmp;
HCONF *akt;
rstop = false;
rrun = true;
i = 0;
while (!rstop)
{
akt = HeatConf;
while (akt)
{
tmp = getTemp(akt->tempsensor);
if (tmp < 99.0 && tmp > -99.0)
akt->ist = tmp;
akt = akt->next;
}
i++;
if (i > 60)
{
i = 0;
akt = HeatConf;
while (akt)
{
answer("IST:" + itostring(akt->rnum) + ":" + doubleToString(akt->ist, 2) + ";");
akt = akt->next;
}
}
sleep(1);
}
rrun = false;
}
double heating::incSoll(int room)
{
HCONF *akt;
char *zErrMsg = nullptr;
int rc;
sqlite3 *db;
sqlite3_stmt *res;
string query;
rc = sqlite3_open(Configure.home, &db);
if (rc)
{
syslog(LOG_DAEMON, "Error opening database %s: %s", Configure.home, sqlite3_errmsg(db));
return 0.0;
}
if ((akt = findRoom(room)) != nullptr)
{
if (akt->soll >= 30.0)
{
sqlite3_close(db);
return akt->soll;
}
akt->soll += 0.5;
query = "update heating set soll = " + doubleToString(akt->soll, 2) + " where id = " + itostring(room);
if ((rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg)) != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
akt->soll -= 0.5;
return akt->soll;
}
sqlite3_close(db);
stringstream stream;
stream << "SOLL:" << setprecision(2) << akt->soll << ";";
answer(stream.str());
return akt->soll;
}
return 0.0;
}
double heating::decSoll(int room)
{
HCONF *akt;
char *zErrMsg = nullptr;
int rc;
sqlite3 *db;
sqlite3_stmt *res;
string query;
rc = sqlite3_open(Configure.home, &db);
if (rc)
{
syslog(LOG_DAEMON, "Error opening database %s: %s", Configure.home, sqlite3_errmsg(db));
return 0.0;
}
if ((akt = findRoom(room)) != nullptr)
{
if (akt->soll <= 10.0)
{
sqlite3_close(db);
return akt->soll;
}
akt->soll -= 0.5;
query = "update heating set soll = " + doubleToString(akt->soll, 2) + " where id = " + itostring(room);
if ((rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg)) != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
akt->soll += 0.5;
return akt->soll;
}
sqlite3_close(db);
stringstream stream;
stream << "SOLL:" << setprecision(2) << akt->soll << ";";
answer(stream.str());
return akt->soll;
}
return 0.0;
}
void heating::setNight(int room, double val)
{
HCONF *akt;
char *zErrMsg = nullptr;
int rc;
sqlite3 *db;
sqlite3_stmt *res;
string query;
if (val < 10.0 || val > 25.0)
{
syslog(LOG_INFO, "Invalid value for the night!");
return;
}
rc = sqlite3_open(Configure.home, &db);
if (rc)
{
syslog(LOG_DAEMON, "Error opening database %s: %s", Configure.home, sqlite3_errmsg(db));
return;
}
if (room > 0 && (akt = findRoom(room)) != nullptr)
{
akt->night = val;
query = "update heating set night = " + doubleToString(val, 2) + " where id = " + itostring(room);
if ((rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg)) != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
return;
}
query = "NIGHT:" + itostring(room) + ":" + doubleToString(val, 2) + ";";
answer(query);
}
else if (room == 0)
{
glb_night = val;
query = "update glbheat set night = " + doubleToString(val, 2);
if ((rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg)) != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
return;
}
query = "CONFIG:GLOBAL NIGHT:" + doubleToString(val, 2) + ";";
answer(query);
}
sqlite3_close(db);
}
void heating::setMinimal(int room, double val)
{
HCONF *akt;
char *zErrMsg = nullptr;
int rc;
sqlite3 *db;
sqlite3_stmt *res;
string query;
if (val < 10.0 || val > 20.0)
{
syslog(LOG_INFO, "Invalid value for the night!");
return;
}
rc = sqlite3_open(Configure.home, &db);
if (rc)
{
syslog(LOG_DAEMON, "Error opening database %s: %s", Configure.home, sqlite3_errmsg(db));
return;
}
if (room > 0 && (akt = findRoom(room)) != nullptr)
{
akt->minimal = val;
query = "update heating set minimal = " + doubleToString(val, 2) + " where id = " + itostring(room);
if ((rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg)) != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
return;
}
query = "MINIMAL:" + itostring(room) + ":" + doubleToString(val, 2) + ";";
answer(query);
}
else if (room == 0)
{
glb_minimal = val;
query = "update glbheat set minimal = " + doubleToString(val, 2);
if ((rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg)) != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
return;
}
query = "CONFIG:GLOBAL MINIMAL:" + doubleToString(val, 2) + ";";
answer(query);
}
sqlite3_close(db);
}
bool heating::switchOnOff(bool what)
{
HCONF *akt;
char *zErrMsg = nullptr;
int rc;
sqlite3 *db;
sqlite3_stmt *res;
string query;
rc = sqlite3_open(Configure.home, &db);
if (rc)
{
syslog(LOG_DAEMON, "Error opening database %s: %s", Configure.home, sqlite3_errmsg(db));
return onoff;
}
onoff = what;
query = "update glbheat set onoff = " + (what)?"1":"0";
if ((rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg)) != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
return onoff;
}
sqlite3_close(db);
query = "POWER:" + (onoff) ? "ON;" : "OFF;";
answer(query);
return onoff;
}
double heating::getNight (int room)
{
HCONF *akt = findRoom(room);
if (akt && akt->night > 0.0)
return akt->night;
return glb_night;
}
double heating::getMinimal (int room)
{
HCONF *akt = findRoom(room);
if (akt && akt->minimal > 0.0)
return akt->minimal;
return glb_minimal;
}
void heating::destroyHConf()
{
HCONF *p, *next;
p = HeatConf;
while (p)
{
next = p->next;
delete p;
p = next;
}
HeatConf = nullptr;
}
void heating::getConfig (int s1)
{
HCONF *akt = HeatConf;
stringstream stream;
stream << "CONFIG:GLOBAL NIGHT:" << setprecision(2) << glb_night << ";";
write(s1, stream.str().c_str(), stream.str().length());
stream << "CONFIG:GLOBAL MINIMAL:" << setprecision(2) << glb_minimal << ";";
write(s1, stream.str().c_str(), stream.str().length());
stream << "CONFIG:ONOFF:" << (onoff) ? "1;" : "0;";
write(s1, stream.str().c_str(), stream.str().length());
while (akt)
{
stream << "CONFIG:ROOM:" << akt->rnum << ":" << string(akt->rname) << ":" <<
setprecision(2) << akt->soll << ":" << akt->night << ":" <<
akt->minimal << ":" << timeToStr(akt->start) << ":" << timeToStr(akt->end) << ";";
write(s1, stream.str().c_str(), stream.str().length());
akt = akt->next;
}
}
heating::HCONF* heating::findRoom (int room)
{
HCONF *akt = HeatConf;
while (akt)
{
if (akt->rnum == room)
return akt;
akt = akt->next;
}
return nullptr;
}
bool heating::readConf()
{
ifstream cfile;
char line[1024], hv0[256], hv1[128];
char *p;
CNFSTAT cs;
HCONF *akt = HeatConf;
// If there's no config file, inform the user about it.
cfile.open(Configure.heatconf, ios::in);
if ((cfile.rdstate() & std::ifstream::failbit) != 0)
{
syslog(LOG_WARNING,"Error opening the config file: %s", strerror(errno));
return false;
}
syslog(LOG_INFO, "Found config file %s.", Configure.heatconf);
cs = NONE;
// Here we've found a config file. We'll read it and set the
// contents to the structure
while (cfile.getline(&line[0], sizeof(line)))
{
int len;
trim (&line[0]);
if (line[0] == '#' || !char_traits<char>::length(line))
continue;
if ((p = findc(line, char_traits<char>::length(line), ']')) != NULL)
{
char *x;
*(p+1) = 0;
if (!compcase(line, "[global]"))
{
cs = GLOBAL;
continue;
}
if ((x = findc(line, char_traits<char>::length(line), '[')) != NULL)
{
*p = 0;
akt = appendHConf();
strncpy(akt->rname, x+1, sizeof(akt->rname) - 1);
akt->soll = 20.0;
akt->minimal = glb_minimal;
akt->night = glb_night;
cs = ROOM;
continue;
}
else
cs = NONE;
}
if ((p = findc(line, char_traits<char>::length(line), '=')) == NULL || cs == NONE)
continue;
*p = 0;
p++;
len = char_traits<char>::length(line);
if (len > (int)sizeof(hv0))
len = (int)sizeof(hv0) - 1;
strncpy(hv0, line, len);
hv0[len] = 0;
trim (hv0);
len = char_traits<char>::length(p);
if (len > (int)sizeof(hv1))
len = (int)sizeof(hv0) - 1;
strncpy(hv1, p, len);
hv1[len] = 0;
trim (hv1);
if (!compcase(hv0, "minimal"))
{
if (cs == GLOBAL)
glb_minimal = atof (hv1);
else
akt->minimal = atof (hv1);
}
if (!compcase(hv0, "night"))
{
if (cs == GLOBAL)
glb_night = atof (hv1);
else
akt->night = atof (hv1);
}
if (!compcase(hv0, "OnOff") && cs == GLOBAL)
onoff = ToBool(hv1);
if (cs == ROOM)
{
if (!compcase(hv0, "soll"))
akt->soll = atoi(hv1);
if (!compcase(hv0, "Sensor"))
akt->tempsensor = atoi(hv1);
if (!compcase(hv0, "GPIO"))
akt->gpio = atoi(hv1);
if (!compcase(hv0, "StartTime"))
akt->start = strToTime(hv1);
if (!compcase(hv0, "EndTime"))
akt->end = strToTime(hv1);
}
}
cfile.close ();
return true;
}
bool heating::buildDB()
{
sqlite3 *db;
sqlite3_stmt *res;
int rc;
char *zErrMsg = nullptr;
string query;
if (access(Configure.home, R_OK | W_OK)) // Try to create a new database if it doesn't exist
{
HCONF *akt;
rc = sqlite3_open(Configure.home, &db);
if (rc)
{
syslog(LOG_DAEMON, "Error opening database %s: %s", Configure.home, sqlite3_errmsg(db));
return false;
}
query = "create table \"main\".\"heating\" (";
query.append("\"id\" INTEGER PRIMARY KEY NOT NULL,");
query.append("\"name\" TEXT NOT NULL,");
query.append("\"soll\" REAL,");
query.append("\"night\" REAL,");
query.append("\"minimal\" REAL,");
query.append("\"start\" INTEGER,");
query.append("\"end\" INTEGER");
query.append(")");
rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg);
if (rc != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
return false;
}
else
syslog(LOG_INFO, "Database \"heating\" was successfully created.");
// Insert the values from the config file into the table
akt = HeatConf;
while (akt)
{
query = "insert into heating (id, name, soll, night, minimal, start, end) values (";
query.append(itostring(akt->rnum));
query.append(","+string(akt->rname));
query.append(","+doubleToString(akt->soll, 2));
query.append(","+doubleToString(akt->night, 2));
query.append(","+doubleToString(akt->minimal, 2));
query.append(","+itostring(akt->start));
query.append(","+itostring(akt->end));
query.append(")");
rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg);
if (rc != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
return false;
}
akt = akt->next;
query = "create table \"main\".\"glbheat\" (";
query.append("\"night\" REAL,");
query.append("\"minimal\" REAL,");
query.append("\"onoff\" INTEGER");
query.append(")");
rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg);
if (rc != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
return false;
}
else
syslog(LOG_INFO, "Database \"glbheat\" was successfully created.");
query = "insert into heating (night, minimal, onoff) values (";
query.append(doubleToString(glb_night, 2));
query.append(","+doubleToString(glb_minimal, 2));
query.append(","+itostring(onoff));
query.append(")");
rc = sqlite3_exec(db, query.c_str(), NULL, NULL, &zErrMsg);
if (rc != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
return false;
}
}
sqlite3_close(db);
}
else
{
HCONF *akt;
rc = sqlite3_open(Configure.home, &db);
if (rc)
{
syslog(LOG_DAEMON, "Error opening database %s: %s", Configure.home, sqlite3_errmsg(db));
return false;
}
akt = HeatConf;
while (akt)
{
query = "select id, name, soll, night, minimal, start, end from heating where id = "+itostring(akt->rnum);
if (sqlite3_prepare(db, query.c_str(), -1, &res, NULL) != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), sqlite3_errmsg(db));
sqlite3_close(db);
return false;
}
if ((rc = sqlite3_step(res)) == SQLITE_ROW)
{
strncpy(akt->rname, (const char *)sqlite3_column_text(res, 1), sizeof(akt->rname) - 1);
akt->soll = sqlite3_column_double(res, 2);
akt->night = sqlite3_column_double(res, 3);
akt->minimal = sqlite3_column_double(res, 4);
akt->start = (time_t)sqlite3_column_int(res, 5);
akt->end = (time_t)sqlite3_column_int(res, 6);
}
akt = akt->next;
}
query = "select night, minimal, onoff from glbheat";
if (sqlite3_prepare(db, query.c_str(), -1, &res, NULL) != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), sqlite3_errmsg(db));
sqlite3_close(db);
return false;
}
if ((rc = sqlite3_step(res)) == SQLITE_ROW)
{
strncpy(akt->rname, (const char *)sqlite3_column_text(res, 1), sizeof(akt->rname) - 1);
glb_night = sqlite3_column_double(res, 0);
glb_minimal = sqlite3_column_double(res, 1);
onoff = (time_t)sqlite3_column_int(res, 2);
}
sqlite3_close(db);
}
return true;
}
void heating::setHandle (int fd)
{
// First look into table to make sure the handle isn't already in the table
for (int i = 0; i < 100; i++)
{
if (s1[i] == fd)
return;
}
// Because we're here, the handle is not in the table. So insert it now.
for (int i = 0; i < 100; i++)
{
if (s1[i] < 0)
{
s1[i] = fd;
break;
}
}
}
void heating::removeHandle (int fd)
{
for (int i = 0; i < 100; i++)
{
if (s1[i] == fd)
{
s1[i] = -1;
break;
}
}
}
void heating::answer (string msg)
{
for (int i = 0; i < 100; i++)
{
if (s1[i] > 0)
write(s1[i], msg.c_str(), strlen(msg.c_str()));
}
}
/*
* This will work only with C++11 or newer!
*/
time_t heating::strToTime (char* str)
{
tm tm;
std::istringstream ss(str);
ss >> std::get_time(&tm, "%H:%M");
return mktime(&tm);
}
string heating::timeToStr(time_t t)
{
tm *ltm = localtime(&t);
string zeit = itostring(ltm->tm_hour) + ":" + itostring(ltm->tm_min);
return zeit;
}
heating::HCONF* heating::appendHConf()
{
HCONF *akt, *p;
akt = new HCONF;
memset(akt, 0, sizeof(HCONF));
if (HeatConf == nullptr)
HeatConf = akt;
else
{
p = HeatConf;
while (p->next)
p = p->next;
p->next = akt;
}
rm++;
akt->rnum = rm;
return akt;
}