@@ -166,42 +166,100 @@ func (a *API) ExchangeSDP(projectID, deviceID, offer string) (string, error) {
166
166
167
167
uri := "https://smartdevicemanagement.googleapis.com/v1/enterprises/" +
168
168
projectID + "/devices/" + deviceID + ":executeCommand"
169
- req , err := http .NewRequest ("POST" , uri , bytes .NewReader (b ))
170
- if err != nil {
171
- return "" , err
172
- }
173
169
174
- req .Header .Set ("Authorization" , "Bearer " + a .Token )
170
+ maxRetries := 3
171
+ retryDelay := time .Second * 30
175
172
176
- client := & http.Client {Timeout : time .Second * 5000 }
177
- res , err := client .Do (req )
178
- if err != nil {
179
- return "" , err
173
+ for attempt := 0 ; attempt < maxRetries ; attempt ++ {
174
+ req , err := http .NewRequest ("POST" , uri , bytes .NewReader (b ))
175
+ if err != nil {
176
+ return "" , err
177
+ }
178
+
179
+ req .Header .Set ("Authorization" , "Bearer " + a .Token )
180
+
181
+ client := & http.Client {Timeout : time .Second * 5000 }
182
+ res , err := client .Do (req )
183
+ if err != nil {
184
+ return "" , err
185
+ }
186
+
187
+ // Handle 409 (Conflict), 429 (Too Many Requests), and 401 (Unauthorized)
188
+ if res .StatusCode == 409 || res .StatusCode == 429 || res .StatusCode == 401 {
189
+ res .Body .Close ()
190
+ if attempt < maxRetries - 1 {
191
+ // Get new token from Google
192
+ if err := a .refreshToken (); err != nil {
193
+ return "" , err
194
+ }
195
+ time .Sleep (retryDelay )
196
+ retryDelay *= 2 // exponential backoff
197
+ continue
198
+ }
199
+ }
200
+
201
+ defer res .Body .Close ()
202
+
203
+ if res .StatusCode != 200 {
204
+ return "" , errors .New ("nest: wrong status: " + res .Status )
205
+ }
206
+
207
+ var resv struct {
208
+ Results struct {
209
+ Answer string `json:"answerSdp"`
210
+ ExpiresAt time.Time `json:"expiresAt"`
211
+ MediaSessionID string `json:"mediaSessionId"`
212
+ } `json:"results"`
213
+ }
214
+
215
+ if err = json .NewDecoder (res .Body ).Decode (& resv ); err != nil {
216
+ return "" , err
217
+ }
218
+
219
+ a .StreamProjectID = projectID
220
+ a .StreamDeviceID = deviceID
221
+ a .StreamSessionID = resv .Results .MediaSessionID
222
+ a .StreamExpiresAt = resv .Results .ExpiresAt
223
+
224
+ return resv .Results .Answer , nil
180
225
}
181
- defer res .Body .Close ()
182
226
183
- if res .StatusCode != 200 {
184
- return "" , errors .New ("nest: wrong status: " + res .Status )
227
+ return "" , errors .New ("nest: max retries exceeded" )
228
+ }
229
+
230
+ func (a * API ) refreshToken () error {
231
+ // Get the cached API with matching token to get credentials
232
+ var refreshKey string
233
+ cacheMu .Lock ()
234
+ for key , api := range cache {
235
+ if api .Token == a .Token {
236
+ refreshKey = key
237
+ break
238
+ }
185
239
}
240
+ cacheMu .Unlock ()
186
241
187
- var resv struct {
188
- Results struct {
189
- Answer string `json:"answerSdp"`
190
- ExpiresAt time.Time `json:"expiresAt"`
191
- MediaSessionID string `json:"mediaSessionId"`
192
- } `json:"results"`
242
+ if refreshKey == "" {
243
+ return errors .New ("nest: unable to find cached credentials" )
193
244
}
194
245
195
- if err = json .NewDecoder (res .Body ).Decode (& resv ); err != nil {
196
- return "" , err
246
+ // Parse credentials from cache key
247
+ parts := strings .Split (refreshKey , ":" )
248
+ if len (parts ) != 3 {
249
+ return errors .New ("nest: invalid cache key format" )
197
250
}
251
+ clientID , clientSecret , refreshToken := parts [0 ], parts [1 ], parts [2 ]
198
252
199
- a .StreamProjectID = projectID
200
- a .StreamDeviceID = deviceID
201
- a .StreamSessionID = resv .Results .MediaSessionID
202
- a .StreamExpiresAt = resv .Results .ExpiresAt
253
+ // Get new API instance which will refresh the token
254
+ newAPI , err := NewAPI (clientID , clientSecret , refreshToken )
255
+ if err != nil {
256
+ return err
257
+ }
203
258
204
- return resv .Results .Answer , nil
259
+ // Update current API with new token
260
+ a .Token = newAPI .Token
261
+ a .ExpiresAt = newAPI .ExpiresAt
262
+ return nil
205
263
}
206
264
207
265
func (a * API ) ExtendStream () error {
@@ -407,20 +465,22 @@ type Device struct {
407
465
}
408
466
409
467
func (a * API ) StartExtendStreamTimer () {
410
- // Calculate the duration until 30 seconds before the stream expires
411
- duration := time .Until (a .StreamExpiresAt .Add (- 30 * time .Second ))
412
- a .extendTimer = time .AfterFunc (duration , func () {
468
+ if a .extendTimer != nil {
469
+ return
470
+ }
471
+
472
+ a .extendTimer = time .NewTimer (time .Until (a .StreamExpiresAt ) - time .Minute )
473
+ go func () {
474
+ <- a .extendTimer .C
413
475
if err := a .ExtendStream (); err != nil {
414
476
return
415
477
}
416
- duration = time .Until (a .StreamExpiresAt .Add (- 30 * time .Second ))
417
- a .extendTimer .Reset (duration )
418
- })
478
+ }()
419
479
}
420
480
421
481
func (a * API ) StopExtendStreamTimer () {
422
- if a .extendTimer == nil {
423
- return
482
+ if a .extendTimer != nil {
483
+ a .extendTimer .Stop ()
484
+ a .extendTimer = nil
424
485
}
425
- a .extendTimer .Stop ()
426
486
}
0 commit comments