Permalink
Browse files

Merge pull request #1 from Hexxeh/master

Pebble auto detection by aleksandyr
  • Loading branch information...
kiding committed Feb 23, 2013
2 parents 49beae3 + 83ea8c1 commit 620275edb45f7b00a501014858d476ee5bd2fae5
Showing with 70 additions and 21 deletions.
  1. 0 __init__.py
  2. +69 −21 pebble.py
  3. +1 −0 requirements.txt
View
No changes.
View
@@ -9,7 +9,16 @@
import traceback
import zipfile
from struct import pack, unpack
+import os
+import glob
+import logging
+log = logging.getLogger()
+logging.basicConfig(format='[%(levelname)-8s] %(message)s')
+log.setLevel(logging.DEBUG)
+
+#DEFAULT_PEBBLE_ID = "402F"
+DEFAULT_PEBBLE_ID = None #Triggers autodetection on unix-like systems
class EndpointSync():
timeout = 10
@@ -29,6 +38,13 @@ def get_data(self):
except:
return False
+class PebbleError(Exception):
+ def __init__(self, id, message):
+ self._id = id
+ self._message = message
+
+ def __str__(self):
+ return "%s (ID:%s)" % (self._message, self._id)
class Pebble(object):
@@ -55,7 +71,27 @@ class Pebble(object):
"PUTBYTES": 48879
}
+ @staticmethod
+ def AutodetectDevice():
+ if os.name != "posix": #i.e. Windows
+ raise NotImplementedError("Autodetection is only implemented on UNIX-like systems.")
+
+ pebbles = glob.glob("/dev/tty.Pebble????-SerialPortSe")
+
+ if len(pebbles) == 0:
+ raise PebbleError(None, "Autodetection could not find any Pebble devices")
+ elif len(pebbles) > 1:
+ log.warn("Autodetect found %d Pebbles; using most recent" % len(pebbles))
+ #NOTE: Not entirely sure if this is the correct approach
+ pebbles.sort(key=lambda x: os.stat(x).st_mtime, reverse=True)
+
+ id = pebbles[0][15:19]
+ log.info("Autodetect found a Pebble with ID %s" % id)
+ return id
+
def __init__(self, id):
+ if id is None:
+ id = Pebble.AutodetectDevice()
self._alive = True
self._endpoint_handlers = {}
self._internal_endpoint_handlers = {
@@ -68,15 +104,19 @@ def __init__(self, id):
}
try:
- self._ser = serial.Serial("/dev/tty.Pebble"+id+"-SerialPortSe", 115200, timeout=2)
+ devicefile = "/dev/tty.Pebble"+id+"-SerialPortSe"
+ log.debug("Attempting to open %s as Pebble device %s" % (devicefile, id))
+ self._ser = serial.Serial(devicefile, 115200, timeout=2)
+ log.debug("Connected, discarding null response")
# we get a null response when we connect, discard it
self._ser.read(5)
-
+ log.debug("Initializing reader thread")
self._read_thread = threading.Thread(target=self._reader)
self._read_thread.setDaemon(True)
self._read_thread.start()
+ log.debug("Reader thread loaded")
except:
- raise Exception("Failed to connect to Pebble")
+ raise PebbleError(id, "Failed to connect to Pebble")
def __del__(self):
try:
@@ -91,7 +131,7 @@ def _reader(self):
if resp == None:
continue
- #print "got message for endpoint "+str(endpoint)+" of length "+str(len(resp))
+ log.debug("Got message for endpoint %s of length %d" % (endpoint, len(resp)))
if endpoint in self._internal_endpoint_handlers:
resp = self._internal_endpoint_handlers[endpoint](endpoint, resp)
@@ -100,15 +140,15 @@ def _reader(self):
self._endpoint_handlers[endpoint](endpoint, resp)
except:
traceback.print_exc()
- raise Exception("Lost connection to Pebble")
+ raise PebbleError(self._id, "Lost connection to Pebble")
self._alive = False
def _build_message(self, endpoint, data):
return pack("!HH", len(data), endpoint)+data
def _send_message(self, endpoint, data, callback = None):
if endpoint not in self.endpoints:
- raise Exception("Invalid endpoint specified")
+ raise PebbleError(self._id, "Invalid endpoint specified")
msg = self._build_message(self.endpoints[endpoint], data)
self._ser.write(msg)
@@ -118,14 +158,14 @@ def _recv_message(self):
if len(data) == 0:
return (None, None)
elif len(data) < 4:
- raise Exception("Malformed response with length "+str(len(data)))
+ raise PebbleError(self._id, "Malformed response with length "+str(len(data)))
size, endpoint = unpack("!HH", data)
resp = self._ser.read(size)
return (endpoint, resp)
def register_endpoint(self, endpoint_name, func):
if endpoint_name not in self.endpoints:
- raise Exception("Invalid endpoint specified")
+ raise PebbleError(self._id, "Invalid endpoint specified")
endpoint = self.endpoints[endpoint_name]
self._endpoint_handlers[endpoint] = func
@@ -242,23 +282,24 @@ def install_app(self, pbz_path):
if app["index"] == first_free:
first_free += 1
if first_free == apps["banks"]:
- raise Exception("No available app banks left")
+ raise PebbleError(self._id, "All %d app banks are full" % apps["banks"])
+ log.debug("Attempting to add app to bank %d of %d" % (first_free, apps["banks"]))
client = PutBytesClient(self, first_free, "BINARY", binary)
self.register_endpoint("PUTBYTES", client.handle_message)
client.init()
while not client._done and not client._error:
pass
if client._error:
- raise Exception("Failed to send application binary")
+ raise PebbleError(self._id, "Failed to send application binary %s/pebble-app.bin" % pbz_path)
client = PutBytesClient(self, first_free, "RESOURCES", resources)
self.register_endpoint("PUTBYTES", client.handle_message)
client.init()
while not client._done and not client._error:
pass
if client._error:
- raise Exception("Failed to send application resources")
+ raise PebbleError(self._id, "Failed to send application resources %s/app_resources.pbpack" % pbz_path)
self._add_app(first_free)
@@ -281,7 +322,7 @@ def install_firmware(self, pbz_path, recovery=False):
while not client._done and not client._error:
pass
if client._error:
- raise Exception("Failed to send firmware resources")
+ raise PebbleError(self._id, "Failed to send firmware resources %s/system_resources.pbpack" % pbz_path)
client = PutBytesClient(self, 0, "RECOVERY" if recovery else "FIRMWARE", binary)
@@ -290,7 +331,7 @@ def install_firmware(self, pbz_path, recovery=False):
while not client._done and not client._error:
pass
if client._error:
- raise Exception("Failed to send firmware binary")
+ raise PebbleError(self._id, "Failed to send firmware binary %s/tintin_fw.bin" % pbz_path)
self.system_message("FIRMWARE_COMPLETE")
@@ -314,8 +355,9 @@ def system_message(self, command):
"BLUETOOTH_END_DISCOVERABLE": 7
}
if command not in commands:
- raise Exception("Invalid command")
+ raise PebbleError(self._id, "Invalid command \"%s\"" % command)
data = pack("!bb", 0, commands[command])
+ log.debug("Sending command %s (code %d)" % (command, commands[command]))
self._send_message("SYSTEM_MESSAGE", data)
def ping(self, cookie = 0, async = False):
@@ -354,12 +396,11 @@ def _get_time_response(self, endpoint, data):
return timestamp
def _system_message_response(self, endpoint, data):
- print "got system message"
- print unpack("!bb", data)
+ log.info("Got system message %s" % repr(unpack('!bb', data)))
def _log_response(self, endpoint, data):
if (len(data) < 8):
- print 'Unable to decode log message'
+ log.warn("Unable to decode log message (length %d is less than 8)" % len(data))
return;
timestamp, level, msgsize, linenumber = unpack("!Ibbh", data[:8])
@@ -466,8 +507,7 @@ def init(self):
def wait_for_token(self, resp):
res, = unpack("!b", resp[0])
if res != 1:
- print "init failed!"
- print res
+ log.error("init failed with code %d" % res)
self._error = True
return
self._token, = unpack("!I", resp[1:])
@@ -482,7 +522,7 @@ def in_progress(self, resp):
return
if self._left > 0:
self.send()
- #print "sent "+str(len(self._buffer)-self._left)+" of "+str(len(self._buffer))+" bytes"
+ log.debug("Sent %d of %d bytes" % (len(self._buffer)-self._left, len(self._buffer)))
else:
self._state = self.states["COMMIT"]
self.commit()
@@ -534,7 +574,15 @@ def handle_message(self, endpoint, resp):
self.handle_complete(resp)
if __name__ == '__main__':
- pebble_id = sys.argv[1] if len(sys.argv) > 1 else "402F"
+ if DEFAULT_PEBBLE_ID is not None:
+ log.debug("Default Pebble ID is %s" % DEFAULT_PEBBLE_ID)
+ else:
+ log.debug("No default Pebble ID, using autodetection if available")
+
+ if len(sys.argv) > 1:
+ pebble_id = sys.argv[1]
+ else:
+ pebble_id = DEFAULT_PEBBLE_ID
pebble = Pebble(pebble_id)
versions = pebble.get_versions()
View
@@ -0,0 +1 @@
+pyserial>=2.6

0 comments on commit 620275e

Please sign in to comment.