Checkpoint 5
This commit is contained in:
parent
9116635efc
commit
9b1eba127a
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
.idea
|
.idea
|
||||||
godoor
|
godoor
|
||||||
godoor_server/godoor_server
|
godoor_server/godoor_server
|
||||||
|
godoor_server/keys.json
|
||||||
|
4
Makefile
Normal file
4
Makefile
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
dev:
|
||||||
|
GOOS=linux GOARCH=arm64 go build .
|
||||||
|
scp godoor workshopdoor:/tmp/
|
||||||
|
ssh workshopdoor 'mv -f /tmp/godoor ~/ && sudo systemctl restart godoor'
|
3
build.sh
3
build.sh
@ -1,3 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
GOOS=linux GOARCH=arm64 go build && scp godoor rpi4b:
|
|
83
godoor.go
83
godoor.go
@ -6,7 +6,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/joho/godotenv"
|
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -15,8 +14,12 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"godoor/hash"
|
||||||
|
|
||||||
|
"github.com/joho/godotenv"
|
||||||
)
|
)
|
||||||
import "time"
|
|
||||||
|
|
||||||
const wiegand_a = 17
|
const wiegand_a = 17
|
||||||
const wiegand_b = 18
|
const wiegand_b = 18
|
||||||
@ -54,12 +57,20 @@ type KeepDoorOpen struct {
|
|||||||
timer *time.Timer
|
timer *time.Timer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OpenedTimestamp struct {
|
||||||
|
Opened time.Time
|
||||||
|
Closed *time.Time
|
||||||
|
}
|
||||||
|
|
||||||
var config Config
|
var config Config
|
||||||
var globalLock sync.Mutex
|
var globalLock sync.Mutex
|
||||||
var validUids ValidUids
|
var validUids ValidUids
|
||||||
var wiegand Wiegand
|
var wiegand Wiegand
|
||||||
var keepDoorOpen KeepDoorOpen
|
var keepDoorOpen KeepDoorOpen
|
||||||
|
|
||||||
|
var lastSyncedTimestamp *time.Time
|
||||||
|
var openDoorTimestamps []OpenedTimestamp
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||||
@ -85,6 +96,7 @@ func main() {
|
|||||||
|
|
||||||
// cleanup
|
// cleanup
|
||||||
}
|
}
|
||||||
|
|
||||||
func setup() {
|
func setup() {
|
||||||
log.Println("Started Setup")
|
log.Println("Started Setup")
|
||||||
|
|
||||||
@ -99,6 +111,8 @@ func setup() {
|
|||||||
}
|
}
|
||||||
log.Println("HW Setup done")
|
log.Println("HW Setup done")
|
||||||
|
|
||||||
|
http.DefaultClient.Timeout = 120 * time.Second
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
waitEvents()
|
waitEvents()
|
||||||
@ -107,8 +121,6 @@ func setup() {
|
|||||||
|
|
||||||
log.Println("Initialized longpoll event loop")
|
log.Println("Initialized longpoll event loop")
|
||||||
|
|
||||||
http.DefaultClient.Timeout = 120 * time.Second
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
log.Println("Start initial token population")
|
log.Println("Start initial token population")
|
||||||
err := reloadTokens()
|
err := reloadTokens()
|
||||||
@ -120,6 +132,8 @@ func setup() {
|
|||||||
time.Sleep(10 * time.Second)
|
time.Sleep(10 * time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
go runHttpServer()
|
||||||
|
|
||||||
log.Println("Initial token population success")
|
log.Println("Initial token population success")
|
||||||
|
|
||||||
go cardRunner(wiegand)
|
go cardRunner(wiegand)
|
||||||
@ -127,13 +141,36 @@ func setup() {
|
|||||||
log.Println("Setup completed")
|
log.Println("Setup completed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runHttpServer() {
|
||||||
|
http.HandleFunc("/lastsync", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
e := json.NewEncoder(w)
|
||||||
|
e.Encode(map[string]any{
|
||||||
|
"last_synced": lastSyncedTimestamp,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
http.HandleFunc("/opened", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
e := json.NewEncoder(w)
|
||||||
|
e.Encode(map[string]any{
|
||||||
|
"open_timestamps": openDoorTimestamps,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
http.HandleFunc("/isopen", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
e := json.NewEncoder(w)
|
||||||
|
open, _ := wiegand.IsDoorOpen()
|
||||||
|
e.Encode(map[string]any{
|
||||||
|
"open": open,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
http.ListenAndServe(":3334", nil)
|
||||||
|
}
|
||||||
|
|
||||||
func OpenAndCloseDoor(w Wiegand) error {
|
func OpenAndCloseDoor(w Wiegand) error {
|
||||||
err := w.OpenDoor()
|
err := OpenDoor(w)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if keepDoorOpen.until.After(time.Now().Add(5 * time.Second)) {
|
if keepDoorOpen.until.After(time.Now()) {
|
||||||
fmt.Println("Door is already open")
|
fmt.Println("Door is already open")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -142,7 +179,7 @@ func OpenAndCloseDoor(w Wiegand) error {
|
|||||||
|
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
|
|
||||||
err = w.CloseDoor()
|
err = CloseDoor(w)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -151,6 +188,27 @@ func OpenAndCloseDoor(w Wiegand) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func OpenDoor(w Wiegand) error {
|
||||||
|
open, _ := w.IsDoorOpen()
|
||||||
|
if open {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
w.OpenDoor()
|
||||||
|
openDoorTimestamps = append(openDoorTimestamps, OpenedTimestamp{Opened: time.Now(), Closed: nil})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CloseDoor(w Wiegand) error {
|
||||||
|
open, _ := w.IsDoorOpen()
|
||||||
|
if !open {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
w.CloseDoor()
|
||||||
|
t := time.Now()
|
||||||
|
openDoorTimestamps[len(openDoorTimestamps)-1].Closed = &t
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func cardRunner(w Wiegand) {
|
func cardRunner(w Wiegand) {
|
||||||
for {
|
for {
|
||||||
card, err := w.GetCardUid()
|
card, err := w.GetCardUid()
|
||||||
@ -159,7 +217,7 @@ func cardRunner(w Wiegand) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
printCardId(card)
|
printCardId(card)
|
||||||
hashedHex := hashCardUid(card)
|
hashedHex := hash.HashCardUid(card)
|
||||||
log.Println(hashedHex)
|
log.Println(hashedHex)
|
||||||
|
|
||||||
globalLock.Lock()
|
globalLock.Lock()
|
||||||
@ -287,6 +345,9 @@ func reloadTokens() error {
|
|||||||
updateKeepOpenDoor(*cl.KeepOpenUntil)
|
updateKeepOpenDoor(*cl.KeepOpenUntil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t := time.Now()
|
||||||
|
lastSyncedTimestamp = &t
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,20 +361,20 @@ func updateKeepOpenDoor(newKeepOpenTime time.Time) {
|
|||||||
|
|
||||||
if newKeepOpenTime.After(time.Now()) {
|
if newKeepOpenTime.After(time.Now()) {
|
||||||
log.Printf("Keeping door open until %v", newKeepOpenTime)
|
log.Printf("Keeping door open until %v", newKeepOpenTime)
|
||||||
wiegand.OpenDoor()
|
OpenDoor(wiegand)
|
||||||
timer := time.AfterFunc(time.Until(newKeepOpenTime), handleKeepDoorOpenCloseCleanup)
|
timer := time.AfterFunc(time.Until(newKeepOpenTime), handleKeepDoorOpenCloseCleanup)
|
||||||
keepDoorOpen = KeepDoorOpen{
|
keepDoorOpen = KeepDoorOpen{
|
||||||
timer: timer,
|
timer: timer,
|
||||||
until: newKeepOpenTime,
|
until: newKeepOpenTime,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
wiegand.CloseDoor()
|
CloseDoor(wiegand)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleKeepDoorOpenCloseCleanup() {
|
func handleKeepDoorOpenCloseCleanup() {
|
||||||
fmt.Println("Keep door open time is reached!")
|
fmt.Println("Keep door open time is reached!")
|
||||||
wiegand.CloseDoor()
|
CloseDoor(wiegand)
|
||||||
keepDoorOpen = KeepDoorOpen{}
|
keepDoorOpen = KeepDoorOpen{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
4
godoor_server/Makefile
Normal file
4
godoor_server/Makefile
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
dev:
|
||||||
|
GOOS=linux GOARCH=arm64 go build .
|
||||||
|
scp godoor_server workshopdoor:/tmp/
|
||||||
|
ssh workshopdoor 'mv -f /tmp/godoor_server ~/'
|
@ -1 +0,0 @@
|
|||||||
[{"token":{"uid_hash":"873636abbcf597a4835afc1c9b16a72eb6175b7ab278a8f18ab13c50172c90ea97cc3e258efd9cc1d885d7ea32d87fd006907892793e7cd6c468417bd8b8421a"}},{"token":{"uid_hash":"a08094343b4057777af4935b79df586d7eb999117883a53393e8b46f1ab19577b12039a5d2e0b9d0364bbed5c82d83a507492fea47ace633acf23da2dcf1560e"}},{"token":{"uid_hash":"d78271547cde009726b159dca09e53bee72feebe90b3eb7cb6e394caafc30bdb1f1567efc2f19bbdf3c6922e0bebed910ee4fa4f5b13bd379651da4f620f3559"}},{"token":{"uid_hash":"d72c87d0f077c7766f2985dfab30e8955c373a13a1e93d315203939f542ff86e73ee37c31f4c4b571f4719fa8e3589f12db8dcb57ea9f56764bb7d58f64cf705"}},{"token":{"uid_hash":"873636abbcf597a4835afc1c9b16a72eb6175b7ab278a8f18ab13c50172c90ea97cc3e258efd9cc1d885d7ea32d87fd006907892793e7cd6c468417bd8b8421a"}},{"token":{"uid_hash":"a08094343b4057777af4935b79df586d7eb999117883a53393e8b46f1ab19577b12039a5d2e0b9d0364bbed5c82d83a507492fea47ace633acf23da2dcf1560e"}},{"token":{"uid_hash":"d78271547cde009726b159dca09e53bee72feebe90b3eb7cb6e394caafc30bdb1f1567efc2f19bbdf3c6922e0bebed910ee4fa4f5b13bd379651da4f620f3559"}},{"token":{"uid_hash":"873636abbcf597a4835afc1c9b16a72eb6175b7ab278a8f18ab13c50172c90ea97cc3e258efd9cc1d885d7ea32d87fd006907892793e7cd6c468417bd8b8421a"}},{"token":{"uid_hash":"a08094343b4057777af4935b79df586d7eb999117883a53393e8b46f1ab19577b12039a5d2e0b9d0364bbed5c82d83a507492fea47ace633acf23da2dcf1560e"}},{"token":{"uid_hash":"d78271547cde009726b159dca09e53bee72feebe90b3eb7cb6e394caafc30bdb1f1567efc2f19bbdf3c6922e0bebed910ee4fa4f5b13bd379651da4f620f3559"}}]
|
|
40
hash/godoor_test.go
Normal file
40
hash/godoor_test.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package hash
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHash(t *testing.T) {
|
||||||
|
hash := HashCardUid(0x000000000F0AD2301)
|
||||||
|
if hash != "a6d9ba36ecb5f8e6312f40ee260ad59e9cca3c6ce073bf072df3324c0072886196e6823a7c758ab567fc53e91fbbda297a4efe0072e41316c56446ef126a5180" {
|
||||||
|
t.Errorf("WRONG Key generated: %q", hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
hash = HashCardUid(0x3CBB5AAD)
|
||||||
|
if hash != "1e7bbc1aeeff4bfe58ea8d97df7cd0f97599fa31633d3a1f57d138549b3968c20affb186fe0b85570d34718b273d269bcd28b2be59faf8122451d91a2cdc5f18" {
|
||||||
|
t.Errorf("WRONG Key generated: %q", hash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBits(t *testing.T) {
|
||||||
|
|
||||||
|
bits := make([]bool, 64)
|
||||||
|
trueBits := []int{0, 1, 2, 3, 8, 10, 12, 13, 15, 18, 22, 23, 31}
|
||||||
|
for _, i := range trueBits {
|
||||||
|
bits[i] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
var card uint64 = 0
|
||||||
|
for i := 63; i >= 0; i-- {
|
||||||
|
if bits[i] == true {
|
||||||
|
card |= 1 << (63 - i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cardId := fmt.Sprintf("%x", card)
|
||||||
|
fmt.Println(cardId)
|
||||||
|
if card != 0xF0AD230100000000 {
|
||||||
|
panic(fmt.Errorf("WROOONG: %x", card))
|
||||||
|
}
|
||||||
|
}
|
21
hash/hash.go
Normal file
21
hash/hash.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package hash
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/hex"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/scrypt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HashCardUid(card uint64) string {
|
||||||
|
b := make([]byte, 8)
|
||||||
|
binary.LittleEndian.PutUint64(b, card)
|
||||||
|
|
||||||
|
hashed, err := scrypt.Key(b, []byte("hkRXwLlQKmCJoy5qaahp"), 16384, 8, 1, 64)
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // can only happen when scrypt params are garbage
|
||||||
|
}
|
||||||
|
|
||||||
|
hashedHex := hex.EncodeToString(hashed)
|
||||||
|
return hashedHex
|
||||||
|
}
|
63
wiegand.go
63
wiegand.go
@ -1,15 +1,11 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"golang.org/x/crypto/scrypt"
|
|
||||||
"log"
|
"log"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/warthog618/gpiod"
|
"github.com/warthog618/gpiod"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -29,41 +25,45 @@ type WiegandHW struct {
|
|||||||
bitTimeoutTimer *time.Timer
|
bitTimeoutTimer *time.Timer
|
||||||
|
|
||||||
solenoidLine *gpiod.Line
|
solenoidLine *gpiod.Line
|
||||||
|
lock sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WiegandHW) OpenDoor() error {
|
func (w *WiegandHW) OpenDoor() error {
|
||||||
|
w.lock.Lock()
|
||||||
|
defer w.lock.Unlock()
|
||||||
|
|
||||||
|
open, _ := w.isDoorOpen()
|
||||||
|
if open {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
return w.solenoidLine.SetValue(1)
|
return w.solenoidLine.SetValue(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WiegandHW) CloseDoor() error {
|
func (w *WiegandHW) CloseDoor() error {
|
||||||
|
w.lock.Lock()
|
||||||
|
defer w.lock.Unlock()
|
||||||
|
|
||||||
|
open, _ := w.isDoorOpen()
|
||||||
|
if !open {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
return w.solenoidLine.SetValue(0)
|
return w.solenoidLine.SetValue(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WiegandHW) IsDoorOpen() (bool, error) {
|
func (w *WiegandHW) IsDoorOpen() (bool, error) {
|
||||||
|
w.lock.RLock()
|
||||||
|
defer w.lock.RUnlock()
|
||||||
|
return w.isDoorOpen()
|
||||||
|
}
|
||||||
|
func (w *WiegandHW) isDoorOpen() (bool, error) {
|
||||||
i, err := w.solenoidLine.Value()
|
i, err := w.solenoidLine.Value()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
return i == 1, nil
|
return i == 1, nil
|
||||||
}
|
}
|
||||||
func printCardId(card uint64) {
|
|
||||||
for i := 0; i < 7; i++ {
|
|
||||||
fmt.Printf("%02x", (card>>(8*i))&0xff)
|
|
||||||
}
|
|
||||||
fmt.Printf("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
func hashCardUid(card uint64) string {
|
|
||||||
b := make([]byte, 8)
|
|
||||||
binary.LittleEndian.PutUint64(b, card)
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
hashedHex := hex.EncodeToString(hashed)
|
|
||||||
return hashedHex
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WiegandHW) GetCardUid() (uint64, error) {
|
func (w *WiegandHW) GetCardUid() (uint64, error) {
|
||||||
// Wait for bit timeout
|
// Wait for bit timeout
|
||||||
@ -78,7 +78,7 @@ func (w *WiegandHW) GetCardUid() (uint64, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var card uint64 = 0
|
var card uint64 = 0
|
||||||
for i := 63; i != 0; i-- {
|
for i := 63; i >= 0; i-- {
|
||||||
if w.bits[i] == true {
|
if w.bits[i] == true {
|
||||||
card |= 1 << (63 - i)
|
card |= 1 << (63 - i)
|
||||||
}
|
}
|
||||||
@ -89,15 +89,15 @@ func (w *WiegandHW) GetCardUid() (uint64, error) {
|
|||||||
|
|
||||||
func (w *WiegandHW) wiegandAEvent(evt gpiod.LineEvent) {
|
func (w *WiegandHW) wiegandAEvent(evt gpiod.LineEvent) {
|
||||||
w.bitTimeoutTimer.Reset(w.bitTimeout)
|
w.bitTimeoutTimer.Reset(w.bitTimeout)
|
||||||
w.bits[w.bitNr] = false
|
w.bits[w.bitNr] = true
|
||||||
fmt.Printf("0")
|
fmt.Printf("1")
|
||||||
w.bitNr += 1
|
w.bitNr += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WiegandHW) wiegandBEvent(evt gpiod.LineEvent) {
|
func (w *WiegandHW) wiegandBEvent(evt gpiod.LineEvent) {
|
||||||
w.bitTimeoutTimer.Reset(w.bitTimeout)
|
w.bitTimeoutTimer.Reset(w.bitTimeout)
|
||||||
w.bits[w.bitNr] = true
|
w.bits[w.bitNr] = false
|
||||||
fmt.Printf("1")
|
fmt.Printf("0")
|
||||||
w.bitNr += 1
|
w.bitNr += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,3 +139,10 @@ func (w *WiegandHW) WiegandClose() {
|
|||||||
w.bLine.Close()
|
w.bLine.Close()
|
||||||
w.solenoidLine.Close()
|
w.solenoidLine.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func printCardId(card uint64) {
|
||||||
|
for i := 0; i < 7; i++ {
|
||||||
|
fmt.Printf("%02x", (card>>(8*i))&0xff)
|
||||||
|
}
|
||||||
|
fmt.Printf("\n")
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user