thermferm/thermferm.c

Sun, 28 Apr 2024 15:50:42 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Sun, 28 Apr 2024 15:50:42 +0200
changeset 713
ea24b4ce02b1
parent 693
3518c07737d8
permissions
-rw-r--r--

It was impossible to remove devices from fermenters. Added beer_address2 device to the simulator.

/*****************************************************************************
 * Copyright (C) 2014-2024
 *   
 * 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 "server.h"
#include "thermferm.h"
#include "devices.h"
#include "delay.h"
#include "simulator.h"
#include "lcd-pcf8574.h"
#include "lcd-buffer.h"
#include "slcd.h"
#include "panel.h"
#include "one-wire.h"
#include "futil.h"
#include "xutil.h"
#include "pid.h"
#include "mqtt.h"
#include "statetbl.h"
#include "websocket.h"


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

extern int		debug;
extern int		foreground;
extern sys_config	Config;
extern int		lcdHandle;
extern int		slcdHandle;
extern int		my_devices_state;
extern int		my_devices_shutdown;
extern int		my_panel_state;
extern int		my_panel_shutdown;
extern int		my_server_state;
extern int		my_server_shutdown;
extern int		my_ws_state;
extern int		my_ws_shutdown;
extern int		my_simulator_state;
#ifdef USE_SIMULATOR
extern int		my_simulator_shutdown;
#endif
extern int		my_one_wire_state;
extern int		my_one_wire_shutdown;
int			setupmenu = MENU_NONE;
units_list		*current_unit = NULL;		/* In panel editor this points to the current unit. */
float			temp_temp = 20.0;

pthread_t		my_one_wire_thread;
pthread_t		my_devices_thread;
pthread_t		my_panel_thread;
pthread_t		my_server_thread;
pthread_t		my_ws_thread;
#ifdef USE_SIMULATOR
pthread_t		my_simulator_thread;
#endif

pthread_mutex_t		mutexes[5];

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              Extra debug logging\n");
    fprintf(stdout, "  -f --foreground         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];

    pthread_mutex_lock(&mutexes[LOCK_LCD]);
    pthread_mutex_lock(&mutexes[LOCK_MENU]);
#ifdef HAVE_WIRINGPI_H
    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, "Restart app.");
#endif
				slcdPuts(slcdHandle, "Restart app.");
				break;
    }

    pthread_mutex_unlock(&mutexes[LOCK_MENU]);
    pthread_mutex_unlock(&mutexes[LOCK_LCD]);
}



void stopLCD(void)
{
    pthread_mutex_lock(&mutexes[LOCK_LCD]);
#ifdef HAVE_WIRINGPI_H
    lcdClear(lcdHandle);
#endif
    slcdClear(slcdHandle);
    setBacklight(0);
    pthread_mutex_unlock(&mutexes[LOCK_LCD]);
}



