diff --git a/.gitignore b/.gitignore index e55c5c7..c62119f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .idea godoor +godoor_server/godoor_server diff --git a/godoor.go b/godoor.go index 64bd233..4753eca 100644 --- a/godoor.go +++ b/godoor.go @@ -2,11 +2,13 @@ package main import ( "bufio" + "bytes" "context" "encoding/json" "fmt" "github.com/joho/godotenv" "io" + "log" "net/http" "os" "os/signal" @@ -31,17 +33,14 @@ type cardList struct { } `json:"allowed_uids"` } -type simpleUids struct { - tokens []string -} - type ValidUids map[string]bool // bool has no meaning type Config struct { - door string - uid_salt string - mock string - api struct { + door string + uidSalt string + doorOpenTime string + mock bool + api struct { allowed string longpoll string swipe string @@ -66,26 +65,29 @@ func main() { config.api.longpoll = os.Getenv("KDOORPI_API_LONGPOLL") config.api.swipe = os.Getenv("KDOORPI_API_SWIPE") config.api.key = os.Getenv("KDOORPI_API_KEY") - config.uid_salt = os.Getenv("KDOORPI_UID_SALT") - config.mock = os.Getenv("KDOORPI_MOCK_HW") + config.uidSalt = os.Getenv("KDOORPI_UID_SALT") + config.doorOpenTime = os.Getenv("KDOORPI_OPEN_TIME") + _, config.mock = os.LookupEnv("KDOORPI_MOCK_HW") - if config.mock == "true" { + go func() { + setup() + }() + + <-ctx.Done() + log.Printf("Cleanup\n") + + // cleanup +} +func setup() { + log.Println("Started Setup") + + if config.mock { + log.Println("MOCK mode enabled") wiegand = &WiegandMock{} } else { wiegand = WiegandSetup(wiegand_a, wiegand_b, wiegand_bit_timeout, solenoid) } - - http.DefaultClient.Timeout = 120 * time.Second - - for { - err := reloadTokens() - if err == nil { - break - } - time.Sleep(10 * time.Second) - } - - go cardRunner(wiegand) + log.Println("HW Setup done") go func() { for { @@ -93,12 +95,44 @@ func main() { } }() - fmt.Printf("Sleeping\n") + log.Println("Initialized longpoll event loop") - <-ctx.Done() - fmt.Printf("Cleanup\n") + http.DefaultClient.Timeout = 120 * time.Second - // cleanup + for { + log.Println("Start initial token population") + err := reloadTokens() + if err == nil { + break + } + log.Printf("Initial token population failed. err: %v", err) + log.Println("Retrying in 10 seconds...") + time.Sleep(10 * time.Second) + } + + log.Println("Initial token population success") + + go cardRunner(wiegand) + + log.Println("Setup completed") +} + +func OpenAndCloseDoor(w Wiegand) error { + err := w.OpenDoor() + if err != nil { + return err + } + fmt.Println("Door is now open") + + time.Sleep(5 * time.Second) + + err = w.CloseDoor() + if err != nil { + return err + } + fmt.Println("Door is now closed") + + return nil } func cardRunner(w Wiegand) { @@ -110,16 +144,26 @@ func cardRunner(w Wiegand) { printCardId(card) hashedHex := hashCardUid(card) - fmt.Println(hashedHex) + log.Println(hashedHex) + + go func() { + err := sendSwipeEvent(hashedHex) + if err != nil { + log.Println("Failed to send swipe event: %v", err) + } + }() globalLock.Lock() ok := validUids[hashedHex] globalLock.Unlock() if ok { - fmt.Println("Opening door") - w.OpenDoor() + log.Println("Opening door") + err := OpenAndCloseDoor(w) + if err != nil { + log.Println("There was an error opening and closing the Door") + } } else { - fmt.Println("Unknown card") + log.Println("Unknown card") } } @@ -159,7 +203,7 @@ func waitEvents() error { if err != nil { return err } - fmt.Printf("%v\n", resp) + log.Printf("%v\n", resp) reader := bufio.NewReader(resp.Body) for { @@ -173,9 +217,12 @@ func waitEvents() error { if !found_data { continue } - fmt.Printf("got server data: %q\n", data) + log.Printf("got server data: %q\n", data) if strings.TrimSpace(data) == config.door { - wiegand.OpenDoor() + err := OpenAndCloseDoor(wiegand) + if err != nil { + log.Println("There was an error opening and closing the Door") + } } go reloadTokens() @@ -183,7 +230,7 @@ func waitEvents() error { } - fmt.Printf("%v\n", resp) + log.Printf("%v\n", resp) return nil } @@ -197,7 +244,7 @@ func reloadTokens() error { if err != nil { return err } - fmt.Printf("%v\n", resp) + log.Printf("%v\n", resp) var cl cardList @@ -215,9 +262,38 @@ func reloadTokens() error { defer globalLock.Unlock() validUids = make(ValidUids) for i, val := range cl.AllowedUids { - fmt.Printf("%d: %+v\n", i, val.Token.UidHash) + log.Printf("%d: %+v\n", i, val.Token.UidHash) validUids[val.Token.UidHash] = true } return nil } + +func sendSwipeEvent(cardUidHash string) error { + swipeEvent := map[string]string{ + "uid_hash": cardUidHash, + "door": config.door, + "timestamp": time.Now().Format(time.DateTime), + } + + data, err := json.Marshal(swipeEvent) + if err != nil { + return err + } + + req, err := http.NewRequest(http.MethodPost, config.api.swipe, bytes.NewReader(data)) + if err != nil { + return err + } + req.Header.Add("KEY", config.api.key) + resp, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + + if !(resp.StatusCode >= 200 && resp.StatusCode < 300) { + return fmt.Errorf("Server responded with %d", resp.StatusCode) + } + + return nil +} diff --git a/godoor_server/godoor_server.go b/godoor_server/godoor_server.go index 977e022..dd8fe74 100644 --- a/godoor_server/godoor_server.go +++ b/godoor_server/godoor_server.go @@ -38,7 +38,7 @@ func getAllowed(w http.ResponseWriter, r *http.Request) { keys := cardList{ AllowedUids: []cardToken{ {card{UidHash: "d72c87d0f077c7766f2985dfab30e8955c373a13a1e93d315203939f542ff86e73ee37c31f4c4b571f4719fa8e3589f12db8dcb57ea9f56764bb7d58f64cf705"}}, - {card{UidHash: "112233445566"}}, + {card{UidHash: "873636abbcf597a4835afc1c9b16a72eb6175b7ab278a8f18ab13c50172c90ea97cc3e258efd9cc1d885d7ea32d87fd006907892793e7cd6c468417bd8b8421a"}}, {card{UidHash: "aabbccddeeff"}}, }, } @@ -114,6 +114,10 @@ func (doobserver *DoorBoyServer) postOpenDoor(w http.ResponseWriter, r *http.Req w.Write([]byte("OK")) } +func postCardSwipe(w http.ResponseWriter, r *http.Request) { + +} + func main() { doorboyserver := DoorBoyServer{longPollers: map[string]chan string{}} http.HandleFunc("/", getRoot) @@ -121,6 +125,7 @@ func main() { http.HandleFunc("/longpoll", doorboyserver.getLongPoll) http.HandleFunc("/jspoll", doorboyserver.getLongPollJson) http.HandleFunc("/open", doorboyserver.postOpenDoor) + http.HandleFunc("/cardswipe", postCardSwipe) err := http.ListenAndServe(":3333", nil) diff --git a/wiegand.go b/wiegand.go index a6f6b5d..90fddf3 100644 --- a/wiegand.go +++ b/wiegand.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "fmt" "golang.org/x/crypto/scrypt" + "log" "time" ) @@ -14,7 +15,9 @@ import ( type Wiegand interface { GetCardUid() (uint64, error) - OpenDoor() + OpenDoor() error + CloseDoor() error + IsDoorOpen() (bool, error) } type WiegandHW struct { @@ -28,15 +31,21 @@ type WiegandHW struct { solenoidLine *gpiod.Line } -func (w *WiegandHW) OpenDoor() { - fmt.Println("Open") - w.solenoidLine.SetValue(1) - d, _ := time.ParseDuration("500ms") - time.Sleep(d) - w.solenoidLine.SetValue(0) - fmt.Println("Close") +func (w *WiegandHW) OpenDoor() error { + return w.solenoidLine.SetValue(1) } +func (w *WiegandHW) CloseDoor() error { + return w.solenoidLine.SetValue(0) +} + +func (w *WiegandHW) IsDoorOpen() (bool, error) { + i, err := w.solenoidLine.Value() + if err != nil { + return false, err + } + return i == 1, nil +} func printCardId(card uint64) { for i := 0; i < 7; i++ { fmt.Printf("%02x", (card>>(8*i))&0xff) @@ -47,7 +56,7 @@ func printCardId(card uint64) { func hashCardUid(card uint64) string { b := make([]byte, 8) binary.LittleEndian.PutUint64(b, card) - hashed, err := scrypt.Key(b, []byte(config.uid_salt), 16384, 8, 1, 64) + hashed, err := scrypt.Key(b, []byte(config.uidSalt), 16384, 8, 1, 64) if err != nil { panic(err) // can only happen when scrypt params are garbage } @@ -58,10 +67,9 @@ func hashCardUid(card uint64) string { func (w *WiegandHW) GetCardUid() (uint64, error) { // Wait for bit timeout - fmt.Printf("Waiting for bit timeout\n") + log.Printf("Waiting for bit timeout\n") <-w.bitTimeoutTimer.C - fmt.Printf("\n") defer func() { w.bitNr = 0 }() diff --git a/wiegand_mock.go b/wiegand_mock.go index 0eaef71..5b5832b 100644 --- a/wiegand_mock.go +++ b/wiegand_mock.go @@ -1,23 +1,32 @@ package main import ( - "fmt" "time" ) type WiegandMock struct { mockUid uint64 + + openState bool } -func (*WiegandMock) OpenDoor() { - fmt.Println("Door is now open") - time.Sleep(500 * time.Millisecond) - fmt.Println("Door is now closed") +func (w *WiegandMock) OpenDoor() error { + w.openState = true + return nil +} + +func (w *WiegandMock) CloseDoor() error { + w.openState = false + return nil +} + +func (w *WiegandMock) IsDoorOpen() (bool, error) { + return w.openState, nil } func (w *WiegandMock) GetCardUid() (uint64, error) { time.Sleep(1 * time.Second) - return w.mockUid, fmt.Errorf("err") + return w.mockUid, nil //fmt.Errorf("err") } func (w *WiegandMock) SetMockUid(mockUid uint64) {