thermferm/server.c

Sat, 14 Jul 2018 21:15:20 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Sat, 14 Jul 2018 21:15:20 +0200
changeset 534
92b546d4a839
parent 533
49580ca85ab7
child 536
e833bbd5e733
permissions
-rw-r--r--

Versie 0.6.4. Aan de global setup de MQTT instellingen toegevoegd. Die zijn er ook als MQTT niet op het systeem aanwezig is. Indien het de global setup wijzigd, wordt de MQTT verbinding opnieuw gemaakt.

/*****************************************************************************
 * Copyright (C) 2008-2018
 *   
 * Michiel Broek <mbroek at mbse dot eu>
 *
 * This file is part of the mbsePi-apps
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * mbsePi-apps is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with ThermFerm; see the file COPYING.  If not, write to the Free
 * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 *****************************************************************************/

#include "rdconfig.h"
#include "thermferm.h"
#include "logger.h"
#include "devices.h"
#include "server.h"
#include "lcd-buffer.h"
#include "xutil.h"
#include "mqtt.h"


extern int		my_shutdown;
extern int		debug;
extern int		run_pause;
extern int		run_hold;
extern sys_config       Config;
extern const char	UNITMODE[5][8];
extern const char	TEMPSTATE[3][8];
extern const char	DEVTYPE[8][6];
extern const char	DEVPRESENT[4][6];
extern const char	DEVDIR[7][11];
extern const char	PROFSTATE[5][6];


int			s;		/* connected socket			*/
int			ls;		/* listen socket			*/

struct sockaddr_in	myaddr_in;	/* for local socket address             */
struct sockaddr_in	peeraddr_in;	/* for peer socket address              */

struct hostent		*hp;

#define SS_BUFSIZE      1024
#define SS_TIMEOUT      300

#define	MAX_INTERVALS	10
const int	GRAPH_INTERVAL[MAX_INTERVALS] = { 0, 1, 5, 15, 30, 60, 120, 240, 480, 960 };
const int	GRAPH_DATALINES[MAX_INTERVALS] = { 0, 800, 3200, 12000, 24000, 48000, 96000, 192000, 384000, 768000 };
const char	MONTH[12][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };


typedef struct _ls_list {
    struct _ls_list	*next;		/* Next record pointer			*/
    char		d_name[256];	/* File name				*/
    mode_t		mode;		/* File mode				*/
    off_t		size;		/* File size				*/
    time_t		mtime;		/* File modification time		*/
} ls_list;



void tidy_lslist(ls_list **);
void fill_list(ls_list **, char *, mode_t, off_t, time_t);
int  comp(ls_list **,ls_list **);
void sort_list(ls_list **);



/*
 * Send message to client
 */
int srv_send(const char *format, ...)
{
    char        out[SS_BUFSIZE];
    va_list     va_ptr;

    if (s == -1)
	return -1;

    va_start(va_ptr, format);
    vsnprintf(out, SS_BUFSIZE-1, format, va_ptr);
    va_end(va_ptr);

    if (debug) {
	syslog(LOG_NOTICE, "send: \"%s\"", out);
	fprintf(stdout, "send: \"%s\"\n", out);
    }

    if (send(s, out, strlen(out), 0) != strlen(out)) {
	syslog(LOG_NOTICE, "srv_send failed");
	return -1;
    }
    
    if (send(s, (char *)"\r\n", 2, 0) != 2) {
	syslog(LOG_NOTICE, "srv_send failed");
	return -1;
    }

    return 0;
}



/*
 * Argument is a buffer of size SS_BUFSIZE.
 * Return -1 if error, else the number of received
 * character. \n is line end, ignore \r.
 */
int srv_recv(char *buffer)
{
    int			bytesloaded = 0;
    ssize_t		ret;
    unsigned char	buf;
    socklen_t		fromlen;

    memset(buffer, 0, SS_BUFSIZE);

    while(1) {
	/*
	 * read a single byte
	 */
	fromlen = sizeof(peeraddr_in);
	ret = recvfrom(s, &buf, 1, 0, (struct sockaddr *)&peeraddr_in, &fromlen);
	if (ret < 0) {
	    syslog(LOG_NOTICE, "recvfrom(): %s", strerror(errno));
	    return -1;	/* error */
	} else if (ret == 0) {
	    return -1;	/* no data */
	}

	if (buf == '\n')
	    break;

	if (buf != '\r') {
	    buffer[bytesloaded] = buf; 
	    bytesloaded++;
	}
    } 

    if (debug) {
	syslog(LOG_NOTICE, "recv: %d `%s'", bytesloaded, buffer);
	fprintf(stdout, "recv: %d `%s'\n", bytesloaded, buffer);
    }
    return bytesloaded;
}



/*
 * Update the device inuse counter.
 */
void device_count(int plus, char *uuid)
{
    devices_list	*device;

    for (device = Config.devices; device; device = device->next) {
	if (strcmp(device->uuid, uuid) == 0) {
#ifdef HAVE_WIRINGPI_H
	    piLock(LOCK_DEVICES);
#endif
	    if (plus == TRUE) {
		device->inuse++;
	    } else {
		if (device->inuse)
		    device->inuse--;
	    }
#ifdef HAVE_WIRINGPI_H
	    piUnlock(LOCK_DEVICES);
#endif
	}
    }
}



int delete_Profile(char *uuid)
{
    profiles_list	*current = Config.profiles;
    profiles_list	*previous = NULL;
    prof_step		*step, *olds;

    while (current) {
	if (strcmp(current->uuid, uuid) == 0) {
	    if (previous == NULL) {
		Config.profiles = current->next;
		free(current->uuid);
		current->uuid = NULL;
		free(current->name);
		current->name = NULL;
		if (current->steps) {
		    for (step = current->steps; step; step = olds) {
			olds = step->next;
			free(step);
		    }
		    current->steps = NULL;
		}
		free(current);
		return 1;
	    } else {
		free(current->uuid);
		current->uuid = NULL;
		free(current->name);
		current->name = NULL;
		if (current->steps) {
		    for (step = current->steps; step; step = olds) {
			olds = step->next;
			free(step);
		    }
		    current->steps = NULL;
		}
		previous->next = current->next;
		free(current);
		current = previous->next;
		return 1;
	    }
	} else {
	    previous = current;
	    current = current->next;
	}
    }

    return 0;
}



void tidy_lslist(ls_list **lap)
{
    ls_list     *tmp, *old;

    for (tmp = *lap; tmp; tmp = old) {
	old = tmp->next;
	free(tmp);
    }
    *lap = NULL;
}



void fill_list(ls_list **lap, char *name, mode_t mode, off_t size, time_t mtime)
{
    ls_list     **tmp;

    for (tmp = lap; *tmp; tmp = &((*tmp)->next));

    *tmp = (ls_list *)malloc(sizeof(ls_list));
    (*tmp)->next = NULL;
    strncpy((*tmp)->d_name, name, 256);
    (*tmp)->mode = mode;
    (*tmp)->size = size;
    (*tmp)->mtime = mtime;
    tmp = &((*tmp)->next);
}



void sort_list(ls_list **lap)
{
    ls_list	*ta, **vector;
    size_t	n = 0, i;

    if (*lap == NULL)
	return;

    for (ta = *lap; ta; ta = ta->next) 
	n++;
    vector = (ls_list **)malloc(n * sizeof(ls_list *));
    i = 0;

    for (ta = *lap; ta; ta = ta->next) {
	vector[i++] = ta;
    }
    qsort(vector, n, sizeof(ls_list *), (int(*)(const void*, const void*))comp);

    (*lap) = vector[0];
	i = 1;

    for (ta = *lap; ta; ta = ta->next) {
	if (i < n) 
	    ta->next = vector[i++];
	else 
	    ta->next = NULL;
    }

    free(vector);
    return;
}



int comp(ls_list **lsp1, ls_list **lsp2)
{
    return strcmp((*lsp1)->d_name, (*lsp2)->d_name);
}



/*
 * ARCHIVE DIR
 * ARCHIVE GET filename
 * ARCHIVE LOG filename
 * ARCHIVE HELP
 */
int cmd_archive(char *buf)
{
    char		*opt, *param, *name = NULL, *filename = NULL, *logname = NULL, mbits[11], tstr[24];
    DIR			*dd;
    FILE		*fp;
    struct dirent	entry, *result;
    ls_list		*lsx = NULL, *tmp;
    struct stat		sbuf;
    struct tm		*tbuf;
    time_t		ftime;
    int			found;
    units_list		*unit;


    opt = strtok(buf, " \0");
    opt = strtok(NULL, " \0");

    if (opt == NULL) {
	srv_send((char *)"501 Subcommand missing");
	return 0;
    }
    param = strtok(NULL, "\0");

    if (strcmp(opt, (char *)"HELP") == 0) {
	srv_send((char *)"100 Help text follows:");
	srv_send((char *)"Recognized commands:");
	srv_send((char *)"ARCHIVE DIR                   Archived logfiles directory");
	srv_send((char *)"ARCHIVE GET filename          Archived logfile download");
	srv_send((char *)"ARCHIVE LOG filename          Archived logfile data in graphsteps");
	srv_send((char *)".");
	return 0;
    }

    if (strcmp(opt, (char *)"DIR") == 0) {

	if (getenv((char *)"USER") == NULL) {
	    name = xstrcpy((char *)"/root");
	} else {
	    name = xstrcpy(getenv((char *)"HOME"));
	}
	name = xstrcat(name, (char *)"/.thermferm/log/");

	if ((dd = opendir(name))) {
	    for (;;) {
		if ((readdir_r(dd, &entry, &result)) != 0) {
		    syslog(LOG_NOTICE, "readdir_r: error=%d", errno);
		    break;
		}
		if (result == NULL)	/* End of directory	*/
		    break;
		if (result->d_name[0] != '.') {
		    filename = xstrcpy(name);
		    filename = xstrcat(filename, result->d_name);
		    /*
		     * Remove files from the list when they are in use
		     */
		    found = 0;
		    for (unit = Config.units; unit; unit = unit->next) {
			if (unit->mode != UNITMODE_OFF) {
			    logname = xstrcpy(unit->name);
			    logname = xstrcat(logname, (char *)".log");
			    if (! strcmp(result->d_name, logname))
				found = 1;
			    free(logname);
			    logname = NULL;
			}
		    }
		    if ((found == 0) && ((stat(filename, &sbuf)) == 0)) {
		        fill_list(&lsx, result->d_name, sbuf.st_mode, sbuf.st_size, sbuf.st_mtime);
		    }
		    free(filename);
		    filename = NULL;
		}
	    }
	    closedir(dd);
	} else {
	    syslog(LOG_NOTICE, "opendir: \"%s\" error=%d", name, errno);    
	}

	sort_list(&lsx);

	srv_send((char *)"212 Archive directory follows:");
	for (tmp = lsx; tmp; tmp = tmp->next) {
	    sprintf(mbits, "----------");
	    if (tmp->mode & S_IRUSR)
		mbits[1] = 'r';
	    if (tmp->mode & S_IWUSR)
		mbits[2] = 'w';
	    if (tmp->mode & S_IXUSR)
		mbits[3] = 'x';
	    if (tmp->mode & S_IRGRP)
		mbits[4] = 'r';
	    if (tmp->mode & S_IWGRP)
		mbits[5] = 'w';
	    if (tmp->mode & S_IXGRP)
		mbits[6] = 'x';
	    if (tmp->mode & S_IROTH)
		mbits[7] = 'r';
	    if (tmp->mode & S_IWOTH)
		mbits[8] = 'w';
	    if (tmp->mode & S_IXOTH)
		mbits[9] = 'x';
	    ftime = tmp->mtime;
	    tbuf = localtime(&ftime);
	    sprintf(tstr, "%02d %s %04d %02d:%02d", tbuf->tm_mday, MONTH[tbuf->tm_mon], tbuf->tm_year+1900, tbuf->tm_hour, tbuf->tm_min);
	    srv_send((char *)"%s,%s,%d,%s", tmp->d_name, mbits, tmp->size, tstr);
	}
	srv_send((char *)".");
	tidy_lslist(&lsx);

	free(name);
	name = NULL;
	return 0;
    }

    if (param == NULL) {
	srv_send((char *)"502 Parameter missing");
	return 0;
    }

    if (strcmp(opt, (char *)"GET") == 0) {
	if (getenv((char *)"USER") == NULL) {
	    name = xstrcpy((char *)"/root");
	} else {
	    name = xstrcpy(getenv((char *)"HOME"));
	}
	name = xstrcat(name, (char *)"/.thermferm/log/");
	name = xstrcat(name, param);

	if ((fp = fopen(name, "r"))) {
	    char	buffer[256];

	    srv_send((char *)"212 Archive file follows:");
	    while (fgets(buffer, sizeof(buffer)-1, fp) != NULL) {
		int	i;

		for (i = 0; i < strlen(buffer); i++) {
		    if (buffer[i] == '\n')
			buffer[i] = '\0';
		    if (buffer[i] == '\r')
			buffer[i] = '\0';
		}
		srv_send(buffer);
	    }
	    srv_send((char *)".");
	    fclose(fp);
	} else {
	    srv_send((char *)"440 No such file");
	}

	free(name);
	name = NULL;
	return 0;
    }

    if (strcmp(opt, (char *)"LOG") == 0) {
	if (getenv((char *)"USER") == NULL) {
	    name = xstrcpy((char *)"/root");
	} else {
	    name = xstrcpy(getenv((char *)"HOME"));
	}
	name = xstrcat(name, (char *)"/.thermferm/log/");
	name = xstrcat(name, param);

	if ((fp = fopen(name, "r"))) {
	    char        buffer[256], outbuf[256], q[5], hr[3];
	    char	*date_n, *mode_n, *air_n, *beer_n, *target_lo_n, *target_hi_n, *heater_n, *cooler_n, *room_n;
	    char	*heater_u, *cooler_u;
	    int		lines = 0, heater_l = 0, cooler_l = 0, h = 0, c = 0, heat_used = 0, cool_used = 0, graphstep = 0;
	    float	room_t = 0.0;

	    srv_send((char *)"212 Logfile list follows:");
	    while (fgets(buffer, sizeof(buffer)-1, fp) != NULL) {
		lines++;
	    }
	    fseek(fp, 0L, SEEK_SET);
	    /*
	     * We have counted the lines in the logfile including the header lines.
	     * The header lines should be ignored but there are so few of them, we
	     * just include them in the total.
	     * Now find a reasonable interval of lines to sent to the client.
	     */
	    for (graphstep = 1; graphstep <= MAX_INTERVALS; graphstep++) {
		if (lines < GRAPH_DATALINES[graphstep]) {
		    break;
		}
	    }
	    if (graphstep > MAX_INTERVALS)
		graphstep = MAX_INTERVALS;
	    syslog(LOG_NOTICE, "ARCHIVE LOG %s: lines=%d, interval=%d, graphstep=%d", param, lines, GRAPH_INTERVAL[graphstep], graphstep);

	    while (fgets(buffer, sizeof(buffer)-1, fp) != NULL) {
		/*
		 * 2014-11-15 18:39,BEER,20.312,19.750,20.0,0,NA,NA,NA,78105,NA,NA,18.000,20.1
		 *       |           |     |      |     |   | |  |  |    |   |  |    |     |
		 *  date_n           |     |      |     |   | |  |  |    |   |  |    |     |
		 *  mode_n ----------+     |      |     |   | |  |  |    |   |  |    |     |
		 *  air_n -----------------+      |     |   | |  |  |    |   |  |    |     |
		 *  beer_n -----------------------+     |   | |  |  |    |   |  |    |     |
		 *  target_lo_n ------------------------+   | |  |  |    |   |  |    |     |
		 *  heater_n -------------------------------+ |  |  |    |   |  |    |     |
		 *  cooler_n ---------------------------------+  |  |    |   |  |    |     |
		 *  not used ------------------------------------+  |    |   |  |    |     |
		 *  not used ---------------------------------------+    |   |  |    |     |
		 *  heater_u --------------------------------------------+   |  |    |     |
		 *  cooler_u ------------------------------------------------+  |    |     |
		 *  not used ---------------------------------------------------+    |     |
		 *  room_n ----------------------------------------------------------+     |
		 *  target_hi_n -----------------------------------------------------------+
		 */
		hr[0] = q[0] = buffer[11];
		hr[1] = q[1] = buffer[12];
		q[2] = buffer[14];
		q[3] = buffer[15];
		hr[2] = '\0';
		buffer[strlen(buffer) -1] = '\0';
		date_n = strtok(buffer, ",\0"); 	/* timestamp            */
		mode_n = strtok(NULL, ",\0");   	/* unit mode            */
		air_n = strtok(NULL, ",\0");    	/* air temp             */
		beer_n = strtok(NULL, ",\0");   	/* beer temp            */
		target_lo_n = strtok(NULL, ",\0");	/* target low temp	*/
		heater_n = strtok(NULL, ",\0"); 	/* current heater state */
		cooler_n = strtok(NULL, ",\0"); 	/* current cooler state */
		heater_u = strtok(NULL, ",\0"); 	/* current fan state    */
		heater_u = strtok(NULL, ",\0"); 	/* current door state   */
		heater_u = strtok(NULL, ",\0"); 	/* heater use counter   */
		cooler_u = strtok(NULL, ",\0"); 	/* cooler use counter   */
		room_n = strtok(NULL, ",\0");		/* fan use counter	*/
		room_n = strtok(NULL, ",\0");		/* room temperature	*/
		target_hi_n = strtok(NULL, ",\0");	/* target high temp	*/

		if (strncmp(mode_n, (char *)"Mode", 4)) {
		    /*
		     * Output a line at the right intervals
		     */
		    int hour = atoi(hr);
		    if (((graphstep == 1)) ||
			((graphstep == 2) && (q[3] == '0' || q[3] == '5')) ||
			((graphstep == 3) && ((q[2] == '0' && q[3] == '0') || (q[2] == '1' && q[3] == '5') || (q[2] == '3' && q[3] == '0') || (q[2] == '4' && q[3] == '5'))) ||
			((graphstep == 4) && ((q[2] == '0' && q[3] == '0') || (q[2] == '3' && q[3] == '0'))) ||
			((graphstep == 5) && (q[2] == '0' && q[3] == '0')) ||
			((graphstep == 6) && (hour % 2 == 0) && (q[2] == '0' && q[3] == '0')) ||	/* 120 minutes	*/
			((graphstep == 7) && (hour % 4 == 0) && (q[2] == '0' && q[3] == '0')) ||	/* 240 minutes	*/
			((graphstep >= 8) && (hour % 8 == 0) && (q[2] == '0' && q[3] == '0')) ) {	/* 480 minutes	*/
			heat_used = cool_used = 0;
			if (heater_u && strcmp(heater_u, "NA") && (sscanf(heater_u, "%d", &h) == 1)) {
			    if (h && heater_l) {
				heat_used = ((h - heater_l) * 100) / (GRAPH_INTERVAL[graphstep] * 60);
			    }
			}
			if (cooler_u && strcmp(cooler_u, "NA") && (sscanf(cooler_u, "%d", &c) == 1)) {
			    if (c && cooler_l) {
				cool_used = ((c - cooler_l) * 100) / (GRAPH_INTERVAL[graphstep] * 60);
			    }
			}
			if (room_n)
			    sscanf(room_n, "%f", &room_t);
			if (target_hi_n == NULL)
			    target_hi_n = target_lo_n;
			snprintf(outbuf, 255, "%s,%s,%s,%s,%s,%s,%s,%d,%d,%.1f,%s", 
				date_n, mode_n, air_n, beer_n, target_lo_n, heater_n, cooler_n, heat_used, cool_used, room_t,target_hi_n);
			srv_send(outbuf);
			if (heater_u && h && strcmp(heater_u, "NA"))
			    heater_l = h;
			if (cooler_u && c & strcmp(cooler_u, "NA"))
			    cooler_l = c;
		    }
		}
	    }

	    srv_send((char *)".");
	    fclose(fp);
	} else {
	    srv_send((char *)"440 No such file");
	}

	free(name);
	name = NULL;
    }

    return 0;
}



