changeset 96:27a685ce2e5d

more laundry web support Ignore-this: c7313465fbd93fa902b909f9128c2ad
author drewp@bigasterisk.com
date Sat, 31 Aug 2013 10:33:50 -0700
parents 5907eeb9a630
children bef1c3e14eaf
files service/laundry/index.html service/laundry/laundry.go service/laundry/static/gui.js
diffstat 3 files changed, 93 insertions(+), 16 deletions(-) [+]
line wrap: on
line diff
--- a/service/laundry/index.html	Mon Aug 26 22:11:29 2013 -0700
+++ b/service/laundry/index.html	Sat Aug 31 10:33:50 2013 -0700
@@ -2,7 +2,7 @@
 <html lang="en" ng-app>
   <head>
     <title>laundry pi</title>
-    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0rc1/angular.min.js"></script>
+    <script src="static/angular.min.js"></script>
     <script src="static/gui.js"></script>
   </head>
   <body ng-controller="Ctrl">
@@ -12,20 +12,23 @@
         current <a href="status">/status</a>
         (as an rdf <a href="graph">graph</a>)
       </div>
+      <div><button ng-click="refresh()">refresh</button></div>
+      <h2>Inputs</h2>
       <div>motion: {{status.motion}}</div>
       <div>switch1: {{status.switch1}}</div>
       <div>switch2: {{status.switch2}}</div>
       <div>switch3: {{status.switch3}}</div>
       <div>doorClosed: {{status.doorClosed}}</div>
-      <hr>
+      <h2>Outputs</h2>
       <div>
         orange led: {{status.led}}
         <button ng-click="setLed('on')">on</button>
         <button ng-click="setLed('off')">off</button>
       </div>
-      <div>strike: <button>unlock for 3 seconds</button></div>
-      <div>speaker: <button>beep</button></div>
+      <div>strike: {{status.strike}} <button ng-click="temporaryUnlock()">unlock for 3 seconds</button></div>
+      <div>speaker: <button ng-click="beep()">beep</button> {{speakerStatus}}</div>
     </div>
-    <div>Status: {{status | json}}</div>
+    <h2>Raw status</h2>
+    <div>{{status | json}}</div>
   </body>
 </html>
--- a/service/laundry/laundry.go	Mon Aug 26 22:11:29 2013 -0700
+++ b/service/laundry/laundry.go	Sat Aug 31 10:33:50 2013 -0700
@@ -5,6 +5,7 @@
 	"log"
 	"net/http"
 	"strconv"
+	"time"
 	"encoding/json"
 	"github.com/bmizerany/pat"
 	"github.com/mrmorphic/hwio"
@@ -74,18 +75,35 @@
 	if err := hwio.PinMode(pins.InSwitch2,		hwio.INPUT_PULLUP); err != nil { panic(err) }
 	if err := hwio.PinMode(pins.InSwitch3,		hwio.INPUT_PULLUP); err != nil { panic(err) }
 	if err := hwio.PinMode(pins.InDoorClosed,	hwio.INPUT_PULLUP); err != nil { panic(err) }
-	if err := hwio.PinMode(pins.OutLed,			hwio.OUTPUT); err != nil { panic(err) }
+	if err := hwio.PinMode(pins.OutLed,		hwio.OUTPUT); err != nil { panic(err) }
 	if err := hwio.PinMode(pins.OutSpeaker,		hwio.OUTPUT); err != nil { panic(err) }
 	if err := hwio.PinMode(pins.OutStrike,		hwio.OUTPUT); err != nil { panic(err) }
 	return pins
 }
 	
