# -*- coding: utf-8 -*- # Copyright 2019 Oticon A/S # SPDX-License-Identifier: Apache-2.0 import string; from enum import IntEnum; from components.utils import *; ATT_CID = 4; class ATTProperty(IntEnum): ATT_PROP_BROADCAST = 1 ATT_PROP_READ = 2 ATT_PROP_WRITE_WITHOUT_RESPONSE = 4 ATT_PROP_WRITE = 8 ATT_PROP_NOTIFY = 16 ATT_PROP_INDICATE = 32 ATT_PROP_AUTHENTICATED_SIGNED_WRITES = 64 ATT_PROP_EXTENDED_PROPERTIES = 128 class ATTPermission(IntEnum): ATT_PERM_NONE = 0 ATT_PERM_READ = 1 ATT_PERM_WRITE = 2 ATT_PERM_READ_ENCRYPT = 4 ATT_PERM_WRITE_ENCRYPT = 8 ATT_PERM_READ_AUTHEN = 16 ATT_PERM_WRITE_AUTHEN = 32 ATT_PERM_WRITE_PREPARED = 64 ATT_PERM_READ_AUTHOR = 128 ATT_PERM_WRITE_AUTHOR = 256 ATT_PERM_ANY_READ = ATT_PERM_READ | ATT_PERM_READ_ENCRYPT | ATT_PERM_READ_AUTHEN | ATT_PERM_READ_AUTHOR ATT_PERM_ANY_WRITE = ATT_PERM_WRITE | ATT_PERM_WRITE_ENCRYPT | ATT_PERM_WRITE_AUTHEN | ATT_PERM_WRITE_PREPARED | ATT_PERM_WRITE_AUTHOR # # The Opcode field has the following bits: # Bits: # 0-5: Method # 6: Command Flag (64) # 7: Authentication Signature Flag (128) # class ATTOpcode(IntEnum): ATT_ERROR_RESPONSE = 1 # Request Opcode in Error, Attribute Handle In Error, Error Code ATT_EXCH_MTU_REQUEST = 2 # Client Rx MTU ATT_EXCH_MTU_RESPONSE = 3 # Server Rx MTU ATT_FIND_INFORMATION_REQUEST = 4 # Starting Handle, Ending Handle ATT_FIND_INFORMATION_RESPONSE = 5 # Format, List of Handle, UUID ATT_FIND_BY_TYPE_VALUE_REQUEST = 6 # Starting Handle, Ending Handle, Attribute Type, Attribute Value ATT_FIND_BY_TYPE_VALUE_RESPONSE = 7 # Handles Information List ATT_READ_BY_TYPE_REQUEST = 8 # Starting Handle, Ending Handle, UUID ATT_READ_BY_TYPE_RESPONSE = 9 # Length, Attribute Data List ATT_READ_REQUEST = 10 # Attribute Handle ATT_READ_RESPONSE = 11 # Attribute Value ATT_READ_BLOB_REQUEST = 12 # Attribute Handle, Value Offset ATT_READ_BLOB_RESPONSE = 13 # Part Attribute Value ATT_READ_MULTIPLE_REQUEST = 14 # Handle Set ATT_READ_MULTIPLE_RESPONSE = 15 # Value Set ATT_READ_BY_GROUP_TYPE_REQUEST = 16 # Start Handle, Ending Handle, UUID ATT_READ_BY_GROUP_TYPE_RESPONSE = 17 # Length, Attribute Data List ATT_WRITE_REQUEST = 18 # Attribute Handle, Attribute Value ATT_WRITE_RESPONSE = 19 # - ATT_PREPARE_WRITE_REQUEST = 22 # Attribute Handle, Value Offset, Part Attribute Value ATT_PREPARE_WRITE_RESPONSE = 23 # Attribute Handle, Value Offset, Part Attribute Value ATT_EXECUTE_WRITE_REQUEST = 24 # Flags ATT_EXECUTE_WRITE_RESPONSE = 25 # - ATT_HANDLE_VALUE_NOTIFICATION = 27 # Attribute Handle, Attribute Value ATT_HANDLE_VALUE_INDICATION = 29 # Attribute Handle, Attribute Value ATT_HANDLE_VALUE_CONFIRMATION = 30 # - ATT_INVALID_REQUEST = 31 # - ATT_WRITE_COMMAND = 82 # Attribute Handle, Attribute Value (64+18) ATT_INVALID_COMMAND = 95 # - ATT_SIGNED_WRITE_COMMAND = 210 # Attribute Handle, Attribute Value, Authentication Signature (128+64+18) class ATTError(IntEnum): ATT_ERROR_INVALID_HANDLE = 0x01 # The attribute handle given was not valid on this server. ATT_ERROR_READ_NOT_PERMITTED = 0x02 # The attribute cannot be read. ATT_ERROR_WRITE_NOT_PERMITTED = 0x03 # The attribute cannot be written. ATT_ERROR_INVALID_PDU = 0x04 # The attribute PDU was invalid. ATT_ERROR_INSUFFICIENT_AUTHENTICATION = 0x05 # The attribute requires authentication before it can be read or written. ATT_ERROR_REQUEST_NOT_SUPPORTED = 0x06 # Attribute server does not support the request received from the client. ATT_ERROR_INVALID_OFFSET = 0x07 # Offset specified was past the end of the attribute. ATT_ERROR_INSUFFICIENT_AUTHORIZATION = 0x08 # The attribute requires authorization before it can be read or written. ATT_ERROR_PREPARE_QUEUE_FULL = 0x09 # Too many prepare writes have been queued. ATT_ERROR_ATTRIBUTE_NOT_FOUND = 0x0A # No attribute found within the given attribute handle range. ATT_ERROR_ATTRIBUTE_NOT_LONG = 0x0B # The attribute cannot be read using the Read Blob Request. ATT_ERROR_INSUFFICIENT_ENCRYPTION_KEY_SIZE = 0x0C # The Encryption Key Size used for encrypting this link is insufficient. ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LENGTH = 0x0D # The attribute value length is invalid for the operation. ATT_ERROR_UNLIKELY_ERROR = 0x0E # The attribute request that was requested has encountered an error that was unlikely, and therefore could not be completed as requested. ATT_ERROR_INSUFFICIENT_ENCRYPTION = 0x0F # The attribute requires encryption before it can be read or written. ATT_ERROR_UNSUPPORTED_GROUP_TYPE = 0x10 # The attribute type is not a supported grouping attribute as defined by a higher layer specification. ATT_ERROR_INSUFFICIENT_RESOURCES = 0x11 # Insufficient Resources to complete the request. ATT_ERROR_APPLICATION_ERROR = 0x80 # Application error code defined by a higher layer specification. ATT_ERROR_WRITE_REQUEST_REJECTED = 0xFC # Write Request Rejected. ATT_ERROR_CCC_DESCRIPTOR_IMPROPERLY_CONFIGURED = 0xFD # Client Characteristic Configuration Descriptor Improperly Configured. ATT_ERROR_PROCEDURE_ALREADY_IN_PROGRESS = 0xFE # Procedure Already in Progress. ATT_ERROR_OUT_OF_RANGE = 0xFF # Attribute value is out of range as defined by a profile or service specification. class ATTData: def __init__(self): self.data = []; def encode(self, opcode, *args): # # encode ATT_EXCH_MTU_REQUEST, # where 2 octets # if ( opcode == ATTOpcode.ATT_EXCH_MTU_REQUEST ): self.data = [ opcode ] + toArray( args[0], 2 ); # # encode ATT_FIND_INFORMATION_REQUEST, , # where 2 octets; 2 octets # elif ( opcode == ATTOpcode.ATT_FIND_INFORMATION_REQUEST ): self.data = [ opcode ] + toArray( args[0], 2 ) + toArray( args[1], 2 ); # # encode ATT_FIND_BY_TYPE_VALUE_REQUEST, , , , ... # where 2 octets; 2 octets; 2 octets UUID; 1 octet each # elif ( opcode == ATTOpcode.ATT_FIND_BY_TYPE_VALUE_REQUEST ): self.data = [ opcode ] + toArray( args[0], 2 ) + toArray( args[1], 2 ) + toArray( args[2], 2 ); for arg in args[3:]: self.data += arg; # # encode ATT_READ_BY_TYPE_REQUEST, , , # where 2 octets; 2 octets; 2 or 16 octets UUID # elif ( opcode == ATTOpcode.ATT_READ_BY_TYPE_REQUEST ): self.data = [ opcode ] + toArray( args[0], 2 ) + toArray( args[1], 2 ) + toArray( args[2], 2 if args[2] <= 0xFFFF else 16); # # encode ATT_READ_REQUEST, # where 2 octets # elif ( opcode == ATTOpcode.ATT_READ_REQUEST ): self.data = [ opcode ] + toArray( args[0], 2 ); # # encode ATT_READ_BLOB_REQUEST, , # where 2 octets; 2 octets # elif ( opcode == ATTOpcode.ATT_READ_BLOB_REQUEST ): self.data = [ opcode ] + toArray( args[0], 2 ) + toArray( args[1], 2 ); # # encode ATT_READ_MULTIPLE_REQUEST, ... # where 2 octets each # elif ( opcode == ATTOpcode.ATT_READ_MULTIPLE_REQUEST ): self.data = [ opcode ]; for arg in args[0]: self.data += toArray( arg, 2 ); # # encode ATT_READ_BY_GROUP_TYPE_REQUEST, , , # where 2 octets; 2 octets; 2 or 16 octets UUID # elif ( opcode == ATTOpcode.ATT_READ_BY_GROUP_TYPE_REQUEST ): self.data = [ opcode ] + toArray( args[0], 2 ) + toArray( args[1], 2 ) + toArray( args[2], 2 if args[2] <= 0xFFFF else 16); # # encode ATT_WRITE_REQUEST, , ... # where 2 octets; 1 octet each # elif ( opcode == ATTOpcode.ATT_WRITE_REQUEST ): self.data = [ opcode ] + toArray( args[0], 2 ); for arg in args[1:]: self.data += arg; # # encode ATT_PREPARE_WRITE_REQUEST, , , ... # where 2 octets; 2 octets; 1 octet each # elif ( opcode == ATTOpcode.ATT_PREPARE_WRITE_REQUEST ): self.data = [ opcode ] + toArray( args[0], 2 ) + toArray( args[1], 2 ); for arg in args[2:]: self.data += arg; # # encode ATT_EXECUTE_WRITE_REQUEST, # where 1 octet # elif ( opcode == ATTOpcode.ATT_EXECUTE_WRITE_REQUEST ): self.data = [ opcode, args[0] ]; # # encode ATT_HANDLE_VALUE_CONFIRMATION # elif ( opcode == ATTOpcode.ATT_HANDLE_VALUE_CONFIRMATION ): self.data = [ opcode ]; # # encode ATT_WRITE_COMMAND, , ... # where 2 octets; 1 octet each # elif ( opcode == ATTOpcode.ATT_WRITE_COMMAND ): self.data = [ opcode ] + toArray( args[0], 2 ); for arg in args[1:]: self.data += arg; # # encode ATT_SIGNED_WRITE_COMMAND, , , ... # where 2 octets; 12 octets; 1 octet each # elif ( opcode == ATTOpcode.ATT_SIGNED_WRITE_COMMAND ): self.data = [ opcode ] + toArray( args[0], 2 ) + args[2:] + toArray( args[1], 12 ); # # encode Illegal command... # else: self.data = [ opcode ]; for arg in args: self.data += arg; # # The first two octets in the L2CAP PDU contains the length of the entire L2CAP PDU in octets, excluding the Length and CID fields. # if len(self.data) > 0: self.data = toArray( len(self.data), 2 ) + toArray( ATT_CID, 2 ) + self.data; return self.data; def decode(self, data): self.data = data[:]; size = toNumber( data[:2] ); cid = toNumber( data[2:4] ); opcode = ATTOpcode(data[4]); result = { "opcode": opcode }; # # decode ATT_ERROR_RESPONSE: , , # where 1 octet; 2 octets; 1 octet # if ( opcode == ATTOpcode.ATT_ERROR_RESPONSE ): result["request opcode"] = ATTOpcode(data[5]); result["handle"] = [ toNumber( data[6:8] ) ]; result["error"] = ATTError(data[8]); # # decode ATT_EXCH_MTU_RESPONSE: # where 2 octets # elif ( opcode == ATTOpcode.ATT_EXCH_MTU_RESPONSE ): result["mtu"] = toNumber( data[5:7] ); # # decode ATT_FIND_INFORMATION_RESPONSE: , { , }... # where 1 octet; 2 octets; 2 octets if == 1 else 16 octets # elif ( opcode == ATTOpcode.ATT_FIND_INFORMATION_RESPONSE ): result["handle"] = []; result["uuid"] = []; length = 4 if data[5] == 1 else 18; n = 6; while n < size+3: result["handle"] += [ toNumber( data[n:n+2] ) ]; result["uuid"] += [ toNumber( data[n+2:n+length] ) ]; n += length; # # decode ATT_FIND_BY_TYPE_VALUE_RESPONSE: ... # 2 octets # elif ( opcode == ATTOpcode.ATT_FIND_BY_TYPE_VALUE_RESPONSE ): result["handle"] = []; n = 5; while n < size+3: result["handle"] += [ toNumber( data[n:n+2] ) ]; n += 2; # # decode ATT_READ_BY_TYPE_RESPONSE: , { , ... } # where 1 octet holding the number of octets in each { , ... } set # 2 octets; 1 octet each (-2) octets in total # elif ( opcode == ATTOpcode.ATT_READ_BY_TYPE_RESPONSE ): result["handle"] = []; result["value"] = []; n = 6; while n < size+3: result["handle"] += [ toNumber( data[n:n+2] ) ]; result["value"] += [ data[n+2:n+data[5]] ]; n += data[5]; # # decode ATT_READ_RESPONSE: ... # where 1 octet each # elif ( opcode == ATTOpcode.ATT_READ_RESPONSE ): result["value"] = data[5:]; # # decode ATT_READ_BLOB_RESPONSE: ... # where 1 octet each # elif ( opcode == ATTOpcode.ATT_READ_BLOB_RESPONSE ): result["value"] = data[5:]; # # decode ATT_READ_MULTIPLE_RESPONSE: ... # where 1 octet each # elif ( opcode == ATTOpcode.ATT_READ_MULTIPLE_RESPONSE ): result["value"] = data[5:]; # # decode ATT_READ_BY_GROUP_TYPE_RESPONSE: , { , , ... }... # where 1 octet; 2 octets; 2 octets; 1 octet each ( - 4) # elif ( opcode == ATTOpcode.ATT_READ_BY_GROUP_TYPE_RESPONSE ): result["first_handle"] = []; result["last_handle"] = []; result["value"] = []; n = 6; while n < size+3: result["first_handle"] += [ toNumber( data[n:n+2] ) ]; result["last_handle"] += [ toNumber( data[n+2:n+4] ) ]; result["value"] += [ data[n+4:n+data[5]] ]; n += data[5]; # # decode ATT_WRITE_RESPONSE: # where # # elif ( opcode == ATTOpcode.ATT_WRITE_RESPONSE ): # # decode ATT_PREPARE_WRITE_RESPONSE: , , ... # where 2 octets; 2 octets; 1 octet each # elif ( opcode == ATTOpcode.ATT_PREPARE_WRITE_RESPONSE ): result["handle"] = toNumber( data[5:7] ); result["offset"] = toNumber( data[7:9] ); result["value"] = data[9:]; # # decode ATT_EXECUTE_WRITE_RESPONSE: # where # # elif ( opcode == ATTOpcode.ATT_EXECUTE_WRITE_RESPONSE ): # # decode ATT_HANDLE_VALUE_NOTIFICATION: , ... # where 2 octets; 1 octet each # elif ( opcode == ATTOpcode.ATT_HANDLE_VALUE_NOTIFICATION ): result["handle"] = toNumber( data[5:7] ); result["value"] = data[7:]; # # decode ATT_HANDLE_VALUE_INDICATION: , ... # where 2 octets; 1 octet each # elif ( opcode == ATTOpcode.ATT_HANDLE_VALUE_INDICATION ): result["handle"] = toNumber( data[5:7] ); result["value"] = data[7:]; return result; def __errorText(self, error): result = ATTError(error).name; result = '_'.join([_.lower().capitalize() if _ != 'ATT' else _ for _ in result.split('_')]); return result; def __opcodeName(self, opcode): result = ATTOpcode(opcode).name; result = '_'.join([_.lower().capitalize() if _ != 'ATT' else _ for _ in result.split('_')]); return result; def __hexByteArray(self, start, end): result = ''; for n in range(start, min(len(self.data), end)): if len(result) > 0: result += ' '; result += '%02X' % self.data[n]; return result; def __hexWordArray(self, start, end): result = ''; for n in range(start, end, 2): if len(result) > 0: result += ' '; result += '%04X' % toNumber(self.data[n:n+2]); return result; def __str__(self): size = toNumber( self.data[:2] ); cid = toNumber( self.data[2:4] ); opcode = ATTOpcode( self.data[4] ); result = self.__opcodeName( opcode ); # # ATT_ERROR_RESPONSE: , , # where 1 octet; 2 octets; 1 octet # if ( opcode == ATTOpcode.ATT_ERROR_RESPONSE ): result += ' request=%s handle=0x%04X error=%s' % (self.__opcodeName(self.data[5]), toNumber(self.data[6:8]), self.__errorText(self.data[8])); # # ATT_EXCH_MTU_REQUEST: # where 2 octets # elif ( opcode == ATTOpcode.ATT_EXCH_MTU_REQUEST ): result += ' mtu=%d' % toNumber(self.data[5:7]); # # ATT_EXCH_MTU_RESPONSE: # where 2 octets # elif ( opcode == ATTOpcode.ATT_EXCH_MTU_RESPONSE ): result += ' mtu=%d' % toNumber(self.data[5:7]); # # ATT_FIND_INFORMATION_REQUEST: , # where 2 octets; 2 octets # elif ( opcode == ATTOpcode.ATT_FIND_INFORMATION_REQUEST ): result += ' start=0x%04X end=0x%04X' % (toNumber(self.data[5:7]), toNumber(self.data[7:9])); # # ATT_FIND_INFORMATION_RESPONSE: , { , }... # where 1 octet; 2 octets; 2 octets if == 1 else 16 octets # elif ( opcode == ATTOpcode.ATT_FIND_INFORMATION_RESPONSE ): result += ' format=%d' % self.data[5]; n, length = 6, 4 if self.data[5] == 1 else 18; while n < size+3: if length == 4: result += ' { 0x%04X 0x%04X }' % (toNumber(self.data[n:n+2]), toNumber(self.data[n+2:n+length])); else: result += ' { 0x%04X %s }' % (toNumber(self.data[n:n+2]), self.uuid(toNumber(self.data[n+2:n+length]))); n += length; # # ATT_FIND_BY_TYPE_VALUE_REQUEST: , , , ... # where 2 octets; 2 octets; 2 octets UUID; 1 octet each # elif ( opcode == ATTOpcode.ATT_FIND_BY_TYPE_VALUE_REQUEST ): result += ' start=0x%04X end=0x%04X type=0x%04X values: %s' % (toNumber(self.data[5:7]), toNumber(self.data[7:9]), toNumber(self.data[9:11]), self.__hexByteArray(11,size+4)); # # ATT_FIND_BY_TYPE_VALUE_RESPONSE: ... # 2 octets # elif ( opcode == ATTOpcode.ATT_FIND_BY_TYPE_VALUE_RESPONSE ): result += ' handles: %s' % self.__hexWordArray(5, size+4); # # ATT_READ_BY_TYPE_REQUEST: , , # where 2 octets; 2 octets; 2 or 16 octets UUID # elif ( opcode == ATTOpcode.ATT_READ_BY_TYPE_REQUEST ): result += ' start=0x%04X end=0x%04X' % (toNumber(self.data[5:7]), toNumber(self.data[7:9])); if size > 7: result += ' group type=%s' % self.uuid(toNumber(self.data[9:25])); else: result += ' group type=%04X' % toNumber(self.data[9:11]); # # ATT_READ_BY_TYPE_RESPONSE: , { , ... } # where 1 octet holding the number of octets in each { , ... } set # 2 octets; 1 octet each (-2) octets in total # elif ( opcode == ATTOpcode.ATT_READ_BY_TYPE_RESPONSE ): result += ' length=%d' % self.data[5]; n = 6; while n < size+3: result += ' { handle=0x%04X values: %s }' % (toNumber(self.data[n:n+2]), self.__hexByteArray(n+2, n+self.data[5])); n += self.data[5]; # # ATT_READ_REQUEST: # where 2 octets # elif ( opcode == ATTOpcode.ATT_READ_REQUEST ): result += ' handle=0x%04X' % toNumber(self.data[5:7]); # # ATT_READ_RESPONSE: ... # where 1 octet each # elif ( opcode == ATTOpcode.ATT_READ_RESPONSE ): result += ' values: %s' % self.__hexByteArray(5, size+4); # # ATT_READ_BLOB_REQUEST: , # where 2 octets; 2 octets # elif ( opcode == ATTOpcode.ATT_READ_BLOB_REQUEST ): result += ' handle=0x%04X offset=0x%04X' % (toNumber(self.data[5:7]), toNumber(self.data[7:9])); # # ATT_READ_BLOB_RESPONSE: ... # where 1 octet each # elif ( opcode == ATTOpcode.ATT_READ_BLOB_RESPONSE ): result += ' values: %s' % self.__hexByteArray(5, size+4); # # ATT_READ_MULTIPLE_REQUEST: ... # where 2 octets each # elif ( opcode == ATTOpcode.ATT_READ_MULTIPLE_REQUEST ): result += ' handles: %s' % self.__hexWordArray(5, size+4); # # ATT_READ_MULTIPLE_RESPONSE: ... # where 1 octet each # elif ( opcode == ATTOpcode.ATT_READ_MULTIPLE_RESPONSE ): result += ' values: %s' % self.__hexByteArray(5, size+4); # # ATT_READ_BY_GROUP_TYPE_REQUEST: , , # where 2 octets; 2 octets; 2 or 16 octets UUID # elif ( opcode == ATTOpcode.ATT_READ_BY_GROUP_TYPE_REQUEST ): result += ' start=0x%04X end=0x%04X' % (toNumber(self.data[5:7]), toNumber(self.data[7:9])); if size > 7: result += ' group type=%s' % uuid(toNumber(self.data[9:25])); else: result += ' group type=%04X' % toNumber(self.data[9:11]); # # ATT_READ_BY_GROUP_TYPE_RESPONSE: , { , , ... }... # where 1 octet; 2 octets; 2 octets; 1 octet each ( - 4) # elif ( opcode == ATTOpcode.ATT_READ_BY_GROUP_TYPE_RESPONSE ): result += ' length=%d' % self.data[5]; n, indent = 6, len(result); while n < size+3: if n > 6: result += '\n%*s' % (indent, ' '); result += ' { handle=0x%04X end group=0x%04X values: %s }' % (toNumber(self.data[n:n+2]), toNumber(self.data[n+2:n+4]), self.__hexByteArray(n+4, n+self.data[5])); n += self.data[5]; # # ATT_WRITE_REQUEST: , ... # where 2 octets; 1 octet each # elif ( opcode == ATTOpcode.ATT_WRITE_REQUEST ): result += ' handle=0x%04X values: %s' % (toNumber(self.data[5:7]), self.__hexByteArray(7, size+4)); # # ATT_WRITE_RESPONSE: # where # elif ( opcode == ATTOpcode.ATT_WRITE_RESPONSE ): pass; # # ATT_PREPARE_WRITE_REQUEST: , , ... # where 2 octets; 2 octets; 1 octet each # elif ( opcode == ATTOpcode.ATT_PREPARE_WRITE_REQUEST ): result += ' handle=0x%04X offset=0x%04X values: %s' % (toNumber(self.data[5:7]), toNumber(self.data[7:9]), self.__hexByteArray(9, size+4)); # # ATT_PREPARE_WRITE_RESPONSE: , , ... # where 2 octets; 2 octets; 1 octet each # elif ( opcode == ATTOpcode.ATT_PREPARE_WRITE_RESPONSE ): result += ' handle=0x%04X offset=0x%04X values: %s' % (toNumber(self.data[5:7]), toNumber(self.data[7:9]), self.__hexByteArray(9, size+4)); # # ATT_EXECUTE_WRITE_REQUEST: # where 1 octet # elif ( opcode == ATTOpcode.ATT_EXECUTE_WRITE_REQUEST ): result += ' flags=0x%02X' % self.data[5]; # # ATT_EXECUTE_WRITE_RESPONSE: # where # elif ( opcode == ATTOpcode.ATT_EXECUTE_WRITE_RESPONSE ): pass; # # ATT_HANDLE_VALUE_NOTIFICATION: , ... # where 2 octets; 1 octet each # elif ( opcode == ATTOpcode.ATT_HANDLE_VALUE_NOTIFICATION ): result += ' handle=0x%04X values: %s' % (toNumber(self.data[5:7]), self.__hexByteArray(7, size+4)); # # ATT_HANDLE_VALUE_INDICATION: , ... # where 2 octets; 1 octet each # elif ( opcode == ATTOpcode.ATT_HANDLE_VALUE_INDICATION ): result += ' handle=0x%04X values: %s' % (toNumber(self.data[5:7]), self.__hexByteArray(7, size+4)); # # ATT_HANDLE_VALUE_CONFIRMATION: # elif ( opcode == ATTOpcode.ATT_HANDLE_VALUE_CONFIRMATION ): pass; # # ATT_WRITE_COMMAND: , ... # where 2 octets; 1 octet each # elif ( opcode == ATTOpcode.ATT_WRITE_COMMAND ): result += ' handle=0x%04X values: %s' % (toNumber(self.data[5:7]), self.__hexByteArray(7, size+4)); # # ATT_SIGNED_WRITE_COMMAND: , , ... # where 2 octets; 12 octets; 1 octet each # elif ( opcode == ATTOpcode.ATT_SIGNED_WRITE_COMMAND ): result += ' handle=0x%04X signature=0x%024X values: %s' % (toNumber(self.data[5:7]), toNumber(self.data[7:19]), self.__hexByteArray(19, size+4)); # # ATT_INVALID_REQUEST: # elif ( opcode == ATTOpcode.ATT_INVALID_REQUEST ): result += ' ' + self.__hexByteArray(5, size+4); # # ATT_INVALID_COMMAND: # elif ( opcode == ATTOpcode.ATT_INVALID_COMMAND ): result += ' ' + self.__hexByteArray(5, size+4); return result; def uuid(self, data): if isinstance(data, str): uuid = 0; octet = ''; for char in data: if char in string.hexdigits: octet += char; if len(octet) == 2: uuid <<= 8; uuid += int(octet, 16); octet = ''; else: if data <= 0xFFFFFFFF: uuid = ('%08X' % data) + '00001000800000805F9B34FB' else: uuid = ('%032X' % data) uuid = uuid[:8] + '-' + uuid[8:12] + '-' + uuid[12:16] + '-' + uuid[16:20] + '-' + uuid[20:]; return uuid; def __formatEnumSet(self, value, texts): txt = ''; for n in range(len(texts)): if ( value & (1<