Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit c77a360
Showing
3 changed files
with
313 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# doors | ||
|
||
## Description | ||
|
||
## What you need | ||
|
||
* [PhidgetSBC3](http://www.phidgets.com/products.php?product_id=1073_0) | ||
* Relays (i.e. [Dual SSR Relay Board](http://www.phidgets.com/products.php?category=9&product_id=3053_0) or [8 Channel SSR Module Board](http://www.sainsmart.com/8-channel-5v-solid-state-relay-module-board-omron-ssr-4-pic-arm-avr-dsp-arduino.html)) | ||
* Door Controller (i.e [KT-300](http://www.kantech.com/Products/controllers_kt300.aspx)) | ||
|
||
## Preperation | ||
|
||
### Hardware | ||
|
||
This will be mostly up to you because it will vary widly depending on what door controller you use. | ||
What you need to do is connect the PhidgetSBC3 to the relays and the relays to the door controller in such a way that when closing the relay, the door will be unlocked. | ||
I recommend the [Kantech KT-300](http://www.kantech.com/Products/controllers_kt300.aspx) and [Kantech EntraPass](http://www.kantech.com/Products/software_entrapass.aspx) because that is what we use at Shopify and we've got it working because we configured the inputs to be secondary Request to exit (REX) for each door. | ||
|
||
### Software | ||
|
||
1. Plug your PhidgetSBC3 into power and network | ||
2. Go to [phidgetsbc.local](http://phidgetsbc.local/) and set a password | ||
<a href="https://i0.wp.com/samuel.kadolph.com/wp-content/uploads/2013/03/Screen_Shot_2013-03-25_at_1.53.39_AM.png"><img src="https://i0.wp.com/samuel.kadolph.com/wp-content/uploads/2013/03/Screen_Shot_2013-03-25_at_1.53.39_AM.png" alt="Go to phidgetsbc.local and set a password" width="828" height="363" class="size-full wp-image-768" /></a> | ||
3. Enable the SSH server | ||
<a href="https://i0.wp.com/samuel.kadolph.com/wp-content/uploads/2013/03/Screen_Shot_2013-03-25_at_1.54.05_AM.png"><img src="https://i0.wp.com/samuel.kadolph.com/wp-content/uploads/2013/03/Screen_Shot_2013-03-25_at_1.54.05_AM.png" alt="Enable the SSH server" width="828" height="656" class="size-full wp-image-769" /></a> | ||
4. Enable the full Debian Package Repository | ||
<a href="https://i0.wp.com/samuel.kadolph.com/wp-content/uploads/2013/03/Screen_Shot_2013-03-25_at_1.58.28_AM.png"><img src="https://i0.wp.com/samuel.kadolph.com/wp-content/uploads/2013/03/Screen_Shot_2013-03-25_at_1.58.28_AM.png" alt="Enable the full Debian Package Repository " width="828" height="748" class="size-full wp-image-772" /></a> | ||
5. Add your ssh key to the phidgetsbc *(optional)* | ||
* `ssh root@phidgetsbc.local "mkdir -p .ssh && echo '$(cat ~/.ssh/id_rsa.pub)' >> .ssh/authorized_keys"` | ||
6. SSH in | ||
* `ssh root@phidgetsbc.local` | ||
7. Add my apt key | ||
* `apt-key adv --keyserver keys.gnupg.net --recv-keys B4F808A2` | ||
8. Add my apt repo | ||
* `echo "deb http://apt.samuelkadolph.com/ wheezy main" > /etc/apt/sources.list.d/samuelkadolph.list` | ||
9. Update apt | ||
* `apt-get update` | ||
10. Install the required packages | ||
* `apt-get install golang-tip build-essential git-core libphidget21-dev ca-certificates -y` | ||
|
||
## Installation | ||
|
||
1. Go to [phidgetsbc.local](http://phidgetsbc.local/) and log in | ||
<a href="https://i0.wp.com/samuel.kadolph.com/wp-content/uploads/2013/03/Screen_Shot_2013-03-25_at_1.35.27_AM.png"><img src="https://i0.wp.com/samuel.kadolph.com/wp-content/uploads/2013/03/Screen_Shot_2013-03-25_at_1.35.27_AM.png" alt="Go to phidgetsbc.local and log in" width="828" height="299" /></a> | ||
2. Create a project | ||
<a href="https://i0.wp.com/samuel.kadolph.com/wp-content/uploads/2013/03/Screen_Shot_2013-03-25_at_1.38.37_AM.png"><img src="https://i0.wp.com/samuel.kadolph.com/wp-content/uploads/2013/03/Screen_Shot_2013-03-25_at_1.38.37_AM.png" alt="Create a project" width="828" height="517" /></a> | ||
3. Install the application | ||
<pre><code>GOPATH=$HOME/go go get github.com/samuelkadolph/doors</code></pre> | ||
4. Copy the binary to the project | ||
<pre><code>cp $HOME/go/bin/doors /usr/userapps/doors/doors</code></pre> | ||
5. Create the config file | ||
<pre><code>TODO</code></pre> | ||
6. Set the executable and enable boot startup for the project | ||
<a href="https://i0.wp.com/samuel.kadolph.com/wp-content/uploads/2013/03/Screen_Shot_2013-03-25_at_1.39.50_AM.png"><img src="https://i0.wp.com/samuel.kadolph.com/wp-content/uploads/2013/03/Screen_Shot_2013-03-25_at_1.39.50_AM.png" alt="Set the executable and enable boot startup for the project" width="828" height="787" /></a> | ||
7. Start the application | ||
<a href="https://i0.wp.com/samuel.kadolph.com/wp-content/uploads/2013/03/Screen_Shot_2013-03-25_at_1.40.49_AM.png"><img src="https://i0.wp.com/samuel.kadolph.com/wp-content/uploads/2013/03/Screen_Shot_2013-03-25_at_1.40.49_AM.png" alt="Start the application" width="828" height="506" /></a> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"github.com/samuelkadolph/go/phidgets" | ||
"time" | ||
) | ||
|
||
type Door struct { | ||
Lock *int | ||
Mag *int | ||
Name string | ||
} | ||
|
||
func (d *Door) MagDisengage(ifk *phidgets.InterfaceKit) error { | ||
return ifk.Outputs[*d.Mag].SetState(false) | ||
} | ||
|
||
func (d *Door) MagEngage(ifk *phidgets.InterfaceKit) error { | ||
return ifk.Outputs[*d.Mag].SetState(true) | ||
} | ||
|
||
func (d *Door) Open(ifk *phidgets.InterfaceKit) error { | ||
if err := ifk.Outputs[*d.Lock].SetState(true); err != nil { | ||
return err | ||
} | ||
|
||
time.Sleep(20 * time.Millisecond) | ||
|
||
return ifk.Outputs[*d.Lock].SetState(false) | ||
} | ||
|
||
func (d *Door) MarshalJSON() ([]byte, error) { | ||
o := make(map[string]string) | ||
|
||
if d.Lock != nil { | ||
o["Lock"] = "supported" | ||
} else { | ||
o["Lock"] = "unsupported" | ||
} | ||
|
||
if d.Mag != nil { | ||
s, err := ifk.Outputs[*d.Mag].State() | ||
if err != nil { | ||
o["Mag"] = "error" | ||
} | ||
if s { | ||
o["Mag"] = "engaged" | ||
} else { | ||
o["Mag"] = "disengaged" | ||
} | ||
} else { | ||
o["Mag"] = "unsupported" | ||
} | ||
|
||
o["Name"] = d.Name | ||
|
||
return json.Marshal(o) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"github.com/gorilla/handlers" | ||
"github.com/gorilla/mux" | ||
"github.com/samuelkadolph/go/phidgets" | ||
"io/ioutil" | ||
"log" | ||
"net/http" | ||
"os" | ||
"time" | ||
) | ||
|
||
type Config struct { | ||
Doors []*Door | ||
Secret string | ||
} | ||
|
||
type hash map[string]interface{} | ||
|
||
var config Config | ||
var ifk *phidgets.InterfaceKit | ||
|
||
func DoorMagDisengage(w http.ResponseWriter, r *http.Request) { | ||
var d *Door | ||
|
||
if !checkSecret(w, r) { | ||
return | ||
} | ||
if !checkDoor(w, r, &d) { | ||
return | ||
} | ||
if !checkDoorMag(w, r, d) { | ||
return | ||
} | ||
|
||
if err := d.MagDisengage(ifk); err != nil { | ||
response(w, 200, hash{}, hash{"success": false, "error": err.Error()}) | ||
} else { | ||
response(w, 200, hash{}, hash{"success": true}) | ||
} | ||
} | ||
|
||
func DoorMagEngage(w http.ResponseWriter, r *http.Request) { | ||
var d *Door | ||
|
||
if !checkSecret(w, r) { | ||
return | ||
} | ||
if !checkDoor(w, r, &d) { | ||
return | ||
} | ||
if !checkDoorMag(w, r, d) { | ||
return | ||
} | ||
|
||
if err := d.MagEngage(ifk); err != nil { | ||
response(w, 200, hash{}, hash{"success": false, "error": err.Error()}) | ||
} else { | ||
response(w, 200, hash{}, hash{"success": true}) | ||
} | ||
} | ||
|
||
func DoorOpen(w http.ResponseWriter, r *http.Request) { | ||
var d *Door | ||
|
||
if !checkSecret(w, r) { | ||
return | ||
} | ||
if !checkDoor(w, r, &d) { | ||
return | ||
} | ||
|
||
if err := d.Open(ifk); err != nil { | ||
response(w, 200, hash{}, hash{"success": false, "error": err.Error()}) | ||
} else { | ||
response(w, 200, hash{}, hash{"success": true}) | ||
} | ||
} | ||
|
||
func ShowDoor(w http.ResponseWriter, r *http.Request) { | ||
var d *Door | ||
|
||
if !checkSecret(w, r) { | ||
return | ||
} | ||
if !checkDoor(w, r, &d) { | ||
return | ||
} | ||
|
||
response(w, 200, hash{}, d) | ||
} | ||
|
||
func Root(w http.ResponseWriter, r *http.Request) { | ||
response(w, 200, hash{}, hash{"hi": true}) | ||
} | ||
|
||
func NotFound(w http.ResponseWriter, r *http.Request) { | ||
response(w, 404, hash{}, hash{"error": "not found"}) | ||
} | ||
|
||
func checkDoor(w http.ResponseWriter, r *http.Request, o **Door) bool { | ||
v := mux.Vars(r) | ||
d := findDoor(v["door"]) | ||
|
||
if d == nil { | ||
response(w, 404, hash{}, hash{"error": "door not found"}) | ||
return false | ||
} | ||
if d.Lock == nil { | ||
response(w, 422, hash{}, hash{"error": "door does not support opening"}) | ||
return false | ||
} | ||
|
||
*o = d | ||
return true | ||
} | ||
|
||
func checkDoorMag(w http.ResponseWriter, r *http.Request, d *Door) bool { | ||
if d.Mag == nil { | ||
response(w, 422, hash{}, hash{"error": "door does not support mag"}) | ||
return false | ||
} | ||
|
||
return true | ||
} | ||
|
||
func checkSecret(w http.ResponseWriter, r *http.Request) bool { | ||
r.ParseForm() | ||
|
||
if config.Secret != r.Form.Get("secret") { | ||
response(w, 403, hash{}, hash{"error": "bad secret"}) | ||
return false | ||
} | ||
|
||
return true | ||
} | ||
|
||
func findDoor(name string) *Door { | ||
for _, d := range config.Doors { | ||
if d.Name == name { | ||
return d | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func response(w http.ResponseWriter, s int, h hash, b interface{}) { | ||
o, _ := json.Marshal(b) | ||
h["Content-Length"] = len(o) | ||
h["Content-Type"] = "application/json" | ||
for k, v := range h { | ||
w.Header().Set(k, fmt.Sprintf("%v", v)) | ||
} | ||
w.WriteHeader(s) | ||
w.Write([]byte(o)) | ||
} | ||
|
||
func init() { | ||
var err error | ||
var file []byte | ||
|
||
if file, err = ioutil.ReadFile("./config.json"); err != nil { | ||
log.Fatalf("Error while opening config.json - %s", err) | ||
} | ||
|
||
if err = json.Unmarshal(file, &config); err != nil { | ||
log.Fatalf("Error while unmarshaling config - %s", err) | ||
} | ||
|
||
if ifk, err = phidgets.NewInterfaceKit(); err != nil { | ||
log.Fatalf("%s", err) | ||
} | ||
|
||
if err = ifk.Open(phidgets.Any); err != nil { | ||
log.Fatalf("%s", err) | ||
} | ||
|
||
if err = ifk.WaitForAttachment(2 * time.Second); err != nil { | ||
log.Fatalf("%s", err) | ||
} | ||
} | ||
|
||
func main() { | ||
r := mux.NewRouter() | ||
r.NewRoute().Methods("POST").Path("/doors/{door}/mag/disengage").Handler(http.HandlerFunc(DoorMagDisengage)) | ||
r.NewRoute().Methods("POST").Path("/doors/{door}/mag/engage").Handler(http.HandlerFunc(DoorMagEngage)) | ||
r.NewRoute().Methods("POST").Path("/doors/{door}/open").Handler(http.HandlerFunc(DoorOpen)) | ||
r.NewRoute().Methods("GET").Path("/doors/{door}").Handler(http.HandlerFunc(ShowDoor)) | ||
r.NewRoute().Methods("GET").Path("/").Handler(http.HandlerFunc(Root)) | ||
r.NewRoute().Handler(http.HandlerFunc(NotFound)) | ||
|
||
h := handlers.CombinedLoggingHandler(os.Stdout, r) | ||
|
||
log.Fatalf("Error while starting server %s", http.ListenAndServe(":4567", h)) | ||
} |