diff --git a/p.py b/p.py index 53bb407..031b191 100755 --- a/p.py +++ b/p.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 import argparse import pebble as libpebble diff --git a/pebble/pebble.py b/pebble/pebble.py index 807146c..fb7716f 100755 --- a/pebble/pebble.py +++ b/pebble/pebble.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 import binascii import serial @@ -153,6 +153,7 @@ def __init__(self, id = None): self._internal_endpoint_handlers = { self.endpoints["TIME"]: self._get_time_response, self.endpoints["VERSION"]: self._version_response, + self.endpoints["PHONE_VERSION"]: self._phone_version_response, self.endpoints["SYSTEM_MESSAGE"]: self._system_message_response, self.endpoints["LOGS"]: self._log_response, self.endpoints["PING"]: self._ping_response, @@ -164,13 +165,7 @@ def __init__(self, id = None): log.debug("Attempting to open %s as Pebble device %s" % (devicefile, id)) self._ser = serial.Serial(devicefile, 115200, timeout=1) - log.debug("Connected, discarding null response") - # we get a null response when we connect, discard it - self._ser.read(5) - - # Eat any cruft that might be sitting in the serial buffer... - while self._ser.read(): - pass + log.debug("Connected") log.debug("Initializing reader thread") self._read_thread = threading.Thread(target=self._reader) @@ -193,10 +188,6 @@ def _reader(self): if resp == None: continue - if DEBUG_PROTOCOL: - log.debug("Got message for endpoint %s of length %d" % (endpoint, len(resp))) - log.debug('<<< ' + resp.encode('hex')) - if endpoint in self._internal_endpoint_handlers: resp = self._internal_endpoint_handlers[endpoint](endpoint, resp) @@ -228,6 +219,11 @@ def _recv_message(self): raise PebbleError(self.id, "Malformed response with length "+str(len(data))) size, endpoint = unpack("!HH", data) resp = self._ser.read(size) + + if DEBUG_PROTOCOL: + log.debug("Got message for endpoint %s of length %d" % (endpoint, len(resp))) + log.debug('<<< ' + (data + resp).encode('hex')) + return (endpoint, resp) def register_endpoint(self, endpoint_name, func): @@ -454,6 +450,40 @@ def ping(self, cookie = 0, async = False): if not async: return EndpointSync(self, "PING").get_data() + phone_control_commands = { + "ANSWER" : 1, + "HANGUP" : 2, + "GET_STATE" : 3, + "INCOMING_CALL" : 4, + "OUTGOING_CALL" : 5, + "MISSED_CALL" : 6, + "RING" : 7, + "START" : 8, + "END" : 9, + } + + def phone_incoming(self, number, name, cookie = 0, async = False): + + """Send a 'phone_control' notification for incoming call.""" + + fmt = "!bL" + str(1+len(number)) + "p" + str(1+len(name)) + "p" + data = pack(fmt, self.phone_control_commands["INCOMING_CALL"], cookie, number, name) + self._send_message("PHONE_CONTROL", data) + + def phone_start(self, cookie = 0, async = False): + + """Send a 'phone_control' notification of start.""" + + data = pack("!bL", self.phone_control_commands["START"], cookie) + self._send_message("PHONE_CONTROL", data) + + def phone_end(self, cookie = 0, async = False): + + """Send a 'phone_control' notification of end.""" + + data = pack("!bL", self.phone_control_commands["END"], cookie) + self._send_message("PHONE_CONTROL", data) + def reset(self): """Reset the watch remotely.""" @@ -558,6 +588,44 @@ def _version_response(self, endpoint, data): return resp + def _phone_version_response(self, endpoint, data): + session_cap = { + "GAMMA_RAY" : 0x80000000, + } + remote_cap = { + "TELEPHONY" : 16, + "SMS" : 32, + "GPS" : 64, + "BTLE" : 128, + # XXX: CAMERA_FRONT is 240 in the APK, but this can't + # be right, as this will ruin the bitmask. Check + # future app versions + "CAMERA_FRONT" : 240, + "CAMERA_REAR" : 256, + "ACCEL" : 512, + "GYRO" : 1024, + "COMPASS" : 2048, + } + os = { + "UNKNOWN" : 0, + "IOS" : 1, + "ANDROID" : 2, + "OSX" : 3, + "LINUX" : 4, + "WINDOWS" : 5, + } + # Magic prefix that the app adds + prefix = "\x01\xff\xff\xff\xff" + # Then session capabilities, android adds GAMMA_RAY and it's + # the only session flag so far + session = session_cap["GAMMA_RAY"] + # Then phone capabilities, android app adds TELEPHONY and SMS, + # and the phone type (we know android works for now) + remote = remote_cap["TELEPHONY"] | remote_cap["SMS"] | os["ANDROID"] + msg = prefix + pack("!II", session, remote) + self._send_message("PHONE_VERSION", msg); + return data + class PutBytesClient(object): states = { "NOT_STARTED": 0, diff --git a/repl.py b/repl.py index 84119ee..521bb46 100755 --- a/repl.py +++ b/repl.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 import argparse import pebble as libpebble