def display(self, buffer: bytes, delay=10): """ Displays the buffer during 10 seconds by default Args: buffer (bytes): buffer containing the pattern delay (int, optional): time during which the pattern is displayed. Defaults to 10. Raises: BTLEException: is thrown if no glass are connected when the function is called BTLEException: is thrown if the writing characteristic hasn't been found when the function is called """ if self.connected: if self.write_characteristic: print("[" + Fore.CYAN + "*" + Fore.RESET + "] Sending buffer ...") for byte in buffer: self.write_characteristic.write(byte) print("[" + Fore.CYAN + "*" + Fore.RESET + "] Buffer sent") sleep(delay) else: raise BTLEException("Characteristic not found") else: raise BTLEException("No glasses are connected")
def writeCharacteristic(self, handle, payload, withResponse=False): LOGGER.debug("write(0x{:x}) payload: %a".format(handle, payload)) if self._connected is False: raise BTLEException(BTLEException.INTERNAL_ERROR, "Helper not started (did you call connect()?)") if handle == mi.handle_measurement_control and payload[0] == 0xa0: self.cache[mi.handle_measurement_read] = VALUE_MEASUREMENT if handle == mi.handle_history_control: if payload == mi.cmd_history_read_init: self.cache[mi.handle_history_read] = (self.history_items).to_bytes(2, BYTEORDER) + \ (0).to_bytes(14, BYTEORDER) elif payload[0] == int.from_bytes(b'\xa1', BYTEORDER): address = int.from_bytes(payload[1:3], byteorder=BYTEORDER) self.cache[mi.handle_history_read] = \ (3600*(self.history_items - address)).to_bytes(4, BYTEORDER) + \ int(20.6*10).to_bytes(2, BYTEORDER) + b'\x00' + \ (1066).to_bytes(3, BYTEORDER) + b'\x00' + (57).to_bytes(1, BYTEORDER) + \ (123).to_bytes(2, BYTEORDER) + b'\x00\x00' elif payload == mi.cmd_history_read_success: self.cache[mi.handle_history_read] = VALUE_NO_DATA self.history_items = 0 elif payload == mi.cmd_history_read_failed: self.cache[mi.handle_history_read] = VALUE_NO_DATA return {'resp': 'wr'} if withResponse else None
def test_read_base_throw_exception(self, mocker): ble_char_mock = mocker.Mock(spec=btle.Characteristic) ble_char_mock.read.side_effect = BTLEException("Test IO Exception") ble_service_mock = mocker.Mock(spec=btle.Service) ble_service_mock.getCharacteristics.return_value = [ble_char_mock] peripheral_mock = mocker.Mock(spec=Peripheral) peripheral_mock.getServiceByUUID.return_value = ble_service_mock mocker.patch("omron.env_sensor.Peripheral", autospec=True, return_value=peripheral_mock) with pytest.raises(BTLEException) as e: env_sensor = OmronEnvSensor(TEST_MAC_ADDRESS) (ble_chara, raw_data) = env_sensor.read_char_base(0x1800, 0x2a00) assert str(e.value) == "Test IO Exception" peripheral_mock.getServiceByUUID.assert_called_with(uuidVal=0x1800) assert peripheral_mock.getServiceByUUID.call_count == 5 ble_service_mock.getCharacteristics.assert_called_with(forUUID=0x2a00) assert ble_service_mock.getCharacteristics.call_count == 5 ble_char_mock.read.assert_called_with() assert ble_char_mock.read.call_count == 5
def readCharacteristic(self, handle): if self._connected is False: raise BTLEException(BTLEException.INTERNAL_ERROR, "Helper not started (did you call connect()?)") response = self.cache.get(handle, b'\x00\x00') self._read_log.append((handle, response)) LOGGER.debug("read(0x{:x}) value: %a".format(handle, [x for x in response])) return response
def process(self, timeout=10.0): if self._helper is None: raise BTLEException(BTLEException.INTERNAL_ERROR, "Helper not started (did you call start()?)") start = time.time() while True: if timeout: remain = start + timeout - time.time() if remain <= 0.0: break else: remain = None resp = self._waitResp(['scan', 'stat'], remain) if resp is None: break respType = resp['rsp'][0] if respType == 'stat': # if scan ended, restart it if resp['state'][0] == 'disc': self._mgmtCmd("scan") elif respType == 'scan': # device found addr = self._decode_address(resp) dev = self._find_or_create(addr) isNewData = dev._update(resp) if self.delegate is not None: self.delegate.handleDiscovery(dev, (dev.updateCount <= 1), isNewData) if self.mac is not None and dev.addr == self.mac: break else: raise BTLEException(BTLEException.INTERNAL_ERROR, "Unexpected response: " + respType)
def gatherAverageRSSI(self, manufacturer, n_samples, passive=False): self.clear() self.start(passive=passive) rssi_scans = [] while len(rssi_scans) < n_samples: # wait 3 seconds before a timeout resp = self._waitResp(['scan', 'stat'], 3.0) # ^ if resp is None: break respType = resp['rsp'][0] if respType == 'stat': # if scan ended, restart it if resp['state'][0] == 'disc': self._mgmtCmd("scan") elif respType == 'scan': # device found addr = binascii.b2a_hex(resp['addr'][0]).decode('utf-8') addr = ':'.join([addr[i:i + 2] for i in range(0, 12, 2)]) if addr in self.scanned: dev = self.scanned[addr] else: dev = ScanEntry(addr, self.iface) self.scanned[addr] = dev isNewData = dev._update(resp) for (adtype, desc, value) in dev.getScanData(): if desc == "Manufacturer" and value == manufacturer: rssi_scans.append(dev.rssi) else: raise BTLEException(BTLEException.INTERNAL_ERROR, "Unexpected response: " + respType) mean_rssi = mean(rssi_scans) std_dev = stdev(rssi_scans) cutoff_rssi = [] lower_cutoff = mean_rssi - std_dev upper_cutoff = mean_rssi + std_dev for x in rssi_scans: if not (x < lower_cutoff or x > upper_cutoff): cutoff_rssi.append(x) self.stop() return mean(cutoff_rssi)
def join(self): """Join the thread. Raises: 'BTLEException' is raised if this method is not run as root. """ super(_StoppableScanner, self).join() if self._exc: msg = '\nBluetooth scanning requires root privilege, ' \ 'so please run the script with \"sudo\".' raise BTLEException(0, msg)
def disconnect(self): """ Disconnects from the glasses Raises: BTLEException: is thrown if no glasses are connected """ if self.connected: self.peripheral.disconnect() print("[" + Fore.RED + "-" + Fore.RESET + "] Glasses disconnected") else: raise BTLEException("No glasses are connected")
def discover(self, timeout_s=SCANNING_TIME_DEFAULT_s, asynchronous=False, show_warnings=False): """Perform the discovery process. This method can be run in synchronous (blocking) or asynchronous (non-blocking) way. Default is synchronous. The discovery process will last *timeout_s* seconds if provided, a default timeout otherwise. Args: timeout_s (int, optional): Time in seconds to wait before stopping the discovery process. asynchronous (bool, optional): If True the method is run in asynchronous way, thus non-blocking the execution of the thread, the opposite otherwise. show_warnings (bool, optional): If True shows warnings, if any, when discovering devices not respecting the BlueSTSDK's advertising data format, nothing otherwise. Returns: bool: True if the synchronous discovery has finished or if the asynchronous discovery has started, False if a discovery is already running. Raises: 'BTLEException' is raised if this method is not run as root. """ try: if not asynchronous: # Synchronous version. if self.is_discovering(): return False self._discovered_nodes = [] self._notify_discovery_change(True) self._scanner = \ Scanner().withDelegate(_ScannerDelegate(show_warnings)) self._scanner.scan(timeout_s) self._notify_discovery_change(False) return True else: # Asynchronous version. if not self.start_discovery(show_warnings): return False threading.Timer(timeout_s, self.stop_discovery).start() return True except BTLEException as e: msg = '\nBluetooth scanning requires root privilege, ' \ 'so please run the script with \"sudo\".' raise BTLEException(msg)
def process(self, timeout=10.0): if self._helper is None: raise BTLEException(BTLEException.INTERNAL_ERROR, "Helper not started (did you call start()?)") start = time.time() while True: if timeout: remain = start + timeout - time.time() if remain <= 0.0: break else: remain = None resp = self._waitResp(['scan', 'stat'], remain) if resp is None: break respType = resp['rsp'][0] if respType == 'stat': # if scan ended, restart it if resp['state'][0] == 'disc': self._mgmtCmd("scan") elif respType == 'scan': # device found addr = binascii.b2a_hex(resp['addr'][0]).decode('utf-8') addr = ':'.join([addr[i:i + 2] for i in range(0, 12, 2)]) if addr in self.scanned: dev = self.scanned[addr] else: dev = ScanEntry(addr, self.iface) self.scanned[addr] = dev isNewData = dev._update(resp) if self.delegate is not None: self.delegate.handleDiscovery(dev, (dev.updateCount <= 1), isNewData) else: raise BTLEException(BTLEException.INTERNAL_ERROR, "Unexpected response: " + respType)
class TestBluepy(unittest.TestCase): """Unit tests for the bluepy backend.""" # pylint: disable=no-self-use @mock.patch('bluepy.btle.Peripheral') def test_configuration_default(self, mock_peripheral): """Test adapter name pattern parsing.""" backend = BluepyBackend() backend.connect(TEST_MAC) mock_peripheral.assert_called_with(TEST_MAC, addrType='public', iface=0) @mock.patch('bluepy.btle.Peripheral') def test_configuration_hci12(self, mock_peripheral): """Test adapter name pattern parsing.""" backend = BluepyBackend(adapter='hci12') backend.connect(TEST_MAC) mock_peripheral.assert_called_with(TEST_MAC, addrType='public', iface=12) @mock.patch('bluepy.btle.Peripheral') def test_configuration_invalid(self, _): """Test adapter name pattern parsing.""" backend = BluepyBackend(adapter='somestring') with self.assertRaises(BluetoothBackendException): backend.connect(TEST_MAC) def test_check_backend_ok(self): """Test check_backend successfully.""" self.assertTrue(BluepyBackend.check_backend()) @mock.patch('bluepy.btle.Peripheral', **{'side_effect': BTLEException(1, 'text')}) def test_connect_exception(self, _): """Test exception wrapping.""" backend = BluepyBackend() with self.assertRaises(BluetoothBackendException): backend.connect(TEST_MAC) @mock.patch('bluepy.btle.Peripheral') def test_wait_for_notification(self, mock_peripheral): """Test writing to a handle successfully.""" backend = BluepyBackend() backend.connect(TEST_MAC) self.assertTrue(backend.wait_for_notification(0xFF, None, 10)) mock_peripheral.assert_called_with(TEST_MAC, addrType='public', iface=0)
def _waitResp(self, wantType, timeout=None): self._log.debug("Z1: _waitResp override method in timeout:" + str(timeout)) while True: if self._helper.poll() is not None: raise BTLEInternalError("Helper exited") if timeout: fds = self._poller.poll(timeout * 1000) if len(fds) == 0: self._log.info("Select timeout:" + str(timeout)) return None rv = self._helper.stdout.readline() self._log.debug("Got:" + str(repr(rv))) if rv.startswith('#') or rv == '\n' or len(rv) == 0: continue resp = BluepyHelper.parseResp(rv) if 'rsp' not in resp: raise BTLEInternalError("No response type indicator", resp) respType = resp['rsp'][0] if respType in wantType: return resp elif respType == 'stat': if 'state' in resp and len( resp['state']) > 0 and resp['state'][0] == 'disc': self._stopHelper() raise BTLEDisconnectError("Device disconnected", resp) elif respType == 'err': errcode = resp['code'][0] if errcode == 'nomgmt': raise BTLEManagementError( "Management not available (permissions problem?)", resp) elif errcode == 'atterr': raise BTLEGattError("Bluetooth command failed", resp) else: raise BTLEException( "Error from bluepy-helper (%s)" % errcode, resp) elif respType == 'scan': # Scan response when we weren't interested. Ignore it continue else: raise BTLEInternalError("Unexpected response (%s)" % respType, resp)
def __init__(self, peripheral, callback): try: service = peripheral.getServiceByUUID(self._UUID_SERVICE) self._char_tx = service.getCharacteristics( forUUID=self._UUID_CHARACTERISTIC_TX)[0] self._char_rx = service.getCharacteristics( forUUID=self._UUID_CHARACTERISTIC_RX)[0] self._desc_tx = service.getDescriptors( forUUID=self._UUID_DESCRIPTOR_TX)[0] except (BTLEException, IndexError) as exception: raise BTLEException( "Transparent UART service not found") from exception # Set up the notification delegate and turn them on delegate = self._TransparentDelegate(callback) peripheral.setDelegate(delegate) self._desc_tx.write(b'\x01\x00')
def discover(self, show_warnings=False, timeout_s=0): """Perform the discovery process. The discovery process will last *timeout_s* seconds if provided, a default timeout otherwise. Synchronous method. Args: show_warnings (bool, optional): If True shows warnings, if any, when discovering devices not respecting the BlueSTSDK's advertising data format, nothing otherwise. timeout_s (int, optional): Time in seconds to wait before stopping the discovery process. Returns: bool: True if the discovery has finished, False if a discovery is already running. Raises: 'BTLEException' is raised if this method is not run as root. """ try: if self.is_discovering(): return False if timeout_s == 0: timeout_s = _ScannerDelegate.SCANNING_TIME_DEFAULT_s self._notify_discovery_change(True) self._scanner = Scanner().withDelegate( _ScannerDelegate(show_warnings)) self._scanner.scan(timeout_s) self._notify_discovery_change(False) return True except BTLEException as e: msg = '\nBluetooth scanning requires root privilege, ' \ 'so please run the script with \"sudo\".' raise BTLEException(e.code, msg)
def process(self, timeout=300.0): """ Method receives advertisements from nodes. Variable timeout has default value 300 seconds, it define time after which method will end. """ if self._helper is None: raise BTLEException(BTLEException.INTERNAL_ERROR, "Helper not started (did you call start()?)") start = time.time() while True: if timeout: remain = start + timeout - time.time() if remain <= 0.0: break else: remain = None resp = self._waitResp(['scan', 'stat'], remain) if resp is None: break respType = resp['rsp'][0] if respType == 'stat': logging.info("STAT message,") # if scan ended, restart it if resp['state'][0] == 'disc': logging.warning("Executing SCAN cmd!") self._mgmtCmd("scan") elif respType == 'scan': # device found addr = binascii.b2a_hex(resp['addr'][0]).decode('utf-8') addr = ':'.join([addr[i:i + 2] for i in range(0, 12, 2)]) addr = addr.upper() if not Device.select(lambda d: d.id.upper() == addr).first( ) and not settings.ALLOW_ANY: logging.warning( "Unknown device {} send message, skipping...".format( addr)) continue dev = ScanEntry(addr, self.iface) logging.info("SCAN message from {}".format(addr)) dev._update(resp) if not settings.ALLOW_ANY: name = '' for data in dev.getScanData(): for x in data: if type("Name") == type(x) and "Name" in x: name = data[-1] if not settings.NAME_VALIDATION: logging.warning( "Accepting device without name validation...") elif Device[addr].name != name and Device[ addr].name != None: logging.warning( "{} invalid name valid: {}, received: {}, skipping..." .format(addr, name, Device[addr].name)) continue if self.delegate is not None: self.delegate.handleDiscovery(dev, (dev.updateCount <= 1), True) else: raise BTLEException(BTLEException.INTERNAL_ERROR, "Unexpected response: " + respType)
def test_connect_exception(self, mock_peripheral): """Test exception wrapping.""" mock_peripheral.side_effect = BTLEException("Test") backend = BluepyBackend() with self.assertRaises(BluetoothBackendException): backend.connect(TEST_MAC)
def _fail_for_n(self, n): self.failcount += 1 if self.failcount < n: raise BTLEException(0, 'test exception') return self.failcount
def __init__(self, peripheral): try: self._service = peripheral.getServiceByUUID(self._UUID_SERVICE) except (BTLEException, IndexError) as exception: raise BTLEException("DeviceInfo service not found") from exception