Successful RFID implementation
This commit is contained in:
parent
2bf5c0ff99
commit
75035e2751
11
go.mod
11
go.mod
@ -4,15 +4,18 @@ go 1.21.4
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
git.tcp.direct/kayos/zwrap v0.4.2
|
git.tcp.direct/kayos/zwrap v0.4.2
|
||||||
github.com/cretz/bine v0.2.0
|
github.com/clausecker/nfc/v2 v2.1.4
|
||||||
github.com/davecgh/go-spew v1.1.0
|
github.com/davecgh/go-spew v1.1.1
|
||||||
|
github.com/pelletier/go-toml/v2 v2.1.0
|
||||||
github.com/rs/zerolog v1.31.0
|
github.com/rs/zerolog v1.31.0
|
||||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a
|
github.com/stianeikeland/go-rpio/v4 v4.6.0
|
||||||
|
periph.io/x/conn/v3 v3.7.0
|
||||||
|
periph.io/x/devices/v3 v3.7.1
|
||||||
|
periph.io/x/host/v3 v3.8.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5 // indirect
|
|
||||||
golang.org/x/sys v0.12.0 // indirect
|
golang.org/x/sys v0.12.0 // indirect
|
||||||
)
|
)
|
||||||
|
41
go.sum
41
go.sum
@ -1,11 +1,14 @@
|
|||||||
git.tcp.direct/kayos/zwrap v0.4.2 h1:yXO21VNkAb+iMi3dOAythw42dvv1bVzXw+TJJXENVdQ=
|
git.tcp.direct/kayos/zwrap v0.4.2 h1:yXO21VNkAb+iMi3dOAythw42dvv1bVzXw+TJJXENVdQ=
|
||||||
git.tcp.direct/kayos/zwrap v0.4.2/go.mod h1:SA9+Sww1LBKMw54Gjot5t4AhwEgRX1lFhy6pv5Mm78Q=
|
git.tcp.direct/kayos/zwrap v0.4.2/go.mod h1:SA9+Sww1LBKMw54Gjot5t4AhwEgRX1lFhy6pv5Mm78Q=
|
||||||
|
github.com/clausecker/nfc/v2 v2.1.4 h1:zw2Cnny7pxPnuxVMBo+DXqXYETzUN7pMhNEA61yT5gY=
|
||||||
|
github.com/clausecker/nfc/v2 v2.1.4/go.mod h1:BjRBQUQTQmiwh2tEfQ+xBM5xY05sV2gnZ0JRYEHog/o=
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
|
|
||||||
github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI=
|
|
||||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
github.com/jonboulle/clockwork v0.3.0 h1:9BSCMi8C+0qdApAp4auwX0RkLGUjs956h0EkuQymUhg=
|
||||||
|
github.com/jonboulle/clockwork v0.3.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
|
||||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
@ -13,6 +16,8 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k
|
|||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
@ -20,26 +25,28 @@ github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
|||||||
github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w=
|
github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w=
|
||||||
github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
|
github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
|
||||||
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||||
|
github.com/stianeikeland/go-rpio/v4 v4.6.0 h1:eAJgtw3jTtvn/CqwbC82ntcS+dtzUTgo5qlZKe677EY=
|
||||||
|
github.com/stianeikeland/go-rpio/v4 v4.6.0/go.mod h1:A3GvHxC1Om5zaId+HqB3HKqx4K/AqeckxB7qRjxMK7o=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5 h1:wjuX4b5yYQnEQHzd+CBcrcC6OVR2J1CN6mUy0oSxIPo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
periph.io/x/conn/v3 v3.7.0 h1:f1EXLn4pkf7AEWwkol2gilCNZ0ElY+bxS4WE2PQXfrA=
|
||||||
|
periph.io/x/conn/v3 v3.7.0/go.mod h1:ypY7UVxgDbP9PJGwFSVelRRagxyXYfttVh7hJZUHEhg=
|
||||||
|
periph.io/x/devices/v3 v3.7.1 h1:BsExlfYJlZUZoawzpMF7ksgC9f1eBAdqvKRCGvb+VYw=
|
||||||
|
periph.io/x/devices/v3 v3.7.1/go.mod h1:ezQOe8WknDaMbKZXVwQUQkIauyLyJshwAHkIohHXA94=
|
||||||
|
periph.io/x/host/v3 v3.8.0 h1:T5ojZ2wvnZHGPS4h95N2ZpcCyHnsvH3YRZ1UUUiv5CQ=
|
||||||
|
periph.io/x/host/v3 v3.8.0/go.mod h1:rzOLH+2g9bhc6pWZrkCrmytD4igwQ2vxFw6Wn6ZOlLY=
|
||||||
|
152
main.go
152
main.go
@ -1,104 +1,92 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"os"
|
||||||
"net"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.tcp.direct/kayos/zwrap"
|
|
||||||
"github.com/cretz/bine/tor"
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
"github.com/stianeikeland/go-rpio/v4"
|
||||||
|
|
||||||
|
"git.tcp.direct/kayos/door5/config"
|
||||||
|
"git.tcp.direct/kayos/door5/iot"
|
||||||
"git.tcp.direct/kayos/door5/logger"
|
"git.tcp.direct/kayos/door5/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Knobs struct {
|
var conf *config.Config
|
||||||
conf tor.ListenConf
|
|
||||||
log zwrap.Logger
|
func OpenDoor() {
|
||||||
tor.Tor
|
pin := rpio.Pin(conf.Pins[iot.Door])
|
||||||
}
|
pin.Output()
|
||||||
type BlueDoor struct {
|
pin.High()
|
||||||
conf *Knobs
|
|
||||||
listeners []net.Listener
|
|
||||||
localPaths []string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func MemoryMapOSFolder() (path string, err error) {
|
func CloseDoor() {
|
||||||
// create memory mapped folder for tor data directory (linux)
|
pin := rpio.Pin(conf.Pins[iot.Door])
|
||||||
// un
|
pin.Output()
|
||||||
return "", nil
|
pin.Low()
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewListener(listen string) (net.Listener, error) {
|
func watchButton() {
|
||||||
l, err := net.Listen("tcp", listen)
|
logger.Get().Logger.Debug().Uint8("pin", uint8(conf.Pins[iot.Button])).Msg("exit button daemon started")
|
||||||
if err != nil {
|
defer logger.Get().Logger.Debug().Msg("exit button daemon stopped")
|
||||||
|
pin := rpio.Pin(conf.Pins[iot.Button])
|
||||||
}
|
pin.Input()
|
||||||
}
|
pin.PullUp()
|
||||||
|
count := 0
|
||||||
func NewOnion() (*tor.Tor, error) {
|
for {
|
||||||
log := logger.Get()
|
if pin.Read() != rpio.Low {
|
||||||
|
time.Sleep(5 * time.Millisecond)
|
||||||
log.Logger.Info().Msg("starting and registering onion service...")
|
if count > 0 {
|
||||||
|
count--
|
||||||
t, err := tor.Start(nil, &tor.StartConf{
|
}
|
||||||
RetainTempDataDir: false,
|
continue
|
||||||
DebugWriter: logger.Get().With().Str("module", "tor").Logger(),
|
|
||||||
|
|
||||||
NoAutoSocksPort: false,
|
|
||||||
GeoIPFileReader: nil,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Panicf("Unable to start Tor: %v", err)
|
|
||||||
}
|
|
||||||
defer func(t *tor.Tor) {
|
|
||||||
if err = t.Close(); err != nil {
|
|
||||||
log.Panicf("Unable to close Tor: %v", err)
|
|
||||||
}
|
}
|
||||||
}(t)
|
count++
|
||||||
|
if count >= 100 {
|
||||||
// Wait at most a few minutes to publish the service
|
logger.Get().Logger.Info().Int("count", count).Msg("door opened")
|
||||||
listenCtx, listenCancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
OpenDoor()
|
||||||
defer listenCancel()
|
time.Sleep(5 * time.Second)
|
||||||
|
CloseDoor()
|
||||||
// Create a v3 onion service to listen on any port but show as 80
|
count = 0
|
||||||
onion, err := t.Listen(listenCtx, &tor.ListenConf{
|
|
||||||
LocalPort: 0,
|
|
||||||
LocalListener: nil,
|
|
||||||
RemotePorts: []int{80},
|
|
||||||
Key: nil,
|
|
||||||
Version3: false,
|
|
||||||
ClientAuths: nil,
|
|
||||||
MaxStreams: 0,
|
|
||||||
DiscardKey: false,
|
|
||||||
Detach: false,
|
|
||||||
NonAnonymous: false,
|
|
||||||
MaxStreamsCloseCircuit: false,
|
|
||||||
NoWait: false,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Panicf("Unable to create onion service: %v", err)
|
|
||||||
}
|
|
||||||
defer func(onion *tor.OnionService) {
|
|
||||||
if err = onion.Close(); err != nil {
|
|
||||||
log.Panicf("Unable to close onion service: %v", err)
|
|
||||||
}
|
}
|
||||||
}(onion)
|
time.Sleep(5 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
log.Logger.Info().Msgf("live: %v.onion", onion.ID)
|
func loadConfig() {
|
||||||
return t, nil
|
var err error
|
||||||
|
path, _ := os.UserConfigDir()
|
||||||
|
path = filepath.Join(path, "door5", "config.toml")
|
||||||
|
if conf, err = config.ReadConfig(path); err != nil {
|
||||||
|
println(err.Error())
|
||||||
|
println("Writing default config")
|
||||||
|
_ = os.MkdirAll(filepath.Dir(path), 0755)
|
||||||
|
c := &config.Config{
|
||||||
|
Pins: config.Pins{
|
||||||
|
iot.Door: 22,
|
||||||
|
iot.Button: 5,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err = c.WriteConfig(path); err != nil {
|
||||||
|
println(err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
conf = c
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
o, e := NewOnion()
|
logger.Get().Logger.Info().Msg("starting door5")
|
||||||
if e != nil {
|
logger.Get().Logger.Trace().Msg("loading config")
|
||||||
panic(e)
|
loadConfig()
|
||||||
|
logger.Get().Logger.Debug().Msg(spew.Sdump(conf))
|
||||||
|
logger.Get().Logger.Info().Msg("initializing gpio")
|
||||||
|
if err := rpio.Open(); err != nil {
|
||||||
|
logger.Get().Logger.Fatal().Msg(err.Error())
|
||||||
}
|
}
|
||||||
pi, err := o.Control.ProtocolInfo()
|
|
||||||
if err != nil {
|
go watchButton()
|
||||||
panic(err)
|
|
||||||
}
|
select {}
|
||||||
spew.Dump(pi)
|
|
||||||
}
|
}
|
||||||
|
379
pkg/access/rfid.go
Normal file
379
pkg/access/rfid.go
Normal file
@ -0,0 +1,379 @@
|
|||||||
|
package access
|
||||||
|
|
||||||
|
// built for PN53x
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/clausecker/nfc/v2"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
|
||||||
|
"git.tcp.direct/kayos/door5/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConnectionMode uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
I2C ConnectionMode = iota
|
||||||
|
SPI
|
||||||
|
)
|
||||||
|
|
||||||
|
type Keys struct {
|
||||||
|
*nfc.Device
|
||||||
|
AccessLog *CardTimeSeries
|
||||||
|
incoming chan nfc.Target
|
||||||
|
connectivity ConnectionMode
|
||||||
|
infoString string
|
||||||
|
readDeadline time.Time
|
||||||
|
writeDeadline time.Time
|
||||||
|
resetPin int
|
||||||
|
}
|
||||||
|
|
||||||
|
type localAddr struct {
|
||||||
|
*Keys
|
||||||
|
}
|
||||||
|
|
||||||
|
func (la *localAddr) String() string {
|
||||||
|
return la.Keys.infoString
|
||||||
|
}
|
||||||
|
|
||||||
|
func (la *localAddr) Network() string {
|
||||||
|
switch la.Keys.connectivity {
|
||||||
|
case I2C:
|
||||||
|
return "i2c"
|
||||||
|
case SPI:
|
||||||
|
return "spi"
|
||||||
|
}
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rfid *Keys) Write(b []byte) (n int, err error) {
|
||||||
|
logger.Get().Logger.Panic().Msg("not implemented")
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rfid *Keys) Network() string {
|
||||||
|
switch rfid.connectivity {
|
||||||
|
case I2C:
|
||||||
|
return "i2c"
|
||||||
|
case SPI:
|
||||||
|
return "spi"
|
||||||
|
}
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rfid *Keys) LocalAddr() net.Addr {
|
||||||
|
return &localAddr{rfid}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rfid *Keys) RemoteAddr() net.Addr {
|
||||||
|
return rfid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rfid *Keys) SetDeadline(t time.Time) error {
|
||||||
|
rfid.readDeadline = t
|
||||||
|
rfid.writeDeadline = t
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rfid *Keys) SetReadDeadline(t time.Time) error {
|
||||||
|
rfid.readDeadline = t
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rfid *Keys) SetWriteDeadline(t time.Time) error {
|
||||||
|
rfid.writeDeadline = t
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rfid *Keys) HasIRQ() bool {
|
||||||
|
return rfid.connectivity == SPI && rfid.resetPin != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:typecheck
|
||||||
|
func NewRFID(connString string, mode ConnectionMode, ResetPin ...int) (*Keys, error) {
|
||||||
|
log := logger.Get().Logger
|
||||||
|
|
||||||
|
lg := log.With().Str("conn_string", connString).Logger()
|
||||||
|
log = &lg
|
||||||
|
|
||||||
|
reader := &Keys{
|
||||||
|
AccessLog: NewCardTimeSeries(),
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.incoming = make(chan nfc.Target, 1)
|
||||||
|
reader.connectivity = mode
|
||||||
|
if len(ResetPin) > 0 {
|
||||||
|
lg = log.With().Int("reset_pin", ResetPin[0]).Logger()
|
||||||
|
log = &lg
|
||||||
|
reader.resetPin = ResetPin[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
tries := 0
|
||||||
|
|
||||||
|
lg = log.With().Int("tries", tries).Bool("hasIRQ", reader.HasIRQ()).Logger()
|
||||||
|
log = &lg
|
||||||
|
|
||||||
|
try:
|
||||||
|
log.Trace().Msg("opening connection to NFC reader")
|
||||||
|
|
||||||
|
dev, err := nfc.Open(connString)
|
||||||
|
switch {
|
||||||
|
case err != nil && reader.HasIRQ():
|
||||||
|
if tries > 3 {
|
||||||
|
return nil, fmt.Errorf("failed to open device: %w", err)
|
||||||
|
}
|
||||||
|
log.Debug().Msg("errored during open, have IRQ, resetting and trying again...")
|
||||||
|
reader.Reset()
|
||||||
|
tries++
|
||||||
|
goto try
|
||||||
|
case err != nil:
|
||||||
|
log.Debug().Err(err).Msg("FUBAR")
|
||||||
|
return nil, fmt.Errorf("failed to open device: %w", err)
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.Device = &dev
|
||||||
|
|
||||||
|
if reader.infoString, err = reader.Information(); err != nil {
|
||||||
|
log.Warn().Err(err).Msg("failed to get device info, continuing anyway...")
|
||||||
|
}
|
||||||
|
|
||||||
|
if reader.infoString != "" {
|
||||||
|
log.Debug().Str("info", reader.infoString).Msg("got device info")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Trace().Msg("initializing NFC reader")
|
||||||
|
if err = reader.InitiatorInit(); err != nil {
|
||||||
|
log.Debug().Err(err).Msg("FUBAR")
|
||||||
|
return nil, fmt.Errorf("failed to initialize reader: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return reader, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rfid *Keys) Reset() {
|
||||||
|
log := logger.Get().Logger
|
||||||
|
log.Debug().Msg("Resetting the RFID chip...")
|
||||||
|
log.Panic().Msg("haha jk not implemented yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rfid *Keys) Close() error {
|
||||||
|
if rfid.HasIRQ() {
|
||||||
|
rfid.Reset()
|
||||||
|
}
|
||||||
|
return rfid.Device.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
type tagReadStatus struct {
|
||||||
|
err error
|
||||||
|
tagCount int
|
||||||
|
currentTarget nfc.Target
|
||||||
|
found []*Card
|
||||||
|
}
|
||||||
|
|
||||||
|
func (trs *tagReadStatus) Run(e *zerolog.Event, level zerolog.Level, message string) {
|
||||||
|
stringers := make([]fmt.Stringer, len(trs.found))
|
||||||
|
for i, v := range trs.found {
|
||||||
|
stringers[i] = v
|
||||||
|
}
|
||||||
|
e.Int("tagCount", trs.tagCount).Stringers("found", stringers)
|
||||||
|
if trs.err != nil {
|
||||||
|
e.Err(trs.err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (trs *tagReadStatus) Error() string {
|
||||||
|
return trs.err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
type Card struct {
|
||||||
|
nfc.Target
|
||||||
|
Type string `json:"type"`
|
||||||
|
Details map[string]interface{} `json:"details"`
|
||||||
|
UID string `json:"uid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Card) String() string {
|
||||||
|
return fmt.Sprintf("%s (%s)", c.UID, c.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rfid *Keys) ListenForTags(ctx context.Context, want int) ([]*Card, error) {
|
||||||
|
var status = &tagReadStatus{
|
||||||
|
err: nil,
|
||||||
|
tagCount: 0,
|
||||||
|
found: make([]*Card, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
log := logger.Get().Logger.Hook(status)
|
||||||
|
|
||||||
|
var modulations = []nfc.Modulation{
|
||||||
|
{Type: nfc.ISO14443a, BaudRate: nfc.Nbr106},
|
||||||
|
{Type: nfc.ISO14443b, BaudRate: nfc.Nbr106},
|
||||||
|
{Type: nfc.Felica, BaudRate: nfc.Nbr212},
|
||||||
|
{Type: nfc.Felica, BaudRate: nfc.Nbr424},
|
||||||
|
{Type: nfc.Jewel, BaudRate: nfc.Nbr106},
|
||||||
|
{Type: nfc.ISO14443biClass, BaudRate: nfc.Nbr106},
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
errChan = make(chan error, want)
|
||||||
|
)
|
||||||
|
|
||||||
|
var cancel context.CancelFunc
|
||||||
|
ctx, cancel = context.WithCancel(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
var err = ctx.Err()
|
||||||
|
if rfid.LastError() != nil {
|
||||||
|
err = fmt.Errorf("error from NFC reader: %w + context canceled: %s", rfid.LastError(), ctx.Err())
|
||||||
|
}
|
||||||
|
errChan <- err
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
var found int
|
||||||
|
|
||||||
|
if found, status.currentTarget, status.err =
|
||||||
|
rfid.InitiatorPollTarget(modulations, 1, 300*time.Millisecond); status.err != nil || found == 0 {
|
||||||
|
log.Warn().Err(status.err).Int("found", found).Msg("error polling for target")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
status.tagCount++
|
||||||
|
|
||||||
|
log.Debug().Msgf("got tag: %s", status.currentTarget.String())
|
||||||
|
|
||||||
|
var (
|
||||||
|
card *Card
|
||||||
|
)
|
||||||
|
|
||||||
|
// Transform the target to a specific tag Type and send the UID to the channel
|
||||||
|
switch status.currentTarget.Modulation() {
|
||||||
|
|
||||||
|
case nfc.Modulation{Type: nfc.ISO14443a, BaudRate: nfc.Nbr106}:
|
||||||
|
cardCasted := status.currentTarget.(*nfc.ISO14443aTarget)
|
||||||
|
card = &Card{
|
||||||
|
UID: hex.EncodeToString(cardCasted.UID[:cardCasted.UIDLen]),
|
||||||
|
Type: "ISO14443a",
|
||||||
|
Target: cardCasted,
|
||||||
|
}
|
||||||
|
|
||||||
|
case nfc.Modulation{Type: nfc.ISO14443b, BaudRate: nfc.Nbr106}:
|
||||||
|
cardCasted := status.currentTarget.(*nfc.ISO14443bTarget)
|
||||||
|
card = &Card{
|
||||||
|
UID: hex.EncodeToString(cardCasted.ApplicationData[:len(cardCasted.ApplicationData)]),
|
||||||
|
Type: "ISO14443b",
|
||||||
|
Target: cardCasted,
|
||||||
|
Details: map[string]interface{}{
|
||||||
|
"ProtocolInfo": cardCasted.ProtocolInfo,
|
||||||
|
"BaudRate": cardCasted.Modulation().BaudRate,
|
||||||
|
"ApplicationData": cardCasted.ApplicationData,
|
||||||
|
"PUPI": cardCasted.Pupi,
|
||||||
|
"CardIdentifier": cardCasted.CardIdentifier,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
case nfc.Modulation{Type: nfc.Felica, BaudRate: nfc.Nbr212},
|
||||||
|
nfc.Modulation{Type: nfc.Felica, BaudRate: nfc.Nbr424}:
|
||||||
|
cardCasted := status.currentTarget.(*nfc.FelicaTarget)
|
||||||
|
card = &Card{
|
||||||
|
UID: hex.EncodeToString(cardCasted.ID[:cardCasted.Len]),
|
||||||
|
Type: "Felica",
|
||||||
|
Target: cardCasted,
|
||||||
|
Details: map[string]interface{}{
|
||||||
|
"Len": cardCasted.Len,
|
||||||
|
"Pad": cardCasted.Pad,
|
||||||
|
"SysCode": cardCasted.SysCode,
|
||||||
|
"ResCode": cardCasted.ResCode,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
switch cardCasted.Modulation().BaudRate {
|
||||||
|
case nfc.Nbr212:
|
||||||
|
card.Details["kbps"] = 212
|
||||||
|
case nfc.Nbr424:
|
||||||
|
card.Details["kbps"] = 424
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
case nfc.Modulation{Type: nfc.Jewel, BaudRate: nfc.Nbr106}:
|
||||||
|
cardCasted := status.currentTarget.(*nfc.JewelTarget)
|
||||||
|
card = &Card{
|
||||||
|
UID: hex.EncodeToString(cardCasted.ID[:len(cardCasted.ID)]),
|
||||||
|
Type: "Jewel",
|
||||||
|
Target: cardCasted,
|
||||||
|
Details: map[string]interface{}{
|
||||||
|
"SensRes": cardCasted.SensRes,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
case nfc.Modulation{Type: nfc.ISO14443biClass, BaudRate: nfc.Nbr106}:
|
||||||
|
cardCasted := status.currentTarget.(*nfc.ISO14443biClassTarget)
|
||||||
|
card = &Card{
|
||||||
|
UID: hex.EncodeToString(cardCasted.UID[:len(cardCasted.UID)]),
|
||||||
|
Type: "ISO14443biClass",
|
||||||
|
Target: cardCasted,
|
||||||
|
Details: map[string]interface{}{
|
||||||
|
"Baud": cardCasted.Baud,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cardLog := log.With().Fields(card.Details).Str("uid", card.UID).Str("type", card.Type).Logger()
|
||||||
|
|
||||||
|
cardLog.Info().Msg("+1 card")
|
||||||
|
|
||||||
|
cardLog.Trace().Msg("checking card into timeseries log")
|
||||||
|
rfid.AccessLog.CheckIn(card)
|
||||||
|
cardLog.Trace().Msg("checked card into timeseries log")
|
||||||
|
status.found = append(status.found, card)
|
||||||
|
|
||||||
|
if status.tagCount >= want {
|
||||||
|
cancel()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(300 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case e := <-errChan:
|
||||||
|
return status.found, e
|
||||||
|
case <-ctx.Done():
|
||||||
|
switch status.tagCount {
|
||||||
|
case 0:
|
||||||
|
return nil, fmt.Errorf("failed to read any tags")
|
||||||
|
case want:
|
||||||
|
return status.found, nil
|
||||||
|
default:
|
||||||
|
return status.found, fmt.Errorf("failed to read %d tags, only read %d", want, status.tagCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rfid *Keys) ListenForOneTag(ctx context.Context) (*Card, error) {
|
||||||
|
if cards, err := rfid.ListenForTags(ctx, 1); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
return cards[0], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rfid *Keys) Read(buf []byte) (int, error) {
|
||||||
|
found, err := rfid.ListenForOneTag(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return copy(buf, []byte(found.UID)), nil
|
||||||
|
|
||||||
|
}
|
122
pkg/access/rfid_test.go
Normal file
122
pkg/access/rfid_test.go
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
package access
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Helper function to create a mock card
|
||||||
|
func mockCard(uid string, cardType string) *Card {
|
||||||
|
return &Card{
|
||||||
|
UID: uid,
|
||||||
|
Type: cardType,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestNewCardTimeSeries tests the NewCardTimeSeries constructor.
|
||||||
|
func TestNewCardTimeSeries(t *testing.T) {
|
||||||
|
cts := NewCardTimeSeries()
|
||||||
|
if cts.Map == nil || cts.crossRef == nil || cts.mu == nil {
|
||||||
|
t.Error("NewCardTimeSeries() failed to initialize all fields")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCardTimeSeries_CheckIn tests the CheckIn method.
|
||||||
|
func TestCardTimeSeries_CheckIn(t *testing.T) {
|
||||||
|
cts := NewCardTimeSeries()
|
||||||
|
card := mockCard("12345", "TestType")
|
||||||
|
cts.CheckIn(card)
|
||||||
|
|
||||||
|
if _, ok := cts.Map[cts.last]; !ok {
|
||||||
|
t.Errorf("CheckIn() failed to add card to Map")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := cts.crossRef[card]; !ok {
|
||||||
|
t.Errorf("CheckIn() failed to add card to crossRef")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCardTimeSeries_Get tests the Get method.
|
||||||
|
func TestCardTimeSeries_Get(t *testing.T) {
|
||||||
|
cts := NewCardTimeSeries()
|
||||||
|
card := mockCard("12345", "TestType")
|
||||||
|
now := time.Now()
|
||||||
|
cts.Map[now] = card
|
||||||
|
|
||||||
|
if got := cts.Get(now); !reflect.DeepEqual(got, card) {
|
||||||
|
t.Errorf("Get() = %v, want %v", got, card)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCardTimeSeries_GetClosest tests the GetClosest method.
|
||||||
|
func TestCardTimeSeries_GetClosest(t *testing.T) {
|
||||||
|
cts := NewCardTimeSeries()
|
||||||
|
card1 := mockCard("12345", "TestType")
|
||||||
|
card2 := mockCard("67890", "TestType")
|
||||||
|
now := time.Now()
|
||||||
|
cts.Map[now] = card1
|
||||||
|
cts.Map[now.Add(-1*time.Hour)] = card2
|
||||||
|
|
||||||
|
if got := cts.GetClosest(now.Add(-30 * time.Minute)); !reflect.DeepEqual(got, card2) {
|
||||||
|
t.Errorf("GetClosest() = %v, want %v", got, card2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCardTimeSeries_GetLast tests the GetLast method.
|
||||||
|
func TestCardTimeSeries_GetLast(t *testing.T) {
|
||||||
|
cts := NewCardTimeSeries()
|
||||||
|
card := mockCard("12345", "TestType")
|
||||||
|
cts.CheckIn(card)
|
||||||
|
|
||||||
|
if got := cts.GetLast(); !reflect.DeepEqual(got, card) {
|
||||||
|
t.Errorf("GetLast() = %v, want %v", got, card)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestKeys_ListenForTags tests the ListenForTags method.
|
||||||
|
func TestKeys_ListenForTags(t *testing.T) {
|
||||||
|
// This test may require mocking of the NFC reader and its methods
|
||||||
|
// Assuming a mock implementation is available, this test would simulate the behavior of the ListenForTags method
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestKeys_ListenForOneTag tests the ListenForOneTag method.
|
||||||
|
func TestKeys_ListenForOneTag(t *testing.T) {
|
||||||
|
connString := os.Getenv("TEST_RFID")
|
||||||
|
if connString == "" {
|
||||||
|
t.Skip("TEST_RFID environment variable not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assuming NewRFID function and related dependencies are properly mocked
|
||||||
|
rfid, err := NewRFID(connString, SPI) // Or I2C, depending on the test setup
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create RFID instance: %v", err)
|
||||||
|
}
|
||||||
|
defer rfid.Close()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
card, err := rfid.ListenForOneTag(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("ListenForOneTag() returned an error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if card == nil {
|
||||||
|
t.Errorf("ListenForOneTag() returned nil, expected a card")
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("CheckIn", func(t *testing.T) {
|
||||||
|
if rfid.AccessLog.GetLast() != card {
|
||||||
|
t.Errorf("ListenForOneTag() failed to check card into timeseries log")
|
||||||
|
}
|
||||||
|
if rfid.AccessLog.GetClosest(time.Now()) != card {
|
||||||
|
t.Errorf("ListenForOneTag() failed to check card into timeseries log")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: Additional mocking and setup may be required to test methods that interact with external dependencies like NFC reader.
|
60
pkg/access/timeseries.go
Normal file
60
pkg/access/timeseries.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package access
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CardTimeSeries struct {
|
||||||
|
Map map[time.Time]*Card `json:"nfc_checkins"`
|
||||||
|
crossRef map[*Card][]time.Time
|
||||||
|
last time.Time
|
||||||
|
mu *sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCardTimeSeries() *CardTimeSeries {
|
||||||
|
return &CardTimeSeries{
|
||||||
|
Map: make(map[time.Time]*Card),
|
||||||
|
crossRef: make(map[*Card][]time.Time),
|
||||||
|
mu: &sync.RWMutex{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cts *CardTimeSeries) CheckIn(c *Card) {
|
||||||
|
cts.mu.Lock()
|
||||||
|
defer cts.mu.Unlock()
|
||||||
|
tnow := time.Now()
|
||||||
|
cts.Map[tnow] = c
|
||||||
|
if cts.crossRef[c] == nil {
|
||||||
|
cts.crossRef[c] = make([]time.Time, 0)
|
||||||
|
}
|
||||||
|
cts.crossRef[c] = append(cts.crossRef[c], tnow)
|
||||||
|
cts.last = tnow
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cts *CardTimeSeries) Get(t time.Time) *Card {
|
||||||
|
cts.mu.RLock()
|
||||||
|
defer cts.mu.RUnlock()
|
||||||
|
return cts.Map[t]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cts *CardTimeSeries) GetClosest(t time.Time) *Card {
|
||||||
|
cts.mu.RLock()
|
||||||
|
defer cts.mu.RUnlock()
|
||||||
|
if cts.Map[t] != nil {
|
||||||
|
return cts.Map[t]
|
||||||
|
}
|
||||||
|
var closest time.Time
|
||||||
|
for k := range cts.Map {
|
||||||
|
if k.Before(t) && k.After(closest) {
|
||||||
|
closest = k
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cts.Map[closest]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cts *CardTimeSeries) GetLast() *Card {
|
||||||
|
cts.mu.RLock()
|
||||||
|
defer cts.mu.RUnlock()
|
||||||
|
return cts.Map[cts.last]
|
||||||
|
}
|
42
pkg/config/toml.go
Normal file
42
pkg/config/toml.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/pelletier/go-toml/v2"
|
||||||
|
|
||||||
|
"git.tcp.direct/kayos/door5/iot"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Pins map[iot.Thing]iot.GPIO
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Pins Pins `toml:"pins"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Pins) MarshalTOML() ([]byte, error) {
|
||||||
|
var rendered string
|
||||||
|
for thing, pin := range p {
|
||||||
|
rendered += fmt.Sprintf("%s = %d\n", thing.String(), pin)
|
||||||
|
}
|
||||||
|
return []byte(rendered), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadConfig(path string) (*Config, error) {
|
||||||
|
var config = &Config{}
|
||||||
|
b, e := os.ReadFile(path)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
err := toml.Unmarshal(b, config)
|
||||||
|
return config, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) WriteConfig(path string) error {
|
||||||
|
b, e := toml.Marshal(c)
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
return os.WriteFile(path, b, 0644)
|
||||||
|
}
|
32
pkg/iot/types.go
Normal file
32
pkg/iot/types.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package iot
|
||||||
|
|
||||||
|
type (
|
||||||
|
Thing uint16
|
||||||
|
GPIO uint8
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Door Thing = iota
|
||||||
|
Button
|
||||||
|
NFC
|
||||||
|
Webcam
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t Thing) MarshalText() ([]byte, error) {
|
||||||
|
return []byte(t.String()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Thing) String() string {
|
||||||
|
switch t {
|
||||||
|
case Door:
|
||||||
|
return "Door"
|
||||||
|
case Button:
|
||||||
|
return "Button"
|
||||||
|
case NFC:
|
||||||
|
return "NFC"
|
||||||
|
case Webcam:
|
||||||
|
return "Webcam"
|
||||||
|
default:
|
||||||
|
return "Unknown"
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@ package logger
|
|||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@ -17,7 +18,7 @@ var (
|
|||||||
|
|
||||||
func Get() *zwrap.Logger {
|
func Get() *zwrap.Logger {
|
||||||
setupOnce.Do(func() {
|
setupOnce.Do(func() {
|
||||||
if err := Setup("", zerolog.InfoLevel); err != nil {
|
if err := Setup("", zerolog.TraceLevel); err != nil {
|
||||||
println(err.Error())
|
println(err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
@ -42,7 +43,7 @@ func fileWriter(path string) (io.Writer, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var f *os.File
|
var f *os.File
|
||||||
if f, err = os.OpenFile(path+"/openspa.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644); err != nil {
|
if f, err = os.OpenFile(filepath.Join(path, "door5.log"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return f, nil
|
return f, nil
|
Loading…
Reference in New Issue
Block a user