diff --git a/.gitignore b/.gitignore index 09fb6c1..92bf293 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ /bin /pkg /logs +/controller.db # Generated files assets.bin.go diff --git a/cmd/controller/main.go b/cmd/controller/main.go index 1be2e8b..0cf4f43 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -11,6 +11,7 @@ import ( "github.com/ents-source/door-control/api" "github.com/ents-source/door-control/api/auth" "github.com/ents-source/door-control/assets" + "github.com/ents-source/door-control/db" "github.com/ents-source/door-control/doors" "github.com/kelseyhightower/envconfig" ) @@ -36,6 +37,8 @@ type config struct { AmpCategoryId int `envconfig:"amp_category_id"` AmpBufferDays int `envconfig:"amp_buffer_days" default:"3"` AmpResyncHours int `envconfig:"amp_resync_hours" default:"4"` + + DbPath string `envconfig:"db_path" default:"./controller.db"` } func main() { @@ -47,6 +50,8 @@ func main() { webPath := assets.SetupWeb() + db.ConnectionString = c.DbPath + auth.ApiAuthKey = getPassword(c.ApiSharedKey, c.ApiSharedKeyFile) doors.OfflineAfter = time.Duration(c.EspInterval) * time.Second @@ -96,6 +101,9 @@ func main() { log.Println("Stopping api...") api.Stop() + log.Println("Stopping database...") + db.Stop() + log.Println("Cleaning up...") _ = os.RemoveAll(webPath) diff --git a/db/db.go b/db/db.go new file mode 100644 index 0000000..32b1538 --- /dev/null +++ b/db/db.go @@ -0,0 +1,53 @@ +package db + +import ( + "database/sql" + "time" + + _ "github.com/mattn/go-sqlite3" +) + +var ConnectionString string + +var db *sql.DB +var insertStatement *sql.Stmt + +func prepare() error { + if db != nil { + return nil + } + + var err error + db, err = sql.Open("sqlite3", ConnectionString) + if err != nil { + return err + } + + _, err = db.Exec("CREATE TABLE IF NOT EXISTS access (fob TEXT NOT NULL, door TEXT NOT NULL, ts BIGINT NOT NULL, allowed BOOL NOT NULL)") + if err != nil { + return err + } + + insertStatement, err = db.Prepare("INSERT INTO access (fob, door, ts, allowed) VALUES (?, ?, ?, ?)") + if err != nil { + return err + } + + return nil +} + +func Stop() { + _ = db.Close() +} + +func InsertAccess(door string, fob string, time time.Time, granted bool) error { + if err := prepare(); err != nil { + return err + } + + if _, err := insertStatement.Exec(fob, door, time.UnixMilli(), granted); err != nil { + return err + } + + return nil +} diff --git a/doors/mqtt.go b/doors/mqtt.go index 525bc20..7ddb790 100644 --- a/doors/mqtt.go +++ b/doors/mqtt.go @@ -8,6 +8,7 @@ import ( "time" "github.com/eclipse/paho.mqtt.golang" + "github.com/ents-source/door-control/db" ) type MqttOptions struct { @@ -86,6 +87,51 @@ func onDoorSync(client mqtt.Client, message mqtt.Message) { func onDoorSend(client mqtt.Client, message mqtt.Message) { log.Printf("[MQTT:Send<<] %s %s", message.Topic(), message.Payload()) + msg := parseMessage(message.Payload()) + + if c, err := readMessageVal[string](msg, "cmd"); err != nil { + log.Println("[MQTT:Send<<] [Command Parse Error]", err) + return + } else if c == "log" { + if t, err := readMessageVal[string](msg, "type"); err != nil { + log.Println("[MQTT:Send<<] [Type Parse Error]", err) + return + } else if t == "access" { + ts, err := readMessageVal[float64](msg, "time") + if err != nil { + log.Println("[MQTT:Send<<] [TS Parse Error]", err) + return + } + + fob, err := readMessageVal[string](msg, "uid") + if err != nil { + log.Println("[MQTT:Send<<] [Fob Parse Error]", err) + return + } + + door, err := readMessageVal[string](msg, "door") + if err != nil { + log.Println("[MQTT:Send<<] [Door Parse Error]", err) + return + } + + accessStr, err := readMessageVal[string](msg, "access") + if err != nil { + log.Println("[MQTT:Send<<] [Access Parse Error]", err) + return + } + + access := accessStr == "Always" + + err = db.InsertAccess(door, fob, time.UnixMilli(int64(ts)*1000), access) + if err != nil { + log.Println("[MQTT:Send<<] [DB Insert Error]", err) + return + } + + log.Println("Access record stored in database") + } + } } func onDoorRoot(client mqtt.Client, message mqtt.Message) { diff --git a/go.mod b/go.mod index 8f792b8..56b390c 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,13 @@ module github.com/ents-source/door-control go 1.20 require ( - github.com/eclipse/paho.mqtt.golang v1.4.3 // indirect + github.com/eclipse/paho.mqtt.golang v1.4.3 + github.com/kelseyhightower/envconfig v1.4.0 + github.com/mattn/go-sqlite3 v1.14.17 +) + +require ( github.com/gorilla/websocket v1.5.0 // indirect - github.com/kelseyhightower/envconfig v1.4.0 // indirect golang.org/x/net v0.8.0 // indirect golang.org/x/sync v0.1.0 // indirect ) diff --git a/go.sum b/go.sum index 1437a3c..367e5a6 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= +github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= +github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=