-
Notifications
You must be signed in to change notification settings - Fork 0
/
wifire.go
144 lines (117 loc) · 3.33 KB
/
wifire.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// Package wifire implements a client for connecting to the Traeger REST and
// MQTT APIs. The goal is to support temperature monitoring with a potential
// longterm goal of controlling the grill.
package wifire
import (
"bytes"
"encoding/json"
"net/http"
"time"
)
// WiFire is a handle for the WiFire API connection.
type WiFire struct {
token string
tokenExpires time.Time
config config
}
type config struct {
username string
password string
cognitoURL string
baseURL string
clientID string
}
var defaultConfig = config{
cognitoURL: "https://cognito-idp.us-west-2.amazonaws.com/",
baseURL: "https://1ywgyc65d1.execute-api.us-west-2.amazonaws.com",
clientID: "2fuohjtqv1e63dckp5v84rau0j",
}
type requestTokenBody struct {
AuthFlow string `json:"AuthFlow"`
AuthParameters authParameters `json:"AuthParameters"`
ClientID string `json:"ClientId"`
}
type authParameters struct {
Username string `json:"USERNAME"`
Password string `json:"PASSWORD"`
}
type requestTokenResponse struct {
AuthenticationResult authenticationResult
}
type authenticationResult struct {
AccessToken string `json:"AccessToken"`
ExpiresIn int `json:"ExpiresIn"`
IDToken string `json:"IdToken"`
RefreshToken string `json:"RefreshToken"`
TokenType string `json:"TokenType"`
}
// Credentials is an option setting function for New(). It sets the user and
// password credentials for logging into the API. These are the same values used
// by the Traeger App.
func Credentials(username, password string) func(*WiFire) {
return func(w *WiFire) {
w.config.username = username
w.config.password = password
}
}
// ClientID is an option setting function for New(). It sets the client
// identifier for the WiFire API. This should be set to the ID of the Traeger
// App.
func ClientID(id string) func(*WiFire) {
return func(w *WiFire) {
w.config.clientID = id
}
}
// URLs is an option setting function for New(). It sets the WiFire API URLs
// used to pull the user information and obtain a token.
func URLs(base, cognito string) func(*WiFire) {
return func(w *WiFire) {
w.config.baseURL = base
w.config.cognitoURL = cognito
}
}
// New returns a new WiFire connection or an error.
func New(opts ...func(*WiFire)) (*WiFire, error) {
w := WiFire{config: defaultConfig}
for _, o := range opts {
o(&w)
}
if err := w.refresh(); err != nil {
return nil, err
}
return &w, nil
}
func (w *WiFire) refresh() error {
body := requestTokenBody{
AuthFlow: "USER_PASSWORD_AUTH",
AuthParameters: authParameters{
Username: w.config.username,
Password: w.config.password,
},
ClientID: w.config.clientID,
}
b, err := json.Marshal(body)
if err != nil {
return err
}
client := http.Client{}
req, err := http.NewRequest("POST", w.config.cognitoURL, bytes.NewReader(b))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/x-amz-json-1.1")
req.Header.Set("X-Amz-Target", "AWSCognitoIdentityProviderService.InitiateAuth")
t0 := time.Now()
r, err := client.Do(req)
if err != nil {
return err
}
defer r.Body.Close()
var auth requestTokenResponse
if err := json.NewDecoder(r.Body).Decode(&auth); err != nil {
return err
}
w.token = auth.AuthenticationResult.IDToken
w.tokenExpires = t0.Add(time.Second * time.Duration(auth.AuthenticationResult.ExpiresIn))
return nil
}