thermferm/thermferm.c

Sat, 12 Sep 2020 15:23:49 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Sat, 12 Sep 2020 15:23:49 +0200
changeset 608
a69b5d92fd72
parent 606
798dd0c4fd00
child 611
732d482f47c8
permissions
-rw-r--r--

Changed pub_domoticz mqtt publishing.

/*****************************************************************************
 * Copyright (C) 2014-2020
 *   
 * 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 "lock.h"
#include "rdconfig.h"
#include "devices.h"
#include "server.h"
#include "thermferm.h"
#include "simulator.h"
#include "lcd-pcf8574.h"
#include "lcd-buffer.h"
#include "slcd.h"
#include "panel.h"
#include "futil.h"
#include "xutil.h"
#include "pid.h"
#include "mqtt.h"


int			my_shutdown = FALSE;
int			my_reboot = FALSE;
static pid_t		pgrp, mypid;
int			run_pause = FALSE;
int			run_hold = FALSE;

extern int		debug;
extern sys_config	Config;
extern int		lcdHandle;
extern int		slcdHandle;
int			setupmenu = MENU_NONE;
units_list		*current_unit = NULL;		/* In panel editor this points to the current unit. */
float			temp_temp = 20.0;

#ifndef HAVE_WIRINGPI_H
pthread_t		threads[5];
#endif
extern const char       UNITMODE[5][8];
extern const char	PROFSTATE[4][6];

extern int		sock;


unsigned char		degC[8] 	= { 0b01000, 0b10100, 0b01000, 0b00111, 0b01000, 0b01000, 0b01000, 0b00111 };	// [1] degree c sybmol
unsigned char		SP_Symbol[8]    = { 0b11100, 0b10000, 0b11100, 0b00111, 0b11101, 0b00111, 0b00100, 0b00100 };	// [2] SP Symbol
unsigned char		CoolONOFF[8]    = { 0b00000, 0b01110, 0b01000, 0b01000, 0b01000, 0b01000, 0b01110, 0b00000 };	// [3] Cool Symbol
unsigned char		RevCoolONOFF[8] = { 0b11111, 0b10001, 0b10111, 0b10111, 0b10111, 0b10111, 0b10001, 0b11111 };	// [4] Reverse Cool Symbol
unsigned char		HeatONOFF[8]    = { 0b00000, 0b01010, 0b01010, 0b01110, 0b01110, 0b01010, 0b01010, 0b00000 };	// [5] HEAT symbol
unsigned char		RevHeatONOFF[8] = { 0b11111, 0b10101, 0b10101, 0b10001, 0b10001, 0b10101, 0b10101, 0b11111 };	// [6] reverse HEAT symbol


int  server(void);
void help(void);
void die(int);
void stopLCD(void);
#ifdef HAVE_WIRINGPI_H
void sendRCswitch(char *, int);
void stopRCswitch(void);
#endif



extern int		mqtt_qos;
extern int		mqtt_last_mid;
extern int		mqtt_last_mid_sent;
extern int		mqtt_mid_sent;
extern int		mqtt_disconnect_sent;
extern int		mqtt_connected;
extern int		mqtt_status;
extern int		mqtt_use;
extern struct mosquitto	*mosq;
extern char		*state;



void help(void)
{
    fprintf(stdout, "mbsePi-apps thermferm v%s starting\n\n", VERSION);
    fprintf(stdout, "Usage: thermferm [-d] [-h]\n");
    fprintf(stdout, "  -d --debug              Debug and run in foreground\n");
    fprintf(stdout, "  -h --help               Display this help\n");
}



void die(int onsig)
{
    switch (onsig) {
	case SIGHUP:	syslog(LOG_NOTICE, "Got SIGHUP, shutting down");
			break;
	case SIGINT:	syslog(LOG_NOTICE, "Keyboard interrupt, shutting down");
			break;
	case SIGTERM:	syslog(LOG_NOTICE, "Got SIGTERM, shutting down");
			break;
	case SIGSEGV:	syslog(LOG_NOTICE, "Got SIGSEGV, shutting down");
			my_shutdown = TRUE;
			exit(SIGSEGV);
			break;
	default:	syslog(LOG_NOTICE, "die() on signal %d", onsig);
    }

    my_shutdown = TRUE;
}



void show_mode(void)
{
    char	buf[21];

    snprintf(buf, 20, "Old mode %s", UNITMODE[current_unit->mode]);
#ifdef HAVE_WIRINGPI_H
    lcdPuts(lcdHandle, buf);
    lcdPosition(lcdHandle, 0, 1);
#endif
    slcdPuts(slcdHandle, buf);
    slcdPosition(slcdHandle, 0, 1);
}



