thermferm/devices.c

Wed, 01 May 2024 14:38:37 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Wed, 01 May 2024 14:38:37 +0200
changeset 715
f5d85af156ab
parent 714
24749c296a50
child 716
5c30c8ef83a8
permissions
-rw-r--r--

Added device_present() function to easy update device present from one-wire and simulator devices. When a simulator temperature sensor present is changed, the device table is changed too. Controlling simulator relays is now for each simulator. The simulator runs under the state machine. If something changed in the running simulator, all data is broadcasted over websocket. Completed the web editor.

/*****************************************************************************
 * 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 "thermferm.h"
#include "delay.h"
#include "devices.h"
#include "rc-switch.h"
#include "panel.h"
#include "xutil.h"
#include "websocket.h"


int			my_devices_shutdown = 0;
int			my_devices_state = 0;

extern sys_config	Config;
extern pthread_mutex_t  mutexes[5];
extern w1_list		*w1_devices;

extern const char	DEVTYPE[8][6];
extern const char	DEVPRESENT[4][6];
extern const char	DEVDIR[7][11];



/*
 * Since kernel version 4 there is a module, and a dtoverlay so that the
 * temperature and humidity can simply read from the /sys filesystem.
 */
int             dht11_temperature = -1;
int             dht11_humidity = -1;
int		dht11_state = DEVPRESENT_UNDEF;
time_t		dht11_next;



/*
 * DHT11 sensor read.
 * Called at 30 seconds interval if all is well.
 * If last read was an error, the interval is 2 seconds.
 */
void dht11Read(char *address)
{
    int        	temp, hum;
    int		fd, rc, err, oldstate;
    char	buffer[25], *dhtpath = NULL;

    dht11_temperature = -1;
    dht11_humidity = -1;
    oldstate = dht11_state;
    dhtpath = xstrcpy((char *)"/sys/bus/iio/devices/");
    dhtpath = xstrcat(dhtpath, address);
    dhtpath = xstrcat(dhtpath, (char *)"/in_temp_input");

    fd = open(dhtpath, O_RDONLY);
    if (fd < 0) {
	/*
	 * The sensor is gone.
	 */
	err = errno;
	syslog(LOG_NOTICE, "DHT11 open temperature: %d %s", err, strerror(err));
	free(dhtpath);
	dht11_state = DEVPRESENT_NO;
	return;
    }

    rc = read(fd, buffer, 25);
    if (rc == -1) {
	err = errno;
	if (err == 110) {
		dht11_state = DEVPRESENT_NO;	/* Device is gone */
	} else {
		dht11_state = DEVPRESENT_ERROR;
	}
	syslog(LOG_NOTICE, "DHT11 read temperature: %s", strerror(err));
    } else {
	sscanf(buffer, "%d", &temp);
	dht11_temperature = temp;
	dht11_state = DEVPRESENT_YES;
    }
    close(fd);
    free(dhtpath);
    dhtpath = NULL;

    /*
     * Only read humidity if state is DEVPRESENT_YES
     */
    if (dht11_state == DEVPRESENT_YES) {
	dhtpath = xstrcpy((char *)"/sys/bus/iio/devices/");
	dhtpath = xstrcat(dhtpath, address);
	dhtpath = xstrcat(dhtpath, (char *)"/in_humidityrelative_input");

	fd = open(dhtpath, O_RDONLY);
	rc = read(fd, buffer, 25);
	if (rc == -1) {
	    err = errno;
	    if (err == 110) {
		    dht11_state = DEVPRESENT_NO;
	    } else {
		    dht11_state = DEVPRESENT_ERROR;
	    }
	    syslog(LOG_NOTICE, "DHT11 read humidity: %s", strerror(err));
	    dht11_temperature = -1;	/* Make invalid	*/
	} else {
	    sscanf(buffer, "%d", &hum);
	    dht11_humidity = hum;
	    dht11_state = DEVPRESENT_YES;
	}
	close(fd);
	free(dhtpath);
	dhtpath = NULL;
    }

    if (oldstate != dht11_state)
	syslog(LOG_NOTICE, "dht11 t:%d h:%d state:%d", dht11_temperature, dht11_humidity, dht11_state);
}



/*
 * Read one byte from a 1-wire device like a DS2413
 */
