thermferm/rdconfig.c

Tue, 31 Jul 2018 16:42:11 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Tue, 31 Jul 2018 16:42:11 +0200
changeset 554
ab9f22ab57b5
parent 553
4091d4fe217f
child 561
fcfc3dbe85fa
permissions
-rw-r--r--

Versie 0.8.5. Implementatie product code en product naam. Implementatie DLOG MQTT berichten. Verminderde server rotocol debug berichten.

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

#include "rdconfig.h"
#include "thermferm.h"
#include "pid.h"
#include "futil.h"
#include "xutil.h"

int		debug = FALSE;
sys_config	Config;			/* System configuration		*/

#define MY_ENCODING "utf-8"

const char	TEMPSTATE[3][8] = { "OK", "MISSING", "ERROR" };
const char      UNITMODE[5][8]	= { "OFF", "NONE", "FRIDGE", "BEER", "PROFILE" };
const char	PROFSTATE[5][6]	= { "OFF", "PAUSE", "RUN", "DONE", "ABORT" };
const char	DEVTYPE[8][6] = { "NA", "W1", "GPIO", "RC433", "DHT", "I2C", "SPI", "SIM" };
const char	DEVPRESENT[4][6] = { "UNDEF", "NO", "YES", "ERROR" };
const char	DEVDIR[7][11] = { "UNDEF", "IN_BIN", "OUT_BIN", "IN_ANALOG", "OUT_ANALOG", "OUT_PWM", "INTERN" };
const char	PIDMODE[3][5] = { "NONE", "AUTO", "BOO" };


void killconfig(void)
{
    units_list		*tmp2;
    profiles_list	*tmp3;
    prof_step		*tmp4;
    devices_list	*device;
#ifdef USE_SIMULATOR
    simulator_list	*simulator;
#endif

    if (Config.name)
	free(Config.name);
    Config.name = NULL;

    Config.my_port = 6554;
    Config.tempFormat = 'C';
    if (Config.temp_address)
	free(Config.temp_address);
    if (Config.hum_address)
	free(Config.hum_address);
    Config.temp_hum_idx = 0;
    Config.temp_address = Config.hum_address = NULL;
    Config.temp_value = 20000;
    Config.temp_state = Config.hum_state = 1;	// missing
    Config.hum_value = 50000;
    if (Config.mqtt_host)
	free(Config.mqtt_host);
    Config.mqtt_host = NULL;
    Config.mqtt_port = 1883;
    if (Config.mqtt_username)
	free(Config.mqtt_username);
    Config.mqtt_username = NULL;
    if (Config.mqtt_password)
	free(Config.mqtt_password);
    Config.mqtt_password = NULL;
    if (Config.uuid)
	free(Config.uuid);
    Config.uuid = NULL;
 
    for (tmp2 = Config.units; tmp2; tmp2 = tmp2->next) {
	if (tmp2->uuid)
	    free(tmp2->uuid);
	if (tmp2->product_uuid)
	    free(tmp2->product_uuid);
	if (tmp2->product_code)
	    free(tmp2->product_code);
	if (tmp2->product_name)
	    free(tmp2->product_name);
	if (tmp2->air_address)
	    free(tmp2->air_address);
	if (tmp2->beer_address)
	    free(tmp2->beer_address);
	if (tmp2->chiller_address)
	    free(tmp2->chiller_address);
	if (tmp2->heater_address)
	    free(tmp2->heater_address);
	if (tmp2->cooler_address)
	    free(tmp2->cooler_address);
	if (tmp2->fan_address)
	    free(tmp2->fan_address);
	if (tmp2->light_address)
	    free(tmp2->light_address);
	if (tmp2->door_address)
	    free(tmp2->door_address);
	if (tmp2->psu_address)
	    free(tmp2->psu_address);
	if (tmp2->profile)
	    free(tmp2->profile);
	free(tmp2);
    }
    Config.units = NULL;

    for (tmp3 = Config.profiles; tmp3; tmp3 = tmp3->next) {
	if (tmp3->uuid)
	    free(tmp3->uuid);
	if (tmp3->name)
	    free(tmp3->name);
	if (tmp3->steps) {
	    for (tmp4 = tmp3->steps; tmp4; tmp4 = tmp4->next) {
		free(tmp4);
	    }
	}
	free(tmp3);
    }
    Config.profiles = NULL;

    for (device = Config.devices; device; device = device->next) {
	if (device->uuid)
	    free(device->uuid);
	if (device->address)
	    free(device->address);
	if (device->description)
	    free(device->description);
	if (device->comment)
	    free(device->comment);
	free(device);
    }
    Config.devices = NULL;

#ifdef USE_SIMULATOR
    for (simulator = Config.simulators; simulator; simulator = simulator->next) {
	if (simulator->uuid)
	    free(simulator->uuid);
	if (simulator->name)
	    free(simulator->name);
	free(simulator);
    }
    Config.simulators = NULL;
#endif

    Config.lcd_cols = 16;
    Config.lcd_rows = 2;
}