int delete_Device(char *uuid)
{
    devices_list	*current = Config.devices;
    devices_list	*previous = NULL;

    while (current) {
	if (strcmp(current->uuid, uuid) == 0) {
	    if (previous == NULL) {
		Config.devices = current->next;
		free(current->uuid);
		current->uuid = NULL;
		free(current->address);
		current->address = NULL;
		free(current->description);
		current->description = NULL;
		free(current->comment);
		current->comment = NULL;
		free(current);
		return 1;
	    } else {
		free(current->uuid);
		current->uuid = NULL;
		free(current->address);
		current->address = NULL;
		free(current->description);
		current->description = NULL;
		free(current->comment);
		current->comment = NULL;
		previous->next = current->next;
		free(current);
		current = previous->next;
		return 1;
	    }
	} else {
	    previous = current;
	    current = current->next;
	}
    }
    return 0;
}



/*
 * DEVICE ADD type
 * DEVICE DEL uuid
 * DEVICE LIST
 * DEVICE GET uuid
 * DEVICE PUT uuid
 */
int cmd_device(char *buf)
{
    char		*opt, *param, *kwd, *val, ibuf[SS_BUFSIZE];
    devices_list	*device, *tmpd;
    int			i, rc, rlen, ival;
    uuid_t		uu;

    opt = strtok(buf, " \0");
    opt = strtok(NULL, " \0");

    if (opt == NULL) {
	srv_send((char *)"501 Subcommand missing");
	return 0;
    }
    param = strtok(NULL, "\0");

    if (strcmp(opt, (char *)"HELP") == 0) {
	srv_send((char *)"100 Help text follows:");
	srv_send((char *)"Recognized commands:");
	srv_send((char *)"DEVICE ADD type               Add device (RC433/DHT/I2C/SPI)");
	srv_send((char *)"DEVICE DEL uuid               Delete device by uuid");
	srv_send((char *)"DEVICE LIST                   List all devices");
	srv_send((char *)"DEVICE GET uuid               Read device by uuid parameters");
	srv_send((char *)"DEVICE PUT uuid               Write device by uuid parameters");
	srv_send((char *)".");
	return 0;
    }

    if (strcmp(opt, (char *)"LIST") == 0) {
	srv_send((char *)"212 Devices list follows:");
	for (device = Config.devices; device; device = device->next) {
	    srv_send((char *)"%s,%s,%d,%d,%s,%s,%d", device->uuid, device->address, device->subdevice,
			    device->inuse, device->comment, DEVDIR[device->direction], device->value + device->offset);
	}
	srv_send((char *)".");
	return 0;
    }

    if (param == NULL) {
	srv_send((char *)"502 Parameter missing");
	return 1;
    }

    if (strcmp(opt, (char *)"ADD") == 0) {
	if ((strcmp(param, (char *)"RC433") == 0) || (strcmp(param, (char *)"DHT") == 0) ||
	    (strcmp(param, (char *)"I2C") == 0) || (strcmp(param, (char *)"SPI") == 0)) {

	    device = (devices_list *)malloc(sizeof(devices_list));
	    device->next = NULL;
	    device->version = 1;
	    device->uuid = malloc(37);
	    uuid_generate(uu);
	    uuid_unparse(uu, device->uuid);
	    for (i = 0; i < 8; i++) {
		if (strcmp(param, DEVTYPE[i]) == 0) {
		    device->type = i;
		    break;
		}
	    }
	    device->direction = DEVDIR_UNDEF;
	    device->value = device->offset = device->subdevice = device->inuse = 0;
	    device->present = DEVPRESENT_UNDEF;
	    device->address = xstrcpy((char *)"Enter address here");
	    device->gpiopin = -1;
	    device->description = xstrcpy((char *)"Describe me here");
	    device->comment = xstrcpy((char *)"Comment here");

#ifdef HAVE_WIRINGPI_H
	    piLock(LOCK_DEVICES);
#endif
	    if (Config.devices == NULL) {
		Config.devices = device;
	    } else {
	        for (tmpd = Config.devices; tmpd; tmpd = tmpd->next) {
	    	    if (tmpd->next == NULL) {
	    		tmpd->next = device;
    			break;
		    }
		}
	    }
#ifdef HAVE_WIRINGPI_H
	    piUnlock(LOCK_DEVICES);
#endif
	    syslog(LOG_NOTICE, "Device %s added", device->uuid);
	    srv_send((char *)"211 Device %s added", device->uuid);
	    return 1;

	} else {
	    srv_send((char *)"503 Parameter error");
	    return 0;
	}
    }

    if (strcmp(opt, (char *)"DEL") == 0) {
	
#ifdef HAVE_WIRINGPI_H
	piLock(LOCK_DEVICES);
#endif
	rc = delete_Device(param);
#ifdef HAVE_WIRINGPI_H
	piUnlock(LOCK_DEVICES);
#endif
	if (rc) {
	    syslog(LOG_NOTICE, "Device %s deleted", param);
	    srv_send((char *)"211 Device %s deleted", param);
	    return 1;
	} else {
	    srv_send((char *)"440 No such device");
	    return 0;
	}
    }

    if (strcmp(opt, (char *)"GET") == 0) {
	for (device = Config.devices; device; device = device->next) {
	    if (strcmp(device->uuid, param) == 0) {
#ifdef HAVE_WIRINGPI_H
		piLock(LOCK_DEVICES);
#endif
		int	my_value = device->value;
		int	my_timestamp = (int)device->timestamp;
#ifdef HAVE_WIRINGPI_H
		piUnlock(LOCK_DEVICES);
#endif
		srv_send((char *)"213 Device record follows:");
		srv_send((char *)"TYPE,%s", DEVTYPE[device->type]);
		srv_send((char *)"ADDRESS,%s", device->address);
		srv_send((char *)"DIRECTION,%s", DEVDIR[device->direction]);
		srv_send((char *)"VALUE,%d", my_value);
		srv_send((char *)"OFFSET,%d", device->offset);
		srv_send((char *)"PRESENT,%s", DEVPRESENT[device->present]);
		srv_send((char *)"SUBDEVICE,%d", device->subdevice);
		srv_send((char *)"GPIOPIN,%d", device->gpiopin);
		srv_send((char *)"DESCRIPTION,%s", device->description);
		srv_send((char *)"INUSE,%d", device->inuse);
		srv_send((char *)"COMMENT,%s", device->comment);
		srv_send((char *)"TIMESTAMP,%d", my_timestamp);
		srv_send((char *)".");
		return 0;
	    }
	}
	srv_send((char *)"440 No such device");
	return 0;
    }

    if (strcmp(opt, (char *)"PUT") == 0) {
	for (device = Config.devices; device; device = device->next) {
	    if (strcmp(device->uuid, param) == 0) {
		while (1) {
		    rlen = srv_recv(ibuf);
    		    if (rlen == -1) {
			return 0;
		    }
		    if (strlen(ibuf)) {
			if (strcmp(ibuf, (char *)".") == 0) {
			    srv_send((char *)"219 Accepted Device record");
			    return 1;
			}
			kwd = strtok(ibuf, ",\0");
			val = strtok(NULL, "\0");
			if (kwd && val) {
			    if (strcmp(kwd, (char *)"TYPE") == 0) {
				for (i = 0; i < 8; i++) {
				    if (strcmp(val, DEVTYPE[i]) == 0) {
#ifdef HAVE_WIRINGPI_H
					piLock(LOCK_DEVICES);
#endif
					if (device->type != i)
					    syslog(LOG_NOTICE, "Device %s changed type %s to %s", device->uuid, DEVTYPE[device->type], DEVTYPE[i]);
					device->type = i;
#ifdef HAVE_WIRINGPI_H
					piUnlock(LOCK_DEVICES);
#endif
					break;
				    }
				}

			    } else if (strcmp(kwd, (char *)"DIRECTION") == 0) {
				for (i = 0; i < 7; i++) {
				    if (strcmp(val, DEVDIR[i]) == 0) {
#ifdef HAVE_WIRINGPI_H
					piLock(LOCK_DEVICES);
#endif
					if (device->direction != i)
					    syslog(LOG_NOTICE, "Device %s changed direction %s to %s", device->uuid, DEVDIR[device->type], DEVDIR[i]);
					device->direction = i;
#ifdef HAVE_WIRINGPI_H
					piUnlock(LOCK_DEVICES);
#endif
					break;
				    }
				}

			    } else if (strcmp(kwd, (char *)"VALUE") == 0) {
				if (sscanf(val, "%d", &ival) == 1) {
#ifdef HAVE_WIRINGPI_H
				    piLock(LOCK_DEVICES);
#endif
    				    if (device->value != ival)
					syslog(LOG_NOTICE, "Device %s changed value %d to %d", device->uuid, device->value, ival);
				    device->value = ival;
#ifdef HAVE_WIRINGPI_H
				    piUnlock(LOCK_DEVICES);
#endif
				}

			    } else if (strcmp(kwd, (char *)"OFFSET") == 0) {
				if (sscanf(val, "%d", &ival) == 1) {
#ifdef HAVE_WIRINGPI_H
				    piLock(LOCK_DEVICES);
#endif
    				    if (device->offset != ival)
					syslog(LOG_NOTICE, "Device %s changed offset %d to %d", device->uuid, device->offset, ival);
				    device->offset = ival;
#ifdef HAVE_WIRINGPI_H
				    piUnlock(LOCK_DEVICES);
#endif
				}

			    } else if (strcmp(kwd, (char *)"PRESENT") == 0) {
				for (i = 0; i < 4; i++) {
				    if (strcmp(val, DEVPRESENT[i]) == 0) {
#ifdef HAVE_WIRINGPI_H
					piLock(LOCK_DEVICES);
#endif
					if (device->present != i)
					    syslog(LOG_NOTICE, "Device %s changed present %s to %s", device->uuid, DEVPRESENT[device->present], DEVPRESENT[i]);
					device->present = i;
#ifdef HAVE_WIRINGPI_H
					piUnlock(LOCK_DEVICES);
#endif
					break;
				    }
				}

			    } else if (strcmp(kwd, (char *)"ADDRESS") == 0) {
#ifdef HAVE_WIRINGPI_H
				piLock(LOCK_DEVICES);
#endif
				if (device->address) {
				    if (strcmp(device->address, val))
					syslog(LOG_NOTICE, "Device %s changed address `%s' to `%s'", device->uuid, device->address, val);
				    free(device->address);
				}
				device->address = xstrcpy(val);
#ifdef HAVE_WIRINGPI_H               
				piUnlock(LOCK_DEVICES);
#endif

			    } else if (strcmp(kwd, (char *)"SUBDEVICE") == 0) {
				if (sscanf(val, "%d", &ival) == 1) {
#ifdef HAVE_WIRINGPI_H
				    piLock(LOCK_DEVICES);
#endif
				    if (device->subdevice != ival)
					syslog(LOG_NOTICE, "Device %s changed subdevice %d to %d", device->uuid, device->subdevice, ival);
				    device->subdevice = ival;
#ifdef HAVE_WIRINGPI_H               
				    piUnlock(LOCK_DEVICES);
#endif
				}

			    } else if (strcmp(kwd, (char *)"GPIOPIN") == 0) {
				if (sscanf(val, "%d", &ival) == 1) {
#ifdef HAVE_WIRINGPI_H
				    piLock(LOCK_DEVICES);
#endif
				    if (device->gpiopin != ival)
					syslog(LOG_NOTICE, "Device %s changed gpiopin %d to %d", device->uuid, device->gpiopin, ival);
				    device->gpiopin = ival;
#ifdef HAVE_WIRINGPI_H               
				    piUnlock(LOCK_DEVICES);
#endif
				}

			    } else if (strcmp(kwd, (char *)"DESCRIPTION") == 0) {
#ifdef HAVE_WIRINGPI_H
				piLock(LOCK_DEVICES);
#endif
				if (device->description) {
				    if (strcmp(device->description, val))
					syslog(LOG_NOTICE, "Device %s changed description `%s' to `%s'", device->uuid, device->description, val);
				    free(device->description);
				}
				device->description = xstrcpy(val);
#ifdef HAVE_WIRINGPI_H               
				piUnlock(LOCK_DEVICES);
#endif

			    } else if (strcmp(kwd, (char *)"COMMENT") == 0) {
#ifdef HAVE_WIRINGPI_H
				piLock(LOCK_DEVICES);
#endif
				if (device->comment) {
				    if (strcmp(device->comment, val))
					syslog(LOG_NOTICE, "Device %s changed comment `%s' to `%s'", device->uuid, device->comment, val);
				    free(device->comment);
				}
				device->comment = xstrcpy(val);
#ifdef HAVE_WIRINGPI_H               
				piUnlock(LOCK_DEVICES);
#endif

			    }
			}
		    }
		}
	    }
	}
	srv_send((char *)"440 No such device");
	return 0;
    }

    srv_send((char *)"504 Subcommand error");
    return 0;
}



