#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
#include <signal.h>
#include <syslog.h>
#include <sys/ioctl.h>


// from ds1620.c or therm.h
#define CMD_SET_THERMOSTATE	0x53
#define CMD_GET_THERMOSTATE	0x54
#define CMD_GET_STATUS		0x56
#define CMD_GET_TEMPERATURE	0x57
#define CMD_SET_THERMOSTATE2	0x58
#define CMD_GET_THERMOSTATE2	0x59
#define CMD_GET_TEMPERATURE2	0x5a
#define CMD_GET_FAN		0x5b
#define CMD_SET_FAN		0x5c

struct therm {
	int hi;
	int lo;
};
// from ds1620.c


/*
 * To keep the fan from continuously cycling on/off, we turn the fan
 * on at the lo speed setting, but turn it off at lo speed - a
 * delta. This define controls the delta. For example HI = 50, LO =
 * 48, DELTA = 2. Fan will go ON at 48 and OFF at 45 (lo - delta - 1).
 */
#define OFF_DELTA			4


// "Wed Jun 30 21:49:08 1993\n" => "Jun 30 21:49:08"
char *timestamp(void)
{
  static char str[30];
  time_t now = time(NULL);

  strcpy(str, ctime(&now));
  str[19] = '\0';
  return &str[4];
}


void fan_ctrl(int fd, int on)
{
  syslog(LOG_INFO, "%s fan %s\n", timestamp(), on ? "on" : "off");

  ioctl(fd, CMD_SET_FAN, &on);

  // Force the fan on
  if(on) {
    struct therm old, new = { 1, 0 };

    ioctl(fd, CMD_GET_THERMOSTATE2, &old);
    ioctl(fd, CMD_SET_THERMOSTATE2, &new);
    sleep(2);
    ioctl(fd, CMD_SET_THERMOSTATE2, &old);
  }
}


void turnon(int signum)
{ // Always leave fan on!
  syslog(LOG_INFO, "Exiting %s signal %d\n", timestamp(), signum);
  // fan_ctrl function does not work here (sleep?)
  system("fan_ctrl on");
  closelog();
  exit(signum);
}


void fan(int fd, int verbose);


int main(int argc, char *argv[])
{
  int fd;
  int verbose;

  if((fd = open("/dev/temperature", O_RDONLY)) == 0) {
    perror("/dev/temperature");
    exit(1);
  }

  verbose = argc > 1 && strcmp(argv[1], "-v") == 0;

  if(verbose) {
    char host[20];
    int len;

    strcpy(host, "/tmp/temp-");
    len = strlen(host);
    if(gethostname(host + len, sizeof(host) - len)) {
      perror("gethostname");
      exit(1);
    }

    if((verbose = open(host, O_RDWR | O_CREAT, 0644)) == -1) {
      perror(host);
      exit(1);
    }
  }

  if(fork() == 0) {
    // this is the daemon
    fan(fd, verbose);
  }

  if(verbose) close(verbose);

  exit(0); // kill the parent
}


void fan(int fd, int verbose)
{
  int temp, lasttemp = 0, fan;
  int offdelta = 0;
  struct therm therm;

  signal(SIGINT,  turnon);
  signal(SIGHUP,  turnon);
  signal(SIGTERM, turnon);

  openlog("fan", LOG_CONS, LOG_DAEMON);

  syslog(LOG_NOTICE, "Started %s PID %d\n", timestamp(), getpid());

  /*
   * If you reset the system during a fan start, the hi/lo will
   * be left at 1/0. Reset it to a sane value.
   */
  ioctl(fd, CMD_GET_THERMOSTATE2, &therm);
  if(therm.hi == 1) {
    syslog(LOG_ERR, "Invalid hi/lo %d/%d. Reseting to defaults.\n",
	   therm.hi, therm.lo);
    therm.hi = 50;
    therm.lo = 48;
    ioctl(fd, CMD_SET_THERMOSTATE2, &therm);
  }

  /*
   * We read the hi/lo and fan states every iteration in case someone
   * has called `set_therm' or `fan_ctrl'.
 	 */
  while(1) {
    ioctl(fd, CMD_GET_TEMPERATURE, &temp);
    ioctl(fd, CMD_GET_THERMOSTATE2, &therm);
    ioctl(fd, CMD_GET_FAN, &fan);

    if(temp != lasttemp) {
      lasttemp = temp;
			
      if(verbose) {
	lseek(verbose, 0, SEEK_SET);
	write(verbose, &temp, sizeof(temp));
	syslog(LOG_NOTICE, "%s lo %2d fan %d temp %d\n",
	       timestamp(), therm.lo, fan, temp);
      }
    }

    if(fan) {
      if(temp < (therm.lo - offdelta))
	fan_ctrl(fd, 0);
      offdelta = OFF_DELTA;
    } else if(temp >= therm.lo)
      fan_ctrl(fd, 1);

    sleep(15);
  }
}


/*
 * Local Variables:
 * compile-command: "cc -O3 -Wall fan.c -o fan"
 * End:
 */

