Skip to content

Commit 320a379

Browse files
committed
Reworked library to support multiple logins. Restructured some files and folders.
1 parent 2b7ccab commit 320a379

28 files changed

Lines changed: 595 additions & 393 deletions

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@ dmypy.json
127127
# Pyre type checker
128128
.pyre/
129129

130-
# PyCharm
130+
# IDE
131131
.idea/
132+
.vscode/
132133

133134
release.sh

README.md

Lines changed: 112 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,29 @@ Asynchronous Python library to get data from the cloud, and switch Crownstones.
88
* Easy to use: sync all your Crownstone Cloud data with just one command!
99
* Structurally sound: find your data with ease!
1010
* Complete: set the switch state and brightness of your Crownstones remotely!
11+
* Flexible: Login and get the data for multiple accounts at once!
1112

1213
## Requirements
1314

14-
* Python 3.7 (only version that it's tested on currently)
15-
* Aiohttp 3.6.1
15+
* Python 3.7 or higher
16+
* Aiohttp 3.6.2
1617

1718
## Standard installation
1819

1920
cd to the project folder and run:
2021
```console
21-
$ python3.7 setup.py install
22+
$ python setup.py install
2223
```
2324

2425
## Install in a virtual environment
2526

26-
To install the library excute the following command:
27+
To install the library execute the following command:
2728
```console
28-
$ python3.7 -m venv venv3.7
29+
$ python -m venv venv
2930
```
3031
Activate your venv using:
3132
```console
32-
$ source venv3.7/bin/activate
33+
$ source venv/bin/activate
3334
```
3435
Once activated, the venv is used to executed python files, and libraries will be installed in the venv.<br>
3536
To install this library, cd to the project folder and run:
@@ -43,77 +44,99 @@ $ python setup.py install
4344

4445
#### Async example
4546

46-
```Python
47-
from crownstone_cloud.lib.cloud import CrownstoneCloud
47+
```python
48+
from crownstone_cloud import CrownstoneCloud
4849
import logging
4950
import asyncio
5051

51-
# enable logging
52-
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.INFO)
52+
# Enable logging.
53+
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)
5354

5455

5556
async def main():
56-
# init cloud
57-
cloud = CrownstoneCloud('email', 'password')
58-
await cloud.async_initialize()
57+
# Initialize cloud.
58+
cloud = CrownstoneCloud()
59+
# Login to the Crownstone Cloud and synchronize all cloud data.
60+
# Important is to save your user id which is returned from the function!
61+
user_id_1 = await cloud.async_initialize('email_user_1', 'password_user_1')
62+
63+
# Get a crownstone by name that can dim, and put it on 20% brightness for user 1.
64+
crownstone_lamp = cloud.get_crownstone('Lamp', user_id_1)
65+
await crownstone_lamp.async_set_brightness(20)
5966

60-
# get a crownstone by name that can dim, and put it on 50% brightness
61-
crownstone_lamp = cloud.get_crownstone('Lamp')
62-
await crownstone_lamp.async_set_brightness(0.5)
67+
# Login & synchronize data for an other account.
68+
user_id_2 = await cloud.async_initialize("email_user_2", "password_user_2")
6369

64-
# get a crownstone by name and turn it on
65-
crownstone_tv = cloud.get_crownstone('TV')
70+
# Get a crownstone by name and turn it on for user 2.
71+
crownstone_tv = cloud.get_crownstone('TV', user_id_2)
6672
await crownstone_tv.async_turn_on()
6773

68-
# close the session after we are done
74+
# If you want to update specific data you can get the cloud data object for your user.
75+
# This object has all the cloud data for your user saved in it, which was synced with async_initialize()
76+
# Parts of the data can also be synced individually without touching the other data.
77+
# To sync all data at once, use async_synchronize() instead.
78+
my_cloud_data = cloud.get_cloud_data(user_id_1)
79+
# Now find the specific sphere object
80+
my_sphere = my_cloud_data.find("my_sphere_name")
81+
# request to sync only the locations with the cloud
82+
my_sphere.locations.async_update_location_data()
83+
# get the keys for this sphere so you can use them with the Crownstone BLE python library
84+
sphere_keys = my_sphere.async_get_keys()
85+
86+
# Close the aiohttp clientsession after we are done.
6987
await cloud.async_close_session()
7088