int read_w1(char *address, char *file)
{
    char	*addr = NULL;
    int		fn = -1, rc = -1, retries = 5;
    uint8_t	val;

    addr = xstrcpy((char *)"/sys/bus/w1/devices/");
    addr = xstrcat(addr, address);
    addr = xstrcat(addr, (char *)"/");
    addr = xstrcat(addr, file);

    if ((fn = open(addr, O_RDONLY)) >= 0) {

    	if ((lseek(fn, 0L, SEEK_SET)) == 0) {

	    while (retries--) {
    	    	if ((read(fn, &val, 1)) == 1) {
		    rc = (int)val;
		    goto leave;
    	    	}
	    }
	    syslog(LOG_NOTICE, "read_w1() read %s fatal: %s", addr, strerror(errno));

	} else {
	    syslog(LOG_NOTICE, "read_w1() lseek %s: %s", addr, strerror(errno));
	}
    	
    } else {
	syslog(LOG_NOTICE, "read_w1() open %s: %s", addr, strerror(errno));
    }

leave:
    if (fn != -1) {
	if ((close(fn)) == -1) {
	    syslog(LOG_NOTICE, "read_w1() close %s: %s", addr, strerror(errno));
	}
    }

    free(addr);
    return rc;
}



/*
 * Write a byte to a 1-wire device like a DS2413
 */
int write_w1(char *address, char *file, uint8_t val)
{
    char	*addr = NULL;
    int		fn = -1, rc = -1, retries = 5;

    addr = xstrcpy((char *)"/sys/bus/w1/devices/");
    addr = xstrcat(addr, address);
    addr = xstrcat(addr, (char *)"/");
    addr = xstrcat(addr, file);

    if ((fn = open(addr, O_WRONLY)) >= 0) {

	if ((lseek(fn, 0L, SEEK_SET)) == 0) {

	    while (retries--) {
	    	if ((write(fn, &val, 1)) == 1) {
		    rc = 0;
		    goto leave;
	    	}
	    }
	    syslog(LOG_NOTICE, "write_w1() write %s fatal: %s", addr, strerror(errno));

	} else {
	    syslog(LOG_NOTICE, "write_w1() lseek %s: %s", addr, strerror(errno));
	}

    } else {
	syslog(LOG_NOTICE, "write_w1() open %s: %s", addr, strerror(errno));
    }

leave:
    if (fn != -1) {
	if ((close(fn)) == -1) {
	    syslog(LOG_NOTICE, "write_w1() close %s: %s", addr, strerror(errno));
	}
    }

    free(addr);
    return rc;
}