int do_wrconfig(void);
int do_wrconfig(void)
{
    int			rc = 0;
    FILE		*fp;
    char		*mypath = NULL;
    xmlTextWriterPtr	writer;
    xmlBufferPtr	buf;
    units_list		*tmp3;
    profiles_list	*tmp4;
    prof_step		*tmp5;
    devices_list	*device;
#ifdef USE_SIMULATOR
    simulator_list	*simulator;
#endif

    /* 
     * Create a new XML buffer, to which the XML document will be written
     */
    if ((buf = xmlBufferCreate()) == NULL) {
	syslog(LOG_NOTICE, "wrconfig: error creating the xml buffer");
	return 1;
    }

    /* 
     * Create a new XmlWriter for memory, with no compression.
     */
    if ((writer = xmlNewTextWriterMemory(buf, 0)) == NULL) {
	syslog(LOG_NOTICE, "wrconfig: error creating the xml writer");
	return 1;
    }

    /*
     * Use indentation instead of one long line
     */
    if ((rc = xmlTextWriterSetIndent(writer, 2)) < 0) {
	syslog(LOG_NOTICE, "wrconfig: error setting Indent");
	return 1;
    }

    /* 
     * Start the document with the xml default for the version,
     * encoding ISO 8859-1 and the default for the standalone
     * declaration. 
     */
    if ((rc = xmlTextWriterStartDocument(writer, NULL, MY_ENCODING, NULL)) < 0) {
	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterStartDocument");
	return 1;
    }

    /* 
     * Start an element named "THERMFERM". Since thist is the first
     * element, this will be the root element of the document.
     */
    if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "THERMFERM")) < 0) {
	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterStartElement");
	return 1;
    }

    /* 
     * Add an attribute with name "VERSION" and value "1" to THERMFERM. 
     */
    if ((rc = xmlTextWriterWriteElement(writer, BAD_CAST "VERSION", BAD_CAST "1")) < 0) {
	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
	return 1;
    }
    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "NAME", "%s", Config.name)) < 0) {
	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
	return 1;
    }
    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "UUID", "%s", Config.uuid)) < 0) {
	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
	return 1;
    }
    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "LISTEN_PORT", "%d", Config.my_port)) < 0) {
	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
	return 1;
    }
    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "TEMPFORMAT", "%c", Config.tempFormat)) < 0) {
	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
	return 1;
    }
    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "TEMP_ADDRESS", "%s", Config.temp_address)) < 0) {
	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
	return 1;
    }
    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "TEMP_STATE", "%d", Config.temp_state)) < 0) {
	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
	return 1;                           
    }
    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "TEMP_VALUE", "%d", Config.temp_value)) < 0) {
	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
	return 1;
    }
    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HUM_ADDRESS", "%s", Config.hum_address)) < 0) {
	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
	return 1;
    }   
    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HUM_STATE", "%d", Config.hum_state)) < 0) {
	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
	return 1;    
    }   
    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HUM_VALUE", "%d", Config.hum_value)) < 0) {
	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
	return 1;
    }
    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "TEMP_HUM_IDX", "%d", Config.temp_hum_idx)) < 0) {
	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
	return 1;
    }
    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "NEXT_UNIT", "%d", Config.next_unit)) < 0) {
	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
	return 1;
    }
    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "MQTT_HOST", "%s", Config.mqtt_host)) < 0) {
	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
	return 1;
    }
    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "MQTT_PORT", "%d", Config.mqtt_port)) < 0) {
	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
	return 1;
    }
    if (Config.mqtt_username && Config.mqtt_password) {
    	if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "MQTT_USER", "%s", Config.mqtt_username)) < 0) {
	    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
	    return 1;
	}
    	if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "MQTT_PASS", "%s", Config.mqtt_password)) < 0) {
	    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
	    return 1;
	}
    }

    /* 
     * Start an element named "LCDS" as child of THERMFERM.
     */
    if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "LCDS")) < 0) {
	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterStartElement");
	return 1;
    }
    /*
     * Start one LCD. It is possible to connect 7 LCD displays on the i2c bus.
     * However this program doesn't use more then one yet.
     */
    if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "LCD")) < 0) {
	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterStartElement");
	return 1;
    }
    if ((rc = xmlTextWriterWriteElement(writer, BAD_CAST "VERSION", BAD_CAST "1")) < 0) {
	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
	return 1;
    }
    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "ADDRESS", "0x%x", Config.lcd_address)) < 0) {
	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
	return 1;
    }
    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "COLUMNS", "%d", Config.lcd_cols)) < 0) {
	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
	return 1;
    }
    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "ROWS", "%d", Config.lcd_rows)) < 0) {
	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
	return 1;
    }
    /* 
     * Close the element named LCD.
     */
    if ((rc = xmlTextWriterEndElement(writer)) < 0) {
	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterEndElement");
	return 1;
    }
    /*
     * Close the element LCDS.
     */
    if ((rc = xmlTextWriterEndElement(writer)) < 0) {
	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterEndElement");
	return 1;
    }

    /*
     * Fermenter units
     */
    if (Config.units) {
	if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "FERMENTERS")) < 0) {
	    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterStartElement");
	    return 1;
	}
	for (tmp3 = Config.units; tmp3; tmp3 = tmp3->next) {
	    /*
	     * Only configuration items are written, measured values and states
	     * are written to a state file.
	     */
	    if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "UNIT")) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterStartElement");
		return 1;
	    }
	    if ((rc = xmlTextWriterWriteElement(writer, BAD_CAST "VERSION", BAD_CAST "1")) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
		return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "UUID", "%s", tmp3->uuid)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PRODUCT_UUID", "%s", tmp3->product_uuid)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PRODUCT_CODE", "%s", tmp3->product_code)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PRODUCT_NAME", "%s", tmp3->product_name)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "ALIAS", "%s", tmp3->alias)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "VOLUME", "%.1f", tmp3->volume)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		return 1;
	    }
	    if (tmp3->air_address) {
	    	if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "AIR_ADDRESS", "%s", tmp3->air_address)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
	    	}
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "AIR_STATE", "%d", tmp3->air_state)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "AIR_TEMPERATURE", "%d", tmp3->air_temperature)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "AIR_IDX", "%d", tmp3->air_idx)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
	    }
	    if (tmp3->beer_address) {
	    	if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "BEER_ADDRESS", "%s", tmp3->beer_address)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
	    	}
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "BEER_STATE", "%d", tmp3->beer_state)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "BEER_TEMPERATURE", "%d", tmp3->beer_temperature)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "BEER_IDX", "%d", tmp3->beer_idx)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
	    }
	    if (tmp3->chiller_address) {
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "CHILLER_ADDRESS", "%s", tmp3->chiller_address)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "CHILLER_STATE", "%d", tmp3->chiller_state)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "CHILLER_TEMPERATURE", "%d", tmp3->chiller_temperature)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "CHILLER_IDX", "%d", tmp3->chiller_idx)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
	    }
	    if (tmp3->heater_address) {
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_ADDRESS", "%s", tmp3->heater_address)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_STATE", "%d", tmp3->heater_state)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_DELAY", "%d", tmp3->heater_delay)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_USAGE", "%d", tmp3->heater_usage)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_IDX", "%d", tmp3->heater_idx)) < 0)) {
		   syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		   return 1;
		}
	    }
	    if (tmp3->cooler_address) {
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_ADDRESS", "%s", tmp3->cooler_address)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_STATE", "%d", tmp3->cooler_state)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;                                   
		}
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_DELAY", "%d", tmp3->cooler_delay)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_USAGE", "%d", tmp3->cooler_usage)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_IDX", "%d", tmp3->cooler_idx)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
	    }
	    if (tmp3->fan_address) {
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "FAN_ADDRESS", "%s", tmp3->fan_address)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "FAN_STATE", "%d", tmp3->fan_state)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;                                   
		}
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "FAN_DELAY", "%d", tmp3->fan_delay)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "FAN_USAGE", "%d", tmp3->fan_usage)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "FAN_IDX", "%d", tmp3->fan_idx)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
	    }
	    if (tmp3->light_address) {
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "LIGHT_ADDRESS", "%s", tmp3->light_address)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "LIGHT_STATE", "%d", tmp3->light_state)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "LIGHT_DELAY", "%d", tmp3->light_delay)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "LIGHT_USAGE", "%d", tmp3->light_usage)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "LIGHT_IDX", "%d", tmp3->light_idx)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
	    }
	    if (tmp3->door_address) {
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "DOOR_ADDRESS", "%s", tmp3->door_address)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		return 1;
		}
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "DOOR_STATE", "%d", tmp3->door_state)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		return 1;
		}
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "DOOR_IDX", "%d", tmp3->door_idx)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
	    }
	    if (tmp3->psu_address) {
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PSU_ADDRESS", "%s", tmp3->psu_address)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PSU_STATE", "%d", tmp3->psu_state)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if (((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PSU_IDX", "%d", tmp3->psu_idx)) < 0)) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "MODE", "%s", UNITMODE[tmp3->mode] )) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "BEER_SET", "%.1f", tmp3->beer_set)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "FRIDGE_SET", "%.1f", tmp3->fridge_set)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "TEMP_SET_MIN", "%.1f", tmp3->temp_set_min)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "TEMP_SET_MAX", "%.1f", tmp3->temp_set_max)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		return 1;
	    }
	    if (tmp3->profile) {
		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PROFILE", "%s", tmp3->profile)) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PROF_STARTED", "%d", (unsigned int)tmp3->prof_started)) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PROF_PAUSED", "%d", (unsigned int)tmp3->prof_paused)) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PROF_STATE", "%s", PROFSTATE[tmp3->prof_state] )) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PROF_PEAK_ABS", "%.3f", tmp3->prof_peak_abs)) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PROF_PEAK_REL", "%.3f", tmp3->prof_peak_rel)) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PROF_PRIMARY_DONE", "%d", (unsigned int)tmp3->prof_primary_done)) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
	    }
	    if (tmp3->PID_cool) {
		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_IMAX", "%.2f", tmp3->PID_cool->iMax)) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_IGAIN", "%.3f", tmp3->PID_cool->iGain)) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
	        if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_PGAIN", "%.3f", tmp3->PID_cool->pGain)) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
	        if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_DGAIN", "%.3f", tmp3->PID_cool->dGain)) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_IDLERANGE", "%.2f", tmp3->PID_cool->idleRange)) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_INPUT", "%.2f", tmp3->PID_cool->Input)) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_ERR", "%.2f", tmp3->PID_cool->Err)) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_ISTATE", "%.2f", tmp3->PID_cool->iState)) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_SETP", "%.2f", tmp3->PID_cool->SetP)) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_OUTP", "%.2f", tmp3->PID_cool->OutP)) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_MODE", "%s", PIDMODE[tmp3->PID_cool->Mode])) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDC_TYPE", "COOL")) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
	    }
	    if (tmp3->PID_heat) {
		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_IMAX", "%.2f", tmp3->PID_heat->iMax)) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_IDLERANGE", "%.2f", tmp3->PID_heat->idleRange)) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_IGAIN", "%.3f", tmp3->PID_heat->iGain)) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_PGAIN", "%.3f", tmp3->PID_heat->pGain)) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_DGAIN", "%.3f", tmp3->PID_heat->dGain)) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_INPUT", "%.2f", tmp3->PID_heat->Input)) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_ERR", "%.2f", tmp3->PID_heat->Err)) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_ISTATE", "%.2f", tmp3->PID_heat->iState)) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_SETP", "%.2f", tmp3->PID_heat->SetP)) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_OUTP", "%.2f", tmp3->PID_heat->OutP)) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_MODE", "%s", PIDMODE[tmp3->PID_heat->Mode])) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
		if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PIDH_TYPE", "HEAT")) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		    return 1;
		}
	    }
	    if ((rc = xmlTextWriterEndElement(writer)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterEndElement");
		return 1;
	    }
	}
	if ((rc = xmlTextWriterEndElement(writer)) < 0) {
	    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterEndElement");
	    return 1;
	}
    }

    /*
     * Fermenting profiles
     */
    if (Config.profiles) {
	if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "PROFILES")) < 0) {
	    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterStartElement");
	    return 1;
	}
	for (tmp4 = Config.profiles; tmp4; tmp4 = tmp4->next) {
	    if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "PROFILE")) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterStartElement");
		return 1;
	    }
	    if ((rc = xmlTextWriterWriteElement(writer, BAD_CAST "VERSION", BAD_CAST "1")) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
		return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "UUID", "%s", tmp4->uuid)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "NAME", "%s", tmp4->name)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "BUSY", "%d", tmp4->busy)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "INITTEMP_LO", "%.1f", tmp4->inittemp_lo)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "INITTEMP_HI", "%.1f", tmp4->inittemp_hi)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "FRIDGE_MODE", "%d", tmp4->fridge_mode)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		return 1;
	    }
	    if (tmp4->steps) {
		if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "STEPS")) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterStartElement");
		    return 1;
		}
		for (tmp5 = tmp4->steps; tmp5; tmp5 = tmp5->next) {
		    if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "STEP")) < 0) {
			syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterStartElement");
			return 1;
		    }
		    if ((rc = xmlTextWriterWriteElement(writer, BAD_CAST "VERSION", BAD_CAST "1")) < 0) {
			syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
			return 1;
		    }
		    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "RESTTIME", "%d", tmp5->resttime)) < 0) {
		        syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
			return 1;
		    }
		    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "STEPTIME", "%d", tmp5->steptime)) < 0) {
			syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
			return 1;
		    }
		    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "TARGET_LO", "%.1f", tmp5->target_lo)) < 0) {
		        syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
			return 1;
		    }
		    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "TARGET_HI", "%.1f", tmp5->target_hi)) < 0) {
			syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
			return 1;
		    }
		    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "FRIDGE_MODE", "%d", tmp5->fridge_mode)) < 0) {
			syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
			return 1;
		    }
		    if ((rc = xmlTextWriterEndElement(writer)) < 0) {
			syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterEndElement");
			return 1;
		    }
		}
		if ((rc = xmlTextWriterEndElement(writer)) < 0) {
		    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterEndElement");
		    return 1;
		}
	    }
	    if ((rc = xmlTextWriterEndElement(writer)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterEndElement");
		return 1;
	    }
	}
	if ((rc = xmlTextWriterEndElement(writer)) < 0) {
	    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterEndElement");
	    return 1;
	}
    }

    if (Config.devices) {
	if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "DEVICES")) < 0) {
	    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterStartElement");
	    return 1;
	}