7189
asyncio.run(main())
7290
```
7391

7492
#### Sync example
7593

76-
```Python
77-
from crownstone_cloud.lib.cloud import CrownstoneCloud
94+
```python
95+
from crownstone_cloud import CrownstoneCloud, run_async
7896
import logging
7997

80-
# enable logging
81-
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.INFO)
98+
# Enable logging.
99+
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)
82100

83-
# init cloud
84-
cloud = CrownstoneCloud('email', 'password')
85-
cloud.initialize()
101+
# Initialize cloud.
102+
cloud = CrownstoneCloud()
103+
# Use 'run_async' to run async functions in sync context.
104+
# Login & synchronize all cloud data. Save the user id that the function returns for later use.
105+
my_user_id = run_async(cloud.async_initialize('email', 'password'))
86106

87-
# get a crownstone by name and turn it on
88-
crownstone_coffee_machine = cloud.get_crownstone('Coffee machine')
89-
crownstone_coffee_machine.turn_on()
107+
# Get a crownstone by name and turn it on.
108+
crownstone_coffee_machine = cloud.get_crownstone('Coffee machine', my_user_id)
109+
run_async(crownstone_coffee_machine.async_turn_on())
90110

91-
cloud.close_session()
111+
# Close the session after we are done.
112+
run_async(cloud.async_close_session())
92113
```
93114

94115
### Initialization
95-
Crownstone cloud is initialized with 2 arguments:
116+
117+
The Crownstone cloud can be created without any arguments like so:
118+
```python
119+
cloud = CrownstoneCloud()
120+
```
121+
A new aiohttp session will be created, which has to be closed at the end of the program.
122+
If you are using this library in existing software that already uses an own websession, you can use this like so:
123+
```python
124+
cloud = CrownstoneCloud(websession)
125+
```
126+
To log in to the Crownstone Cloud, the following are required:
127+
96128
* User email
97129
* User password
98130