int device_out(char *uuid, int value)
{
    devices_list	*device;
    time_t		now, my_timestamp;
    int			my_value, test_value;
#ifdef HAVE_WIRINGPI_H
    int			rc, i;
    char		buf[40];
#endif
#ifdef USE_SIMULATOR
    simulator_list      *simulator;
#endif

    if (uuid == NULL)
	return 0;

    now = time(NULL);
//    pthread_mutex_lock(&mutexes[LOCK_DEVICES]);

    for (device = Config.devices; device; device = device->next) {
	if (! strcmp(uuid, device->uuid)) {
	    /*
	     * Execute command if different then the old value. But also
	     * every 2 minutes because commands can have temporary
	     * disconnects, or have radio problems.
	     */
	    my_timestamp = device->timestamp;
	    my_value = device->value;

	    if ((device->type == DEVTYPE_W1) && (device->direction == DEVDIR_OUT_BIN)) {
		test_value = (value == 0) ? 0 : 1;
	    } else {
		test_value = value;
	    }

	    if ((test_value != my_value) || (((int)now - (int)my_timestamp) >= 120)) {

#ifdef HAVE_WIRINGPI_H
		rc = 0;
		if ((device->type == DEVTYPE_RC433) && (device->gpiopin != -1) && (device->present == DEVPRESENT_YES)) {
		    snprintf(buf, 39, "%s,%d", device->address, value ? 1:0);
		    for (i = 0; i < strlen(buf); i++)
			if (buf[i] == '-')
			    buf[i] = ',';
//		    pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
	    	    enableTransmit(device->gpiopin);	    
		    rc = toggleSwitch(buf);
		    disableTransmit();
//		    pthread_mutex_lock(&mutexes[LOCK_DEVICES]);
		    syslog(LOG_NOTICE, "RC433 command %s rc=%d", buf, rc);
                    device->value = value;
		    device->timestamp = time(NULL);
		    devices_ws();
//		    pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
		    return rc;
	        }

		if ((device->type == DEVTYPE_GPIO) && (device->gpiopin != -1) && (device->present == DEVPRESENT_YES)) {

		}
#endif
		if ((device->type == DEVTYPE_W1) && (device->direction == DEVDIR_OUT_BIN) && (device->present == DEVPRESENT_YES)) {
		    if (strncmp(device->address, (char *)"3a", 2) == 0) {
//		    	syslog(LOG_NOTICE, "DS2413 PIO%c value=%d (%s)", (device->subdevice == 0) ? 'A' : 'B', (value == 0) ? 0 : 1, device->comment);
//			pthread_mutex_lock(&mutexes[LOCK_DEVICES]);
			if (device->value != (value == 0) ? 0 : 1) {
			    syslog(LOG_NOTICE, "DS2413 PIO%c value=%d (%s)", (device->subdevice == 0) ? 'A' : 'B', (value == 0) ? 0 : 1, device->comment);
			    device->value = (value == 0) ? 0 : 1;
			    device->timestamp = time(NULL);
			    devices_ws();
			}
//			pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
		    }
		}

#ifdef USE_SIMULATOR
		if ((device->type == DEVTYPE_SIM) && (device->direction == DEVDIR_OUT_BIN)) {
		    for (simulator = Config.simulators; simulator; simulator = simulator->next) {
			if ((strcmp(device->address, simulator->cooler_address) == 0) ||
			    (strcmp(device->address, simulator->heater_address) == 0) ||
			    (strcmp(device->address, simulator->fan_address) == 0) ||
			    (strcmp(device->address, simulator->light_address) == 0)) {
			    if (value != device->value) {
				int rc = DEVPRESENT_UNDEF;
			    	device->value = value;
                            	device->timestamp = time(NULL);
			    	if (strcmp(device->address, simulator->cooler_address) == 0) {
                            	    simulator->cooler_power = value;
				    rc = simulator->cooler_present;
			    	} else if (strcmp(device->address, simulator->heater_address) == 0) {
				    simulator->heater_power = value;
				    rc = simulator->heater_present;
			    	} else if (strcmp(device->address, simulator->fan_address) == 0) {
				    simulator->fan_power = value;
				    rc = simulator->fan_present;
			    	} else if (strcmp(device->address, simulator->light_address) == 0) {
				    simulator->light_power = value;
				    rc = simulator->light_present;
			    	}
				syslog(LOG_NOTICE, "SIM %s value=%d, present=%s", device->address, value, DEVPRESENT[rc]);
				device->present = rc;
				devices_ws();
			    }
			}
		    }
		}
#endif
	    } else {
//		pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
		return 0;
	    } // if ((test_value != my_value) || (((int)now - (int)my_timestamp) >= 120))
	} // if (! strcmp(uuid, device->uuid))
    } // for (device = Config.devices; device; device = device->next)

//    pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
    return 0;
}	   


/*
 * Returns DEVPRESENT_NO  if failed.
 * Returns DEVPRESENT_YES if success, value contains new value.
 */
int device_in(char *uuid, int *value)
{
    devices_list	*device;
    int			tmp, present;

    if (uuid == NULL)
	return DEVPRESENT_UNDEF;

    for (device = Config.devices; device; device = device->next) {
	if (! strcmp(uuid, device->uuid)) {
	    present = device->present;
	    if (present == DEVPRESENT_YES) {
		tmp = device->value + device->offset;
	    } else {
		tmp = 0;
	    }
	    *value = tmp;
	    return present;
	}
    }

    return DEVPRESENT_NO;
}


int device_present(char *address, int present)
{
    devices_list        *device;

    if (address == NULL || present < DEVPRESENT_UNDEF || present > DEVPRESENT_ERROR) {
	return -1;
    }

    for (device = Config.devices; device; device = device->next) {
	if (! strcmp(address, device->address)) {
	    if (device->present != present) {
	    	device->present = present;
		device->timestamp = time(NULL);
		return 1;
	    }
	    return 0;
	}
    }
    return -1;
}


/*
 * Return json data for one device.
 */