#ifdef HAVE_WIRINGPI_H
	piLock(LOCK_DEVICES);
#endif
	for (device = Config.devices; device; device = device->next) {
	    if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "DEVICE")) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterStartElement");
		return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "VERSION", "%d", device->version)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
		return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "UUID", "%s", device->uuid)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "TYPE", "%s", DEVTYPE[device->type])) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "DIRECTION", "%s", DEVDIR[device->direction])) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "VALUE", "%d", device->value)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
		return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "OFFSET", "%d", device->offset)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
		return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "PRESENT", "%s", DEVPRESENT[device->present])) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		return 1;                           
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "ADDRESS", "%s", device->address)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
		return 1;                           
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "SUBDEVICE", "%d", device->subdevice)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
		return 1;                           
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "GPIOPIN", "%d", device->gpiopin)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
		return 1;                           
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "DESCRIPTION", "%s", device->description)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
		return 1;                           
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "INUSE", "%d", device->inuse)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
		return 1;                           
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "COMMENT", "%s", device->comment)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
		return 1;                           
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "TIMESTAMP", "%d", (int)device->timestamp)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
		return 1;                           
	    }
	    if ((rc = xmlTextWriterEndElement(writer)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterEndElement");
		return 1;
	    }
	}
#ifdef HAVE_WIRINGPI_H
	piUnlock(LOCK_DEVICES);
