Skip to content

Commit 910ed5c

Browse files
authored
Merge pull request #17 from devicehive/develop
2.0.1 release
2 parents 2e2a7c8 + 7a0349b commit 910ed5c

13 files changed

Lines changed: 161 additions & 269 deletions

File tree

README.md

Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class SimpleHandler(Handler):
1818
def handle_connect(self):
1919
info = self.api.get_info()
2020
print(info)
21+
self.api.disconnect()
2122
```
2223

2324
`handle_connect` is the only one required method. If you want to handle server
@@ -31,9 +32,12 @@ from devicehive import Handler
3132
class SimpleHandler(Handler):
3233

3334
def handle_connect(self):
34-
self.api.subscribe_insert_commands()
35-
self.api.subscribe_update_commands()
36-
self.api.subscribe_notifications()
35+
device_ids = ['example-device-1', 'example-device-2']
36+
for device_id in device_ids:
37+
self.api.put_device(device_id)
38+
self.api.subscribe_insert_commands(device_ids)
39+
self.api.subscribe_update_commands(device_ids)
40+
self.api.subscribe_notifications(device_ids)
3741

3842
def handle_command_insert(self, command):
3943
print(command.command)
@@ -58,6 +62,9 @@ from devicehive import DeviceHive
5862
class SimpleHandler(Handler):
5963

6064
def handle_connect(self):
65+
device_ids = ['example-device-1', 'example-device-2']
66+
for device_id in device_ids:
67+
self.api.put_device(device_id)
6168
self.api.subscribe_insert_commands()
6269
self.api.subscribe_update_commands()
6370
self.api.subscribe_notifications()
@@ -76,7 +83,6 @@ url = 'http://playground.dev.devicehive.com/api/rest'
7683
refresh_token = 'SOME_REFRESH_TOKEN'
7784
dh = DeviceHive(SimpleHandler)
7885
dh.connect(url, refresh_token=refresh_token)
79-
dh.join()
8086
```
8187

8288
### Custom handler args
@@ -98,6 +104,7 @@ class SimpleHandler(Handler):
98104
def handle_connect(self):
99105
info = self.api.get_info()
100106
print(info)
107+
self.api.disconnect()
101108

102109
dh = DeviceHive(SimpleHandler, 'some_arg', some_kwarg='some_kwarg')
103110
```
@@ -143,12 +150,14 @@ custom handler with `self.api`.
143150
### Info
144151

145152
`self.api.get_info()` returns `dict` with the next fields:
153+
146154
* `api_version`
147155
* `server_timestamp`
148156
* `rest_server_url`
149157
* `websocket_server_url`
150158

151159
`self.api.get_cluster_info()` returns `dict` with the next fields:
160+
152161
* `bootstrap.servers`
153162
* `zookeeper.connect`
154163

@@ -164,11 +173,13 @@ class SimpleHandler(Handler):
164173
print(info)
165174
cluster_info = self.api.get_cluster_info()
166175
print(cluster_info)
176+
self.api.disconnect()
167177
```
168178

169179
### Properties
170180

171181
`self.api.get_property(name)` returns `dict` with the next fields:
182+
172183
* `entity_version`
173184
* `name`
174185
* `value`
@@ -191,12 +202,14 @@ class SimpleHandler(Handler):
191202
entity_version = self.api.get_property(name, 'value')
192203
print(entity_version)
193204
self.api.delete_property(name)
205+
self.api.disconnect()
194206
```
195207

196208
### Tokens
197209

198210
`self.api.create_token(user_id, expiration, actions, network_ids, device_ids)`
199211
returns `dict` with the next fields:
212+
200213
* `access_token`
201214
* `refresh_token`
202215

@@ -216,6 +229,7 @@ class SimpleHandler(Handler):
216229
print(tokens)
217230
access_token = self.api.refresh_token()
218231
print(access_token)
232+
self.api.disconnect()
219233
```
220234

221235
### Commands subscription and unsubscription
@@ -300,38 +314,32 @@ Only `device_id` arg is required.
300314
#### Device object
301315

302316
Properties:
317+
303318
* `id` (read only)
304319
* `name`
305320
* `data`
306321
* `network_id`
307322
* `is_blocked`
308323

