Checkpoint 2
This commit is contained in:
		
							
								
								
									
										1
									
								
								.env
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								.env
									
									
									
									
									
								
							| @@ -1,3 +1,4 @@ | ||||
| KDOORPI_API_ALLOWED=http://127.0.0.1:3333/allowed | ||||
| KDOORPI_API_LONGPOLL=http://127.0.0.1:3333/longpoll | ||||
| KDOORPI_API_KEY=keykey | ||||
| KDOORPI_MOCK_HW=true | ||||
|   | ||||
							
								
								
									
										125
									
								
								godoor.go
									
									
									
									
									
								
							
							
						
						
									
										125
									
								
								godoor.go
									
									
									
									
									
								
							| @@ -1,6 +1,7 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"context" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| @@ -9,6 +10,8 @@ import ( | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"os/signal" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"syscall" | ||||
| ) | ||||
| import "time" | ||||
| @@ -37,6 +40,7 @@ type ValidUids map[string]bool // bool has no meaning | ||||
| type Config struct { | ||||
| 	door     string | ||||
| 	uid_salt string | ||||
| 	mock     string | ||||
| 	api      struct { | ||||
| 		allowed  string | ||||
| 		longpoll string | ||||
| @@ -46,6 +50,9 @@ type Config struct { | ||||
| } | ||||
|  | ||||
| var config Config | ||||
| var globalLock sync.Mutex | ||||
| var validUids ValidUids | ||||
| var wiegand Wiegand | ||||
|  | ||||
| func main() { | ||||
|  | ||||
| @@ -60,16 +67,31 @@ func main() { | ||||
| 	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") | ||||
|  | ||||
| 	//wiegand := WiegandSetup(wiegand_a, wiegand_b, wiegand_bit_timeout, solenoid) | ||||
| 	if config.mock == "true" { | ||||
| 		wiegand = &WiegandMock{} | ||||
| 	} else { | ||||
| 		wiegand = WiegandSetup(wiegand_a, wiegand_b, wiegand_bit_timeout, solenoid) | ||||
| 	} | ||||
|  | ||||
| 	http.DefaultClient.Timeout = 60 | ||||
| 	http.DefaultClient.Timeout = 120 * time.Second | ||||
|  | ||||
| 	reloadTokens() | ||||
| 	for { | ||||
| 		err := reloadTokens() | ||||
| 		if err == nil { | ||||
| 			break | ||||
| 		} | ||||
| 		time.Sleep(10 * time.Second) | ||||
| 	} | ||||
|  | ||||
| 	//go wiegand.cardRunner(validUids) | ||||
| 	go cardRunner(wiegand) | ||||
|  | ||||
| 	go func() { | ||||
| 		for { | ||||
| 			waitEvents() | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	fmt.Printf("Sleeping\n") | ||||
|  | ||||
| @@ -79,36 +101,101 @@ func main() { | ||||
| 	// cleanup | ||||
| } | ||||
|  | ||||
| func waitEvents() { | ||||
| func cardRunner(w Wiegand) { | ||||
| 	for { | ||||
| 		card, err := w.GetCardUid() | ||||
| 		if err != nil { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		printCardId(card) | ||||
| 		hashedHex := hashCardUid(card) | ||||
| 		fmt.Println(hashedHex) | ||||
|  | ||||
| 		globalLock.Lock() | ||||
| 		ok := validUids[hashedHex] | ||||
| 		globalLock.Unlock() | ||||
| 		if ok { | ||||
| 			fmt.Println("Opening door") | ||||
| 			w.OpenDoor() | ||||
| 		} else { | ||||
| 			fmt.Println("Unknown card") | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func ParseNextMessage(r *bufio.Reader) (string, error) { | ||||
| 	var message string | ||||
|  | ||||
| 	for { | ||||
| 		s, err := r.ReadString('\n') | ||||
| 		if err != nil { | ||||
| 			return message, err | ||||
| 		} | ||||
|  | ||||
| 		message = message + s | ||||
| 		nextBytes, err := r.ReadByte() | ||||
| 		if err != nil { | ||||
| 			return message, err | ||||
| 		} | ||||
|  | ||||
| 		if nextBytes == '\n' { | ||||
| 			return message, nil | ||||
| 		} | ||||
|  | ||||
| 		r.UnreadByte() | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| func waitEvents() error { | ||||
| 	req, err := http.NewRequest(http.MethodGet, config.api.longpoll, nil) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 		return err | ||||
| 	} | ||||
| 	req.Header.Add("KEY", config.api.key) | ||||
| 	resp, err := http.DefaultClient.Do(req) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 		return err | ||||
| 	} | ||||
| 	fmt.Printf("%v\n", resp) | ||||
|  | ||||
| 	_, err = io.ReadAll(resp.Body) | ||||
| 	reader := bufio.NewReader(resp.Body) | ||||
| 	for { | ||||
| 		msg, err := ParseNextMessage(reader) | ||||
| 		if err != nil { | ||||
| 		panic(err) | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		for _, line := range strings.Split(msg, "\n") { | ||||
| 			data, found_data := strings.CutPrefix(line, "data:") | ||||
| 			if !found_data { | ||||
| 				continue | ||||
| 			} | ||||
| 			fmt.Printf("got server data: %q\n", data) | ||||
| 			if strings.TrimSpace(data) == config.door { | ||||
| 				wiegand.OpenDoor() | ||||
| 			} | ||||
|  | ||||
| 			go reloadTokens() | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	fmt.Printf("%v\n", resp) | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func reloadTokens() { | ||||
| func reloadTokens() error { | ||||
| 	req, err := http.NewRequest(http.MethodGet, config.api.allowed, nil) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 		return err | ||||
| 	} | ||||
| 	req.Header.Add("KEY", config.api.key) | ||||
| 	resp, err := http.DefaultClient.Do(req) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 		return err | ||||
| 	} | ||||
| 	fmt.Printf("%v\n", resp) | ||||
|  | ||||
| @@ -116,17 +203,21 @@ func reloadTokens() { | ||||
|  | ||||
| 	body, err := io.ReadAll(resp.Body) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	err = json.Unmarshal(body, &cl) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	validUids := make(ValidUids) | ||||
| 	globalLock.Lock() | ||||
| 	defer globalLock.Unlock() | ||||
| 	validUids = make(ValidUids) | ||||
| 	for i, val := range cl.AllowedUids { | ||||
| 		fmt.Printf("%d: %+v\n", i, val.Token.UidHash) | ||||
| 		validUids[val.Token.UidHash] = false | ||||
| 		validUids[val.Token.UidHash] = true | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|   | ||||
| @@ -9,6 +9,7 @@ import ( | ||||
| 	"io" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| type DoorBoyServer struct { | ||||
| @@ -36,7 +37,7 @@ func getAllowed(w http.ResponseWriter, r *http.Request) { | ||||
| 	w.Header().Set("Content-Type", "application/json") | ||||
| 	keys := cardList{ | ||||
| 		AllowedUids: []cardToken{ | ||||
| 			{card{UidHash: "0a0b0c0d0e0f"}}, | ||||
| 			{card{UidHash: "d72c87d0f077c7766f2985dfab30e8955c373a13a1e93d315203939f542ff86e73ee37c31f4c4b571f4719fa8e3589f12db8dcb57ea9f56764bb7d58f64cf705"}}, | ||||
| 			{card{UidHash: "112233445566"}}, | ||||
| 			{card{UidHash: "aabbccddeeff"}}, | ||||
| 		}, | ||||
| @@ -66,29 +67,42 @@ func (doorboyserver *DoorBoyServer) getLongPoll(w http.ResponseWriter, r *http.R | ||||
| 	} | ||||
| 	events := make(chan string) | ||||
| 	doorboyserver.longPollers[r.RemoteAddr] = events | ||||
| 	defer delete(doorboyserver.longPollers, r.RemoteAddr) | ||||
|  | ||||
| 	err = writeAndFlush(w, []byte("data: watch-stream-opened\n\n")) | ||||
| 	if err != nil { | ||||
| 		delete(doorboyserver.longPollers, r.RemoteAddr) | ||||
| 		return | ||||
| 	} | ||||
| 	d := <-events // get door open event | ||||
| 	_, err = w.Write([]byte("data: ")) | ||||
| 	if err != nil { | ||||
| 		delete(doorboyserver.longPollers, r.RemoteAddr) | ||||
| 		return | ||||
| 	} | ||||
| 	_, err = w.Write([]byte(d)) | ||||
| 	if err != nil { | ||||
| 		delete(doorboyserver.longPollers, r.RemoteAddr) | ||||
| 		return | ||||
| 	} | ||||
| 	err = writeAndFlush(w, []byte("\n\n")) | ||||
| 	if err != nil { | ||||
| 		delete(doorboyserver.longPollers, r.RemoteAddr) | ||||
| 		return | ||||
| 	} | ||||
| 	delete(doorboyserver.longPollers, r.RemoteAddr) | ||||
| } | ||||
|  | ||||
| func (doorboyserver *DoorBoyServer) getLongPollJson(w http.ResponseWriter, r *http.Request) { | ||||
| 	flusher, ok := w.(http.Flusher) | ||||
| 	if !ok { | ||||
| 		fmt.Println("Failed to get flusher for http response") | ||||
| 	} | ||||
| 	w.WriteHeader(http.StatusOK) | ||||
| 	flusher.Flush() | ||||
| 	respJson, _ := json.Marshal([]string{"123456789", "aabbccddeeff", "aabbcc"}) | ||||
| 	for i := 1; i <= 10; i++ { | ||||
| 		w.Write(respJson) | ||||
| 		w.Write([]byte("\n")) | ||||
| 		fmt.Println("send") | ||||
| 		flusher.Flush() // Trigger "chunked" encoding and send a chunk... | ||||
| 		time.Sleep(500 * time.Millisecond) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (doobserver *DoorBoyServer) postOpenDoor(w http.ResponseWriter, r *http.Request) { | ||||
| @@ -105,6 +119,7 @@ func main() { | ||||
| 	http.HandleFunc("/", getRoot) | ||||
| 	http.HandleFunc("/allowed", getAllowed) | ||||
| 	http.HandleFunc("/longpoll", doorboyserver.getLongPoll) | ||||
| 	http.HandleFunc("/jspoll", doorboyserver.getLongPollJson) | ||||
| 	http.HandleFunc("/open", doorboyserver.postOpenDoor) | ||||
|  | ||||
| 	err := http.ListenAndServe(":3333", nil) | ||||
|   | ||||
							
								
								
									
										62
									
								
								wiegand.go
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								wiegand.go
									
									
									
									
									
								
							| @@ -12,7 +12,12 @@ import ( | ||||
| 	"github.com/warthog618/gpiod" | ||||
| ) | ||||
|  | ||||
| type Wiegand struct { | ||||
| type Wiegand interface { | ||||
| 	GetCardUid() (uint64, error) | ||||
| 	OpenDoor() | ||||
| } | ||||
|  | ||||
| type WiegandHW struct { | ||||
| 	aLine           *gpiod.Line | ||||
| 	bLine           *gpiod.Line | ||||
| 	bits            [64]bool | ||||
| @@ -23,7 +28,7 @@ type Wiegand struct { | ||||
| 	solenoidLine *gpiod.Line | ||||
| } | ||||
|  | ||||
| func (w *Wiegand) OpenDoor() { | ||||
| func (w *WiegandHW) OpenDoor() { | ||||
| 	fmt.Println("Open") | ||||
| 	w.solenoidLine.SetValue(1) | ||||
| 	d, _ := time.ParseDuration("500ms") | ||||
| @@ -39,15 +44,29 @@ func printCardId(card uint64) { | ||||
| 	fmt.Printf("\n") | ||||
| } | ||||
|  | ||||
| func (w *Wiegand) cardRunner(validUids ValidUids) { | ||||
| 	for { | ||||
| 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) | ||||
| 	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) { | ||||
| 	// Wait for bit timeout | ||||
| 	fmt.Printf("Waiting for bit timeout\n") | ||||
|  | ||||
| 	<-w.bitTimeoutTimer.C | ||||
| 	fmt.Printf("\n") | ||||
|  | ||||
| 	defer func() { w.bitNr = 0 }() | ||||
|  | ||||
| 	if w.bitNr != 64 { | ||||
| 			fmt.Printf("We got less than 64 bits: %d\n", w.bitNr) | ||||
| 		return 0, fmt.Errorf("We got less than 64 bits: %d\n", w.bitNr) | ||||
| 	} | ||||
|  | ||||
| 	var card uint64 = 0 | ||||
| @@ -57,47 +76,26 @@ func (w *Wiegand) cardRunner(validUids ValidUids) { | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 		printCardId(card) | ||||
|  | ||||
| 		b := make([]byte, 8) | ||||
| 		binary.LittleEndian.PutUint64(b, card) | ||||
| 		hashed, err := scrypt.Key(b, []byte("hashsah"), 16384, 8, 1, 64) | ||||
| 		if err != nil { | ||||
| 			panic(err) | ||||
| 	return card, nil | ||||
| } | ||||
|  | ||||
| 		hashedHex := hex.EncodeToString(hashed) | ||||
| 		fmt.Println(hashedHex) | ||||
|  | ||||
| 		_, ok := validUids[hashedHex] | ||||
| 		if ok { | ||||
| 			fmt.Println("Opening door") | ||||
| 			w.OpenDoor() | ||||
| 		} else { | ||||
| 			fmt.Println("Unknown card") | ||||
| 		} | ||||
|  | ||||
| 		w.bitNr = 0 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (w *Wiegand) wiegandAEvent(evt gpiod.LineEvent) { | ||||
| func (w *WiegandHW) wiegandAEvent(evt gpiod.LineEvent) { | ||||
| 	w.bitTimeoutTimer.Reset(w.bitTimeout) | ||||
| 	w.bits[w.bitNr] = false | ||||
| 	fmt.Printf("0") | ||||
| 	w.bitNr += 1 | ||||
| } | ||||
|  | ||||
| func (w *Wiegand) wiegandBEvent(evt gpiod.LineEvent) { | ||||
| func (w *WiegandHW) wiegandBEvent(evt gpiod.LineEvent) { | ||||
| 	w.bitTimeoutTimer.Reset(w.bitTimeout) | ||||
| 	w.bits[w.bitNr] = true | ||||
| 	fmt.Printf("1") | ||||
| 	w.bitNr += 1 | ||||
| } | ||||
|  | ||||
| func WiegandSetup(a int, b int, bitTimeout time.Duration, solenoid int) *Wiegand { | ||||
| func WiegandSetup(a int, b int, bitTimeout time.Duration, solenoid int) *WiegandHW { | ||||
|  | ||||
| 	var wiegand Wiegand | ||||
| 	var wiegand WiegandHW | ||||
| 	wiegand.bitTimeout = bitTimeout | ||||
|  | ||||
| 	wiegand.bitTimeoutTimer = time.NewTimer(wiegand.bitTimeout) | ||||
| @@ -128,7 +126,7 @@ func WiegandSetup(a int, b int, bitTimeout time.Duration, solenoid int) *Wiegand | ||||
| 	return &wiegand | ||||
| } | ||||
|  | ||||
| func (w *Wiegand) WiegandClose() { | ||||
| func (w *WiegandHW) WiegandClose() { | ||||
| 	w.aLine.Close() | ||||
| 	w.bLine.Close() | ||||
| 	w.solenoidLine.Close() | ||||
|   | ||||
							
								
								
									
										25
									
								
								wiegand_mock.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								wiegand_mock.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| type WiegandMock struct { | ||||
| 	mockUid uint64 | ||||
| } | ||||
|  | ||||
| func (*WiegandMock) OpenDoor() { | ||||
| 	fmt.Println("Door is now open") | ||||
| 	time.Sleep(500 * time.Millisecond) | ||||
| 	fmt.Println("Door is now closed") | ||||
| } | ||||
|  | ||||
| func (w *WiegandMock) GetCardUid() (uint64, error) { | ||||
| 	time.Sleep(1 * time.Second) | ||||
| 	return w.mockUid, fmt.Errorf("err") | ||||
| } | ||||
|  | ||||
| func (w *WiegandMock) SetMockUid(mockUid uint64) { | ||||
| 	w.mockUid = mockUid | ||||
| } | ||||
		Reference in New Issue
	
	Block a user