#endif

	if ((rc = xmlTextWriterEndElement(writer)) < 0) {
	    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterEndElement");
	    return 1;
	}
    }

#ifdef USE_SIMULATOR
    if (Config.simulators) {
	if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "SIMULATORS")) < 0) {
	    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterStartElement");
	    return 1;
	}
	for (simulator = Config.simulators; simulator; simulator = simulator->next) {
    	    if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "SIMULATOR")) < 0) {
	    	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterStartElement");
	    	return 1;
    	    }
    	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "VERSION", "%d", simulator->version)) < 0) {
	    	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
	    	return 1;
    	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "UUID", "%s", simulator->uuid)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "NAME", "%s", simulator->name)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteFormatElement");
		return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "VOLUME_AIR", "%d", simulator->volume_air)) < 0) {
	    	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
	    	return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "VOLUME_BEER", "%d", simulator->volume_beer)) < 0) {
	    	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
	    	return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "ROOM_TEMPERATURE", "%.1f", simulator->room_temperature)) < 0) {
	   	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
	   	return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "ROOM_HUMIDITY", "%.1f", simulator->room_humidity)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
		return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "AIR_TEMPERATURE", "%f", simulator->air_temperature)) < 0) {
	    	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
	    	return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "BEER_TEMPERATURE", "%f", simulator->beer_temperature)) < 0) {
	    	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
	    	return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "CHILLER_TEMPERATURE", "%f", simulator->chiller_temperature)) < 0) {
		syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
		return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_TEMP", "%f", simulator->cooler_temp)) < 0) {
	    	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
	    	return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_TIME", "%d", simulator->cooler_time)) < 0) {
	    	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
	    	return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_SIZE", "%.3f", simulator->cooler_size)) < 0) {
	    	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
	    	return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_TEMP", "%f", simulator->heater_temp)) < 0) {
	    	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
	    	return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_TIME", "%d", simulator->heater_time)) < 0) {
	    	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
	    	return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_SIZE", "%.3f", simulator->heater_size)) < 0) {
	    	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
	    	return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_STATE", "%d", simulator->heater_state)) < 0) {
	    	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
	    	return 1;                           
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_STATE", "%d", simulator->cooler_state)) < 0) {
	    	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
	    	return 1;                       
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "FRIGO_ISOLATION", "%.3f", simulator->frigo_isolation)) < 0) {
	    	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
	    	return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "S_YEAST_HEAT", "%f", simulator->s_yeast_heat)) < 0) {
	    	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
	    	return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "S_YEAST_STARTED", "%d", (int)simulator->s_yeast_started)) < 0) {
	    	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
	    	return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "S_COOL_TEMP", "%f", simulator->s_cool_temp)) < 0) {
	    	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
	    	return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "S_HEAT_TEMP", "%f", simulator->s_heat_temp)) < 0) {
	    	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
	    	return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "S_COOL_CHANGED", "%d", (int)simulator->s_cool_changed)) < 0) {
	    	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
	    	return 1;
	    }
	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "S_HEAT_CHANGED", "%d", (int)simulator->s_heat_changed)) < 0) {
	    	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterWriteElement");
	    	return 1;
	    }
    	    if ((rc = xmlTextWriterEndElement(writer)) < 0) {
	    	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterEndElement");
	    	return 1;
    	    }
	}
	if ((rc = xmlTextWriterEndElement(writer)) < 0) {
	    syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterEndElement");
	    return 1;
	}
    }
