Subversion Repositories heating

Rev

Rev 54 | 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 <thread>
#include <cstring>
#include <cctype>
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <fstream>
#include <cerrno>
#include <cmath>
#include <unistd.h>
#include <syslog.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sqlite3.h>
#include <bcm2835.h>
#include "heating.h"
#include "html.h"
#include "config.h"
#include "helper.h"

using namespace std;

#define PIN1    RPI_V2_GPIO_P1_03
#define PIN2    RPI_V2_GPIO_P1_05
#define PIN3    RPI_V2_GPIO_P1_07
#define PIN4    RPI_V2_GPIO_P1_11
#define PIN5    RPI_V2_GPIO_P1_13
#define PIN6    RPI_V2_GPIO_P1_15
#define PIN7    RPI_V2_GPIO_P1_16
#define PIN8    RPI_V2_GPIO_P1_18
#define PIN9    RPI_V2_GPIO_P1_22

heating::heating()
{
        enableDebug(Configure.debug);
        glb_minimal = 16.0;
        glb_night = 18.0;
        rm = 0;
        outSensor = 0;
        rstop = false;
        rrun = false;
        HeatConf = nullptr;
        HtmlServer = nullptr;

        for (int i = 0; i < 100; i++)
                s1[i] = -1;

        initialized = false;

        if (readConf())
        {
                if (buildDB())
                {
                        initialized = true;
                        debug("Initialisation successfully done.");
                }
                else
                        debug("Error initializing database");
        }
        else
                debug("Error reading config file.");
}

heating::~heating()
{
        destroyHConf();
}

void heating::pthrTemps()
{
        trun();
}

void heating::pthrHtml()
{
        if (HtmlServer != nullptr)
                return;

        HtmlServer = new html();
        HtmlServer->setGlobals(glb_night, glb_minimal, onoff);
        applyConfig();

        HtmlServer->run();
        delete HtmlServer;
        HtmlServer = nullptr;
}

void heating::applyConfig()
{
HCONF *akt = HeatConf;
html::HTCONF ht;

        while (akt)
        {
                strcpy (ht.rname, akt->rname);
                ht.rnum = akt->rnum;
                ht.soll = akt->soll;
                ht.night = akt->night;
                ht.minimal = akt->minimal;
                ht.start1 = akt->start;
                ht.end1 = akt->end;
                ht.start2 = akt->wstart;
                ht.end2 = akt->wend;

                HtmlServer->addConfig(&ht);
                akt = akt->next;
        }
}

