Example #1
0
	def discoverCharacteristics(self, uuid=None):
		"""Find characteristics under the service

		Notes: if uuid is None, all characteristics are discovered. Any
			previously discovered characteristics are invalidated.
		Args:
			uuid : a specific uuid to discover
		Returns:
			A list of discovered characteristics
		"""
		if uuid and not isinstance(uuid, UUID):
			uuid = UUID(uuid)

		allCharacs, allCharacHandles = self.__discover_all_characteristics()
		if uuid is None:
			self._characteristicHandles = allCharacHandles
			self.characteristics = allCharacs
			return allCharacs
		else:
			characs = [x for x in allCharacs if x.uuid == uuid]
			for charac in characs:
				if charac._handleNo not in self._characteristicHandles:
					self._characteristicHandles[charac._handleNo] = charac
					self.characteristics.append(charac)
			self.characteristics.sort(key=lambda x: x._handleNo)
			return characs
Example #2
0
	def discoverServices(self, uuid=None, startHandle=1, endHandle=0xFFFF):
		"""Find services on the server.

		Notes: if uuid is None, all services are discovered. Any previously
			discovered services are invalidated.
		Args:
			uuid : type of service to discover
			startHandle : beginning of the handle range
			endHandle : end of the handle range
		Returns:
			A list of services."""
		if startHandle == 0:
			raise ClientError("invalid start handle")

		if startHandle > endHandle:
			raise ClientError("invalid handle range")

		if uuid and not isinstance(uuid, UUID):
			uuid = UUID(uuid)

		if uuid is None:
			return self.__discover_all_services(startHandle=startHandle,
				endHandle=endHandle)
		else:
			return self.__discover_services_by_uuid(uuid,
				startHandle=startHandle, endHandle=endHandle)
Example #3
0
	def __discover_services(self, startHandle, endHandle):
		primSvcUuid = UUID(gatt.PRIM_SVC_UUID)

		services = []
		serviceHandles = {}
		currHandle = startHandle
		while True:
			req = att_pdu.new_read_by_group_req(currHandle, endHandle,
				primSvcUuid)

			resp = self._new_transaction(req)
			if not resp:
				raise ClientError("transaction failed")

			if resp[0] == att.OP_READ_BY_GROUP_RESP:
				attDataLen = resp[1]
				idx = 2

				endGroup = currHandle # not needed
				while idx < len(resp) and idx + attDataLen <= len(resp):
					handleNo = att_pdu.unpack_handle(resp, idx)
					endGroup = att_pdu.unpack_handle(resp, idx + 2)
					uuid = UUID(resp[idx+4:idx+attDataLen], reverse=True)

					service = _ClientService(self, uuid, handleNo, endGroup)
					services.append(service)
					serviceHandles[handleNo] = service

					idx += attDataLen

				currHandle = endGroup + 1
				if currHandle >= endHandle:
					break

			elif (resp[0] == att.OP_ERROR and len(resp) == att_pdu.ERROR_PDU_LEN
				and resp[1] == att.OP_READ_BY_GROUP_REQ
				and resp[4] == att.ECODE_ATTR_NOT_FOUND):
				break

			elif (resp[0] == att.OP_ERROR and len(resp) == att_pdu.ERROR_PDU_LEN
				and resp[1] == att.OP_READ_BY_GROUP_REQ):
				raise ClientError("error - %s" % att.ecodeLookup(resp[4]))

			else:
				raise ClientError("unexpected - %s" % att.opcodeLookup(resp[0]))

		return services, serviceHandles
Example #4
0
	def __discover_services_by_uuid(self, uuid, startHandle, endHandle):
		primSvcUuid = UUID(gatt.PRIM_SVC_UUID)

		services = []
		newServices = []
		serviceHandles = {}
		currHandle = startHandle

		while True:
			req = att_pdu.new_find_by_type_value_req(currHandle, endHandle,
				primSvcUuid, uuid.raw[::-1])

			resp = self._new_transaction(req)
			if not resp:
				raise ClientError("transaction failed")

			if resp[0] == att.OP_FIND_BY_TYPE_REQ:
				idx = 2

				endGroup = currHandle # not needed
				while idx < len(resp) and idx + 4 <= len(resp):
					handleNo = att_pdu.unpack_handle(resp, idx)
					endGroup = att_pdu.unpack_handle(resp, idx + 2)

					if handleNo in self._serviceHandles:
						service = self._serviceHandles[handleNo]
					else:
						service = _ClientService(self, uuid, handleNo, endGroup)
						serviceHandles[handleNo] = service
						newServices.append(service)
					services.append(service)

					idx += 4

				currHandle = endGroup + 1
				if currHandle >= endHandle:
					break

			elif (resp[0] == att.OP_ERROR and len(resp) == att_pdu.ERROR_PDU_LEN
				and resp[1] == att.OP_FIND_BY_TYPE_REQ
				and resp[4] == att.ECODE_ATTR_NOT_FOUND):
				break

			elif (resp[0] == att.OP_ERROR and len(resp) == att_pdu.ERROR_PDU_LEN
				and resp[1] == att.OP_FIND_BY_TYPE_REQ):
				raise ClientError("error - %s" % att.ecodeLookup(resp[4]))

			else:
				raise ClientError("unexpected - %s" % att.opcodeLookup(resp[0]))

		self.services.extend(newServices)
		self.services.sort(key=lambda x: x._handleNo)
		self._serviceHandles.update(serviceHandles)

		return services