#endif

    /*
     * All done, close any open elements
     */
    if ((rc = xmlTextWriterEndDocument(writer)) < 0) {
	syslog(LOG_NOTICE, "wrconfig: error at xmlTextWriterEndDocument");
	return 1;
    }
    xmlFreeTextWriter(writer);

    /*
     * Now write the XML configuration
     */
    if (getenv((char *)"USER") == NULL) {
	mypath = xstrcpy((char *)"/root");
    } else {
	mypath = xstrcpy(getenv((char *)"HOME"));
    }
    mypath = xstrcat(mypath, (char *)"/.thermferm/etc/");
    mkdirs(mypath, 0755);
    mypath = xstrcat(mypath, (char *)"thermferm.xml");

    if (debug)
	fprintf(stdout, "Writing %s\n", mypath);

    if ((fp = fopen(mypath, "w")) == NULL) {
	syslog(LOG_NOTICE, "could not rewrite %s", mypath);
	free(mypath);
	return 1;
    }
    free(mypath);

    fprintf(fp, "%s", (const char *) buf->content);
    fclose(fp);
    xmlBufferFree(buf);

    return 0;
}



int wrconfig(void)
{
    int		rc;

    rc = do_wrconfig();
    syslog(LOG_NOTICE, "Rewritten configuration, rc=%d", rc);
    return rc;
}



/*
 * Parse one LCD display
 */
int parseLCD(xmlDocPtr doc, xmlNodePtr cur)
{
    xmlChar	*key;
    int		ival;

    cur = cur->xmlChildrenNode;
    while (cur != NULL) {
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"COLUMNS"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
	        Config.lcd_cols = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"ROWS"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		Config.lcd_rows = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"ADDRESS"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%x", &ival) == 1)
		Config.lcd_address = ival;
	    xmlFree(key);
	}
	cur = cur->next;
    }

    return 0;
}



int parseLCDs(xmlDocPtr doc, xmlNodePtr cur)
{
    cur = cur->xmlChildrenNode;
    while (cur != NULL) {
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"LCD"))) {
	    parseLCD(doc, cur);
	}
	cur = cur->next;
    }
    return 0;
}



/*
 * Parse a fermenter unit
 */
int parseUnit(xmlDocPtr doc, xmlNodePtr cur/* , int number */)
{
    xmlChar     *key;
    int         i, ival;
    float	val;
    units_list	*unit, *tmp;

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

    cur = cur->xmlChildrenNode;
    while (cur != NULL) {
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"VERSION"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (xmlStrcmp(key, (const xmlChar *)"1")) {
		xmlFree(key);
		return 1;
	    }
	    unit->version = 1;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"UUID"))) {
	    unit->uuid = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"NAME"))) {
	    /*
	     * Upgrade to product code and name
	     */
	    char  *oldname =  (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (strstr(oldname, (char *)" ")) {
		unit->product_code = xstrcpy(strtok(oldname, " "));
		unit->product_name = xstrcpy(strtok(NULL, "\0"));
	    } else {
		unit->product_code = xstrcpy((char *)"000000");
		unit->product_name = xstrcpy(oldname);
	    }
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PRODUCT_UUID"))) {
	    unit->product_uuid = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PRODUCT_CODE"))) {
	    unit->product_code = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PRODUCT_NAME"))) {
	    unit->product_name = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"ALIAS"))) {
	    unit->alias = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"VOLUME"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		unit->volume = val;
		xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"AIR_ADDRESS"))) {
	    unit->air_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"AIR_STATE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		unit->air_state = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"AIR_IDX"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		unit->air_idx = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"AIR_TEMPERATURE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		unit->air_temperature = ival;
	    xmlFree(key);                           
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"BEER_ADDRESS"))) {
	    unit->beer_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"BEER_STATE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		unit->beer_state = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"BEER_IDX"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		unit->beer_idx = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"BEER_TEMPERATURE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		unit->beer_temperature = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"CHILLER_ADDRESS"))) {
	    unit->chiller_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"CHILLER_STATE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		unit->chiller_state = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"CHILLER_IDX"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		unit->chiller_idx = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"CHILLER_TEMPERATURE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		unit->chiller_temperature = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"HEATER_ADDRESS"))) {
	    unit->heater_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"HEATER_DELAY"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		unit->heater_delay = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"HEATER_IDX"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		unit->heater_idx = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"HEATER_USAGE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		unit->heater_usage = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"COOLER_ADDRESS"))) {
	    unit->cooler_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"COOLER_DELAY"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		unit->cooler_delay = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"COOLER_IDX"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		unit->cooler_idx = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"COOLER_USAGE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		unit->cooler_usage = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"FAN_ADDRESS"))) {
	    unit->fan_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"FAN_DELAY"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		unit->fan_delay = ival;
	    xmlFree(key);                           
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"FAN_IDX"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		unit->fan_idx = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"FAN_USAGE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		unit->fan_usage = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"LIGHT_ADDRESS"))) {
	    unit->light_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"LIGHT_DELAY"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		unit->light_delay = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"LIGHT_IDX"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		unit->light_idx = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"LIGHT_USAGE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		unit->light_usage = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"DOOR_ADDRESS"))) {
	    unit->door_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"DOOR_IDX"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		unit->door_idx = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PSU_ADDRESS"))) {
	    unit->psu_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PSU_IDX"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		unit->psu_idx = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"MODE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    for (i = 0; i < 5; i++) {
	    	if (! xmlStrcmp(key, (const xmlChar *)UNITMODE[i])) {
	   	    unit->mode = i;
		    break;
		}
	    }
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"BEER_SET"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		unit->beer_set = val;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"FRIDGE_SET"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		unit->fridge_set = val;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"TEMP_SET_MIN"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		unit->temp_set_min = val;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"TEMP_SET_MAX"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		unit->temp_set_max = val;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_KP"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		unit->PID_cool->pGain = unit->PID_heat->pGain = val;	/* Upgrade config	*/
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_KD"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		unit->PID_cool->dGain = unit->PID_heat->dGain = val;	/* Upgrade config       */
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PID_KI"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		unit->PID_cool->iGain = unit->PID_heat->iGain = val;	/* Upgrade config       */
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_IMAX"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		unit->PID_cool->iMax = val;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_IDLERANGE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		unit->PID_cool->idleRange = val;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_IGAIN"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		unit->PID_cool->iGain = val;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_PGAIN"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		unit->PID_cool->pGain = val;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_DGAIN"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		unit->PID_cool->dGain = val;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_INPUT"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		unit->PID_cool->Input = val;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_ERR"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		unit->PID_cool->Err = val;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_ISTATE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		unit->PID_cool->iState = val;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_SETP"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		unit->PID_cool->SetP = val;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_OUTP"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		unit->PID_cool->OutP = val;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDC_MODE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    for (i = 0; i < 3; i++) {
		if (! xmlStrcmp(key, (const xmlChar *)PIDMODE[i])) {
		    unit->PID_cool->Mode = i;
		    break;
	    	}
	    }
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_IMAX"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		unit->PID_heat->iMax = val;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_IDLERANGE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		unit->PID_heat->idleRange = val;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_IGAIN"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		unit->PID_heat->iGain = val;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_PGAIN"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		unit->PID_heat->pGain = val;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_DGAIN"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		unit->PID_heat->dGain = val;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_INPUT"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		unit->PID_heat->Input = val;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_ERR"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		unit->PID_heat->Err = val;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_ISTATE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		unit->PID_heat->iState = val;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_SETP"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		unit->PID_heat->SetP = val;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_OUTP"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		unit->PID_heat->OutP = val;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PIDH_MODE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    for (i = 0; i < 3; i++) {
		if (! xmlStrcmp(key, (const xmlChar *)PIDMODE[i])) {
		    unit->PID_heat->Mode = i;
		    break;
		}
	    }
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PROFILE"))) {
	    unit->profile = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PROF_STARTED"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		unit->prof_started = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PROF_PAUSED"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		unit->prof_paused = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PROF_STATE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    for (i = 0; i < 5; i++) {
		if (! xmlStrcmp(key, (const xmlChar *)PROFSTATE[i])) {
		    unit->prof_state = i;
		    break;
		}
	    }
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PROF_PEAK_ABS"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		unit->prof_peak_abs = val;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PROF_PEAK_REL"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		unit->prof_peak_rel = val;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PROF_PRIMARY_DONE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		unit->prof_primary_done = ival;
	    xmlFree(key);
	}
	cur = cur->next;
    }

    if (Config.units == NULL) {
	Config.units = unit;
    } else {
	for (tmp = Config.units; tmp; tmp = tmp->next) {
	    if (tmp->next == NULL) {
		tmp->next = unit;
		break;
	    }
	}
    }

    return 0;
}