+
+func booleanBody(w http.ResponseWriter, r *http.Request) (level int, err error) {
+	body, err := ioutil.ReadAll(r.Body)
+	if err != nil {
+		panic(err)
+	}
+	level, err2 := strconv.Atoi(string(body[:]))
+	if err2 != nil {
+		http.Error(w, "body must be '0' or '1'", http.StatusBadRequest)
+		return 0, err
+	}
+	return level, nil
+}
+
 func main() {
 	pins := SetupIo()
 
 	m := pat.New()
 	
 	m.Get("/", http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
+		// this one needs to fail if the hardware is broken in
+		// any way that we can determine, though I'm not sure
+		// what that will mean on rpi
 		http.ServeFile(w, r, "index.html")
 	}));
 
@@ -100,29 +118,74 @@
 			"switch3": DigitalRead(pins.InSwitch3),
 			"doorClosed": DigitalRead(pins.InDoorClosed),
 			"led": pins.LastOutLed,
+			"strike": pins.LastOutStrike,
 		})
 	}));
-
+	
 	m.Put("/led", http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
 		body, err := ioutil.ReadAll(r.Body)
 		if err != nil {
 			panic(err)
 		}
-		level, err := strconv.Atoi(string(body[:]))
-		if err != nil {
-			http.Error(w, "body must be '0' or '1'", http.StatusBadRequest)
-			return
+		var level int
+		if string(body) == "on" {
+			level = 1
+		} else if string(body) == "off" {
+			level = 0
+		} else {
+			http.Error(w, "body must be 'on' or 'off'", http.StatusBadRequest)
+			return 
 		}
 
 		hwio.DigitalWrite(pins.OutLed, level)
 		pins.LastOutLed = level
-
 		http.Error(w, "", http.StatusAccepted)
-	}));
+	}))
 
+	setStrike := func (level int) {
+		hwio.DigitalWrite(pins.OutStrike, level)
+		pins.LastOutStrike = level
+	}
+	
+	m.Put("/strike", http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
+		level, err := booleanBody(w, r)
+		if err != nil {
+			panic(err)
+		}
+		setStrike(level)
+		http.Error(w, "", http.StatusAccepted)
+	}))
+	
+	m.Put("/strike/temporaryUnlock", http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
+		err := r.ParseForm()
+		if err != nil {
+			panic(err)
+		}
+		seconds, err2 := strconv.ParseFloat(string(r.Form["seconds"][0]), 32)
+		if err2 != nil {
+			http.Error(w, "seconds must be a float", http.StatusBadRequest)
+			return
+		}
+
+		// This is not correctly reentrant. There should be a
+		// stack of temporary effects that unpop correctly,
+		// and status should show you any running effects.
+		setStrike(1)
+		go func() {
+			time.Sleep(time.Duration(seconds * float64(time.Second)))
+			setStrike(0)
+		}()
+		http.Error(w, "", http.StatusAccepted)
+	}))
+
+	m.Put("/speaker/beep", http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
+		// queue a beep
+		http.Error(w, "", http.StatusAccepted)
+	}))
+	
 	http.Handle("/", m)
-	log.Printf("Listening on port 8080")
-	err := http.ListenAndServe(":8080", nil)
+	log.Printf("Listening on port 8081")
+	err := http.ListenAndServe(":8081", nil)
 	if err != nil {
 		log.Fatal("ListenAndServe: ", err)
 	}
--- a/service/laundry/static/gui.js	Mon Aug 26 22:11:29 2013 -0700
+++ b/service/laundry/static/gui.js	Sat Aug 31 10:33:50 2013 -0700
@@ -11,6 +11,17 @@
         $http.put("led", value).succeed(function () {
             refresh();
         });
+    };
+    $scope.temporaryUnlock = function () {
+        var seconds = 3;
+        $http.put("strike/temporaryUnlock", {seconds: seconds}).succeed(function () {
+            refresh();
+            setTimeout(function () { refresh(); }, (seconds + .1) * 1000);
+        });
+    };
+    $scope.beep = function () {
+        $http.put("speaker/beep").succeed(function () {
+            $scope.speakerStatus = "sent at " + new Date();
+        });
     }
-                                        
 }