Example #5
0
	def addService(self, uuid):
		"""Add a service of type uuid.

		Returns:
			The newly added service.
		"""
		if not isinstance(uuid, UUID):
			uuid = UUID(uuid)

		service = _ServerService(self, uuid)
		self.services.append(service)
		return service
Example #6
0
	def addDescriptor(self, uuid, value=None):
		"""Add a descriptor to the last characteristic

		Args:
			uuid : the type of the descriptor
			value : default value for descriptor
		Returns:
			The newly added characteristic
		"""
		if not isinstance(uuid, UUID):
			uuid = UUID(uuid)

		return _ServerDescriptor(self, uuid, value)
Example #7
0
	def __init__(self, server, uuid):
		assert isinstance(server, GattServer)
		assert isinstance(uuid, UUID)

		# Public members
		self.uuid = uuid
		self.characteristics = []

		# Protected members
		self._handle = _ServerHandle(server, self, UUID(gatt.PRIM_SVC_UUID))

		# reversed by convention
		self._handle._set_read_value(uuid.raw[::-1])
Example #8
0
	def addCharacteristic(self, uuid, value=None, allowNotify=False,
		allowIndicate=False):
		"""Add a characteristic to the last service.

		Notes: permissions are learned by adding callbacks and values to the
		characteristic.

		Args:
			uuid : type of the characteristic
			value : static value for the characteristic
			allowNotify : allow notifications
			allowIndicate :	allow indications
		Returns:
			The newly added characteristic
		"""
		if not isinstance(uuid, UUID):
			uuid = UUID(uuid)

		return _ServerCharacteristic(self, uuid, value, allowNotify,
			allowIndicate)
Example #9
0
	def __discover_all_characteristics(self):
		startHandle = self._handleNo + 1
		endHandle = self._endGroup
		if startHandle > endHandle:
			return [], {}

		characUuid = UUID(gatt.CHARAC_UUID)

		characs = []
		characHandles = {}
		currHandle = startHandle
		while True:
			req = att_pdu.new_read_by_type_req(currHandle, endHandle,
				characUuid)

			resp = self.client._new_transaction(req)
			if not resp:
				raise ClientError("transaction failed")

			if resp[0] == att.OP_READ_BY_TYPE_RESP:
				attDataLen = resp[1]
				idx = 2

				maxHandleNo = currHandle # not needed
				while idx < len(resp) and idx + attDataLen <= len(resp):
					handleNo = att_pdu.unpack_handle(resp, idx)
					properties = resp[idx+2]
					valHandleNo = att_pdu.unpack_handle(resp, idx + 3)
					uuid = UUID(resp[idx+5:idx+attDataLen], reverse=True)

					charac = _ClientCharacteristic(self.client, self, uuid,
						handleNo, properties, valHandleNo)
					characs.append(charac)
					characHandles[handleNo] = characs

					idx += attDataLen
					maxHandleNo = valHandleNo

				currHandle = maxHandleNo + 1
				if currHandle >= endHandle:
					break

			elif (resp[0] == att.OP_ERROR and len(resp) == att_pdu.ERROR_PDU_LEN
				and resp[1] == att.OP_READ_BY_TYPE_REQ
				and resp[4] == att.ECODE_ATTR_NOT_FOUND):
				break

			elif (resp[0] == att.OP_ERROR and len(resp) == att_pdu.ERROR_PDU_LEN
				and resp[1] == att.OP_READ_BY_TYPE_REQ
				and resp[4] == att.ECODE_READ_NOT_PERM):
				currHandle = currHandle + 1
				if currHandle >= endHandle:
					break

			elif (resp[0] == att.OP_ERROR and len(resp) == att_pdu.ERROR_PDU_LEN
				and resp[1] == att.OP_READ_BY_TYPE_REQ):
				raise ClientError("error - %s" % att.ecodeLookup(resp[4]))

			else:
				raise ClientError("unexpected - %s" % att.opcodeLookup(resp[0]))

		for i, charac in enumerate(characs):
			if i + 1 < len(characs):
				charac._set_end_group(characs[i+1]._handleNo-1)
			else:
				charac._set_end_group(self._endGroup)

		return characs, characHandles
