/* *********************************
* HeatControl network interface *
* Author: Mateusz Viste *
*********************************
Change log:
[18 Oct 2014]
- fixed a slot-selection bug
- fixed segfaults when data files do not exist
[26 Dec 2013]
- rewritten in ANSI C.
[15 Nov 2011]
- Changed the input method for driving data, now heatinterface
is looking into the QUERY_STRING variable (before it was
expecting to be launched by the HTTP server with correct
parameters, but it had some troubles with Apache...). The
QUERY_STRING method seems to be much more reliable.
[12 Nov 2009]
- Added a thin (2px) column between each day,
- Added version information in the program's output,
- Added a popup (title) with the timeslot on each line.
[11 Nov 2009]
- Added support for a 4th zone.
[04 Oct 2009]
- First public release.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define HomepageURL "
http://heatcontrol.sourceforge.net/"
#define pDate "18 Oct 2014"
enum useractions {
SLOTACTIVATE = 1,
FORCEZONE = 2,
MODIFY = 3,
VIEWSLOT = 4
};
struct progdata {
int WeekProgram[4][7][96];
int ActiveSlot;
int SelectedSlot;
int userparams[8];
char SlotName[10][128];
time_t ForcedTime[4];
int ForcedZone[4];
int CurZoneState[4];
};
void LoadSlotInfo(struct progdata *pdata) {
int x, y;
char tempstring[1024];
FILE *fd;
/* load slot names */
fd = fopen("/etc/heatcontrol/slotnames.cfg", "r");
if (fd != NULL) {
for (x = 0; x < 10; x++) {
fgets(tempstring, 1024, fd);
if (tempstring[0] != 0) {
y = strlen(tempstring) - 1;
if (tempstring[y] == '\n') tempstring[y] = 0; /* trim the trailing LF, if present */
sprintf(pdata->SlotName[x], "%s", tempstring);
} else {
sprintf(pdata->SlotName[x], "Slot %d", x + 1);
}
}
fclose(fd);
}
/* load active slot */
fd = fopen("/etc/heatcontrol/active.cfg", "r");
if (fd != NULL) {
fscanf(fd, "%d", &(pdata->ActiveSlot));
fclose(fd);
}
}
void LoadForcedInfos(struct progdata *pdata) {
FILE *fd;
int x;
fd = fopen("/etc/heatcontrol/forcing", "r");
if (fd == NULL) return;
for (x = 0; x < 4; x++) fscanf(fd, "%d", &(pdata->ForcedZone[x]));
for (x = 0; x < 4; x++) fscanf(fd, "%ld", &(pdata->ForcedTime[x]));
fclose(fd);
}
void LoadCurState(struct progdata *pdata) {
FILE *fd;
int x;
fd = fopen("/etc/heatcontrol/curstate.dat", "r");
if (fd == NULL) return;
for (x = 0; x < 4; x++) {
fscanf(fd, "%d", &(pdata->CurZoneState[x]));
}
fclose(fd);
}
void LoadWeekProgram(struct progdata *pdata) {
FILE *fd;
int x, y, z;
char filename[256];
sprintf(filename, "/etc/heatcontrol/%d.cfg", pdata->SelectedSlot);
fd = fopen(filename, "r");
if (fd == NULL) return;
for (x = 0; x < 4; x++) { /* Zones 1-3 */
for (y = 0; y < 7; y++) { /* Weekdays 1-7 */
for (z = 0; z < 96; z++) { /* Time spaces 1-96 (24h) */
fscanf(fd, "%d", &(pdata->WeekProgram[x][y][z]));
}
}
}
fclose(fd);
}
void LoadUserVariables(struct progdata *pdata) {
/* heatinterface is parsing the QUERY_STRING environnement variable, and dispatches the content
of this variable to the following list of local variables:
$UserAction $UserParam1 $UserParam2 $UserParam3 $UserParam4 $UserParam5
Then, the program will execute whatever orders we got into our user variables.
Eg.
modify 0 1 2 96 2 (set the program of slot 0, zone 1, tuesday, 23h45 to "eco")
slot 3 (display the slot 3)
slotactivate 7 (activate the slot 7)
forcezone 1 3 2 15 (force zone 3 into HorsGel mode for 15 minutes) */
int x, y, z;
char *SrvSideParams;
char tempstring[256] = {0};
SrvSideParams = getenv("QUERY_STRING");
if (SrvSideParams == NULL) return;
y = 0;
z = 0;
for (x = 0; ; x++) {
if ((SrvSideParams[x] != '&') && (SrvSideParams[x] != ';') && (SrvSideParams[x] != 0)) {
tempstring[z] = SrvSideParams[x];
if (z < 63) z++;
} else {
tempstring[z] = 0;
z = 0;
pdata->userparams[y] = atoi(tempstring);
tempstring[0] = 0;
if (y < 7) y++;
if (SrvSideParams[x] == 0) break;
}
}
}
void SaveConfiguration(struct progdata *pdata) {
FILE *fd;
char tempstring[8192];
char *tstring;
char slotfilename[64];
int x, y, z;
tstring = tempstring;
/* save the currently active slot */
fd = fopen("/etc/heatcontrol/active.cfg", "w");
if (fd != NULL) {
fprintf(fd, "%d\n", pdata->ActiveSlot);
fclose(fd);
}
for (x = 0; x < 4; x++) { /* Zones 1-4 */
for (y = 0; y < 7; y++) { /* Weekdays 1-7 */
for (z = 0; z < 96; z++) { /* Time spaces 1-96 (24h) */
tstring += sprintf(tstring, "%d ", pdata->WeekProgram[x][y][z]);
}
}
}
sprintf(slotfilename, "/etc/heatcontrol/%d.cfg", pdata->SelectedSlot);
fd = fopen(slotfilename, "w");
if (fd != NULL) {
fprintf(fd, "%s\n", tempstring);
fclose(fd);
}
}
char *SecondsToString(int seconds) {
static char Result[8];
int FullHours, MinutesLeft, SecondsLeft;
FullHours = seconds / 3600;
SecondsLeft = seconds % 3600;
MinutesLeft = SecondsLeft / 60;
SecondsLeft %= 60;
if (FullHours > 0) {
sprintf(Result, "%02dh%02d", FullHours, MinutesLeft);
} else {
sprintf(Result, "%02d:%02d", MinutesLeft, SecondsLeft);
}
return(Result);
}
/* * Here starts the real program * */
/* States are: 0 = OFF ; 1 = HorsGel ; 2 = ECO ; 3 = COMFORT */
int main(void) {
int x, y, z;
int Modified = 0;
char *ScriptURL;
struct progdata *pdata;
char *HeatState[4] = {"OFF", "No freeze", "ECO", "COMFORT"};
char *HeatStateColor[4] = {"#A0A0A0", "#C5C5C5", "#C0F0C0", "#FFC0C0"};
char *Timespace[96] = {"00h00", "00h15", "00h30", "00h45", "01h00", "01h15", "01h30", "01h45",
"02h00", "02h15", "02h30", "02h45", "03h00", "03h15", "03h30", "03h45",
"04h00", "04h15", "04h30", "04h45", "05h00", "05h15", "05h30", "05h45",
"06h00", "06h15", "06h30", "06h45", "07h00", "07h15", "07h30", "07h45",
"08h00", "08h15", "08h30", "08h45", "09h00", "09h15", "09h30", "09h45",
"10h00", "10h15", "10h30", "10h45", "11h00", "11h15", "11h30", "11h45",
"12h00", "12h15", "12h30", "12h45", "13h00", "13h15", "13h30", "13h45",
"14h00", "14h15", "14h30", "14h45", "15h00", "15h15", "15h30", "15h45",
"16h00", "16h15", "16h30", "16h45", "17h00", "17h15", "17h30", "17h45",
"18h00", "18h15", "18h30", "18h45", "19h00", "19h15", "19h30", "19h45",
"20h00", "20h15", "20h30", "20h45", "21h00", "21h15", "21h30", "21h45",
"22h00", "22h15", "22h30", "22h45", "23h00", "23h15", "23h30", "23h45"};
/* first send out the CGI http header */
puts("Content-Type: text/html; charset=UTF-8");
puts(""); /* An empty line to notify the CGI parser that the HTTP header ends here */
pdata = calloc(1, sizeof(*pdata));
if (pdata == NULL) {
puts("<html><head><title>Error</title></head><body>FATAL ERROR: OUT OF MEMORY!</body></html>\n");
return(4);
}
ScriptURL = getenv("SCRIPT_NAME");
LoadUserVariables(pdata);
LoadSlotInfo(pdata);
if (pdata->userparams[0] == 0) { /* If no selected slot, then we assume a 'VIEW' action on active slot */
pdata->userparams[0] = VIEWSLOT;
pdata->userparams[1] = pdata->ActiveSlot;
}
pdata->SelectedSlot = pdata->userparams[1];
LoadForcedInfos(pdata);
LoadWeekProgram(pdata);
LoadCurState(pdata);
if (pdata->userparams[0] == SLOTACTIVATE) {
Modified = 1;
pdata->ActiveSlot = pdata->userparams[1];
} else if (pdata->userparams[0] == FORCEZONE) {
FILE *fd;
if (pdata->userparams[2] > 4) pdata->userparams[2] = 1;
if (pdata->userparams[2] < 0) pdata->userparams[2] = 0;
if (pdata->userparams[3] < 0) pdata->userparams[3] = 0;
if (pdata->userparams[3] > 3) pdata->userparams[3] = 0;
if (pdata->userparams[4] < 0) pdata->userparams[4] = 0;
pdata->ForcedZone[pdata->userparams[2]] = pdata->userparams[3];
pdata->ForcedTime[pdata->userparams[2]] = time(NULL) + pdata->userparams[4];
fd = fopen("/etc/heatcontrol/forcing", "w");
if (fd != NULL) {
for (x = 0; x < 4; x++) fprintf(fd, "%d ", pdata->ForcedZone[x]);
for (x = 0; x < 4; x++) fprintf(fd, "%ld ", pdata->ForcedTime[x]);
fclose(fd);
}
}
if (pdata->userparams[0] == MODIFY) {
if (pdata->userparams[5] > 3) pdata->userparams[5] = 0;
Modified = 1;
pdata->WeekProgram[pdata->userparams[2]][pdata->userparams[3]][pdata->userparams[4]] = pdata->userparams[5];
}
if (Modified != 0) SaveConfiguration(pdata); /* If any modifications have been done, save the whole thing */
/* *** Now, let's output all the html mess! *** */
puts("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"
http://www.w3.org/TR/html4/strict.dtd\">");
puts("<html>");
puts(" <head>");
printf(" <meta http-equiv=\"refresh\" content=\"300; URL=%s?%d&%d\">\n", ScriptURL, VIEWSLOT, pdata->SelectedSlot); /* Refresh every 5 minute */
puts(" <title>HeatControl</title>");
puts(" </head>");
puts(" <body>");
puts(" <table style=\"width: 100%;\">");
puts(" <tr>");
puts(" <td style=\"width: 25%;\"><table style=\"border: 1px solid black; margin-right: auto;\"><tr><td style=\"padding: 0.2em 0.7em 0.2em 0.7em;\"><b>Forced states:</b><br>");
/* Forcing zones table Start */
for (x = 0; x < 4; x++) {
y = pdata->ForcedTime[x] - time(NULL);
if ((y > 0) && (pdata->ForcedZone[x] <= 3) && (pdata->ForcedZone[x] >= 0)) {
printf(" Zone %d: <a href=\"%s?%d&%d&%d&%d&%d\">%s</a>", x + 1, ScriptURL, FORCEZONE, pdata->SelectedSlot, x, pdata->ForcedZone[x] + 1, y, HeatState[pdata->ForcedZone[x]]);
printf(" (%s left) ", SecondsToString(pdata->ForcedTime[x] - time(NULL)));
printf("<a href=\"%s?%d&%d&%d&%d&%d\">+</a> / ", ScriptURL, FORCEZONE, pdata->SelectedSlot, x, pdata->ForcedZone[x], y + 900);
printf("<a href=\"%s?%d&%d&%d&%d&%d\">-</a><br>\n", ScriptURL, FORCEZONE, pdata->SelectedSlot, x, pdata->ForcedZone[x], y - 900);
} else {
printf(" Zone %d: <a href=\"%s?%d&%d&%d&0&900\">Not forced</a><br>\n", x + 1, ScriptURL, FORCEZONE, pdata->SelectedSlot, x);
}
}
/* Forcing zones table End */
puts(" </td></tr></table></td>");
printf(" <td style=\"width: 50%%; font-weight: bold; text-align: center; font-size: 1.5em;\">Home heat controlling system<br><a href=\"%s?%d&%d\" style=\"font-weight: normal; font-size: 0.6em;\">[Refresh]</a></td>\n", ScriptURL, VIEWSLOT, pdata->SelectedSlot);
printf(" <td style=\"width: 25%%;\"><table style=\"border: 1px solid black; margin-left: auto;\"><tr><td style=\"padding: 0.2em 0.7em 0.2em 0.7em;\"><b>Current state:</b><br>");
printf("Zone 1: %s<br>Zone 2: %s<br>Zone 3: %s<br>Zone 4: %s</td></tr></table></td>\n", HeatState[pdata->CurZoneState[0]], HeatState[pdata->CurZoneState[1]], HeatState[pdata->CurZoneState[2]], HeatState[pdata->CurZoneState[3]]);
puts(" </tr>");
puts(" </table>");
puts(" <table style=\"border: 1px solid black; border-spacing: 1px; width: 100%; background-color: #E0E0F0; margin-bottom: 0.4em;\">");
puts(" <tr style=\"text-align: center;\">");
puts(" <td>Slot list:</td>");
for (x = 0; x < 10; x++) {
printf(" <td style=\"");
if (x == pdata->ActiveSlot) printf("font-weight: bold;");
if (x == pdata->SelectedSlot) printf("background-color: #F5F5FF;");
printf("\"><a href=\"%s?%d&%d\">%s</a></td>\n", ScriptURL, VIEWSLOT, x, pdata->SlotName[x]);
}
puts(" </tr>");
puts(" </table>");
if (pdata->SelectedSlot != pdata->ActiveSlot) {
printf(" <p style=\"text-align: center; font-weight: bold;\"><a href=\"%s?%d&%d\">Activate this slot</a></p>\n", ScriptURL, SLOTACTIVATE, pdata->SelectedSlot);
}
puts(" <table style=\"border: 1px solid black; border-spacing: 1px; width: 100%; background-color: #F8F8FF;\">");
puts(" <tr style=\"text-align: center; font-weight: bold;\"><td> </td><td colspan=\"4\">Monday</td><td style=\"padding: 2px;\"></td><td colspan=\"4\">Tuesday</td><td style=\"padding: 2px;\"></td><td colspan=\"4\">Wednesday</td><td style=\"padding: 2px;\"></td><td colspan=\"4\">Thursday</td><td style=\"padding: 2px;\"></td><td colspan=\"4\">Friday</td><td style=\"padding: 2px;\"></td><td colspan=\"4\">Saturday</td><td style=\"padding: 2px;\"></td><td colspan=\"4\">Sunday</td></tr>");
printf(" <tr align=\"center\"><td> </td>");
for (x = 0; x < 7; x++) {
printf("<td>Zone 1</td><td>Zone 2</td><td>Zone 3</td><td>Zone 4</td>");
if (x < 6) printf("<td></td>");
}
puts("</tr>");
for (x = 0; x < 96; x++) {
puts(" <tr>");
printf(" <td style=\"font-size: 0.9em;\"><a id=\"row%d\">%s</a></td>", x, Timespace[x]);
for (y = 0; y < 7; y++) {
for (z = 0; z < 4; z++) {
printf("<td style=\"font-size: 0.6em; text-align: center; background-color: %s;\">", HeatStateColor[pdata->WeekProgram[z][y][x]]);
printf("<a href=\"%s?%d&%d&%d&%d&%d&%d", ScriptURL, MODIFY, pdata->SelectedSlot, z, y, x, pdata->WeekProgram[z][y][x] + 1);
if (x > 20) printf("#row%d", x - 20);
printf("\" title=\"%s\">%s</a></td>", Timespace[x], HeatState[pdata->WeekProgram[z][y][x]]);
}
if (y < 7) printf("<td></td>");
}
puts("");
puts(" </tr>");
}
puts(" </table>");
printf(" <p style=\"text-align: right; Font-size: 0.85em; margin-top: 0.2em;\"><a href=\"%s\">HeatControl</a> [%s]</p>\n", HomepageURL, pDate);
puts(" </body>");
puts("</html>");
return(0);
}