/*
 * GLOBAL GET
 * GLOBAL PUT
 */
int cmd_global(char *buf)
{
    char	*opt, *kwd, *val, ibuf[SS_BUFSIZE];
    int		ival, rlen;

    opt = strtok(buf, " \0");
    opt = strtok(NULL, "\0");

    if (opt == NULL) {
	srv_send((char *)"501 Subcommand missing");
	return 0;
    }

    if (strcmp(opt, (char *)"HELP") == 0) {
	srv_send((char *)"100 Help text follows:");
	srv_send((char *)"Recognized commands:");
	srv_send((char *)"GLOBAL GET                    Get global settings");
	srv_send((char *)"GLOBAL PUT                    Put global settings");
	srv_send((char *)".");
	return 0;
    }

    if (strcmp(opt, (char *)"GET") == 0) {
	srv_send((char *)"213 Global Settings record follows:");
	srv_send((char *)"RELEASE,%s", VERSION);
	srv_send((char *)"NAME,%s", Config.name);
	srv_send((char *)"PORT,%d", Config.my_port);
	srv_send((char *)"TEMPFORMAT,%c", Config.tempFormat);
	srv_send((char *)"TEMP_ADDRESS,%s", Config.temp_address);
	srv_send((char *)"TEMP_STATE,%s", TEMPSTATE[Config.temp_state]);
	srv_send((char *)"TEMP_VALUE,%.1f", Config.temp_value / 1000.0);
	srv_send((char *)"HUM_ADDRESS,%s", Config.hum_address);
	srv_send((char *)"HUM_STATE,%s", TEMPSTATE[Config.hum_state]);
	srv_send((char *)"HUM_VALUE,%.0f", Config.hum_value / 1000.0);
	srv_send((char *)"TEMP_HUM_IDX,%d", Config.temp_hum_idx);
	srv_send((char *)"LCD_COLS,%d", Config.lcd_cols);
	srv_send((char *)"LCD_ROWS,%d", Config.lcd_rows);
	srv_send((char *)"NEXT_UNIT,%d", Config.next_unit);
	srv_send((char *)"MQTT_HOST,%s", Config.mqtt_host);
	srv_send((char *)"MQTT_PORT,%d", Config.mqtt_port);
	srv_send((char *)"MQTT_USER,%s", Config.mqtt_username);
	srv_send((char *)"MQTT_PASS,%s", Config.mqtt_password);
	srv_send((char *)".");
	return 0;
    }

    if (strcmp(opt, (char *)"PUT") == 0) {
	bool mqtt_reconnect = false;
	while (1) {
	    rlen = srv_recv(ibuf);
	    if (rlen == -1) {
		return 0;
	    }
	    if (strlen(ibuf)) {
		if (strcmp(ibuf, (char *)".") == 0) {
		    srv_send((char *)"219 Accepted Global record");
		    if (mqtt_reconnect)
			mqtt_connect();
		    return 1;
		}
		kwd = strtok(ibuf, ",\0");
		val = strtok(NULL, "\0");
		if (kwd) {
		    if (strcmp(kwd, (char *)"NAME") == 0) {
			if (val && Config.name && strcmp(val, Config.name))
			    syslog(LOG_NOTICE, "Global name `%s' to `%s'", Config.name, val);
			if (Config.name)
			    free(Config.name);
			if (val)
			    Config.name = xstrcpy(val);
			else
			    Config.name = NULL;

		    } else if (val && (strcmp(kwd, (char *)"PORT") == 0)) {
			if (sscanf(val, "%d", &ival) == 1) {
			    if (Config.my_port != ival)
			    	syslog(LOG_NOTICE, "Global port %d to %d", Config.my_port, ival);
			    Config.my_port = ival;
			}

		    } else if (val && (strcmp(kwd, (char *)"TEMPFORMAT") == 0)) {
			if ((val[0] == 'C') || (val[0] == 'F')) {
			    if (Config.tempFormat != val[0])
				syslog(LOG_NOTICE, "Global port %c to %c", Config.tempFormat, val[0]);
			    Config.tempFormat = val[0];
			}

		    } else if (strcmp(kwd, (char *)"TEMP_ADDRESS") == 0) {
			if (val && Config.temp_address && (strcmp(val, Config.temp_address)))
			    syslog(LOG_NOTICE, "Global temperature address `%s' to `%s'", Config.temp_address, val);
		        if (Config.temp_address) {
			    device_count(FALSE, Config.temp_address);
			    free(Config.temp_address);
			}
			if (val) {
			    Config.temp_address = xstrcpy(val);
			    device_count(TRUE, Config.temp_address);
			} else
			    Config.temp_address = NULL;

		    } else if (strcmp(kwd, (char *)"HUM_ADDRESS") == 0) {
			if (val && Config.hum_address && (strcmp(val, Config.hum_address)))
			    syslog(LOG_NOTICE, "Global humidity address `%s' to `%s'", Config.hum_address, val);
			if (Config.hum_address) {
			    device_count(FALSE, Config.hum_address);
			    free(Config.hum_address);
			}
			if (val) {
			    Config.hum_address = xstrcpy(val);
			    device_count(TRUE, Config.hum_address);
			} else
			    Config.hum_address = NULL;

		    } else if (val && (strcmp(kwd, (char *)"TEMP_HUM_IDX") == 0)) {
			if (sscanf(val, "%d", &ival) == 1) {
			    if (Config.temp_hum_idx != ival)
				syslog(LOG_NOTICE, "Global Temp/Humidity idx %d to %d", Config.temp_hum_idx, ival);
			    Config.temp_hum_idx = ival;
			}

		    } else if (val && (strcmp(kwd, (char *)"LCD_COLS") == 0)) {
			if (sscanf(val, "%d", &ival) == 1) {
			    if (Config.lcd_cols != ival)
				syslog(LOG_NOTICE, "Global LCD columns %d to %d", Config.lcd_cols, ival);
			    Config.lcd_cols = ival;
			}

		    } else if (val && (strcmp(kwd, (char *)"LCD_ROWS") == 0)) {
			if (sscanf(val, "%d", &ival) == 1) {
			    if (Config.lcd_rows != ival)
				syslog(LOG_NOTICE, "Global LCD rows %d to %d", Config.lcd_rows, ival);
			    Config.lcd_rows = ival;
			}
		
		    } else if (strcmp(kwd, (char *)"MQTT_HOST") == 0) {
			if (val && Config.mqtt_host && (strcmp(val, Config.mqtt_host)))
			    syslog(LOG_NOTICE, "Global MQTT host `%s' to `%s'", Config.mqtt_host, val);
			mqtt_disconnect();
			mqtt_reconnect = true;
			if (Config.mqtt_host)
			    free(Config.mqtt_host);
			if (val)
			    Config.mqtt_host = xstrcpy(val);
			else
			    Config.mqtt_host = NULL;

		    } else if (val && (strcmp(kwd, (char *)"MQTT_PORT") == 0)) {
			if (sscanf(val, "%d", &ival) == 1) {
			    if (Config.mqtt_port != ival) {
				syslog(LOG_NOTICE, "Global MQTT port %d to %d", Config.mqtt_port, ival);
				mqtt_disconnect();
				mqtt_reconnect = true;
			        Config.mqtt_port = ival;
			    }
			}

		    } else if (strcmp(kwd, (char *)"MQTT_USER") == 0) {
			if (val && Config.mqtt_username && (strcmp(val, Config.mqtt_username)))
			    syslog(LOG_NOTICE, "Global MQTT username `%s' to `%s'", Config.mqtt_username, val);
			mqtt_disconnect();
			mqtt_reconnect = true;
			if (Config.mqtt_username)
			    free(Config.mqtt_username);
			if (val)
			    Config.mqtt_username = xstrcpy(val);
			else
			    Config.mqtt_username = NULL;

		    } else if (strcmp(kwd, (char *)"MQTT_PASS") == 0) {
			if (val && Config.mqtt_password && (strcmp(val, Config.mqtt_password)))
			    syslog(LOG_NOTICE, "Global MQTT password `%s' to `%s'", Config.mqtt_password, val);
			mqtt_disconnect();
			mqtt_reconnect = true;
			if (Config.mqtt_password)
			    free(Config.mqtt_password);
			if (val)
			    Config.mqtt_password = xstrcpy(val);
			else
			    Config.mqtt_password = NULL;

		    }
		}
	    }
	}
    }

    srv_send((char *)"504 Subcommand error");
    return 0;
}



/*
 * LIST
 * LIST LOG
 */
