index.cgi (5618B)
1 #!/bin/bash 2 3 source delta_t.sh 4 5 cat <<-EOF 6 Content-Type: text/html 7 8 <!DOCTYPE html> 9 <HTML> 10 <head> 11 <title>LaundrySorcery</title> 12 <meta charset="UTF-8"/> 13 <style type="text/CSS"> 14 body{ 15 background-color:#111111; 16 color:silver; 17 font-family:sans; 18 } 19 #status{ 20 text-align:center; 21 font-size:x-large; 22 } 23 #status.off .guess{ 24 display:none; 25 } 26 .guess{ 27 font-size:large; 28 } 29 #power{ 30 font-size:xx-large; 31 } 32 #power img{ 33 width:5em; 34 max-height:50vh; 35 } 36 #footer{ 37 text-align:right; 38 } 39 a{ 40 color:gold; 41 } 42 a:visited{ 43 color:brown; 44 } 45 </style> 46 </head> 47 <body> 48 <div id="footer"> 49 <a href="details.html">Details</a> 50 </div> 51 <div id="status"> 52 <div id="power"><img src="off.svg" /></div> 53 <div id="delta_t"></div> 54 <div id="guessTime" class="guess"></div> 55 <div id="guessProg" class="guess"></div> 56 </div> 57 <script type="text/JavaScript"> 58 <!-- 59 do_notify=false; 60 delta_t=${RTIME}; 61 function updateDisplay(){ 62 if(!document.hasFocus()) return; 63 var g=document.getElementById("guessTime"); 64 var g2=document.getElementById("guessProg"); 65 var dt=document.getElementById("delta_t"); 66 var st=document.getElementById("status"); 67 var txt=""; 68 var classn=""; 69 if(delta_t < 0){ 70 txt="since "+timeSince(-delta_t); 71 classn="off"; 72 } 73 else{ 74 c=guessCluster(delta_t); 75 txt="since "+timeSince(delta_t); 76 g.textContent="The crystal ball guesses: "+timeSince(Math.round(guessTimeLeft(delta_t)))+" left"; 77 g2.textContent="Guessed program lasts "+timeSince(Math.round(c.mean)); 78 classn="on"; 79 } 80 dt.textContent=txt; 81 st.setAttribute("class",classn); 82 st.getElementsByTagName("img")[0].setAttribute("src",classn+".svg"); 83 } 84 85 function updateDeltaT(dt){ 86 if(do_notify){ 87 if(delta_t<0 && dt >= 0){ 88 new Notification("Washing machine has been turned on"); 89 } 90 else if(delta_t >= 0 && dt < 0){ 91 new Notification("Washing machine is now off"); 92 } 93 } 94 delta_t=dt; 95 } 96 97 setInterval(function(){ if(delta_t>=0){delta_t++}else{delta_t--}; updateDisplay();}, 1000); 98 setInterval(function(){ readTextFile("print_delta_t.cgi", function(rawFile){updateDeltaT(parseInt(rawFile.responseText));});}, 20*1000); 99 readTextFile("clusters", function(rawFile){parseClusters(rawFile.responseText);}); 100 clusters=[]; 101 function Cluster(apriori, mean, variance){ 102 this.apriori=apriori; 103 this.mean=mean; 104 this.variance=variance; 105 } 106 function parseClusters(txt){ 107 var lines=txt.split("\n"); 108 clusters=[]; 109 var sum=0; 110 for(var i=0; i<lines.length; i++){ 111 var line=lines[i]; 112 if(line.length==0) continue; 113 var spl=line.split("\t"); 114 clusters.push(new Cluster(parseInt(spl[0]),parseFloat(spl[1]),parseFloat(spl[2]))); 115 sum+=parseInt(spl[0]); 116 } 117 for(var i=0; i<clusters.length; i++){ 118 clusters[i].apriori/=sum; 119 } 120 } 121 122 function guessCluster(elapsed){ 123 var max=-Infinity; 124 var maxc={}; 125 for(var i=0; i<clusters.length; i++){ 126 var cluster=clusters[i] 127 var prob=cluster.apriori*(1-cluster.cdf(elapsed)); 128 if(prob>max){ 129 maxc=cluster; 130 max=prob; 131 } 132 } 133 return maxc; 134 } 135 136 function guessTimeLeft(elapsed){ 137 return guessTime(elapsed)-elapsed; 138 } 139 140 function guessTime(elapsed){ 141 var expect=0; 142 var obsprior=observationPrior(elapsed); 143 for(var i=0; i<clusters.length; i++){ 144 cluster=clusters[i]; 145 var prior=(1-cluster.cdf(elapsed))*cluster.apriori/obsprior; 146 expect+=prior*Math.max(cluster.mean,elapsed); 147 } 148 return expect; 149 } 150 151 function observationPrior(elapsed){ 152 var prior=0; 153 for(var i=0; i<clusters.length; i++){ 154 cluster=clusters[i]; 155 prior+=(1-cluster.cdf(elapsed))*cluster.apriori 156 } 157 return prior; 158 } 159 160 Cluster.prototype.cdf=function(to) { 161 variance=this.variance; 162 mean=this.mean; 163 var z = (to-mean)/Math.sqrt(2*variance); 164 var t = 1/(1+0.3275911*Math.abs(z)); 165 var a1 = 0.254829592; 166 var a2 = -0.284496736; 167 var a3 = 1.421413741; 168 var a4 = -1.453152027; 169 var a5 = 1.061405429; 170 var erf = 1-(((((a5*t + a4)*t) + a3)*t + a2)*t + a1)*t*Math.exp(-z*z); 171 var sign = 1; 172 if(z < 0){ 173 sign = -1; 174 } 175 return (1/2)*(1+sign*erf); 176 } 177 178 Cluster.prototype.pdf=function(to){ 179 mean=this.mean; 180 variance=this.variance; 181 return 1/(Math.sqrt(2*pi*variance))*Math.exp(-((to-mean)^2/(4*variance))); 182 } 183 function readTextFile(file, func){ 184 var rawFile = new XMLHttpRequest(); 185 rawFile.open("GET", file, true); 186 rawFile.onreadystatechange = function () 187 { 188 if(rawFile.readyState === 4) 189 { 190 if(rawFile.status === 200 || rawFile.status == 0) 191 { 192 func(rawFile); 193 } 194 } 195 } 196 rawFile.send(null); 197 } 198 function timeSince(delta_t) { 199 var seconds = delta_t; 200 var timeValues=[ 201 [60, "seconds"], 202 [60, "minutes"], 203 [24, "hours"], 204 [7, "days"], 205 [30.25, "weeks"] 206 ]; 207 var str=""; 208 for(i=0; i<timeValues.length; i++){ 209 var timeValue=timeValues[i]; 210 if(delta_t > 0){ 211 str=(delta_t % timeValue[0]) + " " + timeValue[1]+", "+str; 212 delta_t=Math.floor(delta_t/timeValue[0]); 213 } 214 } 215 return str.substr(0,str.length-2); 216 } 217 updateDisplay(); 218 219 if("Notification" in window){ 220 if(Notification.permission==="granted"){ 221 do_notify=true 222 } 223 else if(Notification.permission!=="denied"){ 224 var res=Notification.requestPermission(function(res){do_notify=(res==="granted");}); 225 } 226 } 227 --> 228 </script> 229 </body> 230 </HTML> 231 EOF