void go_menu(int menu)
{
    char		buf[21];

#ifdef HAVE_WIRINGPI_H
    piLock(LOCK_LCD);
    piLock(LOCK_MENU);
    lcdClear(lcdHandle);
    lcdPosition(lcdHandle, 0, 0);
#endif
    slcdClear(slcdHandle);
    slcdPosition(slcdHandle, 0, 0);
    setupmenu = menu;

    switch (menu) {
	case MENU_NONE:		
				lcd_buf_show();
				break;

	case MENU_TOP_DEFAULT:	
#ifdef HAVE_WIRINGPI_H
				lcdPuts(lcdHandle, "Setup mode.");
				lcdPosition(lcdHandle, 0, 1);
				lcdPuts(lcdHandle, "Up&Down = Escape");
#endif
				slcdPuts(slcdHandle, "Setup mode.");
				slcdPosition(slcdHandle, 0, 1);
				slcdPuts(slcdHandle, "Up&Down = Escape");
				break;

	case MENU_TOP_UNITS:	
#ifdef HAVE_WIRINGPI_H
				lcdPuts(lcdHandle, "Select units");
#endif
				slcdPuts(slcdHandle, "Select units");
				break;

	case MENU_UNITS:	
#ifdef HAVE_WIRINGPI_H
				lcdPuts(lcdHandle, "Choose unit:");
				lcdPosition(lcdHandle, 0, 1);
				lcdPuts(lcdHandle, current_unit->alias);
#endif
				slcdPuts(slcdHandle, "Choose unit:");
				slcdPosition(slcdHandle, 0, 1);
				slcdPuts(slcdHandle, current_unit->alias);
				break;

	case MENU_MODE_OFF:	show_mode();
#ifdef HAVE_WIRINGPI_H
				lcdPuts(lcdHandle, "New mode OFF");
#endif
				slcdPuts(slcdHandle, "New mode OFF");
				break;

	case MENU_MODE_NONE:	show_mode();
#ifdef HAVE_WIRINGPI_H
				lcdPuts(lcdHandle, "New mode NONE");
#endif
				slcdPuts(slcdHandle, "New mode NONE");
				break;

	case MENU_NONE_HEAT:	snprintf(buf, Config.lcd_cols, "Set heater %s", current_unit->heater_state ? "OFF":"ON");
#ifdef HAVE_WIRINGPI_H
				lcdPuts(lcdHandle, buf);
#endif
				slcdPuts(slcdHandle, buf);
				break;

	case MENU_NONE_COOL:	snprintf(buf, Config.lcd_cols, "Set cooler %s", current_unit->cooler_state ? "OFF":"ON");
#ifdef HAVE_WIRINGPI_H
				lcdPuts(lcdHandle, buf);
#endif
				slcdPuts(slcdHandle, buf);
				break;

	case MENU_NONE_FAN:	snprintf(buf, Config.lcd_cols, "Set fan %s", current_unit->fan_state ? "OFF":"ON");
#ifdef HAVE_WIRINGPI_H
				lcdPuts(lcdHandle, buf);
#endif
				slcdPuts(slcdHandle, buf);
				break;

	case MENU_MODE_BEER:	show_mode();
#ifdef HAVE_WIRINGPI_H
				lcdPuts(lcdHandle, "New mode BEER");
#endif
				slcdPuts(slcdHandle, "New mode BEER");
				break;

	case MENU_BEER_TEMP_LO:
#ifdef HAVE_WIRINGPI_H
				lcdPuts(lcdHandle, "Set beer low");
				lcdPosition(lcdHandle, 0, 1);
#endif
				slcdPuts(slcdHandle, "Set beer low");
				slcdPosition(slcdHandle, 0, 1);
				snprintf(buf, Config.lcd_cols, "Set %.1f", temp_temp);
#ifdef HAVE_WIRINGPI_H
				lcdPuts(lcdHandle, buf);
#endif
				slcdPuts(slcdHandle, buf);
				break;

        case MENU_BEER_TEMP_HI:
#ifdef HAVE_WIRINGPI_H
				lcdPuts(lcdHandle, "Set beer high");
				lcdPosition(lcdHandle, 0, 1);
#endif
				slcdPuts(slcdHandle, "Set beer high");
				slcdPosition(slcdHandle, 0, 1);
				snprintf(buf, Config.lcd_cols, "Set %.1f", temp_temp);
#ifdef HAVE_WIRINGPI_H
				lcdPuts(lcdHandle, buf);
#endif
				slcdPuts(slcdHandle, buf);
				break;

	case MENU_MODE_FRIDGE:	show_mode();
#ifdef HAVE_WIRINGPI_H
				lcdPuts(lcdHandle, "New mode FRIDGE");
#endif
				slcdPuts(slcdHandle, "New mode FRIDGE");
				break;

	case MENU_FRIDGE_TEMP_LO:
#ifdef HAVE_WIRINGPI_H
				lcdPuts(lcdHandle, "Set fridge low");
				lcdPosition(lcdHandle, 0, 1);
#endif
				slcdPuts(slcdHandle, "Set fridge low");
				slcdPosition(slcdHandle, 0, 1);
				snprintf(buf, Config.lcd_cols, "Set %.1f", temp_temp);
#ifdef HAVE_WIRINGPI_H
				lcdPuts(lcdHandle, buf);
#endif
				slcdPuts(slcdHandle, buf);
				break;

        case MENU_FRIDGE_TEMP_HI:
#ifdef HAVE_WIRINGPI_H
				lcdPuts(lcdHandle, "Set fridge high");
				lcdPosition(lcdHandle, 0, 1);
#endif
				slcdPuts(slcdHandle, "Set fridge high");
				slcdPosition(slcdHandle, 0, 1);
				snprintf(buf, Config.lcd_cols, "Set %.1f", temp_temp);
#ifdef HAVE_WIRINGPI_H
				lcdPuts(lcdHandle, buf);
#endif
				slcdPuts(slcdHandle, buf);
				break;

	case MENU_MODE_PROFILE:	show_mode();
#ifdef HAVE_WIRINGPI_H
				lcdPuts(lcdHandle, "New mode PROFILE");
#endif
				slcdPuts(slcdHandle, "New mode PROFILE");
				break;

	case MENU_PROFILE_START:	snprintf(buf, Config.lcd_cols, "%s", current_unit->profile_name);
#ifdef HAVE_WIRINGPI_H
					lcdPuts(lcdHandle, buf);
					lcdPosition(lcdHandle, 0, 1);
					lcdPuts(lcdHandle, "Start profile");
#endif
					slcdPuts(slcdHandle, buf);
					slcdPosition(slcdHandle, 0, 1);
					slcdPuts(slcdHandle, "Start profile");
					break;
	
	case MENU_PROFILE_PAUSE:	snprintf(buf, Config.lcd_cols, "%s", current_unit->profile_name);
#ifdef HAVE_WIRINGPI_H
					lcdPuts(lcdHandle, buf);
					lcdPosition(lcdHandle, 0, 1);
					lcdPuts(lcdHandle, "Pause profile");
#endif
					slcdPuts(slcdHandle, buf);
					slcdPosition(slcdHandle, 0, 1);
					slcdPuts(slcdHandle, "Pause profile");
					break;

	case MENU_PROFILE_ABORT:	snprintf(buf, Config.lcd_cols, "%s", current_unit->profile_name);
#ifdef HAVE_WIRINGPI_H
					lcdPuts(lcdHandle, buf);
					lcdPosition(lcdHandle, 0, 1);
					lcdPuts(lcdHandle, "Abort profile");
#endif
					slcdPuts(slcdHandle, buf);
					slcdPosition(slcdHandle, 0, 1);
					slcdPuts(slcdHandle, "Abort profile");
					break;

	case MENU_PROFILE_RESUME:	snprintf(buf, Config.lcd_cols, "%s", current_unit->profile_name);
#ifdef HAVE_WIRINGPI_H
					lcdPuts(lcdHandle, buf);
					lcdPosition(lcdHandle, 0, 1);
					lcdPuts(lcdHandle, "Resume profile");
#endif
					slcdPuts(slcdHandle, buf);
					slcdPosition(slcdHandle, 0, 1);
					slcdPuts(slcdHandle, "Resume profile");
					break;

	case MENU_PROFILE_GOOFF:	snprintf(buf, Config.lcd_cols, "%s", current_unit->profile_name);
#ifdef HAVE_WIRINGPI_H
					lcdPuts(lcdHandle, buf);
					lcdPosition(lcdHandle, 0, 1);
					lcdPuts(lcdHandle, "Set profile OFF");
#endif
					slcdPuts(slcdHandle, buf);
					slcdPosition(slcdHandle, 0, 1);
					slcdPuts(slcdHandle, "Set profile OFF");
					break;

	case MENU_TOP_SYS:	
#ifdef HAVE_WIRINGPI_H
					lcdPuts(lcdHandle, "System menu");
#endif
					slcdPuts(slcdHandle, "System menu");
					break;

	case MENU_SYS_HALT:	
#ifdef HAVE_WIRINGPI_H				
				lcdPuts(lcdHandle, "Halt system");
#endif
				slcdPuts(slcdHandle, "Halt system");
				break;

	case MENU_SYS_REBOOT:	
#ifdef HAVE_WIRINGPI_H				
				lcdPuts(lcdHandle, "Reboot system");
#endif
				slcdPuts(slcdHandle, "Reboot system");
				break;
    }

#ifdef HAVE_WIRINGPI_H
    piUnlock(LOCK_MENU);
    piUnlock(LOCK_LCD);
#endif
}



void stopLCD(void)
{
#ifdef HAVE_WIRINGPI_H
    piLock(LOCK_LCD);
    lcdClear(lcdHandle);
#endif
    slcdClear(slcdHandle);
    setBacklight(0);
#ifdef HAVE_WIRINGPI_H
    piUnlock(LOCK_LCD);
#endif
}



/*
 * Change mode of current_unit
 */
void change_mode(int mode)
{
    current_unit->mqtt_flag |= MQTT_FLAG_DATA;
    if ((current_unit->mode == UNITMODE_OFF) && (mode != UNITMODE_OFF)) {
	current_unit->mqtt_flag |= MQTT_FLAG_BIRTH;
    }
    syslog(LOG_NOTICE, "Mode from %s to %s via panel interface", UNITMODE[current_unit->mode], UNITMODE[mode]);
    current_unit->mode = mode;
    /* Allways turn everything off after a mode change */
    current_unit->PID_cool->OutP = current_unit->PID_heat->OutP = 0.0;
    current_unit->PID_cool->Mode = current_unit->PID_heat->Mode = PID_MODE_NONE;
    current_unit->heater_state = current_unit->cooler_state = current_unit->fan_state = current_unit->light_state = current_unit->light_timer = 0;
    current_unit->heater_wait = current_unit->cooler_wait = current_unit->fan_wait = current_unit->light_wait = 0;
    device_out(current_unit->heater_address, current_unit->heater_state);
    device_out(current_unit->cooler_address, current_unit->cooler_state);
    device_out(current_unit->fan_address, current_unit->fan_state);
    device_out(current_unit->light_address, current_unit->light_state);
    if (current_unit->mode == UNITMODE_PROFILE) {
	/*
	 * Set a sane default until it will be overruled by the
	 * main processing loop.
	 */
	current_unit->prof_target_lo = 20.0;
	current_unit->prof_target_hi = 20.0;
	current_unit->prof_fridge_mode = 0;
    }
}



/*
 * Handle panel key events
 */