int parseFermenters(xmlDocPtr doc, xmlNodePtr cur)
{
    cur = cur->xmlChildrenNode;
    while (cur != NULL) {
        if ((!xmlStrcmp(cur->name, (const xmlChar *)"UNIT"))) {
            parseUnit(doc, cur);
        }
        cur = cur->next;
    }
    return 0;
}



int parseStep(xmlDocPtr doc, xmlNodePtr cur, prof_step **profstep)
{
    xmlChar     *key;
    int         ival;
    float       val;
    prof_step	*step, *tmp;

    step = (prof_step *)malloc(sizeof(prof_step));
    step->next = NULL;
    step->version = 1;
    step->steptime = step->resttime = step->fridge_mode = 0;
    step->target_lo = step->target_hi = 20.0;

    cur = cur->xmlChildrenNode;
    while (cur != NULL) {
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"VERSION"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (xmlStrcmp(key, (const xmlChar *)"1")) {
		xmlFree(key);
		return 1;
	    }
	    step->version = 1;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"RESTTIME"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		step->resttime = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"STEPTIME"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		step->steptime = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"TARGET"))) {	/* Upgrade from single values */
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1) {
		step->target_lo = val - 0.2;
		step->target_hi = val + 0.2;
	    }
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"TARGET_LO"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		step->target_lo = val;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"TARGET_HI"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &val) == 1)
		step->target_hi = val;
	    xmlFree(key);   
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"FRIDGE_MODE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		step->fridge_mode = ival;
	    xmlFree(key);
	}
	cur = cur->next;
    }

    if (*profstep == NULL) {
	*profstep = step;
    } else {
	for (tmp = *profstep; tmp; tmp = tmp->next) {
	    if (tmp->next == NULL) {
		tmp->next = step;
		break;
	    }
	}
    }
    return 0;
}



int parseSteps(xmlDocPtr doc, xmlNodePtr cur, prof_step **step)
{
    cur = cur->xmlChildrenNode;
    while (cur != NULL) {
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"STEP"))) {
	    parseStep(doc, cur, step);
	}
	cur = cur->next;
    }
    return 0;
}



