Skip to content

Commit ceb64be

Browse files
committed
Hologram Python SDK v0.7.6 release
1 parent a57c2a6 commit ceb64be

File tree

7 files changed

+51
-22
lines changed

7 files changed

+51
-22
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# What's New in Hologram Python SDK
22

3+
## v0.7.6
4+
5+
2018-04-04 Hologram <[email protected]>
6+
* hologram receive reconnects after forced disconnect
7+
* Replace usb dependency with cross platform library
8+
39
## v0.7.5
410

511
2018-02-13 Hologram <[email protected]>

Hologram/Cloud.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from Network import NetworkManager
1414
from Authentication import *
1515

16-
__version__ = '0.7.5'
16+
__version__ = '0.7.6'
1717

1818
class Cloud(object):
1919

Hologram/Event/Event.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#
77
#
88
# LICENSE: Distributed under the terms of the MIT License
9+
import logging
910

1011
class Event(object):
1112
_funcLookupTable = {}
@@ -17,7 +18,10 @@ def subscribe(self, event, callback):
1718
if not self.__dict__.get(event):
1819
self.__dict__[event] = [callback]
1920
else:
20-
self.__dict__[event].append(callback)
21+
if callback not in self.__dict__[event]:
22+
self.__dict__[event].append(callback)
23+
else:
24+
logging.debug("Callback already subscribed: event[%s] callback[%s]", event, str(callback))
2125

2226
def unsubscribe(self, event, callback):
2327

Hologram/Network/Cellular.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from Modem import NovaM_R404
1919
from Network import Network, NetworkScope
2020
import time
21-
import usb.core
21+
from serial.tools import list_ports
2222

2323
# Cellular return codes.
2424
CLOUD_DISCONNECTED = 0
@@ -44,6 +44,7 @@ def __init__(self, event=Event()):
4444
self._connection_status = CLOUD_DISCONNECTED
4545
self._modem = None
4646
self._route = Route()
47+
self.__receive_port = None
4748

4849
def autodetect_modem(self):
4950
# scan for a modem and set it if found
@@ -126,6 +127,8 @@ def send_message(self, data):
126127
return self.modem.send_message(data)
127128

128129
def open_receive_socket(self, receive_port):
130+
self.__receive_port = receive_port
131+
self.event.subscribe('cellular.forced_disconnect', self.__reconnect_after_forced_disconnect)
129132
return self.modem.open_receive_socket(receive_port)
130133

131134
def pop_received_message(self):
@@ -147,6 +150,21 @@ def popReceivedSMS(self):
147150
def get_sim_otp_response(self, command):
148151
return self.modem.get_sim_otp_response(command)
149152

153+
def __reconnect_and_receive(self):
154+
if not self.at_sockets_available:
155+
self.connect()
156+
self.open_receive_socket(self.__receive_port)
157+
158+
def __reconnect_after_forced_disconnect(self):
159+
self.logger.info('Reconnecting after forced disconnect...')
160+
time.sleep(5) # uBlox takes some time to update internal state after disconnect
161+
self.__reconnect_and_receive()
162+
while not self.is_connected():
163+
self.logger.info('Reconnect failed. Retrying in 5 seconds...')
164+
time.sleep(5)
165+
self.__reconnect_and_receive()
166+
self.logger.info('Ready to receive data on port %s', self.__receive_port)
167+
150168
def __configure_routing(self):
151169
self.logger.info('Adding routes to Hologram cloud')
152170
self._route.add('10.176.0.0/16', self.localIPAddress)
@@ -170,10 +188,7 @@ def _scan_for_modem(self, modemHandler):
170188
if not vid_pid:
171189
continue
172190
self.logger.debug('checking for vid_pid: %s', str(vid_pid))
173-
vid = int(vid_pid[0], 16)
174-
pid = int(vid_pid[1], 16)
175-
dev = usb.core.find(idVendor=vid, idProduct=pid)
176-
if dev:
191+
for dev in list_ports.grep("{0}:{1}".format(vid_pid[0], vid_pid[1])):
177192
self.logger.info('Detected modem %s', modemHandler.__name__)
178193
return True
179194
return False

