def scan(match=None, duration=5, just_one=False, match_scan_entry=False): """This looks through any advertising btle devices for matching ones. *** Note you must run this as root *** match(scan_data) should return True if scan_data is from the device you're looking for. If no match function is provided, it is treated as always returning True (you want to see all devices that respond within duration). scan_data is a dict mapping description to value, unless match_scan_entry is True in which case scan_data is a btle ScanEntry object. Duration is how many seconds to scan before returning. If just_one is False, scans for the full duration and then returns a list of matching ScanEntry objects. (https://ianharvey.github.io/bluepy-doc/scanentry.html) If just_one is True, this returns the first match immediately (no list) or None after full duration timeout. Note (match==None and just_one==True) results in just the first device to respond being immediately returned. """ scanner = Scanner().withDelegate(DefaultDelegate()) scanner.clear() scanner.start() if not just_one: found = [] try: for i in range(max(int(duration * 10), 1)): # Break duration into 10ths scanner.process( 0.1) # Handle incoming messages for 1/10th of a second... devices = scanner.getDevices() for dev in devices: if debug: BtleDevice.dump_scan_entry(dev) if match_scan_entry: params = dev else: params = { name: val for tag, name, val in dev.getScanData() } if match is None or match(params): if just_one: return dev else: found.append(dev) scanner.clear() finally: scanner.stop() if just_one: return None return found
class BLEService(Thread): def __init__(self, q): Thread.__init__(self) self.scanner = Scanner() self.queue = q self.devicesScanned = [] def run(self): print("Starting BLE service...") self.scanner.start() while True: self.devicesScanned.clear() self.scanner.clear() self.scanner.process(5.0) devices = self.scanner.getDevices() self.fillQueue(devices) #if len(self.queue) > 0: #test = self.getFromQueue() #for dev in test: # print("DEV: {}".format(dev.getJson())) # print("_________________________") print( "_____________________Save ble setup, queue size = {}".format( len(self.queue))) delay(3) def fillQueue(self, devices): for d in devices: devDto = BLEDeviceDto(d.addr, d.addrType, d.rssi, d.getScanData()) self.devicesScanned.append(devDto) self.queue.append(self.devicesScanned) def getFromQueue(self): if len(self.queue) is not 0: msg = self.queue.popleft() return msg else: return None
import sys from bluepy.btle import Peripheral, Scanner, DefaultDelegate, UUID, ADDR_TYPE_PUBLIC # class ScanDelegate(DefaultDelegate): # def __init__(self): # DefaultDelegate.__init__(self) # def handleDiscovery(self, dev, isNewDev, isNewData): # if isNewDev: # print ("Discovered device", dev.addr) # elif isNewData: # print ("Received new data from", dev.addr) scanner = Scanner()#.withDelegate(ScanDelegate()) scanner.scan(10.0) devices = scanner.getDevices() for i, device in enumerate(devices): print(f"\n~~~~~~ Device {i} ~~~~~~") print(f"Device Address: {device.addr}, RSSI: {device.rssi}, Connectable: {device.connectable}") for (adtype, desc, value) in device.getScanData(): print (f"AD Type {adtype}, Type Name: {desc}, Value: {value}") print("\n>>>>>>>>>> Connect to Arduino Nano 33 BLE and read characteristics <<<<<<<<<<\n") arduino = Peripheral(addrType=ADDR_TYPE_PUBLIC, iface=None) for device in devices: if device.addr == "e9:be:2d:f0:66:70": arduino.connect(device) for characteristic in arduino.getCharacteristics():
class InkbirdUpdater(Entity): entity_id = "inkbird.updater" def __init__(self, hass, inkbird_devices): """Initialize the thermometer.""" Entity.__init__(self) self._name = 'Inkbird Updater' self._state = None self._mac = None self.hass = hass self.scanner = Scanner() self.scanner.clear() self.scanner.start() self.no_results_counter = 0 self.inkbird_devices = inkbird_devices @property def mac(self): """Return the mac of the sensor.""" return self._mac @property def name(self): """Return the name of the sensor.""" return self._name @property def state(self): """Return the state of the sensor.""" return self._state @property def should_poll(self): """Return the name of the sensor.""" _LOGGER.debug("Should_Poll called") return True def update(self): """Get the latest data and use it to update our sensor state.""" _LOGGER.debug("UPDATE called") _LOGGER.debug(f"scanner here is {self.scanner}") # The btle on my raspberry pi 4 seems to go MIA if self.no_results_counter >= 5: _LOGGER.error("Btle went away .. restarting entire btle stack") self.scanner = Scanner() self.scanner.clear() self.scanner.start() self.no_results_counter = 0 try: self.scanner.process(timeout=8.0) except: e = sys.exc_info()[0] _LOGGER.error(f" Exception occoured during scanning: {e}") results = self.scanner.getDevices() _LOGGER.debug(f"got results {results}") for dev in results: self.handleDiscovery(dev) # if we have no results at all, the scanner may have gone MIA. # it happens apparently. So, let's count upto 5 and then, if it # still happens, restart/refresh the btle stack. # any results though will reset the btle 'MIA counter' to 0 if not any(results): self.no_results_counter += 1 else: self.no_results_counter = 0 self.scanner.clear() self._state = [] return True def handleDiscovery(self, dev): _LOGGER.debug(f"Discovered device {dev.addr}") _LOGGER.debug("Device {} ({}), RSSI={} dB".format( dev.addr, dev.addrType, dev.rssi)) for (adtype, desc, value) in dev.getScanData(): _LOGGER.debug("[%s] %s = %s" % (adtype, desc, value)) if adtype == 255: humidity = "%2.2f" % (int(value[6:8] + value[4:6], 16) / 100) #temperature = "%2.2f" % (int(value[2:4]+value[:2], 16)/100) temperature = int(value[2:4] + value[:2], 16) temperature_bits = 16 if temperature & (1 << (temperature_bits - 1)): temperature -= 1 << temperature_bits temperature = "%2.2f" % (temperature / 100) battery = int(value[14:16], 16) _LOGGER.debug(self.inkbird_devices) for device in self.inkbird_devices: _LOGGER.debug( f" dev addr is {dev.addr} and mac is {device.mac}") _LOGGER.debug( f" --> {temperature} - {humidity} - {battery} ") if dev.addr == device.mac: _LOGGER.debug( f" dev addr is {dev.addr} and mac is {device.mac} with parameter of {device.parameter}" ) old_state = self.hass.states.get( f"sensor.{device.entity_name}") if old_state: attrs = old_state.attributes else: attrs = None if device.parameter == "temperature": _LOGGER.debug( f" >>>> updating device {device.mac} with {temperature}" ) device.temperature = temperature device._state = temperature #self.hass.states.set(f"sensor.{device.entity_name}", temperature, attrs) elif device.parameter == "humidity": _LOGGER.debug( f" >>>> updating device {device.mac} with {humidity}" ) device.humidity = humidity device._state = humidity #self.hass.states.set(f"sensor.{device.entity_name}", humidity, attrs) else: _LOGGER.debug( f" >>>> updating device {device.mac} with {battery}" ) device.battery = battery device._state = battery
class InkbirdUpdater(Entity): entity_id = "inkbird.updater" def __init__(self, hass, inkbird_devices): """Initialize the thermometer.""" Entity.__init__(self) self._name = 'Inkbird Updater' self._state = None self._mac = None self.hass = hass self.scanner = Scanner() self.scanner.clear() self.scanner.start() self.inkbird_devices = inkbird_devices @property def mac(self): """Return the mac of the sensor.""" return self._mac @property def name(self): """Return the name of the sensor.""" return self._name @property def state(self): """Return the state of the sensor.""" return self._state @property def should_poll(self): """Return the name of the sensor.""" _LOGGER.debug("Should_Poll called") return True def update(self): # added global pid ## """Get the latest data and use it to update our sensor state.""" _LOGGER.debug("UPDATE called") _LOGGER.debug(f"scanner here is {self.scanner}") try: self.scanner.process(timeout=8.0) except: e = sys.exc_info()[0] _LOGGER.error(f" Exception occoured during scanning: {e}") results = self.scanner.getDevices() _LOGGER.debug(f"got results {results}") # The btle on my raspberry pi 4 seems to go MIA # if we have no results at all, the scanner may have gone MIA. # it happens apparently. So, let's count upto 5 and then, if it # still happens, restart/refresh the btle stack. # Seems to go MIA more frequently on RPi3B: removed counter and restart immediately if no results obtained # May generate 10 s update timeout errors if not any(results): _LOGGER.error("Btle went away .. restarting entire btle stack") ## Kill the bluepy-helper process # There is a memory leak: new instance of bluepy-helper is created with Scanner(), without terminating running instance # https://github.com/IanHarvey/bluepy/issues/267#issuecomment-657183840 # adapted from: https://github.com/JsBergbau/MiTemperature2/blob/master/LYWSD03MMC.py del self.scanner #probably not needed? bluepypid = 0 pstree = os.popen("pstree -p " + str(pid)).read( ) #we want to kill only bluepy from our own process tree, because other python scripts have there own bluepy-helper process _LOGGER.debug("PSTree: " + pstree) try: bluepypid = re.findall( r'bluepy-helper\((.*)\)', pstree)[0] #Store the bluepypid, to kill it later except IndexError: #Should not happen since we're now connected _LOGGER.debug("Couldn't find pid of bluepy-helper") if bluepypid is not 0: os.system("kill " + bluepypid) _LOGGER.debug("Killed bluepy with pid: " + str(bluepypid)) # Kill bluepy-helper systemwide anyways...not good if there are other scripts using bluepy # else: # os.system('pkill bluepy-helper') # _LOGGER.debug("Killed bluepy-helper") ## self.scanner = Scanner() self.scanner.clear() self.scanner.start() try: self.scanner.process(timeout=8.0) except: e = sys.exc_info()[0] _LOGGER.error(f" Exception occoured during scanning: {e}") results = self.scanner.getDevices() _LOGGER.debug(f"Got new results {results}") for dev in results: if dev.addr in devicemacs: self.handleDiscovery(dev) self.scanner.clear() self._state = [] return True def handleDiscovery(self, dev): # _LOGGER.debug(f"Discovered device {dev.addr}") _LOGGER.debug("Discovered device {} ({}), RSSI={} dB".format( dev.addr, dev.addrType, dev.rssi)) for (adtype, desc, value) in dev.getScanData(): _LOGGER.debug("[%s] %s = %s" % (adtype, desc, value)) if adtype == 255: _LOGGER.debug( f"{dev.addr} is in devicemacs list and now gets parameters!" ) humidity = "%2.2f" % (int(value[6:8] + value[4:6], 16) / 100) #temperature = "%2.2f" % (int(value[2:4]+value[:2], 16)/100) temperature = int(value[2:4] + value[:2], 16) temperature_bits = 16 if temperature & (1 << (temperature_bits - 1)): temperature -= 1 << temperature_bits temperature = "%2.2f" % (temperature / 100) battery = int(value[14:16], 16) _LOGGER.debug(self.inkbird_devices) for device in self.inkbird_devices: _LOGGER.debug( f" dev addr is {dev.addr} and mac is {device.mac}") # _LOGGER.debug(f" --> {temperature} - {humidity} - {battery} ") if dev.addr == device.mac: _LOGGER.debug( f" dev addr is {dev.addr} and mac is {device.mac} with parameter of {device.parameter}" ) # What does this do? Removed # old_state = self.hass.states.get(f"sensor.{device.entity_name}") # if old_state: # attrs = old_state.attributes # else: # attrs = None if device.parameter == "temperature": _LOGGER.debug( f" >>>> updating device {device.mac} with {temperature}" ) device.temperature = temperature device._state = temperature #self.hass.states.set(f"sensor.{device.entity_name}", temperature, attrs) elif device.parameter == "humidity": _LOGGER.debug( f" >>>> updating device {device.mac} with {humidity}" ) device.humidity = humidity device._state = humidity #self.hass.states.set(f"sensor.{device.entity_name}", humidity, attrs) else: _LOGGER.debug( f" >>>> updating device {device.mac} with {battery}" ) device.battery = battery device._state = battery #self.hass.states.set(f"sensor.{device.entity_name}", battery, attrs) _LOGGER.debug(f" Done with handleDiscovery")
from blind import Blind from bluepy.btle import Scanner, DefaultDelegate # Where in the config to look for the name, and the name to search for BLIND_AD_TYPE_NAME = 9 BLIND_NAME = "Blind" SCAN_TIME = 10.0 if __name__ == "__main__": scanner = Scanner() print("Scanning for devices...") devices = scanner.scan(SCAN_TIME) blindaddrs = [] print("\nDevices found:") for dev in scanner.getDevices(): print("Device %s (%s), RSSI=%d dB" % (dev.addr, dev.addrType, dev.rssi)) for (adtype, desc, value) in dev.getScanData(): print(" %s = %s" % (desc, value)) if dev.getValueText(BLIND_AD_TYPE_NAME) == BLIND_NAME: blindaddrs.append(dev.addr) if len(blindaddrs) > 0: print("\nBlinds found:") for i, addr in enumerate(blindaddrs): print(str(i) + ": " + addr) sel = -1 while sel < 0 or sel >= len(blindaddrs): try: sel = int( input(
class CentralManager(DefaultDelegate): SCAN_COUNT = 5 SCAN_INTERVAL = 2.0 def __init__(self): DefaultDelegate.__init__(self) self._scanner = Scanner().withDelegate(self) self._lamps = [] self._candidateFound = False self.isConnected = False def scan(self): self.isConnected = False # Clear existing connected lamps for lamp in self._lamps: lamp.disconnect() self._lamps = [] # Scan for new lamps to connect to print("Scanning for devices...") self._candidateFound = False self._scanner.clear() self._scanner.start() for iter in range(0, CentralManager.SCAN_COUNT): self._scanner.process(CentralManager.SCAN_INTERVAL) if (self._candidateFound): print("Candidate found after {} scan(s). Ending early".format( iter + 1)) break self._scanner.stop() scanResult = self._scanner.getDevices() # Connect to any lamps found for device in scanResult: if (Lampi.isLampCandidate(device)): lamp = Lampi(device) lamp.validate() if (lamp.isValid): print("Connected to a lamp with MAC address {}".format( device.addr)) self._lamps.append(lamp) self.isConnected = True else: lamp.disconnect() print("Scan complete") def handleDiscovery(self, device, isNew, _): if isNew and device.connectable: if (Lampi.isLampCandidate(device)): self._candidateFound = True print(" • Candidate found with MAC address {}".format( device.addr)) # Discard any lamps that are no longer connected def pruneLamps(self): for i in range(0, len(self._lamps)): lamp = self._lamps[i] if (not lamp.isConnected()): print("Pruned {}".format(lamp.addr)) self._lamps.pop(i) i -= 1 def toggleOnOff(self): print("Toggling on/off") self.pruneLamps() for lamp in self._lamps: lamp.toggleOnOff() def brightnessUp(self): print("Sending brightness up") self.pruneLamps() for lamp in self._lamps: lamp.brightnessUp() def brightnessDown(self): print("Sending brightness down") self.pruneLamps() for lamp in self._lamps: lamp.brightnessDown() def preset(self, number): print("Starting Preset", str(number)) self.pruneLamps() for lamp in self._lamps: lamp.setPreset(number)