thermferm/server.c

Sun, 07 Jul 2019 13:04:16 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Sun, 07 Jul 2019 13:04:16 +0200
changeset 599
b44eb07cab48
parent 594
1904badedf8f
child 604
e2766e538d0e
permissions
-rw-r--r--

Fix one byte too large buffer copy

/*****************************************************************************
 * Copyright (C) 2008-2019
 *   
 * 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 "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	UNITSTAGE[4][12];
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
	}
    }
}



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, 255);
    (*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);
}



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->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) {
	int mqtt_reconnect = 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 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 = 1;
			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 = 1;
			        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 = 1;
			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 = 1;
			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
 */
int cmd_list(char *buf)
{
    char		*opt;
    units_list		*unit;

    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->alias, 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 *)".");
	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->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->room_humidity = 48.6;
	simulator->chiller_temperature = 1.5;	/* Chiller temperature */
	simulator->cooler_temp =  1.5;	/* 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 *)"ROOM_HUMIDITY,%.1f", simulator->room_humidity);
		srv_send((char *)"AIR_TEMPERATURE,%.3f", simulator->air_temperature);
		srv_send((char *)"BEER_TEMPERATURE,%.3f", simulator->beer_temperature);
		srv_send((char *)"CHILLER_TEMPERATURE,%.3f", simulator->chiller_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 *)"ROOM_HUMIDITY") == 0) {
				if (sscanf(val, "%f", &fval) == 1) {
				    if (simulator->room_humidity != fval)
					syslog(LOG_NOTICE, "Simulator %s room hunidity %.1f to %.1f", simulator->uuid, simulator->room_humidity, fval);
				    simulator->room_humidity = 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 *)"CHILLER_TEMPERATURE") == 0) {
				if (sscanf(val, "%f", &fval) == 1) {
				    if (simulator->chiller_temperature != fval)
					syslog(LOG_NOTICE, "Simulator %s chiller temperature %.1f to %.1f", simulator->uuid, simulator->chiller_temperature, fval);
				    simulator->chiller_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;
    prof_step	*step, *olds;

    while (current) {
	if (strcmp(current->uuid, uuid) == 0) {
	    if (previous == NULL) {
		Config.units = current->next;
		free(current->uuid);
		current->uuid = NULL;
		if (current->product_uuid)
		    free(current->product_uuid);
		current->product_uuid = NULL;
		if (current->product_code)
		    free(current->product_code);
		current->product_code = NULL;
		if (current->product_name)
		    free(current->product_name);
		current->product_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_uuid)
		    free(current->profile_uuid);
		current->profile_uuid = NULL;
		if (current->profile_name)
		    free(current->profile_name);
		current->profile_name = 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;
		if (current->profile_steps) {
		    for (step = current->profile_steps; step; step = olds) {
			olds = step->next;
			free(step);
		    }
		    current->profile_steps = NULL;
		}
		free(current);
		return 1;
	    } else {
		free(current->uuid);
		current->uuid = NULL;
		if (current->product_uuid)
		    free(current->product_uuid);
		current->product_uuid = NULL;
		if (current->product_code)
		    free(current->product_code);
		current->product_code = NULL;
		if (current->product_name)
		    free(current->product_name);
		current->product_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_uuid)
		    free(current->profile_uuid);
		current->profile_uuid = NULL;
		if (current->profile_name)
		    free(current->profile_name);
		current->profile_name = 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;
		if (current->profile_steps) {
		    for (step = current->profile_steps; step; step = olds) {
			olds = step->next;
			free(step);
		    }
		    current->profile_steps = 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) {
	    if (strlen(unit->product_code) && strlen(unit->product_name)) {
		srv_send((char *)"%s,%s %s,%s", unit->uuid, unit->product_code, unit->product_name, UNITMODE[unit->mode]);
	    } else {
	    	srv_send((char *)"%s,%s,%s", unit->uuid, unit->alias, 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->uuid = malloc(37);
	uuid_generate(uu);
	uuid_unparse(uu, unit->uuid);
	unit->product_uuid = NULL;
	unit->product_code = xstrcpy((char *)"FAKE0000");
	unit->product_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_uuid = unit->profile_name = 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 = unit->profile_fridge_mode = \
			unit->profile_duration = unit->profile_totalsteps = 0;
	unit->profile_steps = NULL;
	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_lo = unit->beer_set_hi = unit->fridge_set_lo = unit->fridge_set_hi = unit->profile_inittemp_lo = unit->profile_inittemp_hi =20.0;
	unit->heater_state = unit->cooler_state = unit->fan_state = unit->door_state = unit->mode = \
			     unit->light_state = unit->light_timer = unit->psu_state = unit->prof_state = unit->stage = 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 *)"UUID,%s", unit->uuid);
		srv_send((char *)"ALIAS,%s", unit->alias);
		srv_send((char *)"PRODUCT_NAME,%s", unit->product_name);
		srv_send((char *)"PRODUCT_CODE,%s", unit->product_code);
		srv_send((char *)"MODE,%s", UNITMODE[unit->mode]);
		srv_send((char *)"STAGE,%s", UNITSTAGE[unit->stage]);
		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_LO,%.1f", unit->fridge_set_lo);
		srv_send((char *)"FRIDGE_SET_HI,%.1f", unit->fridge_set_hi);
		srv_send((char *)"BEER_SET_LO,%.1f", unit->beer_set_lo);
		srv_send((char *)"BEER_SET_HI,%.1f", unit->beer_set_hi);
		if (unit->profile_uuid) {
		    srv_send((char *)"PROFILE_UUID,%s", unit->profile_uuid);
		    srv_send((char *)"PROFILE_NAME,%s", unit->profile_name);
		    srv_send((char *)"PROFILE_INITTEMP_LO,%.1f", unit->profile_inittemp_lo);
		    srv_send((char *)"PROFILE_INITTEMP_HI,%.1f", unit->profile_inittemp_hi);
		    srv_send((char *)"PROFILE_FRIDGE_MODE,%d", unit->profile_fridge_mode);
		    srv_send((char *)"PROFILE_DURATION,%d", unit->profile_duration);
		    srv_send((char *)"PROFILE_TOTALSTEPS,%d", unit->profile_totalsteps);
		    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 *)"PRODUCT_CODE") == 0)) {
				if (unit->product_code) {
				    if (strcmp(unit->product_code, val)) {
					syslog(LOG_NOTICE, "Fermenter unit %s name `%s' to `%s'", unit->uuid, unit->product_code, val);
				    }
				    free(unit->product_code);
				}
				unit->product_code = xstrcpy(val);
				unit->mqtt_flag |= MQTT_FLAG_DATA;

			    } else if (val && (strcmp(kwd, (char *)"PRODUCT_NAME") == 0)) {
				if (unit->product_name) {
				    if (strcmp(unit->product_name, val)) {
					syslog(LOG_NOTICE, "Fermenter unit %s name `%s' to `%s'", unit->uuid, unit->product_name, val);
				    }
				    free(unit->product_name);
				}
				unit->product_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 *)"STAGE") == 0)) {
				for (i = 0; i < 4; i++) {
				    if (strcmp(val, UNITSTAGE[i]) == 0) {
					if (i != unit->stage) {
					    syslog(LOG_NOTICE, "Fermenter unit %s stage %s to %s", unit->uuid, UNITSTAGE[unit->stage], UNITSTAGE[i]);
					    unit->mqtt_flag |= MQTT_FLAG_DATA;
					}
					unit->stage = i;
					break;
				    }
				}

			    } 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)) {
					    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 = unit->light_timer = 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_uuid) {
						unit->mqtt_flag |= MQTT_FLAG_DATA;
					    }
					}
					break;
				    }
				}

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

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

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

			    } else if (val && (strcmp(kwd, (char *)"BEER_SET_HI") == 0)) {
				if ((sscanf(val, "%f", &fval) == 1) && (fval >= unit->temp_set_min) && (fval <= unit->temp_set_max)) {
				    if (unit->beer_set_hi != fval)
					syslog(LOG_NOTICE, "Fermenter unit %s beer temp high %.1f to %.1f", unit->uuid, unit->beer_set_hi, fval);
				    unit->beer_set_hi = 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 (val && (strcmp(kwd, (char *)"PROF_STATE") == 0) && unit->profile_uuid) {
				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 = 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 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 (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, "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 *)"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");
#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");

#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;
	}
	usleep(100000);
    }
}

mercurial