int parseProfile(xmlDocPtr doc, xmlNodePtr cur)
{
    xmlChar     	*key;
    profiles_list	*profile, *tmp;
    int			ival;
    float		fval;

    profile = (profiles_list *)malloc(sizeof(profiles_list));
    profile->next = NULL;
    profile->version = 1;
    profile->uuid = profile->name = NULL;
    profile->busy = profile->fridge_mode = 0;
    profile->inittemp_lo = profile->inittemp_hi = 20.0;
    profile->steps = NULL;

    cur = cur->xmlChildrenNode;
    while (cur != NULL) {
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"VERSION"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (xmlStrcmp(key, (const xmlChar *)"1")) {
		xmlFree(key);
		return 1;
	    }
	    profile->version = 1;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"UUID"))) {
	    profile->uuid = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"NAME"))) {
	    profile->name = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"BUSY"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		profile->busy = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"INITTEMP"))) {	/* Upgrade from single temp */
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &fval) == 1) {
		profile->inittemp_lo = fval - 0.2;
		profile->inittemp_hi = fval + 0.2;
	    }
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"INITTEMP_LO"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &fval) == 1)
		profile->inittemp_lo = fval;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"INITTEMP_HI"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &fval) == 1)
		profile->inittemp_hi = fval;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"FRIDGE_MODE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		profile->fridge_mode = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"STEPS"))) {
	    parseSteps(doc, cur, &(profile)->steps);
	}
	cur = cur->next;
    }

    if (Config.profiles == NULL) {
	Config.profiles = profile;
    } else {
	for (tmp = Config.profiles; tmp; tmp = tmp->next) {
	    if (tmp->next == NULL) {
		tmp->next = profile;
		break;
	    }
	}
    }

    return 0;
}



int parseProfiles(xmlDocPtr doc, xmlNodePtr cur)
{
    cur = cur->xmlChildrenNode;
    while (cur != NULL) {
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PROFILE"))) {
	    parseProfile(doc, cur);
	}
	cur = cur->next;
    }
    return 0;
}



int parseDevice(xmlDocPtr doc, xmlNodePtr cur)
{
    xmlChar             *key;
    devices_list	*device, *tmp;
    int                 i, ival;

    device = (devices_list *)malloc(sizeof(devices_list));
    device->next = NULL;
    device->version = 1;
    device->uuid = device->address = device->description = device->comment = NULL;
    device->type = device->direction = device->present = device->subdevice = device->inuse = device->offset = 0;
    device->gpiopin = -1;
    device->timestamp = (time_t)0;

    cur = cur->xmlChildrenNode;
    while (cur != NULL) {
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"VERSION"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (xmlStrcmp(key, (const xmlChar *)"1")) {
		xmlFree(key);
		return 1;
	    }
	    device->version = 1;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"UUID"))) {
	    device->uuid = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"TYPE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    for (i = 0; i < 8; i++) {
		if (! xmlStrcmp(key, (const xmlChar *)DEVTYPE[i])) {
		    device->type = i;
		    break;
		}
	    }
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"DIRECTION"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    for (i = 0; i < 7; i++) {
		if (! xmlStrcmp(key, (const xmlChar *)DEVDIR[i])) {
		    device->direction = i;
		    break;
		}
	    }
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"VALUE"))) {
		key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
		if (sscanf((const char *)key, "%d", &ival) == 1)
			device->value = ival;
		xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"OFFSET"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		device->offset = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PRESENT"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    for (i = 0; i < 4; i++) {
		if (! xmlStrcmp(key, (const xmlChar *)DEVPRESENT[i])) {
		    device->present = i;
		    break;
		}
	    }
	    xmlFree(key);                           
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"ADDRESS"))) {
	    device->address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"SUBDEVICE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		device->subdevice = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"GPIOPIN"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		device->gpiopin = ival;
	    xmlFree(key);                                       
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"DESCRIPTION"))) {
	    device->description = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"INUSE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		device->inuse = ival;
	    xmlFree(key);                                       
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"COMMENT"))) {
	    device->comment = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"TIMESTAMP"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		device->timestamp = (time_t)ival;
	    xmlFree(key);                                   
	}

	cur = cur->next;
    }

    if (Config.devices == NULL) {
	Config.devices = device;
    } else {
	for (tmp = Config.devices; tmp; tmp = tmp->next) {
	    if (tmp->next == NULL) {
		tmp->next = device;
		break;
	    }
	}
    }

    return 0;
}



int parseDevices(xmlDocPtr doc, xmlNodePtr cur)
{
    cur = cur->xmlChildrenNode;
    while (cur != NULL) {
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"DEVICE"))) {
	    parseDevice(doc, cur);
	}
	cur = cur->next;
    }
    return 0;
}



#ifdef USE_SIMULATOR
int parseSimulator(xmlDocPtr doc, xmlNodePtr cur)
{
    xmlChar             *key;
    simulator_list      *simulator, *tmp;
    int                 ival;
    float		fval;

    simulator = (simulator_list *)malloc(sizeof(simulator_list));
    simulator->next = NULL;
    simulator->version = 1;
    simulator->uuid = simulator->name = NULL;
    simulator->volume_air = simulator->volume_beer = 0;
    simulator->room_temperature = simulator->air_temperature = simulator->beer_temperature = simulator->s_cool_temp = simulator->s_heat_temp = 20.0;
    simulator->chiller_temperature = 1.5;
    simulator->room_humidity = 49.2;
    simulator->cooler_temp = simulator->cooler_size = simulator->heater_temp = simulator->heater_size = simulator->frigo_isolation = 0.0;
    simulator->cooler_time = simulator->heater_time = simulator->cooler_state = simulator->heater_state = 0;
    simulator->s_yeast_started = simulator->s_cool_changed = simulator->s_heat_changed = (time_t)0;
    simulator->s_yeast_heat = simulator->s_cool_temp = simulator->s_heat_temp = 0.0;

    cur = cur->xmlChildrenNode;
    while (cur != NULL) {
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"VERSION"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (xmlStrcmp(key, (const xmlChar *)"1")) {
		xmlFree(key);
		return 1;
	    }
	    simulator->version = 1;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"UUID"))) {
	    simulator->uuid = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"NAME"))) {
	    simulator->name = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"VOLUME_AIR"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		simulator->volume_air = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"VOLUME_BEER"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		simulator->volume_beer = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"ROOM_TEMPERATURE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &fval) == 1)
		simulator->room_temperature = fval;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"ROOM_GUMIDITY"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &fval) == 1)
		simulator->room_humidity= fval;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"AIR_TEMPERATURE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &fval) == 1)
		simulator->air_temperature = fval;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"BEER_TEMPERATURE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &fval) == 1)
		simulator->beer_temperature = fval;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"CHILLER_TEMPERATURE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &fval) == 1)
		simulator->chiller_temperature = fval;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"COOLER_TEMP"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &fval) == 1)
		simulator->cooler_temp = fval;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"COOLER_TIME"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		simulator->cooler_time = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"COOLER_SIZE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &fval) == 1)
		simulator->cooler_size = fval;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"HEATER_TEMP"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &fval) == 1)
		simulator->heater_temp = fval;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"HEATER_TIME"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		simulator->heater_time = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"HEATER_SIZE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &fval) == 1)
		simulator->heater_size = fval;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"HEATER_STATE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		simulator->heater_state = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"COOLER_STATE"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		simulator->cooler_state = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"FRIGO_ISOLATION"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &fval) == 1)
		simulator->frigo_isolation = fval;
	    xmlFree(key);
	}

	if ((!xmlStrcmp(cur->name, (const xmlChar *)"S_YEAST_HEAT"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &fval) == 1)
		simulator->s_yeast_heat = fval;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"S_YEAST_STARTED"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		simulator->s_yeast_started = (time_t)ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"S_COOL_TEMP"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &fval) == 1)
		simulator->s_cool_temp = fval;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"S_HEAT_TEMP"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%f", &fval) == 1)
		simulator->s_heat_temp = fval;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"S_COOL_CHANGED"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		simulator->s_cool_changed = (time_t)ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"S_HEAT_CHANGED"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		simulator->s_heat_changed = (time_t)ival;
	    xmlFree(key);
	}

	cur = cur->next;
    }

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

    return 0;
}