void heating::run()
{
int i;
double tmp;
HCONF *akt;
time_t loctime = getTime();

        rstop = false;
        rrun = true;
        i = 0;

        debug ("heating::run() reached!");
        // First start the runtime routine of class temperature.
        // We need this to get our temperatures
        thread pthr_temps(&heating::pthrTemps, this);
        pthr_temps.detach();
        debug ("heating::run() started thread temperature::trun() and detached");
        // Then we start the HTML thread.
        thread pthr_html(&heating::pthrHtml, this);
        pthr_html.detach();
        debug("heating::run() started thread html");
        // Here we read the previous colected temperatures and evaluate them.
        while (!rstop)
        {
                akt = HeatConf;

                if (HtmlServer->hasChanged())
                {
                        html::HTCONF *hta;
                        string ans;

                        glb_minimal = HtmlServer->getGlbAbsent();
                        glb_night = HtmlServer->getGlbNight();
                        onoff = HtmlServer->getRunMode();
                        ans = "CONFIG:GLOBAL NIGHT:" + doubleToString(glb_night, 1) + ";";
                        answer(ans);
                        ans = "CONFIG:GLOBAL MINIMAL:" + doubleToString(glb_minimal, 1) + ";";
                        answer(ans);
                        ans = "POWER:";
                        ans.append((onoff) ? "ON;" : "OFF;");
                        answer(ans);
                        
                        hta = HtmlServer->getHConfig();

                        while (hta)
                        {
                                akt = HeatConf;

                                while (akt)
                                {
                                        if (akt->rnum == hta->rnum)
                                        {
                                                stringstream stream;

                                                akt->soll = hta->soll;
                                                akt->night = hta->night;
                                                akt->minimal = hta->minimal;
                                                akt->start = hta->start1;
                                                akt->end = hta->end1;
                                                akt->wstart = hta->start2;
                                                akt->wend = hta->end2;
                                                stream << "CONFIG:ROOM:" << akt->rnum << ":" << string(akt->rname) << ":" << fixed <<
                                                setprecision(1) << akt->soll << ":" << akt->night << ":" <<
                                                akt->minimal << ":" << timeToStr(akt->start) << ":" << timeToStr(akt->end) << ":" <<
                                                timeToStr(akt->wstart) << ":" << timeToStr(akt->wend) << ";";
                                                answer(stream.str());
                                                break;
                                        }

                                        akt = akt->next;
                                }

                                hta = hta->next;
                        }

                        akt = HeatConf;
                }

                while (akt)
                {
                        /// Set the mode for every room.
                        if (onoff)
                        {
                                if (loctime >= akt->start && loctime <= akt->end)
                                        akt->status = NORMAL;
                                else
                                        akt->status = NIGHT;
                        }
                        else
                                akt->status = OFF;

                        tmp = getTemp(akt->tempsensor);

                        if (tmp < 99.0 && tmp > -99.0)
                        {
                                akt->ist = tmp;

                                if (evaluateTemp(akt->rnum, tmp))               // Should we start heating?
                                        startHeat(akt->rnum);                           // yes
                                else                                                                    // Or should we stop heating
                                        stopHeat(akt->rnum);                            // yes

                                if (isHeating())                                                // As long as there is a room who need heating, this function returns true.
                                        controlHeat(0, true);
                                else
                                        controlHeat(0, false);
                        }

                        akt = akt->next;
                }

                i++;

                if (i > 60)
                {
                        i = 0;
                        akt = HeatConf;

                        while (akt)
                        {
                                answer("IST:" + itostring(akt->rnum) + ":" + doubleToString(akt->ist, 1) + ";");
                                akt = akt->next;
                        }
                }

                sleep(1);
        }

        rrun = false;
}

void heating::controlHeat (int room, bool stst)
{
HCONF *akt = HeatConf;
int pin, gpio;

        if (room > 0)
        {
                while (akt)
                {
                        if (akt->rnum == room)
                                break;

                        akt = akt->next;
                }

                if (!akt)
                {
                        syslog(LOG_WARNING, "controlHeat: Room %d not found!", room);
                        return;
                }

                if (akt->valve != stst)
                {
                        if (Configure.debug)
                                syslog(LOG_DEBUG, "Heating for room %d switched %s", room, (stst) ? "ON" : "OFF");

                        string h = "HEATING:";
                        h.append(itostring(room) + ":");
                        h.append((stst)? "ON;" : "OFF;");
                        answer(h);
                }

                gpio = akt->gpio;
        }
        else if (heatIO <= 0)
                return;
        else
                gpio = heatIO;

        switch (gpio)
        {
                case 1: pin = PIN1; break;
                case 2: pin = PIN2; break;
                case 3: pin = PIN3; break;
                case 4: pin = PIN4; break;
                case 5: pin = PIN5; break;
                case 6: pin = PIN6; break;
                case 7: pin = PIN7; break;
                case 8: pin = PIN8; break;
                case 9: pin = PIN9; break;
                default:
                        return;
        }

        if (room && akt)
                akt->valve = stst;
        // Enable the GPIO pin to start heating
        if (!bcmInitialized)
                return;

        // Set the pin to be an output
        bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_OUTP);
        // Turn it on
        bcm2835_gpio_write(pin, (stst)? HIGH : LOW);
}

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;
                applyConfig();
                query = "update heating set soll = " + doubleToString(akt->soll, 1) + " 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:" << fixed << setprecision(1) << 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;
                applyConfig();
                query = "update heating set soll = " + doubleToString(akt->soll, 1) + " 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:" << fixed << setprecision(1) << akt->soll << ";";
                answer(stream.str());
                return akt->soll;
        }

        return 0.0;
}

