1
+ from datetime import datetime
2
+
1
3
import requests
2
4
import pickle
3
5
import os
4
6
5
7
AUTH_URI = "https://www.googleapis.com/oauth2/v4/token"
8
+ REFRESH_URI = "https://www.googleapis.com/oauth2/v4/token?client_id={client_id}" \
9
+ "&client_secret={client_secret}&refresh_token={refresh_token}" \
10
+ "&grant_type=refresh_token"
6
11
DEFAULT_BASE_URI = "https://smartdevicemanagement.googleapis.com/v1"
7
12
DEFAULT_REDIRECT_URI = "https://www.google.com"
8
13
DEVICES_URI = (
9
14
"https://smartdevicemanagement.googleapis.com/v1/enterprises/{project_id}/devices"
10
15
)
16
+ EXECUTE_COMMAND_URI = "https://smartdevicemanagement.googleapis.com/v1/enterprises/" \
17
+ "{project_id}/devices/{device_id}:executeCommand"
11
18
12
19
13
20
class ArgumentsMissingError (Exception ):
@@ -18,6 +25,10 @@ class AuthorizationError(Exception):
18
25
pass
19
26
20
27
28
+ class InvalidActionError (Exception ):
29
+ pass
30
+
31
+
21
32
class NestDeviceAccessAuth (requests .auth .AuthBase ):
22
33
def __init__ (
23
34
self ,
@@ -34,16 +45,16 @@ def __init__(
34
45
self .project_id = project_id
35
46
self .client_id = client_id
36
47
self .client_secret = client_secret
48
+ self ._res = {}
49
+ self .access_token = None
50
+ self .refresh_token = None
37
51
38
52
if not code or code == "" :
39
53
self .invalid_token ()
40
54
raise ArgumentsMissingError ()
41
55
42
56
self .code = code
43
57
44
- self ._res = {}
45
- self .access_token = None
46
- self .refresh_token = None
47
58
self .redirect_uri = redirect_uri
48
59
self ._session = session
49
60
@@ -53,6 +64,7 @@ def login(self):
53
64
creds = pickle .load (token )
54
65
self .access_token = creds ["access" ]
55
66
self .refresh_token = creds ["refresh" ]
67
+ self .refresh ()
56
68
return
57
69
58
70
data = {
@@ -86,20 +98,44 @@ def __call__(self, r):
86
98
return r
87
99
88
100
def invalid_token (self ):
101
+ if self .refresh_token :
102
+ self .refresh ()
103
+ return
89
104
print (
90
105
f"Go to this link to get OAuth token: https://nestservices.google.com/partnerconnections/{ self .project_id } "
91
106
f"/auth?redirect_uri=https://www.google.com&access_type=offline&prompt=consent&client_id={ self .client_id } "
92
107
f"&response_type=code&scope=https://www.googleapis.com/auth/sdm.service"
93
108
)
94
109
110
+ def refresh (self ):
111
+ if not self .refresh_token :
112
+ self .invalid_token ()
113
+ return
114
+ response = requests .post (REFRESH_URI .format (client_id = self .client_id ,
115
+ client_secret = self .client_secret ,
116
+ refresh_token = self .refresh_token ))
117
+ if response .status_code != 200 :
118
+ if response .status_code == 400 :
119
+ self .invalid_token ()
120
+ raise AuthorizationError (response )
121
+ self .access_token = response .json ()["access_token" ]
122
+
95
123
96
124
class Device (object ):
97
125
def __init__ (self , dict ):
98
126
self .name = dict ["name" ]
127
+ self .device_id = self .name .split ('/' )[3 ]
99
128
self .type = dict ["type" ]
100
129
self .traits = dict ["traits" ]
101
130
102
131
132
+ class CameraStream (object ):
133
+ def __init__ (self , dict ):
134
+ self .rtsp_stream_url = dict ["results" ]["streamUrls" ]["rtspUrl" ]
135
+ self .stream_token = dict ["results" ]["streamToken" ]
136
+ self .expires_at = datetime .strptime (dict ["results" ]["expiresAt" ], "%Y-%m-%dT%H:%M:%S.%fZ" )
137
+
138
+
103
139
class NestDeviceAccess (object ):
104
140
def __init__ (
105
141
self ,
@@ -126,7 +162,7 @@ def login(self):
126
162
print ("Authorization Error" )
127
163
pass
128
164
129
- def devices (self ):
165
+ def get_devices (self ):
130
166
if not self .auth .access_token :
131
167
raise AuthorizationError ()
132
168
@@ -139,18 +175,23 @@ def devices(self):
139
175
devices_dict = response .json ()
140
176
141
177
devices = []
142
- for device in devices_dict ["devices" ]:
143
- devices .append (Device (device ))
178
+ for device_dict in devices_dict ["devices" ]:
179
+ device = Device (device_dict )
180
+ devices .append (device )
144
181
return devices
145
182
183
+ def get_camera_stream (self , device ):
184
+ if device .type not in ["sdm.devices.types.DOORBELL" , "sdm.devices.types.CAMERA" ]:
185
+ raise InvalidActionError ()
186
+ if not self .auth .access_token :
187
+ raise AuthorizationError ()
146
188
147
- if __name__ == "__main__" :
148
- nda = NestDeviceAccess (
149
- project_id = "2a7ad63f-af0f-414a-b218-23dd6b39d0c5" ,
150
- client_id = "484808906646-a9tche4b03q56u47fiojh04tbf7r56m8.apps.googleusercontent.com" ,
151
- client_secret = "uS-rH6Fqcr_d_vsTHpZOZd6l" ,
152
- code = "4/0AY0e-g6INJsCbfeHVxZV_Eg1jSEdWaxI22DgTfxUFTbPSBMXAEexjT_4VY9Rf1H5jht-hQ" ,
153
- )
154
- nda .login ()
155
- for device in nda .devices ():
156
- print (device .name )
189
+ data = {
190
+ "command" : "sdm.devices.commands.CameraLiveStream.GenerateRtspStream" ,
191
+ "params" : {}
192
+ }
193
+ response = requests .post (EXECUTE_COMMAND_URI .format (project_id = self .project_id ,
194
+ device_id = device .device_id ),
195
+ json = data ,
196
+ auth = self .auth )
197
+ return CameraStream (response .json ())
0 commit comments