Mercurial > code > home > repos > homeauto
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(); + }); } - }