1- from devicecloud . api . sci import ServerCommandInterfaceAPI
1+ import json
22
33__version__ = "0.1"
44
5- from devicecloud .api .devicecore import DeviceCoreAPI
65from requests .auth import HTTPBasicAuth
7- from api .filedata import FileDataAPI
8- from api .streams import StreamAPI
96import logging
107import requests
118import time
2118 201
2219]
2320
24- PING_URL = "/ws/DeviceCore?size=1"
25- ONE_DAY = 86400 # in seconds
26-
2721logger = logging .getLogger ("dc" )
2822
2923
@@ -33,22 +27,24 @@ class DeviceCloudException(Exception):
3327
3428class DeviceCloudHttpException (DeviceCloudException ):
3529 """Exception raised when we failed a request to the DC over HTTP"""
30+
3631 def __init__ (self , response , * args , ** kwargs ):
3732 DeviceCloudException .__init__ (self , * args , ** kwargs )
3833 self .response = response
3934
4035
4136class _DeviceCloudConnection (object ):
37+ """Encapsulate information about a connection to the device cloud
38+
39+ This class is used internally and does not represent a part of the public API
40+ to the device cloud.
41+
42+ """
43+
4244 def __init__ (self , auth , base_url ):
4345 self ._auth = auth
4446 self ._base_url = base_url
4547
46- def connect (self ):
47- """Establish/Verify a connection with the Device Cloud"""
48-
49- # Ping the device cloud, raises exception if it fails
50- self .ping ()
51-
5248 def _make_url (self , path ):
5349 if not path .startswith ("/" ):
5450 path = "/" + path
@@ -67,13 +63,25 @@ def _make_request(self, retries, method, url, **kwargs):
6763 raise DeviceCloudHttpException (response , err )
6864
6965 def ping (self ):
70- """Ping the Device Cloud using the authorization provided"""
71- self .get (PING_URL ) # TODO: Is this sufficient, valid?
66+ """Ping the Device Cloud using the authorization provided
67+
68+ :returns: The response of getting a single device from DeviceCore on success
69+ :raises: :class:`.DeviceCloudHttpException` if there is a problem
70+
71+ """
72+ return self .get ("/ws/DeviceCore?size=1" )
7273
7374 def get (self , path , retries = 0 , ** kwargs ):
7475 url = self ._make_url (path )
7576 return self ._make_request (retries , "GET" , url , ** kwargs )
7677
78+ def get_json (self , path , retries = 0 , ** kwargs ):
79+ url = self ._make_url (path )
80+ headers = kwargs .setdefault ('headers' , {})
81+ headers .update ({'Accept' : 'application/json' })
82+ response = self ._make_request (retries , "GET" , url , ** kwargs )
83+ return json .loads (response .text )
84+
7785 def post (self , path , data , retries = 0 , ** kwargs ):
7886 url = self ._make_url (path )
7987 return self ._make_request (retries , "POST" , url , data = data , ** kwargs )
@@ -88,91 +96,80 @@ def delete(self, path, retries=0):
8896
8997
9098class DeviceCloud (object ):
91- """Provides access to information/operations on a device cloud account"""
99+ """Provide access to core device cloud features
100+
101+ This class is the primary interface to the device cloud through which access to individual
102+ device cloud services is provided. Creating a ``DeviceCloud`` object is as easy as doing
103+ the following::
104+
105+ from devicecloud import DeviceCloud
106+
107+ dc = DeviceCloud('user', 'pass')
108+ if dc.has_valid_credentials():
109+ devicecore = dc.get_devicecore_api()
110+ print devicecore.list_devices()
111+
112+ From there, access to all of the device clouds features are possible. In some cases, methods
113+ for quickly performing selected actions may be provided directly via the ``DeviceCloud`` object
114+ while advanced usage requires using functionality exposed through other interfaces.
115+
116+ """
92117
93118 def __init__ (self , username , password , base_url = "https://login.etherios.com" ):
94119 self ._conn = _DeviceCloudConnection (HTTPBasicAuth (username , password ), base_url )
95- self ._conn .connect ()
96-
97- # API Components
98- self ._file_data = FileDataAPI (self ._conn )
99- self ._streams = StreamAPI (self ._conn )
100- self ._sci = ServerCommandInterfaceAPI (self ._conn )
101- self ._device_core = DeviceCoreAPI (self ._conn , self ._sci )
102-
103- #---------------------------------------------------------------------------
104- # API - Streams
105- #---------------------------------------------------------------------------
106- def create_data_stream (self , name , data_type , description = None ,
107- data_ttl = (ONE_DAY * 2 ),
108- rollup_ttl = (ONE_DAY * 5 )):
109- """Create and return a DataStream object from the Device Cloud
110-
111- TODO: Describe the usage of `data_ttl` and `rollup_ttl`
120+
121+ def has_valid_credentials (self ):
122+ """Verify that the device cloud url, username, and password are valid
123+
124+ This method will attempt to "ping" the device cloud in order to ensure that all
125+ of the provided information is correct.
126+
127+ :returns: True if the credentials are valid and false if not
128+
112129 """
113- return self ._streams .create_data_stream (name , data_type , description ,
114- data_ttl , rollup_ttl )
115-
116- def get_available_streams (self , cached = False ):
117- """Return a list of all available streams"""
118- return self ._streams .get_streams (cached )
119-
120- def get_stream (self , stream_id , cached = False ):
121- return self ._streams .get_stream (stream_id , cached )
122-
123- def stream_write (self , stream_id , data ):
124- """Write a DataPoint to a previously opened DataStream"""
125- self ._streams .stream_write (stream_id , data )
126-
127- def stream_read (self , stream_id ):
128- """Return data from some stream"""
129- return self ._streams .stream_read (stream_id )
130-
131- #---------------------------------------------------------------------------
132- # API - FileData
133- #---------------------------------------------------------------------------
134- def get_filedata_file (self , file_glob , device = None , from_date = None , contents = False ):
135- """Gets files that match the *file_glob* pattern using the FileData API
136-
137- If *device* is not ``None`` then it will only look for files from *device*.
138- If *from_date* is not ``None`` then it will only for files modified since
139- *from_date*. If *include_contents* is ``True`` then the file contents
140- will be retrieved.
130+ try :
131+ self ._conn .ping ()
132+ except DeviceCloudException :
133+ return False
134+ else :
135+ return True
136+
137+ def get_streams_api (self ):
138+ """Returns a :class:`.StreamAPI` bound to this device cloud instance
139+
140+ :returns: :class:`.StreamAPI` object bound to this device cloud account
141+
141142 """
142- return self . _file_data . get_files ( file_glob , device , from_date , contents )
143+ from devicecloud . api . streams import StreamAPI
143144
144- def put_filedata_file (self , file_name , file_data ):
145- self ._file_data .put_file (file_name , file_data )
145+ return StreamAPI (self ._conn )
146146
147- #---------------------------------------------------------------------------
148- # API - DeviceCore
149- #---------------------------------------------------------------------------
150- def list_devices (self ):
151- """Get information about all devices associated with this device cloud account
147+ def get_filedata_api (self ):
148+ """Returns a :class:`.FileDataAPI` bound to this device cloud instance
152149
153- This method will return a list of :class:`.Device` instances. Additional operations
154- can be performed on these instances.
150+ :returns: :class:`.FileDataAPI` bound to this device cloud account
155151
156152 """
157- return self ._device_core .list_devices ()
158-
159- #---------------------------------------------------------------------------
160- # API - Devices (SCI)
161- #---------------------------------------------------------------------------
162- def sci_put_file (self , addr , file_name , file_data ):
163- """Put a file onto the filesystem of a connected device"""
164- raise NotImplementedError ()
165-
166- def sci_get_file (self , addr , glob ):
167- """Get a file from the filesystem of a connected device"""
168- raise NotImplementedError ()
169-
170- #---------------------------------------------------------------------------
171- # API - Monitors
172- #---------------------------------------------------------------------------
173- # TODO: Implement me
174-
175- #---------------------------------------------------------------------------
176- # API - Alarms
177- #---------------------------------------------------------------------------
178- # TODO: Implement me
153+ from devicecloud .api .filedata import FileDataAPI # prevent circular imports
154+
155+ return FileDataAPI (self ._conn )
156+
157+ def get_devicecore_api (self ):
158+ """Returns a :class:`.DeviceCoreAPI` bound to this device cloud instance
159+
160+ :returns: :class:`.DeviceCoreAPI` bound to this device cloud account
161+
162+ """
163+ from devicecloud .api .devicecore import DeviceCoreAPI
164+
165+ return DeviceCoreAPI (self ._conn , self .get_sci_api ())
166+
167+ def get_sci_api (self ):
168+ """Returns a :class:`.ServerCommandInterfaceAPI` bound to this device cloud instance
169+
170+ :returns: :class:`.ServerCommandInterfaceAPI` bound to this device cloud account
171+
172+ """
173+ from devicecloud .api .sci import ServerCommandInterfaceAPI
174+
175+ return ServerCommandInterfaceAPI (self ._conn )
0 commit comments