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_ALLOWED=http://127.0.0.1:3333/allowed | ||||||
| KDOORPI_API_LONGPOLL=http://127.0.0.1:3333/longpoll | KDOORPI_API_LONGPOLL=http://127.0.0.1:3333/longpoll | ||||||
| KDOORPI_API_KEY=keykey | KDOORPI_API_KEY=keykey | ||||||
|  | KDOORPI_MOCK_HW=true | ||||||
|   | |||||||
							
								
								
									
										145
									
								
								godoor.go
									
									
									
									
									
								
							
							
						
						
									
										145
									
								
								godoor.go
									
									
									
									
									
								
							| @@ -1,6 +1,7 @@ | |||||||
| package main | package main | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"bufio" | ||||||
| 	"context" | 	"context" | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| @@ -9,6 +10,8 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"os" | 	"os" | ||||||
| 	"os/signal" | 	"os/signal" | ||||||
|  | 	"strings" | ||||||
|  | 	"sync" | ||||||
| 	"syscall" | 	"syscall" | ||||||
| ) | ) | ||||||
| import "time" | import "time" | ||||||
| @@ -37,6 +40,7 @@ type ValidUids map[string]bool // bool has no meaning | |||||||
| type Config struct { | type Config struct { | ||||||
| 	door     string | 	door     string | ||||||
| 	uid_salt string | 	uid_salt string | ||||||
|  | 	mock     string | ||||||
| 	api      struct { | 	api      struct { | ||||||
| 		allowed  string | 		allowed  string | ||||||
| 		longpoll string | 		longpoll string | ||||||
| @@ -46,6 +50,9 @@ type Config struct { | |||||||
| } | } | ||||||
|  |  | ||||||
| var config Config | var config Config | ||||||
|  | var globalLock sync.Mutex | ||||||
|  | var validUids ValidUids | ||||||
|  | var wiegand Wiegand | ||||||
|  |  | ||||||
| func main() { | func main() { | ||||||
|  |  | ||||||
| @@ -60,16 +67,31 @@ func main() { | |||||||
| 	config.api.swipe = os.Getenv("KDOORPI_API_SWIPE") | 	config.api.swipe = os.Getenv("KDOORPI_API_SWIPE") | ||||||
| 	config.api.key = os.Getenv("KDOORPI_API_KEY") | 	config.api.key = os.Getenv("KDOORPI_API_KEY") | ||||||
| 	config.uid_salt = os.Getenv("KDOORPI_UID_SALT") | 	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() | 			waitEvents() | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  |  | ||||||
| 	fmt.Printf("Sleeping\n") | 	fmt.Printf("Sleeping\n") | ||||||
|  |  | ||||||
| @@ -79,36 +101,101 @@ func main() { | |||||||
| 	// cleanup | 	// cleanup | ||||||
| } | } | ||||||
|  |  | ||||||
| func waitEvents() { | func cardRunner(w Wiegand) { | ||||||
| 	req, err := http.NewRequest(http.MethodGet, config.api.longpoll, nil) | 	for { | ||||||
|  | 		card, err := w.GetCardUid() | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 		panic(err) | 			continue | ||||||
| 	} |  | ||||||
| 	req.Header.Add("KEY", config.api.key) |  | ||||||
| 	resp, err := http.DefaultClient.Do(req) |  | ||||||
| 	if err != nil { |  | ||||||
| 		panic(err) |  | ||||||
| 	} |  | ||||||
| 	fmt.Printf("%v\n", resp) |  | ||||||
|  |  | ||||||
| 	_, err = io.ReadAll(resp.Body) |  | ||||||
| 	if err != nil { |  | ||||||
| 		panic(err) |  | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 	fmt.Printf("%v\n", resp) | 		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 reloadTokens() { | func waitEvents() error { | ||||||
| 	req, err := http.NewRequest(http.MethodGet, config.api.allowed, nil) | 	req, err := http.NewRequest(http.MethodGet, config.api.longpoll, nil) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		panic(err) | 		return err | ||||||
| 	} | 	} | ||||||
| 	req.Header.Add("KEY", config.api.key) | 	req.Header.Add("KEY", config.api.key) | ||||||
| 	resp, err := http.DefaultClient.Do(req) | 	resp, err := http.DefaultClient.Do(req) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		panic(err) | 		return err | ||||||
|  | 	} | ||||||
|  | 	fmt.Printf("%v\n", resp) | ||||||
|  |  | ||||||
|  | 	reader := bufio.NewReader(resp.Body) | ||||||
|  | 	for { | ||||||
|  | 		msg, err := ParseNextMessage(reader) | ||||||
|  | 		if err != nil { | ||||||
|  | 			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() error { | ||||||
|  | 	req, err := http.NewRequest(http.MethodGet, config.api.allowed, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	req.Header.Add("KEY", config.api.key) | ||||||
|  | 	resp, err := http.DefaultClient.Do(req) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
| 	} | 	} | ||||||
| 	fmt.Printf("%v\n", resp) | 	fmt.Printf("%v\n", resp) | ||||||
|  |  | ||||||
| @@ -116,17 +203,21 @@ func reloadTokens() { | |||||||
|  |  | ||||||
| 	body, err := io.ReadAll(resp.Body) | 	body, err := io.ReadAll(resp.Body) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		panic(err) | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	err = json.Unmarshal(body, &cl) | 	err = json.Unmarshal(body, &cl) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		panic(err) | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	validUids := make(ValidUids) | 	globalLock.Lock() | ||||||
|  | 	defer globalLock.Unlock() | ||||||
|  | 	validUids = make(ValidUids) | ||||||
| 	for i, val := range cl.AllowedUids { | 	for i, val := range cl.AllowedUids { | ||||||
| 		fmt.Printf("%d: %+v\n", i, val.Token.UidHash) | 		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" | 	"io" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"os" | 	"os" | ||||||
|  | 	"time" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type DoorBoyServer struct { | type DoorBoyServer struct { | ||||||
| @@ -36,7 +37,7 @@ func getAllowed(w http.ResponseWriter, r *http.Request) { | |||||||
| 	w.Header().Set("Content-Type", "application/json") | 	w.Header().Set("Content-Type", "application/json") | ||||||
| 	keys := cardList{ | 	keys := cardList{ | ||||||
| 		AllowedUids: []cardToken{ | 		AllowedUids: []cardToken{ | ||||||
| 			{card{UidHash: "0a0b0c0d0e0f"}}, | 			{card{UidHash: "d72c87d0f077c7766f2985dfab30e8955c373a13a1e93d315203939f542ff86e73ee37c31f4c4b571f4719fa8e3589f12db8dcb57ea9f56764bb7d58f64cf705"}}, | ||||||
| 			{card{UidHash: "112233445566"}}, | 			{card{UidHash: "112233445566"}}, | ||||||
| 			{card{UidHash: "aabbccddeeff"}}, | 			{card{UidHash: "aabbccddeeff"}}, | ||||||
| 		}, | 		}, | ||||||
| @@ -66,29 +67,42 @@ func (doorboyserver *DoorBoyServer) getLongPoll(w http.ResponseWriter, r *http.R | |||||||
| 	} | 	} | ||||||
| 	events := make(chan string) | 	events := make(chan string) | ||||||
| 	doorboyserver.longPollers[r.RemoteAddr] = events | 	doorboyserver.longPollers[r.RemoteAddr] = events | ||||||
|  | 	defer delete(doorboyserver.longPollers, r.RemoteAddr) | ||||||
|  |  | ||||||
| 	err = writeAndFlush(w, []byte("data: watch-stream-opened\n\n")) | 	err = writeAndFlush(w, []byte("data: watch-stream-opened\n\n")) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		delete(doorboyserver.longPollers, r.RemoteAddr) |  | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	d := <-events // get door open event | 	d := <-events // get door open event | ||||||
| 	_, err = w.Write([]byte("data: ")) | 	_, err = w.Write([]byte("data: ")) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		delete(doorboyserver.longPollers, r.RemoteAddr) |  | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	_, err = w.Write([]byte(d)) | 	_, err = w.Write([]byte(d)) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		delete(doorboyserver.longPollers, r.RemoteAddr) |  | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	err = writeAndFlush(w, []byte("\n\n")) | 	err = writeAndFlush(w, []byte("\n\n")) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		delete(doorboyserver.longPollers, r.RemoteAddr) |  | ||||||
| 		return | 		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) { | func (doobserver *DoorBoyServer) postOpenDoor(w http.ResponseWriter, r *http.Request) { | ||||||
| @@ -105,6 +119,7 @@ func main() { | |||||||
| 	http.HandleFunc("/", getRoot) | 	http.HandleFunc("/", getRoot) | ||||||
| 	http.HandleFunc("/allowed", getAllowed) | 	http.HandleFunc("/allowed", getAllowed) | ||||||
| 	http.HandleFunc("/longpoll", doorboyserver.getLongPoll) | 	http.HandleFunc("/longpoll", doorboyserver.getLongPoll) | ||||||
|  | 	http.HandleFunc("/jspoll", doorboyserver.getLongPollJson) | ||||||
| 	http.HandleFunc("/open", doorboyserver.postOpenDoor) | 	http.HandleFunc("/open", doorboyserver.postOpenDoor) | ||||||
|  |  | ||||||
| 	err := http.ListenAndServe(":3333", nil) | 	err := http.ListenAndServe(":3333", nil) | ||||||
|   | |||||||
							
								
								
									
										62
									
								
								wiegand.go
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								wiegand.go
									
									
									
									
									
								
							| @@ -12,7 +12,12 @@ import ( | |||||||
| 	"github.com/warthog618/gpiod" | 	"github.com/warthog618/gpiod" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type Wiegand struct { | type Wiegand interface { | ||||||
|  | 	GetCardUid() (uint64, error) | ||||||
|  | 	OpenDoor() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type WiegandHW struct { | ||||||
| 	aLine           *gpiod.Line | 	aLine           *gpiod.Line | ||||||
| 	bLine           *gpiod.Line | 	bLine           *gpiod.Line | ||||||
| 	bits            [64]bool | 	bits            [64]bool | ||||||
| @@ -23,7 +28,7 @@ type Wiegand struct { | |||||||
| 	solenoidLine *gpiod.Line | 	solenoidLine *gpiod.Line | ||||||
| } | } | ||||||
|  |  | ||||||
| func (w *Wiegand) OpenDoor() { | func (w *WiegandHW) OpenDoor() { | ||||||
| 	fmt.Println("Open") | 	fmt.Println("Open") | ||||||
| 	w.solenoidLine.SetValue(1) | 	w.solenoidLine.SetValue(1) | ||||||
| 	d, _ := time.ParseDuration("500ms") | 	d, _ := time.ParseDuration("500ms") | ||||||
| @@ -39,15 +44,29 @@ func printCardId(card uint64) { | |||||||
| 	fmt.Printf("\n") | 	fmt.Printf("\n") | ||||||
| } | } | ||||||
|  |  | ||||||
| func (w *Wiegand) cardRunner(validUids ValidUids) { | func hashCardUid(card uint64) string { | ||||||
| 	for { | 	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 | 	// Wait for bit timeout | ||||||
| 	fmt.Printf("Waiting for bit timeout\n") | 	fmt.Printf("Waiting for bit timeout\n") | ||||||
|  |  | ||||||
| 	<-w.bitTimeoutTimer.C | 	<-w.bitTimeoutTimer.C | ||||||
| 	fmt.Printf("\n") | 	fmt.Printf("\n") | ||||||
|  |  | ||||||
|  | 	defer func() { w.bitNr = 0 }() | ||||||
|  |  | ||||||
| 	if w.bitNr != 64 { | 	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 | 	var card uint64 = 0 | ||||||
| @@ -57,47 +76,26 @@ func (w *Wiegand) cardRunner(validUids ValidUids) { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 		printCardId(card) | 	return card, nil | ||||||
|  |  | ||||||
| 		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) |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		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.bitTimeoutTimer.Reset(w.bitTimeout) | ||||||
| 	w.bits[w.bitNr] = false | 	w.bits[w.bitNr] = false | ||||||
| 	fmt.Printf("0") | 	fmt.Printf("0") | ||||||
| 	w.bitNr += 1 | 	w.bitNr += 1 | ||||||
| } | } | ||||||
|  |  | ||||||
| func (w *Wiegand) 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] = true | ||||||
| 	fmt.Printf("1") | 	fmt.Printf("1") | ||||||
| 	w.bitNr += 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.bitTimeout = bitTimeout | ||||||
|  |  | ||||||
| 	wiegand.bitTimeoutTimer = time.NewTimer(wiegand.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 | 	return &wiegand | ||||||
| } | } | ||||||
|  |  | ||||||
| func (w *Wiegand) WiegandClose() { | func (w *WiegandHW) WiegandClose() { | ||||||
| 	w.aLine.Close() | 	w.aLine.Close() | ||||||
| 	w.bLine.Close() | 	w.bLine.Close() | ||||||
| 	w.solenoidLine.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