#!/usr/bin/perl -w

# This program downloads records from your Sleeptracker Pro
# and displays them as text, xml, or csv 
#
# Sleeptracker and Sleeptracker Pro are trademarks of
#
#      Innovative Sleep Solutions
#      2870 Peachtree Road #140
#      Atlanta, GA 30305
#
# This program is NOT developed or supported by Innovative Sleep Solutions
#
# This program 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. 
#
# You can purchase your Sleeptracker or Sleeptracker Pro from:
#
# http://www.sleeptracker.com
# http://www.sleeptracker.de
# http://www.sleeptracker.fr
# ... and some other stores
#

# Chagelog:
# --------------
# 09.12.2007: fixed bug in xml output tag <alarmTime>
# 10.12.2007: fixed wrong cecksum calculation
# 11.12.2007: added browser output
#
#
#


use Device::SerialPort;
use Time::Local;
use strict;


my $browser="firefox"; # which browser to call
my $uploadURL="http://www.sleeptracker.net/import.php"; # which upload URL to call

my $numArgs = $#ARGV + 1;
my $ttyPath="";
my $format="browser"; #set default

if($numArgs > 1){
	$format=$ARGV[0];
	$ttyPath=$ARGV[1];
} elsif ($numArgs == 1){
	$ttyPath=$ARGV[0];
} else {
	help();
	exit 1;
}

if (not -w $ttyPath) {
	print STDERR "Can't access Sleeptracker at device '$ttyPath'\n";
	exit 1;
}

print STDERR "Reading Sleeptracker data from $ttyPath\n\n";

my $serial = Device::SerialPort->new("$ttyPath");
$serial->baudrate(2400);
$serial->databits(8);
$serial->purge_all();
$serial->rts_active(0);
$serial->dtr_active(1);
 # Send request
$serial->write("V");
 # Wait one second
select(undef, undef, undef, 1);
 # Read response
my($count, $data) = $serial->read(250); # goog for more than 70 awake moments

if($count<15){
	print STDERR "Error while reading from Sleeptracker!\nWatch showing DATA screen?\n";
	exit 1;
}

