def __init__(self, id=None, using_lightblue=True, pair_first=False): if id is None and not using_lightblue: id = Pebble.AutodetectDevice() self.id = id self.using_lightblue = using_lightblue self._alive = True self._endpoint_handlers = {} 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["MUSIC_CONTROL"]: self._music_control_response, self.endpoints["APPLICATION_MESSAGE"]: self._application_message_response, self.endpoints["LAUNCHER"]: self._application_message_response, self.endpoints["LOGS"]: self._log_response, self.endpoints["PING"]: self._ping_response, self.endpoints["APP_LOGS"]: self._app_log_response, self.endpoints["APP_MANAGER"]: self._appbank_status_response } try: if using_lightblue: self._ser = LightBluePebble(self.id, pair_first) signal.signal(signal.SIGINT, self._exit_signal_handler) else: 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=1) 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 on tid %s" % self._read_thread.name) except PebbleError: raise PebbleError(id, "Failed to connect to Pebble") except: raise
def __init__(self, id = None, using_lightblue = True, pair_first = False, device = None): if id is None and not using_lightblue: id = Pebble.AutodetectDevice() self.id = id self.using_lightblue = using_lightblue self._alive = True self._endpoint_handlers = {} 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["MUSIC_CONTROL"]: self._music_control_response, self.endpoints["APPLICATION_MESSAGE"]: self._application_message_response, self.endpoints["LAUNCHER"]: self._application_message_response, self.endpoints["LOGS"]: self._log_response, self.endpoints["PING"]: self._ping_response, self.endpoints["APP_LOGS"]: self._app_log_response, self.endpoints["APP_MANAGER"]: self._appbank_status_response } try: if device: self._ser = device elif using_lightblue: from LightBluePebble import LightBluePebble self._ser = LightBluePebble(self.id, pair_first) signal.signal(signal.SIGINT, self._exit_signal_handler) else: import serial 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=1) 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 on tid %s" % self._read_thread.name) except PebbleError: raise PebbleError(id, "Failed to connect to Pebble") except: raise
class Pebble(object): """ A connection to a Pebble watch; data and commands may be sent to the watch through an instance of this class. """ endpoints = { "TIME": 11, "VERSION": 16, "PHONE_VERSION": 17, "SYSTEM_MESSAGE": 18, "MUSIC_CONTROL": 32, "PHONE_CONTROL": 33, "APPLICATION_MESSAGE": 48, "LAUNCHER": 49, "LOGS": 2000, "PING": 2001, "LOG_DUMP": 2002, "RESET": 2003, "APP": 2004, "NOTIFICATION": 3000, "RESOURCE": 4000, "APP_MANAGER": 6000, "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=None, using_lightblue=True, pair_first=False): if id is None and not using_lightblue: id = Pebble.AutodetectDevice() self.id = id self.using_lightblue = using_lightblue self._alive = True self._endpoint_handlers = {} 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["MUSIC_CONTROL"]: self._music_control_response, self.endpoints["APPLICATION_MESSAGE"]: self._application_message_response, self.endpoints["LAUNCHER"]: self._application_message_response, self.endpoints["LOGS"]: self._log_response, self.endpoints["PING"]: self._ping_response, self.endpoints["APP_MANAGER"]: self._appbank_status_response } try: if using_lightblue: self._ser = LightBluePebble(self.id, pair_first) signal.signal(signal.SIGINT, self._exit_signal_handler) else: 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=1) 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 on tid %s" % self._read_thread.name) except PebbleError: raise PebbleError(id, "Failed to connect to Pebble") except: raise def _exit_signal_handler(self, signum, frame): print "Disconnecting before exiting..." self.disconnect() time.sleep(1) os._exit(0) def __del__(self): try: self._ser.close() except: pass def _reader(self): try: while self._alive: endpoint, resp = self._recv_message() if resp == None: continue if endpoint in self._internal_endpoint_handlers: resp = self._internal_endpoint_handlers[endpoint](endpoint, resp) if endpoint in self._endpoint_handlers and resp: self._endpoint_handlers[endpoint](endpoint, resp) except: traceback.print_exc() raise PebbleError(self.id, "Lost connection to Pebble") self._alive = False def _pack_message_data(self, lead, parts): pascal = map(lambda x: x[:255], parts) d = pack( "b" + reduce(lambda x, y: str(x) + "p" + str(y), map(lambda x: len(x) + 1, pascal)) + "p", lead, *pascal) return d 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 PebbleError(self.id, "Invalid endpoint specified") msg = self._build_message(self.endpoints[endpoint], data) if DEBUG_PROTOCOL: log.debug('>>> ' + msg.encode('hex')) self._ser.write(msg) def _recv_message(self): if self.using_lightblue: try: endpoint, resp, data = self._ser.read() if resp is None: return None, None except TypeError: # the lightblue process has likely shutdown and cannot be read from self.alive = False return None, None else: data = self._ser.read(4) if len(data) == 0: return (None, None) elif len(data) < 4: 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): if endpoint_name not in self.endpoints: raise PebbleError(self.id, "Invalid endpoint specified") endpoint = self.endpoints[endpoint_name] self._endpoint_handlers[endpoint] = func def notification_sms(self, sender, body): """Send a 'SMS Notification' to the displayed on the watch.""" ts = str(int(time.time()) * 1000) parts = [sender, body, ts] self._send_message("NOTIFICATION", self._pack_message_data(1, parts)) def notification_email(self, sender, subject, body): """Send an 'Email Notification' to the displayed on the watch.""" ts = str(int(time.time()) * 1000) parts = [sender, body, ts, subject] self._send_message("NOTIFICATION", self._pack_message_data(0, parts)) def set_nowplaying_metadata(self, track, album, artist): """Update the song metadata displayed in Pebble's music app.""" parts = [artist[:30], album[:30], track[:30]] self._send_message("MUSIC_CONTROL", self._pack_message_data(16, parts)) def get_versions(self, async=False): """ Retrieve a summary of version information for various software (firmware, bootloader, etc) running on the watch. """ self._send_message("VERSION", "\x00") if not async: return EndpointSync(self, "VERSION").get_data()
class Pebble(object): """ A connection to a Pebble watch; data and commands may be sent to the watch through an instance of this class. """ endpoints = { "TIME": 11, "VERSION": 16, "PHONE_VERSION": 17, "SYSTEM_MESSAGE": 18, "MUSIC_CONTROL": 32, "PHONE_CONTROL": 33, "APPLICATION_MESSAGE": 48, "LAUNCHER": 49, "LOGS": 2000, "PING": 2001, "LOG_DUMP": 2002, "RESET": 2003, "APP": 2004, "APP_LOGS": 2006, "NOTIFICATION": 3000, "RESOURCE": 4000, "APP_MANAGER": 6000, "PUTBYTES": 48879 } log_levels = { 0: "*", 1: "E", 50: "W", 100: "I", 200: "D", 250: "V" } @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 = None, using_lightblue = True, pair_first = False): if id is None and not using_lightblue: id = Pebble.AutodetectDevice() self.id = id self.using_lightblue = using_lightblue self._alive = True self._endpoint_handlers = {} 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["MUSIC_CONTROL"]: self._music_control_response, self.endpoints["APPLICATION_MESSAGE"]: self._application_message_response, self.endpoints["LAUNCHER"]: self._application_message_response, self.endpoints["LOGS"]: self._log_response, self.endpoints["PING"]: self._ping_response, self.endpoints["APP_LOGS"]: self._app_log_response, self.endpoints["APP_MANAGER"]: self._appbank_status_response } try: if using_lightblue: self._ser = LightBluePebble(self.id, pair_first) signal.signal(signal.SIGINT, self._exit_signal_handler) else: 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=1) 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 on tid %s" % self._read_thread.name) except PebbleError: raise PebbleError(id, "Failed to connect to Pebble") except: raise def _exit_signal_handler(self, signum, frame): print "Disconnecting before exiting..." self.disconnect() time.sleep(1) os._exit(0) def __del__(self): try: self._ser.close() except: pass def _reader(self): try: while self._alive: endpoint, resp = self._recv_message() if resp == None: continue if endpoint in self._internal_endpoint_handlers: resp = self._internal_endpoint_handlers[endpoint](endpoint, resp) if endpoint in self._endpoint_handlers and resp: self._endpoint_handlers[endpoint](endpoint, resp) except: traceback.print_exc() raise PebbleError(self.id, "Lost connection to Pebble") self._alive = False def _pack_message_data(self, lead, parts): pascal = map(lambda x: x[:255], parts) d = pack("b" + reduce(lambda x,y: str(x) + "p" + str(y), map(lambda x: len(x) + 1, pascal)) + "p", lead, *pascal) return d 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 PebbleError(self.id, "Invalid endpoint specified") msg = self._build_message(self.endpoints[endpoint], data) if DEBUG_PROTOCOL: log.debug('>>> ' + msg.encode('hex')) self._ser.write(msg) def _recv_message(self): if self.using_lightblue: try: endpoint, resp, data = self._ser.read() if resp is None: return None, None except TypeError: # the lightblue process has likely shutdown and cannot be read from self.alive = False return None, None else: data = self._ser.read(4) if len(data) == 0: return (None, None) elif len(data) < 4: 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): if endpoint_name not in self.endpoints: raise PebbleError(self.id, "Invalid endpoint specified") endpoint = self.endpoints[endpoint_name] self._endpoint_handlers[endpoint] = func def notification_sms(self, sender, body): """Send a 'SMS Notification' to the displayed on the watch.""" ts = str(int(time.time())*1000) parts = [sender, body, ts] self._send_message("NOTIFICATION", self._pack_message_data(1, parts)) def notification_email(self, sender, subject, body): """Send an 'Email Notification' to the displayed on the watch.""" ts = str(int(time.time())*1000) parts = [sender, body, ts, subject] self._send_message("NOTIFICATION", self._pack_message_data(0, parts)) def set_nowplaying_metadata(self, track, album, artist): """Update the song metadata displayed in Pebble's music app.""" parts = [artist[:30], album[:30], track[:30]] self._send_message("MUSIC_CONTROL", self._pack_message_data(16, parts)) def get_versions(self, async = False): """ Retrieve a summary of version information for various software (firmware, bootloader, etc) running on the watch. """ self._send_message("VERSION", "\x00") if not async: return EndpointSync(self, "VERSION").get_data()
class Pebble(object): """ A connection to a Pebble watch; data and commands may be sent to the watch through an instance of this class. """ endpoints = { "TIME": 11, "VERSION": 16, "PHONE_VERSION": 17, "SYSTEM_MESSAGE": 18, "MUSIC_CONTROL": 32, "PHONE_CONTROL": 33, "APPLICATION_MESSAGE": 48, "LAUNCHER": 49, "LOGS": 2000, "PING": 2001, "LOG_DUMP": 2002, "RESET": 2003, "APP": 2004, "APP_LOGS": 2006, "NOTIFICATION": 3000, "RESOURCE": 4000, "APP_MANAGER": 6000, "PUTBYTES": 48879 } log_levels = { 0: "*", 1: "E", 50: "W", 100: "I", 200: "D", 250: "V" } @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 = None, using_lightblue = True, pair_first = False): if id is None and not using_lightblue: id = Pebble.AutodetectDevice() self.id = id self.using_lightblue = using_lightblue self._alive = True self._endpoint_handlers = {} 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["MUSIC_CONTROL"]: self._music_control_response, self.endpoints["APPLICATION_MESSAGE"]: self._application_message_response, self.endpoints["LAUNCHER"]: self._application_message_response, self.endpoints["LOGS"]: self._log_response, self.endpoints["PING"]: self._ping_response, self.endpoints["APP_LOGS"]: self._app_log_response, self.endpoints["APP_MANAGER"]: self._appbank_status_response } try: if using_lightblue: self._ser = LightBluePebble(self.id, pair_first) signal.signal(signal.SIGINT, self._exit_signal_handler) else: 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=1) 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 on tid %s" % self._read_thread.name) except PebbleError: raise PebbleError(id, "Failed to connect to Pebble") except: raise def _exit_signal_handler(self, signum, frame): print "Disconnecting before exiting..." self.disconnect() time.sleep(1) os._exit(0) def __del__(self): try: self._ser.close() except: pass def _reader(self): try: while self._alive: endpoint, resp = self._recv_message() if resp == None: continue if endpoint in self._internal_endpoint_handlers: resp = self._internal_endpoint_handlers[endpoint](endpoint, resp) if endpoint in self._endpoint_handlers and resp: self._endpoint_handlers[endpoint](endpoint, resp) except: traceback.print_exc() raise PebbleError(self.id, "Lost connection to Pebble") self._alive = False def _pack_message_data(self, lead, parts): pascal = map(lambda x: x[:255], parts) d = pack("b" + reduce(lambda x,y: str(x) + "p" + str(y), map(lambda x: len(x) + 1, pascal)) + "p", lead, *pascal) return d 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 PebbleError(self.id, "Invalid endpoint specified") msg = self._build_message(self.endpoints[endpoint], data) if DEBUG_PROTOCOL: log.debug('>>> ' + msg.encode('hex')) self._ser.write(msg) def _recv_message(self): if self.using_lightblue: try: endpoint, resp, data = self._ser.read() if resp is None: return None, None except TypeError: # the lightblue process has likely shutdown and cannot be read from self.alive = False return None, None else: data = self._ser.read(4) if len(data) == 0: return (None, None) elif len(data) < 4: 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): if endpoint_name not in self.endpoints: raise PebbleError(self.id, "Invalid endpoint specified") endpoint = self.endpoints[endpoint_name] self._endpoint_handlers[endpoint] = func def set_nowplaying_metadata(self, track, album, artist): """Update the song metadata displayed in Pebble's music app.""" parts = [artist[:30], album[:30], track[:30]] self._send_message("MUSIC_CONTROL", self._pack_message_data(16, parts)) def system_message(self, command): """ Send a 'system message' to the watch. These messages are used to signal important events/state-changes to the watch firmware. """ commands = { "FIRMWARE_AVAILABLE": 0, "FIRMWARE_START": 1, "FIRMWARE_COMPLETE": 2, "FIRMWARE_FAIL": 3, "FIRMWARE_UP_TO_DATE": 4, "FIRMWARE_OUT_OF_DATE": 5, "BLUETOOTH_START_DISCOVERABLE": 6, "BLUETOOTH_END_DISCOVERABLE": 7 } if command not in commands: 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 disconnect(self): """Disconnect from the target Pebble.""" self._alive = False self._ser.close() def _ping_response(self, endpoint, data): restype, retcookie = unpack("!bL", data) return retcookie def _get_time_response(self, endpoint, data): restype, timestamp = unpack("!bL", data) return timestamp def _system_message_response(self, endpoint, data): if len(data) == 2: log.info("Got system message %s" % repr(unpack('!bb', data))) else: log.info("Got 'unknown' system message...") def _log_response(self, endpoint, data): if (len(data) < 8): log.warn("Unable to decode log message (length %d is less than 8)" % len(data)) return timestamp, level, msgsize, linenumber = unpack("!IBBH", data[:8]) filename = data[8:24].decode('utf-8') message = data[24:24+msgsize].decode('utf-8') str_level = self.log_levels[level] if level in self.log_levels else "?" print timestamp, str_level, filename, linenumber, message def _app_log_response(self, endpoint, data): if (len(data) < 8): log.warn("Unable to decode log message (length %d is less than 8)" % len(data)) return app_uuid = uuid.UUID(bytes=data[0:16]) timestamp, level, msgsize, linenumber = unpack("!IBBH", data[16:24]) filename = data[24:40].decode('utf-8') message = data[40:40+msgsize].decode('utf-8') str_level = self.log_levels[level] if level in self.log_levels else "?" print timestamp, str_level, app_uuid, filename, linenumber, message def _appbank_status_response(self, endpoint, data): apps = {} restype, = unpack("!b", data[0]) app_install_message = { 0: "app available", 1: "app removed", 2: "app updated" } if restype == 1: apps["banks"], apps_installed = unpack("!II", data[1:9]) apps["apps"] = [] appinfo_size = 78 offset = 9 for i in xrange(apps_installed): app = {} try: app["id"], app["index"], app["name"], app["company"], app["flags"], app["version"] = \ unpack("!II32s32sIH", data[offset:offset+appinfo_size]) app["name"] = app["name"].replace("\x00", "") app["company"] = app["company"].replace("\x00", "") apps["apps"] += [app] except: if offset+appinfo_size > len(data): log.warn("Couldn't load bank %d; remaining data = %s" % (i,repr(data[offset:]))) else: raise offset += appinfo_size return apps elif restype == 2: message_id = unpack("!I", data[1:]) message_id = int(''.join(map(str, message_id))) return app_install_message[message_id] def _version_response(self, endpoint, data): fw_names = { 0: "normal_fw", 1: "recovery_fw" } resp = {} for i in xrange(2): fwver_size = 47 offset = i*fwver_size+1 fw = {} fw["timestamp"],fw["version"],fw["commit"],fw["is_recovery"], \ fw["hardware_platform"],fw["metadata_ver"] = \ unpack("!i32s8s?bb", data[offset:offset+fwver_size]) fw["version"] = fw["version"].replace("\x00", "") fw["commit"] = fw["commit"].replace("\x00", "") fw_name = fw_names[i] resp[fw_name] = fw resp["bootloader_timestamp"],resp["hw_version"],resp["serial"] = \ unpack("!L9s12s", data[95:120]) resp["hw_version"] = resp["hw_version"].replace("\x00","") btmac_hex = binascii.hexlify(data[120:126]) resp["btmac"] = ":".join([btmac_hex[i:i+2].upper() for i in reversed(xrange(0, 12, 2))]) return resp def _application_message_response(self, endpoint, data): app_messages = { b'\x01': "PUSH", b'\x02': "REQUEST", b'\xFF': "ACK", b'\x7F': "NACK" } if len(data) > 1: rest = data[1:] else: rest = '' if data[0] in app_messages: return app_messages[data[0]] + rest def _phone_version_response(self, endpoint, data): session_cap = { "GAMMA_RAY" : 0x80000000, } remote_cap = { "TELEPHONY" : 16, "SMS" : 32, "GPS" : 64, "BTLE" : 128, "CAMERA_REAR" : 256, "ACCEL" : 512, "GYRO" : 1024, "COMPASS" : 2048, } os = { "UNKNOWN" : 0, "IOS" : 1, "ANDROID" : 2, "OSX" : 3, "LINUX" : 4, "WINDOWS" : 5, } # 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 = pack("!biII", 1, -1, session, remote) self._send_message("PHONE_VERSION", msg); def _music_control_response(self, endpoint, data): event, = unpack("!b", data) event_names = { 1: "PLAYPAUSE", 4: "NEXT", 5: "PREVIOUS", } return event_names[event] if event in event_names else None