+bin/heat,+bin/usbrelay
[nethome.git] / bin / heat
diff --git a/bin/heat b/bin/heat
new file mode 100755 (executable)
index 0000000..a3ac277
--- /dev/null
+++ b/bin/heat
@@ -0,0 +1,153 @@
+#! /usr/bin/perl
+# * * * * * nice -n20 /root/bin/heat -s
+use strict;
+use warnings;
+$|=1;
+sub readfile($) {
+  my($fname)=@_;
+  local *F;
+  if (!open F,$fname) {
+    warn "$fname: $!";
+    return undef;
+  }
+  my $F=do { local $/; <F>; };
+  defined $F or die "read $fname: $!";
+  close F or die "read-close $fname: $!";
+  return $F;
+}
+sub writefile {
+  my($fname,$content)=@_;
+  local *F;
+  open F,">$fname" or die "$fname: $!";
+  print F $content or die "write $fname: $!";
+  close F or die "write-close $fname: $!";
+}
+sub spawn($) {
+  my($cmd)=@_;
+  $cmd.=" >&2";
+  system $cmd or return 1;
+  warn "$cmd: $!";
+  return 0;
+}
+my($trash,$min,$hour)=localtime;
+my $minutes=$hour*60+$min;
+if (($ARGV[0]||"")=~/^[@]0?(\d+):0?(\d+)$/) {
+  $minutes=$1*60+$2;
+  shift;
+}
+my $schedulefile="/root/heat.schedule";
+my $statefile="/root/heat.state";
+my $resetfile="/root/heat.reset";
+my $usbrelay="/root/bin/usbrelay";
+my $schedule=readfile $schedulefile;
+my $finishline;
+my %schedule;
+while ($schedule=~s/^(0?(\d+):0?(\d+)) ([01s])\n//) {
+  my $tm=$2*60+$3;
+  warn "$schedulefile set twice for: $1" if defined $schedule{$tm};
+  $schedule{$tm-24*60}="$1 $4\n";
+  $schedule{$tm      }="$1 $4\n";
+  $schedule{$tm+24*60}="$1 $4\n";
+}
+warn "$schedulefile garbage: $schedule" if $schedule ne "";
+sub schedulenext($) {
+  my($now)=@_;
+  my($bestprev,$prev,$bestnext,$next);
+  for my $found (keys(%schedule)) {
+    if ($found<=$now) {
+      next if defined $bestprev&&$bestprev>$found;
+      $bestprev=$found;
+      $prev=$schedule{$found};
+    } else {
+      next if defined $bestnext&&$bestnext<$found;
+      $bestnext=$found;
+      $next=$schedule{$found};
+    }
+  }
+  die "No bestprev" if !defined $bestprev;
+  die "No bestnext" if !defined $bestnext;
+  return [$prev,$next];
+}
+$finishline=schedulenext $minutes;
+my $silent=shift if ($ARGV[0]||"") eq "-s";
+sub info($) {
+  my($s)=@_;
+  print $s if !$silent;
+}
+sub finish() {
+  info join("",@$finishline) if defined $finishline;
+  exit 0;
+}
+my $reset=readfile $resetfile if -e $resetfile;
+my($resetminutes,$resetstate);
+sub resetread() {
+  $reset=~/^0?(\d+):0?(\d+) ([01s]|reset)\n$/ or warn "Invalid $resetfile: $reset";
+  $resetminutes=$1*60+$2;
+  $resetstate=($3 eq "reset"?undef:$3);
+}
+if (defined $reset) {
+  resetread;
+  if ($resetminutes==$minutes) {
+    unlink $resetfile or die "$resetfile: $!";
+    $reset=$resetminutes=undef;
+  }
+}
+sub finishlinereset() {
+  $finishline=schedulenext $resetminutes;
+  splice @$finishline,1,0,$reset;
+}
+finishlinereset if defined $reset;
+my $state=readfile $statefile;
+chomp $state;
+info $state;
+my $newstate=shift;
+$newstate=$resetstate if !defined $newstate&&defined $resetstate&&!defined $resetminutes;
+sub printminutes($) {
+  my($m)=@_;
+  return sprintf "%02d:%02d",int($m/60),$m%60;
+}
+if ($silent) {
+  if (!defined $newstate&&!defined $reset) {
+    my $prev=schedulenext($minutes)->[0];
+    $newstate=$prev if $prev=~s{^@[ printminutes $minutes ] (.)\n$}{$1} or $prev=undef;
+  }
+  $newstate=$state if !defined $newstate;
+}
+if (!defined $newstate) {
+  info "\n";
+  finish;
+}
+die "state!={s|0|1}" if $newstate!~/^[s01]$/;
+sub setstate() {
+  info "->$newstate";
+  my $both={"s"=>[0,0],"0"=>[1,0],"1"=>[1,1]}->{$newstate};
+  $state eq $newstate or unlink $statefile or warn "$statefile: $!";
+  (    spawn "$usbrelay 1 ".$both->[0]
+   and spawn "$usbrelay 2 ".$both->[1])
+  or do { unlink $statefile; die "usbrelay error"; };
+  $state eq $newstate or writefile $statefile,"$newstate\n";
+  info "\n";
+}
+my $newreset=shift;
+die "Excessive args: ".join(" ",@ARGV) if @ARGV;
+if (!defined $newreset) {
+  setstate;
+  if (!$silent&&defined $reset) {
+    unlink $resetfile or die "$resetfile: $!";
+    $finishline=schedulenext $minutes;
+  }
+  finish;
+}
+$reset=$newreset;
+if ($reset=~/^\d+$/) {
+  $resetminutes=$reset+$minutes;
+  $reset=printminutes($resetminutes)." reset\n";
+  setstate;
+} else {
+  $reset.=" $newstate\n";
+  resetread;
+  info "\n";
+}
+writefile $resetfile,$reset;
+finishlinereset;
+finish;