Mercurial > code > home > repos > homeauto
comparison service/laundry/laundry.go @ 109:868e759aeb74
strings for all hardware values. rewrite rdf graph output. gofmt
Ignore-this: 9344993fdda111d34191c11e3cda17ec
author | drewp@bigasterisk.com |
---|---|
date | Sun, 01 Sep 2013 01:35:36 -0700 |
parents | 08eb981b6cf5 |
children | 94e26e1e0668 |
comparison
equal
deleted
inserted
replaced
108:64ad5ad17dc1 | 109:868e759aeb74 |
---|---|
1 package main | 1 package main |
2 | 2 |
3 import ( | 3 import ( |
4 "bitbucket.org/ww/goraptor" | |
4 "encoding/json" | 5 "encoding/json" |
6 "errors" | |
7 "github.com/mrmorphic/hwio" | |
8 "github.com/stretchr/goweb" | |
9 "github.com/stretchr/goweb/context" | |
5 "log" | 10 "log" |
6 "net" | 11 "net" |
7 "net/http" | 12 "net/http" |
8 "strconv" | 13 "runtime" |
9 "fmt" | |
10 "time" | 14 "time" |
11 "runtime" | |
12 "github.com/mrmorphic/hwio" | |
13 "github.com/stretchr/goweb" | |
14 "github.com/stretchr/goweb/context" | |
15 "bitbucket.org/ww/goraptor" | |
16 ) | 15 ) |
17 | 16 |
18 /* | 17 /* |
19 hwio.DebugPinMap() wrote this: | 18 hwio.DebugPinMap() wrote this: |
20 | 19 |
40 Pin 26: CE1N,GPIO7 cap:output,input,input_pullup,input_pulldown | 39 Pin 26: CE1N,GPIO7 cap:output,input,input_pullup,input_pulldown |
41 */ | 40 */ |
42 | 41 |
43 type Hardware struct { | 42 type Hardware struct { |
44 InMotion, InSwitch3, InSwitch1, InSwitch2, OutLed, OutSpeaker, InDoorClosed, OutStrike hwio.Pin | 43 InMotion, InSwitch3, InSwitch1, InSwitch2, OutLed, OutSpeaker, InDoorClosed, OutStrike hwio.Pin |
45 LastOutLed, LastOutStrike int | 44 LastOutLed, LastOutStrike string |
45 } | |
46 | |
47 func DigitalRead(p hwio.Pin) int { | |
48 v, err := hwio.DigitalRead(p) | |
49 if err != nil { | |
50 panic(err) | |
51 } | |
52 return v | |
53 } | |
54 | |
55 func (h *Hardware) GetMotion() string { | |
56 if DigitalRead(h.InMotion) == 0 { | |
57 return "motion" | |
58 } else { | |
59 return "noMotion" | |
60 } | |
61 } | |
62 | |
63 func (h *Hardware) GetDoor() string { | |
64 if DigitalRead(h.InDoorClosed) == 1 { | |
65 return "closed" | |
66 } else { | |
67 return "open" | |
68 } | |
69 } | |
70 | |
71 func (h *Hardware) GetSwitch(which string) string { | |
72 var level int | |
73 switch which { | |
74 case "1": | |
75 level = DigitalRead(h.InSwitch1) | |
76 case "2": | |
77 level = DigitalRead(h.InSwitch2) | |
78 case "3": | |
79 level = DigitalRead(h.InSwitch3) | |
80 } | |
81 if level == 0 { | |
82 return "closed" | |
83 } else { | |
84 return "open" | |
85 } | |
86 } | |
87 | |
88 func (h *Hardware) GetLed() string { | |
89 return h.LastOutLed | |
90 } | |
91 | |
92 func (h *Hardware) GetStrike() string { | |
93 return h.LastOutStrike | |
94 } | |
95 | |
96 func (h *Hardware) SetLed(state string) { | |
97 switch state { | |
98 case "on": | |
99 hwio.DigitalWrite(h.OutLed, 1) | |
100 case "off": | |
101 hwio.DigitalWrite(h.OutLed, 0) | |
102 default: | |
103 panic(errors.New("unknown state")) | |
104 } | |
105 h.LastOutLed = state | |
106 } | |
107 | |
108 func (h *Hardware) SetStrike(state string) { | |
109 switch state { | |
110 case "unlocked": | |
111 hwio.DigitalWrite(h.OutStrike, 1) | |
112 case "locked": | |
113 hwio.DigitalWrite(h.OutStrike, 0) | |
114 default: | |
115 panic(errors.New("unknown state")) | |
116 } | |
117 h.LastOutStrike = state | |
46 } | 118 } |
47 | 119 |
48 // hwio.GetPin with a panic instead of an error return | 120 // hwio.GetPin with a panic instead of an error return |
49 func GetPin(id string) hwio.Pin { | 121 func GetPin(id string) hwio.Pin { |
50 p, e := hwio.GetPin(id) | 122 p, e := hwio.GetPin(id) |
52 panic(e) | 124 panic(e) |
53 } | 125 } |
54 return p | 126 return p |
55 } | 127 } |
56 | 128 |
57 func DigitalRead(p hwio.Pin) int { | |
58 v, err := hwio.DigitalRead(p) | |
59 if err != nil { | |
60 panic(err) | |
61 } | |
62 return v | |
63 } | |
64 | |
65 func SetupIo() Hardware { | 129 func SetupIo() Hardware { |
66 // return Hardware{} | 130 // return Hardware{} |
67 pins := Hardware{ | 131 pins := Hardware{ |
68 InMotion: GetPin("GPIO2"), // pi rev2 calls it GPIO2 | 132 InMotion: GetPin("GPIO2"), // pi rev2 calls it GPIO2 |
69 InSwitch3: GetPin("GPIO3"), // pi rev2 calls it GPIO3 | 133 InSwitch3: GetPin("GPIO3"), // pi rev2 calls it GPIO3 |
70 InSwitch1: GetPin("GPIO4"), | 134 InSwitch1: GetPin("GPIO4"), |
71 InSwitch2: GetPin("GPIO17"), | 135 InSwitch2: GetPin("GPIO17"), |
72 OutLed: GetPin("GPIO27"), // pi rev2 calls it GPIO27 | 136 OutLed: GetPin("GPIO27"), // pi rev2 calls it GPIO27 |
73 OutSpeaker: GetPin("GPIO22"), | 137 OutSpeaker: GetPin("GPIO22"), |
74 InDoorClosed: GetPin("GPIO10"), | 138 InDoorClosed: GetPin("GPIO10"), |
75 OutStrike: GetPin("GPIO9"), | 139 OutStrike: GetPin("GPIO9"), |
76 } | 140 } |
77 | 141 |
78 if err := hwio.PinMode(pins.InMotion, hwio.INPUT_PULLUP); err != nil { panic(err) } | 142 if err := hwio.PinMode(pins.InMotion, hwio.INPUT_PULLUP); err != nil { |
79 if err := hwio.PinMode(pins.InSwitch1, hwio.INPUT_PULLUP); err != nil { panic(err) } | 143 panic(err) |
80 if err := hwio.PinMode(pins.InSwitch2, hwio.INPUT_PULLUP); err != nil { panic(err) } | 144 } |
81 if err := hwio.PinMode(pins.InSwitch3, hwio.INPUT_PULLUP); err != nil { panic(err) } | 145 if err := hwio.PinMode(pins.InSwitch1, hwio.INPUT_PULLUP); err != nil { |
82 if err := hwio.PinMode(pins.InDoorClosed, hwio.INPUT_PULLUP); err != nil { panic(err) } | 146 panic(err) |
83 if err := hwio.PinMode(pins.OutLed, hwio.OUTPUT); err != nil { panic(err) } | 147 } |
84 if err := hwio.PinMode(pins.OutSpeaker, hwio.OUTPUT); err != nil { panic(err) } | 148 if err := hwio.PinMode(pins.InSwitch2, hwio.INPUT_PULLUP); err != nil { |
85 if err := hwio.PinMode(pins.OutStrike, hwio.OUTPUT); err != nil { panic(err) } | 149 panic(err) |
150 } | |
151 if err := hwio.PinMode(pins.InSwitch3, hwio.INPUT_PULLUP); err != nil { | |
152 panic(err) | |
153 } | |
154 if err := hwio.PinMode(pins.InDoorClosed, hwio.INPUT_PULLDOWN); err != nil { | |
155 panic(err) | |
156 } | |
157 if err := hwio.PinMode(pins.OutLed, hwio.OUTPUT); err != nil { | |
158 panic(err) | |
159 } | |
160 if err := hwio.PinMode(pins.OutSpeaker, hwio.OUTPUT); err != nil { | |
161 panic(err) | |
162 } | |
163 if err := hwio.PinMode(pins.OutStrike, hwio.OUTPUT); err != nil { | |
164 panic(err) | |
165 } | |
166 pins.SetLed("off") | |
167 pins.SetStrike("locked") | |
86 return pins | 168 return pins |
87 } | 169 } |
88 | 170 |
89 func serializeGowebResponse( | 171 func serializeGowebResponse( |
90 c context.Context, | 172 c context.Context, |
93 serializer := goraptor.NewSerializer(syntaxName) | 175 serializer := goraptor.NewSerializer(syntaxName) |
94 defer serializer.Free() | 176 defer serializer.Free() |
95 | 177 |
96 str, err := serializer.Serialize(statements, "") | 178 str, err := serializer.Serialize(statements, "") |
97 if err != nil { | 179 if err != nil { |
98 panic(err); | 180 panic(err) |
99 } | 181 } |
100 c.HttpResponseWriter().Header().Set("Content-Type", | 182 c.HttpResponseWriter().Header().Set("Content-Type", |
101 goraptor.SerializerSyntax[syntaxName].MimeType) | 183 goraptor.SerializerSyntax[syntaxName].MimeType) |
102 return goweb.Respond.With(c, 200, []byte(str)) | 184 return goweb.Respond.With(c, 200, []byte(str)) |
103 } | 185 } |
104 | 186 |
105 func namespace(ns string) (func(string) *goraptor.Uri) { | 187 func namespace(ns string) func(string) *goraptor.Uri { |
106 return func (path string) *goraptor.Uri { | 188 return func(path string) *goraptor.Uri { |
107 var u goraptor.Uri = goraptor.Uri(ns + path) | 189 var u goraptor.Uri = goraptor.Uri(ns + path) |
108 return &u | 190 return &u |
109 } | 191 } |
110 } | 192 } |
111 | 193 |
125 panic(err) | 207 panic(err) |
126 } | 208 } |
127 return literal(string(rfc3999Time[:]), XS("dateTime")) | 209 return literal(string(rfc3999Time[:]), XS("dateTime")) |
128 } | 210 } |
129 | 211 |
130 func twoState( | |
131 graph *goraptor.Uri, | |
132 subject *goraptor.Uri, | |
133 test interface{}, | |
134 trueVal interface{}, trueObject *goraptor.Uri, | |
135 falseVal interface{}, falseObject *goraptor.Uri, | |
136 ) *goraptor.Statement { | |
137 ROOM := namespace("http://projects.bigasterisk.com/room/") | |
138 var motionState goraptor.Term | |
139 if test == trueVal { | |
140 motionState = trueObject | |
141 } else if test == falseVal { | |
142 motionState = falseObject | |
143 } else { | |
144 motionState = literal(fmt.Sprintf("%v", test), nil) | |
145 } | |
146 return &(goraptor.Statement{ | |
147 subject, ROOM("state"), motionState, graph}) | |
148 } | |
149 | |
150 func main() { | 212 func main() { |
151 pins := SetupIo() | 213 pins := SetupIo() |
152 | 214 |
153 goweb.MapStatic("/static", "static") | 215 goweb.MapStatic("/static", "static") |
154 | 216 |
155 // this one needs to fail if the hardware is broken in | 217 // this one needs to fail if the hardware is broken in |
156 // any way that we can determine, though I'm not sure | 218 // any way that we can determine, though I'm not sure |
157 // what that will mean on rpi | 219 // what that will mean on rpi |
158 goweb.MapStaticFile("/", "index.html") | 220 goweb.MapStaticFile("/", "index.html") |
159 | 221 |
160 goweb.Map("GET", "/status", func(c context.Context) error { | 222 goweb.Map("GET", "/status", func(c context.Context) error { |
161 jsonEncode := json.NewEncoder(c.HttpResponseWriter()) | 223 jsonEncode := json.NewEncoder(c.HttpResponseWriter()) |
162 jsonEncode.Encode(map[string]int{ | 224 jsonEncode.Encode(map[string]interface{}{ |
163 "motion": DigitalRead(pins.InMotion), | 225 "motion": pins.GetMotion(), |
164 "switch1": DigitalRead(pins.InSwitch1), | 226 "switch1": pins.GetSwitch("1"), |
165 "switch2": DigitalRead(pins.InSwitch2), | 227 "switch2": pins.GetSwitch("2"), |
166 "switch3": DigitalRead(pins.InSwitch3), | 228 "switch3": pins.GetSwitch("3"), |
167 "doorClosed": DigitalRead(pins.InDoorClosed), | 229 "doorClosed": pins.GetDoor(), |
168 "led": pins.LastOutLed, | 230 "led": pins.LastOutLed, |
169 "strike": pins.LastOutStrike, | 231 "strike": pins.LastOutStrike, |
170 }) | 232 }) |
171 return nil | 233 return nil |
172 }) | 234 }) |
173 | 235 |
174 goweb.Map("GET", "/graph", func(c context.Context) error { | 236 goweb.Map("GET", "/graph", func(c context.Context) error { |
175 DC := namespace("http://purl.org/dc/terms/") | 237 DC := namespace("http://purl.org/dc/terms/") |
176 ROOM := namespace("http://projects.bigasterisk.com/room/") | 238 ROOM := namespace("http://projects.bigasterisk.com/room/") |
177 | 239 |
178 statements := make(chan *goraptor.Statement, 100) | 240 statements := make(chan *goraptor.Statement, 100) |
179 | 241 |
180 graph := ROOM("laundrySensors") | 242 graph := ROOM("laundryDoor") |
181 | 243 |
182 _, thisFile, _, _ := runtime.Caller(0) | 244 _, thisFile, _, _ := runtime.Caller(0) |
183 statements <- &(goraptor.Statement{ | 245 statements <- &(goraptor.Statement{ |
184 graph, DC("creator"), literal(thisFile, nil), graph}) | 246 graph, DC("creator"), literal(thisFile, nil), graph}) |
185 statements <- &(goraptor.Statement{ | 247 statements <- &(goraptor.Statement{ |
186 graph, DC("modified"), nowLiteral(), graph}) | 248 graph, DC("modified"), nowLiteral(), graph}) |
187 | 249 |
188 statements <- twoState(graph, ROOM("laundryDoorMotion"), | 250 for subj, state := range map[*goraptor.Uri]*goraptor.Uri{ |
189 DigitalRead(pins.InMotion), | 251 ROOM("laundryDoorMotion"): ROOM(pins.GetMotion()), |
190 1, ROOM("motion"), | 252 ROOM("laundryDoorOpen"): ROOM(pins.GetDoor()), |
191 0, ROOM("noMotion")) | 253 ROOM("laundryDoorSwitch1"): ROOM(pins.GetSwitch("1")), |
192 | 254 ROOM("laundryDoorSwitch2"): ROOM(pins.GetSwitch("2")), |
193 statements <- twoState(graph, ROOM("laundryDoorOpen"), | 255 ROOM("laundryDoorSwitch3"): ROOM(pins.GetSwitch("3")), |
194 DigitalRead(pins.InDoorClosed), | 256 ROOM("laundryDoorLed"): ROOM(pins.GetLed()), |
195 1, ROOM("closed"), | 257 ROOM("laundryDoorStrike"): ROOM(pins.GetStrike()), |
196 0, ROOM("open")) | 258 } { |
197 | 259 statements <- &(goraptor.Statement{subj, ROOM("state"), state, graph}) |
198 for i, p := range map[string]hwio.Pin{ | |
199 "1": pins.InSwitch1, | |
200 "2": pins.InSwitch2, | |
201 "3": pins.InSwitch3} { | |
202 statements <- twoState( | |
203 graph, ROOM("laundryDoorSwitch" + i), | |
204 DigitalRead(p), | |
205 1, ROOM("closed"), | |
206 0, ROOM("open")) | |
207 } | 260 } |
208 | 261 |
209 statements <- twoState(graph, ROOM("laundryDoorLed"), | |
210 pins.LastOutLed, 1, ROOM("on"), 0, ROOM("off")) | |
211 | |
212 statements <- twoState(graph, ROOM("laundryDoorStrike"), | |
213 pins.LastOutLed, 1, ROOM("unlocked"), 0, ROOM("locked")) | |
214 | |
215 close(statements) | 262 close(statements) |
216 // type should be chosen with accept header. trig is | 263 // type should be chosen with accept header. trig is |
217 // causing segfaults. | 264 // causing segfaults. |
218 return serializeGowebResponse(c, "nquads", statements) | 265 return serializeGowebResponse(c, "nquads", statements) |
219 }) | 266 }) |
221 goweb.Map("PUT", "/led", func(c context.Context) error { | 268 goweb.Map("PUT", "/led", func(c context.Context) error { |
222 body, err := c.RequestBody() | 269 body, err := c.RequestBody() |
223 if err != nil { | 270 if err != nil { |
224 panic(err) | 271 panic(err) |
225 } | 272 } |
226 | 273 |
227 var level int | 274 pins.SetLed(string(body)) |
228 if string(body) == "on" { | |
229 level = 1 | |
230 } else if string(body) == "off" { | |
231 level = 0 | |
232 } else { | |
233 return goweb.Respond.With(c, http.StatusBadRequest, | |
234 []byte("body must be 'on' or 'off'")) | |
235 } | |
236 | |
237 hwio.DigitalWrite(pins.OutLed, level) | |
238 pins.LastOutLed = level | |
239 return goweb.Respond.WithStatusText(c, http.StatusAccepted) | 275 return goweb.Respond.WithStatusText(c, http.StatusAccepted) |
240 }) | 276 }) |
241 | 277 |
242 setStrike := func (level int) { | |
243 hwio.DigitalWrite(pins.OutStrike, level) | |
244 pins.LastOutStrike = level | |
245 } | |
246 | |
247 goweb.Map("PUT", "/strike", func(c context.Context) error { | 278 goweb.Map("PUT", "/strike", func(c context.Context) error { |
248 body, err := c.RequestBody() | 279 body, err := c.RequestBody() |
249 if err != nil { | 280 if err != nil { |
250 panic(err) | 281 panic(err) |
251 } | 282 } |
252 | 283 |
253 level, err := strconv.Atoi(string(body[:])) | 284 pins.SetStrike(string(body)) |
254 if err != nil { | |
255 return goweb.Respond.With(c, http.StatusBadRequest, | |
256 []byte("body must be '0' or '1'")) | |
257 } | |
258 | |
259 setStrike(level) | |
260 return goweb.Respond.WithStatusText(c, http.StatusAccepted) | 285 return goweb.Respond.WithStatusText(c, http.StatusAccepted) |
261 }) | 286 }) |
262 | 287 |
263 goweb.Map( | 288 goweb.Map( |
264 "PUT", "/strike/temporaryUnlock", | 289 "PUT", "/strike/temporaryUnlock", |
265 func(c context.Context) error { | 290 func(c context.Context) error { |
266 type TemporaryUnlockRequest struct { | 291 type TemporaryUnlockRequest struct { |
267 Seconds float64 | 292 Seconds float64 |
275 } | 300 } |
276 | 301 |
277 // This is not correctly reentrant. There should be a | 302 // This is not correctly reentrant. There should be a |
278 // stack of temporary effects that unpop correctly, | 303 // stack of temporary effects that unpop correctly, |
279 // and status should show you any running effects. | 304 // and status should show you any running effects. |
280 setStrike(1) | 305 pins.SetStrike("unlocked") |
281 go func() { | 306 go func() { |
282 time.Sleep(time.Duration(req.Seconds * | 307 time.Sleep(time.Duration(req.Seconds * |
283 float64(time.Second))) | 308 float64(time.Second))) |
284 setStrike(0) | 309 pins.SetStrike("locked") |
285 }() | 310 }() |
286 return goweb.Respond.WithStatusText( | 311 return goweb.Respond.WithStatusText( |
287 c, http.StatusAccepted) | 312 c, http.StatusAccepted) |
288 }) | 313 }) |
289 | 314 |
290 goweb.Map("PUT", "/speaker/beep", func(c context.Context) error { | 315 goweb.Map("PUT", "/speaker/beep", func(c context.Context) error { |
291 // queue a beep | 316 // queue a beep |
292 return goweb.Respond.WithStatusText(c, http.StatusAccepted) | 317 return goweb.Respond.WithStatusText(c, http.StatusAccepted) |
293 }) | 318 }) |
294 | 319 |
295 | |
296 address := ":8081" | 320 address := ":8081" |
297 | 321 |
298 s := &http.Server{ | 322 s := &http.Server{ |
299 Addr: address, | 323 Addr: address, |
300 Handler: goweb.DefaultHttpHandler(), | 324 Handler: goweb.DefaultHttpHandler(), |
301 ReadTimeout: 10 * time.Second, | 325 ReadTimeout: 10 * time.Second, |
302 WriteTimeout: 10 * time.Second, | 326 WriteTimeout: 10 * time.Second, |
303 } | 327 } |
304 | 328 |
305 log.Printf("Listening on port %s", address) | 329 log.Printf("Listening on port %s", address) |
306 log.Printf("%s", goweb.DefaultHttpHandler()) | 330 log.Printf("%s", goweb.DefaultHttpHandler()) |
307 listener, listenErr := net.Listen("tcp", address) | 331 listener, listenErr := net.Listen("tcp", address) |