int cmd_list(char *buf)
{
    char		*opt, *param, *filename, q[5], hr[3], buffer[256], outbuf[256];
    char		*date_n, *mode_n, *air_n, *beer_n, *target_lo_n, *target_hi_n, *heater_n, *cooler_n, *room_n;
    char		*heater_u, *cooler_u;
    int			heater_l = 0, cooler_l = 0, h = 0, c = 0, heat_used = 0, cool_used = 0, lines = 0, graphstep = 0;
    units_list		*unit;
    FILE		*fp;
    float		room_t;

    opt = strtok(buf, " \0");
    opt = strtok(NULL, " \0");

    if (opt == NULL) {
	/*
	 * Default, list available units
	 */
	srv_send((char *)"212 Fermenter list follows:");
	for (unit = Config.units; unit; unit = unit->next) {
	    srv_send((char *)"%s,%s,%s", unit->uuid, unit->name, UNITMODE[unit->mode]);
	}
	srv_send((char *)".");
	return 0;

    } else if (strcmp(opt, (char *)"HELP") == 0) {
	srv_send((char *)"100 Help text follows:");
	srv_send((char *)"Recognized commands:");
	srv_send((char *)"LIST                           List available units");
	srv_send((char *)"LIST LOG uuid                  List logfile of unit by uuid");
	srv_send((char *)".");
	return 0;

    } else if (strcmp(opt, (char *)"LOG") == 0) {

	param = strtok(NULL, "\0");
	if (param == NULL) {
	    srv_send((char *)"502 Parameter missing");
	    return 0;
	}

	q[0] = q[1] = q[2] = q[3] = q[4] = 'a';
	for (unit = Config.units; unit; unit = unit->next) {
	    if (strcmp(param, unit->uuid) == 0)
		break;
	}

	srv_send((char *)"212 Logfile list follows:");
	if (getenv((char *)"USER") == NULL) {
	    filename = xstrcpy((char *)"/root");
	} else {
	    filename = xstrcpy(getenv((char *)"HOME"));
	}
	filename = xstrcat(filename, (char *)"/.thermferm/log/");
	filename = xstrcat(filename, unit->name);
	filename = xstrcat(filename, (char *)".log");
	if ((fp = fopen(filename, "r"))) {

	    /*
	     * Count the lines in the logfile
	     */
	    lines = 0;
	    while (fgets(buffer, sizeof(buffer)-1, fp) != NULL) {
		lines++;
	    }
	    fseek(fp, 0L, SEEK_SET);
	    /*
	     * We have counted the lines in the logfile including the header lines.
	     * The header lines should be ignored but there are so few of them, we
	     * just include them in the total.
	     * Now find a reasonable interval of lines to sent to the client.
	     */
	    for (graphstep = 1; graphstep <= MAX_INTERVALS; graphstep++) {
		if (lines < GRAPH_DATALINES[graphstep]) {
		    break;
		}
	    }
	    if (graphstep > MAX_INTERVALS)
		graphstep = MAX_INTERVALS;
	    syslog(LOG_NOTICE, "LIST LOG %s: lines=%d, interval=%d, graphstep=%d", unit->name, lines, GRAPH_INTERVAL[graphstep], graphstep);

	    while (fgets(buffer, sizeof(buffer)-1, fp) != NULL) {
		/*
		 * 2014-11-15 18:39,BEER,20.312,19.750,20.0,0,NA,NA,NA,78105,NA,NA
		 */
		hr[0] = q[0] = buffer[11];
		hr[1] = q[1] = buffer[12];
		q[2] = buffer[14];
		q[3] = buffer[15];
		hr[2] = '\0';
		buffer[strlen(buffer) -1] = '\0';
		date_n = strtok(buffer, ",\0");		/* timestamp		*/
		mode_n = strtok(NULL, ",\0");		/* unit mode		*/
		air_n = strtok(NULL, ",\0");		/* air temp		*/
		beer_n = strtok(NULL, ",\0");		/* beer temp		*/
		target_lo_n = strtok(NULL, ",\0");	/* target low temp	*/
		heater_n = strtok(NULL, ",\0");		/* current heater state	*/
		cooler_n = strtok(NULL, ",\0");		/* current cooler state	*/
		heater_u = strtok(NULL, ",\0");		/* current fan state	*/
		heater_u = strtok(NULL, ",\0");		/* current door state	*/
		heater_u = strtok(NULL, ",\0");		/* heater use counter	*/
		cooler_u = strtok(NULL, ",\0");		/* cooler use counter	*/
		room_n = strtok(NULL, ",\0");		/* fan use counter	*/
		room_n = strtok(NULL, ",\0");		/* room temperature	*/
		target_hi_n = strtok(NULL, ",\0");	/* target high temp	*/

		if (strncmp(mode_n, (char *)"Mode", 4)) {
		    /*
		     * Output a line at the right intervals
		     */
		    int hour = atoi(hr);
		    if (((graphstep == 1)) ||
			((graphstep == 2) && (q[3] == '0' || q[3] == '5')) ||
			((graphstep == 3) && ((q[2] == '0' && q[3] == '0') || (q[2] == '1' && q[3] == '5') || (q[2] == '3' && q[3] == '0') || (q[2] == '4' && q[3] == '5'))) ||
			((graphstep == 4) && ((q[2] == '0' && q[3] == '0') || (q[2] == '3' && q[3] == '0'))) ||
			((graphstep == 5) && (q[2] == '0' && q[3] == '0')) ||
			((graphstep == 6) && (hour % 2 == 0) && (q[2] == '0' && q[3] == '0')) ||        /* 120 minutes  */
			((graphstep == 7) && (hour % 4 == 0) && (q[2] == '0' && q[3] == '0')) ||        /* 240 minutes  */
			((graphstep >= 8) && (hour % 8 == 0) && (q[2] == '0' && q[3] == '0')) ) {	/* 480 minutes	*/
			heat_used = cool_used = 0;
			if (strcmp(heater_u, "NA") && (sscanf(heater_u, "%d", &h) == 1)) {
			    if (h && heater_l) {
				heat_used = ((h - heater_l) * 100) / (GRAPH_INTERVAL[graphstep] * 60);
			    }
			}
			if (strcmp(cooler_u, "NA") && (sscanf(cooler_u, "%d", &c) == 1)) {
			    if (c && cooler_l) {
				cool_used = ((c - cooler_l) * 100) / (GRAPH_INTERVAL[graphstep] * 60);
			    }
			}
			if (room_n)
			    sscanf(room_n, "%f", &room_t);
			else
			    room_t = 0.0;
			if (target_hi_n == NULL)
			    target_hi_n = target_lo_n;
			snprintf(outbuf, 255, "%s,%s,%s,%s,%s,%s,%s,%d,%d,%.1f,%s", 
					    date_n, mode_n, air_n, beer_n, target_lo_n, heater_n, cooler_n, heat_used, cool_used, room_t, target_hi_n);
		    	srv_send(outbuf);
			if (h && strcmp(heater_u, "NA"))
			    heater_l = h;
			if (c & strcmp(cooler_u, "NA"))
			    cooler_l = c;
		    }
	    	}
	    }
	}
	free(filename);
	filename = NULL;
	srv_send((char *)".");
	return 0;
    }

    srv_send((char *)"504 Subcommand error");
    return 0;
}



/*
 * PROFILE ADD name	Add a new profile
 * PROFILE DEL uuid	Delete profile with uuid
 * PROFILE LIST		List available profiles
 * PROFILE GET uuid     Get profile record
 * PROFILE PUT uuid     Put profile record
 * PROFILE GETS uuid	Get profile steps list
 * PROFILE PUTS uuid	Put profile steps list
 */
int cmd_profile(char *buf)
{
    char                ibuf[SS_BUFSIZE], *sstep, *rest, *tlarg, *tharg, *frarg, *param, *kwd, *val;
    int                 j, ival, rlen, istep, irest, ifrarg;
    float		ftlarg, ftharg, fval;
    char		*opt;
    profiles_list	*profile, *tmpp;
    prof_step		*step, *olds;
    uuid_t		uu;

    opt = strtok(buf, " \0");
    opt = strtok(NULL, " \0");

    if (opt == NULL) {
	srv_send((char *)"501 Subcommand missing");
	return 0;
    }

    if (strcmp(opt, (char *)"HELP") == 0) {
	srv_send((char *)"100 Help text follows:");
	srv_send((char *)"Recognized commands:");
	srv_send((char *)"PROFILE uuid,name             Profile rename");
	srv_send((char *)"PROFILE ADD name              Add new Profile with name");
	srv_send((char *)"PROFILE DEL uuid              Delete Profile by uuid");
	srv_send((char *)"PROFILE LIST                  List available profiles");
	srv_send((char *)"PROFILE GET uuid              Get Profile record by uuid");
	srv_send((char *)"PROFILE PUT uuid              Put Profile record by uuid");
	srv_send((char *)"PROFILE GETS uuid             Profile get steps list");
	srv_send((char *)"PROFILE PUTS uuid             Profile put steps list");
	srv_send((char *)".");
	return 0;
    }

    if (strcmp(opt, (char *)"LIST") == 0) {
	/*
	 * Fermenting profiles
	 */
	srv_send((char *)"212 Profiles list follows:");
	for (profile = Config.profiles; profile; profile = profile->next) {
	    j = 0;
	    for (step = profile->steps; step; step = step->next)
		j++;
	    srv_send((char *)"%s,%s,%d,%d", profile->uuid, profile->name, j, profile->busy);
	}
	srv_send((char *)".");
	return 0;
    }

    param = strtok(NULL, "\0");
    if (param == NULL) {
	srv_send((char *)"502 Parameter missing");
	return 0;
    }

    if (strcmp(opt, (char *)"ADD") == 0) {
	profile = (profiles_list *)malloc(sizeof(profiles_list));
	profile->next = NULL;
	profile->version = 1;
	profile->uuid = malloc(37);
	uuid_generate(uu);
	uuid_unparse(uu, profile->uuid);
	profile->name = xstrcpy(param);
	profile->busy = profile->fridge_mode = 0;
	profile->inittemp_lo = 19.8;
	profile->inittemp_hi = 20.2;
	profile->steps = NULL;
	if (Config.profiles == NULL) {
	    Config.profiles = profile;
	} else {
	    for (tmpp = Config.profiles; tmpp; tmpp = tmpp->next) {
		if (tmpp->next == NULL) {
		    tmpp->next = profile;
		    break;
		}
	    }
	}

	syslog(LOG_NOTICE, "Profile %s added", profile->uuid);
	srv_send((char *)"211 Profile %s added", profile->uuid);
	return 1;


    } else if (strcmp(opt, (char *)"DEL") == 0) {
	if (delete_Profile(param)) {
	    syslog(LOG_NOTICE, "Profile %s deleted", param);
	    srv_send((char *)"211 Profile %s deleted", param);
	    return 1;
	} else {
	    srv_send((char *)"440 No such profile");
	    return 0;
	}
    
    } else if (strcmp(opt, (char *)"GET") == 0) {
	for (profile = Config.profiles; profile; profile = profile->next) {
	    if (strcmp(profile->uuid, param) == 0) {
		srv_send((char *)"213 Profile record follows:");
		srv_send((char *)"UUID,%s", profile->uuid);
		srv_send((char *)"NAME,%s", profile->name);
		srv_send((char *)"INITTEMP_LO,%.1f", profile->inittemp_lo);
		srv_send((char *)"INITTEMP_HI,%.1f", profile->inittemp_hi);
		srv_send((char *)"FRIDGE_MODE,%d", profile->fridge_mode);
		srv_send((char *)".");
		return 0;
	    }
	}
	srv_send((char *)"440 No such profile");
	return 0;

    } else if (strcmp(opt, (char *)"PUT") == 0) {
	for (profile = Config.profiles; profile; profile = profile->next) {
	    if (strcmp(profile->uuid, param) == 0) {
		while (1) {
		    rlen = srv_recv(ibuf);
		    if (rlen == -1) {
			return 0;
		    }
		    if (strlen(ibuf)) {
			if (strcmp(ibuf, (char *)".") == 0) {
			    srv_send((char *)"219 Accepted Profile record");
			    return 1;
			}
			kwd = strtok(ibuf, ",\0");
			val = strtok(NULL, "\0");
			if (kwd && val) {
			    if (strcmp(kwd, (char *)"NAME") == 0) {
				if (profile->name) {
				    if (strcmp(profile->name, val))
					syslog(LOG_NOTICE, "Profile %s name `%s' to `%s'", profile->uuid, profile->name, val);
				    free(profile->name);
				}
				profile->name = xstrcpy(val);
			    } else if (strcmp(kwd, (char *)"INITTEMP_LO") == 0) {
				if (sscanf(val, "%f", &fval) == 1) {
				    if (profile->inittemp_lo != fval)
					syslog(LOG_NOTICE, "Profile %s initial temperature low %.1f to %.1f", profile->uuid, profile->inittemp_lo, fval);
				    profile->inittemp_lo = fval;
				}
			    } else if (strcmp(kwd, (char *)"INITTEMP_HI") == 0) {
				if (sscanf(val, "%f", &fval) == 1) {
				    if (profile->inittemp_hi != fval)
					syslog(LOG_NOTICE, "Profile %s initial temperature high %.1f to %.1f", profile->uuid, profile->inittemp_hi, fval);
				    profile->inittemp_hi = fval;
				}
			    } else if (strcmp(kwd, (char *)"FRIDGE_MODE") == 0) {
				if (sscanf(val, "%d", &ival) == 1) {
				    if (profile->fridge_mode != ival)
					syslog(LOG_NOTICE, "Profile %s fridge mode %d to %d", profile->uuid, profile->fridge_mode, ival);
				    profile->fridge_mode = ival;
				}
			    }
			}
		    }
		}
	    }               
	}
	srv_send((char *)"440 No such profile");
	return 0;

    } else if (strcmp(opt, (char *)"GETS") == 0) {

	for (profile = Config.profiles; profile; profile = profile->next) {
	    if (strcmp(profile->uuid, param) == 0) {
		srv_send((char *)"215 Profile steps follow:");
		for (step = profile->steps; step; step = step->next) {
		    srv_send((char *)"%d,%d,%.1f,%.1f,%d", step->steptime, step->resttime, step->target_lo, step->target_hi, step->fridge_mode);
		}
		srv_send((char *)".");
		return 0;
	    }
	}

	srv_send((char *)"440 No such profile");
	return 0;

    } else if (strcmp(opt, (char *)"PUTS") == 0) {

	for (profile = Config.profiles; profile; profile = profile->next) {
	    if (strcmp(profile->uuid, param) == 0) {

		if (profile->steps) {
		    syslog(LOG_NOTICE, "PROFILE PUTS %s erased all old steps", profile->uuid);
		    for (step = profile->steps; step; step = olds) {
			olds = step->next;
			free(step);
		    }
		    profile->steps = NULL;		    
		}

		j = 0;
            	while (1) {	
		    rlen = srv_recv(ibuf);
	    	    if (rlen == -1) {
		    	return 0;
	    	    } else {
	    	    	if (strlen(ibuf)) {
		    	    if (strcmp(ibuf, (char *)".") == 0) {

		    	    	srv_send((char *)"219 Accepted Profile steps");
		    	    	return 1;
		    	    }
			    sstep = strtok(ibuf, ",\0");
			    rest = strtok(NULL, ",\0");
			    tlarg = strtok(NULL, ",\0");
			    tharg = strtok(NULL, ",\0");
			    frarg = strtok(NULL, "\0");

			    if ((sscanf(sstep, "%d", &istep) == 1) &&
				(sscanf(rest, "%d", &irest) == 1) &&
				(sscanf(tlarg, "%f", &ftlarg) == 1) && 
				(sscanf(tharg, "%f", &ftharg) == 1) &&
				(sscanf(frarg, "%d", &ifrarg) == 1)) {

				j++;
				syslog(LOG_NOTICE, "PROFILE PUTS %s add step %d: steptime=%d resttime=%d target=%.1f..%.1f fridge_mode=%d", 
						profile->uuid, j, istep, irest, ftlarg, ftharg, ifrarg);
				step = (prof_step *)malloc(sizeof(prof_step));
				step->next = NULL;
				step->version = 1;
				step->steptime = istep;
				step->resttime = irest;
				step->target_lo = ftlarg;
				step->target_hi = ftharg;
				step->fridge_mode = ifrarg;

				if (profile->steps == NULL) {
				    profile->steps = step;
				} else {
				    for (olds = profile->steps; olds; olds = olds->next) {
					if (olds->next == NULL) {
					    olds->next = step;
					    break;
					}
				    }
				}
			    }
		    	}
	    	    }
		}
	    }
	}

	srv_send((char *)"440 No such profile");
	return 0;
    }

    srv_send((char *)"504 Subcommand error");
    return 0;
}



#ifdef USE_SIMULATOR
int delete_Simulator(char *uuid)
{
    simulator_list	*current = Config.simulators;
    simulator_list	*previous = NULL;

    while (current) {
	if (strcmp(current->uuid, uuid) == 0) {
	    if (previous == NULL) {
		Config.simulators = current->next;
	    	free(current->uuid);
	    	current->uuid = NULL;
	    	free(current->name);
	    	current->name = NULL;
	    	free(current);
	    	return 1;
	    } else {
		free(current->uuid);
		current->uuid = NULL;
		free(current->name);
		current->name = NULL;
		previous->next = current->next;
		free(current);
		current = previous->next;
		return 1;
	    }
	} else {
	    previous = current;
	    current = current->next;
	}
    }
    return 0;
}



