@@ -17,16 +17,28 @@ type API struct {
17
17
18
18
StreamProjectID string
19
19
StreamDeviceID string
20
- StreamSessionID string
21
20
StreamExpiresAt time.Time
22
21
22
+ // WebRTC
23
+ StreamSessionID string
24
+
25
+ // RTSP
26
+ StreamToken string
27
+ StreamExtensionToken string
28
+
23
29
extendTimer * time.Timer
24
30
}
25
31
26
32
type Auth struct {
27
33
AccessToken string
28
34
}
29
35
36
+ type DeviceInfo struct {
37
+ Name string
38
+ DeviceID string
39
+ Protocols []string
40
+ }
41
+
30
42
var cache = map [string ]* API {}
31
43
var cacheMu sync.Mutex
32
44
@@ -80,7 +92,7 @@ func NewAPI(clientID, clientSecret, refreshToken string) (*API, error) {
80
92
return api , nil
81
93
}
82
94
83
- func (a * API ) GetDevices (projectID string ) (map [ string ] string , error ) {
95
+ func (a * API ) GetDevices (projectID string ) ([] DeviceInfo , error ) {
84
96
uri := "https://smartdevicemanagement.googleapis.com/v1/enterprises/" + projectID + "/devices"
85
97
req , err := http .NewRequest ("GET" , uri , nil )
86
98
if err != nil {
@@ -108,24 +120,30 @@ func (a *API) GetDevices(projectID string) (map[string]string, error) {
108
120
return nil , err
109
121
}
110
122
111
- devices := map [ string ] string {}
123
+ devices := make ([] DeviceInfo , 0 , len ( resv . Devices ))
112
124
113
125
for _ , device := range resv .Devices {
126
+ // only RTSP and WEB_RTC available (both supported)
114
127
if len (device .Traits .SdmDevicesTraitsCameraLiveStream .SupportedProtocols ) == 0 {
115
128
continue
116
129
}
117
130
118
- if device .Traits .SdmDevicesTraitsCameraLiveStream .SupportedProtocols [0 ] != "WEB_RTC" {
119
- continue
120
- }
121
-
122
131
i := strings .LastIndexByte (device .Name , '/' )
123
132
if i <= 0 {
124
133
continue
125
134
}
126
135
127
136
name := device .Traits .SdmDevicesTraitsInfo .CustomName
128
- devices [name ] = device .Name [i + 1 :]
137
+ // Devices configured through the Nest app use the container/room name as opposed to the customName trait
138
+ if name == "" && len (device .ParentRelations ) > 0 {
139
+ name = device .ParentRelations [0 ].DisplayName
140
+ }
141
+
142
+ devices = append (devices , DeviceInfo {
143
+ Name : name ,
144
+ DeviceID : device .Name [i + 1 :],
145
+ Protocols : device .Traits .SdmDevicesTraitsCameraLiveStream .SupportedProtocols ,
146
+ })
129
147
}
130
148
131
149
return devices , nil
@@ -190,11 +208,20 @@ func (a *API) ExtendStream() error {
190
208
var reqv struct {
191
209
Command string `json:"command"`
192
210
Params struct {
193
- MediaSessionID string `json:"mediaSessionId"`
211
+ MediaSessionID string `json:"mediaSessionId,omitempty"`
212
+ StreamExtensionToken string `json:"streamExtensionToken,omitempty"`
194
213
} `json:"params"`
195
214
}
196
- reqv .Command = "sdm.devices.commands.CameraLiveStream.ExtendWebRtcStream"
197
- reqv .Params .MediaSessionID = a .StreamSessionID
215
+
216
+ if a .StreamToken != "" {
217
+ // RTSP
218
+ reqv .Command = "sdm.devices.commands.CameraLiveStream.ExtendRtspStream"
219
+ reqv .Params .StreamExtensionToken = a .StreamExtensionToken
220
+ } else {
221
+ // WebRTC
222
+ reqv .Command = "sdm.devices.commands.CameraLiveStream.ExtendWebRtcStream"
223
+ reqv .Params .MediaSessionID = a .StreamSessionID
224
+ }
198
225
199
226
b , err := json .Marshal (reqv )
200
227
if err != nil {
@@ -223,8 +250,10 @@ func (a *API) ExtendStream() error {
223
250
224
251
var resv struct {
225
252
Results struct {
226
- ExpiresAt time.Time `json:"expiresAt"`
227
- MediaSessionID string `json:"mediaSessionId"`
253
+ ExpiresAt time.Time `json:"expiresAt"`
254
+ MediaSessionID string `json:"mediaSessionId"`
255
+ StreamExtensionToken string `json:"streamExtensionToken"`
256
+ StreamToken string `json:"streamToken"`
228
257
} `json:"results"`
229
258
}
230
259
@@ -234,6 +263,111 @@ func (a *API) ExtendStream() error {
234
263
235
264
a .StreamSessionID = resv .Results .MediaSessionID
236
265
a .StreamExpiresAt = resv .Results .ExpiresAt
266
+ a .StreamExtensionToken = resv .Results .StreamExtensionToken
267
+ a .StreamToken = resv .Results .StreamToken
268
+
269
+ return nil
270
+ }
271
+
272
+ func (a * API ) GenerateRtspStream (projectID , deviceID string ) (string , error ) {
273
+ var reqv struct {
274
+ Command string `json:"command"`
275
+ Params struct {} `json:"params"`
276
+ }
277
+ reqv .Command = "sdm.devices.commands.CameraLiveStream.GenerateRtspStream"
278
+
279
+ b , err := json .Marshal (reqv )
280
+ if err != nil {
281
+ return "" , err
282
+ }
283
+
284
+ uri := "https://smartdevicemanagement.googleapis.com/v1/enterprises/" +
285
+ projectID + "/devices/" + deviceID + ":executeCommand"
286
+ req , err := http .NewRequest ("POST" , uri , bytes .NewReader (b ))
287
+ if err != nil {
288
+ return "" , err
289
+ }
290
+
291
+ req .Header .Set ("Authorization" , "Bearer " + a .Token )
292
+
293
+ client := & http.Client {Timeout : time .Second * 5000 }
294
+ res , err := client .Do (req )
295
+ if err != nil {
296
+ return "" , err
297
+ }
298
+
299
+ if res .StatusCode != 200 {
300
+ return "" , errors .New ("nest: wrong status: " + res .Status )
301
+ }
302
+
303
+ var resv struct {
304
+ Results struct {
305
+ StreamURLs map [string ]string `json:"streamUrls"`
306
+ StreamExtensionToken string `json:"streamExtensionToken"`
307
+ StreamToken string `json:"streamToken"`
308
+ ExpiresAt time.Time `json:"expiresAt"`
309
+ } `json:"results"`
310
+ }
311
+
312
+ if err = json .NewDecoder (res .Body ).Decode (& resv ); err != nil {
313
+ return "" , err
314
+ }
315
+
316
+ if _ , ok := resv .Results .StreamURLs ["rtspUrl" ]; ! ok {
317
+ return "" , errors .New ("nest: failed to generate rtsp url" )
318
+ }
319
+
320
+ a .StreamProjectID = projectID
321
+ a .StreamDeviceID = deviceID
322
+ a .StreamToken = resv .Results .StreamToken
323
+ a .StreamExtensionToken = resv .Results .StreamExtensionToken
324
+ a .StreamExpiresAt = resv .Results .ExpiresAt
325
+
326
+ return resv .Results .StreamURLs ["rtspUrl" ], nil
327
+ }
328
+
329
+ func (a * API ) StopRTSPStream () error {
330
+ if a .StreamProjectID == "" || a .StreamDeviceID == "" {
331
+ return errors .New ("nest: tried to stop rtsp stream without a project or device ID" )
332
+ }
333
+
334
+ var reqv struct {
335
+ Command string `json:"command"`
336
+ Params struct {
337
+ StreamExtensionToken string `json:"streamExtensionToken"`
338
+ } `json:"params"`
339
+ }
340
+ reqv .Command = "sdm.devices.commands.CameraLiveStream.StopRtspStream"
341
+ reqv .Params .StreamExtensionToken = a .StreamExtensionToken
342
+
343
+ b , err := json .Marshal (reqv )
344
+ if err != nil {
345
+ return err
346
+ }
347
+
348
+ uri := "https://smartdevicemanagement.googleapis.com/v1/enterprises/" +
349
+ a .StreamProjectID + "/devices/" + a .StreamDeviceID + ":executeCommand"
350
+ req , err := http .NewRequest ("POST" , uri , bytes .NewReader (b ))
351
+ if err != nil {
352
+ return err
353
+ }
354
+
355
+ req .Header .Set ("Authorization" , "Bearer " + a .Token )
356
+
357
+ client := & http.Client {Timeout : time .Second * 5000 }
358
+ res , err := client .Do (req )
359
+ if err != nil {
360
+ return err
361
+ }
362
+
363
+ if res .StatusCode != 200 {
364
+ return errors .New ("nest: wrong status: " + res .Status )
365
+ }
366
+
367
+ a .StreamProjectID = ""
368
+ a .StreamDeviceID = ""
369
+ a .StreamExtensionToken = ""
370
+ a .StreamToken = ""
237
371
238
372
return nil
239
373
}
@@ -266,10 +400,10 @@ type Device struct {
266
400
//SdmDevicesTraitsCameraClipPreview struct {
267
401
//} `json:"sdm.devices.traits.CameraClipPreview"`
268
402
} `json:"traits"`
269
- // ParentRelations []struct {
270
- // Parent string `json:"parent"`
271
- // DisplayName string `json:"displayName"`
272
- // } `json:"parentRelations"`
403
+ ParentRelations []struct {
404
+ Parent string `json:"parent"`
405
+ DisplayName string `json:"displayName"`
406
+ } `json:"parentRelations"`
273
407
}
274
408
275
409
func (a * API ) StartExtendStreamTimer () {
@@ -282,7 +416,6 @@ func (a *API) StartExtendStreamTimer() {
282
416
duration = time .Until (a .StreamExpiresAt .Add (- 30 * time .Second ))
283
417
a .extendTimer .Reset (duration )
284
418
})
285
-
286
419
}
287
420
288
421
func (a * API ) StopExtendStreamTimer () {
0 commit comments