char *device_json(devices_list *device)
{
    char	*payload;
    char	vbuf[64];

    payload = xstrcpy((char *)"{\"uuid\":\"");
    payload = xstrcat(payload, device->uuid);
    payload = xstrcat(payload, (char *)"\",\"type\":\"");
    payload = xstrcat(payload, (char *)DEVTYPE[device->type]);
    payload = xstrcat(payload, (char *)"\",\"direction\":\"");
    payload = xstrcat(payload, (char *)DEVDIR[device->direction]);
    payload = xstrcat(payload, (char *)"\",\"address\":\"");
    payload = xstrcat(payload, device->address);
    payload = xstrcat(payload, (char *)"\",\"subdevice\":");
    snprintf(vbuf, 63, "%d", device->subdevice);
    payload = xstrcat(payload, vbuf);
    payload = xstrcat(payload, (char *)",\"value\":");
    snprintf(vbuf, 63, "%d", device->value);
    payload = xstrcat(payload, vbuf);
    payload = xstrcat(payload, (char *)",\"offset\":");
    snprintf(vbuf, 63, "%d", device->offset);
    payload = xstrcat(payload, vbuf);
    payload = xstrcat(payload, (char *)",\"present\":\"");
    payload = xstrcat(payload, (char *)DEVPRESENT[device->present]);
    payload = xstrcat(payload, (char *)"\"");
    payload = xstrcat(payload, (char *)",\"gpiopin\":");
    snprintf(vbuf, 63, "%d", device->gpiopin);
    payload = xstrcat(payload, vbuf);
    payload = xstrcat(payload, (char *)",\"inuse\":");
    snprintf(vbuf, 63, "%d", device->inuse);
    payload = xstrcat(payload, vbuf);
    payload = xstrcat(payload, (char *)",\"description\":\"");
    payload = xstrcat(payload, device->description);
    payload = xstrcat(payload, (char *)"\",\"comment\":\"");
    payload = xstrcat(payload, device->comment);
    payload = xstrcat(payload, (char *)"\",\"timestamp\":");
    snprintf(vbuf, 63, "%ld", (long)device->timestamp);
    payload = xstrcat(payload, vbuf);
    payload = xstrcat(payload, (char *)"}");

    return payload;
}



void devices_ws(void)
{
    bool		comma = false;
    char		*payload = NULL, *payloadu = NULL;
    devices_list	*device;

    payload = xstrcpy((char *)"{\"type\":\"device\",\"metric\":[");
    for (device = Config.devices; device; device = device->next) {
	if (comma)
	    payload = xstrcat(payload, (char *)",");
	payloadu = device_json(device);
	payload = xstrcat(payload, payloadu);
	comma = true;
	free(payloadu);
	payloadu = NULL;
    }
    payload = xstrcat(payload, (char *)"]}");
    ws_broadcast(payload);
    free(payload);
    payload = NULL;
}



/*
 * Auto detect hotplugged or known to be present devices
 */