/*
 * SIMULATOR ADD name
 * SIMULATOR DEL uuid
 * SIMULATOR LIST
 * SIMULATOR GET uuid
 * SIMULATOR PUT uuid
 */
int cmd_simulator(char *buf)
{
    char		*opt, *param, *kwd, *val, ibuf[SS_BUFSIZE];
    simulator_list	*simulator, *tmps;
    int			rc, rlen, ival;
    float		fval;
    uuid_t		uu;

    opt = strtok(buf, " \0");
    opt = strtok(NULL, " \0");

    if (opt == NULL) {
	srv_send((char *)"501 Subcommand missing");
	return 0;
    }
    param = strtok(NULL, "\0");

    if (strcmp(opt, (char *)"HELP") == 0) {
	srv_send((char *)"100 Help text follows:");
	srv_send((char *)"Recognized commands:");
	srv_send((char *)"SIMULATOR ADD name            Add a new Simulator with name");
	srv_send((char *)"SIMULATOR DEL uuid            Delete Simulator by uuid");
	srv_send((char *)"SIMULATOR LIST                List all Simulators");
	srv_send((char *)"SIMULATOR GET uuid            Get Simulator record by uuid");
	srv_send((char *)"SIMULATOR PUT uuid            Put Simulator record by uuid");
	srv_send((char *)".");
	return 0;
    }

    if (strcmp(opt, (char *)"LIST") == 0) {
	srv_send((char *)"212 Simulators list follows:");
	for (simulator = Config.simulators; simulator; simulator = simulator->next) {
	    srv_send((char *)"%s,%s", simulator->uuid, simulator->name);
	}
	srv_send((char *)".");
	return 0;
    }

    if (param == NULL) {
	srv_send((char *)"502 Parameter missing");
	return 0;
    }

    if (strcmp(opt, (char *)"ADD") == 0) {

	/*
	 * For now, only one simulator is allowed.
	 */
	if (Config.simulators) {
	    srv_send((char *)"441 Maximum simulators reached");
	    return 0;
	}

	simulator = (simulator_list *)malloc(sizeof(simulator_list));
	simulator->next = NULL;
	simulator->version = 1;
	simulator->uuid = malloc(37);
	uuid_generate(uu);
	uuid_unparse(uu, simulator->uuid);
	simulator->name = xstrcpy(param);
	simulator->volume_air = 150;
	simulator->volume_beer = 50;
	simulator->room_temperature = simulator->air_temperature = simulator->beer_temperature = simulator->s_cool_temp = simulator->s_heat_temp = 20.0;
	simulator->cooler_temp = -3.0;	/* Cooling temperature */
	simulator->cooler_time = 720;	/* About 12 minutes for the cooler plate */
	simulator->cooler_size = 0.8;	/* 0.8 square meter cooler plate */
	simulator->heater_temp = 150.0;	/* Heating temperature */
	simulator->heater_time = 3;	/* 3 seconds to heat-up	*/
	simulator->heater_size = 0.01;	/* 0.01 square meter heater plate */
	simulator->heater_state = simulator->cooler_state = 0;
	simulator->frigo_isolation = 0.002;
	simulator->s_yeast_heat = 0.0;
	simulator->s_yeast_started = simulator->s_cool_changed = simulator->s_heat_changed = (int)0;

	if (Config.simulators == NULL) {
	    Config.simulators = simulator;
	} else {
	    for (tmps = Config.simulators; tmps; tmps = tmps->next) {
		if (tmps->next == NULL) {
		    tmps->next = simulator;
		    break;
		}
	    }
	}

	syslog(LOG_NOTICE, "Simulator %s added", simulator->uuid);
	srv_send((char *)"211 Simulator %s added", simulator->uuid);
	return 1;
    }

    if (strcmp(opt, (char *)"DEL") == 0) {
	rc = delete_Simulator(param);
	if (rc) {
	    syslog(LOG_NOTICE, "Simulator %s deleted", param);
	    srv_send((char *)"211 Simulator %s deleted", param);
	    return 1;
	} else {
	    srv_send((char *)"440 No such simulator");
	    return 0;
	}
    }

    if (strcmp(opt, (char *)"GET") == 0) {
	for (simulator = Config.simulators; simulator; simulator = simulator->next) {
	    if (strcmp(simulator->uuid, param) == 0) {
		srv_send((char *)"213 Simulator record follows:");
		srv_send((char *)"NAME,%s", simulator->name);
		srv_send((char *)"VOLUME_AIR,%d", simulator->volume_air);
		srv_send((char *)"VOLUME_BEER,%d", simulator->volume_beer);
		srv_send((char *)"ROOM_TEMPERATURE,%.1f", simulator->room_temperature);
		srv_send((char *)"AIR_TEMPERATURE,%.3f", simulator->air_temperature);
		srv_send((char *)"BEER_TEMPERATURE,%.3f", simulator->beer_temperature);
		srv_send((char *)"COOLER_TEMP,%.1f", simulator->cooler_temp);
		srv_send((char *)"COOLER_TIME,%d", simulator->cooler_time);
		srv_send((char *)"COOLER_SIZE,%.3f", simulator->cooler_size);
		srv_send((char *)"HEATER_TEMP,%.1f", simulator->heater_temp);
		srv_send((char *)"HEATER_TIME,%d", simulator->heater_time);
		srv_send((char *)"HEATER_SIZE,%.3f", simulator->heater_size);
		srv_send((char *)"HEATER_STATE,%d", simulator->heater_state);
		srv_send((char *)"COOLER_STATE,%d", simulator->cooler_state);
		srv_send((char *)"FRIGO_ISOLATION,%.3f", simulator->frigo_isolation);
		srv_send((char *)".");
		return 0;
	    }
	}
	srv_send((char *)"440 No such simulator");
	return 0;
    }

    if (strcmp(opt, (char *)"PUT") == 0) {
	for (simulator = Config.simulators; simulator; simulator = simulator->next) {
	    if (strcmp(simulator->uuid, param) == 0) {
		while (1) {
		    rlen = srv_recv(ibuf);
		    if (rlen == -1) {
			return 0;
		    }
		    if (strlen(ibuf)) {
			if (strcmp(ibuf, (char *)".") == 0) {
			    srv_send((char *)"219 Accepted Simulator record");
			    return 1;
			}
			kwd = strtok(ibuf, ",\0");
			val = strtok(NULL, "\0");
			if (kwd && val) {

			    if (strcmp(kwd, (char *)"NAME") == 0) {
				if (simulator->name) {
				    if (strcmp(simulator->name, val))
					syslog(LOG_NOTICE, "Simulator %s name `%s' to `%s'", simulator->uuid, simulator->name, val);
				    free(simulator->name);
				}
				simulator->name = xstrcpy(val);

			    } else if (strcmp(kwd, (char *)"VOLUME_AIR") == 0) {
				if (sscanf(val, "%d", &ival) == 1) {
				    if (simulator->volume_air != ival)
					syslog(LOG_NOTICE, "Simulator %s volume air %d to %d", simulator->uuid, simulator->volume_air, ival);
				    simulator->volume_air = ival;
				}

			    } else if (strcmp(kwd, (char *)"VOLUME_BEER") == 0) {
				if (sscanf(val, "%d", &ival) == 1) {
				    if (simulator->volume_beer != ival)
					syslog(LOG_NOTICE, "Simulator %s volume beer %d to %d", simulator->uuid, simulator->volume_beer, ival);
				    simulator->volume_beer = ival;
				}

			    } else if (strcmp(kwd, (char *)"ROOM_TEMPERATURE") == 0) {
				if (sscanf(val, "%f", &fval) == 1) {
				    if (simulator->room_temperature != fval)
					syslog(LOG_NOTICE, "Simulator %s room temperature %.1f to %.1f", simulator->uuid, simulator->room_temperature, fval);
				    simulator->room_temperature = fval;
				}

			    } else if (strcmp(kwd, (char *)"AIR_TEMPERATURE") == 0) {
				if (sscanf(val, "%f", &fval) == 1) {
				    if (simulator->air_temperature != fval)
					syslog(LOG_NOTICE, "Simulator %s air temperature %.1f to %.1f", simulator->uuid, simulator->air_temperature, fval);
				    simulator->air_temperature = fval;
				}

			    } else if (strcmp(kwd, (char *)"BEER_TEMPERATURE") == 0) {
				if (sscanf(val, "%f", &fval) == 1) {
				    if (simulator->beer_temperature != fval)
					syslog(LOG_NOTICE, "Simulator %s beer temperature %.1f to %.1f", simulator->uuid, simulator->beer_temperature, fval);
				    simulator->beer_temperature = fval;
				}

			    } else if (strcmp(kwd, (char *)"COOLER_TEMP") == 0) {
				if (sscanf(val, "%f", &fval) == 1) {
				    if (simulator->cooler_temp != fval)
					syslog(LOG_NOTICE, "Simulator %s cooler temperature %.1f to %.1f", simulator->uuid, simulator->cooler_temp, fval);
				    simulator->cooler_temp = fval;
				}

			    } else if (strcmp(kwd, (char *)"COOLER_TIME") == 0) {
				if (sscanf(val, "%d", &ival) == 1) {
				    if (simulator->cooler_time != ival)
					syslog(LOG_NOTICE, "Simulator %s cooler time %d to %d", simulator->uuid, simulator->cooler_time, ival);
				    simulator->cooler_time = ival;
				}
	
			    } else if (strcmp(kwd, (char *)"COOLER_SIZE") == 0) {
				if (sscanf(val, "%f", &fval) == 1) {
				    if (simulator->cooler_size != fval)
					syslog(LOG_NOTICE, "Simulator %s cooler size %.1f to %.1f", simulator->uuid, simulator->cooler_size, fval);
				    simulator->cooler_size = fval;
				}

			    } else if (strcmp(kwd, (char *)"HEATER_TEMP") == 0) {
				if (sscanf(val, "%f", &fval) == 1) {
				    if (simulator->heater_temp != fval)
					syslog(LOG_NOTICE, "Simulator %s heater temperature %.1f to %.1f", simulator->uuid, simulator->heater_temp, fval);
				    simulator->heater_temp = fval;
				}

			    } else if (strcmp(kwd, (char *)"HEATER_TIME") == 0) {
				if (sscanf(val, "%d", &ival) == 1) {
				    if (simulator->heater_time != ival)
					syslog(LOG_NOTICE, "Simulator %s heater time %d to %d", simulator->uuid, simulator->heater_time, ival);
				    simulator->heater_time = ival;
				}

			    } else if (strcmp(kwd, (char *)"HEATER_SIZE") == 0) {
				if (sscanf(val, "%f", &fval) == 1) {
				    if (simulator->heater_size != fval)
					syslog(LOG_NOTICE, "Simulator %s heater size %.1f to %.1f", simulator->uuid, simulator->heater_size, fval);
				    simulator->heater_size = fval;
				}

			    } else if (strcmp(kwd, (char *)"HEATER_STATE") == 0) {
				if (sscanf(val, "%d", &ival) == 1) {
				    if (simulator->heater_state != ival)
					syslog(LOG_NOTICE, "Simulator %s heater state %d to %d", simulator->uuid, simulator->heater_state, ival);
				    simulator->heater_state = ival;
				}

			    } else if (strcmp(kwd, (char *)"COOLER_STATE") == 0) {
				if (sscanf(val, "%d", &ival) == 1) {
				    if (simulator->cooler_state != ival)
					syslog(LOG_NOTICE, "Simulator %s cooler state %d to %d", simulator->uuid, simulator->cooler_state, ival);
				    simulator->cooler_state = ival;
				}

			    } else if (strcmp(kwd, (char *)"FRIGO_ISOLATION") == 0) {
				if (sscanf(val, "%f", &fval) == 1) {
				    if (simulator->frigo_isolation != fval)
					syslog(LOG_NOTICE, "Simulator %s frigo isolation %.1f to %.1f", simulator->uuid, simulator->frigo_isolation, fval);
				    simulator->frigo_isolation = fval;
				}

			    }
			}
		    }
		}
	    }
	}
	srv_send((char *)"440 No such simulator");
	return 0;
    }

    srv_send((char *)"504 Subcommand error");
    return 0;
}
#endif



int delete_Unit(char *uuid)
{
    units_list  *current = Config.units;
    units_list	*previous = NULL;

    while (current) {
	if (strcmp(current->uuid, uuid) == 0) {
	    if (previous == NULL) {
		Config.units = current->next;
		free(current->uuid);
		current->uuid = NULL;
		free(current->name);
		current->name = NULL;
		if (current->air_address)
		    free(current->air_address);
		current->air_address = NULL;
		if (current->beer_address)
		    free(current->beer_address);
		current->beer_address = NULL;
		if (current->chiller_address)
		    free(current->chiller_address);
		current->chiller_address = NULL;
		if (current->heater_address)
		    free(current->heater_address);
		current->heater_address = NULL;
		if (current->cooler_address)
		    free(current->cooler_address);
		current->cooler_address = NULL;
		if (current->fan_address)
		    free(current->fan_address);
		current->fan_address = NULL;
		if (current->light_address)
		    free(current->light_address);
		current->light_address = NULL;
		if (current->door_address)
		    free(current->door_address);
		current->door_address = NULL;
		if (current->psu_address)
		    free(current->psu_address);
		current->psu_address = NULL;
		if (current->profile)
		    free(current->profile);
		current->profile = NULL;
		if (current->PID_cool)
		    free(current->PID_cool);
		current->PID_cool = NULL;
		if (current->PID_heat)
		    free(current->PID_heat);
		current->PID_heat = NULL;
		free(current);
		return 1;
	    } else {
		free(current->uuid);
		current->uuid = NULL;
		free(current->name);
		current->name = NULL;
		if (current->air_address)
		    free(current->air_address);
		current->air_address = NULL;
		if (current->beer_address)
		    free(current->beer_address);
		current->beer_address = NULL;
		if (current->chiller_address)
		    free(current->chiller_address);
		current->chiller_address = NULL;
		if (current->heater_address)
		    free(current->heater_address);
		current->heater_address = NULL;
		if (current->cooler_address)
		    free(current->cooler_address);
		current->cooler_address = NULL;
		if (current->fan_address)
		    free(current->fan_address);
		current->fan_address = NULL;
		if (current->door_address)
		    free(current->door_address);
		current->door_address = NULL;
		if (current->light_address)
		    free(current->light_address);
		current->light_address = NULL;
		if (current->psu_address)
		    free(current->psu_address);
		current->psu_address = NULL;
		if (current->profile)
		    free(current->profile);
		current->profile = NULL;
		if (current->PID_cool)
		    free(current->PID_cool);
		current->PID_cool = NULL;
		if (current->PID_heat)
		    free(current->PID_heat);
		current->PID_heat = NULL;
		previous->next = current->next;
		free(current);
		current = previous->next;
		return 1;
	    }
	} else {
	    previous = current;
	    current = current->next;
	}
    }
    return 0;
}