Hologram/Network/Modem/Modem.py

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
import datetime
2222
import logging
2323
import os
24-
import pyudev
2524
import serial
25+
from serial.tools import list_ports
2626
import time
2727
from serial.serialutil import Timeout
2828

@@ -42,6 +42,7 @@ class Modem(IModem):
4242
SOCKET_WRITE_STATE = 1
4343
SOCKET_RECEIVE_READ = 2
4444
SOCKET_SEND_READ = 3
45+
SOCKET_CLOSED = 4
4546

4647
GSM = u"@£$¥èéùìòÇ\nØø\rÅåΔ_ΦΓΛΩΠΨΣΘΞ ÆæßÉ !\"#¤%&'()*+,-./0123456789:;<=>?¡ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÑܧ¿abcdefghijklmnopqrstuvwxyzäöñüà"
4748
EXT = {
@@ -180,31 +181,28 @@ def popReceivedSMS(self):
180181

181182
def __detect_all_serial_ports(self, stop_on_first=False, include_all_ports=True):
182183
# figures out the serial ports associated with the modem and returns them
183-
context = pyudev.Context()
184184
device_names = []
185185
for usb_id in self.usb_ids:
186186
vid = usb_id[0]
187187
pid = usb_id[1]
188-
for udevice in context.list_devices(subsystem='tty', ID_BUS='usb',
189-
ID_VENDOR_ID=vid):
190-
# pyudev has some weird logic where you can't AND two different
191-
# properties together so we have to check it later
192-
if udevice['ID_MODEL_ID'] != pid:
193-
continue
194-
devname = udevice['DEVNAME']
195188

189+
# The list_ports function returns devices in descending order, so reverse
190+
# the order here to iterate in ascending order (e.g. from /dev/xx0 to /dev/xx6)
191+
# since our usable serial devices usually start at 0.
192+
udevices = [x for x in list_ports.grep("{0}:{1}".format(vid, pid))]
193+
for udevice in reversed(udevices):
196194
if include_all_ports == False:
197-
self.logger.debug('checking port %s', devname)
198-
port_opened = self.openSerialPort(devname)
195+
self.logger.debug('checking port %s', udevice.name)
196+
port_opened = self.openSerialPort(udevice.device)
199197
if not port_opened:
200198
continue
201199

202200
res = self.command('', timeout=1)
203201
if res[0] != ModemResult.OK:
204202
continue
205-
self.logger.info('found working port at %s', devname)
203+
self.logger.info('found working port at %s', udevice.name)
206204

207-
device_names.append(devname)
205+
device_names.append(udevice.device)
208206
if stop_on_first:
209207
break
210208
if stop_on_first and device_names:
@@ -249,6 +247,8 @@ def send_message(self, data, timeout=DEFAULT_SEND_TIMEOUT):
249247
if loop_timeout.expired():
250248
raise SerialError('Timeout occurred waiting for message status')
251249
time.sleep(self._RETRY_DELAY)
250+
elif self.urc_state == Modem.SOCKET_CLOSED:
251+
return '[1,0]' #this is connection closed for hologram cloud response
252252

253253
return self.read_socket()
254254

@@ -389,6 +389,10 @@ def handleURC(self, urc):
389389
self._handle_listen_urc(urc)
390390
self.last_read_payload_length = 0
391391
next_urc_state = Modem.SOCKET_RECEIVE_READ
392+
elif urc.startswith('+UUPSDD: '):
393+
self.event.broadcast('cellular.forced_disconnect')
394+
elif urc.startswith('+UUSOCL: '):
395+
next_urc_state = Modem.SOCKET_CLOSED
392396
else:
393397
self.logger.debug("URC was not handled. \'%s\'", urc)
394398

tests/MessageMode/test_Cloud.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,4 @@ def test_invalid_send_sms(self):
4848
def test_sdk_version(self):
4949
cloud = Cloud(None, send_host = '127.0.0.1', send_port = 9999)
5050

51-
assert cloud.version == '0.7.5'
51+
assert cloud.version == '0.7.6'

version.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.7.5
1+
0.7.6

0 commit comments

Comments
 (0)