my @stBytes=();
my $cnt=0;
my $crc=0;
foreach my $byu (split (//, $data)) {
    $cnt++;
    # for debug
    #printf "%03d: %02x = %d \n",$cnt,ord($byu),ord($byu);
    push @stBytes,ord($byu);
    if($cnt>1 && $cnt<$count-1){
    	$crc+=ord($byu);
    }
}
#printf "%x=%d\n\n",$crc,$crc;
$crc=$crc%256;

# compare checksum
if($crc!=$stBytes[$count-2]){
	print STDERR "Checksum Error while reading. $crc!=$stBytes[$count-2]";
	exit 1;
}


# calculations and data formating

my $pos=1;

my (undef,undef,undef,$mday,$mon,$year,undef,undef,undef)=localtime();
my $jahr=$year+1900;
my $monat=$mon+1;
my $tag=$mday;

my $stTag=$stBytes[$pos+1];
my $stMonat=$stBytes[$pos+0];
my $stJahr=$jahr;

if($stMonat>$monat || ($stMonat==$monat && $stTag>$tag) ){
	$stJahr=$jahr-1;
}

my $datum = sprintf "%02d.%02d.%4d",$stTag,$stMonat,$stJahr; 
$pos+=2;

#$unknown=$stBytes[$pos];
$pos+=1;

my $window = sprintf "%2d",$stBytes[$pos];
$pos+=1;

my $toBed= sprintf "%02d:%02d",$stBytes[$pos+0],$stBytes[$pos+1];
my $toBedTS = timelocal(0,$stBytes[$pos+1],$stBytes[$pos],$tag,$monat-1,$jahr);
$pos+=2;

my $alarm =sprintf "%02d:%02d",$stBytes[$pos],$stBytes[$pos+1];
$pos+=2;

my $cntData=$stBytes[$pos];
$pos+=1;

my @almostAwake=();
my @almostAwakePst=();

my $lastAwake=$toBedTS;
my $thisAwake=$lastAwake;
my $alltimes="";
for(my $i=0;$i<$cntData;$i++){
	my $aaw=sprintf "%02d:%02d:%02d", $stBytes[$pos+($i*3)+0],$stBytes[$pos+($i*3)+1],$stBytes[$pos+($i*3)+2];
        my $aawShort=sprintf "%02d:%02d", $stBytes[$pos+($i*3)+0],$stBytes[$pos+($i*3)+1];
        $alltimes.=$aawShort.",";
	push @almostAwake,$aaw;
	$thisAwake = timelocal($stBytes[$pos+($i*3)+2],$stBytes[$pos+($i*3)+1],$stBytes[$pos+($i*3)+0],$tag,$monat-1,$jahr);
	if($thisAwake<$lastAwake){
		$thisAwake+=(24*60*60);	# day has changed
	}
	my $slept=sprintf "%d",($thisAwake-$lastAwake)/60;
	$lastAwake=$thisAwake;
	push @almostAwakePst, $slept; #period sleeping time in sec
}
$pos+=($cntData*3);

if($thisAwake<$toBedTS){
	$thisAwake+=(24*60*60);	# day has changed
}

my $dataAVal=($stBytes[$pos+1]*0xff)+$stBytes[$pos];
my $dataASec=$dataAVal%60;
my $dataA=sprintf "%d:%2d",(($dataAVal-$dataASec)/60),$dataASec ;
my $dataACalc=sprintf "%d:%2d",(($thisAwake-$toBedTS)/$cntData)/60,(($thisAwake-$toBedTS)/$cntData)%60;

# output Browser
if($format eq "browser"){
        my $URLParam=sprintf "a=%s&w=%s&t=%s&dt=%s&da=%s",$alarm,$window,$toBed,$alltimes,$dataA;        
        my $callFirefox=sprintf "%s \"%s?%s\" &",$browser,$uploadURL,$URLParam;
        print "\n";
        print  $callFirefox;
        print "\n";
        my $ret=system($callFirefox);  
}

# output Text
if($format eq "text"){
	print "Date:                   $datum\n";
	print "To Bed:                 $toBed\n";
	print "Alarm Time:             $alarm\n";
	printf "Effective Alarm Time:   %s\n",$almostAwake[@almostAwake-1];
	print "Window:                 $window min\n";
	print "Data A (Clock):         $dataA min\n";
	print "Data A (Calculated):    $dataACalc min\n\n";

	print "Awake moments ($cntData): \n";

	my $aawCnt=0;       
	foreach my $awm (@almostAwake) {
		printf " Data %2d: %s (slept: %3d min)\n",$aawCnt+1,$awm,$almostAwakePst[$aawCnt];               
		$aawCnt++;
	}
	print "\n";
             
}

# output CSV
if($format eq "csv"){
	printf "%s;%s;%s;%s;%s;%s;%s;%s",$datum,$toBed,$alarm,$almostAwake[@almostAwake-1],$window,$dataA,$dataACalc,$cntData;
	my $aawCnt=0;
	foreach my $awm (@almostAwake) {
		printf ";%s",$awm;
		$aawCnt++;
	}
	print ";\n";
}

# output XML
if($format eq "xml"){
	print "<sleepRecord date=\"$datum\">\n";
	print "   <toBed>$toBed</toBed>\n";
	print "   <alarmTime>$alarm</alarmTime>\n";
	printf "   <effectiveAlarmTime>%s</effectiveAlarmTime>\n",$almostAwake[@almostAwake-1];
	print "   <window>$window</window>\n";
	print "   <dataA clc=\"$dataACalc\">$dataA</dataA>\n";
	print "   <awakeMoments cnt=\"$cntData\">\n";

	my $aawCnt=0;
	foreach my $awm (@almostAwake) {
		printf "      <data count=\"%d\">%s</data>\n",$aawCnt+1,$awm,$almostAwakePst[$aawCnt];
		$aawCnt++;
	}
	print "   </awakeMoments>\n";
	print "</sleepRecord>\n";
}



sub help {
	print STDERR "Usage: sleeptracker [format] device\n";
	print STDERR "format:     browser(default), text, csv, xml\n";
}