void panel_key_events(int key)
{
    units_list		*unit;
    int			rc;

    switch (setupmenu) {
	case MENU_NONE:
		    	if ((key == KEY_DOWN) || (key == KEY_UP))
	    		    lcd_buf_step(key);
			if ((key == KEY_CONFIRM) && (setupmenu == MENU_NONE))
			    go_menu(MENU_TOP_DEFAULT);
			break;

	case MENU_TOP_DEFAULT:
	    		if (key == KEY_ESCAPE)
			    go_menu(MENU_NONE);
			if (key == KEY_DOWN)
			    go_menu(MENU_TOP_UNITS);
			if (key == KEY_UP)
			    go_menu(MENU_TOP_SYS);
			break;

	case MENU_TOP_UNITS:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_NONE);
			if (key == KEY_DOWN)
			    go_menu(MENU_TOP_SYS);
			if (key == KEY_UP)
			    go_menu(MENU_TOP_DEFAULT);
			if ((key == KEY_ENTER) && Config.units) {
			    /*
			     * Start with the first unit
			     */
			    current_unit = Config.units;
			    go_menu(MENU_UNITS);
			}
			break;

	case MENU_UNITS:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_TOP_UNITS);
			if (key == KEY_DOWN) {
			    if (current_unit->next) {
				current_unit = current_unit->next;
				go_menu(MENU_UNITS);
			    }
			}
			if (key == KEY_UP) {
			    for (unit = Config.units; unit; unit = unit->next) {
				if (unit->next && (unit->next == current_unit)) {
				    current_unit = unit;
				    go_menu(MENU_UNITS);
				    break;
				}
			    }
			}
			if (key == KEY_ENTER) {
			    /*
			     * Drop into the current mode
			     */
			    switch (current_unit->mode) {
				case UNITMODE_OFF:	go_menu(MENU_MODE_OFF);
							break;
				case UNITMODE_NONE:	go_menu(MENU_MODE_NONE);
							break;
				case UNITMODE_FRIDGE:	go_menu(MENU_MODE_FRIDGE);
							break;
				case UNITMODE_BEER:	go_menu(MENU_MODE_BEER);
							break;
				case UNITMODE_PROFILE:	go_menu(MENU_MODE_PROFILE);
							break;
			    }
			}
			break;

	case MENU_MODE_OFF:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_UNITS);
			if (key == KEY_DOWN)
			    go_menu(MENU_MODE_NONE);
			if (key == KEY_UP) {
			    if (current_unit->profile_uuid)
			    	go_menu(MENU_MODE_PROFILE);
			    else
				go_menu(MENU_MODE_BEER);
			}
			if (key == KEY_ENTER) {
			    change_mode(UNITMODE_OFF);
			    go_menu(MENU_MODE_OFF);
			}
			break;

	case MENU_MODE_NONE:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_UNITS);
			if (key == KEY_DOWN)
			    go_menu(MENU_MODE_FRIDGE);
			if (key == KEY_UP)
			    go_menu(MENU_MODE_OFF);
			if (key == KEY_ENTER) {
			    if (current_unit->mode == UNITMODE_NONE)
				go_menu(MENU_NONE_HEAT);
			    else {
			    	change_mode(UNITMODE_NONE);
				go_menu(MENU_MODE_NONE);
			    }
			}
			break;

	case MENU_NONE_HEAT:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_MODE_NONE);
			if (key == KEY_DOWN)
			    go_menu(MENU_NONE_COOL);
			if (key == KEY_UP)
			    go_menu(MENU_NONE_FAN);
			if (key == KEY_ENTER) {
			    if (current_unit->heater_state)
				current_unit->heater_state = 0;
			    else
				current_unit->heater_state = 100;
			    go_menu(MENU_NONE_HEAT);
			}
			break;

	case MENU_NONE_COOL:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_MODE_NONE);
			if (key == KEY_DOWN)
			    go_menu(MENU_NONE_FAN);
			if (key == KEY_UP)      
			    go_menu(MENU_NONE_HEAT);
			if (key == KEY_ENTER) {
			    if (current_unit->cooler_state)
				current_unit->cooler_state = 0;
			    else
				current_unit->cooler_state = 100;
			    go_menu(MENU_NONE_COOL);
			}
			break;

	case MENU_NONE_FAN:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_MODE_NONE);
			if (key == KEY_DOWN)
			    go_menu(MENU_NONE_HEAT);
			if (key == KEY_UP)      
			    go_menu(MENU_NONE_COOL);
			if (key == KEY_ENTER) {
			    if (current_unit->fan_state)
				current_unit->fan_state = 0;
			    else
				current_unit->fan_state = 100;
			    go_menu(MENU_NONE_FAN);
			}
			break;

	case MENU_MODE_FRIDGE:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_UNITS);
			if (key == KEY_DOWN)
			    go_menu(MENU_MODE_BEER);
			if (key == KEY_UP)
			    go_menu(MENU_MODE_NONE);
			if (key == KEY_ENTER) {
			    if (current_unit->mode == UNITMODE_FRIDGE) {
				temp_temp = current_unit->fridge_set_lo;
				go_menu(MENU_FRIDGE_TEMP_LO);
			    } else {
			    	change_mode(UNITMODE_FRIDGE);
			    	go_menu(MENU_MODE_FRIDGE);
			    }
			}
			break;

	case MENU_FRIDGE_TEMP_LO:
			if (key == KEY_ESCAPE) {
			    temp_temp = current_unit->fridge_set_hi;
			    go_menu(MENU_FRIDGE_TEMP_HI);
			}
			if (key == KEY_DOWN) {
			    if (temp_temp > current_unit->temp_set_min)
				temp_temp -= 0.1;
			    go_menu(MENU_FRIDGE_TEMP_LO);
			}
			if (key == KEY_UP) {
			    if (temp_temp < current_unit->temp_set_max)
				temp_temp += 0.1;
			    go_menu(MENU_FRIDGE_TEMP_LO);
			}
			if (key == KEY_CONFIRM) {
			    if (temp_temp != current_unit->fridge_set_lo) {
				syslog(LOG_NOTICE, "Fridge temp low changed from %.1f to %.1f from the panel", current_unit->fridge_set_lo, temp_temp);
				current_unit->fridge_set_lo = temp_temp;
				current_unit->mqtt_flag |= MQTT_FLAG_DATA;
			    }
			    temp_temp = current_unit->fridge_set_hi;
			    go_menu(MENU_FRIDGE_TEMP_HI);
			}
			break;

	case MENU_FRIDGE_TEMP_HI:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_MODE_FRIDGE);
			if (key == KEY_DOWN) {
			    if (temp_temp > current_unit->temp_set_min)
				temp_temp -= 0.1;
			    go_menu(MENU_FRIDGE_TEMP_HI);
			}
			if (key == KEY_UP) {
			    if (temp_temp < current_unit->temp_set_max)
				temp_temp += 0.1;
			    go_menu(MENU_FRIDGE_TEMP_HI);
			}
			if (key == KEY_CONFIRM) {
			    if (temp_temp != current_unit->fridge_set_hi) {
				syslog(LOG_NOTICE, "Fridge temp high changed from %.1f to %.1f from the panel", current_unit->fridge_set_hi, temp_temp);
				current_unit->fridge_set_hi = temp_temp;
				current_unit->mqtt_flag |= MQTT_FLAG_DATA;
			    }
			    go_menu(MENU_MODE_FRIDGE);
			}
			break;

	case MENU_MODE_BEER:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_UNITS);
			if (key == KEY_DOWN) {
			    if (current_unit->profile_uuid)
			    	go_menu(MENU_MODE_PROFILE);
			    else
				go_menu(MENU_MODE_OFF);
			}
			if (key == KEY_UP)
			    go_menu(MENU_MODE_FRIDGE);
			if (key == KEY_ENTER) {
			    if (current_unit->mode == UNITMODE_BEER) {
				temp_temp = current_unit->beer_set_lo;
				go_menu(MENU_BEER_TEMP_LO);
			    } else {
			    	change_mode(UNITMODE_BEER);
			    	go_menu(MENU_MODE_BEER);
			    }
			}
			break;

	case MENU_BEER_TEMP_LO:
			if (key == KEY_ESCAPE) {
			    temp_temp = current_unit->beer_set_hi;
			    go_menu(MENU_BEER_TEMP_HI);
			}
			if (key == KEY_DOWN) {
			    if (temp_temp > current_unit->temp_set_min)
				temp_temp -= 0.1;
			    go_menu(MENU_BEER_TEMP_LO);
			}
			if (key == KEY_UP) {
			    if (temp_temp < current_unit->temp_set_max)
				temp_temp += 0.1;
			    go_menu(MENU_BEER_TEMP_LO);
			}
			if (key == KEY_CONFIRM) {
			    if (temp_temp != current_unit->beer_set_lo ) {
				syslog(LOG_NOTICE, "Beer temp low changed from %.1f to %.1f from the panel", current_unit->beer_set_lo, temp_temp);
				current_unit->beer_set_lo = temp_temp;
				current_unit->mqtt_flag |= MQTT_FLAG_DATA;
			    }
			    temp_temp = current_unit->beer_set_hi;
			    go_menu(MENU_BEER_TEMP_HI);
			}
			break;

	case MENU_BEER_TEMP_HI:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_MODE_BEER);
			if (key == KEY_DOWN) {
			    if (temp_temp > current_unit->temp_set_min)
				temp_temp -= 0.1;
			    go_menu(MENU_BEER_TEMP_HI);
			}
			if (key == KEY_UP) {
			    if (temp_temp < current_unit->temp_set_max)
				temp_temp += 0.1;
			    go_menu(MENU_BEER_TEMP_HI);
			}
			if (key == KEY_CONFIRM) {
			    if (temp_temp != current_unit->beer_set_hi ) {
				syslog(LOG_NOTICE, "Beer temp high changed from %.1f to %.1f from the panel", current_unit->beer_set_hi, temp_temp);
				current_unit->beer_set_hi = temp_temp;
				current_unit->mqtt_flag |= MQTT_FLAG_DATA;
			    }
			    go_menu(MENU_MODE_BEER);
			}
			break;

	case MENU_MODE_PROFILE:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_UNITS);
			if (key == KEY_DOWN)
			    go_menu(MENU_MODE_OFF);     
			if (key == KEY_UP)
			    go_menu(MENU_MODE_BEER);                                
			if (key == KEY_ENTER) {
			    if (current_unit->mode == UNITMODE_PROFILE) {
				switch (current_unit->prof_state) {
				    case PROFILE_OFF:	go_menu(MENU_PROFILE_START);
							break;
				    case PROFILE_PAUSE:	go_menu(MENU_PROFILE_RESUME);
							break;
				    case PROFILE_RUN:	go_menu(MENU_PROFILE_PAUSE);
							break;
				    case PROFILE_DONE:	go_menu(MENU_PROFILE_GOOFF);
							break;
				}
			    } else {
			    	change_mode(UNITMODE_PROFILE);
			    	go_menu(MENU_MODE_PROFILE);
			    }
			}
			break;

	case MENU_PROFILE_START:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_MODE_PROFILE);
			if (key == KEY_ENTER) {
			    current_unit->prof_state = PROFILE_RUN;
			    current_unit->prof_started = time(NULL);
			    current_unit->prof_paused = current_unit->prof_primary_done = 0;
			    current_unit->prof_peak_abs = current_unit->prof_peak_rel = 0.0;
			    syslog(LOG_NOTICE, "Profile started from the panel");
			    current_unit->mqtt_flag |= MQTT_FLAG_DATA;
			    go_menu(MENU_MODE_PROFILE);
			}
			break;

	case MENU_PROFILE_PAUSE:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_MODE_PROFILE);
			if ((key == KEY_DOWN) || (key == KEY_UP)) 
			    go_menu(MENU_PROFILE_ABORT);
			if (key == KEY_ENTER) {
			    current_unit->prof_state = PROFILE_PAUSE;
			    current_unit->mqtt_flag |= MQTT_FLAG_DATA;
			    syslog(LOG_NOTICE, "Profile pause from the panel");
			    go_menu(MENU_MODE_PROFILE);
			}
			break;

	case MENU_PROFILE_ABORT:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_MODE_PROFILE);
			if ((key == KEY_DOWN) || (key == KEY_UP)) {
			    if (current_unit->prof_state == PROFILE_PAUSE)
				go_menu(MENU_PROFILE_RESUME);
			    else if (current_unit->prof_state == PROFILE_RUN)
				go_menu(MENU_PROFILE_PAUSE);
			}
			if ((key == KEY_ENTER) && ((current_unit->prof_state == PROFILE_RUN) || (current_unit->prof_state == PROFILE_PAUSE))) {
			    current_unit->prof_state = PROFILE_OFF;
			    current_unit->prof_started = 0;
			    syslog(LOG_NOTICE, "Profile aborted from the panel");
			    current_unit->mqtt_flag |= MQTT_FLAG_DATA;
			    go_menu(MENU_MODE_PROFILE);
			}
			break;

	case MENU_PROFILE_RESUME:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_MODE_PROFILE);
			if ((key == KEY_DOWN) || (key == KEY_UP))
			    go_menu(MENU_PROFILE_ABORT);
			if (key == KEY_ENTER) {
			    current_unit->prof_state = PROFILE_RUN;
			    syslog(LOG_NOTICE, "Profile resume from the panel");
			    current_unit->mqtt_flag |= MQTT_FLAG_DATA;
			    go_menu(MENU_MODE_PROFILE);
			}
			break;

	case MENU_PROFILE_GOOFF:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_MODE_PROFILE);
			if (key == KEY_ENTER) {
			    if (current_unit->prof_state == PROFILE_DONE) {
				current_unit->prof_state = PROFILE_OFF;
				current_unit->mqtt_flag |= MQTT_FLAG_DATA;
				syslog(LOG_NOTICE, "Profile from done to off from the panel");
			    }
			}
			break;

	case MENU_TOP_SYS:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_NONE);
			if (key == KEY_DOWN)
			    go_menu(MENU_TOP_DEFAULT);
			if (key == KEY_UP)
			    go_menu(MENU_TOP_UNITS);
			if (key == KEY_ENTER)
			    go_menu(MENU_SYS_HALT);
			break;

	case MENU_SYS_HALT:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_TOP_SYS);
			if ((key == KEY_DOWN) || (key == KEY_UP))
			    go_menu(MENU_SYS_REBOOT);
			if (key == KEY_CONFIRM) {
			    rc = system("/sbin/halt");
			    syslog(LOG_NOTICE, "System halt from panel: /sbin/halt rc=%d", rc);
			    go_menu(MENU_NONE);
			}
			break;

	case MENU_SYS_REBOOT:
			if (key == KEY_ESCAPE)
			    go_menu(MENU_TOP_SYS);
			if ((key == KEY_DOWN) || (key == KEY_UP))
			    go_menu(MENU_SYS_HALT);
			if (key == KEY_CONFIRM) {
			    rc = system("/sbin/reboot");
			    syslog(LOG_NOTICE, "System reboot from panel: /sbin/reboot rc=%d", rc);
			    go_menu(MENU_NONE);
			}
			break;
    }
}



