def _connect(self, addr, addrType=btle.ADDR_TYPE_PUBLIC, iface=None, timeout=None): if len(addr.split(":")) != 6: raise ValueError("Expected MAC address, got %s" % repr(addr)) if addrType not in (btle.ADDR_TYPE_PUBLIC, btle.ADDR_TYPE_RANDOM): raise ValueError( "Expected address type public or random, got {}".format( addrType)) self._startHelper(iface) self.addr = addr self.addrType = addrType self.iface = iface if iface is not None: self._writeCmd("conn %s %s %s\n" % (addr, addrType, "hci" + str(iface))) else: self._writeCmd("conn %s %s\n" % (addr, addrType)) rsp = self._getResp('stat', timeout) if rsp is None: raise btle.BTLEDisconnectError( "Timed out while trying to connect to peripheral %s, addr type: %s" % (addr, addrType), rsp) while rsp['state'][0] == 'tryconn': rsp = self._getResp('stat', timeout) if rsp['state'][0] != 'conn': self._stopHelper() raise btle.BTLEDisconnectError( "Failed to connect to peripheral %s, addr type: %s" % (addr, addrType), rsp)
def _connect(self, addr, addrType=btle.ADDR_TYPE_PUBLIC, iface=None, timeout=5): """ Temporary manual patch see https://github.com/IanHarvey/bluepy/pull/434 also added a default `timeout` as this is not part yet of the release bluepy package """ if len(addr.split(":")) != 6: raise ValueError("Expected MAC address, got %s" % repr(addr)) if addrType not in (btle.ADDR_TYPE_PUBLIC, btle.ADDR_TYPE_RANDOM): raise ValueError("Expected address type public or random, got {}".format(addrType)) self._startHelper(iface) self.addr = addr self.addrType = addrType self.iface = iface if iface is not None: self._writeCmd("conn %s %s %s\n" % (addr, addrType, "hci"+str(iface))) else: self._writeCmd("conn %s %s\n" % (addr, addrType)) rsp = self._getResp('stat', timeout) if rsp is None: self._stopHelper() raise btle.BTLEDisconnectError("Timed out while trying to connect to peripheral %s, addr type: %s" % (addr, addrType), rsp) while rsp and rsp['state'][0] == 'tryconn': rsp = self._getResp('stat', timeout) if rsp is None: self._stopHelper() raise btle.BTLEDisconnectError("Timed out while trying to connect to peripheral %s, addr type: %s" % (addr, addrType), rsp) if rsp['state'][0] != 'conn': self._stopHelper() raise btle.BTLEDisconnectError("Failed to connect to peripheral %s, addr type: %s [%s]" % (addr, addrType, rsp), rsp)
def btle_connection(self): """Contextmanager to handle a bluetooth connection to Comet Blue device. Any problem that arises when using the connection, will be handled, the connection closed and any resources aquired released. Debug logging for analysis is integrated, the errors are raised to be handled by the user. The following aspects are managed: * Connect handles setup, preparations for read/write and authentication. * Read/Write error handling. * Disconnect handles proper releasing of any aquired resources. """ conn = self._connect() self.available = True try: yield conn except btle.BTLEException as ex: _LOGGER.debug( "Couldn't read/write cometblue data for device %s:\n%s", self._address, ex) self.available = False raise except BrokenPipeError as ex: _LOGGER.debug("Device %s BrokenPipeError: %s", self._address, ex) self.available = False raise btle.BTLEDisconnectError() from ex finally: self._disconnect(conn)
def _disconnect(self, connection): """Disconnect from thermostat""" try: connection.disconnect() except (btle.BTLEException, BrokenPipeError) as ex: _LOGGER.debug("Couldn't disconnect from device %s:\n%s", self._address, ex) raise btle.BTLEDisconnectError() from ex else: _LOGGER.debug("Disconnected from device %s", self._address)
def _waitResp(self, wantType, timeout=None): while True: if self._helper.poll() is not None: raise btle.BTLEInternalError("Helper exited") if timeout: logger.debug("_waitResp - set timeout to %d", timeout) fds = self._poller.poll(timeout * 1000) if len(fds) == 0: logger.debug("Select timeout") return None rv = self._helper.stdout.readline() if rv.startswith('#') or rv == '\n' or len(rv) == 0: continue resp = btle.BluepyHelper.parseResp(rv) if 'rsp' not in resp: raise btle.BTLEInternalError("No response type indicator", resp) respType = resp['rsp'][0] if respType in wantType: logger.debug("_waitResp - resp [%s]", resp) return resp elif respType == 'stat': if 'state' in resp and len( resp['state']) > 0 and resp['state'][0] == 'disc': self._stopHelper() raise btle.BTLEDisconnectError("Device disconnected", resp) elif respType == 'err': errcode = resp['code'][0] if errcode == 'nomgmt': raise btle.BTLEManagementError( "Management not available (permissions problem?)", resp) elif errcode == 'atterr': raise btle.BTLEGattError("Bluetooth command failed", resp) else: raise btle.BTLEException( "Error from bluepy-helper (%s)" % errcode, resp) elif respType == 'scan': # Scan response when we weren't interested. Ignore it continue else: raise btle.BTLEInternalError( "Unexpected response (%s)" % respType, resp)
def _disconnect(self): """Disconnect from the weather station. If we are not already connected, raise a btle.BTLEDisconnectError exception. This method is called by most other methods if an exception is caught, whilst they're doing anything, to avoid jamming the Bluetooth stack on the weather station. """ if not self._station: raise btle.BTLEDisconnectError( "already disconnected from weather station") self._station.disconnect() self._station = None logging.debug("disconnected from weather station: %s", self._mac)
def _connect(self): """This method connects to the weather station. If the weather station is already connected, it will be disconnected (to avoid jamming up a station's Bluetooth stack) and a btle.BTLEDisconnectError exception raised. """ # if the station is already connected, disconnect and raise exception if self._station: self._disconnect() raise btle.BTLEDisconnectError( "already connected to weather station: %s" % self._mac) # connect to the station logging.debug("connecting to weather station: %s", self._mac) try: self._station = btle.Peripheral(self._mac, btle.ADDR_TYPE_RANDOM) except btle.BTLEDisconnectError: logging.debug("error connecting to weather station") raise # set up the notification delegate try: self._station.withDelegate(_WeatherStationDelegate()) except btle.BTLEDisconnectError: self._disconnect() logging.debug("error setting notification delegate for station") raise logging.debug("connected to weather station: %s", self._mac)