/*
 * UNIT ADD name
 * UNIT DEL uuid
 * UNIT LIST
 * UNIT GET uuid
 * UNIT PUT uuid
 */
int cmd_unit(char *buf)
{
    char                *opt, *param = NULL, *kwd, *val, ibuf[SS_BUFSIZE];
    units_list          *unit, *tmpu;
    uuid_t		uu;
    int			ival, i, rc, rlen;
    float		fval;

    opt = strtok(buf, " \0");
    opt = strtok(NULL, " \0");

    if (opt == NULL) {
	srv_send((char *)"501 Subcommand missing");
	return 0;
    }
    param = strtok(NULL, "\0");

    if (strcmp(opt, (char *)"HELP") == 0) {
	srv_send((char *)"100 Help text follows:");
	srv_send((char *)"Recognized commands:");
	srv_send((char *)"UNIT ADD name                 Add a new Unit with name");
	srv_send((char *)"UNIT DEL uuid                 Delete Unit by uuid");
	srv_send((char *)"UNIT LIST                     List all Units");
	srv_send((char *)"UNIT GET uuid                 Get Unit record by uuid");
	srv_send((char *)"UNIT PUT uuid                 Put Unit record by uuid");
	srv_send((char *)".");
	return 0;
    }

    if ((strcmp(opt, (char *)"LIST") == 0) && (param == NULL)) {
	srv_send((char *)"212 Fermenter list follows:");
	for (unit = Config.units; unit; unit = unit->next) {
	    srv_send((char *)"%s,%s,%s", unit->uuid, unit->name, UNITMODE[unit->mode]);
	}
	srv_send((char *)".");
	return 0;
    }

    if (param == NULL) {
	srv_send((char *)"502 Parameter missing");
	return 0;
    }

    if (strcmp(opt, (char *)"ADD") == 0) {
	char an[128];

	sprintf(an, "unit%d", Config.next_unit);
	Config.next_unit++;
	unit = (units_list *)malloc(sizeof(units_list));
	unit->next = NULL;
	unit->version = 1;
	unit->uuid = malloc(37);
	uuid_generate(uu);
	uuid_unparse(uu, unit->uuid);
	unit->name = xstrcpy(param);
	unit->alias = xstrcpy(an);
	unit->air_address = unit->beer_address = unit->chiller_address = unit->heater_address = unit->cooler_address = \
			    unit->fan_address = unit->door_address = unit->light_address = \
			    unit->psu_address = unit->profile = NULL;
	unit->air_idx = unit->beer_idx = unit->chiller_idx = unit->heater_idx = unit->cooler_idx = unit->fan_idx = \
			unit->door_idx = unit->light_idx = unit->psu_idx = 0;
	unit->volume = unit->prof_peak_abs = unit->prof_peak_rel = 0.0;
	unit->air_state = unit->beer_state = unit->chiller_state = 1;
	unit->air_temperature = unit->beer_temperature = unit->chiller_temperature = 20000;
	unit->beer_set = unit->fridge_set = 20.0;
	unit->heater_state = unit->cooler_state = unit->fan_state = unit->door_state = unit->mode = \
			     unit->light_state = unit->psu_state = unit->prof_state = 0;
	unit->heater_delay = unit->cooler_delay = unit->fan_delay = 20;	/* 5 minutes delay	*/
	unit->light_delay = 1;						/* 15 seconds delay	*/
	unit->heater_wait = unit->cooler_wait = unit->fan_wait = unit->light_wait = 0;
	unit->heater_usage = unit->cooler_usage = unit->fan_usage = unit->light_usage = 0;
	unit->temp_set_min = 1.0;
	unit->temp_set_max = 30.0;
	unit->prof_started = unit->prof_paused = unit->prof_primary_done = (time_t)0;
	unit->prof_percent = 0;
	unit->PID_cool = (pid_var *)malloc(sizeof(pid_var));
	unit->PID_heat = (pid_var *)malloc(sizeof(pid_var));
	InitPID(unit->PID_cool, PID_TYPE_COOL);
	InitPID(unit->PID_heat, PID_TYPE_HEAT);

	/*
	 * Block main process
	 */
	run_pause = TRUE;
	for (;;) {
	    usleep(100000);
	    if (run_hold)
		break;
	}

	if (Config.units == NULL) {
	    Config.units = unit;
	} else {
	    for (tmpu = Config.units; tmpu; tmpu = tmpu->next) {
		if (tmpu->next == NULL) {
		    tmpu->next = unit;
		    break;
		}
	    }
	}
	lcd_buf_reset();
	run_pause = FALSE;

	syslog(LOG_NOTICE, "Unit %s added", unit->uuid);
	srv_send((char *)"211 Unit %s added", unit->uuid);
	return 1;
    }

    if (strcmp(opt, (char *)"DEL") == 0) {
	/*
	 * Block main process.
	 */
	run_pause = TRUE;
	for (;;) {
	    usleep(100000);
	    if (run_hold)
		break;
	}

	rc = delete_Unit(param);
	lcd_buf_reset();
	run_pause = FALSE;

	if (rc) {
	    syslog(LOG_NOTICE, "Unit %s deleted", param);
	    srv_send((char *)"211 Unit %s deleted", param);
	    return 1;
	} else {
	    srv_send((char *)"440 No such unit");
	    return 0;
	}
    }

    if (strcmp(opt, (char *)"GET") == 0) {
	for (unit = Config.units; unit; unit = unit->next) {
	    if (strcmp(param, unit->uuid) == 0) {
		srv_send((char *)"213 Unit listing follows:");
		srv_send((char *)"NAME,%s", unit->name);
		srv_send((char *)"UUID,%s", unit->uuid);
		srv_send((char *)"ALIAS,%s", unit->alias);
		srv_send((char *)"MODE,%s", UNITMODE[unit->mode]);
		srv_send((char *)"VOLUME,%2f", unit->volume);
		srv_send((char *)"AIR_ADDRESS,%s", unit->air_address);
		srv_send((char *)"AIR_STATE,%s", TEMPSTATE[unit->air_state]);
		srv_send((char *)"AIR_TEMPERATURE,%.3f", unit->air_temperature / 1000.0);
		srv_send((char *)"AIR_IDX,%d", unit->air_idx);
		srv_send((char *)"BEER_ADDRESS,%s", MBSE_SS(unit->beer_address));
		srv_send((char *)"BEER_STATE,%s", TEMPSTATE[unit->beer_state]);
		srv_send((char *)"BEER_TEMPERATURE,%.3f", unit->beer_temperature / 1000.0);
		srv_send((char *)"BEER_IDX,%d", unit->beer_idx);
		srv_send((char *)"CHILLER_ADDRESS,%s", MBSE_SS(unit->chiller_address));
		srv_send((char *)"CHILLER_STATE,%s", TEMPSTATE[unit->chiller_state]);
		srv_send((char *)"CHILLER_TEMPERATURE,%.3f", unit->chiller_temperature / 1000.0);
		srv_send((char *)"CHILLER_IDX,%d", unit->chiller_idx);
		srv_send((char *)"HEATER_ADDRESS,%s", unit->heater_address);
		srv_send((char *)"HEATER_STATE,%d", unit->heater_state);
		srv_send((char *)"HEATER_DELAY,%d", unit->heater_delay);
		srv_send((char *)"HEATER_USAGE,%d", unit->heater_usage);
		srv_send((char *)"HEATER_IDX,%d", unit->heater_idx);
		if (unit->PID_heat) {
		    srv_send((char *)"PIDH_IMAX,%.1f", unit->PID_heat->iMax);
		    srv_send((char *)"PIDH_IDLERANGE,%.2f", unit->PID_heat->idleRange);
		    srv_send((char *)"PIDH_PGAIN,%.3f", unit->PID_heat->pGain);
		    srv_send((char *)"PIDH_IGAIN,%.3f", unit->PID_heat->iGain);
		    srv_send((char *)"PIDH_DGAIN,%.3f", unit->PID_heat->dGain);
		    srv_send((char *)"PIDH_SV,%.2f", unit->PID_heat->SetP);
		}
		srv_send((char *)"COOLER_ADDRESS,%s", unit->cooler_address);
		srv_send((char *)"COOLER_STATE,%d", unit->cooler_state);
		srv_send((char *)"COOLER_DELAY,%d", unit->cooler_delay);
		srv_send((char *)"COOLER_USAGE,%d", unit->cooler_usage);
		srv_send((char *)"COOLER_IDX,%d", unit->cooler_idx);
		if (unit->PID_cool) {
		    srv_send((char *)"PIDC_IMAX,%.1f", unit->PID_cool->iMax);
		    srv_send((char *)"PIDC_IDLERANGE,%.2f", unit->PID_cool->idleRange);
		    srv_send((char *)"PIDC_PGAIN,%.3f", unit->PID_cool->pGain);
		    srv_send((char *)"PIDC_IGAIN,%.3f", unit->PID_cool->iGain);
		    srv_send((char *)"PIDC_DGAIN,%.3f", unit->PID_cool->dGain);
		    srv_send((char *)"PIDC_SV,%.2f", unit->PID_cool->SetP);
		}
		srv_send((char *)"FAN_ADDRESS,%s", unit->fan_address);
		srv_send((char *)"FAN_STATE,%d", unit->fan_state);
		srv_send((char *)"FAN_DELAY,%d", unit->fan_delay);
		srv_send((char *)"FAN_USAGE,%d", unit->fan_usage);
		srv_send((char *)"FAN_IDX,%d", unit->fan_idx);
		srv_send((char *)"LIGHT_ADDRESS,%s", unit->light_address);
		srv_send((char *)"LIGHT_STATE,%d", unit->light_state);
		srv_send((char *)"LIGHT_DELAY,%d", unit->light_delay);
		srv_send((char *)"LIGHT_USAGE,%d", unit->light_usage);
		srv_send((char *)"LIGHT_IDX,%d", unit->light_idx);
		srv_send((char *)"DOOR_ADDRESS,%s", unit->door_address);
		srv_send((char *)"DOOR_STATE,%d", unit->door_state);
		srv_send((char *)"DOOR_IDX,%d", unit->door_idx);
		srv_send((char *)"PSU_ADDRESS,%s", unit->psu_address);
		srv_send((char *)"PSU_STATE,%d", unit->psu_state);
		srv_send((char *)"PSU_IDX,%d", unit->psu_idx);
		srv_send((char *)"FRIDGE_SET,%.1f", unit->fridge_set);
		srv_send((char *)"BEER_SET,%.1f", unit->beer_set);
		srv_send((char *)"PROFILE,%s", unit->profile);
		srv_send((char *)"PROF_STARTED,%d", (int)unit->prof_started);
		if (unit->prof_state == PROFILE_RUN) {
		    srv_send((char *)"PROF_STATE,%s %d%%", PROFSTATE[unit->prof_state], unit->prof_percent);
		} else {
		    srv_send((char *)"PROF_STATE,%s", PROFSTATE[unit->prof_state]);
		}
		srv_send((char *)"PROF_TARGET_LO,%.3f", unit->prof_target_lo);
		srv_send((char *)"PROF_TARGET_HI,%.3f", unit->prof_target_hi);
		srv_send((char *)"PROF_FRIDGE_MODE,%d", unit->prof_fridge_mode);
		srv_send((char *)"PROF_PEAK_ABS,%.3f", unit->prof_peak_abs);
		srv_send((char *)"PROF_PEAK_REL,%.3f", unit->prof_peak_rel);
		srv_send((char *)"PROF_PRIMARY_DONE,%d", (int)unit->prof_primary_done);
		srv_send((char *)"TEMP_SET_MIN,%.1f", unit->temp_set_min);
		srv_send((char *)"TEMP_SET_MAX,%.1f", unit->temp_set_max);
		srv_send((char *)"ALARM,%d", unit->alarm_flag);
		srv_send((char *)".");
		return 0;
	    }
	}
	srv_send((char *)"440 No such unit");
	return 0;
    }

    if (strcmp(opt, (char *)"PUT") == 0) {
	/*
	 * Block main process
	 */
	run_pause = TRUE;
	for (;;) {
	    usleep(100000);
	    if (run_hold)
		break;
	}

	for (unit = Config.units ; unit; unit = unit->next) {
	     if (strcmp(unit->uuid, param) == 0) {
		while (1) {
		    unit->mqtt_flag = 0;
		    rlen = srv_recv(ibuf);
		    if (rlen == -1) {
			run_pause = FALSE;
			return 0;
		    }
		    if (strlen(ibuf)) {
			if (strcmp(ibuf, (char *)".") == 0) {
			    srv_send((char *)"219 Accepted Unit record");
			    run_pause = FALSE;
			    return 1;
			}
			kwd = strtok(ibuf, ",\0");
			val = strtok(NULL, "\0");
			if (kwd) {
			    unit->mqtt_flag = 0;
			    /*
			     * Accept writable data. The client can sent just one line,
			     * but may also sent everything. Simply ignore things we
			     * don't understand.
			     */
			    if (val && (strcmp(kwd, (char *)"NAME") == 0)) {
				if (unit->name) {
				    if (strcmp(unit->name, val)) {
					syslog(LOG_NOTICE, "Fermenter unit %s name `%s' to `%s'", unit->uuid, unit->name, val);
				    }
				    free(unit->name);
				}
				unit->name = xstrcpy(val);
				unit->mqtt_flag |= MQTT_FLAG_DATA;

			    } else if (val && (strcmp(kwd, (char *)"VOLUME") == 0)) {
				if (sscanf(val, "%f", &fval) == 1) {
				    if (unit->volume != fval)
					syslog(LOG_NOTICE, "Fermenter unit %s volume %.3f to %.3f", unit->uuid, unit->volume, fval);
				    unit->volume = fval;
				}

			    } else if (strcmp(kwd, (char *)"AIR_ADDRESS") == 0) {
				if (val && unit->air_address && (strcmp(val, unit->air_address)))
				    syslog(LOG_NOTICE, "Fermenter unit %s air address `%s' to `%s'", unit->uuid, unit->air_address, val);
				if (unit->air_address) {
				    device_count(FALSE, unit->air_address);
				    free(unit->air_address);
				}
				if (val) {
				    unit->air_address  = xstrcpy(val);
				    device_count(TRUE, unit->air_address);
				} else
				    unit->air_address  = NULL;
				unit->mqtt_flag |= MQTT_FLAG_DATA;

			    } else if (val && (strcmp(kwd, (char *)"AIR_IDX") == 0)) {
				if (sscanf(val, "%d", &ival) == 1) {
				    if (unit->air_idx != ival)
					syslog(LOG_NOTICE, "Fermenter unit %s air idx %d to %d", unit->uuid, unit->air_idx, ival);
				    unit->air_idx = ival;
				}

			    } else if (strcmp(kwd, (char *)"BEER_ADDRESS") == 0) {
				if (val && unit->beer_address && (strcmp(val, unit->beer_address)))
				    syslog(LOG_NOTICE, "Fermenter unit %s beer address `%s' to `%s'", unit->uuid, unit->beer_address, val);
				if (unit->beer_address) {
				    device_count(FALSE, unit->beer_address);
				    free(unit->beer_address);
				}
				if (val) {
				    unit->beer_address = xstrcpy(val);
				    device_count(TRUE, unit->beer_address);
				} else
				    unit->beer_address = NULL;
				unit->mqtt_flag |= MQTT_FLAG_DATA;

			    } else if (val && (strcmp(kwd, (char *)"BEER_IDX") == 0)) {
				if (sscanf(val, "%d", &ival) == 1) {
				    if (unit->beer_idx != ival)
					syslog(LOG_NOTICE, "Fermenter unit %s beer idx %d to %d", unit->uuid, unit->beer_idx, ival);
				    unit->beer_idx = ival;
				}

			    } else if (strcmp(kwd, (char *)"CHILLER_ADDRESS") == 0) {
				if (val && unit->chiller_address && (strcmp(val, unit->chiller_address)))
				    syslog(LOG_NOTICE, "Fermenter unit %s chiller address `%s' to `%s'", unit->uuid, unit->chiller_address, val);
				if (unit->chiller_address) {
				    device_count(FALSE, unit->chiller_address);
				    free(unit->chiller_address);
				}
				if (val) {
				    unit->chiller_address = xstrcpy(val);
				    device_count(TRUE, unit->chiller_address);
				} else
				    unit->chiller_address = NULL;
				unit->mqtt_flag |= MQTT_FLAG_DATA;

			    } else if (val && (strcmp(kwd, (char *)"CHILLER_IDX") == 0)) {
				if (sscanf(val, "%d", &ival) == 1) {
				    if (unit->chiller_idx != ival)
					syslog(LOG_NOTICE, "Fermenter unit %s chiller idx %d to %d", unit->uuid, unit->chiller_idx, ival);
				    unit->chiller_idx = ival;
				}

			    } else if (strcmp(kwd, (char *)"HEATER_ADDRESS") == 0) {
				if (val && unit->heater_address && (strcmp(val, unit->heater_address)))
				    syslog(LOG_NOTICE, "Fermenter unit %s heater address `%s' to `%s'", unit->uuid, unit->heater_address, val);
				if (unit->heater_address) {
				    device_count(FALSE, unit->heater_address);
				    free(unit->heater_address);
				}
				if (val) {
				    unit->heater_address = xstrcpy(val);
				    device_count(TRUE, unit->heater_address);
				} else
				    unit->heater_address = NULL;
				unit->mqtt_flag |= MQTT_FLAG_DATA;

			    } else if (val && (strcmp(kwd, (char *)"HEATER_STATE") == 0)) {
				if ((sscanf(val, "%d", &ival) == 1) && ((ival == 0) || (ival == 100))) {
				    if (unit->heater_state != ival)
					syslog(LOG_NOTICE, "Fermenter unit %s heater state %d to %d", unit->uuid, unit->heater_state, ival);
				    unit->heater_state = ival;
				    unit->mqtt_flag |= MQTT_FLAG_DATA;
				}

			    } else if (val && (strcmp(kwd, (char *)"HEATER_IDX") == 0)) {
				if (sscanf(val, "%d", &ival) == 1) {
				    if (unit->heater_idx != ival)
					syslog(LOG_NOTICE, "Fermenter unit %s heater idx %d to %d", unit->uuid, unit->heater_idx, ival);
				    unit->heater_idx = ival;
				}

			    } else if (val && (strcmp(kwd, (char *)"HEATER_DELAY") == 0)) {
				if (sscanf(val, "%d", &ival) == 1) {
				    if (unit->heater_delay != ival)
					syslog(LOG_NOTICE, "Fermenter unit %s heater delay %d to %d", unit->uuid, unit->heater_delay, ival);
				    unit->heater_delay = ival;
				}

			    } else if (strcmp(kwd, (char *)"COOLER_ADDRESS") == 0) {
				if (val && unit->cooler_address && (strcmp(val, unit->cooler_address)))
				    syslog(LOG_NOTICE, "Fermenter unit %s cooler address `%s' to `%s'", unit->uuid, unit->cooler_address, val);
				if (unit->cooler_address) {
				    device_count(FALSE, unit->cooler_address);
				    free(unit->cooler_address);
				}
				if (val) {
				    unit->cooler_address = xstrcpy(val);
				    device_count(TRUE, unit->cooler_address);
				} else
				    unit->cooler_address = NULL;
				unit->mqtt_flag |= MQTT_FLAG_DATA;

			    } else if (val && (strcmp(kwd, (char *)"COOLER_STATE") == 0)) {
				if ((sscanf(val, "%d", &ival) == 1) && ((ival == 0) || (ival == 100))) {
				    if (unit->cooler_state != ival)
					syslog(LOG_NOTICE, "Fermenter unit %s cooler state %d to %d", unit->uuid, unit->cooler_state, ival);
				    unit->cooler_state = ival;
				    unit->mqtt_flag |= MQTT_FLAG_DATA;
				}

			    } else if (val && (strcmp(kwd, (char *)"COOLER_DELAY") == 0)) {
				if (sscanf(val, "%d", &ival) == 1) {
				    if (unit->cooler_delay != ival)
					syslog(LOG_NOTICE, "Fermenter unit %s cooler delay %d to %d", unit->uuid, unit->cooler_delay, ival);
				    unit->cooler_delay = ival;
				}

			    } else if (val && (strcmp(kwd, (char *)"COOLER_IDX") == 0)) {
				if (sscanf(val, "%d", &ival) == 1) {
				    if (unit->cooler_idx != ival)
					syslog(LOG_NOTICE, "Fermenter unit %s cooler idx %d to %d", unit->uuid, unit->cooler_idx, ival);
				    unit->cooler_idx = ival;
				}

			    } else if (strcmp(kwd, (char *)"FAN_ADDRESS") == 0) {
				if (val && unit->fan_address && (strcmp(val, unit->fan_address)))
				    syslog(LOG_NOTICE, "Fermenter unit %s fan address `%s' to `%s'", unit->uuid, unit->fan_address, val);
				if (unit->fan_address) {
				    device_count(FALSE, unit->fan_address);
				    free(unit->fan_address);
				}
				if (val) {
				    unit->fan_address = xstrcpy(val);
				    device_count(TRUE, unit->fan_address);
				} else
				    unit->fan_address = NULL;
				unit->mqtt_flag |= MQTT_FLAG_DATA;
			   
			    } else if (val && (strcmp(kwd, (char *)"FAN_STATE") == 0)) {
				if ((sscanf(val, "%d", &ival) == 1) && ((ival == 0) || (ival == 100))) {
				    if (unit->fan_state != ival)
					syslog(LOG_NOTICE, "Fermenter unit %s fan state %d to %d", unit->uuid, unit->fan_state, ival);    
				    unit->fan_state = ival;
				    unit->mqtt_flag |= MQTT_FLAG_DATA;
				}
			    
			    } else if (val && (strcmp(kwd, (char *)"FAN_DELAY") == 0)) {
				if (sscanf(val, "%d", &ival) == 1) {
				    if (unit->fan_delay != ival)
					syslog(LOG_NOTICE, "Fermenter unit %s fan delay %d to %d", unit->uuid, unit->fan_delay, ival);
				    unit->fan_delay = ival;
				}

			    } else if (val && (strcmp(kwd, (char *)"FAN_IDX") == 0)) {
				if (sscanf(val, "%d", &ival) == 1) {
				    if (unit->fan_idx != ival)
					syslog(LOG_NOTICE, "Fermenter unit %s fan idx %d to %d", unit->uuid, unit->fan_idx, ival);
				    unit->fan_idx = ival;
				}

			    } else if (strcmp(kwd, (char *)"LIGHT_ADDRESS") == 0) {
				if (val && unit->light_address && (strcmp(val, unit->light_address)))
				    syslog(LOG_NOTICE, "Fermenter unit %s light address `%s' to `%s'", unit->uuid, unit->light_address, val);
				if (unit->light_address) {
				    device_count(FALSE, unit->light_address);
				    free(unit->light_address);
				}
				if (val) {
				    unit->light_address = xstrcpy(val);
				    device_count(TRUE, unit->light_address);
				} else
				    unit->light_address = NULL;
				unit->mqtt_flag |= MQTT_FLAG_DATA;

			    } else if (val && (strcmp(kwd, (char *)"LIGHT_STATE") == 0)) {
				if ((sscanf(val, "%d", &ival) == 1) && ((ival == 0) || (ival == 100))) {
				    if (unit->light_state != ival)
					syslog(LOG_NOTICE, "Fermenter unit %s light state %d to %d", unit->uuid, unit->light_state, ival);
				    unit->light_state = ival;
				    unit->mqtt_flag |= MQTT_FLAG_DATA;
				}

			    } else if (val && (strcmp(kwd, (char *)"LIGHT_DELAY") == 0)) {
				if (sscanf(val, "%d", &ival) == 1) {
				    if (unit->light_delay != ival)
					syslog(LOG_NOTICE, "Fermenter unit %s light delay %d to %d", unit->uuid, unit->light_delay, ival);
				    unit->light_delay = ival;
				}

			    } else if (val && (strcmp(kwd, (char *)"LIGHT_IDX") == 0)) {
				if (sscanf(val, "%d", &ival) == 1) {
				    if (unit->light_idx != ival)
					syslog(LOG_NOTICE, "Fermenter unit %s light idx %d to %d", unit->uuid, unit->light_idx, ival);
				    unit->light_idx = ival;
				}

			    } else if (strcmp(kwd, (char *)"DOOR_ADDRESS") == 0) {
				if (val && unit->door_address && (strcmp(val, unit->door_address)))
				    syslog(LOG_NOTICE, "Fermenter unit %s door address `%s' to `%s'", unit->uuid, unit->door_address, val);
				if (unit->door_address) {
				    device_count(FALSE, unit->door_address);
				    free(unit->door_address);
				}
				if (val) {
				    unit->door_address = xstrcpy(val);
				    device_count(TRUE, unit->door_address);
				} else
				    unit->door_address = NULL;

			    } else if (val && (strcmp(kwd, (char *)"DOOR_IDX") == 0)) {
				if (sscanf(val, "%d", &ival) == 1) {
				    if (unit->door_idx != ival)
					syslog(LOG_NOTICE, "Fermenter unit %s door idx %d to %d", unit->uuid, unit->door_idx, ival);
				    unit->door_idx = ival;
				}

			    } else if (strcmp(kwd, (char *)"PSU_ADDRESS") == 0) {
				if (val && unit->psu_address && (strcmp(val, unit->psu_address)))
				    syslog(LOG_NOTICE, "Fermenter unit %s psu address `%s' to `%s'", unit->uuid, unit->psu_address, val);
				if (unit->psu_address) {
				    device_count(FALSE, unit->psu_address);
				    free(unit->psu_address);
				}
				if (val) {
				    unit->psu_address = xstrcpy(val);
				    device_count(TRUE, unit->psu_address);
				} else
				    unit->psu_address = NULL;
				unit->mqtt_flag |= MQTT_FLAG_DATA;

			    } else if (val && (strcmp(kwd, (char *)"PSU_IDX") == 0)) {
				if (sscanf(val, "%d", &ival) == 1) {
				    if (unit->psu_idx != ival)
					syslog(LOG_NOTICE, "Fermenter unit %s psu idx %d to %d", unit->uuid, unit->psu_idx, ival);
				    unit->psu_idx = ival;
				}

			    } else if (val && (strcmp(kwd, (char *)"MODE") == 0)) {
				for (i = 0; i < 5; i++) {
				    if (strcmp(val, UNITMODE[i]) == 0) {
					unit->mqtt_flag |= MQTT_FLAG_DATA;
					/* Initialize log if the unit is turned on */
					if ((unit->mode == UNITMODE_OFF) && (i != UNITMODE_OFF)) {
					    initlog(unit->name);
					    unit->mqtt_flag |= MQTT_FLAG_BIRTH;
					} else if ((unit->mode != UNITMODE_OFF) && (i == UNITMODE_OFF)) {
					    unit->mqtt_flag |= MQTT_FLAG_DEATH;
					}
					syslog(LOG_NOTICE, "Fermenter unit %s mode %s to %s", unit->uuid, UNITMODE[unit->mode], UNITMODE[i]);
					unit->mode = i;
					/* Allways turn everything off after a mode change */
					unit->PID_cool->OutP = unit->PID_heat->OutP = 0.0;
					unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_NONE;
					unit->heater_state = unit->cooler_state = unit->fan_state = unit->light_state = 0;
					unit->heater_wait = unit->cooler_wait = unit->fan_wait = unit->light_wait = 0;
					device_out(unit->heater_address, unit->heater_state);
					device_out(unit->cooler_address, unit->cooler_state);
					device_out(unit->fan_address, unit->fan_state);
					device_out(unit->light_address, unit->light_state);
					if (unit->mode == UNITMODE_PROFILE) {
					    /*
					     * Set a sane default until it will be overruled by the
					     * main processing loop.
					     */
					    unit->prof_target_lo = unit->prof_target_hi = 20.0;
					    unit->prof_fridge_mode = 0;
					    if (unit->profile) {
						unit->mqtt_flag |= MQTT_FLAG_DATA;
					    }
					}
					break;
				    }
				}

			    } else if (val && (strcmp(kwd, (char *)"FRIDGE_SET") == 0)) {
				if ((sscanf(val, "%f", &fval) == 1) && (fval >= unit->temp_set_min) && (fval <= unit->temp_set_max)) {
				    if (unit->fridge_set != fval)
				    	syslog(LOG_NOTICE, "Fermenter unit %s fridge temperature %.1f to %.1f", unit->uuid, unit->fridge_set, fval);
				    unit->fridge_set = fval;
				    unit->mqtt_flag |= MQTT_FLAG_DATA;
				}

			    } else if (val && (strcmp(kwd, (char *)"BEER_SET") == 0)) {
				if ((sscanf(val, "%f", &fval) == 1) && (fval >= unit->temp_set_min) && (fval <= unit->temp_set_max)) {
				    if (unit->beer_set != fval)
				    	syslog(LOG_NOTICE, "Fermenter unit %s beer temperature %.1f to %.1f", unit->uuid, unit->beer_set, fval);
				    unit->beer_set = fval;
				    unit->mqtt_flag |= MQTT_FLAG_DATA;
				}

			    } else if (val && (strcmp(kwd, (char *)"PIDC_IMAX") == 0)) {
				if ((sscanf(val, "%f", &fval) == 1) && (fval >= 0.0)) {
				    if (unit->PID_cool->iMax != fval)
					syslog(LOG_NOTICE, "Fermenter unit %s PID_cool iGain %.1f to %.1f", unit->uuid, unit->PID_cool->iMax, fval);
				    unit->PID_cool->iMax = fval;
				}

			    } else if (val && (strcmp(kwd, (char *)"PIDC_PGAIN") == 0)) {
				if ((sscanf(val, "%f", &fval) == 1) && (fval >= 0.0)) {
				    if (unit->PID_cool->pGain != fval)
					syslog(LOG_NOTICE, "Fermenter unit %s PID_cool pGain %.3f to %.3f", unit->uuid, unit->PID_cool->pGain, fval);
				    unit->PID_cool->pGain = fval;
				}

			    } else if (val && (strcmp(kwd, (char *)"PIDC_DGAIN") == 0)) {
				if ((sscanf(val, "%f", &fval) == 1) && (fval >= 0.0)) {
				    if (unit->PID_cool->dGain != fval)
					syslog(LOG_NOTICE, "Fermenter unit %s PID_cool dGain %.3f to %.3f", unit->uuid, unit->PID_cool->dGain, fval);
				    unit->PID_cool->dGain = fval;
				}
	
			    } else if (val && (strcmp(kwd, (char *)"PIDC_IGAIN") == 0)) {
				if ((sscanf(val, "%f", &fval) == 1) && (fval >= 0.0)) {
				    if (unit->PID_cool->iGain != fval)
					syslog(LOG_NOTICE, "Fermenter unit %s PID_cool iGain %.3f to %.3f", unit->uuid, unit->PID_cool->iGain, fval);
				    unit->PID_cool->iGain = fval;
				}

			    } else if (val && (strcmp(kwd, (char *)"PIDC_IDLERANGE") == 0)) {
				if ((sscanf(val, "%f", &fval) == 1) && (fval >= 0.0)) {
				    if (unit->PID_cool->idleRange != fval)
					syslog(LOG_NOTICE, "Fermenter unit %s PID_cool idleRange %.2f to %.2f", unit->uuid, unit->PID_cool->idleRange, fval);
				    unit->PID_cool->idleRange = fval;
				}

			    } else if (val && (strcmp(kwd, (char *)"PIDH_IMAX") == 0)) {
				if ((sscanf(val, "%f", &fval) == 1) && (fval >= 0.0)) {
				    if (unit->PID_heat->iMax != fval)
					syslog(LOG_NOTICE, "Fermenter unit %s PID_heat iGain %.1f to %.1f", unit->uuid, unit->PID_heat->iMax, fval);
				    unit->PID_heat->iMax = fval;
				}

			    } else if (val && (strcmp(kwd, (char *)"PIDH_PGAIN") == 0)) {
				if ((sscanf(val, "%f", &fval) == 1) && (fval >= 0.0)) {
				    if (unit->PID_heat->pGain != fval)
					syslog(LOG_NOTICE, "Fermenter unit %s PID_heat pGain %.3f to %.3f", unit->uuid, unit->PID_heat->pGain, fval);
				    unit->PID_heat->pGain = fval;
				}

			    } else if (val && (strcmp(kwd, (char *)"PIDH_DGAIN") == 0)) {
				if ((sscanf(val, "%f", &fval) == 1) && (fval >= 0.0)) {
				    if (unit->PID_heat->dGain != fval)
					syslog(LOG_NOTICE, "Fermenter unit %s PID_heat dGain %.3f to %.3f", unit->uuid, unit->PID_heat->dGain, fval);
				    unit->PID_heat->dGain = fval;
				}

			    } else if (val && (strcmp(kwd, (char *)"PIDH_IGAIN") == 0)) {
				if ((sscanf(val, "%f", &fval) == 1) && (fval >= 0.0)) {
				    if (unit->PID_heat->iGain != fval)
					syslog(LOG_NOTICE, "Fermenter unit %s PIH_heat iGain %.3f to %.3f", unit->uuid, unit->PID_heat->iGain, fval);
				    unit->PID_heat->iGain = fval;
				}

			    } else if (val && (strcmp(kwd, (char *)"PIDH_IDLERANGE") == 0)) {
				if ((sscanf(val, "%f", &fval) == 1) && (fval >= 0.0)) {
				    if (unit->PID_heat->idleRange != fval)
					syslog(LOG_NOTICE, "Fermenter unit %s PID_heat idleRange %.2f to %.2f", unit->uuid, unit->PID_heat->idleRange, fval);
				    unit->PID_heat->idleRange = fval;
				}

			    } else if (strcmp(kwd, (char *)"PROFILE") == 0) {
				if (unit->prof_state == PROFILE_OFF) {
				    /*
				     * Only change profile if it is not active, else drop this one.
				     */
				    if (unit->profile && val && strcmp(unit->profile, val))
					syslog(LOG_NOTICE, "Fermenter unit %s profile name `%s' to `%s'", unit->uuid, unit->profile, val);
				    if (unit->profile)
				    	free(unit->profile);

				    if (val)
				    	unit->profile = xstrcpy(val);
				    else
				    	unit->profile = NULL;
				    /*
				     * Reset all output devices
				     */
				    unit->PID_cool->OutP = unit->PID_heat->OutP = 0.0;
				    unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_NONE;
				    unit->heater_state = unit->cooler_state = unit->fan_state = unit->light_state = 0;
				    unit->heater_wait = unit->cooler_wait = unit->fan_wait = unit->light_wait = 0;
				    device_out(unit->heater_address, unit->heater_state);
				    device_out(unit->cooler_address, unit->cooler_state);
				    device_out(unit->fan_address, unit->fan_state);
				    device_out(unit->light_address, unit->light_state);
				    unit->mqtt_flag |= MQTT_FLAG_DATA;
				}

			    } else if (val && (strcmp(kwd, (char *)"PROF_STATE") == 0)) {
				for (i = 0; i < 5; i++) {
				    if (strcmp(val, PROFSTATE[i]) == 0) {
					switch (i) {
						case PROFILE_OFF:	if (unit->prof_state == PROFILE_DONE) {
									    unit->prof_state = PROFILE_OFF;
									    syslog(LOG_NOTICE, "Fermenter unit %s profile to OFF", unit->uuid);
									}
									break;
						case PROFILE_PAUSE:	if (unit->prof_state == PROFILE_RUN) {
									    unit->prof_state = PROFILE_PAUSE;
									    syslog(LOG_NOTICE, "Fermenter unit %s profile PAUSE", unit->uuid);
									} else if (unit->prof_state == PROFILE_PAUSE) {
									    unit->prof_state = PROFILE_RUN;
									    syslog(LOG_NOTICE, "Fermenter unit %s profile RESUME", unit->uuid);
									}
									break;
						case PROFILE_RUN:	if (unit->prof_state == PROFILE_OFF) {
									    unit->prof_state = PROFILE_RUN;
									    unit->prof_started = time(NULL);
									    unit->prof_paused = unit->prof_primary_done = 0;
									    unit->prof_peak_abs = unit->prof_peak_rel = 0.0;
									    syslog(LOG_NOTICE, "Fermenter unit %s profile to RUN", unit->uuid);
									}
									break;
						case PROFILE_DONE:	break;	/* Command is illegal */
						case PROFILE_ABORT:	if ((unit->prof_state == PROFILE_RUN) || (unit->prof_state == PROFILE_PAUSE)) {
									    unit->prof_state = PROFILE_OFF;
									    unit->prof_started = 0;
									    syslog(LOG_NOTICE, "Fermenter unit %s profile ABORT", unit->uuid);
									}
									break;
					}
					unit->mqtt_flag |= MQTT_FLAG_DATA;
					break;
				    }
				}

			    } else if (val && (strcmp(kwd, (char *)"TEMP_SET_MIN") == 0)) {
				if (sscanf(val, "%f", &fval) == 1) {
				    if (unit->temp_set_min != fval)
					syslog(LOG_NOTICE, "Fermenter unit %s temperature set minimum %.1f to %.1f", unit->uuid, unit->temp_set_min, fval);
				    unit->temp_set_min = fval;
				}

			    } else if (val && (strcmp(kwd, (char *)"TEMP_SET_MAX") == 0)) {
				if (sscanf(val, "%f", &fval) == 1) {
				    if (unit->temp_set_max != fval)
					syslog(LOG_NOTICE, "Fermenter unit %s temperature set maximum %.1f to %.1f", unit->uuid, unit->temp_set_max, fval);
				    unit->temp_set_max = fval;
				}

			    }
			    if (unit->mqtt_flag) {
				    if (debug)
					    fprintf(stdout, "flag value %d\n", unit->mqtt_flag);
				if (unit->mqtt_flag & MQTT_FLAG_BIRTH) {
				    publishDBirth(unit);
				} else {
				    publishDData(unit);
				}
				if (unit->mqtt_flag & MQTT_FLAG_DEATH) {
				    publishDDeath(unit);
				}
			    }
			}
		    }
		}
	    }
	}
	srv_send((char *)"440 No such unit");
	run_pause = FALSE;
	return 0;
    }

    srv_send((char *)"504 Subcommand error");
    return 0;
}