int main(int argc, char *argv[])
{
    int		rc, c, i;
    pid_t	frk;

    while (1) {
	int option_index = 0;
	static struct option long_options[] = {
	    {"debug", 0, 0, 'c'},
	    {"help", 0, 0, 'h'},
	    {0, 0, 0, 0}
	};

	c = getopt_long(argc, argv, "dh", long_options, &option_index);
	if (c == -1)
	    break;

	switch (c) {
	    case 'd':	debug = TRUE;
			break;
	    case 'h':	help();
			return 1;
	}
    }

    openlog("thermferm", LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_USER);
    syslog(LOG_NOTICE, "mbsePi-apps thermferm v%s starting", VERSION);
    if (debug)
	fprintf(stdout, "mbsePi-apps thermferm v%s starting\n", VERSION);

    if (rdconfig()) {
	fprintf(stderr, "Error reading configuration\n");
	syslog(LOG_NOTICE, "Error reading configuration: halted");
	return 1;
    }

    /*
     *  Catch all the signals we can, and ignore the rest. Note that SIGKILL can't be ignored
     *  but that's live. This daemon should only be stopped by SIGTERM.
     *  Don't catch SIGCHLD.
     */
    for (i = 0; i < NSIG; i++) {
    	if ((i != SIGCHLD) && (i != SIGKILL) && (i != SIGSTOP))
	    signal(i, (void (*))die);
    }

#ifdef HAVE_WIRINGPI_H
    if (wiringPiSetup () )
	return 1;
#endif

    if ((rc = initLCD (Config.lcd_cols, Config.lcd_rows))) {
	fprintf(stderr, "Cannot initialize LCD display, rc=%d\n", rc);
	return 1;
    }

#ifdef HAVE_WIRINGPI_H
    lcdCharDef(lcdHandle, 1, degC);
    lcdCharDef(lcdHandle, 2, SP_Symbol);
    lcdCharDef(lcdHandle, 3, CoolONOFF);
    lcdCharDef(lcdHandle, 4, RevCoolONOFF);
    lcdCharDef(lcdHandle, 5, HeatONOFF);
    lcdCharDef(lcdHandle, 6, RevHeatONOFF);
#endif
    slcdCharDef(slcdHandle, 1, degC);
    slcdCharDef(slcdHandle, 2, SP_Symbol);
    slcdCharDef(slcdHandle, 3, CoolONOFF);
    slcdCharDef(slcdHandle, 4, RevCoolONOFF);
    slcdCharDef(slcdHandle, 5, HeatONOFF);
    slcdCharDef(slcdHandle, 6, RevHeatONOFF);

    if (debug) {
	/*
	 * For debugging run in foreground.
	 */
	do {
	    rc = server();
	} while (my_reboot == TRUE);
    } else {
	/*
	 * Server initialization is complete. Now we can fork the 
	 * daemon and return to the user. We need to do a setpgrp
	 * so that the daemon will no longer be assosiated with the
	 * users control terminal. This is done before the fork, so
	 * that the child will not be a process group leader. Otherwise,
	 * if the child were to open a terminal, it would become
	 * associated with that terminal as its control terminal.
	 */
	if ((pgrp = setpgid(0, 0)) == -1) {
	    syslog(LOG_NOTICE, "setpgpid failed");
	}

	frk = fork();
	switch (frk) {
	    case -1:	
		    	syslog(LOG_NOTICE, "Daemon fork failed: %s", strerror(errno));
			stopLCD();
			exit(1);
	    case 0:	/*
			 * Run the daemon
			 */
			fclose(stdin);
			if (open("/dev/null", O_RDONLY) != 0) {
			    syslog(LOG_NOTICE, "Reopen of stdin to /dev/null failed");
			    _exit(2);
			}
			fclose(stdout);
			if (open("/dev/null", O_WRONLY | O_APPEND | O_CREAT,0600) != 1) {
			    syslog(LOG_NOTICE, "Reopen of stdout to /dev/null failed");
			    _exit(2);
			}
			fclose(stderr);
			if (open("/dev/null", O_WRONLY | O_APPEND | O_CREAT,0600) != 2) {
			    syslog(LOG_NOTICE, "Reopen of stderr to /dev/null failed");
			    _exit(2);
			}
			mypid = getpid();
			do {
			    rc = server();
			} while (my_reboot == TRUE);
			break;
			/* Not reached */
	    default:
			/*
			 * Here we detach this process and let the child
			 * run the deamon process.
			 */
			syslog(LOG_NOTICE, "Starting daemon with pid %d", frk);
			exit(0);
	}
    }

    killconfig();
    syslog(LOG_NOTICE, "Finished, rc=%d", rc);
    if (debug)
        fprintf(stdout, "Finished, rc=%d\n", rc);
    return rc;
}