void heating::setSoll (int room, double val)
{
HCONF *akt = HeatConf;
char *zErrMsg = nullptr;
int rc;
sqlite3 *db;
sqlite3_stmt *res;
string query;

        if (val < 10.0 || val > 30.0)
        {
                syslog(LOG_INFO, "Invalid value for maximum room temperature!");
                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->soll = val;
                applyConfig();
                query = "update heating set soll = " + doubleToString(val, 1) + " 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 = "SOLL:" + itostring(room) + ":" + doubleToString(val, 1) + ";";
                answer(query);
        }

        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;
                applyConfig();
                query = "update heating set night = " + doubleToString(val, 1) + " 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, 1) + ";";
                answer(query);
        }
        else if (room == 0)
        {
                glb_night = val;

                if (HtmlServer != nullptr)
                        HtmlServer->setGlobals(glb_night, glb_minimal, onoff);

                query = "update glbheat set night = " + doubleToString(val, 1);

                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, 1) + ";";
                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;
                applyConfig();
                query = "update heating set minimal = " + doubleToString(val, 1) + " 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, 1) + ";";
                answer(query);
        }
        else if (room == 0)
        {
                glb_minimal = val;

                if (HtmlServer != nullptr)
                        HtmlServer->setGlobals(glb_night, glb_minimal, onoff);

                query = "update glbheat set minimal = " + doubleToString(val, 1);

                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, 1) + ";";
                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;

        if (HtmlServer != nullptr)
                HtmlServer->setGlobals(glb_night, glb_minimal, onoff);

        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:";
        query.append((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:" << fixed << setprecision(1) << glb_night << ";";
        stream << "CONFIG:GLOBAL MINIMAL:" << fixed << setprecision(1) << glb_minimal << ";";
        stream << "CONFIG:ONOFF:" << ((onoff) ? "ON;" : "OFF;");

        while (akt)
        {
                stream << "CONFIG:ROOM:" << itostring(akt->rnum) << ":" << string(akt->rname) << ":" <<
                        fixed << setprecision(1) << akt->soll << ":" << akt->night << ":" <<
                        akt->minimal << ":" << timeToStr(akt->start) << ":" << timeToStr(akt->end) << ":" <<
                        timeToStr(akt->wstart) << ":" << timeToStr(akt->wend) << ";";

                stream << "HEATING:" << itostring(akt->rnum) << ":";

                if (akt->valve)
                        stream << "ON;";
                else
                        stream << "OFF;";

                akt = akt->next;
        }

        write(s1, stream.str().c_str(), stream.str().length());
}

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;
                                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 (!compcase(hv0, "OutSensor") && cs == GLOBAL)
                        outSensor = atoi(hv1);

                if (!compcase(hv0, "HeatIO") && cs == GLOBAL)
                        heatIO = atoi(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);
                                debug("StartTime: " + itostring(akt->start) + "(" + ToString(hv1) + ")");
                        }

                        if (!compcase(hv0, "EndTime"))
                        {
                                akt->end = strToTime(hv1);
                                debug("EndTime: " + itostring(akt->end) + "(" + ToString(hv1) + ")");
                        }

                        if (!compcase(hv0, "WorkStartTime"))
                        {
                                akt->wstart = strToTime(hv1);
                                debug("WorkStartTime: " + itostring(akt->wstart) + "(" + ToString(hv1) + ")");
                        }
                        
                        if (!compcase(hv0, "WorkEndTime"))
                        {
                                akt->wend = strToTime(hv1);
                                debug("WorkEndTime: " + itostring(akt->wend) + "(" + ToString(hv1) + ")");
                        }
                }
        }

        akt = HeatConf;
        time_t loctime = getTime();

        while (akt)
        {
                if (onoff)
                        akt->status = OFF;
                else if (loctime > akt->start && loctime < akt->end)
                        akt->status = NORMAL;
                else
                        akt->status = NIGHT;

                akt = akt->next;
        }

        cfile.close ();

        if (heatIO <= 0)
                syslog (LOG_DAEMON, "Missing the GPIO number to start and stop the heating! This heat control will not work!");

        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;

                debug("Creating SQL3 database.");
                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("\"wstart\" INTEGER,");
                query.append("\"wend\" 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.");

                query = "create table \"main\".\"kitable\" (";
                query.append("\"id\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,");
                query.append("\"rnum\" INTEGER NOT NULL,");
                query.append("\"diffout\" INTEGER,");
                query.append("\"diffin\" INTEGER,");
                query.append("\"heat\" INTEGER)");
                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 \"kitable\" was successfully created.");

                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.");

                // Insert the values from the config file into the table
                debug("Inserting configuration into SQL3 database");
                akt = HeatConf;

                while (akt)
                {
                        query = "insert into heating (id, name, soll, night, minimal, start, end, wstart, wend) values (";
                        query.append(itostring(akt->rnum));
                        query.append(",\""+string(akt->rname)+"\"");
                        query.append(","+doubleToString(akt->soll, 1));
                        query.append(","+doubleToString(akt->night, 1));
                        query.append(","+doubleToString(akt->minimal, 1));
                        query.append(","+itostring(akt->start));
                        query.append(","+itostring(akt->end));
                        query.append(","+itostring(akt->wstart));
                        query.append(","+itostring(akt->wend));
                        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 = "insert into glbheat (night, minimal, onoff) values (";
                query.append(doubleToString(glb_night, 1));
                query.append(","+doubleToString(glb_minimal, 1));
                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;

                debug("Reading configuration from SQL3 database");
                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;
                time_t loctime = getTime();

                while (akt)
                {
                        debug("Queriing "+itostring(akt->rnum)+", "+ToString(akt->rname));
                        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);
                        }

                        sqlite3_finalize(res);
                        query = "select rnum, diffout, diffin, heat from kitable where rnum = " + 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;
                        }

                        while ((rc = sqlite3_step(res)) == SQLITE_ROW)
                        {
                                HTABLE *tb = appendHTable(akt);
                                tb->tempDifferOut = sqlite3_column_double(res, 1);
                                tb->tempDifferIn = sqlite3_column_double(res, 2);
                                tb->heat = sqlite3_column_int(res, 3);
                                debug("KITABLE: read "+itostring(sqlite3_column_int(res, 0)));
                        }

                        sqlite3_finalize(res);

                        if (onoff)
                                akt->status = OFF;
                        else if (loctime > akt->start && loctime < akt->end)
                                akt->status = NORMAL;
                        else
                                akt->status = NIGHT;

                        akt = akt->next;
                }

                debug("Reading global parameters from SQL3 database");
                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)
                {
                        glb_night = sqlite3_column_double(res, 0);
                        glb_minimal = sqlite3_column_double(res, 1);
                        onoff = (time_t)sqlite3_column_int(res, 2);
                }

                sqlite3_finalize(res);
                sqlite3_close(db);
        }

        debug("All database operations done");
        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()));
        }
}