Example #10
0
	def __init__(self, server, uuid, staticValue=None, allowNotify=False,
		allowIndicate=False):
		assert isinstance(server, GattServer)
		assert isinstance(uuid, UUID)

		# Public members
		self.server = server
		self.uuid = uuid
		self.descriptors = []
		self.service = server.services[-1]

		self.service._add_characteristic(self)

		# Protected members
		self._properties = 0
		if staticValue:
			self._properties |= gatt.CHARAC_PROP_READ
		if allowNotify:
			self._properties |= gatt.CHARAC_PROP_NOTIFY
		if allowIndicate:
			self._properties |= gatt.CHARAC_PROP_IND

		self._has_subscriber = False
		self._subscribe_callback = lambda: None
		self._unsubscribe_callback = lambda: None

		self._handle = _ServerHandle(server, self, UUID(gatt.CHARAC_UUID))
		self._valHandle = _ServerHandle(server, self, self.uuid)

		if allowNotify or allowIndicate:
			self._cccd = _ServerDescriptor(server,
				UUID(gatt.CLIENT_CHARAC_CFG_UUID))
			def cccd_cb(value):
				"""Special callback for CCCD"""
				assert isinstance(value, bytearray)
				if len(value) != 2:
					raise ServerError("invalid cccd pdu")
				else:
					if value[0] == 1 or value[1] == 1:
						if not self._has_subscriber:
							self._subscribe_callback(value)
							self._has_subscriber = True
					else:
						if self._has_subscriber:
							self._unsubscribe_callback()
							self._has_subscriber = False
					self._cccd.write(value)

			self._cccd._handle._set_write_callback(cccd_cb)
			self._cccd.write(bytearray([0, 0]))
		else:
			self._cccd = None

		if staticValue:
			self._valHandle._set_read_value(staticValue)

		def read_cb():
			"""Properties may change."""
			handleValue = bytearray([self._properties])
			handleValue += self._valHandle._get_handle_as_bytearray()
			handleValue += self.uuid.raw[::-1]
			return handleValue
		self._handle._set_read_callback(read_cb)
Example #11
0
	def discoverDescriptors(self):
		"""Return a list of descriptors"""

		assert self._endGroup is not None

		startHandle = self._handleNo + 1
		endHandle = self._endGroup
		if startHandle > endHandle:
			return []

		descriptors = []

		cccdUuid = UUID(gatt.CLIENT_CHARAC_CFG_UUID)
		cccd = None

		userDescUuid = UUID(gatt.CHARAC_USER_DESC_UUID)
		userDesc = None

		currHandle = startHandle
		while True:
			req = att_pdu.new_find_info_req(currHandle, endHandle)

			resp = self.client._new_transaction(req)
			if not resp:
				raise ClientError("transaction failed")

			if resp[0] == att.OP_FIND_INFO_RESP:
				attDataLen = 4 if resp[1] == att.FIND_INFO_RESP_FMT_16BIT \
					else 18
				idx = 2

				handleNo = currHandle # not needed
				while idx < len(resp) and idx + attDataLen <= len(resp):
					handleNo = att_pdu.unpack_handle(resp, idx)
					uuid = UUID(resp[idx+2:idx+attDataLen], reverse=True)

					if handleNo == self._valHandleNo:
						idx += attDataLen
						continue

					if uuid == userDescUuid:
						descriptor = _ClientDescriptor(self.client, self, uuid,
							handleNo, cacheable=True)
						userDesc = descriptor
					else:
						descriptor = _ClientDescriptor(self.client, self, uuid,
							handleNo)

					if uuid == cccdUuid:
						# hide the cccd from users
						cccd = descriptor
					else:
						descriptors.append(descriptor)

					idx += attDataLen

				currHandle = handleNo + 1
				if currHandle >= endHandle:
					break

			elif (resp[0] == att.OP_ERROR and len(resp) == att_pdu.ERROR_PDU_LEN
				and resp[1] == att.OP_FIND_INFO_REQ
				and resp[4] == att.ECODE_ATTR_NOT_FOUND):
				break

			elif (resp[0] == att.OP_ERROR and len(resp) == att_pdu.ERROR_PDU_LEN
				and resp[1] == att.OP_FIND_INFO_REQ):
				raise ClientError("error - %s" % att.ecodeLookup(resp[4]))

			else:
				raise ClientError("unexpected - %s" % att.opcodeLookup(resp[0]))

		self.descriptors = descriptors
		self._cccd = cccd
		self._user_desc = userDesc
		return descriptors
Example #12
0
def parse_read_by_group_req(pdu):
    return pdu[0], unpack_handle(pdu, 1), unpack_handle(pdu, 3), \
     UUID(pdu[5:], reverse=True)
Example #13
0
def parse_find_by_type_req(pdu):
    return pdu[0], unpack_handle(pdu, 1), unpack_handle(pdu, 3), \
     UUID(pdu[5:7], reverse=True), pdu[7:]