int devices_detect(void)
{
    struct dirent	*de;
    DIR			*fd;
    w1_list		*dev_w1;
    devices_list	*device, *ndev;
    int			found, subdevices, i, rc = 0;
    uuid_t		uu;
#ifdef HAVE_WIRINGPI_H
    int			pin;
    char		buf[40];
#endif
#ifdef USE_SIMULATOR
    simulator_list	*simulator;
#endif

    /*
     * Scan for 1-wire devices. These are already detected by the
     * one-wire thread that is already running. So we just check
     * these detected devices.
     */
    for (dev_w1 = w1_devices; dev_w1; dev_w1 = dev_w1->next) {
	found = FALSE;
	for (device = Config.devices; device; device = device->next) {
	    if (strcmp(device->address, dev_w1->address) == 0) {
		found = TRUE;
		break;
	    }
	}

	if (found == FALSE) {
	    subdevices = (strcmp(dev_w1->family, (char *)"3a") == 0) ? 2:1;
	    for (i = 0; i < subdevices; i++) {
		ndev = (devices_list *)malloc(sizeof(devices_list));
		ndev->next = NULL;
		ndev->uuid = malloc(37);
		uuid_generate(uu);
		uuid_unparse(uu, ndev->uuid);
		ndev->type = DEVTYPE_W1;
		ndev->direction = DEVDIR_IN_ANALOG;
		if (strcmp(dev_w1->family, (char *)"10") == 0) {
		    ndev->description = xstrcpy((char *)"DS18S20 Digital thermometer");
		} else if (strcmp(dev_w1->family, (char *)"22") == 0) {
		    ndev->description = xstrcpy((char *)"DS1822 Digital thermometer");
		} else if (strcmp(dev_w1->family, (char *)"28") == 0) {
		    ndev->description = xstrcpy((char *)"DS18B20 Digital thermometer");
		} else if (strcmp(dev_w1->family, (char *)"3a") == 0) {
		    ndev->description = xstrcpy((char *)"DS2413 Dual channel addressable switch");
		    ndev->direction = DEVDIR_IN_BIN;
		} else if (strcmp(dev_w1->family, (char *)"3b") == 0) {
		    ndev->description = xstrcpy((char *)"DS1825 Digital thermometer");
		} else if (strcmp(dev_w1->family, (char *)"42") == 0) {
		    ndev->description = xstrcpy((char *)"DS28EA00 Digital thermometer");
		} else {
		    ndev->description = xstrcpy((char *)"Unknown device family ");
		    ndev->direction = DEVDIR_UNDEF;
		}
		ndev->value = ndev->offset = ndev->inuse = 0;
		ndev->present = DEVPRESENT_YES;
		ndev->address = xstrcpy(dev_w1->address);
		ndev->subdevice = i;
		ndev->gpiopin = -1;
		ndev->comment = xstrcpy((char *)"Auto detected device");
		ndev->timestamp = time(NULL);

		syslog(LOG_NOTICE, "New W1 device %s, subdevice %d, %s", ndev->address, ndev->subdevice, ndev->description);

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

    /*
     * DHT11 as kernel module. The number after the @ is the hexadecimal
     * number of the BCM gpio pin. Since we use pin 22 at connector pin 15
     * the number is 16.
     */
    char *dhtaddr = NULL;
    if ((fd = opendir((char *)"/sys/devices/platform/dht11@16"))) {
	while ((de = readdir(fd))) {
	    if (de->d_name[0] != '.') {
		if (strncmp(de->d_name, (char *)"iio", 3) == 0) {
		    dhtaddr = xstrcpy(de->d_name);
		}
	    }
	}
	closedir(fd);
    }

    if (dhtaddr) {

	found = FALSE;
	for (device = Config.devices; device; device = device->next) {
	    if (strcmp(device->address, dhtaddr) == 0) {
		found = TRUE;
		break;
	    }
	}

	if (found == FALSE) {
	    for (i = 0; i < 2; i++) {
		ndev = (devices_list *)malloc(sizeof(devices_list));
		ndev->next = NULL;
		ndev->uuid = malloc(37);
		uuid_generate(uu);
		uuid_unparse(uu, ndev->uuid);
		ndev->type = DEVTYPE_DHT;
		ndev->direction = DEVDIR_IN_ANALOG;
		if (i == 0)
		    ndev->description = xstrcpy((char *)"DHT11 temperature sensor");
		else
		    ndev->description = xstrcpy((char *)"DHT11 humidity sensor");
		ndev->value = ndev->offset = ndev->inuse = 0;
	        ndev->present = DEVPRESENT_YES;
		ndev->address = xstrcpy(dhtaddr);
		ndev->subdevice = i;
		ndev->gpiopin = -1;
		ndev->comment = xstrcpy((char *)"Auto detected device");
		ndev->timestamp = time(NULL);

		syslog(LOG_NOTICE, "New DHT11 device %s, subdevice %d, %s", ndev->address, ndev->subdevice, ndev->description);

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

#ifdef HAVE_WIRINGPI_H
    if (piBoardRev() == 2) {
	/*
	 * Support rev B and newer boards only
	 */
	found = FALSE;
	for (device = Config.devices; device; device = device->next) {
	    if (device->type == DEVTYPE_GPIO) {
		found = TRUE;
		break;
	    }
	}

    	if (found == FALSE) {
	    /*
	     * There were no GPIO devices found.
	     */
	    subdevices = 12;
	    pin = 0;
	    for (i = 0; i < subdevices; i++) {
	    	if (i == 8)
		    pin = 17;

	    	ndev = (devices_list *)malloc(sizeof(devices_list));
	    	ndev->next = NULL;
	    	ndev->uuid = malloc(37);
	    	uuid_generate(uu);
	    	uuid_unparse(uu, ndev->uuid);
	    	ndev->type = DEVTYPE_GPIO;
	    	ndev->value = digitalRead(pin);
		ndev->offset = 0;
	    	ndev->present = DEVPRESENT_YES;
	    	ndev->address = xstrcpy((char *)"GPIO");
	    	snprintf(buf, 39, "Raspberry GPIO %d", i);
	    	ndev->description = xstrcpy(buf);
	    	ndev->subdevice = i;
	    	ndev->gpiopin = pin;
	    	ndev->timestamp = time(NULL);
		if (i == 0) {
		    ndev->direction = DEVDIR_INTERN;
		    ndev->inuse = 1;
		    ndev->comment = xstrcpy((char *)"433 Mhz transmitter");
		} else if (i == 3) {
		    ndev->direction = DEVDIR_INTERN;
		    ndev->inuse = 1;
		    ndev->comment = xstrcpy((char *)"DHT11 sensor");
		} else if (i == PANEL_LED) {
		    ndev->direction = DEVDIR_OUT_BIN;
		    ndev->inuse = 1;
		    ndev->comment = xstrcpy((char *)"Frontpanel LED");
		} else if (i == PANEL_ENTER) {
		    ndev->direction = DEVDIR_IN_BIN;
		    ndev->inuse = 1;
		    ndev->comment = xstrcpy((char *)"Frontpanel Enter key");
		} else if (i == PANEL_DOWN) {
		    ndev->direction = DEVDIR_IN_BIN;
		    ndev->inuse = 1;
		    ndev->comment = xstrcpy((char *)"Frontpanel Down key");
		} else if (i == PANEL_UP) {
		    ndev->direction = DEVDIR_IN_BIN;
		    ndev->inuse = 1;
		    ndev->comment = xstrcpy((char *)"Frontpanel Up key");
		} else if (i == 7) {
		    ndev->direction = DEVDIR_INTERN;
		    ndev->inuse = 1;
		    ndev->comment = xstrcpy((char *)"1-Wire bus");
	    	} else {
		    ndev->direction = DEVDIR_IN_BIN;
		    ndev->inuse = 0;
		    ndev->comment = xstrcpy((char *)"Raspberry GPIO");
	    	}
	    	pin++;

		syslog(LOG_NOTICE, "New GPIO device %s, subdevice %d, %s", ndev->address, ndev->subdevice, ndev->description);

	    	if (Config.devices == NULL) {
		    Config.devices = ndev;
	    	} else {
		    for (device = Config.devices; device; device = device->next) {
		        if (device->next == NULL) {
			    device->next = ndev;
			    break;
		    	}
		    }
	    	}
	    	rc++;
	    }
	}
    }
#endif

#ifdef USE_SIMULATOR

    for (simulator = Config.simulators; simulator; simulator = simulator->next) {
	for (i = 0; i < 10; i++) {

	    found = FALSE;
            for (device = Config.devices; device; device = device->next) {
            	if ((i == 0 && strcmp(device->address, simulator->room_tempaddress) == 0) ||
		    (i == 1 && strcmp(device->address, simulator->air_address) == 0) ||
		    (i == 2 && strcmp(device->address, simulator->beer_address) == 0) ||
		    (i == 3 && strcmp(device->address, simulator->heater_address) == 0) ||
		    (i == 4 && strcmp(device->address, simulator->cooler_address) == 0) ||
		    (i == 5 && strcmp(device->address, simulator->room_humaddress) == 0) ||
		    (i == 6 && strcmp(device->address, simulator->chiller_address) == 0) ||
		    (i == 7 && strcmp(device->address, simulator->fan_address) == 0) ||
		    (i == 8 && strcmp(device->address, simulator->light_address) == 0) ||
		    (i == 9 && strcmp(device->address, simulator->beer_address2) == 0)) {
                    found = TRUE;
                    break;
            	}
            }

	    if (found == FALSE) {
		ndev = (devices_list *)malloc(sizeof(devices_list));
		ndev->next = NULL;
		ndev->uuid = malloc(37);
                uuid_generate(uu);
                uuid_unparse(uu, ndev->uuid);
		ndev->type = DEVTYPE_SIM;
        	ndev->value = ndev->offset = 0;
        	ndev->present = DEVPRESENT_NO;
        	ndev->gpiopin = -1;
		ndev->subdevice = i;
        	ndev->comment = xstrcpy((char *)"Auto detected device");
        	ndev->timestamp = time(NULL);
        	ndev->inuse = 0;

		switch (i) {
		    case 0:	ndev->direction = DEVDIR_IN_ANALOG;
				ndev->address = xstrcpy(simulator->room_tempaddress);
				ndev->description = xstrcpy((char *)"Simulated room temperature");
				ndev->present = DEVPRESENT_YES;
				break;
		    case 1:	ndev->direction = DEVDIR_IN_ANALOG;
				ndev->address = xstrcpy(simulator->air_address);
				ndev->description = xstrcpy((char *)"Simulated air temperature");
				ndev->present = simulator->air_present;
				break;
		    case 2:	ndev->direction = DEVDIR_IN_ANALOG;
				ndev->address = xstrcpy(simulator->beer_address);
				ndev->description = xstrcpy((char *)"Simulated beer temperature");
				ndev->present = simulator->beer_present;
				break;
		    case 3:	ndev->direction = DEVDIR_OUT_BIN;
				ndev->address = xstrcpy(simulator->heater_address);
				ndev->description = xstrcpy((char *)"Simulated heater");
				ndev->present = simulator->heater_present;
				break;
		    case 4:	ndev->direction = DEVDIR_OUT_BIN;
				ndev->address = xstrcpy(simulator->cooler_address);
				ndev->description = xstrcpy((char *)"Simulated cooler");
				ndev->present = simulator->cooler_present;
				break;
		    case 5:	ndev->direction = DEVDIR_IN_ANALOG;
				ndev->address = xstrcpy(simulator->room_humaddress);
				ndev->description = xstrcpy((char *)"Simulated room humidity");
				ndev->present = DEVPRESENT_YES;
				break;
		    case 6:	ndev->direction = DEVDIR_IN_ANALOG;
				ndev->address = xstrcpy(simulator->chiller_address);
				ndev->description = xstrcpy((char *)"Simulated Chiller temperature");
				ndev->present = simulator->chiller_present;
				break;
		    case 7:	ndev->direction = DEVDIR_OUT_BIN;
				ndev->address = xstrcpy(simulator->fan_address);
				ndev->description = xstrcpy((char *)"Simulated fan");
				ndev->present = simulator->fan_present;
				break;
		    case 8:	ndev->direction = DEVDIR_OUT_BIN;
				ndev->address = xstrcpy(simulator->light_address);
				ndev->description = xstrcpy((char *)"Simulated light");
				ndev->present = simulator->light_present;
				break;
		    case 9:	ndev->direction = DEVDIR_IN_ANALOG;
				ndev->address = xstrcpy(simulator->beer_address2);
				ndev->description = xstrcpy((char *)"Simulated beer temperature (alt)");
				ndev->present = simulator->beer_present2;
				break;
		}

		syslog(LOG_NOTICE, "New Simulator device %s, subdevice %d", ndev->address, ndev->subdevice);

		if (Config.devices == NULL) {
		    Config.devices = ndev;
		} else {
		    for (device = Config.devices; device; device = device->next) {
			if (device->next == NULL) {
			    device->next = ndev;
			    break;
			}
		    }
		}
    		rc++;
	    }
	}
    }
#endif

    return rc;
}



void *my_devices_loop(void *threadid)
{
    devices_list	*device;
    w1_list		*dev_w1;
#ifdef USE_SIMULATOR
    simulator_list	*simulator;
#endif
    int			found;
    time_t		now;
    bool		changed;

    my_devices_state = 1;
    syslog(LOG_NOTICE, "Thread my_devices_loop started");
    dht11_next = time(NULL);

    /*
     * Loop forever until the external shutdown variable is set.
     */
    for (;;) {

	changed = false;
    	/*
    	 * Process all devices.
    	 */
    	for (device = Config.devices; device; device = device->next) {

	    if (my_devices_shutdown)
		break;

	    switch (device->type) {
		case DEVTYPE_W1:
			/*
			 * Only tested with DS18B20 but from the kernel source this
			 * should work with all 1-wire thermometer sensors.
			 */
			if ((strncmp(device->address, (char *)"10", 2) == 0) ||
			    (strncmp(device->address, (char *)"22", 2) == 0) ||
			    (strncmp(device->address, (char *)"28", 2) == 0) ||
			    (strncmp(device->address, (char *)"3b", 2) == 0) ||
			    (strncmp(device->address, (char *)"42", 2) == 0)) {
			    found = FALSE;
			    for (dev_w1 = w1_devices; dev_w1; dev_w1 = dev_w1->next) {
				if (strcmp(device->address, dev_w1->address) == 0) {
				    found = TRUE;
				    if ((dev_w1->value == -1) || (dev_w1->value < -55000)) {
					if (device->present != DEVPRESENT_ERROR)
					    syslog(LOG_NOTICE, "sensor %s value error %d, keep %d", device->address, dev_w1->value, device->value);
					device->present = DEVPRESENT_ERROR;
					changed = true;
				    } else {
					if (device->present != DEVPRESENT_YES) {
					    syslog(LOG_NOTICE, "sensor %s value ok %d", device->address, dev_w1->value);
					    changed = true;
					    device->present = DEVPRESENT_YES;
					}
					if (device->value != dev_w1->value) {
					    changed = true;
					    device->value = dev_w1->value;
					    device->timestamp = time(NULL);
					}
				    }
				}
			    }

			    if (found == FALSE) {
				if (device->present != DEVPRESENT_NO) {
				    syslog(LOG_NOTICE, "sensor %s is missing", device->address);
//				    pthread_mutex_lock(&mutexes[LOCK_DEVICES]);
				    device->present = DEVPRESENT_NO;
				    changed = true;
//				    pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
				}
			    }

			} /* if temperature sensor */

			break;

		case DEVTYPE_DHT:
			/*
			 * Don't read these to often, 2 seconds minimum delay.
			 * Delay 30 seconds if last update was successfull,
			 * else delay 2 seconds.
			 */
			now = time(NULL);
			if ((int)(now >= dht11_next)) {
			    if (device->subdevice == 0) {
				/* Read once during subdevice 0 */
			    	dht11Read(device->address);
//				pthread_mutex_lock(&mutexes[LOCK_DEVICES]);
				device->present = dht11_state;
			    	if (dht11_state == DEVPRESENT_YES) {
				    if (device->value != dht11_temperature) {
					changed = true;
				    	device->value = dht11_temperature;
				    	device->timestamp = time(NULL);
				    }
			    	}
//				pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
			    } else if (device->subdevice == 1) {
				/* Data already present, valid or not. */
//				pthread_mutex_lock(&mutexes[LOCK_DEVICES]);
				device->present = dht11_state;
			    	if (dht11_state == DEVPRESENT_YES) {
				    if (device->value != dht11_humidity) {
					changed = true;
				    	device->value = dht11_humidity;
				    	device->timestamp = time(NULL);
				    }
				    dht11_next = now + 29;
			    	} else {
				    dht11_next = now + 1;
				}
//				pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
			    }
			}
			break;

#ifdef HAVE_WIRINGPI_H
		case DEVTYPE_GPIO:
			if (device->direction == DEVDIR_IN_BIN) {
//			    pthread_mutex_lock(&mutexes[LOCK_DEVICES]);
			    if (device->value != digitalRead(device->gpiopin)) {
				changed = true;
			    	device->value = digitalRead(device->gpiopin);
			    	device->offset = 0;
			    	device->timestamp = time(NULL);
			    }
//			    pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
			}
			break;

#endif
#ifdef USE_SIMULATOR
		case DEVTYPE_SIM:
//			pthread_mutex_lock(&mutexes[LOCK_DEVICES]);
			if (Config.simulators) {
			    int		val;

			    simulator = Config.simulators;
			    if (device->subdevice == 0) {
				val = (int)((int)(simulator->room_temperature * 1000) / 500) * 500;
				if (device->value != val) {
			    	    device->value = val;
			    	    device->timestamp = time(NULL);
				    changed = true;
				}
			    } else if (device->subdevice == 1) {
				val = (int)((int)(simulator->air_temperature * 1000) / 62.5) * 62.5;
				if (device->value != val) {
			    	    device->value = val;
			    	    device->timestamp = time(NULL);
				    changed = true;
				}
			    } else if (device->subdevice == 2) {
				val = (int)((int)(simulator->beer_temperature * 1000) / 62.5) * 62.5;
				if (device->value != val) {
			    	    device->value = val;
			    	    device->timestamp = time(NULL);
				    changed = true;
				}
			    } else if (device->subdevice == 5) {
				val = (int)((int)(simulator->room_humidity * 1000) / 500) * 500;
				if (device->value != val) {
				    device->value = val;
				    device->timestamp = time(NULL);
				    changed = true;
				}
			    } else if (device->subdevice == 6) {
				val = (int)((int)(simulator->chiller_temperature * 1000) / 62.5) * 62.5;
				if (device->value != val) {
				    device->value = val;
				    device->timestamp = time(NULL);
				    changed = true;
				}
			    }
			}
//			pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
			break;
#endif
		default:
			break;
	    }

	    if (my_devices_shutdown)
		break;
	    /*
	     * Delay a bit after procesing a device.
	     */
	    mDelay(10);
	}
	if (my_devices_shutdown)
	    break;
	/*
	 * Delay a bit after all devices
	 */
	mDelay(50);
	if (changed) {
	    devices_ws();
	}
	mDelay(50);
    }

    syslog(LOG_NOTICE, "Thread my_devices_loop stopped");
    my_devices_state = 0;
    return 0;
}


mercurial