void cmd_server(void)
{
    char                buf[SS_BUFSIZE];
    int                 rlen;

    rlen = srv_recv(buf);
    if (rlen != -1) {
	if (strlen(buf)) {
	    /*
	     * Process commands from the client
	     */
	    if (strncmp(buf, "ARCHIVE", 7) == 0) {
		cmd_archive(buf);

	    } else if (strncmp(buf, "DEVICE", 6) == 0) {
		if (cmd_device(buf))
		    wrconfig();

	    } else if (strncmp(buf, "GLOBAL", 6) == 0) {
		if (cmd_global(buf))
		    wrconfig();

	    } else if (strncmp(buf, "HELP", 4) == 0) {
		srv_send((char *)"100 Help text follows");
		srv_send((char *)"Recognized commands:");
		srv_send((char *)"");
//                                12345678901234567890123456789012345678901234567890123456789012345678901234567890
		srv_send((char *)"ARCHIVE <CMD> [parameters]    Archive commands");
		srv_send((char *)"ARCHIVE HELP                  Archive help screen");
		srv_send((char *)"DEVICE <CMD> [parameters]     Device commands");
		srv_send((char *)"DEVICE HELP                   Device help screen");
		srv_send((char *)"GLOBAL <CMD> [parameters]     Global commands");
		srv_send((char *)"GLOBAL HELP                   Global help screen");
		srv_send((char *)"LIST <CMD> [parameters]       List commands");
		srv_send((char *)"LIST HELP                     List help screen");
		srv_send((char *)"PING                          Check if server is alive");
		srv_send((char *)"PROFILE <CMD> [parameters]    Profile commands");
		srv_send((char *)"PROFILE HELP                  Profile help screen");
#ifdef USE_SIMULATOR
		srv_send((char *)"SIMULATOR <CMD> [parameters]  Simulator commands");
		srv_send((char *)"SIMULATOR HELP                Simulator help screen");
#endif
		srv_send((char *)"UNIT <CMD> [parameters]       Unit commands");
		srv_send((char *)"UNIT HELP                     Unit help screen");
		srv_send((char *)".");

	    } else if (strncmp(buf, "LIST", 4) == 0) {
		cmd_list(buf);

	    } else if (strncmp(buf, "PING", 4) == 0) {
		srv_send((char *)"101 PONG");

	    } else if (strncmp(buf, "PROFILE", 7) == 0) {
		if (cmd_profile(buf))
		    wrconfig();

#ifdef USE_SIMULATOR
	    } else if (strncmp(buf, "SIMULATOR", 9) == 0) {
		if (cmd_simulator(buf))
		    wrconfig();

#endif
	    } else if (strncmp(buf, "UNIT", 4) == 0) {
		if (cmd_unit(buf))
		    wrconfig();

	    } else {
		srv_send((char *)"500 Unknown command");
	    }
	}
    }

    close(s);
}