time_t heating::strToTime (char* str)
{
int hour, min;

        if (strchr(str, ':') != NULL)
        {
                sscanf(str, "%d:%d", &hour, &min);
                return (time_t)hour * 3600 + (time_t)min * 60;
        }

        return 0;
}

string heating::timeToStr(time_t t)
{
int hour, min;
char hv0[48];

        hour = (int)(t / 3600);
        min = (int)((t - (time_t)hour * 3600) / 60);
        sprintf(hv0, "%02d:%02d", hour, min);
        string zeit(hv0);
        return zeit;
}

time_t heating::getTime()
{
tm *ltm;
time_t t = time(NULL);
time_t out;

        ltm = localtime(&t);
        out = (time_t)(ltm->tm_hour * 3600 + ltm->tm_min * 60 + ltm->tm_sec);
        return out;
}

heating::HCONF* heating::appendHConf()
{
HCONF *akt, *p;

        akt = new HCONF;
        memset(akt, 0, sizeof(HCONF));
        akt->soll = 0.0;
        akt->night = 0.0;
        akt->minimal = 0.0;
        akt->ist = 0.0;

        if (HeatConf == nullptr)
                HeatConf = akt;
        else
        {
                p = HeatConf;

                while (p->next)
                        p = p->next;

                p->next = akt;
        }

        rm++;
        akt->rnum = rm;
        return akt;
}