309324
Methods:
325+
310326
* `save()` Does not return anything.
311327
* `remove()` Does not return anything.
312-
* `subscribe_insert_commands(names, timestamp)` Does not return anything. All
313-
args are optional.
328+
* `subscribe_insert_commands(names, timestamp)` Does not return anything. All args are optional.
314329
* `unsubscribe_insert_commands()` Does not return anything.
315-
* `subscribe_update_commands(names, timestamp)` Does not return anything.
316-
All args are optional.
330+
* `subscribe_update_commands(names, timestamp)` Does not return anything. All args are optional.
317331
* `unsubscribe_update_commands()` Does not return anything.
318-
* `list_commands(start, end, command, status, sort_field, sort_order, take,
319-
skip)` Returns list of `Command` objects. All args are
320-
optional.
321-
* `send_command(command_name, parameters, lifetime, timestamp, status, result)`
322-
Returns `Command` object. Only `command_name` is required.
323-
* `subscribe_notifications(names, timestamp)` Does not return anything. All args
324-
are optional.
332+
* `list_commands(start, end, command, status, sort_field, sort_order, take, skip)` Returns list of `Command` objects. All args are optional.
333+
* `send_command(command_name, parameters, lifetime, timestamp, status, result)` Returns `Command` object. Only `command_name` is required.
334+
* `subscribe_notifications(names, timestamp)` Does not return anything. All args are optional.
325335
* `unsubscribe_notifications()` Does not return anything.
326-
* `list_notifications(start, end, notification, sort_field, sort_order, take,
327-
skip)` Returns list of `Notification` objects. All args
328-
are optional.
329-
* `send_notification(notification_name, parameters, timestamp)` Returns
330-
`Notification` object. Only `notification_name` is required.
336+
* `list_notifications(start, end, notification, sort_field, sort_order, take, skip)` Returns list of `Notification` objects. All args are optional.
337+
* `send_notification(notification_name, parameters, timestamp)` Returns `Notification` object. Only `notification_name` is required.
331338

332339
#### Command object
333340

334341
Properties:
342+
335343
* `id` (read only)
336344
* `user_id` (read only)
337345
* `command` (read only)
@@ -343,11 +351,13 @@ Properties:
343351
* `result`
344352

345353
Methods:
354+
346355
* `save()` Does not return anything.
347356

348357
#### Notification object
349358

350359
Properties:
360+
351361
* `device_id` (read only)
352362
* `id` (read only)
353363
* `notification` (read only)
@@ -372,6 +382,7 @@ class SimpleHandler(Handler):
372382
print('Device: %s, name: %s, data: %s' % (device.id, device.name,
373383
device.data))
374384
device.remove()
385+
self.api.disconnect()
375386
```
376387

377388
### Networks
@@ -386,15 +397,16 @@ returns list of `Network` objects. All args are optional.
386397
#### Network object
387398

388399
Properties:
400+
389401
* `id` (read only)
390402
* `name`
391403
* `description`
392404

393405
Methods:
406+
394407
* `save()` Does not return anything.
395408
* `remove()` Does not return anything.
396-
* `list_devices(name, name_pattern, sort_field, sort_order, take, skip)`
397-
Returns list of `Device` objects. All args are optional.
409+
* `list_devices(name, name_pattern, sort_field, sort_order, take, skip)` Returns list of `Device` objects. All args are optional.
398410

399411
Example:
400412
```python
@@ -408,6 +420,7 @@ class SimpleHandler(Handler):
408420
network_description = 'example-description'
409421
network = self.api.create_network(network_name, network_description)
410422
print(network.name)
423+
self.api.disconnect()
411424
```
412425

413426
### Users
@@ -425,6 +438,7 @@ class SimpleHandler(Handler):
425438
#### User object
426439

427440
Properties:
441+
428442
* `id` (read only)
429443
* `login` (read only)
430444
* `last_login` (read only)
@@ -434,6 +448,7 @@ Properties:
434448
* `data`
435449

436450
Methods:
451+
437452
* `save()` Does not return anything.
438453
* `update_password(password)` Does not return anything.
439454
* `remove()` Does not return anything.
@@ -456,6 +471,7 @@ class SimpleHandler(Handler):
456471
data = {'key': 'value'}
457472
user = self.api.create_user(login, password, role, data)
458473
print(user.login)
474+
self.api.disconnect()
459475
```
460476

461477
## Extended example:
@@ -497,8 +513,6 @@ url = 'ws://playground.dev.devicehive.com/api/websocket'
497513
refresh_token = 'SOME_REFRESH_TOKEN'
498514
dh = DeviceHive(ReceiverHandler)
499515
dh.connect(url, refresh_token=refresh_token)
500-
dh.join()
501-
dh.print_exception()
502516
```
503517

504518
On the next step we will create `sender.py`
@@ -524,6 +538,7 @@ class SenderHandler(Handler):
524538
notification = '%s-notification' % num_notification
525539
self._device.send_notification(notification)
526540
print('Sending notification "%s"' % notification)
541+
self.api.disconnect()
527542

528543
def handle_connect(self):
529544
self._device = self.api.get_device(self._device_id)
@@ -541,8 +556,6 @@ url = 'http://playground.dev.devicehive.com/api/rest'
541556
refresh_token = 'SOME_REFRESH_TOKEN'
542557
dh = DeviceHive(SenderHandler)
543558
dh.connect(url, refresh_token=refresh_token)
544-
dh.join()
545-
dh.print_exception()
546559
```
547560

548561
Run `python receiver.py` in the first terminal. And `python sender.py` in the

devicehive/api.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class Api(object):
1515
def __init__(self, transport, auth):
1616
self._transport = transport
1717
self._token = Token(self, auth)
18+
self._connected = True
1819
self._subscriptions = {}
1920
self._removed_subscription_ids = {}
2021
self.server_timestamp = None
@@ -53,6 +54,10 @@ def transport(self):
5354
def token(self):
5455
return self._token
5556