#ifdef HAVE_WIRINGPI_H
PI_THREAD (my_server_loop)
#else
void *my_server_loop(void *threadid)
#endif
{
    socklen_t   addrlen;
    int         optval = 1;

    syslog(LOG_NOTICE, "Thread my_server_loop started");
    if (debug)
	fprintf(stdout, "Thread my_server_loop started\n");

    memset((char *)&myaddr_in, 0, sizeof(struct sockaddr_in));
    memset((char *)&peeraddr_in, 0, sizeof(struct sockaddr_in));
    myaddr_in.sin_family = AF_INET;
    myaddr_in.sin_addr.s_addr = INADDR_ANY;
    myaddr_in.sin_port = htons(Config.my_port);

    ls = socket(AF_INET, SOCK_STREAM, 0);
    if (ls == -1) {
	syslog(LOG_NOTICE, "Can't create listen socket: %s", strerror(errno));
	fprintf(stderr, "Can't create listen socket: %s\n", strerror(errno));
	return 0;
    }

    if (setsockopt(ls, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)) == -1) {
	syslog(LOG_NOTICE, "Can't setsockopt SO_KEEPALIVE socket: %s", strerror(errno));
	close(ls);
	return 0;
    }

    if (setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1) {
	syslog(LOG_NOTICE, "Can't setsockopt SO_REUSEADDR socket: %s", strerror(errno));
	close(ls);
	return 0;
    }

    if (bind(ls, (struct sockaddr *)&myaddr_in, sizeof(struct sockaddr_in)) == -1) {
	syslog(LOG_NOTICE, "Can't bind to listen socket: %s", strerror(errno));
	close(ls);
	return 0;
    }

    if (listen(ls, 5) == -1) {
	syslog(LOG_NOTICE, "Can't listen on listen socket: %s", strerror(errno));
	close(ls);
	return 0;
    }

    syslog(LOG_NOTICE, "listen socket created %d", ls);


    /*
     * Loop forever until the external shutdown variable is set.
     */
    for (;;) {
	
	addrlen = sizeof(struct sockaddr_in);
        /*
	 * This call will block until a new connection
	 * arrives. Then it will return the address of
	 * the connecting peer, and a new socket
	 * descriptor, s, for that connection.
	 */
	s = accept(ls, (struct sockaddr *)&peeraddr_in, &addrlen);
	if (s == -1) {
	    syslog(LOG_NOTICE, "my_server_loop accept failed %s", strerror(errno));
	    if (debug)
		fprintf(stdout, "my_server_loop accept failed %s\n", strerror(errno));
	    return 0;
	}

	cmd_server();

	if (my_shutdown) {
	    syslog(LOG_NOTICE, "Thread my_server_loop stopped");
	    if (debug)
		fprintf(stdout, "Thread my_server_loop stopped\n");
	    return 0;
	}

    }
}

mercurial