/*
 * 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) {
			    /*
			     * Restart. The server process will restart which is handled
			     * in the main thread loop.
			     */
			    my_reboot = my_shutdown = TRUE;
			    syslog(LOG_NOTICE, "Application restart from panel");
			    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'},
	    {"foreground", 0, 0, 'f'},
	    {"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 'f':	foreground = 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 (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
    syslog(LOG_NOTICE, "Build with wiringPi");
    if (wiringPiSetup () ) {
	syslog(LOG_NOTICE, "Error wiringPiSetup(): halted");
	return 1;
    }
#endif

#ifdef USE_SIMULATOR
    syslog(LOG_NOTICE, "Build with simulator");
#endif

    if (foreground) {
	/*
	 * 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);
    return rc;
}


void do_unit(units_list *unit, int LCDunit, int seconds, int minutes)
{
    time_t		now;
    prof_step		*step;
    int			rc, temp;
    int			run_seconds, run_minutes, run_hours, tot_minutes;
    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;

//    unit->mqtt_flag &= ~MQTT_FLAG_DATA;
    unit->alarm_flag = 0;

    if (unit->air_address) {
	unit->air_state = rc = device_in(unit->air_address, &temp);
	if (rc == DEVPRESENT_YES) {
	    if (unit->air_temperature != temp) {
		unit->mqtt_flag |= MQTT_FLAG_DATA;
	    }
	    unit->air_temperature = temp;
	}
    } else {
	unit->air_state = DEVPRESENT_NO;
    }

    if (unit->beer_address) {
	rc = device_in(unit->beer_address, &temp);
	if ((rc == DEVPRESENT_NO) && unit->beer_address2) {
	    /* Read alternative sensor */
	    rc = device_in(unit->beer_address2, &temp);
	}
	unit->beer_state = rc;
	if (rc == DEVPRESENT_YES) {
	    if (unit->beer_temperature != temp) {
		unit->mqtt_flag |= MQTT_FLAG_DATA;
	    }
	    unit->beer_temperature = temp;
	}
    } else {
	unit->beer_state = DEVPRESENT_NO;
    }

    if (unit->chiller_address) {
	unit->chiller_state = rc = device_in(unit->chiller_address, &temp);
	if (rc == DEVPRESENT_YES) {
	    if (unit->chiller_temperature != temp) {
		unit->mqtt_flag |= MQTT_FLAG_DATA;
	    }
	    unit->chiller_temperature = temp;
	}
    } else {
	unit->chiller_state = DEVPRESENT_NO;
    }

    /*
     * 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;
		    unit->mqtt_flag |= MQTT_FLAG_DATA;
		}
	    } else {
		if (unit->door_state) {
		    syslog(LOG_NOTICE, "Unit `%s' door opened", unit->alias);
		    unit->door_state = 0;
		    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;
		    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;
		    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;
				now = time(NULL);
				run_seconds = (int)(now - unit->prof_started - unit->prof_paused);
				run_minutes = run_seconds / 60;
				run_hours = run_minutes / 60;
				if (debug)
				    syslog(LOG_NOTICE, "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)
						syslog(LOG_NOTICE, "prof_fridge_mode=%d run_minutes=%d steptime=%d time_until_now=%d",
								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 by running 10 times.
    	 */
    	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;
		    	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;
		    	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;
			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;
			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;
			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;
			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->mqtt_flag |= MQTT_FLAG_DATA;
    }
    pthread_mutex_lock(&mutexes[LOCK_LCD]);
    /*
     * 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);
    pthread_mutex_unlock(&mutexes[LOCK_LCD]);

    /*
     * 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;
    }
}


SM_DECL(thermferm,(char *)"thermferm")
SM_STATES
    CheckRun,
    WaitMinute,
    DateTime,
    RoomTHB,
    Units,
    ShowLCD,
    Minute,
    Keys
SM_NAMES
    (char *)"CheckRun",
    (char *)"WaitMinute",
    (char *)"DateTime",
    (char *)"RoomTHB",
    (char *)"Units",
    (char *)"ShowLCD",
    (char *)"Minute",
    (char *)"Keys"
SM_EDECL

    time_t	now, last = (time_t)0, ndata = (time_t)0;
    int		key, LCDunit, rc, temp, seconds = 0, minutes = 0;
    struct tm	*tm;
    units_list	*unit;

SM_START(CheckRun)

SM_STATE(CheckRun)

    if (my_shutdown) {
	SM_SUCCESS;
    }

    /*
     * 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 (;;) {
	    mDelay(100);
	    if (! run_pause)
		break;
	}
	syslog(LOG_NOTICE, "run_pause: leaving hold state");
	run_hold = FALSE;
        /*
         * In case the LCD buffers were cleared, setup the first page.
         */
        pthread_mutex_lock(&mutexes[LOCK_LCD]);
        lcd_buf_write(1, (char *)"   ThermFerm    ");
        lcd_buf_write(2, (char *)"Version %s      ", VERSION);
        pthread_mutex_unlock(&mutexes[LOCK_LCD]);
    }
    SM_PROCEED(WaitMinute);

SM_STATE(WaitMinute)

    if (my_shutdown) {
        SM_SUCCESS;
    }

    now = time(NULL);
    if (now != last) {
	/*
	 * Each second
	 */
	last = now;
	seconds++;
	SM_PROCEED(DateTime);
    } else {
	SM_PROCEED(Keys);
    }

SM_STATE(DateTime)

    row = 3;
    tm = localtime(&now);
    pthread_mutex_lock(&mutexes[LOCK_LCD]);
    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);
    pthread_mutex_unlock(&mutexes[LOCK_LCD]);
    SM_PROCEED(RoomTHB);

SM_STATE(RoomTHB)

    pthread_mutex_lock(&mutexes[LOCK_LCD]);
    lcd_buf_write(row, "Room temp N/A       ");
    pthread_mutex_unlock(&mutexes[LOCK_LCD]);
    int updateHT = 0;
    if (Config.temp_uuid) {
	Config.temp_state = rc = device_in(Config.temp_uuid, &temp);
	if (rc == DEVPRESENT_YES) {
	    if (Config.temp_value != temp)
		updateHT = 1;
	    Config.temp_value = temp;
	    pthread_mutex_lock(&mutexes[LOCK_LCD]);
	    lcd_buf_write(row, "Room temp %.1f%c    ", Config.temp_value / 1000.0, 0x01);
	    pthread_mutex_unlock(&mutexes[LOCK_LCD]);
	}
	mDelay(10);
    }
    row++;

    pthread_mutex_lock(&mutexes[LOCK_LCD]);
    lcd_buf_write(row, " Humidity N/A       ");
    pthread_mutex_unlock(&mutexes[LOCK_LCD]);
    if (Config.hum_uuid) {
	Config.hum_state = rc = device_in(Config.hum_uuid, &temp);
	if (rc == DEVPRESENT_YES) {
	    if (Config.hum_value != temp)
		updateHT = 1;
	    Config.hum_value = temp;
	    pthread_mutex_lock(&mutexes[LOCK_LCD]);
	    lcd_buf_write(row, " Humidity %.1f%%     ", Config.hum_value / 1000.0);
	    pthread_mutex_unlock(&mutexes[LOCK_LCD]);
	}
	mDelay(10);
    }
    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;
    }
    SM_PROCEED(Units);

