Rev 3 | Rev 5 | 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;
HeatConf = nullptr;
initialized = false;
if (readConf())
{
if (buildDB())
initialized = true;
}
}
heating::~heating()
{
destroyHConf();
}
void heating::run()
{
}
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 = " + string(akt->soll) + " where id = " + itostring(room);
if ((rc = sqlite3_exec(db, query, 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);
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 = " + string(akt->soll) + " where id = " + itostring(room);
if ((rc = sqlite3_exec(db, query, 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);
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 = " + string(val) + " where id = " + itostring(room);
if ((rc = sqlite3_exec(db, query, NULL, NULL, &zErrMsg)) != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
return;
}
}
sqlite3_close(db);
}
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 = " + string(val) + " where id = " + itostring(room);
if ((rc = sqlite3_exec(db, query, NULL, NULL, &zErrMsg)) != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
return;
}
}
else if (room == 0)
{
glb_night = val;
query = "update glbheat set night = " + string(val);
if ((rc = sqlite3_exec(db, query, NULL, NULL, &zErrMsg)) != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
return;
}
}
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 = " + string(val) + " where id = " + itostring(room);
if ((rc = sqlite3_exec(db, query, NULL, NULL, &zErrMsg)) != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
return;
}
}
else if (room == 0)
{
glb_minimal = val;
query = "update glbheat set minimal = " + string(val);
if ((rc = sqlite3_exec(db, query, NULL, NULL, &zErrMsg)) != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
return;
}
}
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 = what;
query = "update glbheat set onoff = " + string(what);
if ((rc = sqlite3_exec(db, query, NULL, NULL, &zErrMsg)) != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query.c_str(), zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
return;
}
sqlite3_close(db);
}
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;
}
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, 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(","+string(akt->soll));
query.append(","+string(akt->night));
query.append(","+string(akt->minimal));
query.append(","+itostring(akt->start));
query.append(","+itostring(akt->end));
query.append(")");
rc = sqlite3_exec(db, query, 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, 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(string(glb_night));
query.append(","+string(glb_minimal));
query.append(","+itostring(onoff));
query.append(")");
rc = sqlite3_exec(db, query, 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, -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, -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;
}
/*
* This will work only with C++11 or newer!
*/
time_t heating::strToTime (char* str)
{
struct std::tm tm;
std::istringstream ss(str);
ss >> std::get_time(&tm, "%H:%M");
return mktime(tm);
}
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;
}