int parseSimulators(xmlDocPtr doc, xmlNodePtr cur)
{
    cur = cur->xmlChildrenNode;
    while (cur != NULL) {
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"SIMULATOR"))) {
	    parseSimulator(doc, cur);
	}
	cur = cur->next;
    }
    return 0;
}
#endif



int rdconfig(void) 
{
    int		rc = 0, ival;
    char	*mypath;
    xmlDocPtr	doc;
    xmlNodePtr	cur;
    xmlChar	*key;

    killconfig();
    syslog(LOG_NOTICE, "HOME='%s' USER='%s' LOGNAME='%s'", MBSE_SS(getenv((char *)"HOME")), MBSE_SS(getenv((char *)"USER")), MBSE_SS(getenv((char *)"LOGNAME")));

    /*
     * Search config file
     */
    if (getenv((char *)"USER") == NULL) {
	mypath = xstrcpy((char *)"/root");
    } else {
    	mypath = xstrcpy(getenv((char *)"HOME"));
    }
    mypath = xstrcat(mypath, (char *)"/.thermferm/etc/");
    mkdirs(mypath, 0755);
    mypath = xstrcat(mypath, (char *)"thermferm.xml");
    if ((doc = xmlParseFile(mypath)) == NULL) {
	/*
	 * No config file, create a fresh one
	 */
	syslog(LOG_NOTICE, "rdconfig: %s not found, creating", mypath);
	wrconfig();

	if ((doc = xmlParseFile(mypath)) == NULL) {
	    syslog(LOG_NOTICE, "rdconfig: could not create %s", mypath);
	    free(mypath);
	    return 1;
	}
    }
    syslog(LOG_NOTICE, "rdconfig: using %s", mypath);

    Config.mqtt_host = xstrcpy((char *)"localhost");
    Config.mqtt_port = 1883;
    Config.next_unit = 1;

    if ((cur = xmlDocGetRootElement(doc)) == NULL) {
	syslog(LOG_NOTICE, "XML file %s empty.", mypath);
	xmlFreeDoc(doc);
	return 1;
    }
    if (xmlStrcmp(cur->name, (const xmlChar*)"THERMFERM")) {
	syslog(LOG_NOTICE, "XML file %s is not a valid configuration file.", mypath);
	xmlFreeDoc(doc);
	return 1;
    }

    /*
     * Parse configuration
     */
    cur = cur->xmlChildrenNode;
    while (cur != NULL) {
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"VERSION"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (xmlStrcmp(key, (const xmlChar *)"1")) {
		xmlFree(key);
		syslog(LOG_NOTICE, "XML file %s is not a valid version", mypath);
		return 1;
	    }
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"NAME"))) {
	    Config.name = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"LISTEN_PORT"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		Config.my_port = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"TEMPFORMAT"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    Config.tempFormat = key[0];
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"TEMP_ADDRESS"))) {
	    Config.temp_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"HUM_ADDRESS"))) {
	    Config.hum_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"TEMP_HUM_IDX"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		Config.temp_hum_idx = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"NEXT_UNIT"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		Config.next_unit = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"MQTT_HOST"))) {
	    Config.mqtt_host = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"MQTT_PORT"))) {
	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	    if (sscanf((const char *)key, "%d", &ival) == 1)
		Config.mqtt_port = ival;
	    xmlFree(key);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"MQTT_USER"))) {
	    Config.mqtt_username = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"MQTT_PASS"))) {
	    Config.mqtt_password = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"UUID"))) {
	    Config.uuid= (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"LCDS"))) {
	    parseLCDs(doc, cur);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"FERMENTERS"))) {
	    parseFermenters(doc, cur);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"PROFILES"))) {
	    parseProfiles(doc, cur);
	}
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"DEVICES"))) {
	    parseDevices(doc, cur);
	}
#ifdef USE_SIMULATOR
	if ((!xmlStrcmp(cur->name, (const xmlChar *)"SIMULATORS"))) {
	    parseSimulators(doc, cur);
	}
#endif
	cur = cur->next;
    }
    xmlFreeDoc(doc);

    free(mypath);
    mypath = NULL;

    /*
     * If the system uuid is not set, do it now.
     */
    if (Config.uuid == NULL) {
	uuid_t		uu;

	Config.uuid = malloc(37);
	uuid_generate(uu);
	uuid_unparse(uu, Config.uuid);
    }

    return rc;
}


mercurial