99131
If you do not yet have a Crownstone account, go to [My Crownstone](https://my.crownstone.rocks) to set one up.
100132
The email and password are used to re-login after an access token has expired.
101-
```Python
102-
cloud = CrownstoneCloud('email', 'password')
103-
```
104-
if already have an access token and you want to skip to first login for speed you can use:
105-
```Python
106-
cloud.set_access_token('myAccessToken')
107-
```
108-
to initialize all the cloud data into the lib, for async usage use:
109-
```Python
110-
await cloud.async_initialize()
111-
```
112-
Or for sync usage use:
113-
```Python
114-
cloud.initialize()
133+
134+
To log in and get all your Crownstone from the cloud:
135+
```python
136+
await cloud.async_initialize('email', 'password')
115137
```
116-
It is only required to call initialize once at the beginning of the program.
138+
This library supports logging in to multiple accounts. Simply call `async_initialize()` again with the email and
139+
password of the other account. It is only required to call initialize once for each account.
117140

118141
## Data structure
119142

@@ -144,7 +167,7 @@ Spheres are the main data entry. They have rooms (locations), Crownstones and us
144167
Example spheres:
145168
* House
146169
* Office
147-
* Apartement
170+
* Apartment
148171

149172
A Sphere has the following fields in the cloud lib:
150173
* crownstones: Crownstones
@@ -160,7 +183,7 @@ A Sphere has the following fields in the cloud lib:
160183

161184
Locations are the rooms in your house or other building.<br>
162185
For example for a house:
163-
* Livingroom
186+
* Living room
164187
* Bedroom
165188
* Garage
166189
* Bathroom
@@ -209,24 +232,25 @@ A User has the following fields in the cloud lib:
209232

210233
### Cloud
211234

212-
#### async_initialize()
213-
> Login if no access token available, and sync all data for the user from the cloud.
235+
#### async_initialize(email: String, password: String)
236+
> Login and sync all data for the user from the cloud.
237+
238+
#### async_synchronize(user_id: String)
239+
> Synchronize all data for a user. Use case is to update the local data with new data from the cloud.
240+
> This function is already called in `async_initialize()` for new logins.
214241
215-
#### set_access_token(access_token: String)
216-
> Set an access token to skip the login part, if you already have one
242+
#### get_cloud_data(user_id: String)
243+
> Get the cloud data object for a logged in user.
217244
218-
#### get_crownstone(crownstone_name: String) -> Crownstone
219-
> Get a Crownstone object by name, if it exists.
245+
#### get_crownstone(crownstone_name: String, user_id: String) -> Crownstone
246+
> Get a Crownstone object by name for a user, if it exists.
220247
221-
#### get_crownstone_by_id(crownstone_id: String) -> Crownstone
222-
> Get a Crownstone object by it's id, it's it exists.
248+
#### get_crownstone_by_id(crownstone_id: String, user_id: String) -> Crownstone
249+
> Get a Crownstone object by it's id for a user, if it exists.
223250
224251
#### async_close_session()
225252
> Async function. This will close the websession in requestHandler to cleanup nicely after the program has finished.
226253
227-
#### reset()
228-
> Reset the requestHandler parameters in case the cloud instance was cleaned up and needs to be recreated.
229-
230254
### Spheres
231255

232256
#### async_update_sphere_data()
@@ -240,13 +264,18 @@ A User has the following fields in the cloud lib:
240264
241265
### Sphere
242266

267+
#### async_update_sphere_presence()
268+
> Async function. Sync the presence of users in the sphere with the cloud.
269+
243270
#### async_get_keys() -> Dict
244-
> Async function. Returns a dict with the keys of this sphere. The keys can be used for BLE connectivity with the Crownstones.
271+
> Async function. Returns a dict with the keys of this sphere.
272+
> The keys can be used for BLE connectivity with the Crownstones.
245273
246274
### Crownstones
247275

248276
#### async_update_crownstone_data()
249-
> Async function. Sync the Crownstones with the cloud for a sphere. Calling the function again after init will update the current data.
277+
> Async function. Sync the Crownstones with the cloud for a sphere.
278+
> Calling the function again after init will update the current data.
250279
251280
#### find(crownstone_name: String) -> Crownstone
252281
> Return a Crownstone object if one exists by that name.
@@ -257,13 +286,17 @@ A User has the following fields in the cloud lib:
257286
### Crownstone
258287

259288
#### async_turn_on()
260-
> Async function. Send a command to turn a Crownstone on. To make this work make sure to be in the selected sphere and have Bluetooth enabled on your phone.
289+
> Async function. Send a command to turn a Crownstone on.
290+
> To make this work make sure to be in the selected sphere and have Bluetooth enabled on your phone.
261291
262292
#### async_turn_off()
263-
> Async function. Send a command to turn a Crownstone off. To make this work make sure to be in the selected sphere and have Bluetooth enabled on your phone.
293+
> Async function. Send a command to turn a Crownstone off.
294+
> To make this work make sure to be in the selected sphere and have Bluetooth enabled on your phone.
264295
265-
#### async_set_brightness(value: Float)
266-
> Async function. Send a command to set a Crownstone to a given brightness level. To make this work make sure to be in the selected sphere and have Bluetooth enabled on your phone.
296+
#### async_set_brightness(value: Integer)
297+
> Async function. Send a command to set a Crownstone to a given brightness level.
298+
> To make this work make sure to be in the selected sphere and have Bluetooth enabled on your phone.
299+
> The value parameter should be between 0 and 100.
267300
268301
### Locations
269302

@@ -294,21 +327,30 @@ A User has the following fields in the cloud lib:
294327
> Return a location object if one exists by that id.
295328
296329
## Async vs sync
297-
The lib can be used synchonously and asynchronously.<br>
330+
The lib can be used synchronously and asynchronously.<br>
331+
The disadvantage of sync context is that all functions are blocking.
332+
The program will simply wait until a function is complete. In async context, functions (coroutines) can yield control,
333+
which means that functions can be "paused" while they are waiting for external data to come in, like data from a server.
334+
Other functions can then be executed in the meantime. This way the program is always busy.<br>
335+
298336
All async functions in the library API functions in this library have the prefix **async_**
299337
Async functions need to be awaited:
300338
```Python
301-
await cloud.spheres.async_update_sphere_data()
339+
await cloud.async_close_session()
302340
```
303341
All the async functions mentioned above can also be used synchronously.<br>
304-
Sync functions don't have the async prefix. for example:
342+
Use the `run_async()` function like so:
305343
```Python
306-
cloud.initialize()
307-
cloud.spheres.update_sphere_data()
344+
from crownstone_cloud import run_async
345+
346+
run_async(cloud.async_close_session())
308347
```
309348
Make sure to see the examples above!
310349

311350
## Testing
351+
352+
### Tests are not up-to-date yet for the newest commit, only run for version 1.2.1.
353+
312354
To run the tests using tox install tox first by running:
313355
```console
314356
$ pip install tox

crownstone_cloud/_RequestHandlerInstance.py

Lines changed: 0 additions & 4 deletions
This file was deleted.

crownstone_cloud/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
from crownstone_cloud._RequestHandlerInstance import RequestHandler
2-
from crownstone_cloud.lib.cloud import CrownstoneCloud
1+
"""Top level imports."""
2+
from crownstone_cloud.cloud import CrownstoneCloud
3+
from crownstone_cloud.util.runners import run_async

0 commit comments

Comments
 (0)