SM_STATE(Units)

    LCDunit = 0;
    for (unit = Config.units; unit; unit = unit->next) {
	LCDunit++;
	do_unit(unit, LCDunit, seconds, minutes);
    }
    SM_PROCEED(ShowLCD);

SM_STATE(ShowLCD)

    pthread_mutex_lock(&mutexes[LOCK_MENU]);
    if (setupmenu == MENU_NONE) {
	pthread_mutex_lock(&mutexes[LOCK_LCD]);
	lcd_buf_show();
	pthread_mutex_unlock(&mutexes[LOCK_LCD]);
    }
    pthread_mutex_unlock(&mutexes[LOCK_MENU]);
    SM_PROCEED(Minute);

SM_STATE(Minute)

    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();
    }
    SM_PROCEED(Keys);

SM_STATE(Keys)

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

    mDelay(25);
    ws_check();
    mDelay(25);
    SM_PROCEED(CheckRun);

SM_END
SM_RETURN


int server(void)
{
    units_list	*unit;
    int		rc;
    long	t = 0;

    syslog(LOG_NOTICE, "Server process started");

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

    my_shutdown = my_reboot = FALSE;
    my_devices_shutdown = my_panel_shutdown = my_server_shutdown = my_ws_shutdown = my_one_wire_shutdown = 0;
    my_devices_state = my_panel_state = my_server_state = my_ws_state = my_one_wire_state = 0;
    my_simulator_state = 0;
#ifdef USE_SIMULATOR
    my_simulator_shutdown = 0;
#endif
    if (lockprog((char *)"thermferm")) {
	syslog(LOG_NOTICE, "Can't lock");
	return 1;
    }
    mqtt_connect();

    /*
     * Start websockets first.
     */
    rc = pthread_create(&my_ws_thread, NULL, my_ws_loop, (void *)t );
    if (rc) {
        fprintf(stderr, "my_ws_loop thread didn't start rc=%d\n", rc);
        syslog(LOG_NOTICE, "my_ws_loop thread didn't start rc=%d", rc);
    } else {
        t++;
    }

    /*
     * Next scan the one-wire bus
     */
    rc = pthread_create(&my_one_wire_thread, NULL, my_one_wire_loop, (void *)t );
    if (rc) {
	fprintf(stderr, "my_one_wire_loop thread didn't start rc=%d\n", rc);
	syslog(LOG_NOTICE, "my_one_wire_loop thread didn't start rc=%d", rc);
    } else {
	t++;
	mDelay(2500);	/* Wait a while to detect the devices */
    }

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

    rc = pthread_create(&my_devices_thread, NULL, my_devices_loop, (void *)t );
    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);
    } else {
	t++;
    }

    rc = pthread_create(&my_server_thread, NULL, my_server_loop, (void *)t );
    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);
    } else {
	t++;
    }

    rc = pthread_create(&my_panel_thread, NULL, my_panel_loop, (void *)t );
    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);
    } else {
	t++;
    }

#ifdef USE_SIMULATOR
    rc = pthread_create(&my_simulator_thread, NULL, my_simulator_loop, (void *)t );
    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);
    } else {
	t++;
    }
#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;
	}
    }

    pthread_mutex_lock(&mutexes[LOCK_LCD]);
    lcd_buf_write(1, (char *)"   ThermFerm    ");
    lcd_buf_write(2, (char *)"Version %s      ", VERSION);
    //                                0.9.17a2
    pthread_mutex_unlock(&mutexes[LOCK_LCD]);

    /*
     * Run state table
     */
    thermferm();

    /*
     * 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]);
    }
    syslog(LOG_NOTICE, "Out of loop, stopping threads..");

    /*
     * Stop threads
     */
#ifdef USE_SIMULATOR
    my_simulator_shutdown = 1;
    while (my_simulator_state) { mDelay(50); };
#endif
    my_panel_shutdown = 1;
    while (my_panel_state) { mDelay(50); };

    /*
     * Cancel command and shutdown via variable, one of them
     * will stop this thread. Includes a failsafe.
     */
    my_server_shutdown = 1;
    rc = pthread_cancel(my_server_thread);
    rc = 0;
    while (my_server_state) {
	mDelay(50);
	if (rc++ > 20) {
	    syslog(LOG_NOTICE, "Cannot terminate my_server_loop()");
	    break;
	}
    }

    my_devices_shutdown = 1;
    while (my_devices_state) { mDelay(50); };
    my_one_wire_shutdown = 1;
    while (my_one_wire_state) { mDelay(50); };
    my_ws_shutdown = 1;
    while (my_ws_state) { mDelay(50); };
    mqtt_disconnect();

    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