/*
 * This function checks if there is a room where heating is requested.
 * If only one room requests heating, it returns true. This means, that
 * the therme, the heating, should start.
 * Otherwise it should stop heating.
 * This will work only, if there's a GPIO pin defined for the heating!
 * Beside this the function checks whether the defined GPIO pin is
 * unique. If not, it always returns false.
 */
bool heating::isHeating()
{
HCONF *akt = HeatConf;

        if (heatIO <= 0)
                return false;

        // Make sure the defined GPIO pin for the heating is unique.
        while (akt)
        {
                if (akt->gpio == heatIO)
                        return false;

                akt = akt->next;
        }

        // Find out if there is a room who requested heating.
        akt = HeatConf;

        while (akt)
        {
                if (akt->valve)
                        return true;

                akt = akt->next;
        }

        return false;
}

heating::HTABLE* heating::appendHTable(HCONF *ht)
{
HTABLE *akt, *p;

        akt = new HTABLE;
        memset(akt, 0, sizeof(HTABLE));

        if (ht->hTable == nullptr)
                ht->hTable = akt;
        else
        {
                p = ht->hTable;

                while (p->next)
                        p = p->next;

                p->next = akt;
        }

        return akt;
}

bool heating::evaluateTemp (int room, double temp)
{
HCONF *akt;
double soll, diffOut, diffIn;
time_t loctime;

        akt = HeatConf;

        while (akt)
        {
                if (akt->rnum == room)
                        break;

                akt = akt->next;
        }

        if (!akt)
        {
                syslog(LOG_WARNING, "evaluateTemp: Room %s not found!", room);
                return false;
        }

        loctime = getTime();

        switch(akt->status)
        {
                case NORMAL:
                        if (akt->wstart && akt->wstart < akt->wend && loctime >= akt->wstart && loctime <= akt->wend)
                                soll = akt->night;
                        else if ((loctime >= akt->start && loctime <= akt->wstart) ||
                                (loctime >= akt->wend && loctime <= akt->end)) 
                                soll = akt->soll;
                        
                        diffOut = trunc(soll - getOutside());
                        diffIn = trunc(soll - temp);
                break;

                case NIGHT:
                        if (akt->night)
                                soll = akt->night;
                        else
                                soll = glb_night;

                        diffOut = trunc(soll - getOutside());
                        diffIn = trunc(soll - temp);
                break;

                case OFF:
                        if (akt->minimal)
                                soll = akt->minimal;
                        else
                                soll = glb_minimal;

                        diffOut = trunc(soll - getOutside());
                        diffIn = trunc(soll - temp);
                break;
        }

        if (!akt->hTable)                       // If we've no heating table, we've to evaluate the temperature difference
        {
                if (!akt->valve && temp <= (soll - 0.5))
                {
                        HTABLE *tb;
                        tb = appendHTable(akt);
                        tb->tempDifferOut = trunc(soll - getOutside());
                        tb->tempDifferIn = trunc(soll - temp);
                        tb->start = getTime();
                        return true;
                }

                if (akt->valve && temp < soll)
                        return true;
                else
                        return false;
        }
        else if (akt->valve && temp < soll)
                return true;
        else if (akt->valve && temp >= soll)
        {
                HTABLE *tb = akt->hTable;

                while (tb)
                {
                        if (tb->start)
                                break;

                        if (tb->next)
                                tb = tb->next;
                        else
                                break;
                }

                if (tb && tb->start)
                {
                        if (loctime > tb->start)
                                tb->heat = loctime - tb->start;
                        else
                                tb->heat = (86400 + loctime) - tb->start;

                        tb->start = 0;
                }

                return false;
        }
        else if (!akt->valve && temp <= (soll - 0.5))           // If temperature is beyond soll, start immediately
        {
                HTABLE *tb = akt->hTable;
                // Find out whether we've already a correct time in the table or not.
                while (tb)
                {
                        if (tb->tempDifferOut == diffOut && tb->tempDifferIn == diffIn)
                                break;

                        tb = tb->next;
                }

                if (!tb)
                {
                        tb = appendHTable(akt);
                        tb->tempDifferOut = diffOut;
                        tb->tempDifferIn = diffIn;
                        tb->start = loctime;
                }

                return true;
        }
        else
        {
                HTABLE *tb = akt->hTable;
                time_t t = getTime();
                // Check the current time and look in learned table whether we should
                // start heating or not.
                if (tb)                                                                                 // Only if we already have a table!
                {                                                                                               // Yes there is one, so search through it.
                        bool strt = false;

                        if (diffIn <= 0.0)                                                      // Is it warmer inside than it should be?
                                return false;                                                   // yes, then turn heating off

                        if (akt->status == OFF && diffIn >= 0.5)        // Are we in off mode and is the temperature lower than it should be?
                                return true;                                                    // yes, then start heating
                        else if (akt->status == OFF)                            // otherwise ...
                                return false;                                                   // no heating

                        while (tb)                                                                      // Search through learned table
                        {
                                if (!tb->start && tb->tempDifferOut == diffOut && tb->tempDifferIn == diffIn)
                                        break;

                                tb = tb->next;
                        }

                        if (tb)                                                                         // Have we found a known time to heat?
                        {                                                                                       // Yes, then look if it's time to heat
                                if (akt->status == NORMAL)
                                {
                                        if ((t + tb->heat) >= akt->start && (t + tb->heat) <= akt->end)
                                                return true;
                                }
                                else if (akt->status == NIGHT)
                                {
                                        if ((t + tb->heat) >= akt->end || t < (akt->end - tb->heat))
                                                return true;
                                }
                        }
                        else                                                                            // None of the saved temperture differences match.
                        {                                                                                       // So test if it's time to add a new one
                                if (akt->status == NORMAL)
                                {
                                        if ((t + 600) >= akt->start && (t + 600) <= akt->end)
                                                strt = true;
                                }
                                else if (akt->status == NIGHT)
                                {
                                        if ((t + 600) >= akt->end || t < (akt->end - 600))
                                                strt = true;
                                }

                                if (strt)
                                {
                                        bool add = true;

                                        // Make sure there is no open start
                                        tb = akt->hTable;
                                        
                                        while (tb)
                                        {
                                                if (tb->start)
                                                {
                                                        add = false;
                                                        break;
                                                }

                                                tb = tb->next;
                                        }

                                        if (add)
                                                tb = appendHTable(akt);

                                        tb->tempDifferOut = diffOut;
                                        tb->tempDifferIn = diffIn;
                                        tb->start = t;
                                        return true;
                                }
                        }
                }
        }

        return false;
}

time_t heating::calcTime(double soll, double ist)
{
double pwm, len;
double correct= 100;
double parallel = 110;

        pwm = (log((soll - ist) + correct * 0.001) + parallel * 0.01) * 100.0;
        len = (10 * 60) * pwm / 100.0;
        return time_t(len * 10.0);
}