57+
@property
58+
def connected(self):
59+
return self._connected
60+
5661
def ensure_subscription_not_exist(self, action, device_ids):
5762
for device_id in device_ids:
5863
if not self.subscription_id(action, device_id):
@@ -99,6 +104,37 @@ def removed_subscription_id_exists(self, action, subscription_id):
99104
return False
100105
return subscription_id in subscription_ids
101106

107+
def resubscribe(self):
108+
subscription_calls = {}
109+
for action in self._subscriptions:
110+
subscription_calls[action] = []
111+
for subscription in self._subscriptions[action]:
112+
found = False
113+
for subscription_call in subscription_calls[action]:
114+
if subscription_call['names'] == subscription['names']:
115+
found = True
116+
device_id = subscription['device_id']
117+
subscription_call['device_ids'].append(device_id)
118+
break
119+
if not found:
120+
device_id = subscription['device_id']
121+
subscription_call = {'device_ids': [device_id],
122+
'names': subscription['names']}
123+
subscription_calls[action].append(subscription_call)
124+
self._subscriptions = {}
125+
action = 'command/insert'
126+
if action in subscription_calls:
127+
for subscription_call in subscription_calls[action]:
128+
self.subscribe_insert_commands(**subscription_call)
129+
action = 'command/update'
130+
if action in subscription_calls:
131+
for subscription_call in subscription_calls[action]:
132+
self.subscribe_update_commands(**subscription_call)
133+
action = 'notification/insert'
134+
if action in subscription_calls:
135+
for subscription_call in subscription_calls[action]:
136+
self.subscribe_notifications(**subscription_call)
137+
102138
def get_info(self):
103139
api_request = ApiRequest(self)
104140
api_request.url('info')
@@ -419,3 +455,9 @@ def create_user(self, login, password, role, data):
419455
user[User.STATUS_KEY] = status
420456
user[User.DATA_KEY] = data
421457
return User(self, user)
458+
459+
def disconnect(self):
460+
self._connected = False
461+
if not self._transport.connected:
462+
return
463+
self._transport.disconnect()

devicehive/api_handler.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,18 @@ def __init__(self, transport, auth, handler_class, handler_args,
2222
**handler_kwargs)
2323
self._handle_connect = False
2424

25+
@property
26+
def handler(self):
27+
return self._handler
28+
2529
def handle_connect(self):
2630
self._api.token.auth()
2731
self._api.server_timestamp = self._api.get_info()['server_timestamp']
2832
if not self._handle_connect:
29-
self._handler.handle_connect()
3033
self._handle_connect = True
34+
self._handler.handle_connect()
35+
return
36+
self._api.resubscribe()
3137

3238
def handle_event(self, event):
3339
api_event = ApiEvent(event)
@@ -47,5 +53,4 @@ def handle_event(self, event):
4753
return self._handler.handle_notification(notification)
4854

4955
def handle_disconnect(self):
50-
# TODO: handle disconnect here.
5156
pass

devicehive/device_hive.py

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from devicehive.data_formats.json_data_format import JsonDataFormat
22
from devicehive.api_handler import ApiHandler
3-
import traceback
3+
import time
4+
import six
45

56

67
class DeviceHive(object):
@@ -35,20 +36,29 @@ def connect(self, transport_url, **options):
3536
'password': options.pop('password', None),
3637
'refresh_token': options.pop('refresh_token', None),
3738
'access_token': options.pop('access_token', None)}
39+
connect_timeout = options.pop('connect_timeout', 30)
40+
max_num_connect = options.pop('max_num_connect', 10)
41+
connect_interval = options.pop('connect_interval', 1)
3842
self._api_handler_options['auth'] = auth
3943
self._init_transport()
40-
self._transport.connect(transport_url, **options)
41-
42-
def join(self, timeout=None):
43-
self._transport.join(timeout)
44-
45-
def exception_info(self):
46-
return self._transport.exception_info
47-
48-
def print_exception(self):
49-
transport_exception_info = self._transport.exception_info
50-
if not transport_exception_info:
51-
return
52-
traceback.print_exception(transport_exception_info[0],
53-
transport_exception_info[1],
54-
transport_exception_info[2])
44+
connect_time = time.time()
45+
num_connect = 0
46+
while True:
47+
if self._transport.connected:
48+
self._transport.disconnect()
49+
self._transport.connect(transport_url, **options)
50+
self._transport.join()
51+
exception_info = self._transport.exception_info
52+
if exception_info and not isinstance(exception_info[1],
53+
self._transport.error):
54+
six.reraise(*exception_info)
55+
if not self._transport.handler.handler.api.connected:
56+
return
57+
if time.time() - connect_time < connect_timeout:
58+
num_connect += 1
59+
if num_connect > max_num_connect:
60+
six.reraise(*exception_info)
61+
time.sleep(connect_interval)
62+
continue
63+
connect_time = time.time()
64+
num_connect = 0

0 commit comments

Comments
 (0)