+bin/heat,+bin/usbrelay
[nethome.git] / bin / heat
1 #! /usr/bin/perl
2 # * * * * * nice -n20 /root/bin/heat -s
3 use strict;
4 use warnings;
5 $|=1;
6 sub readfile($) {
7   my($fname)=@_;
8   local *F;
9   if (!open F,$fname) {
10     warn "$fname: $!";
11     return undef;
12   }
13   my $F=do { local $/; <F>; };
14   defined $F or die "read $fname: $!";
15   close F or die "read-close $fname: $!";
16   return $F;
17 }
18 sub writefile {
19   my($fname,$content)=@_;
20   local *F;
21   open F,">$fname" or die "$fname: $!";
22   print F $content or die "write $fname: $!";
23   close F or die "write-close $fname: $!";
24 }
25 sub spawn($) {
26   my($cmd)=@_;
27   $cmd.=" >&2";
28   system $cmd or return 1;
29   warn "$cmd: $!";
30   return 0;
31 }
32 my($trash,$min,$hour)=localtime;
33 my $minutes=$hour*60+$min;
34 if (($ARGV[0]||"")=~/^[@]0?(\d+):0?(\d+)$/) {
35   $minutes=$1*60+$2;
36   shift;
37 }
38 my $schedulefile="/root/heat.schedule";
39 my $statefile="/root/heat.state";
40 my $resetfile="/root/heat.reset";
41 my $usbrelay="/root/bin/usbrelay";
42 my $schedule=readfile $schedulefile;
43 my $finishline;
44 my %schedule;
45 while ($schedule=~s/^(0?(\d+):0?(\d+)) ([01s])\n//) {
46   my $tm=$2*60+$3;
47   warn "$schedulefile set twice for: $1" if defined $schedule{$tm};
48   $schedule{$tm-24*60}="$1 $4\n";
49   $schedule{$tm      }="$1 $4\n";
50   $schedule{$tm+24*60}="$1 $4\n";
51 }
52 warn "$schedulefile garbage: $schedule" if $schedule ne "";
53 sub schedulenext($) {
54   my($now)=@_;
55   my($bestprev,$prev,$bestnext,$next);
56   for my $found (keys(%schedule)) {
57     if ($found<=$now) {
58       next if defined $bestprev&&$bestprev>$found;
59       $bestprev=$found;
60       $prev=$schedule{$found};
61     } else {
62       next if defined $bestnext&&$bestnext<$found;
63       $bestnext=$found;
64       $next=$schedule{$found};
65     }
66   }
67   die "No bestprev" if !defined $bestprev;
68   die "No bestnext" if !defined $bestnext;
69   return [$prev,$next];
70 }
71 $finishline=schedulenext $minutes;
72 my $silent=shift if ($ARGV[0]||"") eq "-s";
73 sub info($) {
74   my($s)=@_;
75   print $s if !$silent;
76 }
77 sub finish() {
78   info join("",@$finishline) if defined $finishline;
79   exit 0;
80 }
81 my $reset=readfile $resetfile if -e $resetfile;
82 my($resetminutes,$resetstate);
83 sub resetread() {
84   $reset=~/^0?(\d+):0?(\d+) ([01s]|reset)\n$/ or warn "Invalid $resetfile: $reset";
85   $resetminutes=$1*60+$2;
86   $resetstate=($3 eq "reset"?undef:$3);
87 }
88 if (defined $reset) {
89   resetread;
90   if ($resetminutes==$minutes) {
91     unlink $resetfile or die "$resetfile: $!";
92     $reset=$resetminutes=undef;
93   }
94 }
95 sub finishlinereset() {
96   $finishline=schedulenext $resetminutes;
97   splice @$finishline,1,0,$reset;
98 }
99 finishlinereset if defined $reset;
100 my $state=readfile $statefile;
101 chomp $state;
102 info $state;
103 my $newstate=shift;
104 $newstate=$resetstate if !defined $newstate&&defined $resetstate&&!defined $resetminutes;
105 sub printminutes($) {
106   my($m)=@_;
107   return sprintf "%02d:%02d",int($m/60),$m%60;
108 }
109 if ($silent) {
110   if (!defined $newstate&&!defined $reset) {
111     my $prev=schedulenext($minutes)->[0];
112     $newstate=$prev if $prev=~s{^@[ printminutes $minutes ] (.)\n$}{$1} or $prev=undef;
113   }
114   $newstate=$state if !defined $newstate;
115 }
116 if (!defined $newstate) {
117   info "\n";
118   finish;
119 }
120 die "state!={s|0|1}" if $newstate!~/^[s01]$/;
121 sub setstate() {
122   info "->$newstate";
123   my $both={"s"=>[0,0],"0"=>[1,0],"1"=>[1,1]}->{$newstate};
124   $state eq $newstate or unlink $statefile or warn "$statefile: $!";
125   (    spawn "$usbrelay 1 ".$both->[0]
126    and spawn "$usbrelay 2 ".$both->[1])
127   or do { unlink $statefile; die "usbrelay error"; };
128   $state eq $newstate or writefile $statefile,"$newstate\n";
129   info "\n";
130 }
131 my $newreset=shift;
132 die "Excessive args: ".join(" ",@ARGV) if @ARGV;
133 if (!defined $newreset) {
134   setstate;
135   if (!$silent&&defined $reset) {
136     unlink $resetfile or die "$resetfile: $!";
137     $finishline=schedulenext $minutes;
138   }
139   finish;
140 }
141 $reset=$newreset;
142 if ($reset=~/^\d+$/) {
143   $resetminutes=$reset+$minutes;
144   $reset=printminutes($resetminutes)." reset\n";
145   setstate;
146 } else {
147   $reset.=" $newstate\n";
148   resetread;
149   info "\n";
150 }
151 writefile $resetfile,$reset;
152 finishlinereset;
153 finish;