int server(void)
{
    time_t		now, last = (time_t)0, ndata = (time_t)0;;
    units_list		*unit;
    prof_step		*step;
    int			row, rc, run = 1, seconds = 0, minutes = 0, temp;
    int			run_seconds, run_minutes, run_hours, tot_minutes, key;
    struct tm		*tm;
#ifndef HAVE_WIRINGPI_H
    long		t = 0;
#endif
    int			current_step, valid_step, time_until_now, previous_fridge_mode;
    float		previous_target_lo, previous_target_hi;
    float		LCDair, LCDbeer, LCDspL, LCDspH;
    unsigned char	LCDstatC, LCDstatH;
    int			LCDunit;

    syslog(LOG_NOTICE, "Server process started");
    my_shutdown = my_reboot = FALSE;
    if (lockprog((char *)"thermferm")) {
	syslog(LOG_NOTICE, "Can't lock");
	return 1;
    }
    mqtt_connect();

    if ((rc = devices_detect())) {
	syslog(LOG_NOTICE, "Detected %d new devices", rc);
	wrconfig();
    }

#ifdef HAVE_WIRINGPI_H
    rc = piThreadCreate(my_devices_loop);
#else
    rc = pthread_create(&threads[t], NULL, my_devices_loop, (void *)t );
#endif
    if (rc) {
	fprintf(stderr, "my_devices_loop thread didn't start rc=%d\n", rc);
	syslog(LOG_NOTICE, "my_devices_loop thread didn't start rc=%d", rc);
#ifndef HAVE_WIRINGPI_H
    } else {
	t++;
#endif
    }

#ifdef HAVE_WIRINGPI_H
    rc = piThreadCreate(my_server_loop);
#else
    rc = pthread_create(&threads[t], NULL, my_server_loop, (void *)t );
#endif
    if (rc) {
	fprintf(stderr, "my_server_loop thread didn't start rc=%d\n", rc);
	syslog(LOG_NOTICE, "my_server_loop thread didn't start rc=%d", rc);
#ifndef HAVE_WIRINGPI_H
    } else {
	t++;
#endif
    }

#ifdef HAVE_WIRINGPI_H
    rc = piThreadCreate(my_panel_loop);
#else
    rc = pthread_create(&threads[t], NULL, my_panel_loop, (void *)t );
#endif
    if (rc) {
	fprintf(stderr, "my_panel_loop thread didn't start rc=%d\n", rc);
	syslog(LOG_NOTICE, "my_panel_loop thread didn't start rc=%d", rc);
#ifndef HAVE_WIRINGPI_H
    } else {
	t++;
#endif
    }

#ifdef USE_SIMULATOR
#ifdef HAVE_WIRINGPI_H
    rc = piThreadCreate(my_simulator_loop);
#else
    rc = pthread_create(&threads[t], NULL, my_simulator_loop, (void *)t );
#endif
    if (rc) {
	fprintf(stderr, "my_simulator_loop thread didn't start rc=%d\n", rc);
	syslog(LOG_NOTICE, "my_simulator_loop thread didn't start rc=%d", rc);
#ifndef HAVE_WIRINGPI_H
    } else {
	t++;
#endif
    }
#endif


    /*
     * Initialize units for processing
     */
    for (unit = Config.units; unit; unit = unit->next) {
	/*
	 * Safety, turn everything off
	 */
	unit->mqtt_flag = unit->alarm_flag = unit->alarm_last = 0;
	unit->heater_state = unit->cooler_state = unit->fan_state = unit->door_state = unit->light_state = unit->light_timer = 0;
	unit->heater_wait = unit->cooler_wait = unit->fan_wait = unit->light_wait = 0;
	if (unit->mode == UNITMODE_PROFILE) {
	    if (!unit->profile_uuid)
		syslog(LOG_NOTICE, "Starting unit `%s' in profile mode, no profile defined.", unit->alias);
	    else {
	    	syslog(LOG_NOTICE, "Starting unit `%s' in profile state %s.", unit->alias, PROFSTATE[unit->prof_state]);
	    }
	} else if (unit->mode == UNITMODE_BEER) {
	    syslog(LOG_NOTICE, "Starting unit `%s' beer cooler at %.1f - %.1f degrees", unit->alias, unit->beer_set_lo, unit->beer_set_hi);
	} else if (unit->mode == UNITMODE_FRIDGE) {
	    syslog(LOG_NOTICE, "Starting unit `%s' as refridgerator at %.1f - %.1f degrees", unit->alias, unit->fridge_set_lo, unit->fridge_set_hi);
	} else if (unit->mode == UNITMODE_NONE) {
	    syslog(LOG_NOTICE, "Starting unit `%s' in inactive state", unit->alias);
	} else {
	    syslog(LOG_NOTICE, "Starting unit `%s' in off state", unit->alias);
	}
    }
    publishDBirthAll();

    for (unit = Config.units; unit; unit = unit->next) {
	if (unit->mode != UNITMODE_OFF) {
    	    unit->event_msg = xstrcpy((char *)"Startup");
            publishDLog(unit);
	    free(unit->event_msg);
	    unit->event_msg = NULL;
	}
    }

#ifdef HAVE_WIRINGPI_H
    piLock(LOCK_LCD);
#endif
    lcd_buf_write(1, (char *)"   ThermFerm    ");
    lcd_buf_write(2, (char *)" Version %s     ", VERSION);
#ifdef HAVE_WIRINGPI_H
    piUnlock(LOCK_LCD);
#endif


    do {
	if (my_shutdown)
	    run = 0;

	/*
	 * Use to stop processing units. Should be used when a unit is
	 * added or removed.
	 */
	if (run_pause) {
	    run_hold = TRUE;
	    syslog(LOG_NOTICE, "run_pause: entering hold state");
	    for (;;) {
		usleep(100000);
		if (! run_pause)
		    break;
	    }
	    syslog(LOG_NOTICE, "run_pause: leaving hold state");
	    run_hold = FALSE;
#ifdef HAVE_WIRINGPI_H
	    /*
	     * In case the LCD buffers were cleared, setup the first page.
	     */
	    piLock(LOCK_LCD);
#endif
	    lcd_buf_write(1, (char *)"   ThermFerm    ");
	    lcd_buf_write(2, (char *)" Version %s     ", VERSION);
#ifdef HAVE_WIRINGPI_H
	    piUnlock(LOCK_LCD);
#endif
	}

	now = time(NULL);
	if (now != last) {
	    /*
	     * Each second
	     */
	    last = now;
	    seconds++;

	    row = 3;
	    tm = localtime(&now);
#ifdef HAVE_WIRINGPI_H
	    piLock(LOCK_LCD);
#endif
	    lcd_buf_write(row++, "   %02d-%02d-%04d   ", tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900);
	    lcd_buf_write(row++, "    %02d:%02d:%02d    ", tm->tm_hour, tm->tm_min, tm->tm_sec);
#ifdef HAVE_WIRINGPI_H
	    piUnlock(LOCK_LCD);
#endif

#ifdef HAVE_WIRINGPI_H
	    piLock(LOCK_LCD);
#endif
	    lcd_buf_write(row, "Room temp N/A       ");
#ifdef HAVE_WIRINGPI_H
	    piUnlock(LOCK_LCD);
#endif
	    int updateHT = 0;
            if (Config.temp_address) {
		rc = device_in(Config.temp_address, &temp);
		if (rc == DEVPRESENT_YES) {
		    if (Config.temp_value != temp)
			updateHT = 1;
		    Config.temp_value = temp;
		    Config.temp_state = 0;
#ifdef HAVE_WIRINGPI_H
		    piLock(LOCK_LCD);
#endif
		    lcd_buf_write(row, "Room temp %.1f%c    ", Config.temp_value / 1000.0, 0x01);
#ifdef HAVE_WIRINGPI_H
		    piUnlock(LOCK_LCD);
#endif
		} else if (rc == DEVPRESENT_ERROR) {
		    Config.temp_state = 1;
		} else {
		    Config.temp_state = 2;
		}
		usleep(10000);
	    }
	    row++;

#ifdef HAVE_WIRINGPI_H
	    piLock(LOCK_LCD);
#endif
	    lcd_buf_write(row, " Humidity N/A       ");
#ifdef HAVE_WIRINGPI_H
	    piUnlock(LOCK_LCD);
#endif

	    if (Config.hum_address) {
		rc = device_in(Config.hum_address, &temp);
		if (rc == DEVPRESENT_YES) {
		    if (Config.hum_value != temp)
			updateHT = 1;
		    Config.hum_value = temp;
		    Config.hum_state = 0;
#ifdef HAVE_WIRINGPI_H
		    piLock(LOCK_LCD);
#endif
		    lcd_buf_write(row, " Humidity %.1f%%     ", Config.hum_value / 1000.0);
#ifdef HAVE_WIRINGPI_H
		    piUnlock(LOCK_LCD);
#endif
		} else if (rc == DEVPRESENT_ERROR) {
		    Config.hum_state = 1;
		} else {
		    Config.hum_state = 2;
		}
		usleep(10000);
	    }
	    row++;
	    /*
	     * If TH(B) changed. or if 5 minutes without
	     * update, send the NDATA message.
	     */
	    if (updateHT || (now > (ndata + 300))) {
		publishNData(false, 0);
		ndata = now;
	    }

	    LCDunit = 0;
	    for (unit = Config.units; unit; unit = unit->next) {
		LCDunit++;
		unit->mqtt_flag &= ~MQTT_FLAG_DATA;
		unit->alarm_flag = 0;

		if (unit->air_address) {
		    rc = device_in(unit->air_address, &temp);
		    if (rc == DEVPRESENT_YES) {
			if (unit->air_temperature != temp) {
			    unit->mqtt_flag |= MQTT_FLAG_DATA;
//			    pub_domoticz_temp(unit->air_idx, temp);
			}
			unit->air_temperature = temp;
			unit->air_state = 0;
		    } else if (rc == DEVPRESENT_ERROR) {
			unit->air_state = 1;
		    } else {
			unit->air_state = 2;
		    }
		}

		if (unit->beer_address) {
		    rc = device_in(unit->beer_address, &temp);
		    if (rc == DEVPRESENT_YES) {
			if (unit->beer_temperature != temp) {
			    unit->mqtt_flag |= MQTT_FLAG_DATA;
//			    pub_domoticz_temp(unit->beer_idx, temp);
			}
    			unit->beer_temperature = temp;
			unit->beer_state = 0;
		    } else if (rc == DEVPRESENT_ERROR) {
			unit->beer_state = 1;
		    } else {
			unit->beer_state = 2;
		    }
		}

		if (unit->chiller_address) {
		    rc = device_in(unit->chiller_address, &temp);
		    if (rc == DEVPRESENT_YES) {
			if (unit->chiller_temperature != temp) {
			    unit->mqtt_flag |= MQTT_FLAG_DATA;
//			    pub_domoticz_temp(unit->chiller_idx, temp);
			}
			unit->chiller_temperature = temp;
			unit->chiller_state = 0;
		    } else if (rc == DEVPRESENT_ERROR) {
			unit->chiller_state = 1;
		    } else {
			unit->chiller_state = 2;
		    }
		}

		/*
		 * Unit door state, default is closed.
		 */
		if (unit->door_address) {
		    rc = device_in(unit->door_address, &temp);
		    if (rc == DEVPRESENT_YES) {
			if (temp) {
			    if (unit->door_state == 0) {
			    	syslog(LOG_NOTICE, "Unit `%s' door closed", unit->alias);
			    	unit->door_state = 1;
//				pub_domoticz_output(unit->door_idx, unit->door_state);
				unit->mqtt_flag |= MQTT_FLAG_DATA;
			    }
			} else {
			    if (unit->door_state) {
			    	syslog(LOG_NOTICE, "Unit `%s' door opened", unit->alias);
			    	unit->door_state = 0;
//				pub_domoticz_output(unit->door_idx, unit->door_state);
				unit->mqtt_flag |= MQTT_FLAG_DATA;
			    }
			    /*
			     * If unit is active and the door is open
			     */
			    if (unit->mode != UNITMODE_NONE) {
				unit->alarm_flag |= ALARM_FLAG_DOOR;
			    }
			}
		    } else {
			unit->door_state = 1;
		    }
		} else {
		    unit->door_state = 1;
		}

		/*
		 * Unit PSU state
		 */
		if (unit->psu_address) {
		    rc = device_in(unit->psu_address, &temp);
		    if (rc == DEVPRESENT_YES) {
			if (temp) {
			    if (unit->psu_state == 0) {
				syslog(LOG_NOTICE, "Unit `%s' PSU (12 volt) is on", unit->alias);
				unit->psu_state = 1;
//				pub_domoticz_output(unit->psu_idx, unit->psu_state);
				unit->mqtt_flag |= MQTT_FLAG_DATA;
			    }
			} else {
			    if (unit->psu_state) {
				syslog(LOG_NOTICE, "Unit `%s' PSU (12 volt) is off", unit->alias);
				unit->psu_state = 0;
//				pub_domoticz_output(unit->psu_idx, unit->psu_state);
				unit->mqtt_flag |= MQTT_FLAG_DATA;
			    }
			    unit->alarm_flag |= ALARM_FLAG_PSU;
			}
		    } else {
			unit->psu_state = 1;
		    }
		} else {
		    /*
		     * No state available, assume Ok.
		     */
		    unit->psu_state = 1;
		}

		/*
		 * Handle profile
		 */
		if ((unit->mode == UNITMODE_PROFILE) && (unit->profile_uuid)) {
		    /*
		     * unit->prof_started	- start time or 0 if not yet running.
		     * unit->prof_state		- PROFILE_OFF|PROFILE_PAUSE|PROFILE_RUN|PROFILE_DONE
		     * unit->prof_target	- Calculated target temperature.
		     * unit->prof_paused	- Internal pause counter.
		     * unit->prof_peak_abs	- Peak temperature of the beer.
		     * unit->prof_peak_rel	- Peak temperature between beer and fridge.
		     * unit->prof_primary_done	- time when primary fermentation was over the peak.
		     */

		    /*
		     * Safe defaults
		     */
		    unit->prof_target_lo = unit->profile_inittemp_lo;
		    unit->prof_target_hi = unit->profile_inittemp_hi;
		    unit->prof_fridge_mode = 0;

		    switch (unit->prof_state) {
			case PROFILE_OFF:
					unit->prof_percent = 0;
					break;
			case PROFILE_PAUSE:
					/*
					 * Keep current temperature, measure pause time. For
					 * temperature fall thru.
					 */
					unit->prof_paused++;
			case PROFILE_RUN:
					/*
					 * Calculate current profile step and desired temperature.
					 * When all steps are done, set state to PROFILE_DONE.
					 */
					previous_target_lo = unit->profile_inittemp_lo;
					previous_target_hi = unit->profile_inittemp_hi;
					previous_fridge_mode = unit->profile_fridge_mode;
					time_until_now = current_step = 0;
					run_seconds = (int)(now - unit->prof_started - unit->prof_paused);
					run_minutes = run_seconds / 60;
					run_hours = run_minutes / 60;
					if (debug)
					    fprintf(stdout, "run_HMS=%d,%d,%d ", run_hours, run_minutes, run_seconds);

					/*
					 * Primary fermentation tests
					 */
					if ((unit->beer_temperature / 1000.0) > unit->prof_peak_abs)
					    unit->prof_peak_abs = unit->beer_temperature / 1000.0;
					if (((unit->beer_temperature - unit->air_temperature) / 1000.0) > unit->prof_peak_rel)
					    unit->prof_peak_rel = (unit->beer_temperature - unit->air_temperature) / 1000.0;
					if (unit->prof_primary_done == 0) {
					    if (unit->cooler_address) {
						/*
						 * There is a cooler. If the difference between the beer and air temperature
						 * drops we assume the primary fermentation is done.
						 */
						if (((unit->beer_temperature - unit->air_temperature) / 1000.0) < (unit->prof_peak_rel - 0.5)) {
						    unit->prof_primary_done = time(NULL);
						    syslog(LOG_NOTICE, "Profile `%s' primary fermentation is ready (cooler mode)", unit->profile_name);
						    if (! unit->event_msg)
							unit->event_msg = xstrcpy((char *)"Primary peak");
						}
					    } else {
					    	/*
					    	 * This method works if the unit has no cooling or if the profile allowed the
					    	 * beer temperature to rise freely.
					    	 */
					    	if ((unit->beer_temperature / 1000.0) <  (unit->prof_peak_abs - 0.5)) {
						    unit->prof_primary_done = time(NULL);
						    syslog(LOG_NOTICE, "Profile `%s' primary fermentation is ready (free rise mode)", unit->profile_name);
						    if (! unit->event_msg)
							unit->event_msg = xstrcpy((char *)"Primary peak");
					    	}
					    }
					}

					/*
					 * See how long this profile will take
					 */
					tot_minutes = 0;
					for (step = unit->profile_steps; step; step = step->next) {
					    tot_minutes += ((step->steptime + step->resttime) * 60);
					}
					if ((tot_minutes == 0) && unit->profile_totalsteps) {
					    syslog(LOG_NOTICE, "Profile `%s' steps disappeared", unit->profile_name);
					    unit->prof_state = PROFILE_OFF;
					    break;
					}

					valid_step = FALSE;
					for (step = unit->profile_steps; step; step = step->next) {
					    /*
					     * step->steptime
					     * step->resttime
					     * step->target
					     */
					    current_step++;
					    if ((run_hours >= time_until_now) && (run_hours < (time_until_now + step->steptime + step->resttime))) {
						/*
						 * This is our current step
						 */
						valid_step = TRUE;
						if ((run_hours - time_until_now) < step->steptime) {
						    unit->prof_target_lo = previous_target_lo + (((run_minutes - (time_until_now * 60.0)) / (step->steptime * 60.0)) * (step->target_lo - previous_target_lo));
						    unit->prof_target_hi = previous_target_hi + (((run_minutes - (time_until_now * 60.0)) / (step->steptime * 60.0)) * (step->target_hi - previous_target_hi));
						    if (step->fridge_mode > previous_fridge_mode) {
						    	unit->prof_fridge_mode = (((run_minutes - (time_until_now * 60)) * 100) / (step->steptime * 60));
						    } else if (step->fridge_mode < previous_fridge_mode) {
							unit->prof_fridge_mode = 100 - (((run_minutes - (time_until_now * 60)) * 100) / (step->steptime * 60));
						    } else {
							unit->prof_fridge_mode = step->fridge_mode;
						    }
						    if (debug)
							fprintf(stdout, "prof_fridge_mode=%d run_minutes=%d steptime=%d time_until_now=%d\n",
									unit->prof_fridge_mode, run_minutes, step->steptime, time_until_now);
						} else {
						    unit->prof_target_lo = step->target_lo;
						    unit->prof_target_hi = step->target_hi;
						    unit->prof_fridge_mode = step->fridge_mode;
						}
						break;
					    }
					    time_until_now += step->steptime + step->resttime;
					    previous_target_lo = step->target_lo;
					    previous_target_hi = step->target_hi;
					    previous_fridge_mode = step->fridge_mode;
					}

					if (valid_step == TRUE) {
					    unit->prof_percent = (100 * run_minutes) / tot_minutes;
					    if (((minutes == 10) || (minutes == 40)) && (seconds == 1)) {
					    	syslog(LOG_NOTICE, "Profile `%s' running %dd %02d:%02d in step %d, %d%% done, fridge/beer %d%% %.3f..%.3f degrees", 
							unit->profile_name, run_hours / 24, run_hours % 24, run_minutes % 60, current_step, 
							unit->prof_percent, unit->prof_fridge_mode, unit->prof_target_lo, unit->prof_target_hi);
						unit->mqtt_flag |= MQTT_FLAG_DATA;
					    }
					} else {
					    /*
					     * No more steps to do
					     */
					    unit->prof_state = PROFILE_DONE;
					    unit->prof_percent = 100;
					    syslog(LOG_NOTICE, "Profile `%s' is done", unit->profile_name);
					    unit->mqtt_flag |= MQTT_FLAG_DATA;
					    if (! unit->event_msg)
						unit->event_msg = xstrcpy((char *)"Profile finished");
					}
					break;

			case PROFILE_DONE:
					/*
					 * Keep this state, set target temperature to the last step.
					 */
					previous_target_lo = unit->profile_inittemp_lo;
					previous_target_hi = unit->profile_inittemp_hi;
					previous_fridge_mode = unit->profile_fridge_mode;
					for (step = unit->profile_steps; step; step = step->next) {
					    if ((step->steptime + step->resttime) == 0)
						break;
					    previous_target_lo = step->target_lo;
					    previous_target_hi = step->target_hi;
					    previous_fridge_mode = step->fridge_mode;
					}
					unit->prof_target_lo = previous_target_lo;
					unit->prof_target_hi = previous_target_hi;
					unit->prof_fridge_mode = previous_fridge_mode;
					unit->prof_percent = 100;
					break;
		    } /* switch */
		} else {
		    /*
		     * Set some sane values
		     */
		    unit->prof_target_lo = 19.8;
		    unit->prof_target_hi = 20.2;
		    unit->prof_fridge_mode = 0;
		}

		/*
		 * Manual switching
		 */
		if (unit->mode == UNITMODE_NONE) {
		    device_out(unit->heater_address, unit->heater_state);
		    device_out(unit->cooler_address, unit->cooler_state);
		    device_out(unit->fan_address, unit->fan_state);
		}

		/*
		 * Usage counters
		 */
		if (unit->heater_address && unit->heater_state)
		    unit->heater_usage++;
		if (unit->cooler_address && unit->cooler_state)
		    unit->cooler_usage++;
		if (unit->fan_address && unit->fan_state)
		    unit->fan_usage++;
		if (unit->light_address && unit->light_state)
		    unit->light_usage++;

		/*
		 * Interior lights
		 */
		if (unit->light_address) {
		    if (unit->light_timer) {
			unit->light_timer--;
		    }
		    if (unit->door_state && !unit->light_timer && unit->light_state) {
			if (unit->light_wait > 0) {
			    unit->light_wait--;
			} else {
			    unit->light_state = 0;
			    syslog(LOG_NOTICE, "Unit `%s' lights On => Off", unit->alias);
			    unit->mqtt_flag |= MQTT_FLAG_DATA;
			}
		    }
		    if ((!unit->door_state || unit->light_timer) && !unit->light_state) {
			unit->light_wait = unit->light_delay;   /* No delay to turn lights on   */
			unit->light_state = 1;
			unit->mqtt_flag |= MQTT_FLAG_DATA;
			syslog(LOG_NOTICE, "Unit `%s' lights Off => On", unit->alias);
		    }
		    device_out(unit->light_address, unit->light_state);
		}

	    	/*
	    	 * Temperature control in this unit
	    	 */
		if ((unit->mode == UNITMODE_FRIDGE) || (unit->mode == UNITMODE_BEER) || (unit->mode == UNITMODE_PROFILE)) {

		    /*
		     * Set both PID's to their input values.
		     */
		    unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_NONE;
		    if (unit->mode == UNITMODE_FRIDGE) {
			unit->PID_cool->SetP = unit->fridge_set_hi;
			unit->PID_heat->SetP = unit->fridge_set_lo;
			unit->PID_cool->Input = unit->PID_heat->Input = unit->air_temperature / 1000.0;
			unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_BOO;
		    } else if (unit->mode == UNITMODE_BEER) {
			unit->PID_cool->SetP = unit->beer_set_hi;
			unit->PID_heat->SetP = unit->beer_set_lo;
			unit->PID_cool->Input = unit->PID_heat->Input = unit->beer_temperature / 1000.0;
			unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_AUTO;
		    } else if (unit->mode == UNITMODE_PROFILE) {
			double  usetemp;
			unit->PID_cool->SetP = unit->prof_target_hi;
			unit->PID_heat->SetP = unit->prof_target_lo;
			/*
			 * Get percentage to use from each thermometer. unit->prof_fridge_mode = 0..100
			 */
			usetemp = ((unit->prof_fridge_mode * (unit->air_temperature / 1000.0)) +
			          ((100 - unit->prof_fridge_mode) * (unit->beer_temperature / 1000.0))) / 100.0;
			unit->PID_cool->Input = unit->PID_heat->Input = usetemp;
			unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_AUTO;
		    }

		    /*
		     * PID controller compute, simulate 100 mSec loops.
		     */
		    for (int i = 0; i < 10; i++) {
		    	UpdatePID(unit->PID_heat);
		    	UpdatePID(unit->PID_cool);
		    }

		    /*
		     * Logging
		     */
		    if (unit->heater_address) {
			/*
			 * Prevent extreme heating
			 */
			if ((unit->mode == UNITMODE_BEER) && ((unit->air_temperature / 1000.0) > (unit->PID_heat->Input + 8.0))) {
			    unit->PID_heat->OutP = 0.0;
			}
			if (seconds == 60) {
			    syslog(LOG_NOTICE, "Heat: sp=%.3f Input=%.3f iState=%.3f Err=%.3f Out=%.1f",
				unit->PID_heat->SetP, unit->PID_heat->Input, unit->PID_heat->iState, unit->PID_heat->Err, unit->PID_heat->OutP);
			}
		    } else {
			unit->PID_heat->OutP = 0.0;
		    }
		    if (unit->cooler_address) {
			/*
			 * Prevent extreme cooling
			 */
			if ((unit->mode == UNITMODE_BEER) && ((unit->air_temperature / 1000.0) < (unit->PID_cool->Input - 8.0))) {
			    unit->PID_cool->OutP = 0.0;
			}
			/*
			 * Prevent cooling if we use a chiller and the chiller temperature is not low enough.
			 */
			if (unit->chiller_address && (unit->chiller_state == 0)) {
			    if ((unit->chiller_temperature / 1000.0) > ((unit->air_temperature / 1000.0) - 1)) {
			    	unit->PID_cool->OutP = 0.0;
				unit->alarm_flag |= ALARM_FLAG_CHILLER;
				if (seconds == 60) {
				    syslog(LOG_NOTICE, "Cool: Air=%.2f Chiller=%.2f alarm", unit->air_temperature / 1000.0, unit->chiller_temperature / 1000.0);
				}
			    }
			}
		    	if (seconds == 60) {
			    syslog(LOG_NOTICE, "Cool: sp=%.3f Input=%.3f iState=%.3f Err=%.3f Out=%.1f",
				unit->PID_cool->SetP, unit->PID_cool->Input, unit->PID_cool->iState, unit->PID_cool->Err, unit->PID_cool->OutP);
		    	}
		    } else {
			unit->PID_cool->OutP = 0.0;
		    }

		    /*
		     * Deadlock, kill lowest value.
		     */
		    if (unit->PID_cool->OutP && unit->PID_heat->OutP) {
			if (unit->PID_cool->OutP > unit->PID_heat->OutP)
			    unit->PID_heat->OutP = 0.0;
			else
			    unit->PID_cool->OutP = 0.0;
		    }

		    if (unit->heater_address && ! unit->cooler_state) {
			if (unit->PID_heat->OutP >= 50) {
			    if (unit->heater_wait < unit->heater_delay) {
				unit->heater_wait++;
			    } else {
				int	power = round(unit->PID_heat->OutP);
				if (unit->heater_state != power) {
				    syslog(LOG_NOTICE, "Unit `%s' heater %d%% => %d%%", unit->alias, unit->heater_state, power);
				    unit->heater_state = power;
//				    pub_domoticz_output(unit->heater_idx, unit->heater_state);
				    if (unit->heater_address) {
					unit->mqtt_flag |= MQTT_FLAG_DATA;
				    }
				}
			    }
			} else {
			    if (unit->heater_wait > 0) {
				unit->heater_wait--;
			    } else {
				if (unit->heater_state) {
				    syslog(LOG_NOTICE, "Unit `%s' heater On => Off", unit->alias);
				    unit->heater_state = 0;
//				    pub_domoticz_output(unit->heater_idx, unit->heater_state);
				    if (unit->heater_address) {
					unit->mqtt_flag |= MQTT_FLAG_DATA;
				    }
				}
			    }
			}
			if (unit->door_state) {
			    device_out(unit->heater_address, unit->heater_state);
			} else {
			    device_out(unit->heater_address, 0);
			}
		    }

		    if (unit->cooler_address && ! unit->heater_state) {
			if (unit->PID_cool->OutP >= 50) {
			    if (unit->cooler_wait < unit->cooler_delay) {
				unit->cooler_wait++;
			    } else {
				int	power = round(unit->PID_cool->OutP); 
				if (unit->cooler_state != power) {
				    syslog(LOG_NOTICE, "Unit `%s' cooler %d%% => %d%%", unit->alias, unit->cooler_state, power);
				    unit->cooler_state = power;
//				    pub_domoticz_output(unit->cooler_idx, unit->cooler_state);
				    if (unit->cooler_address) {
					unit->mqtt_flag |= MQTT_FLAG_DATA;
				    }
				}
			    }
			} else {
			    if (unit->cooler_wait > 0) {
				unit->cooler_wait--;
			    } else {
				if (unit->cooler_state) {
				    syslog(LOG_NOTICE, "Unit `%s' cooler On => Off", unit->alias);
				    unit->cooler_state = 0;
//				    pub_domoticz_output(unit->cooler_idx, unit->cooler_state);
				    if (unit->cooler_address) {
					unit->mqtt_flag |= MQTT_FLAG_DATA;
				    }
				}
			    }
			}
			if (unit->door_state) {
			    device_out(unit->cooler_address, unit->cooler_state);
			} else {
			    device_out(unit->cooler_address, 0);
			}
		    }

		    /*
		     * If there is a fan, and the unit door is closed, and the unit should be doing
		     * something, then turn on the global fan.
		     * But if there is a chiller, do not turn it on if cooling.
		     */
		    if (unit->fan_address) {
			if ((unit->door_state) && (unit->cooler_state == 0)) {
			    if (unit->fan_wait < unit->fan_delay) {
			    	unit->fan_wait++;
			    } else {
			    	if (! unit->fan_state) {
				    syslog(LOG_NOTICE, "Unit `%s' Fan Off => On", unit->alias);
				    unit->fan_state = 100;
//				    pub_domoticz_output(unit->fan_idx, unit->fan_state);
				    if (unit->fan_address) {
					unit->mqtt_flag |= MQTT_FLAG_DATA;
				    }
			    	}
			    }
			} else {
			    if (unit->fan_wait > 0) {
				unit->fan_wait--;
			    } else {
			    	if (unit->fan_state) {
				    syslog(LOG_NOTICE, "Unit `%s' Fan On => Off", unit->alias);
			    	    unit->fan_state = 0;
//				    pub_domoticz_output(unit->fan_idx, unit->fan_state);
				    if (unit->fan_address) {
					unit->mqtt_flag |= MQTT_FLAG_DATA;
				    }
				}
			    }
			}
			device_out(unit->fan_address, unit->fan_state);
		    }

		} else {
		    unit->PID_cool->Mode = unit->PID_heat->Mode = PID_MODE_NONE;
		} /* fridge beer or profile mode */

		/*
		 * Now everything is set and done, update the LCD display
		 */
		LCDair = unit->air_temperature / 1000.0;
		LCDbeer = unit->beer_temperature / 1000.0;
		LCDstatC = LCDstatH = ' ';
		if (unit->heater_address) {
		    if (unit->heater_state) 
			LCDstatH = '\6';
		    else
			LCDstatH = '\5';
		}
		if (unit->cooler_address) {
		    if (unit->cooler_state) 
			LCDstatC = '\4';
		    else
			LCDstatC = '\3';
		}
		LCDspH = LCDspL = 0.0;
		if (unit->mode == UNITMODE_BEER) {
		    LCDspH = unit->beer_set_hi;
		    LCDspL = unit->beer_set_lo;
		} else if (unit->mode == UNITMODE_FRIDGE) {
		    LCDspH = unit->fridge_set_hi;
		    LCDspL = unit->fridge_set_lo;
		} else if (unit->mode == UNITMODE_PROFILE) {
		    if (unit->prof_state != PROFILE_OFF) {
			LCDspL = unit->prof_target_lo;
			LCDspH = unit->prof_target_hi;
		    }
		}
//		if ((seconds == 60) && ((unit->mode == UNITMODE_FRIDGE) || (unit->mode == UNITMODE_BEER) || (unit->mode == UNITMODE_PROFILE))) {
//		    unit->mqtt_flag |= MQTT_FLAG_DATA;
//		}
		if (seconds == 60) {
		    unit->mqtt_flag |= MQTT_FLAG_DATA;
		}
#ifdef HAVE_WIRINGPI_H
		piLock(LOCK_LCD);
#endif
		/*
		 * Write 4 rows to the LCD to display the unit state
		 */
		lcd_buf_write(row++, "Unit %d: %s          ", LCDunit, UNITMODE[unit->mode]);
		lcd_buf_write(row++, "%s                   ", unit->product_name);
		lcd_buf_write(row++, "%c%5.1f\2 A%6.2f\1    ", LCDstatC, LCDspH, LCDair);
		lcd_buf_write(row++, "%c%5.1f\2 B%6.2f\1    ", LCDstatH, LCDspL, LCDbeer);
#ifdef HAVE_WIRINGPI_H
		piUnlock(LOCK_LCD);
#endif

		/*
		 * Publish MQTT messages set in flag
		 */
		if (unit->mqtt_flag) {
		    if (unit->mqtt_flag & MQTT_FLAG_BIRTH) {
			publishDBirth(unit);
			unit->mqtt_flag &= ~MQTT_FLAG_BIRTH;
		    } else {
			publishDData(unit);
			unit->mqtt_flag &= ~MQTT_FLAG_DATA;
		    }
		    if (unit->mqtt_flag & MQTT_FLAG_DEATH) {
			publishDDeath(unit);
			unit->mqtt_flag &= ~MQTT_FLAG_DEATH;
		    }
		}

		/*
		 * Handle changed alarms
		 */
		if (unit->alarm_flag != unit->alarm_last) {
		    syslog(LOG_NOTICE, "Unit `%s' Alarm %d => %d", unit->alias, unit->alarm_last, unit->alarm_flag);
		    unit->alarm_last = unit->alarm_flag;
		}
	    } /* for units */

#ifdef HAVE_WIRINGPI_H
	    piLock(LOCK_MENU);
#endif
	    if (setupmenu == MENU_NONE) {
#ifdef HAVE_WIRINGPI_H
    		piLock(LOCK_LCD);
#endif
	    	lcd_buf_show();
#ifdef HAVE_WIRINGPI_H
		piUnlock(LOCK_LCD);
#endif
	    }
#ifdef HAVE_WIRINGPI_H
	    piUnlock(LOCK_MENU);
#endif

	    if (seconds == 60) {
		seconds = 0;

		/*
		 * Publish data every minute if unit is active.
		 */
		for (unit = Config.units; unit; unit = unit->next) {
		    if (unit->mode != UNITMODE_OFF) {
			publishDLog(unit);
			if (unit->event_msg)
			    free(unit->event_msg);
			unit->event_msg = NULL;
		    }
		}

		minutes++;
		if (minutes == 60) {
		    minutes = 0;
		    /*
		     * Log usage counters every hour
		     */
		    for (unit = Config.units; unit; unit = unit->next) {
		    	syslog(LOG_NOTICE, "Unit `%s' usage heater=%d cooler=%d fan=%d", unit->alias, unit->heater_usage, unit->cooler_usage, unit->fan_usage);
		    }
		}

		/*
		 * Save the configuration each half hour.
		 */
		if ((minutes == 15) || (minutes == 45))
		    wrconfig();
	    }
	}

	slcdDummy(slcdHandle);
	key = keycheck();
	if (key != KEY_NONE)
	    panel_key_events(key);

	usleep(100000);

    } while (run);

    /*
     * Stop units processing in a neat way
     */
    for (unit = Config.units; unit; unit = unit->next) {
	/*
	 * Turn everything off
	 */
	unit->heater_state = unit->cooler_state = unit->fan_state = unit->door_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);
	pub_domoticz_output(unit->heater_idx, unit->heater_state);
	device_out(unit->cooler_address, unit->cooler_state);
	pub_domoticz_output(unit->cooler_idx, unit->cooler_state);
	device_out(unit->fan_address, unit->fan_state);
	pub_domoticz_output(unit->fan_idx, unit->fan_state);
	device_out(unit->light_address, unit->light_state);
	if (unit->mode != UNITMODE_OFF) {
	    /*
	     * If unit is active, publish we are dying.
	     */
	    unit->mqtt_flag = MQTT_FLAG_DATA;
	    publishDData(unit);
	    publishDDeath(unit);
	    unit->event_msg = xstrcpy((char *)"Shutdown");
	    publishDLog(unit);
	    free(unit->event_msg);
	    unit->event_msg = NULL;
	}
	syslog(LOG_NOTICE, "Unit `%s' stopped in mode %s", unit->alias, UNITMODE[unit->mode]);
    }

    usleep(100000);
    mqtt_disconnect();

    syslog(LOG_NOTICE, "Out of loop");

    /*
     * Give threads time to cleanup
     */
    usleep(1500000);

    stopLCD();
    if (sock != -1) {
	if (shutdown(sock, SHUT_RDWR)) {
	    syslog(LOG_NOTICE, "Can't shutdown socket: %s", strerror(errno));
	}
	sock = -1;
    }
    lcd_buf_reset();
    wrconfig();
    ulockprog((char *)"thermferm");

    syslog(LOG_NOTICE, "Server process ended");
    return 0;
}

mercurial