Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add token caching #20

Merged
merged 3 commits into from
Oct 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@ This app uses the MQTT broker bundled with [TeslaMate](https://github.com/adrian
This app is provided as a docker image. You will need to download the [config.example.yml](config.example.yml) file (or the simplified [config.simple.example.yml](config.simple.example.yml)), edit it appropriately, and then mount it to the container at runtime. For example:

```bash
# see docker compose example below for parameter explanations
docker run \
-e [email protected] \ # optional, can also be saved in the config.yml file
-e MYQ_PASS=my_super_secret_pass \ # optional, can also be saved in the config.yml file
-e TZ=America/New_York \ # optional, sets timezone for container
-v /etc/tesla-youq:/app/config:ro \ # required, mounts folder containing config file(s) into container
--user 1000:1000 \
-e [email protected] \
-e MYQ_PASS=my_super_secret_pass \
-e TZ=America/New_York \
-v /etc/tesla-youq:/app/config \
brchri/tesla-youq:latest
```

Expand All @@ -43,12 +45,13 @@ services:
tesla-youq:
image: brchri/tesla-youq:latest
container_name: tesla-youq
user: 1000:1000 # optional, sets user to run in container; must have read access to mounted config volume (+ write if using token caching)
environment:
- [email protected] # optional, can also be saved in the config.yml file
- MYQ_PASS=my_super_secret_pass # optional, can also be saved in the config.yml file
- TZ=America/New_York # optional, sets timezone for container
volumes:
- /etc/tesla-youq:/app/config:ro # required, mounts folder containing config file(s) into container
- /etc/tesla-youq:/app/config # required, mounts folder containing config file(s) into container
restart: unless-stopped
```

Expand Down
2 changes: 2 additions & 0 deletions config.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ global:
cooldown: 5 # minutes to wait after operating garage before allowing another garage operation
myq_email: [email protected] # email to auth to myq account; can also be passed as env var MYQ_EMAIL
myq_pass: super_secret_password # password to auth to myq account; can also be passed as env var MYQ_PASS
cache_token_file: config/token_cache.txt # location to cache myq auth token; omit to disable caching token; useful to prevent generating too many myq auth requests, especially when testing
# WARNING: using cache_token_file will store your auth token in plaintext at the specified location!

garage_doors:
- # main garage example
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ module github.com/brchri/tesla-youq
go 1.19

require (
github.com/brchri/myq v0.0.0-20231002041725-18aa0c937db3
github.com/eclipse/paho.mqtt.golang v1.4.2
github.com/google/uuid v1.3.0
github.com/joeshaw/myq v0.0.0-20221122173250-4d1216b9fc87
github.com/stretchr/testify v1.8.4
gopkg.in/yaml.v3 v3.0.1
)
Expand All @@ -15,6 +15,6 @@ require (
github.com/gorilla/websocket v1.4.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/objx v0.5.0 // indirect
golang.org/x/net v0.7.0 // indirect
golang.org/x/net v0.15.0 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
)
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/brchri/myq v0.0.0-20231002041725-18aa0c937db3 h1:t5edNn/KiV1Kgz1Ll1kdpupABPUtOUDIebHLm5JmDV8=
github.com/brchri/myq v0.0.0-20231002041725-18aa0c937db3/go.mod h1:EDuAgiwrpS8cfzKCUrXpelEw1YOjxO/jhklEthAKmEs=
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=
Expand All @@ -7,8 +9,6 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/joeshaw/myq v0.0.0-20221122173250-4d1216b9fc87 h1:8VEyynqlAGlKh7cd6VFUTXwbYPXsoQKWu0FGUisvE4o=
github.com/joeshaw/myq v0.0.0-20221122173250-4d1216b9fc87/go.mod h1:thEiR7+j6aW9O5xvqIp5njxp1UYjMwcg4P7+2XjeTJI=
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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
Expand All @@ -22,8 +22,8 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
Expand Down
47 changes: 45 additions & 2 deletions internal/geo/geo.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ package geo

import (
"fmt"
"io"
"log"
"math"
"os"
"time"

util "github.com/brchri/tesla-youq/internal/util"

"github.com/joeshaw/myq"
"github.com/brchri/myq"
)

// interface that allows api calls to myq to be abstracted and mocked by testing functions
Expand All @@ -19,6 +20,8 @@ type MyqSessionInterface interface {
SetDoorState(serialNumber, action string) error
SetUsername(string)
SetPassword(string)
GetToken() string
SetToken(string)
New()
}

Expand All @@ -40,7 +43,22 @@ func (m *MyqSessionWrapper) DeviceState(s string) (string, error) {
}

func (m *MyqSessionWrapper) Login() error {
return m.myqSession.Login()
err := m.myqSession.Login()
// cache token if requested
if err == nil && util.Config.Global.CacheTokenFile != "" {
file, fileErr := os.OpenFile(util.Config.Global.CacheTokenFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
if fileErr != nil {
log.Printf("WARNING: Unable to write to cache file %s", util.Config.Global.CacheTokenFile)
} else {
defer file.Close()

_, writeErr := file.WriteString(m.GetToken())
if writeErr != nil {
log.Printf("WARNING: Unable to write to cache file %s", util.Config.Global.CacheTokenFile)
}
}
}
return err
}

func (m *MyqSessionWrapper) SetDoorState(serialNumber, action string) error {
Expand All @@ -51,6 +69,14 @@ func (m *MyqSessionWrapper) New() {
m.myqSession = &myq.Session{}
}

func (m *MyqSessionWrapper) GetToken() string {
return m.myqSession.GetToken()
}

func (m *MyqSessionWrapper) SetToken(token string) {
m.myqSession.SetToken(token)
}

var myqExec MyqSessionInterface // executes myq package commands

func init() {
Expand Down Expand Up @@ -204,6 +230,23 @@ func setGarageDoor(config util.ConfigStruct, deviceSerial string, action string)
return nil
}

// check for cached token if we haven't retrieved it already
if util.Config.Global.CacheTokenFile != "" && myqExec.GetToken() == "" {
file, err := os.Open(util.Config.Global.CacheTokenFile)
if err != nil {
log.Printf("WARNING: Unable to read token cache from %s", util.Config.Global.CacheTokenFile)
} else {
defer file.Close()

data, err := io.ReadAll(file)
if err != nil {
log.Printf("WARNING: Unable to read token cache from %s", util.Config.Global.CacheTokenFile)
} else {
myqExec.SetToken(string(data))
}
}
}

curState, err := myqExec.DeviceState(deviceSerial)
if err != nil {
// fetching device state may have failed due to invalid session token; try fresh login to resolve
Expand Down
3 changes: 2 additions & 1 deletion internal/geo/geo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import (
"testing"
"time"

"github.com/brchri/myq"
"github.com/brchri/tesla-youq/internal/mocks"
"github.com/joeshaw/myq"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"

Expand All @@ -27,6 +27,7 @@ var (

func init() {
util.LoadConfig(filepath.Join("..", "..", "config.example.yml"))
util.Config.Global.CacheTokenFile = "" // dont assume cached token in testing

// used for testing events based on distance
distanceGarageDoor = util.Config.GarageDoors[0]
Expand Down
74 changes: 74 additions & 0 deletions internal/mocks/MyqSessionInterface.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions internal/util/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ type (
OpCooldown int `yaml:"cooldown"`
MyQEmail string `yaml:"myq_email"`
MyQPass string `yaml:"myq_pass"`
CacheTokenFile string `yaml:"cache_token_file"`
} `yaml:"global"`
GarageDoors []*GarageDoor `yaml:"garage_doors"`
Testing bool
Expand Down